From 2b39f6ae88d79b714d395aba15b3e2e20761ec02 Mon Sep 17 00:00:00 2001 From: gered Date: Tue, 4 Apr 2023 18:26:38 -0400 Subject: [PATCH] add indexed triangle 2d visual reference tests --- ggdt/tests/graphics_indexed.rs | 364 ++++++++++++++++++ ggdt/tests/ref/indexed/triangle_2d.png | Bin 0 -> 2082 bytes .../ref/indexed/triangle_2d_solid_blended.png | Bin 0 -> 2499 bytes .../indexed/triangle_2d_solid_textured.png | Bin 0 -> 2095 bytes .../triangle_2d_solid_textured_blended.png | Bin 0 -> 4034 bytes 5 files changed, 364 insertions(+) create mode 100644 ggdt/tests/ref/indexed/triangle_2d.png create mode 100644 ggdt/tests/ref/indexed/triangle_2d_solid_blended.png create mode 100644 ggdt/tests/ref/indexed/triangle_2d_solid_textured.png create mode 100644 ggdt/tests/ref/indexed/triangle_2d_solid_textured_blended.png diff --git a/ggdt/tests/graphics_indexed.rs b/ggdt/tests/graphics_indexed.rs index 1769f87..0bc762c 100644 --- a/ggdt/tests/graphics_indexed.rs +++ b/ggdt/tests/graphics_indexed.rs @@ -1923,3 +1923,367 @@ fn rotozoom_transparent_offset_blits() { //screen.to_png_file(path.as_path(), &palette).unwrap(); assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); } + +#[test] +fn triangle_2d() { + use IndexedTriangle2d::*; + + let (mut screen, palette) = setup(); + screen.clear(0); + + let color = 1; + let v1 = Vector2::new(32.0, 36.0); + let v2 = Vector2::new(32.0, 63.0); + let v3 = Vector2::new(73.0, 36.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(50.0, 0.0), // + v2 - Vector2::new(50.0, 0.0), + v3 - Vector2::new(50.0, 0.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(0.0, 50.0), // + v2 - Vector2::new(0.0, 50.0), + v3 - Vector2::new(0.0, 50.0), + ], + color, + }); + + let color = 2; + let v1 = Vector2::new(123.0, 60.0); + let v2 = Vector2::new(162.0, 60.0); + let v3 = Vector2::new(144.0, 32.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(0.0, 45.0), // + v2 - Vector2::new(0.0, 45.0), + v3 - Vector2::new(0.0, 45.0), + ], + color, + }); + + let color = 3; + let v1 = Vector2::new(265.0, 74.0); + let v2 = Vector2::new(265.0, 37.0); + let v3 = Vector2::new(231.0, 37.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(-70.0, 0.0), // + v2 - Vector2::new(-70.0, 0.0), + v3 - Vector2::new(-70.0, 0.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(0.0, 55.0), // + v2 - Vector2::new(0.0, 55.0), + v3 - Vector2::new(0.0, 55.0), + ], + color, + }); + + let color = 4; + let v1 = Vector2::new(33.0, 108.0); + let v2 = Vector2::new(33.0, 137.0); + let v3 = Vector2::new(59.0, 122.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(45.0, 0.0), // + v2 - Vector2::new(45.0, 0.0), + v3 - Vector2::new(45.0, 0.0), + ], + color, + }); + + let color = 5; + let v1 = Vector2::new(161.0, 132.0); + let v2 = Vector2::new(145.0, 92.0); + let v3 = Vector2::new(120.0, 115.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + + let color = 6; + let v1 = Vector2::new(237.0, 120.0); + let v2 = Vector2::new(267.0, 136.0); + let v3 = Vector2::new(267.0, 105.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(-70.0, 0.0), // + v2 - Vector2::new(-70.0, 0.0), + v3 - Vector2::new(-70.0, 0.0), + ], + color, + }); + + let color = 7; + let v1 = Vector2::new(29.0, 194.0); + let v2 = Vector2::new(62.0, 194.0); + let v3 = Vector2::new(29.0, 163.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(45.0, 0.0), // + v2 - Vector2::new(45.0, 0.0), + v3 - Vector2::new(45.0, 0.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(0.0, -55.0), // + v2 - Vector2::new(0.0, -55.0), + v3 - Vector2::new(0.0, -55.0), + ], + color, + }); + + let color = 8; + let v1 = Vector2::new(130.0, 164.0); + let v2 = Vector2::new(155.0, 190.0); + let v3 = Vector2::new(177.0, 164.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(0.0, -60.0), // + v2 - Vector2::new(0.0, -60.0), + v3 - Vector2::new(0.0, -60.0), + ], + color, + }); + + let color = 9; + let v1 = Vector2::new(235.0, 193.0); + let v2 = Vector2::new(269.0, 193.0); + let v3 = Vector2::new(269.0, 163.0); + screen.triangle_2d(&Solid { position: [v1, v2, v3], color }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(-70.0, 0.0), // + v2 - Vector2::new(-70.0, 0.0), + v3 - Vector2::new(-70.0, 0.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + v1 - Vector2::new(0.0, -60.0), // + v2 - Vector2::new(0.0, -60.0), + v3 - Vector2::new(0.0, -60.0), + ], + color, + }); + + // totally off screen + + let color = 12; + + screen.triangle_2d(&Solid { + position: [ + Vector2::new(-32.0, 36.0), // + Vector2::new(-32.0, 63.0), + Vector2::new(-73.0, 36.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + Vector2::new(265.0, -26.0), // + Vector2::new(265.0, -63.0), + Vector2::new(231.0, -63.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + Vector2::new(29.0, 294.0), // + Vector2::new(62.0, 294.0), + Vector2::new(29.0, 263.0), + ], + color, + }); + screen.triangle_2d(&Solid { + position: [ + Vector2::new(335.0, 193.0), // + Vector2::new(369.0, 193.0), + Vector2::new(369.0, 163.0), + ], + color, + }); + + // wrong vertex winding (clockwise instead of counter-clockwise) + + let color = 12; + + screen.triangle_2d(&Solid { + position: [ + Vector2::new(120.0, 115.0), // + Vector2::new(145.0, 92.0), + Vector2::new(161.0, 132.0), + ], + color, + }); + + let path = reference_file(Path::new("triangle_2d.png")); + //screen.to_png_file(path.as_path(), &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); +} + +#[allow(dead_code)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum TriangleType { + Solid = 0, + SolidBlended = 1, + SolidTextured = 2, + SolidTexturedBlended = 3, +} + +fn get_quad<'a>( + mode: TriangleType, + texture: Option<&'a IndexedBitmap>, + blendmap: Option<&'a BlendMap>, + transform: Matrix3x3, + top_left: Vector2, + top_right: Vector2, + bottom_left: Vector2, + bottom_right: Vector2, +) -> [IndexedTriangle2d<'a>; 2] { + let top_left = transform * top_left; + let top_right = transform * top_right; + let bottom_left = transform * bottom_left; + let bottom_right = transform * bottom_right; + + let positions_1 = [top_left, bottom_left, bottom_right]; + let positions_2 = [top_left, bottom_right, top_right]; + let texcoords_1 = [Vector2::new(0.0, 0.0), Vector2::new(0.0, 1.0), Vector2::new(1.0, 1.0)]; + let texcoords_2 = [Vector2::new(0.0, 0.0), Vector2::new(1.0, 1.0), Vector2::new(1.0, 0.0)]; + let color = 5; + + match mode { + TriangleType::Solid => [ + IndexedTriangle2d::Solid { position: positions_1, color }, + IndexedTriangle2d::Solid { position: positions_2, color }, + ], + TriangleType::SolidBlended => [ + IndexedTriangle2d::SolidBlended { position: positions_1, color, blendmap: blendmap.unwrap() }, + IndexedTriangle2d::SolidBlended { position: positions_2, color, blendmap: blendmap.unwrap() }, + ], + TriangleType::SolidTextured => [ + IndexedTriangle2d::SolidTextured { position: positions_1, texcoord: texcoords_1, bitmap: texture.unwrap() }, + IndexedTriangle2d::SolidTextured { position: positions_2, texcoord: texcoords_2, bitmap: texture.unwrap() }, + ], + TriangleType::SolidTexturedBlended => [ + IndexedTriangle2d::SolidTexturedBlended { + position: positions_1, + texcoord: texcoords_1, + bitmap: texture.unwrap(), + blendmap: blendmap.unwrap(), + }, + IndexedTriangle2d::SolidTexturedBlended { + position: positions_2, + texcoord: texcoords_2, + bitmap: texture.unwrap(), + blendmap: blendmap.unwrap(), + }, + ], + } +} + +#[rustfmt::skip] +#[test] +fn triangle_2d_solid_textured() { + let (mut screen, palette) = setup(); + screen.clear(247); + + let texture = generate_bitmap(32, 32); + + let top_left = Vector2::new(0.0, 0.0); + let top_right = Vector2::new(32.0, 0.0); + let bottom_left = Vector2::new(0.0, 32.0); + let bottom_right = Vector2::new(32.0, 32.0); + + let rotate = Matrix3x3::new_2d_rotation(RADIANS_45); + let scale = Matrix3x3::new_2d_scaling(2.0, 2.0); + + let mode = TriangleType::SolidTextured; + + let triangles = get_quad(mode, Some(&texture), None, Matrix3x3::new_2d_translation(40.0, 40.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let triangles = get_quad(mode, Some(&texture), None, scale * Matrix3x3::new_2d_translation(200.0, 40.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let triangles = get_quad(mode, Some(&texture), None, scale * rotate * Matrix3x3::new_2d_translation(120.0, 120.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let path = reference_file(Path::new("triangle_2d_solid_textured.png")); + //screen.to_png_file(path.as_path(), &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); +} + +#[rustfmt::skip] +#[test] +fn triangle_2d_solid_blended() { + let (mut screen, palette, blend_map) = setup_for_blending(); + + let top_left = Vector2::new(0.0, 0.0); + let top_right = Vector2::new(32.0, 0.0); + let bottom_left = Vector2::new(0.0, 32.0); + let bottom_right = Vector2::new(32.0, 32.0); + + let rotate = Matrix3x3::new_2d_rotation(RADIANS_45); + let scale = Matrix3x3::new_2d_scaling(2.0, 2.0); + + let mode = TriangleType::SolidBlended; + + let triangles = get_quad(mode, None, Some(&blend_map), Matrix3x3::new_2d_translation(40.0, 40.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let triangles = get_quad(mode, None, Some(&blend_map), scale * Matrix3x3::new_2d_translation(200.0, 40.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let triangles = get_quad(mode, None, Some(&blend_map), scale * rotate * Matrix3x3::new_2d_translation(120.0, 120.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let path = reference_file(Path::new("triangle_2d_solid_blended.png")); + //screen.to_png_file(path.as_path(), &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); +} + +#[rustfmt::skip] +#[test] +fn triangle_2d_solid_textured_blended() { + let (mut screen, palette, blend_map) = setup_for_blending(); + + let texture = generate_bitmap(32, 32); + + let top_left = Vector2::new(0.0, 0.0); + let top_right = Vector2::new(32.0, 0.0); + let bottom_left = Vector2::new(0.0, 32.0); + let bottom_right = Vector2::new(32.0, 32.0); + + let rotate = Matrix3x3::new_2d_rotation(RADIANS_45); + let scale = Matrix3x3::new_2d_scaling(2.0, 2.0); + + let mode = TriangleType::SolidTexturedBlended; + + let triangles = get_quad(mode, Some(&texture), Some(&blend_map), Matrix3x3::new_2d_translation(40.0, 40.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let triangles = get_quad(mode, Some(&texture), Some(&blend_map), scale * Matrix3x3::new_2d_translation(200.0, 40.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let triangles = get_quad(mode, Some(&texture), Some(&blend_map), scale * rotate * Matrix3x3::new_2d_translation(120.0, 120.0), top_left, top_right, bottom_left, bottom_right); + screen.triangle_list_2d(&triangles); + + let path = reference_file(Path::new("triangle_2d_solid_textured_blended.png")); + //screen.to_png_file(path.as_path(), &palette).unwrap(); + assert!(verify_visual(&screen, &palette, &path), "bitmap differs from source image: {:?}", path); +} diff --git a/ggdt/tests/ref/indexed/triangle_2d.png b/ggdt/tests/ref/indexed/triangle_2d.png new file mode 100644 index 0000000000000000000000000000000000000000..589fef9cd696cceafb9a6be7e78024ac7525eee4 GIT binary patch literal 2082 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 literal 0 HcmV?d00001 diff --git a/ggdt/tests/ref/indexed/triangle_2d_solid_textured.png b/ggdt/tests/ref/indexed/triangle_2d_solid_textured.png new file mode 100644 index 0000000000000000000000000000000000000000..f90a63e46f51456b7c8b75052a2e161932799a14 GIT binary patch literal 2095 zcmbu9dr%Wc9LF~=oS)$NJO2mXmagvMz%An+smr9QGv_k~} z2c05T#i9iUd1?)aHQ`{Wwn!DjKov0uv493F9Z(U(YvMl*(@gD5cfYgyyYFZ3Zht$w zdn77yvyIhKD*%8^M0nU%04SmQc*=sNZ#PYEi35NJqIlc5`bPk2^%d0C8XA2kFoXg_ zA@x;2l7{9@rw{d`ot&IlEEb#1h9HQ`&$rKOdXl?sKTySrPf)xH@| z!hn!l=tj0CX$L&m_!H4S7k@~n(h%1DijBn{G#G9@WHOD+aRu4$l9kT(?M zpn}vXNYWr3sTd>`H^{}n$TbZBKwJR=0Ym^0#2^6zAOyG~i-D1A8bA=jg%AORNFW3; zNP<8D1mYr#fstz(@cBp_ACd5p-FyTwXg9)_ApAIFF)(sX0{~z<0J8wh0WfS32L>z{ z*ujf|k!u>TSg=CELh1;YkGFpC4Tyy3;b$TbbvY?#A_{n>CZ8-@)EhS~lwn*%Qf zMy_dK*yjHz`u*uH|M(sN^d}Kv@V1=OlS4h}BOE&*$roQI2h)dAsm1uE@T<*_?1yeV zTb>{iY@E7%OX}Ar^*bTwP$bk2`e928N4u3QUg*jHX*^GEpSkdv)?wmOvxYT;X%pXJo&rqr5toRBgjM-Zv zaBQE+>x=&Y^8xDHHErwZ%Rtp&jcN^DN2}8CkAzqV&3V)O<8HYp`pkGQ&If9(g=V~x z^O2p_{S00=zqyxjky`bjW{AK7DA8zaRw`Z~{}9C)I!eP_mrI>l?3A@ntaZ~kGp@;j zVBU$Mx^`IgQ+S#kh!y%tEWTR)I8azaFojKK;!{7Zb$(T8B?P?7%|eFi95zp@y4Eaa zs7_+nXjS>mDGb#y>>#bGE4B?2h2du?(Mfe8-1i)zl&@1=6_1Mj@$HmpRE6C;gwtQp z6Q-|OHn<-3YV{=AG-U*Y)-7M+)#@((IqhZ%R^;8fj9_*o_cpR}Y*(~0#d9b_HQus% z_Ij;f0THR6RmZvU7LT0mS?`5O-P`?(9iu2s`_V(>z(?CaFmZnHvn4+dUe(UJB%`Bp8_ucB$5zVp}_uh)b< zoVu7GlKcFfYB|!$v*V_q5+!bIriib!Es`^+isp7T&TJ`de9?H7QK5V zw1kV_8jeMSG_jgo5p-0eEXhhm*S=pYI-g%yfUZx-m@F4F(W2!|u94cDCmoGFp2~qU z-XXIA`)}snU#0$1R5;+86tsV2J|L%ebZ<33M>W@YAY|fj!PA(9#LlkBdIG&7%I=w1 zQ8*d%@+;j$WXl~iQ}puo+z@@ooWL!xdE|xXyM2wLza1B-NQ}RbV*7BNvz0C;b0)e3 z&CD;!9FN)mYI`1f#hjBh79*qYN<*LJ-^m&s5HCp=p+06v_U{8bopL`%_k3fY@n~R| z+ola@z00Kx?LewmlrQR2wl#BjK~GG8ZDe=kcfE OHjLm#hBa&yV}Aih{%pPg literal 0 HcmV?d00001 diff --git a/ggdt/tests/ref/indexed/triangle_2d_solid_textured_blended.png b/ggdt/tests/ref/indexed/triangle_2d_solid_textured_blended.png new file mode 100644 index 0000000000000000000000000000000000000000..5b42e38ecc496292c0789d2374eae385530c29bd GIT binary patch literal 4034 zcmZWs4OEgz_aA#oOhw$)H!V~wb7|MzRYSv)gd*3_PH|*$H&Lza=A<>kPsq~h{a;jXfzUu zl;J@O7Sy@}AsQ69fIKAN*#V9XNI(FZ1&D?LG7JO)fCm5rfExhv+7g*CucauvHjgXh zv5GPi_{nr0gUX?XCs0CYA%T=YVhGVU0E_W;cf+7vfNu%F3IK)=+2fDVXM;2RDwA;3KV zpz#2O0Z7AvR4O2q0+9#^1RygL(CL6o21Ftd0Kfx)13&@*Q~<&O2nK)%~iCnRGxW12Pd1x2*y+Jm_T=Q9#CIyjI_BnD!!zV7bsE-o%ecp zN(k*Z4-y7p+0|y85=Pl=5Vz-;vsM!SKZ}OFK5nEcgETX$@UWA8X)et4?Gs$(TGwy( za&+&e?o3hD+mHnZWY&Ru@JWZOd zD?C|vbM)>W;LwA9?+A@&7WYq;t*~sgT36MJ&xP%YVcF)V2 zlGgtEH~S-tR^FetY84n=F3-Se0&~`Bdfgwx+7B=f{yB}@I>`;wAA(fZ1#nC6xERGv zXOiP5dgLL+OJGuNR21*J6?ievEhcCaz63$u|Jt)Iy~1cFn1!|~$ubES2)4HER{@orAIq-JT?M zk}V=cBhk%~`Rl}pP#$TyMv{{gf!bLigk6seM?3$iU?<0s!i0-*ygeW%AM+H)ofVU} zs4`mbdJ|8BRt~3O5pO8C)G|@AM&r0E%6tC#jo#agMF-5Cj==9GKKAt3?w4ER=V_kO zvMbHge@t_VN%7VN+|jS}S=5S0vejpsYpF`RTCH+iuXeCmt|HP>q}B_dM78nIPKsVXuiU zC#o9p=Q(e(4(d`DIsI1qw`u>sNSCB&ZQT1=;pmCpKkp-pvjct9v8O~BqnPk$R9j-s z4vP~GO|^kIjZe+K>qY}rD)xQ#J7(1T4ao*Y9R)_4`!5K;nGKwCbz1Y!<(gbh)Q>5K zrRn8~OR4+T#&ygJ`w3^i5150lMz=1nPIM06Mn1sXjx6nbeS}pw!jHrEbXL#cJQ9N7 zgGeaVxa}f)X=2p-e$O}|ykg@VG?n%4<+;D_-)hHImbLNxCOMrVyykLO)k7n4^c)Sg z&H!|JYPJpa95w`b(ATs^#C_Ie_kD94q)&+|WQb&R#h|u|ZDDVZDR7l-E}Vh!56HET z*3=Ok4m$Ojvqw-@#+m!C>nqp{Da+sZNY@yaR|7w*^~UI&hZP(hi?=$HLD;%TQCWAH zSvVim=vmo2ed_?Ua%pqbu37Q-Ih@M-;&`RPB{L$F6zOwHA(*qK3F zM>CsA!WGJq2I{Vp&O2J5JFE`ivn$SdM>FKM5~uZZM%*4|c_cW6&Z<%^`$sp8viUO< zRo+L>H?fa7_nYgu8{mjzKPBL$)#kpsAXWXriQ}I6i>cma*rA~zS11TQvP!nY9D?a@ za3Z1K`HYZK_GRU-q+1Iglq=rP%Oe|P4>o&wcA&^DD)#0=D``GPPhN4pc0IRyU>7Sa zzyMw{m5y;Ry7r^02nWK_NqQ(CJN`YbyDCfE~3A|#k|Rwj9Q=Ku>x^ro=)j~s7vis+<$l~g=~%b zr3rHDmI|k)#;X?c!}TY;$3?b&_7`C5{*Sn4b2 zbCUlvaj#fYzs7JI34XkO>9YyjqUlq<(TbpzN54A9?h|W=Js;xv-&jm=Nx&sc&LBPe?y#iL+9y zykS&4bkV-rEgkW_A#A@3Fh}1EvhMf^#_zb^8~7DB6O`y&lh%2-GBxskotL}_rCF)v zMk}mT^~a!>f8nettbnmo8=>K2PFrUEyH8g;s5)SFO<`W*&eNxE(fx9}eZo!}6LMA3ZleF1P{fg^^r_UUChtiz$Hc!W=4HgpC=m*9hK^Hi8Q4=qG74W0i zC?sdwvKB^~iHA9&(dtL&<1IA!qs$RnX>BeWna$Hkp2|DIT|d2*ne$ZhSfB)b7kPQ} z*sahHzF z65GV7K|O24ol5^#d^23!^-}lci6z?BB@j0$YW1l0(TvF#RF* zUHegHyX)Hs`#Mr639KmHynDNjaAQe)1X{Xq4Ld^(RW8nwpe z9%I&@ugsVcl*{gkv+=LKUmkieZF$_DcQ&El+1!&$)*_ovO)v|~UngH$Ob9tNVGzql zXE@w7UDm9R$Uj|h-iy1K5LRr&3(%{SPH;@qgt!Z*@fzjyi6>_$C!PATW);C#^gkgpI{k