diff --git a/README.md b/README.md index 9e79cd0b0..d33217c95 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ Graphics - Built-in effects such as tinting, masking and 2D lighting (with optional per-pixel normal-map shading on sprites for 3D-looking dynamic lights) - Standard spritesheet, single and multiple Packed Textures support - Compressed texture support (DDS, KTX, KTX2, PVR, PKM) with automatic format detection and fallback -- 3D mesh rendering with OBJ/MTL model loading, perspective projection and hardware depth testing +- 3D mesh rendering with OBJ/MTL model loading, multi-material support, perspective projection and hardware depth testing - Built-in shader effects (Flash, Outline, Glow, Dissolve, CRT, Hologram, etc.) with multi-pass chaining via `postEffects`, plus custom shader support via `ShaderEffect` for per-sprite fragment effects (WebGL) - Trail renderable for fading, tapering ribbons behind moving objects (speed lines, sword slashes, magic trails) - System & Bitmap Text with built-in typewriter effect diff --git a/packages/examples/LICENSE.md b/packages/examples/LICENSE.md index 3729a6a78..e55b48f18 100644 --- a/packages/examples/LICENSE.md +++ b/packages/examples/LICENSE.md @@ -37,3 +37,14 @@ published by Casino RPG on OpenGameArt.org: These assets are released under **CC0 1.0 Universal (Public Domain Dedication)** — no attribution legally required, but provided here as a courtesy to the original creator. + +### `multiMaterialMesh` example + +Spacecraft 3D models (`craft_speederA`, `craft_speederB`, `craft_racer`, +`craft_miner`) in `public/assets/multiMaterialMesh/` are taken from +**"Space Kit (2.0)"** published by Kenney: + + + +Released under **CC0 1.0 Universal (Public Domain Dedication)** — no +attribution legally required, credited here as a courtesy. diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_miner.mtl b/packages/examples/public/assets/multiMaterialMesh/craft_miner.mtl new file mode 100644 index 000000000..08b0a85d9 --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_miner.mtl @@ -0,0 +1,14 @@ +# Created by Kenney (www.kenney.nl) + +newmtl dark +Kd 0.2745098 0.2980392 0.3411765 + +newmtl metalDark +Kd 0.6750623 0.7100219 0.7735849 + +newmtl metal +Kd 0.8431373 0.8705882 0.9098039 + +newmtl metalRed +Kd 1 0.6285242 0.2028302 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_miner.obj b/packages/examples/public/assets/multiMaterialMesh/craft_miner.obj new file mode 100644 index 000000000..86c63017c --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_miner.obj @@ -0,0 +1,997 @@ +# Created by Kenney (www.kenney.nl) + +mtllib craft_miner.mtl + +g craft_miner + +v 0.5 0.2 -1.3 +v 0.5 0.2 -1.1 +v 0.3 0.2 -1.3 +v 0.3 0.2 -1.1 +v 0.3 0.5 -1.3 +v 0.3 0.5 -1.1 +v 0.5 0.5 -1.1 +v 0.5 0.5 -1.3 +v -0.3 0.2 -1.3 +v -0.3 0.2 -1.1 +v -0.5 0.2 -1.3 +v -0.5 0.2 -1.1 +v -0.3 0.5 -1.1 +v -0.3 0.5 -1.3 +v -0.5 0.5 -1.1 +v -0.5 0.5 -1.3 +v -0.2 0.6900496 -0.1004963 +v -0.0999999 0.4784731 0.3125871 +v -0.2 0.2668965 0.3256706 +v 0.1 0.4784731 0.3125871 +v 0.2 0.6900496 -0.1004963 +v 0.2 0.2668965 0.3256706 +v 0.6 0.6 -1.2 +v 0.6 0.5 -0.9000001 +v 0.2 0.6 -1.2 +v 0.2 0.54 -1.02 +v 0.2 0.5 -0.9000001 +v 0.6 0.6 -1.3 +v 0.2 0.6 -1.3 +v 0.6 0.1 -1.3 +v 0.6 0.3 -0.9000001 +v 0.6 0.1 -0.3 +v 0.6 0.3 -0.3 +v 0.2 0.1 -0.3 +v 0.2 0.1 -1.1 +v 0.2 0.1 -1.3 +v 0.2 0.5 -1.1 +v 0.2 0.6 -0.9000001 +v -0.2 0.7 -0.7 +v 0.2 0.7 -0.7 +v -0.2 0.6 -0.9000001 +v -0.2 0.54 -1.02 +v -0.2 0.5 -1.1 +v 0.2 0.6 -0.5 +v 0.2 0.7 -0.3 +v 0.2 0.5 -0.5 +v 0.2 0.5 -0.3 +v -0.2 0.1 -1.1 +v 0.2 0 -0.7 +v -0.2 0 -0.7 +v -0.2 0.7 -0.3 +v 0.2 0 -0.3 +v -0.2 0 -0.3 +v -0.2 0.6 -1.2 +v -0.2 0.1 -1.3 +v -0.2 0.6 -1.3 +v -0.5999999 0.6 -1.2 +v -0.2 0.5 -0.9000001 +v -0.5999999 0.5 -0.9000001 +v -0.5999999 0.6 -1.3 +v -0.5999999 0.3 -0.9000001 +v -0.5999999 0.1 -1.3 +v -0.5999999 0.1 -0.3 +v -0.5999999 0.3 -0.3 +v -0.2 0.1 -0.3 +v -0.2 0.6 -0.5 +v -0.2 0.5 -0.3 +v -0.2 0.5 -0.5 +v 0.6894426 0.07888544 0.1 +v 0.65 0 0.1 +v 0.6894426 0.07888544 0.7 +v 0.65 0 0.7 +v 0.6 0.3 0.1 +v 0.5699999 0.36 0.1 +v 0.6 0.3 0.7 +v 0.5699999 0.36 0.7 +v 0.65 0.4 0.1 +v 0.6894426 0.3211145 0.1 +v 0.65 0.4 0.7 +v 0.6894426 0.3211145 0.7 +v 0.5699999 0.04 0.7 +v 0.6 0.1 0.7 +v 0.5699999 0.04 0.1 +v 0.6 0.1 0.1 +v 0.4 0.1 0.1 +v 0.4 0.1540133 0.1 +v 0.4 0.1 0.7 +v 0.4 0.1540133 0.3 +v 0.4 0.2040136 0.3 +v 0.4 0.3 0.7 +v 0.4 0.2540133 0.3 +v 0.4 0.3 0.1 +v 0.4 0.2540133 0.1 +v 0.4 0.125 1 +v 0.4 0.275 1 +v -0.4000001 0.3 0.1 +v -0.4000001 0.25 0.1 +v -0.4000001 0.3 0.7 +v -0.4000001 0.25 0.3 +v -0.4000001 0.15 0.3 +v -0.4000001 0.1 0.7 +v -0.4000001 0.1 0.1 +v -0.4000001 0.15 0.1 +v -0.4000001 0.275 1 +v -0.4000001 0.125 1 +v -0.4000001 0.2 0.3 +v -0.6500001 0 0.1 +v -0.5699999 0.04 0.1 +v -0.6894426 0.07888544 0.1 +v -0.5999999 0.1 0.1 +v -0.6894426 0.3211145 0.1 +v -0.5999999 0.3 0.1 +v -0.5699999 0.36 0.1 +v -0.6500001 0.4 0.1 +v -0.5699999 0.36 0.7 +v -0.5999999 0.3 0.7 +v -0.6894426 0.3211145 0.7 +v -0.6500001 0.4 0.7 +v -0.6500001 0 0.7 +v -0.6894426 0.07888544 0.7 +v -0.5699999 0.04 0.7 +v -0.5999999 0.1 0.7 +v 0.6 0.6 -0.9000001 +v 0.6 0.6 -0.5 +v 0.6 0.5 -0.5 +v 0.45 0.6 -0.5 +v 0.35 0.6 -0.5 +v 0.35 0.6 -0.4 +v 0.45 0.6 -0.4 +v 0.35 0.5 -0.5 +v 0.45 0.5 -0.5 +v 0.45 0.5 -0.3 +v 0.45 0.55 -0.3 +v 0.35 0.55 -0.3 +v 0.35 0.5 -0.3 +v -0.5999999 0.6 -0.9000001 +v -0.3499999 0.6 -0.5 +v -0.45 0.6 -0.5 +v -0.5999999 0.6 -0.5 +v -0.45 0.6 -0.4 +v -0.3499999 0.6 -0.4 +v -0.5999999 0.5 -0.5 +v 0.6 0.275 1 +v 0.6 0.25 1.3 +v 0.4 0.25 1.3 +v 0.6 0.125 1 +v 0.6 0.15 1.3 +v 0.4 0.15 1.3 +v 0.3 0.1540133 0.1 +v 0.3 0.2540133 0.1 +v 0.3 0.1540133 0.2 +v 0.3 0.2540133 0.2 +v 0.35 0.1540133 0.3 +v 0.35 0.2540133 0.3 +v -0.3499999 0.5 -0.5 +v -0.45 0.5 -0.5 +v -0.45 0.5 -0.3 +v -0.45 0.55 -0.3 +v -0.3499999 0.5 -0.3 +v -0.3499999 0.55 -0.3 +v -0.3 0.25 0.1 +v -0.3 0.15 0.1 +v -0.3 0.25 0.2 +v -0.3 0.15 0.2 +v -0.3499999 0.25 0.3 +v -0.3499999 0.15 0.3 +v -0.4000001 0.25 1.3 +v -0.4000001 0.15 1.3 +v -0.5999999 0.15 1.3 +v -0.5999999 0.25 1.3 +v -0.5999999 0.275 1 +v -0.5999999 0.125 1 +v 0.9 0.3 -0.9000001 +v 0.9 0.5 -0.9000001 +v 0.9 0.5 -0.7 +v 0.9 0.3 -0.7 +v 0.7 0.5 -0.3 +v 0.7 0.3 -0.3 +v 0.6 0.5 -0.3 +v 0.2 0.3 0.1 +v 0.2 0.1 0.1 +v -0.9000001 0.3 -0.9000001 +v -0.9000001 0.5 -0.9000001 +v -0.7 0.3 -0.3 +v -0.9000001 0.3 -0.7 +v -0.9000001 0.5 -0.7 +v -0.7 0.5 -0.3 +v 0.2 0.1834482 0.3256706 +v 0.2 0.05 0.1 +v -0.2 0.1834482 0.3256706 +v -0.2 0.05 0.1 +v -0.5999999 0.5 -0.3 +v -0.2 0.1 0.1 +v -0.2 0.3 0.1 + +vn 0 1 0 +vn 1 0 0 +vn 0 0 -1 +vn 0 -1 0 +vn -1 0 0 +vn -0.8155257 0.410667 0.4077628 +vn 0.8155257 0.410667 0.4077628 +vn 0 0.8900457 0.4558712 +vn 0 0.06171992 0.9980935 +vn 0 0.9486833 0.3162278 +vn 0 0.8944272 -0.4472136 +vn 0 -0.9701425 -0.2425356 +vn 0.8944272 -0.4472136 0 +vn -0.8944272 -0.4472136 0 +vn 0.8944272 0.4472136 0 +vn 0 0 1 +vn -0.4472136 -0.8944272 0 +vn -0.8944272 0.4472136 0 +vn -0.4472136 0.8944272 0 +vn 0.4472136 0.8944272 0 +vn 0.4472136 -0.8944272 0 +vn 0 0.8944272 0.4472136 +vn 0 0.9965457 0.08304549 +vn 0 -0.9965457 0.08304549 +vn -0.8944272 0 0.4472136 +vn 0.8944272 0 0.4472136 +vn 0 0.9987586 0.0498137 +vn 0 -0.8607637 0.5090048 +vn 0 -0.9922779 0.1240347 + +vt -19.68504 -51.1811 +vt -19.68504 -43.30709 +vt -11.81102 -51.1811 +vt -11.81102 -43.30709 +vt 51.1811 19.68504 +vt 51.1811 7.874016 +vt 43.30709 19.68504 +vt 43.30709 7.874016 +vt -11.81102 7.874016 +vt -19.68504 7.874016 +vt -11.81102 19.68504 +vt -19.68504 19.68504 +vt 19.68504 -43.30709 +vt 19.68504 -51.1811 +vt 11.81102 -43.30709 +vt 11.81102 -51.1811 +vt -51.1811 7.874016 +vt -51.1811 19.68504 +vt -43.30709 7.874016 +vt -43.30709 19.68504 +vt 19.68504 7.874016 +vt 11.81102 7.874016 +vt 19.68504 19.68504 +vt 11.81102 19.68504 +vt -7.06021 22.60518 +vt 9.246657 13.46949 +vt 7.946689 4.333806 +vt -9.246657 13.46949 +vt 7.06021 22.60518 +vt -7.946689 4.333806 +vt 7.874016 15.9063 +vt 3.937008 -2.365937 +vt -7.874016 15.9063 +vt -3.937008 -2.365937 +vt 7.874016 9.696349 +vt -7.874016 9.696349 +vt 3.937008 18.04205 +vt -3.937008 18.04205 +vt 23.62205 52.28963 +vt 23.62205 39.83972 +vt 7.874016 52.28963 +vt 7.874016 44.81968 +vt 7.874016 39.83972 +vt -23.62205 -51.1811 +vt -23.62205 -47.24409 +vt -7.874016 -51.1811 +vt -7.874016 -47.24409 +vt 51.1811 23.62205 +vt 51.1811 3.937008 +vt 47.24409 23.62205 +vt 35.43307 11.81102 +vt 11.81102 3.937008 +vt 35.43307 19.68504 +vt 11.81102 11.81102 +vt 23.62205 -11.81102 +vt 23.62205 -51.1811 +vt 7.874016 -11.81102 +vt 7.874016 -43.30709 +vt 7.874016 -51.1811 +vt -51.1811 3.937008 +vt -51.1811 23.62205 +vt -43.30709 3.937008 +vt -47.24409 23.62205 +vt -40.15748 21.25984 +vt -7.874016 -26.41025 +vt -7.874016 -21.1282 +vt -7.874016 -29.93162 +vt 7.874016 -12.32478 +vt -7.874016 -12.32478 +vt 7.874016 -21.1282 +vt 7.874016 -26.41025 +vt 7.874016 -29.93162 +vt 35.43307 23.62205 +vt 40.15748 21.25984 +vt 19.68504 23.62205 +vt 27.55906 27.55906 +vt 11.81102 27.55906 +vt 7.874016 3.937008 +vt -7.874016 3.937008 +vt 7.874016 19.68504 +vt -7.874016 19.68504 +vt -7.874016 26.73621 +vt -7.874016 42.96891 +vt 7.874016 26.73621 +vt 7.874016 42.96891 +vt -7.874016 -27.55906 +vt -7.874016 -11.81102 +vt 7.874016 -27.55906 +vt 43.30709 3.937008 +vt 27.55906 0 +vt 11.81102 0 +vt -7.874016 52.28963 +vt -7.874016 44.81968 +vt -23.62205 52.28963 +vt -7.874016 39.83972 +vt -23.62205 39.83972 +vt 7.874016 -47.24409 +vt 23.62205 -47.24409 +vt -35.43307 11.81102 +vt -11.81102 3.937008 +vt -11.81102 11.81102 +vt -35.43307 19.68504 +vt -27.55906 -1.591616E-13 +vt -11.81102 -1.591616E-13 +vt -7.874016 -43.30709 +vt -23.62205 -11.81102 +vt -35.43307 23.62205 +vt -27.55906 27.55906 +vt -19.68504 23.62205 +vt -11.81102 27.55906 +vt -3.937008 14.91675 +vt -3.937008 11.44444 +vt -27.55906 14.91675 +vt -27.55906 11.44444 +vt 3.937008 -4.494183E-13 +vt 3.937008 2.641025 +vt 27.55906 -4.494183E-13 +vt 27.55906 2.641025 +vt -3.937008 2.641025 +vt -3.937008 -0.831282 +vt -27.55906 2.641025 +vt -27.55906 -0.831282 +vt 25.59055 0 +vt 22.44094 1.574803 +vt 27.14341 3.105726 +vt 23.62205 3.937008 +vt 27.14341 12.64231 +vt 23.62205 11.81102 +vt 22.44094 14.17323 +vt 25.59055 15.74803 +vt 27.55906 -22.88889 +vt 3.937008 -22.88889 +vt 27.55906 -19.36752 +vt 3.937008 -19.36752 +vt -25.59055 0 +vt -27.14341 3.105726 +vt -22.44094 1.574803 +vt -23.62205 3.937008 +vt -27.14341 12.64231 +vt -23.62205 11.81102 +vt -22.44094 14.17323 +vt -25.59055 15.74803 +vt 3.937008 11.44444 +vt 3.937008 14.08547 +vt 27.55906 11.44444 +vt 27.55906 14.08547 +vt 3.937008 29.93162 +vt 27.55906 29.93162 +vt 3.937008 26.41025 +vt 27.55906 26.41025 +vt -3.937008 12.64231 +vt -3.937008 3.105726 +vt -27.55906 12.64231 +vt -27.55906 3.105726 +vt 3.937008 3.937008 +vt 3.937008 6.063517 +vt 27.55906 3.937008 +vt 11.81102 6.063517 +vt 11.81102 8.03203 +vt 27.55906 11.81102 +vt 11.81102 10.00052 +vt 3.937008 11.81102 +vt 3.937008 10.00052 +vt 39.37008 4.92126 +vt 39.37008 10.82677 +vt -3.937008 11.81102 +vt -3.937008 9.84252 +vt -27.55906 11.81102 +vt -11.81102 9.84252 +vt -11.81102 5.905512 +vt -27.55906 3.937008 +vt -3.937008 3.937008 +vt -3.937008 5.905512 +vt -39.37008 10.82677 +vt -39.37008 4.92126 +vt -11.81102 7.874016 +vt -3.937008 -4.476419E-13 +vt -27.55906 -4.476419E-13 +vt 3.937008 -0.831282 +vt 27.55906 -0.831282 +vt -25.59055 -6.694648E-30 +vt 3.937008 14.91675 +vt 27.55906 14.91675 +vt -3.937008 14.08547 +vt -27.55906 14.08547 +vt -3.937008 26.41025 +vt -27.55906 26.41025 +vt -3.937008 29.93162 +vt -27.55906 29.93162 +vt 3.937008 3.105726 +vt 3.937008 12.64231 +vt 27.55906 3.105726 +vt 27.55906 12.64231 +vt -27.55906 -19.36752 +vt -3.937008 -19.36752 +vt -27.55906 -22.88889 +vt -3.937008 -22.88889 +vt -7.874016 23.62205 +vt -23.62205 23.62205 +vt -23.62205 19.68504 +vt -23.62205 -35.43307 +vt -23.62205 -19.68504 +vt -7.874016 -35.43307 +vt -17.71654 -19.68504 +vt -13.77953 -19.68504 +vt -13.77953 -15.74803 +vt -7.874016 -19.68504 +vt -17.71654 -15.74803 +vt 13.77953 19.68504 +vt 13.77953 23.62205 +vt 7.874016 23.62205 +vt 23.62205 19.68504 +vt 17.71654 19.68504 +vt 23.62205 23.62205 +vt 17.71654 23.62205 +vt 15.74803 23.62205 +vt 11.81102 21.65354 +vt 17.71654 24.64957 +vt 17.71654 20.24786 +vt 13.77953 24.64957 +vt 13.77953 20.24786 +vt 17.71654 21.65354 +vt 13.77953 21.65354 +vt -15.74803 23.62205 +vt -11.81102 21.65354 +vt 7.874016 -35.43307 +vt 7.874016 -19.68504 +vt 23.62205 -35.43307 +vt 13.77953 -19.68504 +vt 17.71654 -19.68504 +vt 23.62205 -19.68504 +vt 17.71654 -15.74803 +vt 13.77953 -15.74803 +vt 23.62205 -38.33497 +vt 23.62205 -50.18694 +vt 15.74803 -38.33497 +vt 15.74803 -50.18694 +vt -51.1811 9.84252 +vt -51.1811 5.905512 +vt 23.62205 5.905512 +vt 15.74803 5.905512 +vt 23.62205 9.84252 +vt 15.74803 9.84252 +vt 51.1811 5.905512 +vt 51.1811 9.84252 +vt 23.62205 39.64277 +vt 15.74803 39.64277 +vt 23.62205 51.49474 +vt 15.74803 51.49474 +vt 7.874016 6.063517 +vt 7.874016 10.00052 +vt 15.74803 11.81102 +vt 15.74803 3.937008 +vt 13.77953 11.81102 +vt 12.32478 6.063517 +vt 12.32478 10.00052 +vt 16.72649 6.063517 +vt 16.72649 10.00052 +vt 13.77953 6.063517 +vt 13.77953 10.00052 +vt 15.74803 6.063517 +vt 15.74803 8.03203 +vt 15.74803 10.00052 +vt -15.74803 11.81102 +vt -13.77953 11.81102 +vt -15.74803 3.937008 +vt -13.77953 19.68504 +vt -13.77953 23.62205 +vt -17.71654 19.68504 +vt -17.71654 23.62205 +vt -13.77953 21.65354 +vt -17.71654 21.65354 +vt -13.77953 24.64957 +vt -13.77953 20.24786 +vt -17.71654 24.64957 +vt -17.71654 20.24786 +vt -7.874016 9.84252 +vt -7.874016 5.905512 +vt -12.32478 9.84252 +vt -12.32478 5.905512 +vt -16.72649 9.84252 +vt -16.72649 5.905512 +vt -15.74803 5.905512 +vt -15.74803 7.874016 +vt -13.77953 5.905512 +vt -13.77953 9.84252 +vt -15.74803 9.84252 +vt -23.62205 5.905512 +vt -23.62205 9.84252 +vt -15.74803 -38.33497 +vt -15.74803 -50.18694 +vt -23.62205 -38.33497 +vt -23.62205 -50.18694 +vt -15.74803 51.49474 +vt -15.74803 39.64277 +vt -23.62205 51.49474 +vt -23.62205 39.64277 +vt 27.55906 19.68504 +vt 40.49572 19.68504 +vt 40.49572 11.81102 +vt 22.88889 19.68504 +vt 22.88889 11.81102 +vt 35.43307 -27.55906 +vt 35.43307 -35.43307 +vt 27.55906 -11.81102 +vt -35.43307 -35.43307 +vt -35.43307 -27.55906 +vt -27.55906 -11.81102 +vt -17.71654 -11.81102 +vt -13.77953 -11.81102 +vt 23.62205 19.36752 +vt 23.62205 1.760683 +vt 17.71654 19.36752 +vt 15.74803 1.760683 +vt 13.77953 19.36752 +vt 7.874016 1.760683 +vt 7.874016 19.36752 +vt 23.62205 27.55906 +vt 15.74803 27.55906 +vt -27.55906 19.68504 +vt -40.49572 11.81102 +vt -40.49572 19.68504 +vt -22.88889 11.81102 +vt -22.88889 19.68504 +vt 7.874016 13.16918 +vt 7.874016 5.304939 +vt -7.874016 13.16918 +vt -7.874016 5.304939 +vt 7.874016 14.71266 +vt 7.874016 4.390811 +vt -7.874016 14.71266 +vt -7.874016 4.390811 +vt 3.956547 27.16731 +vt -12.82168 10.50774 +vt -3.937008 1.968504 +vt -12.82168 7.222372 +vt 7.874016 7.222372 +vt -7.874016 7.222372 +vt 7.874016 10.50774 +vt -7.874016 10.50774 +vt 7.874016 4.150769 +vt 7.874016 -11.71982 +vt -7.874016 4.150769 +vt -7.874016 -11.71982 +vt -23.62205 27.55906 +vt -15.74803 27.55906 +vt 23.62205 -26.48301 +vt 15.74803 -26.48301 +vt 23.62205 27.79081 +vt 15.74803 27.79081 +vt 3.937008 1.968504 +vt 12.82168 7.222372 +vt 12.82168 10.50774 +vt -3.956547 27.16731 +vt -7.874016 1.760683 +vt -15.74803 1.760683 +vt -7.874016 19.36752 +vt -13.77953 19.36752 +vt -17.71654 19.36752 +vt -23.62205 1.760683 +vt -23.62205 19.36752 +vt 7.874016 11.81102 +vt 17.71654 -11.81102 +vt 13.77953 -11.81102 +vt -7.874016 11.81102 +vt -15.74803 -26.48301 +vt -23.62205 -26.48301 +vt -15.74803 27.79081 +vt -23.62205 27.79081 + +usemtl dark + +f 3/3/1 2/2/1 1/1/1 +f 2/2/1 3/3/1 4/4/1 +f 6/7/2 3/6/2 5/5/2 +f 3/6/2 6/7/2 4/8/2 +f 6/11/3 2/10/3 4/9/3 +f 2/10/3 6/11/3 7/12/3 +f 6/15/4 8/14/4 7/13/4 +f 8/14/4 6/15/4 5/16/4 +f 2/19/5 8/18/5 1/17/5 +f 8/18/5 2/19/5 7/20/5 +f 11/14/1 10/15/1 9/16/1 +f 10/15/1 11/14/1 12/13/1 +f 15/2/4 14/3/4 13/4/4 +f 14/3/4 15/2/4 16/1/4 +f 15/23/3 10/22/3 12/21/3 +f 10/22/3 15/23/3 13/24/3 +f 10/19/5 14/18/5 9/17/5 +f 14/18/5 10/19/5 13/20/5 +f 15/7/2 11/6/2 16/5/2 +f 11/6/2 15/7/2 12/8/2 +f 19/27/6 18/26/6 17/25/6 +f 22/30/7 21/29/7 20/28/7 +f 17/33/8 20/32/8 21/31/8 +f 20/32/8 17/33/8 18/34/8 +f 20/37/9 19/36/9 22/35/9 +f 19/36/9 20/37/9 18/38/9 + +usemtl metalDark + +f 25/41/10 24/40/10 23/39/10 +f 24/40/10 25/41/10 26/42/10 +f 27/43/10 24/40/10 26/42/10 +f 29/46/1 23/45/1 28/44/1 +f 23/45/1 29/46/1 25/47/1 +f 23/50/2 30/49/2 28/48/2 +f 30/49/2 23/50/2 31/51/2 +f 30/49/2 31/51/2 32/52/2 +f 31/51/2 23/50/2 24/53/2 +f 32/52/2 31/51/2 33/54/2 +f 34/57/4 30/56/4 32/55/4 +f 30/56/4 34/57/4 35/58/4 +f 30/56/4 35/58/4 36/59/4 +f 35/62/5 29/61/5 36/60/5 +f 29/61/5 35/62/5 25/63/5 +f 25/63/5 35/62/5 37/20/5 +f 26/64/5 25/63/5 37/20/5 +f 37/67/11 38/66/11 26/65/11 +f 39/68/11 38/66/11 37/67/11 +f 38/66/11 39/68/11 40/69/11 +f 39/68/11 37/67/11 41/70/11 +f 41/70/11 37/67/11 42/71/11 +f 43/72/11 42/71/11 37/67/11 +f 27/53/2 26/74/2 38/73/2 +f 40/76/2 44/75/2 38/73/2 +f 44/75/2 40/76/2 45/77/2 +f 44/75/2 45/77/2 46/23/2 +f 46/23/2 45/77/2 47/24/2 +f 43/80/3 35/79/3 48/78/3 +f 35/79/3 43/80/3 37/81/3 +f 50/84/12 35/83/12 49/82/12 +f 35/83/12 50/84/12 48/85/12 +f 39/88/1 45/87/1 40/86/1 +f 45/87/1 39/88/1 51/57/1 +f 53/87/4 49/88/4 52/57/4 +f 49/88/4 53/87/4 50/86/4 +f 34/52/2 49/90/2 35/89/2 +f 49/90/2 34/52/2 52/91/2 +f 42/74/2 43/7/2 54/50/2 +f 48/89/2 54/50/2 43/7/2 +f 55/49/2 54/50/2 48/89/2 +f 54/50/2 55/49/2 56/48/2 +f 57/94/10 42/93/10 54/92/10 +f 42/93/10 57/94/10 58/95/10 +f 58/95/10 57/94/10 59/96/10 +f 60/56/1 54/97/1 56/59/1 +f 54/97/1 60/56/1 57/98/1 +f 62/60/5 61/99/5 60/61/5 +f 61/99/5 62/60/5 63/100/5 +f 60/61/5 61/99/5 57/63/5 +f 61/99/5 63/100/5 64/101/5 +f 59/102/5 57/63/5 61/99/5 +f 50/103/5 65/100/5 48/62/5 +f 65/100/5 50/103/5 53/104/5 +f 63/106/4 48/105/4 65/87/4 +f 62/44/4 48/105/4 63/106/4 +f 48/105/4 62/44/4 55/46/4 +f 58/102/5 41/107/5 42/64/5 +f 66/109/5 39/108/5 41/107/5 +f 39/108/5 66/109/5 51/110/5 +f 51/110/5 66/109/5 67/11/5 +f 67/11/5 66/109/5 68/12/5 +f 71/113/13 70/112/13 69/111/13 +f 70/112/13 71/113/13 72/114/13 +f 75/117/14 74/116/14 73/115/14 +f 74/116/14 75/117/14 76/118/14 +f 79/121/15 78/120/15 77/119/15 +f 78/120/15 79/121/15 80/122/15 +f 71/125/16 81/124/16 72/123/16 +f 81/124/16 71/125/16 82/126/16 +f 82/126/16 71/125/16 80/127/16 +f 82/126/16 80/127/16 75/128/16 +f 75/128/16 80/127/16 76/129/16 +f 76/129/16 80/127/16 79/130/16 +f 81/133/17 70/132/17 72/131/17 +f 70/132/17 81/133/17 83/134/17 +f 83/137/3 69/136/3 70/135/3 +f 69/136/3 83/137/3 84/138/3 +f 69/136/3 84/138/3 78/139/3 +f 78/139/3 84/138/3 73/140/3 +f 78/139/3 73/140/3 74/141/3 +f 78/139/3 74/141/3 77/142/3 +f 81/145/18 84/144/18 83/143/18 +f 84/144/18 81/145/18 82/146/18 +f 74/149/19 79/148/19 77/147/19 +f 79/148/19 74/149/19 76/150/19 +f 80/153/2 69/152/2 78/151/2 +f 69/152/2 80/153/2 71/154/2 +f 87/157/5 86/156/5 85/155/5 +f 86/156/5 87/157/5 88/158/5 +f 88/158/5 87/157/5 89/159/5 +f 90/160/5 89/159/5 87/157/5 +f 90/160/5 91/161/5 89/159/5 +f 92/162/5 91/161/5 90/160/5 +f 91/161/5 92/162/5 93/163/5 +f 90/160/5 87/157/5 94/164/5 +f 90/160/5 94/164/5 95/165/5 +f 98/168/2 97/167/2 96/166/2 +f 97/167/2 98/168/2 99/169/2 +f 99/169/2 98/168/2 100/170/2 +f 101/171/2 100/170/2 98/168/2 +f 102/172/2 100/170/2 101/171/2 +f 100/170/2 102/172/2 103/173/2 +f 101/171/2 98/168/2 104/174/2 +f 101/171/2 104/174/2 105/175/2 +f 100/170/2 106/176/2 99/169/2 +f 109/125/3 108/124/3 107/123/3 +f 108/124/3 109/125/3 110/126/3 +f 110/126/3 109/125/3 111/127/3 +f 110/126/3 111/127/3 112/128/3 +f 112/128/3 111/127/3 113/129/3 +f 113/129/3 111/127/3 114/130/3 +f 115/121/13 112/177/13 113/119/13 +f 112/177/13 115/121/13 116/178/13 +f 117/180/18 114/116/18 111/179/18 +f 114/116/18 117/180/18 118/118/18 +f 121/137/16 120/136/16 119/181/16 +f 120/136/16 121/137/16 122/138/16 +f 120/136/16 122/138/16 117/139/16 +f 117/139/16 122/138/16 116/140/16 +f 117/139/16 116/140/16 115/141/16 +f 117/139/16 115/141/16 118/142/16 +f 119/145/14 109/182/14 107/143/14 +f 109/182/14 119/145/14 120/183/14 +f 122/185/15 108/112/15 110/184/15 +f 108/112/15 122/185/15 121/114/15 +f 114/188/20 115/187/20 113/186/20 +f 115/187/20 114/188/20 118/189/20 +f 120/192/5 111/191/5 109/190/5 +f 111/191/5 120/192/5 117/193/5 +f 119/196/21 108/195/21 121/194/21 +f 108/195/21 119/196/21 107/197/21 + +usemtl metal + +f 36/79/3 3/9/3 30/138/3 +f 3/9/3 36/79/3 29/198/3 +f 3/9/3 29/198/3 5/11/3 +f 5/11/3 29/198/3 8/12/3 +f 1/10/3 30/138/3 3/9/3 +f 30/138/3 1/10/3 28/199/3 +f 28/199/3 1/10/3 8/12/3 +f 28/199/3 8/12/3 29/198/3 +f 38/198/3 24/200/3 27/81/3 +f 24/200/3 38/198/3 123/199/3 +f 124/75/2 24/53/2 123/73/2 +f 24/53/2 124/75/2 125/23/2 +f 38/203/1 124/202/1 123/201/1 +f 124/202/1 38/203/1 126/204/1 +f 126/204/1 38/203/1 127/205/1 +f 126/204/1 127/205/1 128/206/1 +f 127/205/1 38/203/1 44/207/1 +f 128/206/1 129/208/1 126/204/1 +f 127/210/16 46/80/16 130/209/16 +f 46/80/16 127/210/16 44/211/16 +f 124/214/16 131/213/16 125/212/16 +f 131/213/16 124/214/16 126/215/16 +f 129/216/2 131/23/2 126/75/2 +f 131/23/2 129/216/2 132/24/2 +f 132/24/2 129/216/2 133/217/2 +f 128/220/22 133/219/22 129/218/22 +f 133/219/22 128/220/22 134/221/22 +f 133/222/16 135/209/16 132/213/16 +f 135/209/16 133/222/16 134/223/16 +f 130/12/5 128/224/5 127/109/5 +f 128/224/5 130/12/5 135/11/5 +f 128/224/5 135/11/5 134/225/5 +f 62/126/3 11/21/3 55/78/3 +f 11/21/3 62/126/3 60/214/3 +f 11/21/3 60/214/3 16/23/3 +f 16/23/3 60/214/3 14/24/3 +f 9/22/3 55/78/3 11/21/3 +f 55/78/3 9/22/3 56/211/3 +f 56/211/3 9/22/3 14/24/3 +f 56/211/3 14/24/3 60/214/3 +f 136/228/1 66/227/1 41/226/1 +f 66/227/1 136/228/1 137/229/1 +f 137/229/1 136/228/1 138/230/1 +f 138/230/1 136/228/1 139/231/1 +f 140/232/1 137/229/1 138/230/1 +f 137/229/1 140/232/1 141/233/1 +f 136/214/3 58/80/3 59/212/3 +f 58/80/3 136/214/3 41/211/3 +f 59/102/5 139/109/5 136/107/5 +f 139/109/5 59/102/5 142/12/5 +f 95/236/23 144/235/23 143/234/23 +f 144/235/23 95/236/23 145/237/23 +f 144/238/2 146/175/2 143/174/2 +f 146/175/2 144/238/2 147/239/2 +f 144/242/16 148/241/16 147/240/16 +f 148/241/16 144/242/16 145/243/16 +f 148/244/5 95/165/5 94/164/5 +f 95/165/5 148/244/5 145/245/5 +f 147/248/24 94/247/24 146/246/24 +f 94/247/24 147/248/24 148/249/24 +f 151/250/5 150/163/5 149/156/5 +f 150/163/5 151/250/5 152/251/5 +f 153/254/4 86/253/4 88/252/4 +f 86/253/4 153/254/4 149/52/4 +f 149/52/4 153/254/4 151/22/4 +f 153/257/25 152/256/25 151/255/25 +f 152/256/25 153/257/25 154/258/25 +f 88/261/16 154/260/16 153/259/16 +f 154/260/16 88/261/16 89/262/16 +f 154/260/16 89/262/16 91/263/16 +f 93/266/1 154/265/1 91/264/1 +f 154/265/1 93/266/1 150/100/1 +f 154/265/1 150/100/1 152/9/1 +f 68/81/16 137/268/16 155/267/16 +f 137/268/16 68/81/16 66/198/16 +f 138/270/16 142/200/16 156/269/16 +f 142/200/16 138/270/16 139/199/16 +f 157/11/5 138/109/5 156/12/5 +f 138/109/5 157/11/5 140/224/5 +f 140/224/5 157/11/5 158/225/5 +f 160/271/16 157/269/16 159/267/16 +f 157/269/16 160/271/16 158/272/16 +f 140/275/22 160/274/22 141/273/22 +f 160/274/22 140/275/22 158/276/22 +f 141/216/2 155/23/2 137/75/2 +f 155/23/2 141/216/2 159/24/2 +f 159/24/2 141/216/2 160/217/2 +f 163/277/2 162/173/2 161/167/2 +f 162/173/2 163/277/2 164/278/2 +f 97/253/1 163/22/1 161/52/1 +f 163/22/1 97/253/1 165/254/1 +f 165/254/1 97/253/1 99/252/1 +f 165/281/26 164/280/26 163/279/26 +f 164/280/26 165/281/26 166/282/26 +f 166/285/16 106/284/16 100/283/16 +f 106/284/16 166/285/16 165/286/16 +f 106/284/16 165/286/16 99/287/16 +f 164/9/4 103/266/4 162/100/4 +f 103/266/4 164/9/4 166/265/4 +f 103/266/4 166/265/4 100/264/4 +f 167/238/2 105/175/2 104/174/2 +f 105/175/2 167/238/2 168/239/2 +f 167/287/16 169/288/16 168/283/16 +f 169/288/16 167/287/16 170/289/16 +f 171/292/23 167/291/23 104/290/23 +f 167/291/23 171/292/23 170/293/23 +f 169/244/5 171/165/5 172/164/5 +f 171/165/5 169/244/5 170/245/5 +f 169/296/24 105/295/24 168/294/24 +f 105/295/24 169/296/24 172/297/24 + +usemtl metalRed + +f 24/200/3 173/99/3 31/140/3 +f 173/99/3 24/200/3 174/102/3 +f 175/298/2 173/51/2 174/53/2 +f 173/51/2 175/298/2 176/160/2 +f 177/301/26 176/300/26 175/299/26 +f 176/300/26 177/301/26 178/302/26 +f 178/305/4 173/304/4 176/303/4 +f 173/304/4 178/305/4 31/228/4 +f 31/228/4 178/305/4 33/55/4 +f 24/201/1 175/307/1 174/306/1 +f 175/307/1 24/201/1 177/308/1 +f 177/308/1 24/201/1 179/106/1 +f 179/106/1 24/201/1 125/202/1 +f 179/106/1 125/202/1 131/204/1 +f 179/106/1 131/204/1 132/309/1 +f 130/205/1 47/87/1 135/310/1 +f 47/87/1 130/205/1 46/207/1 +f 177/298/16 33/128/16 178/160/16 +f 33/128/16 177/298/16 179/212/16 +f 132/313/22 73/312/22 179/311/22 +f 73/312/22 132/313/22 92/314/22 +f 92/314/22 132/313/22 135/315/22 +f 92/314/22 135/315/22 180/316/22 +f 180/316/22 135/315/22 47/317/22 +f 87/319/4 84/126/4 82/318/4 +f 84/126/4 87/319/4 32/55/4 +f 32/55/4 87/319/4 85/253/4 +f 32/55/4 85/253/4 34/57/4 +f 34/57/4 85/253/4 181/78/4 +f 73/166/2 33/54/2 179/24/2 +f 33/54/2 73/166/2 32/52/2 +f 32/52/2 73/166/2 84/172/2 +f 183/53/3 61/128/3 182/51/3 +f 61/128/3 183/53/3 59/212/3 +f 184/308/4 61/201/4 64/106/4 +f 61/201/4 184/308/4 182/306/4 +f 182/306/4 184/308/4 185/307/4 +f 185/168/5 183/102/5 182/99/5 +f 183/102/5 185/168/5 186/320/5 +f 184/323/25 186/322/25 185/321/25 +f 186/322/25 184/323/25 187/324/25 +f 51/327/27 21/326/27 45/325/27 +f 21/326/27 51/327/27 17/328/27 +f 190/331/28 189/330/28 188/329/28 +f 189/330/28 190/331/28 191/332/28 +f 21/333/2 47/24/2 45/77/2 +f 47/24/2 21/333/2 180/166/2 +f 180/166/2 21/333/2 22/334/2 +f 180/166/2 22/334/2 181/172/2 +f 181/172/2 22/334/2 189/335/2 +f 189/335/2 22/334/2 188/336/2 +f 52/91/2 181/172/2 189/335/2 +f 181/172/2 52/91/2 34/52/2 +f 22/339/16 190/338/16 188/337/16 +f 190/338/16 22/339/16 19/340/16 +f 191/343/29 52/342/29 189/341/29 +f 52/342/29 191/343/29 53/344/29 +f 92/266/1 75/345/1 73/138/1 +f 75/345/1 92/266/1 90/346/1 +f 143/174/2 82/171/2 75/168/2 +f 82/171/2 143/174/2 146/175/2 +f 75/347/23 95/236/23 143/234/23 +f 95/236/23 75/347/23 90/348/23 +f 94/247/24 82/349/24 146/246/24 +f 82/349/24 94/247/24 87/350/24 +f 192/200/16 184/168/16 64/140/16 +f 184/168/16 192/200/16 187/320/16 +f 191/351/5 65/100/5 53/104/5 +f 65/100/5 191/351/5 193/155/5 +f 193/155/5 191/351/5 190/352/5 +f 193/155/5 190/352/5 194/162/5 +f 19/353/5 194/162/5 190/352/5 +f 17/354/5 194/162/5 19/353/5 +f 51/110/5 194/162/5 17/354/5 +f 194/162/5 51/110/5 67/11/5 +f 122/345/4 102/266/4 101/346/4 +f 63/106/4 102/266/4 122/345/4 +f 65/87/4 102/266/4 63/106/4 +f 102/266/4 65/87/4 193/79/4 +f 63/106/4 122/345/4 110/138/4 +f 67/357/22 96/356/22 194/355/22 +f 96/356/22 67/357/22 159/358/22 +f 96/356/22 159/358/22 157/359/22 +f 96/356/22 157/359/22 112/360/22 +f 112/360/22 157/359/22 192/361/22 +f 86/261/16 181/78/16 85/253/16 +f 181/78/16 86/261/16 149/158/16 +f 149/158/16 180/362/16 181/78/16 +f 180/362/16 149/158/16 150/161/16 +f 180/362/16 150/161/16 93/263/16 +f 180/362/16 93/263/16 92/252/16 +f 183/304/1 142/231/1 59/228/1 +f 142/231/1 183/304/1 187/305/1 +f 187/305/1 183/304/1 186/303/1 +f 192/55/1 142/231/1 187/305/1 +f 157/363/1 142/231/1 192/55/1 +f 142/231/1 157/363/1 156/230/1 +f 155/229/1 67/57/1 68/227/1 +f 67/57/1 155/229/1 159/364/1 +f 193/79/16 162/170/16 102/266/16 +f 162/170/16 193/79/16 194/365/16 +f 102/266/16 162/170/16 103/283/16 +f 162/170/16 194/365/16 161/169/16 +f 96/264/16 161/169/16 194/365/16 +f 161/169/16 96/264/16 97/287/16 +f 112/126/1 98/319/1 96/253/1 +f 98/319/1 112/126/1 116/318/1 +f 116/367/23 104/290/23 98/366/23 +f 104/290/23 116/367/23 171/292/23 +f 172/297/24 101/368/24 105/295/24 +f 101/368/24 172/297/24 122/369/24 +f 110/155/5 64/101/5 63/100/5 +f 64/101/5 110/155/5 192/11/5 +f 192/11/5 110/155/5 112/162/5 +f 172/164/5 116/160/5 122/157/5 +f 116/160/5 172/164/5 171/165/5 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_racer.mtl b/packages/examples/public/assets/multiMaterialMesh/craft_racer.mtl new file mode 100644 index 000000000..d28d4966b --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_racer.mtl @@ -0,0 +1,14 @@ +# Created by Kenney (www.kenney.nl) + +newmtl metal +Kd 0.8431373 0.8705882 0.9098039 + +newmtl metalDark +Kd 0.6750623 0.7100219 0.7735849 + +newmtl dark +Kd 0.2745098 0.2980392 0.3411765 + +newmtl metalRed +Kd 1 0.6285242 0.2028302 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_racer.obj b/packages/examples/public/assets/multiMaterialMesh/craft_racer.obj new file mode 100644 index 000000000..fc2766148 --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_racer.obj @@ -0,0 +1,762 @@ +# Created by Kenney (www.kenney.nl) + +mtllib craft_racer.mtl + +g craft_racer + +v 0.45 0.1 -0.9128351 +v 0.25 0.15 -0.9128351 +v 0.2 0.1 -0.9128351 +v 0.2 0.4 -0.9128351 +v 0.25 0.35 -0.9128351 +v 0.4 0.35 -0.9128351 +v 0.4 0.15 -0.9128351 +v 0.45 0.4 -0.9128351 +v 0.45 0.5 -0.5128353 +v 0.45 0.5 -0.01283526 +v 0.2 0.5 -0.5128353 +v 0.35 0.5 -0.1128353 +v 0.2 0.5 -0.1128353 +v 0.2 0.5 -0.3128352 +v 0.35 0.5 -0.01283526 +v -0.2 0.1 -0.9128351 +v -0.4000001 0.15 -0.9128351 +v -0.45 0.1 -0.9128351 +v -0.45 0.4 -0.9128351 +v -0.4000001 0.35 -0.9128351 +v -0.25 0.35 -0.9128351 +v -0.25 0.15 -0.9128351 +v -0.2 0.4 -0.9128351 +v 0.35 0.4 -0.1128353 +v 0.2 0.4 -0.1128353 +v 0.45 0.4 -0.5128353 +v 0.45 0.4 0.08716476 +v 0.45 0.45 0.08716476 +v 0.35 0.45 0.08716476 +v 0.35 0.4 0.08716476 +v -0.45 0.4 -0.5128353 +v -0.45 0.5 -0.5128353 +v -0.45 0.4 0.08716476 +v -0.45 0.5 -0.01283526 +v -0.45 0.45 0.08716476 +v -0.2 0.4 -0.1128353 +v -0.3499999 0.4 -0.1128353 +v -0.2 0.5 -0.1128353 +v -0.3499999 0.5 -0.1128353 +v -0.3499999 0.5 -0.01283526 +v -0.3499999 0.4 0.08716476 +v -0.3499999 0.45 0.08716476 +v -0.2 0.5 -0.5128353 +v -0.2 0.5 -0.3128352 +v 0.45 0.4 -0.8128352 +v 0.2 0.4 -0.8128352 +v 0.45 0.1 -0.5128353 +v 0.2 0.1 -0.5128353 +v 0.45 0.2 -0.5128353 +v 0.6 0.2 -0.5128353 +v 0.6 0 -0.5128353 +v 0.6 0.2 -0.2128353 +v 0.6 0 -0.2128353 +v 0.2 0 -0.5128353 +v 0.2 0.7 -0.3128352 +v 0.2 0.7 0.08716476 +v -0.2 0.7 -0.3128352 +v -0.2 0.7 0.08716476 +v 0.2 0.4784731 0.08716476 +v 0.2 0.4 0.2123492 +v -0.2 0 -0.3128352 +v 0.2 0 -0.3128352 +v -0.2 0.1 -0.3128352 +v 0.2 0.1 -0.3128352 +v -0.2 0.4 -0.8128352 +v -0.45 0.4 -0.8128352 +v -0.2 0.1 -0.5128353 +v -0.45 0.1 -0.5128353 +v -0.45 0.2 -0.5128353 +v -0.5999999 0 -0.5128353 +v -0.5999999 0.2 -0.5128353 +v -0.5999999 0 -0.2128353 +v -0.5999999 0.2 -0.2128353 +v -0.2 0 -0.5128353 +v 0.04999995 0 0.5871647 +v 0.04999995 0 0.4871647 +v -0.04999995 0 0.5871647 +v -0.04999995 0 0.4871647 +v 0.2 0 0.4871647 +v -0.2 0 0.4871647 +v -0.2 0.1 0.4871647 +v -0.2 0.2277294 0.4871647 +v -0.2 0.1258101 0.6497518 +v 0.2 0.2277294 0.4871647 +v 0.2 0.1 0.4871647 +v 0.2 0.1258101 0.6497518 +v -0.2 0.4 0.2123492 +v -0.2 0.4784731 0.08716476 +v -0.04999995 0.1 0.4871647 +v 0.04999995 0.1 0.4871647 +v 0.04999995 0.1317492 0.6871647 +v 0.04999995 0.05 0.6871647 +v -0.04999995 0.1317492 0.6871647 +v -0.04999995 0.05 0.6871647 +v 0.4 0.35 -0.8128352 +v 0.25 0.35 -0.8128352 +v 0.4 0.15 -0.8128352 +v 0.25 0.15 -0.8128352 +v 0.2707107 0.1 0.1578754 +v 0.5292892 0.1 0.1578754 +v 0.2707107 0.1 0.4164541 +v 0.2707107 0.3 0.4164541 +v 0.5292892 0.3 0.1578754 +v 0.2707107 0.3 0.1578754 +v -0.25 0.15 -0.8128352 +v -0.4000001 0.15 -0.8128352 +v -0.4000001 0.35 -0.8128352 +v -0.25 0.35 -0.8128352 +v -0.2707107 0.1 0.1578754 +v -0.2707107 0.3 0.1578754 +v -0.2707107 0.1 0.4164541 +v -0.2707107 0.3 0.4164541 +v -0.5292892 0.1 0.1578754 +v -0.5292892 0.3 0.1578754 +v 0.2 0.2668965 1.012835 +v -0.2 0.2668965 1.012835 +v 0.1 0.45 0.9371647 +v -0.0999999 0.45 0.9371647 +v 0.2 0.6900496 0.2866684 +v -0.2 0.6900496 0.2866684 +v -0.2 0.1 -1.012835 +v 0.2 0.1 -1.012835 +v -0.2 0.5 -1.012835 +v -0.0999999 0.5 -1.012835 +v 0.1 0.5 -1.012835 +v -0.0999999 0.75 -1.012835 +v 0.1 0.75 -1.012835 +v 0.2 0.5 -1.012835 +v 0.6 0.4 -0.5128353 +v 0.6 0 0.08716476 +v 0.6 0.4 0.08716476 +v 0.2 0.4 0.4871647 +v -0.0999999 0.6142857 -0.6128354 +v 0.1 0.6142857 -0.6128354 +v 0.1 0.75 -0.9128351 +v -0.0999999 0.75 -0.9128351 +v -0.5999999 0.4 -0.5128353 +v -0.5999999 0 0.08716476 +v -0.5999999 0.4 0.08716476 +v -0.2 0.4 0.4871647 +v 0.2 0.1834482 1.012835 +v -0.2 0.1834482 1.012835 + +vn 0 0 -1 +vn 0 1 0 +vn 0 0 1 +vn 1 0 0 +vn 0 0.8944272 0.4472136 +vn -1 0 0 +vn 0 0.9486833 -0.3162278 +vn 0 -1 0 +vn 0 -0.8944272 0.4472136 +vn 0 0.3819364 0.9241886 +vn 0.7684958 0.5528403 0.322152 +vn 0 0.9381591 0.3462045 +vn -0.7684958 0.5528403 0.322152 +vn 0.7071068 0 0.7071068 +vn 0 0.961524 -0.2747211 +vn 0 0.9111079 0.4121679 +vn -0.7071068 0 0.7071068 +vn 0 0.9987586 0.0498137 +vn 0 -0.9876331 0.1567831 + +vt -17.71654 3.937008 +vt -9.84252 5.905512 +vt -7.874016 3.937008 +vt -7.874016 15.74803 +vt -9.84252 13.77953 +vt -15.74803 13.77953 +vt -15.74803 5.905512 +vt -17.71654 15.74803 +vt -17.71654 -20.19037 +vt -17.71654 -0.5053265 +vt -7.874016 -20.19037 +vt -13.77953 -4.442334 +vt -7.874016 -4.442334 +vt -7.874016 -12.31635 +vt -13.77953 -0.5053265 +vt 7.874016 3.937008 +vt 15.74803 5.905512 +vt 17.71654 3.937008 +vt 17.71654 15.74803 +vt 15.74803 13.77953 +vt 9.84252 13.77953 +vt 9.84252 5.905512 +vt 7.874016 15.74803 +vt 13.77953 15.74803 +vt 13.77953 19.68504 +vt 7.874016 19.68504 +vt 20.19037 19.68504 +vt 20.19037 15.74803 +vt 0.5053265 19.68504 +vt -3.431681 15.74803 +vt -3.431681 17.71654 +vt 17.71654 9.255395 +vt 17.71654 4.853686 +vt 13.77953 9.255395 +vt 13.77953 4.853686 +vt -4.442334 15.74803 +vt -4.442334 19.68504 +vt 3.431681 15.74803 +vt -0.5053265 19.68504 +vt 3.431681 17.71654 +vt 17.71654 17.71654 +vt 13.77953 17.71654 +vt -20.19037 15.74803 +vt -20.19037 19.68504 +vt -13.77953 15.74803 +vt -7.874016 19.68504 +vt -13.77953 19.68504 +vt 4.442334 19.68504 +vt 4.442334 15.74803 +vt -13.77953 17.71654 +vt -17.71654 17.71654 +vt 7.874016 -20.19037 +vt 7.874016 -12.31635 +vt 17.71654 -20.19037 +vt 7.874016 -4.442334 +vt 13.77953 -4.442334 +vt 13.77953 -0.5053265 +vt 17.71654 -0.5053265 +vt -13.77953 9.255395 +vt -13.77953 4.853686 +vt -17.71654 9.255395 +vt -17.71654 4.853686 +vt -17.71654 -25.37922 +vt -17.71654 -12.92931 +vt -7.874016 -25.37922 +vt -7.874016 -12.92931 +vt 17.71654 -35.9384 +vt 7.874016 -35.9384 +vt 35.9384 15.74803 +vt 35.9384 3.937008 +vt 32.00139 15.74803 +vt 20.19037 3.937008 +vt 20.19037 7.874016 +vt -17.71654 -35.9384 +vt -17.71654 -32.00139 +vt -7.874016 -35.9384 +vt -7.874016 -32.00139 +vt 20.19037 0 +vt 8.379342 7.874016 +vt 8.379342 0 +vt -7.874016 0 +vt -23.62205 0 +vt -23.62205 7.874016 +vt -17.71654 7.874016 +vt -7.874016 3.431681 +vt 7.874016 3.431681 +vt 12.31635 27.55906 +vt 12.31635 19.68504 +vt -3.431681 27.55906 +vt -3.431681 18.83752 +vt -8.360205 15.74803 +vt 7.874016 -3.556525E-13 +vt -7.874016 -3.556525E-13 +vt 7.874016 -32.00139 +vt 17.71654 -32.00139 +vt 7.874016 -25.37922 +vt 7.874016 -12.92931 +vt 17.71654 -25.37922 +vt 17.71654 -12.92931 +vt -35.9384 3.937008 +vt -35.9384 15.74803 +vt -20.19037 3.937008 +vt -32.00139 15.74803 +vt -20.19037 7.874016 +vt -20.19037 0 +vt -8.379342 0 +vt -8.379342 7.874016 +vt 7.874016 0 +vt 23.62205 0 +vt 23.62205 7.874016 +vt 17.71654 7.874016 +vt 1.968504 23.11672 +vt 1.968504 19.17971 +vt -1.968504 23.11672 +vt -1.968504 19.17971 +vt 7.874016 19.17971 +vt -7.874016 19.17971 +vt 19.17971 3.937008 +vt 19.17971 8.965723 +vt 25.58078 4.953154 +vt -19.17971 8.965723 +vt -19.17971 3.937008 +vt -25.58078 4.953154 +vt 8.360205 15.74803 +vt 3.431681 18.83752 +vt 3.431681 27.55906 +vt -12.31635 27.55906 +vt -12.31635 19.68504 +vt -1.968504 0 +vt -1.968504 3.937008 +vt 1.968504 0 +vt 1.968504 3.937008 +vt -19.17971 0 +vt -27.05373 5.186978 +vt -23.11672 0 +vt -27.05373 1.968504 +vt 19.17971 0 +vt 23.11672 0 +vt 27.05373 5.186978 +vt 27.05373 1.968504 +vt 1.968504 25.07793 +vt 1.968504 20.67622 +vt -1.968504 25.07793 +vt -1.968504 20.67622 +vt 1.968504 1.968504 +vt -1.968504 1.968504 +vt 1.968504 5.186978 +vt -1.968504 5.186978 +vt 15.74803 -32.00139 +vt 15.74803 -35.9384 +vt 9.84252 -32.00139 +vt 9.84252 -35.9384 +vt -15.74803 -35.9384 +vt -15.74803 -32.00139 +vt -9.84252 -35.9384 +vt -9.84252 -32.00139 +vt -35.9384 5.905512 +vt -35.9384 13.77953 +vt -32.00139 5.905512 +vt -32.00139 13.77953 +vt 35.9384 13.77953 +vt 35.9384 5.905512 +vt 32.00139 13.77953 +vt 32.00139 5.905512 +vt -10.6579 6.215566 +vt -20.83816 6.215566 +vt -10.6579 16.39583 +vt 10.6579 16.39583 +vt 20.83816 6.215566 +vt 10.6579 6.215566 +vt 20.83816 3.937008 +vt 10.6579 3.937008 +vt 20.83816 11.81102 +vt 10.6579 11.81102 +vt -6.215566 11.81102 +vt -6.215566 3.937008 +vt -16.39583 11.81102 +vt -16.39583 3.937008 +vt 6.215566 3.937008 +vt 6.215566 11.81102 +vt 16.39583 3.937008 +vt 16.39583 11.81102 +vt -10.6579 3.937008 +vt -20.83816 3.937008 +vt -10.6579 11.81102 +vt -20.83816 11.81102 +vt 7.874016 -5.518738 +vt -7.874016 -5.518738 +vt 3.937008 2.281402 +vt -3.937008 2.281402 +vt -32.50535 4.869821 +vt -7.364489 16.21137 +vt -33.7308 -3.781218 +vt 7.874016 -1.182768 +vt 3.937008 -28.48101 +vt -7.874016 -1.182768 +vt -3.937008 -28.48101 +vt 7.364489 16.21137 +vt 32.50535 4.869821 +vt 33.7308 -3.781218 +vt -7.874016 -39.8754 +vt 7.874016 -39.8754 +vt 3.937008 19.68504 +vt -3.937008 19.68504 +vt 3.937008 29.52756 +vt -3.937008 29.52756 +vt -23.62205 15.74803 +vt 23.62205 3.431681 +vt 23.62205 -8.379342 +vt 23.62205 -20.19037 +vt 14.27674 -8.073708E-15 +vt 10.33974 11.81102 +vt 14.27674 15.74803 +vt -7.994335 15.74803 +vt -4.057327 11.81102 +vt -4.057327 3.937008 +vt 10.33974 3.937008 +vt -7.994335 -8.073708E-15 +vt -7.994335 8.965723 +vt -7.994335 3.937008 +vt -3.431681 0 +vt 39.8754 3.937008 +vt 39.8754 19.68504 +vt 3.937008 -32.93326 +vt 3.937008 -16.55506 +vt 7.874016 -32.93326 +vt 7.874016 -4.271411 +vt -3.937008 -16.55506 +vt -7.874016 -4.271411 +vt -3.937008 -32.93326 +vt -7.874016 -32.93326 +vt 39.8754 29.52756 +vt 35.9384 29.52756 +vt 24.12737 24.18448 +vt -39.8754 29.52756 +vt -35.9384 29.52756 +vt -39.8754 19.68504 +vt -24.12737 24.18448 +vt 3.937008 44.91407 +vt 3.937008 31.95071 +vt -3.937008 44.91407 +vt -3.937008 31.95071 +vt -3.937008 -39.8754 +vt -3.937008 -35.9384 +vt 3.937008 -39.8754 +vt 3.937008 -35.9384 +vt -20.19037 1.136868E-13 +vt -12.31635 1.136868E-13 +vt -12.31635 3.937008 +vt 20.19037 -1.641642E-27 +vt 12.31635 3.937008 +vt 12.31635 -1.641642E-27 +vt 23.62205 15.74803 +vt -23.62205 3.431681 +vt -23.62205 -20.19037 +vt -23.62205 -8.379342 +vt -14.27674 15.74803 +vt -10.33974 3.937008 +vt -14.27674 1.005672E-14 +vt 7.994335 1.005672E-14 +vt 4.057327 3.937008 +vt 4.057327 11.81102 +vt -10.33974 11.81102 +vt 7.994335 15.74803 +vt 7.994335 3.937008 +vt 7.994335 8.965723 +vt 3.431681 0 +vt -13.77953 3.431681 +vt -7.874016 8.360205 +vt -17.71654 3.431681 +vt -39.8754 3.937008 +vt 7.874016 -2.054602 +vt 7.874016 -9.918843 +vt -7.874016 -2.054602 +vt -7.874016 -9.918843 +vt 7.874016 7.222372 +vt -7.874016 7.222372 +vt 7.874016 10.50774 +vt -7.874016 10.50774 +vt 11.28616 27.16731 +vt 19.17971 15.74803 +vt 39.8754 10.50774 +vt 39.8754 7.222372 +vt 7.874016 40.51461 +vt 7.874016 26.041 +vt -7.874016 40.51461 +vt 1.968504 27.53239 +vt 7.874016 19.55977 +vt -1.968504 27.53239 +vt -1.968504 19.55977 +vt -7.874016 19.55977 +vt -7.874016 26.041 +vt 1.968504 19.55977 +vt -11.28616 27.16731 +vt -19.17971 15.74803 +vt -39.8754 10.50774 +vt -39.8754 7.222372 +vt 17.71654 3.431681 +vt 13.77953 3.431681 +vt 7.874016 8.360205 + +usemtl metal + +f 3/3/1 2/2/1 1/1/1 +f 2/2/1 3/3/1 4/4/1 +f 2/2/1 4/4/1 5/5/1 +f 5/5/1 4/4/1 6/6/1 +f 7/7/1 1/1/1 2/2/1 +f 1/1/1 7/7/1 8/8/1 +f 8/8/1 7/7/1 6/6/1 +f 8/8/1 6/6/1 4/4/1 +f 11/11/2 10/10/2 9/9/2 +f 10/10/2 11/11/2 12/12/2 +f 12/12/2 11/11/2 13/13/2 +f 13/13/2 11/11/2 14/14/2 +f 15/15/2 10/10/2 12/12/2 +f 18/18/1 17/17/1 16/16/1 +f 17/17/1 18/18/1 19/19/1 +f 17/17/1 19/19/1 20/20/1 +f 20/20/1 19/19/1 21/21/1 +f 22/22/1 16/16/1 17/17/1 +f 16/16/1 22/22/1 23/23/1 +f 23/23/1 22/22/1 21/21/1 +f 23/23/1 21/21/1 19/19/1 +f 12/25/3 25/23/3 24/24/3 +f 25/23/3 12/25/3 13/26/3 +f 10/29/4 26/28/4 9/27/4 +f 26/28/4 10/29/4 27/30/4 +f 27/30/4 10/29/4 28/31/4 +f 15/34/5 28/33/5 10/32/5 +f 28/33/5 15/34/5 29/35/5 +f 30/38/6 12/37/6 24/36/6 +f 12/37/6 30/38/6 15/39/6 +f 15/39/6 30/38/6 29/40/6 +f 28/41/3 30/24/3 27/19/3 +f 30/24/3 28/41/3 29/42/3 +f 33/38/6 32/44/6 31/43/6 +f 32/44/6 33/38/6 34/39/6 +f 34/39/6 33/38/6 35/40/6 +f 38/46/3 37/45/3 36/4/3 +f 37/45/3 38/46/3 39/47/3 +f 40/29/4 37/49/4 39/48/4 +f 37/49/4 40/29/4 41/30/4 +f 41/30/4 40/29/4 42/31/4 +f 42/50/3 33/8/3 41/45/3 +f 33/8/3 42/50/3 35/51/3 +f 32/54/2 44/53/2 43/52/2 +f 44/53/2 32/54/2 38/55/2 +f 38/55/2 32/54/2 39/56/2 +f 39/56/2 32/54/2 40/57/2 +f 40/57/2 32/54/2 34/58/2 +f 34/61/5 42/60/5 40/59/5 +f 42/60/5 34/61/5 35/62/5 + +usemtl metalDark + +f 46/65/7 9/64/7 45/63/7 +f 9/64/7 46/65/7 11/66/7 +f 48/52/8 1/67/8 47/54/8 +f 1/67/8 48/52/8 3/68/8 +f 45/71/4 1/70/4 8/69/4 +f 1/70/4 45/71/4 47/72/4 +f 47/72/4 45/71/4 9/27/4 +f 47/72/4 9/27/4 26/28/4 +f 47/72/4 26/28/4 49/73/4 +f 4/76/2 45/75/2 8/74/2 +f 45/75/2 4/76/2 46/77/2 +f 52/79/4 51/78/4 50/73/4 +f 51/78/4 52/79/4 53/80/4 +f 48/3/1 51/82/1 54/81/1 +f 51/82/1 48/3/1 47/1/1 +f 51/82/1 47/1/1 50/83/1 +f 50/83/1 47/1/1 49/84/1 +f 57/53/2 56/85/2 55/14/2 +f 56/85/2 57/53/2 58/86/2 +f 56/89/4 14/88/4 55/87/4 +f 14/88/4 56/89/4 13/48/4 +f 13/48/4 56/89/4 25/49/4 +f 25/49/4 56/89/4 59/90/4 +f 25/49/4 59/90/4 60/91/4 +f 63/16/1 62/93/1 61/92/1 +f 62/93/1 63/16/1 64/3/1 +f 19/67/2 65/94/2 23/68/2 +f 65/94/2 19/67/2 66/95/2 +f 68/9/8 16/76/8 67/11/8 +f 16/76/8 68/9/8 18/74/8 +f 66/98/7 43/97/7 65/96/7 +f 43/97/7 66/98/7 32/99/7 +f 68/102/6 19/101/6 18/100/6 +f 19/101/6 68/102/6 66/103/6 +f 66/103/6 68/102/6 32/44/6 +f 32/44/6 68/102/6 69/104/6 +f 32/44/6 69/104/6 31/43/6 +f 72/106/6 71/104/6 70/105/6 +f 71/104/6 72/106/6 73/107/6 +f 70/109/1 68/18/1 74/108/1 +f 68/18/1 70/109/1 71/110/1 +f 68/18/1 71/110/1 69/111/1 +f 67/16/1 74/108/1 68/18/1 +f 77/114/8 76/113/8 75/112/8 +f 78/115/8 76/113/8 77/114/8 +f 61/14/8 76/113/8 78/115/8 +f 62/53/8 76/113/8 61/14/8 +f 76/113/8 62/53/8 79/116/8 +f 61/14/8 78/115/8 80/117/8 +f 83/120/6 82/119/6 81/118/6 +f 86/123/4 85/122/4 84/121/4 +f 87/124/6 38/37/6 36/36/6 +f 38/37/6 87/124/6 88/125/6 +f 58/126/6 38/37/6 88/125/6 +f 57/127/6 38/37/6 58/126/6 +f 38/37/6 57/127/6 44/128/6 +f 89/130/3 80/81/3 78/129/3 +f 80/81/3 89/130/3 81/3/3 +f 85/16/3 76/131/3 79/108/3 +f 76/131/3 85/16/3 90/132/3 +f 91/134/4 76/133/4 90/122/4 +f 76/133/4 91/134/4 75/135/4 +f 75/135/4 91/134/4 92/136/4 +f 77/138/6 89/118/6 78/137/6 +f 89/118/6 77/138/6 93/139/6 +f 93/139/6 77/138/6 94/140/6 +f 94/143/9 75/142/9 92/141/9 +f 75/142/9 94/143/9 77/144/9 +f 91/147/3 94/146/3 92/145/3 +f 94/146/3 91/147/3 93/148/3 + +usemtl dark + +f 96/151/8 6/150/8 95/149/8 +f 6/150/8 96/151/8 5/152/8 +f 2/155/2 97/154/2 7/153/2 +f 97/154/2 2/155/2 98/156/2 +f 96/5/1 97/7/1 98/2/1 +f 97/7/1 96/5/1 95/6/1 +f 97/159/6 6/158/6 7/157/6 +f 6/158/6 97/159/6 95/160/6 +f 96/163/4 2/162/4 5/161/4 +f 2/162/4 96/163/4 98/164/4 +f 101/167/2 100/166/2 99/165/2 +f 104/170/8 103/169/8 102/168/8 +f 103/173/3 99/172/3 100/171/3 +f 99/172/3 103/173/3 104/174/3 +f 102/177/4 99/176/4 104/175/4 +f 99/176/4 102/177/4 101/178/4 +f 17/150/2 105/151/2 22/152/2 +f 105/151/2 17/150/2 106/149/2 +f 107/163/4 17/162/4 20/161/4 +f 17/162/4 107/163/4 106/164/4 +f 107/20/1 105/22/1 106/17/1 +f 105/22/1 107/20/1 108/21/1 +f 107/154/8 21/155/8 108/156/8 +f 21/155/8 107/154/8 20/153/8 +f 105/159/6 21/158/6 22/157/6 +f 21/158/6 105/159/6 108/160/6 +f 111/181/6 110/180/6 109/179/6 +f 110/180/6 111/181/6 112/182/6 +f 113/169/2 111/168/2 109/170/2 +f 114/166/8 110/165/8 112/167/8 +f 110/185/3 113/184/3 109/183/3 +f 113/184/3 110/185/3 114/186/3 +f 117/189/10 116/188/10 115/187/10 +f 116/188/10 117/189/10 118/190/10 +f 115/193/11 119/192/11 117/191/11 +f 120/196/12 117/195/12 119/194/12 +f 117/195/12 120/196/12 118/197/12 +f 116/200/13 118/199/13 120/198/13 + +usemtl metalRed + +f 63/14/8 48/52/8 64/53/8 +f 48/52/8 63/14/8 3/68/8 +f 3/68/8 63/14/8 67/11/8 +f 16/76/8 3/68/8 67/11/8 +f 121/201/8 3/68/8 16/76/8 +f 3/68/8 121/201/8 122/202/8 +f 123/26/1 122/3/1 121/16/1 +f 122/3/1 123/26/1 124/203/1 +f 125/204/1 122/3/1 124/203/1 +f 126/205/1 125/204/1 124/203/1 +f 125/204/1 126/205/1 127/206/1 +f 122/3/1 125/204/1 128/46/1 +f 26/8/1 50/83/1 49/84/1 +f 50/83/1 26/8/1 129/207/1 +f 79/116/8 53/209/8 130/208/8 +f 53/209/8 79/116/8 51/210/8 +f 51/210/8 79/116/8 54/52/8 +f 54/52/8 79/116/8 62/53/8 +f 131/213/14 103/212/14 130/211/14 +f 103/212/14 131/213/14 132/214/14 +f 103/212/14 132/214/14 102/215/14 +f 102/215/14 132/214/14 101/216/14 +f 100/217/14 130/211/14 103/212/14 +f 130/211/14 100/217/14 79/218/14 +f 79/218/14 100/217/14 101/216/14 +f 79/218/14 101/216/14 132/214/14 +f 79/218/14 132/214/14 84/219/14 +f 79/218/14 84/219/14 85/220/14 +f 131/30/4 50/73/4 129/28/4 +f 50/73/4 131/30/4 52/79/4 +f 52/79/4 131/30/4 53/80/4 +f 53/80/4 131/30/4 130/221/4 +f 128/223/4 4/69/4 122/222/4 +f 4/69/4 128/223/4 55/87/4 +f 4/69/4 55/87/4 46/71/4 +f 46/71/4 55/87/4 11/27/4 +f 11/27/4 55/87/4 14/88/4 +f 3/70/4 122/222/4 4/69/4 +f 123/226/15 133/225/15 124/224/15 +f 57/227/15 133/225/15 123/226/15 +f 57/227/15 134/228/15 133/225/15 +f 55/229/15 134/228/15 57/227/15 +f 55/229/15 125/230/15 134/228/15 +f 125/230/15 55/229/15 128/231/15 +f 135/233/4 125/223/4 127/232/4 +f 125/223/4 135/233/4 134/234/4 +f 124/237/6 136/236/6 126/235/6 +f 136/236/6 124/237/6 133/238/6 +f 136/241/16 134/240/16 135/239/16 +f 134/240/16 136/241/16 133/242/16 +f 126/245/2 135/244/2 127/243/2 +f 135/244/2 126/245/2 136/246/2 +f 62/248/6 48/102/6 54/247/6 +f 48/102/6 62/248/6 64/249/6 +f 63/251/4 74/250/4 67/72/4 +f 74/250/4 63/251/4 61/252/4 +f 137/253/1 69/111/1 71/110/1 +f 69/111/1 137/253/1 31/19/1 +f 138/254/8 61/14/8 80/117/8 +f 61/14/8 138/254/8 74/11/8 +f 74/11/8 138/254/8 70/255/8 +f 70/255/8 138/254/8 72/256/8 +f 138/259/17 113/258/17 139/257/17 +f 113/258/17 138/259/17 80/260/17 +f 113/258/17 80/260/17 111/261/17 +f 111/261/17 80/260/17 112/262/17 +f 114/263/17 139/257/17 113/258/17 +f 139/257/17 114/263/17 140/264/17 +f 140/264/17 114/263/17 112/262/17 +f 140/264/17 112/262/17 80/260/17 +f 140/264/17 80/260/17 81/265/17 +f 140/264/17 81/265/17 82/266/17 +f 138/267/6 73/107/6 72/106/6 +f 139/38/6 73/107/6 138/267/6 +f 137/43/6 73/107/6 139/38/6 +f 73/107/6 137/43/6 71/104/6 +f 25/13/2 30/268/2 24/12/2 +f 30/268/2 25/13/2 60/269/2 +f 132/117/2 30/268/2 60/269/2 +f 132/117/2 27/270/2 30/268/2 +f 131/254/2 27/270/2 132/117/2 +f 131/254/2 26/9/2 27/270/2 +f 26/9/2 131/254/2 129/255/2 +f 16/100/6 123/237/6 121/271/6 +f 123/237/6 16/100/6 23/101/6 +f 123/237/6 23/101/6 57/127/6 +f 57/127/6 23/101/6 65/103/6 +f 57/127/6 65/103/6 43/44/6 +f 57/127/6 43/44/6 44/128/6 +f 58/274/18 119/273/18 56/272/18 +f 119/273/18 58/274/18 120/275/18 +f 115/278/3 142/277/3 141/276/3 +f 142/277/3 115/278/3 116/279/3 +f 87/124/6 58/126/6 88/125/6 +f 58/126/6 87/124/6 120/280/6 +f 120/280/6 87/124/6 140/281/6 +f 120/280/6 140/281/6 116/282/6 +f 116/282/6 140/281/6 82/119/6 +f 116/282/6 82/119/6 83/120/6 +f 116/282/6 83/120/6 142/283/6 +f 142/286/19 86/285/19 141/284/19 +f 86/285/19 142/286/19 91/287/19 +f 86/285/19 91/287/19 85/288/19 +f 91/287/19 142/286/19 93/289/19 +f 93/289/19 142/286/19 89/290/19 +f 89/290/19 142/286/19 81/291/19 +f 81/291/19 142/286/19 83/292/19 +f 90/293/19 85/288/19 91/287/19 +f 119/294/4 59/90/4 56/89/4 +f 59/90/4 119/294/4 60/91/4 +f 60/91/4 119/294/4 132/295/4 +f 132/295/4 119/294/4 115/296/4 +f 132/295/4 115/296/4 84/121/4 +f 84/121/4 115/296/4 86/123/4 +f 86/123/4 115/296/4 141/297/4 +f 137/210/2 33/298/2 31/54/2 +f 139/208/2 33/298/2 137/210/2 +f 139/208/2 41/299/2 33/298/2 +f 140/116/2 41/299/2 139/208/2 +f 140/116/2 36/55/2 41/299/2 +f 36/55/2 140/116/2 87/300/2 +f 41/299/2 36/55/2 37/56/2 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_speederA.mtl b/packages/examples/public/assets/multiMaterialMesh/craft_speederA.mtl new file mode 100644 index 000000000..555969a1f --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_speederA.mtl @@ -0,0 +1,14 @@ +# Created by Kenney (www.kenney.nl) + +newmtl metal +Kd 0.8431373 0.8705882 0.9098039 + +newmtl metalRed +Kd 1 0.6285242 0.2028302 + +newmtl dark +Kd 0.2745098 0.2980392 0.3411765 + +newmtl metalDark +Kd 0.6750623 0.7100219 0.7735849 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_speederA.obj b/packages/examples/public/assets/multiMaterialMesh/craft_speederA.obj new file mode 100644 index 000000000..a4dfe00a5 --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_speederA.obj @@ -0,0 +1,828 @@ +# Created by Kenney (www.kenney.nl) + +mtllib craft_speederA.mtl + +g craft_speederA + +v 1 0.2 -0.6500001 +v 1 0.2 -0.35 +v 0.5 0.2833333 -0.6500001 +v 0.4 0.3 0.04999995 +v 0.4 0.3 -0.6500001 +v 0.4 0.3 -0.15 +v 0.5 8.145571E-10 -1.05 +v 0.3 0.1 -1.05 +v 0.2 8.145571E-10 -1.05 +v 0.2 0.5 -1.05 +v 0.3 0.4 -1.05 +v 0.4 0.4 -1.05 +v 0.4 0.1 -1.05 +v 0.5 0.5 -1.05 +v 0.3 0.3 0.04999995 +v 0.2 0.3 -0.04999995 +v 0.25 0.3 0.25 +v 0.2 0.3 0.25 +v 0.3 0.3 0.15 +v -0.2 8.146143E-10 -1.05 +v -0.4000001 0.1 -1.05 +v -0.5 8.146143E-10 -1.05 +v -0.5 0.5 -1.05 +v -0.4000001 0.4 -1.05 +v -0.3 0.4 -1.05 +v -0.3 0.1 -1.05 +v -0.2 0.5 -1.05 +v -0.2 0.3 0.25 +v -0.3 0.3 0.04999995 +v -0.2 0.3 -0.04999995 +v -0.4000001 0.3 -0.15 +v -0.25 0.3 0.25 +v -0.4000001 0.3 0.04999995 +v -0.3 0.3 0.15 +v -0.4000001 0.3 -0.6500001 +v -0.5 0.2833333 -0.6500001 +v -1 0.2 -0.35 +v -1 0.2 -0.6500001 +v 0.3 0.1 0.2495037 +v 0.3 0.1 0.04999995 +v -0.3 0.1 0.2495037 +v 0.2 0.1 0.04999995 +v -0.2 0.1 0.04999995 +v -0.2 0.1 -0.35 +v -0.3 0.1 0.04999995 +v -0.5 0.1 -0.6500001 +v -0.2 0.1 -0.6500001 +v -0.4000001 0.1 0.04999995 +v -1 0.1 -0.35 +v -1 0.1 -0.6500001 +v 0.2 0.1 -0.6500001 +v 0.4 0.1 0.04999995 +v 0.5 0.1 -0.6500001 +v 1 0.1 -0.6500001 +v 1 0.1 -0.35 +v 0.2 0.1 -0.35 +v -0.2 0.3049752 1.05 +v -0.3 0.2 0.25 +v -0.2 0.2 0.25 +v -0.25 0.2 0.25 +v 0.3 0.2 0.25 +v 0.2 0.3049752 1.05 +v 0.25 0.2 0.25 +v 0.2 0.2 0.25 +v -0.3 0.2 0.15 +v -0.2 0.2099504 1.05 +v 0.2 0.2099504 1.05 +v 0.3 0.2 0.15 +v 0.255 0.4 -0.6500001 +v 0.255 0.4 -0.35 +v 0.2 0.4 -0.6500001 +v 0.2 0.4 -0.04999995 +v 0.2 0.4 -0.35 +v 0.4 0.4 -0.15 +v 0.4 0.4 -0.35 +v 0.36375 0.65 -0.75 +v 0.4 0.4 -0.6500001 +v 0.36375 0.65 -0.5999999 +v 0.29125 0.65 -0.75 +v 0.29125 0.65 -0.5999999 +v -0.04999995 0.7 0.04999995 +v -0.2 0.7 0.04999995 +v -0.04999995 0.7 -0.35 +v -0.2 0.7 -0.35 +v 0.2 0.7 -0.35 +v 0.2 0.7 0.04999995 +v 0.04999995 0.7 -0.35 +v 0.04999995 0.7 0.04999995 +v -0.2 0.4 -0.6500001 +v -0.2 0.4 -0.35 +v -0.2550001 0.4 -0.6500001 +v -0.2 0.4 -0.04999995 +v -0.2550001 0.4 -0.35 +v -0.4000001 0.4 -0.15 +v -0.4000001 0.4 -0.35 +v -0.29125 0.65 -0.5999999 +v -0.36375 0.65 -0.5999999 +v -0.4000001 0.4 -0.6500001 +v -0.36375 0.65 -0.75 +v -0.29125 0.65 -0.75 +v 0.2 0.4 1.05 +v 0.15 0.6856326 0.09789133 +v -0.1500001 0.6856326 0.09789133 +v -0.1500001 0.4143674 1.002109 +v 0.15 0.4143674 1.002109 +v -0.2 0.4 1.05 +v -0.2 0 -0.35 +v 0.2 0 -0.35 +v 4.887581E-06 0 -0.35 +v 0.3 0.4 -0.8499999 +v 0.3 0.1 -0.8499999 +v 0.4 0.1 -0.8499999 +v 0.4 0.4 -0.8499999 +v -0.4000001 0.1 -0.8499999 +v -0.3 0.1 -0.8499999 +v -0.4000001 0.4 -0.8499999 +v -0.3 0.4 -0.8499999 +v 0.04999995 0.7 0.35 +v -0.04999995 0.7 0.35 +v 0.5 0.5 -0.9499998 +v 0.2 0.5 -0.9499998 +v 0.5 0.4 -0.6500001 +v 0.2 0.4666667 -0.8499999 +v 0.5 8.145571E-10 -0.6500001 +v 0.2 8.145568E-10 -0.8499999 +v -0.2 8.145568E-10 -0.8499999 +v -0.2 0.4666667 -0.8499999 +v 0.2 0.6 -0.8499999 +v -0.2 0.6 -0.8499999 +v 1.001358E-05 0.7 -0.35 +v 0.04999995 0.8 -0.35 +v 0.04999995 0.8 -0.04999995 +v 0.04999995 0.75 0.04999995 +v -0.04999995 0.8 -0.35 +v -0.04999995 0.75 0.04999995 +v -0.04999995 0.8 -0.04999995 +v -0.2 0.5 -0.9499998 +v -0.5 8.146143E-10 -0.6500001 +v -0.5 0.5 -0.9499998 +v -0.5 0.4 -0.6500001 +v 0.2 8.145568E-10 -0.6500001 +v -0.2 8.145568E-10 -0.6500001 + +vn 0.164399 0.986394 0 +vn 0 0 -1 +vn 0 1 0 +vn -0.164399 0.986394 0 +vn 0 -1 0 +vn 0 0.9915004 -0.1301037 +vn -1 0 0 +vn 0 0 1 +vn 0 -0.9906985 0.1360752 +vn 1 0 0 +vn -0.9922773 -0.0003233984 0.1240396 +vn 0.8944272 0 0.4472135 +vn 0.9922773 -0.0003233984 0.1240396 +vn -0.8944272 0 0.4472136 +vn 0.5547002 0 0.8320503 +vn 0.9896504 0.1434993 0 +vn -0.9896504 0.1434993 0 +vn 0 -0.3713907 -0.9284768 +vn 0 0.7071068 0.7071068 +vn -0.5547002 0 0.8320503 +vn 0 0.9578263 0.2873479 +vn 0 -0.9701425 0.2425356 +vn 0 0.9159844 0.4012138 +vn 0.6529287 0.7254763 0.2176429 +vn -0.6529287 0.7254763 0.2176429 +vn 0 0.9983801 -0.05689657 +vn 0 0.9486833 0.3162278 +vn 0.4472136 0 0.8944272 +vn 0 0.9805807 -0.1961161 +vn 0 0.8944272 0.4472136 +vn -0.4472136 0 0.8944272 + +vt 25.59055 -37.53992 +vt 13.77953 -37.53992 +vt 25.59055 -17.58336 +vt -1.968504 -13.59204 +vt 25.59055 -13.59204 +vt 5.905512 -13.59204 +vt -19.68504 3.206918E-08 +vt -11.81102 3.937008 +vt -7.874016 3.206918E-08 +vt -7.874016 19.68504 +vt -11.81102 15.74803 +vt -15.74803 15.74803 +vt -15.74803 3.937008 +vt -19.68504 19.68504 +vt -15.74803 1.968504 +vt -11.81102 1.968504 +vt -15.74803 -5.905512 +vt -7.874016 -1.968504 +vt -9.84252 9.84252 +vt -7.874016 9.84252 +vt -11.81102 5.905512 +vt 7.874016 3.207143E-08 +vt 15.74803 3.937008 +vt 19.68504 3.207143E-08 +vt 19.68504 19.68504 +vt 15.74803 15.74803 +vt 11.81102 15.74803 +vt 11.81102 3.937008 +vt 7.874016 19.68504 +vt 7.874016 9.84252 +vt 11.81102 1.968504 +vt 7.874016 -1.968504 +vt 15.74803 -5.905512 +vt 9.84252 9.84252 +vt 15.74803 1.968504 +vt 11.81102 5.905512 +vt -25.59055 -13.59204 +vt -5.905512 -13.59204 +vt -25.59055 -17.58336 +vt 1.968504 -13.59204 +vt -13.77953 -37.53992 +vt -25.59055 -37.53992 +vt 11.81102 9.822981 +vt 11.81102 1.968504 +vt -11.81102 9.822981 +vt 7.874016 1.968504 +vt -7.874016 1.968504 +vt -7.874016 -13.77953 +vt -11.81102 1.968504 +vt -19.68504 -25.59055 +vt -7.874016 -25.59055 +vt -15.74803 1.968504 +vt -39.37008 -13.77953 +vt -39.37008 -25.59055 +vt 7.874016 -25.59055 +vt 15.74803 1.968504 +vt 19.68504 -25.59055 +vt 39.37008 -25.59055 +vt 39.37008 -13.77953 +vt 7.874016 -13.77953 +vt 7.874018 42.54936 +vt 11.81102 10.7833 +vt 7.874016 10.7833 +vt 9.842521 10.7833 +vt -11.81102 10.7833 +vt -7.874018 42.54936 +vt -9.842521 10.7833 +vt -7.874016 10.7833 +vt 1.968504 3.937008 +vt 5.905512 7.874016 +vt 9.822981 3.937008 +vt 9.84252 7.874016 +vt 5.905512 11.81102 +vt 1.968504 11.81102 +vt -7.874016 8.265762 +vt -7.874016 12.0069 +vt 7.874016 8.265762 +vt 7.874016 12.0069 +vt 11.81102 10.26734 +vt -11.81102 10.26734 +vt 7.874016 42.07884 +vt -7.874016 42.07884 +vt -1.968504 11.81102 +vt -5.905512 7.874016 +vt -5.905512 11.81102 +vt -9.822981 3.937008 +vt -1.968504 3.937008 +vt -9.84252 7.874016 +vt 8.282086 3.941192 +vt 8.301474 7.8782 +vt 40.04264 8.269947 +vt 40.04264 12.01108 +vt -11.81102 5.905511 +vt -11.81102 9.84252 +vt -1.668339E-07 7.874016 +vt -4.401709 7.874016 +vt -2.193064E-07 11.81102 +vt -4.401709 11.81102 +vt 7.874016 7.874016 +vt 9.84252 11.81102 +vt 7.874016 11.81102 +vt -8.282086 3.941192 +vt -40.04264 8.269947 +vt -8.301474 7.8782 +vt -40.04264 12.01108 +vt 11.81102 5.905511 +vt 11.81102 9.84252 +vt -9.84252 11.81102 +vt -7.874016 7.874016 +vt -7.874016 11.81102 +vt 6.350136E-08 7.874016 +vt 1.159738E-07 11.81102 +vt 4.401709 7.874016 +vt 4.401709 11.81102 +vt 25.59055 7.874016 +vt 25.59055 3.937008 +vt 13.77953 7.874016 +vt 13.77953 3.937008 +vt 40.40139 3.937008 +vt 12.01122 3.937008 +vt 40.40139 7.874016 +vt 12.01122 11.81102 +vt -39.37008 3.937008 +vt -39.37008 7.874016 +vt -19.68504 3.937008 +vt -19.68504 11.15486 +vt -10.03937 -25.59055 +vt -10.03937 -13.77953 +vt -7.874016 -1.968504 +vt -15.74803 -13.77953 +vt 29.52756 23.27067 +vt 25.59055 13.32521 +vt 23.62205 23.27067 +vt 13.77953 13.32521 +vt 25.59055 15.74803 +vt 25.59055 11.81102 +vt 13.77953 15.74803 +vt 5.905512 15.74803 +vt -14.32087 -29.52756 +vt -14.32087 -23.62205 +vt -11.46654 -29.52756 +vt -11.46654 -23.62205 +vt -29.52756 26.97114 +vt -23.62205 26.97114 +vt -25.59055 17.02569 +vt -13.77953 17.02569 +vt -10.03937 24.12577 +vt -15.74803 24.12577 +vt -11.46654 34.72649 +vt -14.32087 34.72649 +vt 15.74803 20.87914 +vt 10.03937 20.87914 +vt 14.32087 34.79856 +vt 11.46654 34.79856 +vt 1.968504 1.968504 +vt 1.968504 -13.77953 +vt -1.968504 -13.77953 +vt -1.968504 1.968504 +vt 10.03937 -25.59055 +vt 7.874016 -1.968504 +vt 10.03937 -13.77953 +vt 15.74803 -13.77953 +vt -10.03937 20.87914 +vt -15.74803 20.87914 +vt -11.46654 34.79856 +vt -14.32087 34.79856 +vt -25.59055 11.81102 +vt -25.59055 15.74803 +vt -13.77953 15.74803 +vt -5.905512 15.74803 +vt -29.52756 23.27067 +vt -23.62205 23.27067 +vt -25.59055 13.32521 +vt -13.77953 13.32521 +vt 29.52756 26.97114 +vt 25.59055 17.02569 +vt 23.62205 26.97114 +vt 13.77953 17.02569 +vt 11.46654 -29.52756 +vt 11.46654 -23.62205 +vt 14.32087 -29.52756 +vt 14.32087 -23.62205 +vt 15.74803 24.12577 +vt 10.03937 24.12577 +vt 14.32087 34.72649 +vt 11.46654 34.72649 +vt -25.59055 3.937008 +vt -25.59055 7.874016 +vt -13.77953 3.937008 +vt -13.77953 7.874016 +vt -40.40139 3.937008 +vt -40.40139 7.874016 +vt -12.01122 3.937008 +vt -12.01122 11.81102 +vt 39.37008 3.937008 +vt 19.68504 3.937008 +vt 39.37008 7.874016 +vt 19.68504 11.15486 +vt 13.77953 27.55906 +vt -1.968504 27.55906 +vt 1.968504 15.74803 +vt -41.33858 15.74803 +vt 7.874016 -35.07002 +vt 5.905512 4.065047 +vt 7.874016 6.033551 +vt 1.968504 6.033551 +vt -5.905512 4.065047 +vt -1.968504 6.033551 +vt -7.874016 6.033551 +vt -5.905512 -33.10151 +vt 5.905512 -33.10151 +vt -7.874016 -35.07002 +vt -13.77953 27.55906 +vt -1.968504 15.74803 +vt 1.968504 27.55906 +vt 41.33858 15.74803 +vt 15.74803 11.81102 +vt 11.81102 11.81102 +vt -11.81102 11.81102 +vt -15.74803 11.81102 +vt -13.77953 -2.273737E-13 +vt 1.968504 3.937008 +vt 13.77953 2.273737E-13 +vt -1.968504 3.937008 +vt 7.874016 2.864594 +vt 7.874016 -13.3681 +vt -7.874016 2.864594 +vt 0.0001911516 -13.3681 +vt -7.874016 -13.3681 +vt 41.33858 3.937008 +vt 33.46457 15.74803 +vt 33.46457 3.937008 +vt -15.74803 -41.33858 +vt -15.74803 -33.46457 +vt -11.81102 -41.33858 +vt -11.81102 -33.46457 +vt 15.74803 -33.46457 +vt 15.74803 -41.33858 +vt 11.81102 -33.46457 +vt 11.81102 -41.33858 +vt -41.33858 3.937008 +vt -33.46457 3.937008 +vt -33.46457 15.74803 +vt 5.905512 -29.59315 +vt -5.905512 -29.59315 +vt 1.968504 -1.564759 +vt -1.968504 -1.564759 +vt -1.788728 13.62952 +vt -35.56101 -1.887771 +vt -12.44991 14.45138 +vt 1.788728 13.62952 +vt 12.44991 14.45138 +vt 35.56101 -1.887771 +vt -5.905512 5.383578 +vt -1.968504 15.32522 +vt 5.905512 5.383578 +vt 1.968504 15.32522 +vt -19.68504 -41.33858 +vt -19.68504 -37.40157 +vt -7.874016 -41.33858 +vt -7.874016 -37.40157 +vt 19.68504 41.70721 +vt 19.68504 29.25729 +vt 7.874016 41.70721 +vt 15.74803 29.25729 +vt 10.03937 29.25729 +vt 7.874016 29.25729 +vt 7.874016 37.55724 +vt 41.33858 3.206918E-08 +vt 25.59055 3.206918E-08 +vt 41.33858 19.68504 +vt 37.40157 19.68504 +vt 25.59055 11.15486 +vt 19.68504 15.74803 +vt 16.72649 11.81102 +vt 7.923076 11.81102 +vt 16.72649 15.74803 +vt 7.923076 15.74803 +vt -41.33858 3.206917E-08 +vt -41.33858 19.68504 +vt -33.46457 3.206916E-08 +vt -37.40157 19.68504 +vt -33.46457 18.3727 +vt 7.874016 3.206916E-08 +vt -7.874016 3.206916E-08 +vt 7.874016 18.3727 +vt -7.874016 18.3727 +vt -7.874016 23.62205 +vt 7.874016 23.62205 +vt -7.874016 -28.18204 +vt -7.874016 -8.107163 +vt 7.874016 -28.18204 +vt -1.968504 -8.107163 +vt -0.0003949364 -8.107163 +vt 1.968504 -8.107163 +vt 7.874016 -8.107163 +vt 33.46457 23.62205 +vt 33.46457 18.3727 +vt 13.77953 31.49606 +vt 1.968504 31.49606 +vt -1.968504 29.52756 +vt -1.968504 31.49606 +vt -0.0003951417 27.55906 +vt 1.968504 31.49606 +vt 1.968504 11.44444 +vt -1.968504 11.44444 +vt 1.968504 15.84615 +vt -1.968504 15.84615 +vt 1.968504 29.52756 +vt -1.968504 -1.968504 +vt 1.968504 -1.968504 +vt -13.77953 31.49606 +vt -1.968504 31.49606 +vt 41.33858 3.207137E-08 +vt 33.46457 3.206911E-08 +vt -41.33858 3.207143E-08 +vt -25.59055 3.207143E-08 +vt -25.59055 11.15486 +vt -7.874016 41.70721 +vt -7.874016 37.55724 +vt -19.68504 41.70721 +vt -7.874016 29.25729 +vt -10.03937 29.25729 +vt -15.74803 29.25729 +vt -19.68504 29.25729 +vt 7.874016 -41.33858 +vt 7.874016 -37.40157 +vt 19.68504 -41.33858 +vt 19.68504 -37.40157 +vt -33.46457 23.62205 +vt -7.923076 11.81102 +vt -16.72649 11.81102 +vt -7.923076 15.74803 +vt -16.72649 15.74803 +vt -19.68504 15.74803 +vt 7.874016 15.74803 +vt -7.874016 15.74803 +vt 1.968504 11.81102 +vt -41.33858 12.0069 +vt -9.84252 7.874015 +vt 9.84252 7.874015 +vt 41.33858 12.0069 +vt -1.968504 11.81102 +vt 0.0001911516 -13.77953 +vt -7.874016 -33.46457 +vt 7.874016 -33.46457 +vt -25.59055 3.206894E-08 +vt -7.874016 3.206826E-08 +vt -19.68504 3.207053E-08 +vt -7.874016 3.937008 +vt 7.874016 3.206826E-08 +vt 7.874016 3.937008 +vt 19.68504 3.206828E-08 +vt 25.59055 3.206939E-08 + +usemtl metal + +f 3/3/1 2/2/1 1/1/1 +f 2/2/1 3/3/1 4/4/1 +f 4/4/1 3/3/1 5/5/1 +f 4/4/1 5/5/1 6/6/1 +f 9/9/2 8/8/2 7/7/2 +f 8/8/2 9/9/2 10/10/2 +f 8/8/2 10/10/2 11/11/2 +f 11/11/2 10/10/2 12/12/2 +f 13/13/2 7/7/2 8/8/2 +f 7/7/2 13/13/2 14/14/2 +f 14/14/2 13/13/2 12/12/2 +f 14/14/2 12/12/2 10/10/2 +f 6/17/3 15/16/3 4/15/3 +f 15/16/3 6/17/3 16/18/3 +f 15/16/3 16/18/3 17/19/3 +f 17/19/3 16/18/3 18/20/3 +f 17/19/3 19/21/3 15/16/3 +f 22/24/2 21/23/2 20/22/2 +f 21/23/2 22/24/2 23/25/2 +f 21/23/2 23/25/2 24/26/2 +f 24/26/2 23/25/2 25/27/2 +f 26/28/2 20/22/2 21/23/2 +f 20/22/2 26/28/2 27/29/2 +f 27/29/2 26/28/2 25/27/2 +f 27/29/2 25/27/2 23/25/2 +f 30/32/3 29/31/3 28/30/3 +f 29/31/3 30/32/3 31/33/3 +f 28/30/3 29/31/3 32/34/3 +f 29/31/3 31/33/3 33/35/3 +f 34/36/3 32/34/3 29/31/3 +f 36/39/4 31/38/4 35/37/4 +f 31/38/4 36/39/4 33/40/4 +f 33/40/4 36/39/4 37/41/4 +f 37/41/4 36/39/4 38/42/4 +f 41/45/5 40/44/5 39/43/5 +f 40/44/5 41/45/5 42/46/5 +f 42/46/5 41/45/5 43/47/5 +f 43/47/5 41/45/5 44/48/5 +f 44/48/5 41/45/5 45/49/5 +f 46/50/5 44/48/5 45/49/5 +f 44/48/5 46/50/5 47/51/5 +f 46/50/5 45/49/5 48/52/5 +f 46/50/5 48/52/5 49/53/5 +f 46/50/5 49/53/5 50/54/5 +f 51/55/5 40/44/5 42/46/5 +f 51/55/5 52/56/5 40/44/5 +f 53/57/5 52/56/5 51/55/5 +f 54/58/5 52/56/5 53/57/5 +f 52/56/5 54/58/5 55/59/5 +f 51/55/5 42/46/5 56/60/5 +f 59/63/6 58/62/6 57/61/6 +f 58/62/6 59/63/6 60/64/6 +f 63/67/6 62/66/6 61/65/6 +f 62/66/6 63/67/6 64/68/6 +f 41/71/7 65/70/7 45/69/7 +f 65/70/7 41/71/7 58/72/7 +f 34/73/7 45/69/7 65/70/7 +f 45/69/7 34/73/7 29/74/7 +f 67/77/8 57/76/8 66/75/8 +f 57/76/8 67/77/8 62/78/8 +f 67/81/9 41/80/9 39/79/9 +f 41/80/9 67/81/9 66/82/9 +f 19/85/10 68/84/10 15/83/10 +f 39/86/10 15/83/10 68/84/10 +f 15/83/10 39/86/10 40/87/10 +f 39/86/10 68/84/10 61/88/10 +f 66/91/11 58/90/11 41/89/11 +f 58/90/11 66/91/11 57/92/11 +f 63/19/3 61/94/3 68/93/3 +f 19/97/12 63/96/12 68/95/12 +f 63/96/12 19/97/12 17/98/12 +f 17/100/8 64/99/8 63/72/8 +f 64/99/8 17/100/8 18/101/8 +f 61/104/13 67/103/13 39/102/13 +f 67/103/13 61/104/13 62/105/13 +f 58/107/3 60/34/3 65/106/3 +f 59/109/8 32/108/8 60/88/8 +f 32/108/8 59/109/8 28/110/8 +f 60/113/14 34/112/14 65/111/14 +f 34/112/14 60/113/14 32/114/14 + +usemtl metalRed + +f 2/117/10 54/116/10 1/115/10 +f 54/116/10 2/117/10 55/118/10 +f 2/121/15 52/120/15 55/119/15 +f 52/120/15 2/121/15 4/122/15 +f 53/125/2 1/124/2 54/123/2 +f 1/124/2 53/125/2 3/126/2 +f 71/51/3 70/128/3 69/127/3 +f 70/128/3 71/51/3 72/129/3 +f 72/129/3 71/51/3 73/48/3 +f 74/17/3 70/128/3 72/129/3 +f 70/128/3 74/17/3 75/130/3 +f 78/133/16 77/132/16 76/131/16 +f 77/132/16 78/133/16 75/134/16 +f 75/137/10 5/136/10 77/135/10 +f 5/136/10 75/137/10 6/73/10 +f 6/73/10 75/137/10 74/138/10 +f 79/141/3 78/140/3 76/139/3 +f 78/140/3 79/141/3 80/142/3 +f 69/145/17 80/144/17 79/143/17 +f 80/144/17 69/145/17 70/146/17 +f 79/149/18 77/148/18 69/147/18 +f 77/148/18 79/149/18 76/150/18 +f 78/153/19 70/152/19 75/151/19 +f 70/152/19 78/153/19 80/154/19 +f 83/156/3 82/46/3 81/155/3 +f 82/46/3 83/156/3 84/60/3 +f 87/157/3 86/47/3 85/48/3 +f 86/47/3 87/157/3 88/158/3 +f 91/159/3 90/60/3 89/55/3 +f 90/60/3 91/159/3 92/160/3 +f 92/160/3 91/159/3 93/161/3 +f 92/160/3 93/161/3 94/33/3 +f 94/33/3 93/161/3 95/162/3 +f 96/165/19 95/164/19 93/163/19 +f 95/164/19 96/165/19 97/166/19 +f 31/85/7 98/168/7 35/167/7 +f 98/168/7 31/85/7 95/169/7 +f 95/169/7 31/85/7 94/170/7 +f 98/173/17 97/172/17 99/171/17 +f 97/172/17 98/173/17 95/174/17 +f 96/177/16 91/176/16 100/175/16 +f 91/176/16 96/177/16 93/178/16 +f 99/181/3 96/180/3 100/179/3 +f 96/180/3 99/181/3 97/182/3 +f 99/185/18 91/184/18 98/183/18 +f 91/184/18 99/185/18 100/186/18 +f 49/189/7 38/188/7 50/187/7 +f 38/188/7 49/189/7 37/190/7 +f 48/193/20 37/192/20 49/191/20 +f 37/192/20 48/193/20 33/194/20 +f 38/197/2 46/196/2 50/195/2 +f 46/196/2 38/197/2 36/198/2 +f 86/200/10 73/137/10 85/199/10 +f 73/137/10 86/200/10 72/201/10 +f 72/201/10 86/200/10 101/202/10 +f 86/205/21 102/204/21 101/203/21 +f 102/204/21 86/205/21 88/206/21 +f 102/204/21 88/206/21 103/207/21 +f 103/207/21 88/206/21 81/208/21 +f 103/207/21 81/208/21 82/209/21 +f 103/207/21 82/209/21 104/210/21 +f 105/211/21 101/203/21 102/204/21 +f 101/203/21 105/211/21 106/212/21 +f 106/212/21 105/211/21 104/210/21 +f 106/212/21 104/210/21 82/209/21 +f 92/214/7 84/213/7 90/169/7 +f 84/213/7 92/214/7 82/215/7 +f 82/215/7 92/214/7 106/216/7 +f 4/217/8 40/28/8 52/23/8 +f 40/28/8 4/217/8 15/218/8 +f 29/219/8 48/13/8 45/8/8 +f 48/13/8 29/219/8 33/220/8 +f 43/222/7 44/189/7 107/221/7 +f 42/224/10 108/223/10 56/118/10 +f 43/227/22 108/226/22 42/225/22 +f 108/226/22 43/227/22 109/228/22 +f 109/228/22 43/227/22 107/229/22 + +usemtl dark + +f 110/231/10 8/230/10 11/216/10 +f 8/230/10 110/231/10 111/232/10 +f 8/235/3 112/234/3 13/233/3 +f 112/234/3 8/235/3 111/236/3 +f 110/11/2 112/13/2 111/8/2 +f 112/13/2 110/11/2 113/12/2 +f 110/239/5 12/238/5 113/237/5 +f 12/238/5 110/239/5 11/240/5 +f 112/242/7 12/202/7 13/241/7 +f 12/202/7 112/242/7 113/243/7 +f 116/26/2 115/28/2 114/23/2 +f 115/28/2 116/26/2 117/27/2 +f 116/234/5 25/235/5 117/236/5 +f 25/235/5 116/234/5 24/233/5 +f 116/231/10 21/230/10 24/216/10 +f 21/230/10 116/231/10 114/232/10 +f 115/242/7 25/202/7 26/241/7 +f 25/202/7 115/242/7 117/243/7 +f 21/238/3 115/239/3 26/240/3 +f 115/239/3 21/238/3 114/237/3 +f 118/246/23 104/245/23 105/244/23 +f 104/245/23 118/246/23 119/247/23 +f 118/250/24 105/249/24 102/248/24 +f 104/253/25 119/252/25 103/251/25 +f 103/256/26 118/255/26 102/254/26 +f 118/255/26 103/256/26 119/257/26 + +usemtl metalDark + +f 10/260/3 120/259/3 14/258/3 +f 120/259/3 10/260/3 121/261/3 +f 121/264/27 122/263/27 120/262/27 +f 122/263/27 121/264/27 77/265/27 +f 77/265/27 121/264/27 69/266/27 +f 69/266/27 121/264/27 71/267/27 +f 71/267/27 121/264/27 123/268/27 +f 14/271/10 124/270/10 7/269/10 +f 124/270/10 14/271/10 120/272/10 +f 124/270/10 120/272/10 122/135/10 +f 124/270/10 122/135/10 53/116/10 +f 53/116/10 122/135/10 3/273/10 +f 122/274/8 5/217/8 3/198/8 +f 5/217/8 122/274/8 77/26/8 +f 74/277/28 16/276/28 6/275/28 +f 16/276/28 74/277/28 72/278/28 +f 125/281/7 10/280/7 9/279/7 +f 10/280/7 125/281/7 121/282/7 +f 121/282/7 125/281/7 123/283/7 +f 127/286/2 125/285/2 126/284/2 +f 125/285/2 127/286/2 123/287/2 +f 123/287/2 127/286/2 128/288/2 +f 128/288/2 127/286/2 129/289/2 +f 129/292/29 85/291/29 128/290/29 +f 85/291/29 129/292/29 87/293/29 +f 87/293/29 129/292/29 130/294/29 +f 130/294/29 129/292/29 83/295/29 +f 83/295/29 129/292/29 84/296/29 +f 85/199/10 123/298/10 128/297/10 +f 123/298/10 85/199/10 71/135/10 +f 71/135/10 85/199/10 73/137/10 +f 131/299/10 88/200/10 87/199/10 +f 88/200/10 131/299/10 132/300/10 +f 88/200/10 132/300/10 133/301/10 +f 130/303/2 131/302/2 87/200/2 +f 131/302/2 130/303/2 83/215/2 +f 131/302/2 83/215/2 134/304/2 +f 132/307/30 135/306/30 133/305/30 +f 135/306/30 132/307/30 136/308/30 +f 133/309/8 81/200/8 88/215/8 +f 81/200/8 133/309/8 135/301/8 +f 131/157/3 136/311/3 132/310/3 +f 136/311/3 131/157/3 134/156/3 +f 81/215/7 134/312/7 83/213/7 +f 134/312/7 81/215/7 136/313/7 +f 136/313/7 81/215/7 135/309/7 +f 27/271/10 126/315/10 20/314/10 +f 126/315/10 27/271/10 137/272/10 +f 126/315/10 137/272/10 127/298/10 +f 138/317/7 23/280/7 22/316/7 +f 23/280/7 138/317/7 139/282/7 +f 139/282/7 138/317/7 140/168/7 +f 140/168/7 138/317/7 46/187/7 +f 140/168/7 46/187/7 36/318/7 +f 139/321/27 127/320/27 137/319/27 +f 127/320/27 139/321/27 89/322/27 +f 89/322/27 139/321/27 91/323/27 +f 91/323/27 139/321/27 98/324/27 +f 98/324/27 139/321/27 140/325/27 +f 23/328/3 137/327/3 27/326/3 +f 137/327/3 23/328/3 139/329/3 +f 89/168/7 129/330/7 127/283/7 +f 129/330/7 89/168/7 84/213/7 +f 84/213/7 89/168/7 90/169/7 +f 92/333/31 31/332/31 30/331/31 +f 31/332/31 92/333/31 94/334/31 +f 35/220/8 140/335/8 36/126/8 +f 140/335/8 35/220/8 98/12/8 +f 101/336/8 57/76/8 62/78/8 +f 57/76/8 101/336/8 106/337/8 +f 101/202/10 16/338/10 72/201/10 +f 16/338/10 101/202/10 18/108/10 +f 62/339/10 18/108/10 101/202/10 +f 18/108/10 62/339/10 64/340/10 +f 57/342/7 28/100/7 59/341/7 +f 106/216/7 28/100/7 57/342/7 +f 106/216/7 30/343/7 28/100/7 +f 30/343/7 106/216/7 92/214/7 +f 109/344/5 141/55/5 108/60/5 +f 126/345/5 141/55/5 109/344/5 +f 125/346/5 141/55/5 126/345/5 +f 9/326/5 141/55/5 125/346/5 +f 7/328/5 141/55/5 9/326/5 +f 141/55/5 7/328/5 124/57/5 +f 126/345/5 109/344/5 142/51/5 +f 142/51/5 109/344/5 107/48/5 +f 126/345/5 142/51/5 138/50/5 +f 22/258/5 126/345/5 138/50/5 +f 126/345/5 22/258/5 20/260/5 +f 107/221/7 47/187/7 142/347/7 +f 47/187/7 107/221/7 44/189/7 +f 47/350/8 138/349/8 142/348/8 +f 138/349/8 47/350/8 46/125/8 +f 124/353/8 51/352/8 141/351/8 +f 51/352/8 124/353/8 53/196/8 +f 51/116/10 108/223/10 141/354/10 +f 108/223/10 51/116/10 56/118/10 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_speederB.mtl b/packages/examples/public/assets/multiMaterialMesh/craft_speederB.mtl new file mode 100644 index 000000000..525d843af --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_speederB.mtl @@ -0,0 +1,14 @@ +# Created by Kenney (www.kenney.nl) + +newmtl metal +Kd 0.8431373 0.8705882 0.9098039 + +newmtl metalRed +Kd 1 0.6285242 0.2028302 + +newmtl metalDark +Kd 0.6750623 0.7100219 0.7735849 + +newmtl dark +Kd 0.2745098 0.2980392 0.3411765 + diff --git a/packages/examples/public/assets/multiMaterialMesh/craft_speederB.obj b/packages/examples/public/assets/multiMaterialMesh/craft_speederB.obj new file mode 100644 index 000000000..ac19ebbb0 --- /dev/null +++ b/packages/examples/public/assets/multiMaterialMesh/craft_speederB.obj @@ -0,0 +1,728 @@ +# Created by Kenney (www.kenney.nl) + +mtllib craft_speederB.mtl + +g craft_speederB + +v 0.3 5.002621E-14 0.6871647 +v 0.2 5.827581E-14 0.4871647 +v 0.2 5.716636E-14 0.6871647 +v 0.2 5.716636E-14 -0.6128354 +v 0.6 5.752731E-14 -0.6128354 +v 1 0 -0.6128354 +v 1 0 -0.01283526 +v 0.2 6.159775E-14 0.08716476 +v 0.6 5.752731E-14 -1.012835 +v 0.3 0.1 -1.012835 +v 0.2 5.752731E-14 -1.012835 +v 0.2 0.5 -1.012835 +v 0.3 0.4 -1.012835 +v 0.5 0.4 -1.012835 +v 0.5 0.1 -1.012835 +v 0.6 0.5 -1.012835 +v 1 0.1 -0.01283526 +v 0.8 0.1 -0.3628353 +v 1 0.1 -0.6128354 +v 0.6 0.1 -0.6128354 +v 0.6 0.1 -0.3628353 +v 0.8 0.1 -0.2628353 +v 0.3 0.1 0.6871647 +v 0.6 0.1 -0.01283526 +v 0.6 0.1 -0.2628353 +v 0.2 0.1 0.3871647 +v 0.2 0.1 0.6871647 +v 0.8 0.15 -0.3628353 +v 0.8 0.15 -0.2628353 +v 0.7 0.2 -0.3628353 +v 0.7 0.2 -0.2628353 +v 0.6 0.2 -0.3628353 +v 0.6 0.2 -0.2628353 +v 0.45 0.5 -0.3128352 +v 0.45 0.5 0.08716476 +v 0.2 0.5 -0.3128352 +v 0.2 0.5 0.08716476 +v 0.2 0.4 -0.3128352 +v 0.45 0.4 -0.3128352 +v 0.45 0.4 0.08716476 +v 0.2 0.4 0.08716476 +v -0.2 5.752731E-14 -1.012835 +v -0.5 0.1 -1.012835 +v -0.5999999 5.752731E-14 -1.012835 +v -0.5999999 0.5 -1.012835 +v -0.5 0.4 -1.012835 +v -0.3 0.4 -1.012835 +v -0.3 0.1 -1.012835 +v -0.2 0.5 -1.012835 +v -0.2 5.716636E-14 0.6871647 +v -0.2 5.690445E-14 0.4871647 +v -0.3 5.002621E-14 0.6871647 +v -0.2 5.716636E-14 0.08716476 +v -0.2 5.752731E-14 -0.6128354 +v -0.5999999 5.752731E-14 -0.6128354 +v -1 0 -0.01283526 +v -1 0 -0.6128354 +v -0.2 5.873156E-14 -0.01283526 +v -0.2 0.4 0.08716476 +v -0.45 0.4 0.08716476 +v -0.2 0.5 0.08716476 +v -0.45 0.5 0.08716476 +v -0.45 0.4 -0.3128352 +v -0.2 0.4 -0.3128352 +v -0.45 0.5 -0.3128352 +v -0.2 0.5 -0.3128352 +v -0.5999999 0.1 -0.2128353 +v -0.5999999 0.1 -0.01283526 +v -0.8 0.1 -0.2128353 +v -1 0.1 -0.01283526 +v -0.3 0.1 0.6871647 +v -0.2 0.1 0.6871647 +v -0.2 0.1 0.3871647 +v -0.8 0.1 -0.3128352 +v -1 0.1 -0.6128354 +v -0.5999999 0.1 -0.3128352 +v -0.5999999 0.1 -0.6128354 +v -0.7 0.2 -0.2128353 +v -0.8 0.15 -0.2128353 +v -0.7 0.2 -0.3128352 +v -0.8 0.15 -0.3128352 +v -0.5999999 0.2 -0.3128352 +v -0.5999999 0.2 -0.2128353 +v 0.2 0.03174925 0.6871647 +v 0.6 0.4 -0.6128354 +v 0.6 0.4 -0.01283526 +v 0.5292892 0.3 0.05787539 +v 0.2 0.4 0.3871647 +v 0.2707107 0.3 0.3164541 +v 0.2707107 0.2 0.3164541 +v 0.5292892 0.2 0.05787539 +v 0.2 0.4 -0.6128354 +v -0.2 0.03174925 0.6871647 +v -0.5999999 0.4 -0.01283526 +v -0.5292892 0.2 0.05787539 +v -0.2707107 0.2 0.3164541 +v -0.2707107 0.3 0.3164541 +v -0.5292892 0.3 0.05787539 +v -0.2 0.4 0.3871647 +v -0.5999999 0.4 -0.6128354 +v -0.2 0.4 -0.6128354 +v -0.2 0.073163 0.9480448 +v 0.2 0.08344824 1.012835 +v -0.2 0.08344824 1.012835 +v 0.2 0.073163 0.9480448 +v 0.6 0.5 -0.9128351 +v 0.2 0.5 -0.9128351 +v 0.2 0.42 -0.6728354 +v 0.2 5.730172E-14 -0.7128353 +v 0.2 0.4 -0.7128353 +v -0.2 0.4 -0.7128353 +v 0.2 0.6 -0.3128352 +v -0.2 0.42 -0.6728354 +v -0.2 0.6 -0.3128352 +v -0.2 5.716636E-14 -0.7128353 +v 0.2 0.6 0.08716476 +v -0.2 0.6 0.08716476 +v -0.2 0.5 -0.9128351 +v -0.5999999 0.5 -0.9128351 +v 0.2 0.5900496 0.2866684 +v -0.2 0.5900496 0.2866684 +v 0.2 0.1668965 1.012835 +v -0.2 0.1668965 1.012835 +v 0.3 0.1 -0.8128352 +v 0.5 0.1 -0.8128352 +v 0.3 0.4 -0.8128352 +v 0.5 0.4 -0.8128352 +v 0.2707107 0.3 0.05787539 +v 0.2707107 0.2 0.05787539 +v -0.5 0.4 -0.8128352 +v -0.5 0.1 -0.8128352 +v -0.3 0.4 -0.8128352 +v -0.3 0.1 -0.8128352 +v -0.2707107 0.2 0.05787539 +v -0.2707107 0.3 0.05787539 +v -0.0999999 0.5668964 0.8128353 +v 0.1 0.5668964 0.8128353 + +vn 0 -1 0 +vn 0 0 -1 +vn 0 1 0 +vn 0.4472136 0.8944272 0 +vn 1 0 0 +vn 0 0 1 +vn -1 0 0 +vn -0.4472136 0.8944272 0 +vn 0.7071068 0 0.7071068 +vn -0.7071068 0 0.7071068 +vn 0 -0.9876331 0.1567831 +vn 0 0.9486833 0.3162278 +vn 0 0.8944272 -0.4472136 +vn 0 0.9987586 0.0498137 +vn -0.9257981 0.3266113 0.1903234 +vn 0.9257981 0.3266113 0.1903234 +vn 0 0.4472136 0.8944272 +vn 0 0.9990332 0.0439609 + +vt 11.81102 27.05373 +vt 7.874016 19.17971 +vt 7.874016 27.05373 +vt 7.874016 -24.12737 +vt 23.62205 -24.12737 +vt 39.37008 -24.12737 +vt 39.37008 -0.5053265 +vt 7.874016 3.431681 +vt -23.62205 2.264855E-12 +vt -11.81102 3.937008 +vt -7.874016 2.264855E-12 +vt -7.874016 19.68504 +vt -11.81102 15.74803 +vt -19.68504 15.74803 +vt -19.68504 3.937008 +vt -23.62205 19.68504 +vt -39.37008 -0.5053265 +vt -31.49606 -14.28485 +vt -39.37008 -24.12737 +vt -23.62205 -24.12737 +vt -23.62205 -14.28485 +vt -31.49606 -10.34785 +vt -11.81102 27.05373 +vt -23.62205 -0.5053265 +vt -23.62205 -10.34785 +vt -7.874016 15.24271 +vt -7.874016 27.05373 +vt 14.28485 -25.52991 +vt 10.34785 -25.52991 +vt 14.28485 -21.1282 +vt 10.34785 -21.1282 +vt -31.49606 3.937008 +vt -31.49606 5.905512 +vt -23.62205 3.937008 +vt -23.62205 7.874016 +vt -27.55906 7.874016 +vt 14.28485 5.905512 +vt 14.28485 3.937008 +vt 10.34785 5.905512 +vt 10.34785 3.937008 +vt -27.55906 -14.28485 +vt -27.55906 -10.34785 +vt 31.49606 3.937008 +vt 23.62205 3.937008 +vt 31.49606 5.905512 +vt 23.62205 7.874016 +vt 27.55906 7.874016 +vt -17.71654 -12.31635 +vt -17.71654 3.431681 +vt -7.874016 -12.31635 +vt -7.874016 3.431681 +vt -7.874016 15.74803 +vt -17.71654 15.74803 +vt -17.71654 19.68504 +vt 12.31635 19.68504 +vt 12.31635 15.74803 +vt -3.431681 19.68504 +vt -3.431681 15.74803 +vt 17.71654 15.74803 +vt 7.874016 15.74803 +vt 17.71654 19.68504 +vt 7.874016 19.68504 +vt 7.874016 2.264855E-12 +vt 19.68504 3.937008 +vt 23.62205 2.264855E-12 +vt 23.62205 19.68504 +vt 19.68504 15.74803 +vt 11.81102 15.74803 +vt 11.81102 3.937008 +vt -7.874016 19.17971 +vt -7.874016 -24.12737 +vt -7.874016 -0.5053265 +vt -12.31635 15.74803 +vt -12.31635 19.68504 +vt 3.431681 15.74803 +vt 3.431681 19.68504 +vt 7.874016 -12.31635 +vt 17.71654 -12.31635 +vt 17.71654 3.431681 +vt 23.62205 -8.379342 +vt 23.62205 -0.5053265 +vt 31.49606 -8.379342 +vt 7.874016 15.24271 +vt 31.49606 -12.31635 +vt 23.62205 -12.31635 +vt -8.379342 -21.1282 +vt -8.379342 -25.52991 +vt -12.31635 -21.1282 +vt -12.31635 -25.52991 +vt 27.55906 -12.31635 +vt 27.55906 -8.379342 +vt -12.31635 3.937008 +vt -12.31635 5.905512 +vt -8.379342 3.937008 +vt -8.379342 5.905512 +vt -39.37008 0 +vt -39.37008 3.937008 +vt 11.81102 1.969536E-12 +vt 7.874016 2.250644E-12 +vt 7.874016 1.249971 +vt 7.874016 3.937008 +vt 24.12737 3.937008 +vt 24.12737 2.813257E-12 +vt 0.5053265 3.937008 +vt 0.5053265 2.813257E-12 +vt 28.19617 3.937008 +vt 28.19617 1.122417E-12 +vt -10.77822 3.937008 +vt -10.77822 3.091952E-12 +vt 24.12737 15.74803 +vt 0.5053265 15.74803 +vt 14.28485 7.874016 +vt 10.34785 7.874016 +vt 17.06063 3.937008 +vt 13.12362 11.81102 +vt 17.06063 15.74803 +vt -5.21045 15.74803 +vt -1.273442 11.81102 +vt -1.273442 7.874016 +vt 13.12362 7.874016 +vt -5.21045 3.937008 +vt -28.19617 1.122417E-12 +vt -28.19617 3.937008 +vt 10.77822 3.091952E-12 +vt 10.77822 3.937008 +vt -11.81102 1.969536E-12 +vt -7.874016 2.250644E-12 +vt -7.874016 1.249971 +vt -7.874016 3.937008 +vt -24.12737 2.813257E-12 +vt -24.12737 3.937008 +vt -0.5053265 2.813257E-12 +vt -0.5053265 3.937008 +vt 39.37008 0 +vt 39.37008 3.937008 +vt -17.06063 15.74803 +vt -13.12362 7.874016 +vt -17.06063 3.937008 +vt 5.21045 3.937008 +vt 1.273442 7.874016 +vt 1.273442 11.81102 +vt -13.12362 11.81102 +vt 5.21045 15.74803 +vt -8.379342 7.874016 +vt -0.5053265 15.74803 +vt -12.31635 7.874016 +vt -24.12737 15.74803 +vt 27.05373 3.682953E-12 +vt 19.17971 3.726632E-12 +vt 27.05373 1.249971 +vt -27.05373 1.249971 +vt -19.17971 4.388796E-12 +vt -27.05373 4.399107E-12 +vt 27.05373 3.937008 +vt 37.3246 2.880433 +vt 15.24271 15.74803 +vt 15.24271 3.937008 +vt 7.874016 39.89736 +vt -7.874016 26.91513 +vt -7.874016 39.89736 +vt -7.874016 37.31461 +vt -7.874016 18.94252 +vt 7.874016 37.31461 +vt 7.874016 18.94252 +vt 7.874016 26.91513 +vt -15.24271 3.937008 +vt -27.05373 3.937008 +vt -15.24271 15.74803 +vt -37.3246 2.880433 +vt 23.62205 40.31911 +vt 23.62205 27.8692 +vt 7.874016 40.31911 +vt 7.874016 27.8692 +vt 7.874016 30.35918 +vt -23.62205 -39.8754 +vt -23.62205 -35.9384 +vt -7.874016 -39.8754 +vt -7.874016 -35.9384 +vt 39.8754 19.68504 +vt 39.8754 2.264855E-12 +vt 35.9384 19.68504 +vt 24.12737 2.264855E-12 +vt -39.8754 2.271339E-12 +vt -39.8754 19.68504 +vt -28.06438 2.262457E-12 +vt -35.9384 19.68504 +vt -28.06438 15.74803 +vt -26.48958 16.53543 +vt -7.874016 -18.05881 +vt -7.874016 -16.29813 +vt 7.874016 -18.05881 +vt -7.874016 -0.4519778 +vt 7.874016 -16.29813 +vt 7.874016 -0.4519778 +vt -7.874016 2.255973E-12 +vt 7.874016 -28.06438 +vt -7.874016 -28.06438 +vt 7.874016 -39.8754 +vt 23.62205 -39.8754 +vt 7.874016 -35.9384 +vt 23.62205 -35.9384 +vt 28.06438 15.74803 +vt 26.48958 16.53543 +vt 28.06438 2.234545E-12 +vt 39.8754 2.248756E-12 +vt -39.8754 2.264855E-12 +vt -24.12737 2.264855E-12 +vt -7.874016 40.31911 +vt -7.874016 30.35918 +vt -23.62205 40.31911 +vt -7.874016 27.8692 +vt -23.62205 27.8692 +vt 7.874016 -2.25072 +vt 7.874016 -10.11496 +vt -7.874016 -2.25072 +vt -7.874016 -10.11496 +vt 12.31635 23.62205 +vt -3.431681 23.62205 +vt -11.28616 23.2303 +vt -39.8754 6.570727 +vt -39.8754 3.285364 +vt 7.874016 3.285364 +vt -7.874016 3.285364 +vt 7.874016 6.570727 +vt -7.874016 6.570727 +vt 3.431681 23.62205 +vt 11.28616 23.2303 +vt 39.8754 6.570727 +vt 39.8754 3.285364 +vt -12.31635 23.62205 +vt -19.68504 -39.8754 +vt -19.68504 -32.00139 +vt -11.81102 -39.8754 +vt -11.81102 -32.00139 +vt 39.8754 15.74803 +vt 39.8754 3.937008 +vt 32.00139 15.74803 +vt 32.00139 3.937008 +vt 19.68504 -32.00139 +vt 19.68504 -39.8754 +vt 11.81102 -32.00139 +vt 11.81102 -39.8754 +vt -39.8754 3.937008 +vt -39.8754 15.74803 +vt -32.00139 3.937008 +vt -32.00139 15.74803 +vt -2.278558 11.81102 +vt -2.278558 7.874016 +vt -12.45882 11.81102 +vt -12.45882 7.874016 +vt -10.6579 2.278558 +vt -20.83816 2.278558 +vt -10.6579 12.45882 +vt 10.6579 12.45882 +vt 20.83816 2.278558 +vt 10.6579 2.278558 +vt 20.83816 7.874016 +vt 10.6579 7.874016 +vt 20.83816 11.81102 +vt 10.6579 11.81102 +vt -10.6579 7.874016 +vt -20.83816 7.874016 +vt -10.6579 11.81102 +vt -20.83816 11.81102 +vt 2.278558 7.874016 +vt 2.278558 11.81102 +vt 12.45882 7.874016 +vt 12.45882 11.81102 +vt 9.469408 18.69498 +vt 30.55309 17.73055 +vt 37.47303 1.068768 +vt -30.55309 17.73055 +vt -9.469408 18.69498 +vt -37.47303 1.068768 +vt 7.874016 -11.95579 +vt -7.874016 -11.95579 +vt 3.937008 5.651048 +vt -3.937008 5.651048 +vt 7.874016 -10.25402 +vt 3.937008 -30.9893 +vt -7.874016 -10.25402 +vt -3.937008 -30.9893 + +usemtl metal + +f 3/3/1 2/2/1 1/1/1 +f 4/4/1 1/1/1 2/2/1 +f 5/5/1 1/1/1 4/4/1 +f 6/6/1 1/1/1 5/5/1 +f 1/1/1 6/6/1 7/7/1 +f 4/4/1 2/2/1 8/8/1 +f 11/11/2 10/10/2 9/9/2 +f 10/10/2 11/11/2 12/12/2 +f 10/10/2 12/12/2 13/13/2 +f 13/13/2 12/12/2 14/14/2 +f 15/15/2 9/9/2 10/10/2 +f 9/9/2 15/15/2 16/16/2 +f 16/16/2 15/15/2 14/14/2 +f 16/16/2 14/14/2 12/12/2 +f 19/19/3 18/18/3 17/17/3 +f 18/18/3 19/19/3 20/20/3 +f 18/18/3 20/20/3 21/21/3 +f 22/22/3 17/17/3 18/18/3 +f 17/17/3 22/22/3 23/23/3 +f 23/23/3 22/22/3 24/24/3 +f 24/24/3 22/22/3 25/25/3 +f 23/23/3 24/24/3 26/26/3 +f 23/23/3 26/26/3 27/27/3 +f 30/30/4 29/29/4 28/28/4 +f 29/29/4 30/30/4 31/31/4 +f 21/34/2 28/33/2 18/32/2 +f 28/33/2 21/34/2 32/35/2 +f 28/33/2 32/35/2 30/36/2 +f 29/39/5 18/38/5 28/37/5 +f 18/38/5 29/39/5 22/40/5 +f 32/21/3 31/42/3 30/41/3 +f 31/42/3 32/21/3 33/25/3 +f 29/45/6 25/44/6 22/43/6 +f 25/44/6 29/45/6 33/46/6 +f 33/46/6 29/45/6 31/47/6 +f 36/50/3 35/49/3 34/48/3 +f 35/49/3 36/50/3 37/51/3 +f 36/12/2 39/53/2 38/52/2 +f 39/53/2 36/12/2 34/54/2 +f 35/57/5 39/56/5 34/55/5 +f 39/56/5 35/57/5 40/58/5 +f 35/61/6 41/60/6 40/59/6 +f 41/60/6 35/61/6 37/62/6 +f 44/65/2 43/64/2 42/63/2 +f 43/64/2 44/65/2 45/66/2 +f 43/64/2 45/66/2 46/67/2 +f 46/67/2 45/66/2 47/68/2 +f 48/69/2 42/63/2 43/64/2 +f 42/63/2 48/69/2 49/62/2 +f 49/62/2 48/69/2 47/68/2 +f 49/62/2 47/68/2 45/66/2 +f 52/23/1 51/70/1 50/27/1 +f 51/70/1 52/23/1 53/51/1 +f 53/51/1 52/23/1 54/71/1 +f 54/71/1 52/23/1 55/20/1 +f 55/20/1 52/23/1 56/17/1 +f 55/20/1 56/17/1 57/19/1 +f 54/71/1 58/72/1 53/51/1 +f 61/12/6 60/53/6 59/52/6 +f 60/53/6 61/12/6 62/54/6 +f 65/61/2 64/60/2 63/59/2 +f 64/60/2 65/61/2 66/62/2 +f 60/75/7 65/74/7 63/73/7 +f 65/74/7 60/75/7 62/76/7 +f 65/78/3 61/8/3 66/77/3 +f 61/8/3 65/78/3 62/79/3 +f 69/82/3 68/81/3 67/80/3 +f 70/7/3 68/81/3 69/82/3 +f 71/1/3 68/81/3 70/7/3 +f 72/3/3 68/81/3 71/1/3 +f 68/81/3 72/3/3 73/83/3 +f 70/7/3 69/82/3 74/84/3 +f 70/7/3 74/84/3 75/6/3 +f 76/85/3 75/6/3 74/84/3 +f 75/6/3 76/85/3 77/5/3 +f 80/88/8 79/87/8 78/86/8 +f 79/87/8 80/88/8 81/89/8 +f 80/90/3 83/80/3 82/85/3 +f 83/80/3 80/90/3 78/91/3 +f 69/94/7 81/93/7 74/92/7 +f 81/93/7 69/94/7 79/95/7 +f 67/34/6 79/33/6 69/32/6 +f 79/33/6 67/34/6 83/35/6 +f 79/33/6 83/35/6 78/36/6 +f 81/45/2 76/44/2 74/43/2 +f 76/44/2 81/45/2 82/46/2 +f 82/46/2 81/45/2 80/47/2 + +usemtl metalRed + +f 5/9/2 19/97/2 6/96/2 +f 19/97/2 5/9/2 20/34/2 +f 23/69/6 3/99/6 1/98/6 +f 3/99/6 23/69/6 84/100/6 +f 84/100/6 23/69/6 27/101/6 +f 17/104/5 6/103/5 19/102/5 +f 6/103/5 17/104/5 7/105/5 +f 23/108/9 7/107/9 17/106/9 +f 7/107/9 23/108/9 1/109/9 +f 86/111/5 20/102/5 85/110/5 +f 20/102/5 86/111/5 32/112/5 +f 32/112/5 86/111/5 33/113/5 +f 33/113/5 86/111/5 25/40/5 +f 25/40/5 86/111/5 24/104/5 +f 21/38/5 20/102/5 32/112/5 +f 86/116/9 87/115/9 24/114/9 +f 87/115/9 86/116/9 88/117/9 +f 87/115/9 88/117/9 89/118/9 +f 89/118/9 88/117/9 90/119/9 +f 91/120/9 24/114/9 87/115/9 +f 24/114/9 91/120/9 26/121/9 +f 26/121/9 91/120/9 90/119/9 +f 26/121/9 90/119/9 88/117/9 +f 92/71/3 39/48/3 85/20/3 +f 39/48/3 92/71/3 38/50/3 +f 40/49/3 85/20/3 39/48/3 +f 40/49/3 86/24/3 85/20/3 +f 86/24/3 40/49/3 88/26/3 +f 88/26/3 40/49/3 41/51/3 +f 52/124/10 70/123/10 56/122/10 +f 70/123/10 52/124/10 71/125/10 +f 50/127/6 71/10/6 52/126/6 +f 71/10/6 50/127/6 93/128/6 +f 71/10/6 93/128/6 72/129/6 +f 56/132/7 75/131/7 57/130/7 +f 75/131/7 56/132/7 70/133/7 +f 75/135/2 55/65/2 57/134/2 +f 55/65/2 75/135/2 77/44/2 +f 68/138/10 95/137/10 94/136/10 +f 95/137/10 68/138/10 73/139/10 +f 95/137/10 73/139/10 96/140/10 +f 96/140/10 73/139/10 97/141/10 +f 98/142/10 94/136/10 95/137/10 +f 94/136/10 98/142/10 99/143/10 +f 99/143/10 98/142/10 97/141/10 +f 99/143/10 97/141/10 73/139/10 +f 68/133/7 83/144/7 67/94/7 +f 94/145/7 83/144/7 68/133/7 +f 94/145/7 82/146/7 83/144/7 +f 100/147/7 82/146/7 94/145/7 +f 100/147/7 76/92/7 82/146/7 +f 76/92/7 100/147/7 77/131/7 +f 100/5/3 64/77/3 101/4/3 +f 64/77/3 100/5/3 63/78/3 +f 63/78/3 100/5/3 60/79/3 +f 94/81/3 60/79/3 100/5/3 +f 99/83/3 60/79/3 94/81/3 +f 60/79/3 99/83/3 59/8/3 +f 84/150/7 2/149/7 3/148/7 +f 50/153/5 51/152/5 93/151/5 +f 102/155/7 72/154/7 93/150/7 +f 99/156/7 72/154/7 102/155/7 +f 72/154/7 99/156/7 73/157/7 +f 51/70/1 8/8/1 2/2/1 +f 8/8/1 51/70/1 53/51/1 +f 104/160/11 93/159/11 103/158/11 +f 93/159/11 104/160/11 102/161/11 +f 51/162/11 103/158/11 93/159/11 +f 51/162/11 105/163/11 103/158/11 +f 2/164/11 105/163/11 51/162/11 +f 105/163/11 2/164/11 84/165/11 +f 88/168/5 27/167/5 26/166/5 +f 27/167/5 88/168/5 105/169/5 +f 27/167/5 105/169/5 84/151/5 + +usemtl metalDark + +f 107/172/12 85/171/12 106/170/12 +f 85/171/12 107/172/12 92/173/12 +f 92/173/12 107/172/12 108/174/12 +f 12/177/3 106/176/3 16/175/3 +f 106/176/3 12/177/3 107/178/3 +f 106/181/5 9/180/5 16/179/5 +f 9/180/5 106/181/5 5/182/5 +f 5/182/5 106/181/5 85/110/5 +f 5/182/5 85/110/5 20/102/5 +f 109/185/7 12/184/7 11/183/7 +f 12/184/7 109/185/7 107/186/7 +f 107/186/7 109/185/7 110/187/7 +f 107/186/7 110/187/7 108/188/7 +f 111/191/13 108/190/13 110/189/13 +f 108/190/13 111/191/13 112/192/13 +f 112/192/13 111/191/13 113/193/13 +f 114/194/13 112/192/13 113/193/13 +f 111/60/2 109/195/2 115/99/2 +f 109/195/2 111/60/2 110/52/2 +f 114/77/3 116/51/3 112/50/3 +f 116/51/3 114/77/3 117/8/3 +f 58/72/1 115/197/1 109/196/1 +f 115/197/1 58/72/1 54/71/1 +f 115/197/1 54/71/1 55/20/1 +f 44/175/1 115/197/1 55/20/1 +f 115/197/1 44/175/1 42/177/1 +f 109/196/1 53/51/1 58/72/1 +f 4/4/1 53/51/1 109/196/1 +f 53/51/1 4/4/1 8/8/1 +f 11/198/1 4/4/1 109/196/1 +f 9/199/1 4/4/1 11/198/1 +f 4/4/1 9/199/1 5/5/1 +f 45/199/3 118/200/3 49/198/3 +f 118/200/3 45/199/3 119/201/3 +f 113/203/5 111/202/5 118/181/5 +f 115/204/5 118/181/5 111/202/5 +f 42/205/5 118/181/5 115/204/5 +f 118/181/5 42/205/5 49/179/5 +f 55/207/7 45/184/7 44/206/7 +f 45/184/7 55/207/7 119/186/7 +f 119/186/7 55/207/7 100/147/7 +f 100/147/7 55/207/7 77/131/7 +f 119/210/12 113/209/12 118/208/12 +f 113/209/12 119/210/12 101/211/12 +f 101/211/12 119/210/12 100/212/12 +f 117/215/14 120/214/14 116/213/14 +f 120/214/14 117/215/14 121/216/14 +f 116/218/5 36/55/5 112/217/5 +f 36/55/5 116/218/5 37/57/5 +f 37/57/5 116/218/5 120/219/5 +f 37/57/5 120/219/5 41/58/5 +f 41/58/5 120/219/5 88/168/5 +f 88/168/5 120/219/5 122/220/5 +f 88/168/5 122/220/5 105/169/5 +f 105/169/5 122/220/5 103/221/5 +f 38/56/5 112/217/5 36/55/5 +f 92/110/5 112/217/5 38/56/5 +f 112/217/5 92/110/5 108/203/5 +f 122/224/6 104/223/6 103/222/6 +f 104/223/6 122/224/6 123/225/6 +f 99/156/7 61/76/7 59/75/7 +f 61/76/7 99/156/7 117/226/7 +f 117/226/7 99/156/7 121/227/7 +f 121/227/7 99/156/7 123/228/7 +f 123/228/7 99/156/7 102/155/7 +f 123/228/7 102/155/7 104/229/7 +f 114/230/7 61/76/7 117/226/7 +f 114/230/7 66/74/7 61/76/7 +f 114/230/7 64/73/7 66/74/7 +f 114/230/7 101/147/7 64/73/7 +f 101/147/7 114/230/7 113/188/7 + +usemtl dark + +f 126/13/2 125/15/2 124/10/2 +f 125/15/2 126/13/2 127/14/2 +f 10/233/3 125/232/3 15/231/3 +f 125/232/3 10/233/3 124/234/3 +f 126/237/5 10/236/5 13/235/5 +f 10/236/5 126/237/5 124/238/5 +f 126/241/1 14/240/1 127/239/1 +f 14/240/1 126/241/1 13/242/1 +f 125/245/7 14/244/7 15/243/7 +f 14/244/7 125/245/7 127/246/7 +f 89/249/5 129/248/5 128/247/5 +f 129/248/5 89/249/5 90/250/5 +f 90/253/3 91/252/3 129/251/3 +f 128/256/1 87/255/1 89/254/1 +f 87/259/6 129/258/6 91/257/6 +f 129/258/6 87/259/6 128/260/6 +f 130/237/5 43/236/5 46/235/5 +f 43/236/5 130/237/5 131/238/5 +f 130/232/1 47/233/1 132/234/1 +f 47/233/1 130/232/1 46/231/1 +f 133/245/7 47/244/7 48/243/7 +f 47/244/7 133/245/7 132/246/7 +f 130/67/2 133/69/2 131/64/2 +f 133/69/2 130/67/2 132/68/2 +f 43/240/3 133/241/3 48/242/3 +f 133/241/3 43/240/3 131/239/3 +f 135/263/6 95/262/6 134/261/6 +f 95/262/6 135/263/6 98/264/6 +f 96/267/7 135/266/7 134/265/7 +f 135/266/7 96/267/7 97/268/7 +f 98/252/1 135/251/1 97/253/1 +f 95/255/3 96/254/3 134/256/3 +f 123/271/15 136/270/15 121/269/15 +f 122/274/16 120/273/16 137/272/16 +f 137/277/17 123/276/17 122/275/17 +f 123/276/17 137/277/17 136/278/17 +f 121/281/18 137/280/18 120/279/18 +f 137/280/18 121/281/18 136/282/18 + diff --git a/packages/examples/src/examples/multiMaterialMesh/ExampleMultiMaterialMesh.tsx b/packages/examples/src/examples/multiMaterialMesh/ExampleMultiMaterialMesh.tsx new file mode 100644 index 000000000..a53bd8187 --- /dev/null +++ b/packages/examples/src/examples/multiMaterialMesh/ExampleMultiMaterialMesh.tsx @@ -0,0 +1,207 @@ +/** + * melonJS — Multi-material OBJ mesh showcase. + * + * Loads four Kenney Space Kit (CC0) spacecraft, each with 3-5 named + * MTL materials (metal / metalRed / metalDark / dark / …), and renders + * them rotating in a 2×2 grid. The OBJ parser emits a Three.js / glTF + * style `groups[]` array keyed by `materialName`; the `Mesh` + * constructor bakes each material's diffuse color (`Kd`) into a + * per-vertex color buffer so the whole mesh draws in a single GPU + * call. `mesh.tint` then multiplies on top at render time — used here + * to give each ship its own team color while keeping the per-material + * palette intact (orange wings stay bright, dark cockpits stay dark). + * + * Compare with the `mesh3d` example: that one binds a single texture + * across the whole mesh (checkerboard on cube / sphere / teapot). + * This one exercises the multi-material code path — same `Mesh` API, + * the only extra wiring is preloading the matching `.mtl` and passing + * its name through `material:`. + * + * Copyright (C) 2011 - 2026 AltByte Pte Ltd — MIT License. + * See `packages/examples/LICENSE.md` for full license + asset credits + * (Kenney Space Kit 2.0, CC0). + */ +import { DebugPanelPlugin } from "@melonjs/debug-plugin"; +import type { CanvasRenderer, WebGLRenderer } from "melonjs"; +import { + Application, + loader, + Mesh, + plugin, + Renderable, + Vector3d, + video, +} from "melonjs"; +import { createExampleComponent } from "../utils"; + +// ─── layout & content ───────────────────────────────────────────── + +const CANVAS_W = 1024; +const CANVAS_H = 768; +const MESH_SIZE = 240; + +// engine-space Y for each row's mesh center. Top row ≈ 26% of the +// canvas, bottom row ≈ 69% — shifted up from cell centers so the +// per-row labels (drawn just above each ship) and any sub-pixel +// canvas cropping don't push the bottom row off-screen. +const ROW_Y = [200, 530]; + +const ASSET_BASE = `${import.meta.env.BASE_URL}assets/multiMaterialMesh/`; + +// The four Kenney spacecraft to showcase. Per-ship `tint` is a +// 0..1 RGB multiplier applied via `mesh.tint` after the MTL palette +// is baked into the vertex stream — multiplicative, so each +// material's contrast survives and the whole craft just shifts hue. +const CRAFTS = [ + { name: "craft_speederA", tint: [1.0, 0.6, 0.55] }, // crimson + { name: "craft_speederB", tint: [0.55, 0.75, 1.0] }, // ice blue + { name: "craft_racer", tint: [0.55, 1.0, 0.65] }, // jade + { name: "craft_miner", tint: [1.0, 0.95, 0.55] }, // gold +]; + +// ─── entry point ────────────────────────────────────────────────── + +const createGame = () => { + const app = new Application(CANVAS_W, CANVAS_H, { + parent: "screen", + // Multi-material 3D meshes need the WebGL renderer for usable + // frame rates — Canvas would solid-fill per triangle in JS, + // correct but 10-50× slower than the GPU rasterizer. + renderer: video.WEBGL, + scale: "auto", + }); + + app.world.backgroundColor.parseCSS("#0a0a1f"); + plugin.register(DebugPanelPlugin, "debugPanel"); + + loader.preload(buildAssetList(), () => { + spawnCrafts(app); + spawnLabels(app); + }); +}; + +// Build the list of (.obj, .mtl) pairs for every craft. `type: "mtl"` +// runs the MTL parser, populating the material cache so the Mesh +// constructor can look entries up by name via `material:`. +function buildAssetList() { + const assets = []; + for (const craft of CRAFTS) { + assets.push({ + name: craft.name, + type: "obj", + src: `${ASSET_BASE}${craft.name}.obj`, + }); + assets.push({ + name: craft.name, + type: "mtl", + src: `${ASSET_BASE}${craft.name}.mtl`, + }); + } + return assets; +} + +// ─── per-craft renderable ───────────────────────────────────────── + +const AXIS_Y = new Vector3d(0, 1, 0); +const AXIS_X = new Vector3d(1, 0, 0); + +/** + * One spinning spacecraft. Owns a `Mesh` constructed from the OBJ + + * MTL pair — the Mesh detects multi-material from the OBJ's `groups` + * array and bakes per-material colors into its vertex stream. The + * team color is then applied via `mesh.tint` (multiplicative against + * the baked palette). + */ +class SpinningCraft extends Renderable { + mesh: Mesh; + + constructor( + modelName: string, + teamTint: number[], + x: number, + y: number, + size: number, + ) { + super(0, 0, CANVAS_W, CANVAS_H); + this.anchorPoint.set(0, 0); + this.mesh = new Mesh(x, y, { + model: modelName, + material: modelName, // MTL name matches OBJ name in the Kenney pack + width: size, + height: size, + cullBackFaces: true, + }); + this.mesh.tint.setColor( + Math.round(teamTint[0] * 255), + Math.round(teamTint[1] * 255), + Math.round(teamTint[2] * 255), + ); + } + + override update(dt: number): boolean { + // Y spin + slight X wobble so every face of the model rotates + // into view across the animation, exercising the per-material + // color separation from all sides. + this.mesh.rotate(dt * 0.0008, AXIS_Y); + this.mesh.rotate(dt * 0.0003, AXIS_X); + return true; + } + + override draw(renderer: WebGLRenderer | CanvasRenderer): void { + this.mesh.preDraw(renderer); + this.mesh.draw(renderer); + this.mesh.postDraw(renderer); + } +} + +// ─── scene helpers ──────────────────────────────────────────────── + +function spawnCrafts(app: Application) { + const cellW = CANVAS_W / 2; + for (let i = 0; i < CRAFTS.length; i++) { + const col = i % 2; + const row = Math.floor(i / 2); + const cx = cellW * col + cellW / 2; + const craft = CRAFTS[i]; + app.world.addChild( + new SpinningCraft(craft.name, craft.tint, cx, ROW_Y[row], MESH_SIZE), + ); + } +} + +/** + * HTML labels positioned just above each craft. The canvas is scaled + * by `scale: "auto"`, so positions are expressed as a percentage of + * the parent element (the canvas wrapper) — they stay aligned with + * the meshes regardless of the displayed canvas size. + */ +function spawnLabels(app: Application) { + const parent = app.renderer.getCanvas().parentElement; + if (!parent) { + return; + } + parent.style.position = "relative"; + + // label Y sits just above the mesh center (mesh half-extent ≈ MESH_SIZE/2) + const labelOffsetY = MESH_SIZE / 2 + 10; + const labelYPct = [ + (ROW_Y[0] - labelOffsetY) / CANVAS_H, + (ROW_Y[1] - labelOffsetY) / CANVAS_H, + ]; + + const baseStyle = + "position:absolute;color:#e0e0e0;font-family:'Courier New',monospace;" + + "font-size:14px;font-weight:bold;text-shadow:0 0 4px #000;" + + "z-index:1000;pointer-events:none;transform:translate(-50%,-50%);"; + + for (let i = 0; i < CRAFTS.length; i++) { + const col = i % 2; + const row = Math.floor(i / 2); + const label = document.createElement("div"); + label.textContent = CRAFTS[i].name.replace("craft_", ""); + label.style.cssText = `${baseStyle}left:${(col * 0.5 + 0.25) * 100}%;top:${labelYPct[row] * 100}%;`; + parent.appendChild(label); + } +} + +export const ExampleMultiMaterialMesh = createExampleComponent(createGame); diff --git a/packages/examples/src/main.tsx b/packages/examples/src/main.tsx index dc3dc2fea..ca36da0ad 100644 --- a/packages/examples/src/main.tsx +++ b/packages/examples/src/main.tsx @@ -108,6 +108,11 @@ const ExampleMesh3dMaterial = lazy(() => default: m.ExampleMesh3dMaterial, })), ); +const ExampleMultiMaterialMesh = lazy(() => + import("./examples/multiMaterialMesh/ExampleMultiMaterialMesh").then((m) => ({ + default: m.ExampleMultiMaterialMesh, + })), +); const ExamplePlatformer = lazy(() => import("./examples/platformer/ExamplePlatformer").then((m) => ({ default: m.ExamplePlatformer, @@ -337,6 +342,14 @@ const examples: { description: "3D cube pet models from Kenney with MTL material support — texture and colors auto-resolved from .mtl files.", }, + { + component: , + label: "Multi-material OBJ", + path: "multi-material-mesh", + sourceDir: "multiMaterialMesh", + description: + "Rotating 3D models with multiple materials and per-mesh tinting — each material region picks up its diffuse color from the .mtl file, multiplied by a runtime tint.", + }, { component: , label: "Platformer", diff --git a/packages/melonjs/CHANGELOG.md b/packages/melonjs/CHANGELOG.md index dbe7d8b7f..6260b0c7b 100644 --- a/packages/melonjs/CHANGELOG.md +++ b/packages/melonjs/CHANGELOG.md @@ -7,6 +7,7 @@ ### Added - **`renderer.setDepth(depth)`** — new public method on the base `Renderer`. Mirrors the existing `setTint` / `setColor` state-setter pattern. Sets `renderer.currentDepth`, which the batchers read at vertex-emit time and push as the z component of each vertex. `Renderable.preDraw` now forwards `this.depth` automatically, so user code typically never needs to call `setDepth` directly. - Per-sprite depth on the GPU: all batched draw paths (`QuadBatcher`, `LitQuadBatcher`, `PrimitiveBatcher`, GPU TMX) now carry the renderable's `.depth` as the z component of their vertex stream. Default shaders consume it via `vec4(aVertex, 1.0)` in `gl_Position`. With an orthographic projection (the engine default), z has no visible effect — existing 2D apps render identically. With a perspective projection, sprites at different depths are correctly scaled and parallaxed by the projection matrix. +- **Multi-material OBJ rendering** — `Mesh` now correctly draws OBJ files with multiple `usemtl` directives + a bound MTL, instead of collapsing the whole model to a single uniform color. The OBJ parser emits `groups: Array<{materialName, start, count}>` matching the Three.js / glTF convention. The Mesh constructor bakes each material's `Kd` into a per-vertex color buffer at construction time, so multi-material meshes need **no extra draw calls per material** vs single-material rendering — large meshes still chunk across multiple draws to respect the WebGL batcher's vertex/index buffer limits (same behavior as today's single-material path), but adding more materials to a mesh doesn't multiply that count. `mesh.tint` multiplies on top at render time — flash / fade / team-color via `setTint` work exactly like single-material meshes. **Scope:** per-material diffuse colors (`Kd`) are fully supported on both WebGL and Canvas; per-material textures (each material with its own `map_Kd`) are NOT supported in this pipeline — the whole mesh shares a single texture binding (the first group's `map_Kd` if any, else a 1×1 white pixel for Kd-only models). Single-material meshes are unchanged — same code path, same allocations, same behavior. New `Multi-material OBJ` example showcases the feature with four Kenney Space Kit spacecraft, each given a distinct team color via `mesh.tint`. ### Changed - **Vertex attribute layout: `aVertex` widened from `vec2` to `vec3`** across `quad-multi.vert`, `quad-multi-lit.vert`, `primitive.vert`, `orthogonal-tmxlayer.vert`. `Mesh`'s shader already used `vec3 aVertex` — all batchers are now uniform. Per-vertex stride grows by 4 bytes (24 → 28 for the quad layout). Custom shaders binding attributes by name (`gl.getAttribLocation`) — the standard pattern, and what `GLShader` enforces — keep working unchanged; the byte-offset shift of `aRegion` / `aColor` / `aTextureId` is transparent because the batcher updates its own `vertexAttribPointer` offsets. Custom shaders declaring `attribute vec2 aVertex;` continue to work — WebGL silently drops the unused z component. diff --git a/packages/melonjs/src/level/tiled/TMXLayer.js b/packages/melonjs/src/level/tiled/TMXLayer.js index 0a5e2f18c..abfcc02d4 100644 --- a/packages/melonjs/src/level/tiled/TMXLayer.js +++ b/packages/melonjs/src/level/tiled/TMXLayer.js @@ -1,7 +1,7 @@ import { vector2dPool } from "../../math/vector2d.ts"; import Renderable from "../../renderable/renderable.js"; import CanvasRenderer from "../../video/canvas/canvas_renderer"; -import { createCanvas } from "../../video/video.js"; +import { createCanvas } from "../../video/canvas_factory.js"; import { TMX_CLEAR_BIT_MASK, TMX_FLIP_AD, diff --git a/packages/melonjs/src/loader/parsers/mtl.js b/packages/melonjs/src/loader/parsers/mtl.js index 1f9270392..62439ae07 100644 --- a/packages/melonjs/src/loader/parsers/mtl.js +++ b/packages/melonjs/src/loader/parsers/mtl.js @@ -1,4 +1,3 @@ -import { warning } from "../../lang/console.js"; import { mtlList } from "../cache.js"; import { fetchData } from "./fetchdata.js"; @@ -38,7 +37,6 @@ const UNSUPPORTED_MAPS = new Set([ * - Only one `map_Kd` texture per material is supported * - Specular (`Ks`, `Ns`), ambient (`Ka`), and illumination model (`illum`) are parsed but ignored * - Normal maps (`map_bump`, `bump`), specular maps (`map_Ks`), and other texture maps are not supported - * - Multiple materials per mesh (`usemtl`) are not supported — only the first material is used * * @param {string} text - raw MTL file contents * @param {string} basePath - base URL path for resolving texture references @@ -48,7 +46,6 @@ const UNSUPPORTED_MAPS = new Set([ function parseMTL(text, basePath) { const materials = {}; let current = null; - let materialCount = 0; const lines = text.split("\n"); for (let i = 0; i < lines.length; i++) { @@ -62,7 +59,9 @@ function parseMTL(text, basePath) { // warn on unsupported texture maps if (UNSUPPORTED_MAPS.has(keyword)) { - warning("MTL: '" + keyword + "' is not supported and will be ignored"); + console.warn( + "MTL: '" + keyword + "' is not supported and will be ignored", + ); continue; } @@ -72,20 +71,14 @@ function parseMTL(text, basePath) { !UNSUPPORTED_MAPS.has(keyword) && keyword !== "Ks" ) { - warning("MTL: unknown property '" + keyword + "' will be ignored"); + console.warn("MTL: unknown property '" + keyword + "' will be ignored"); continue; } switch (keyword) { case "newmtl": - materialCount++; - if (materialCount > 1) { - warning( - "MTL: multiple materials detected — only the first material's texture will be used per mesh", - ); - } if (!parts[1]) { - warning("MTL: newmtl missing material name, skipping"); + console.warn("MTL: newmtl missing material name, skipping"); break; } current = { diff --git a/packages/melonjs/src/loader/parsers/obj.js b/packages/melonjs/src/loader/parsers/obj.js index b8dcee7fb..3cabfa31c 100644 --- a/packages/melonjs/src/loader/parsers/obj.js +++ b/packages/melonjs/src/loader/parsers/obj.js @@ -26,20 +26,33 @@ const OBJ_INDEX_OFFSET = 1; * Parse a Wavefront OBJ file into geometry data. * Supports: `v` (vertex positions), `vt` (texture coordinates), * `f` (faces in `v`, `v/vt`, `v/vt/vn`, or `v//vn` format), - * `mtllib` (material library reference). + * `mtllib` (material library reference), + * `usemtl` (material group boundaries — emitted as `groups[]`). * * Features: * - Quad and n-gon triangulation (fan from first vertex) * - Automatic CW → CCW winding correction via signed volume test * - V texture coordinate flipped for OpenGL convention (OBJ has origin at bottom-left) * - Single-pass parsing with direct vertex unification (no intermediate arrays) + * - Material grouping: each `usemtl` switch emits a new `groups[]` entry + * pointing to a slice of the unified `indices` buffer, so callers + * (e.g. `Mesh`) can render each group with its own material without + * touching the geometry. A model with no `usemtl` directives produces + * a single group with `materialName: null`. * - * Parsed but ignored: `vn` (normals), `g` (groups), `usemtl` (material assignment), - * `s` (smooth shading), `o` (object name). + * Parsed but ignored: `vn` (normals), `g` (groups), `s` (smooth shading), + * `o` (object name). * * @param {string} text - raw OBJ file contents - * @returns {object} parsed geometry with `vertices` (Float32Array), `uvs` (Float32Array), - * `indices` (Uint16Array), `vertexCount` (number), and `mtllib` (string|null) + * @returns {object} parsed geometry with `vertices` (Float32Array), + * `uvs` (Float32Array), `indices` (Uint16Array), `vertexCount` (number), + * `mtllib` (string|null), and `groups` + * (Array<{materialName: string|null, start: number, count: number}>). + * `groups` follows the Three.js / glTF convention — each entry is a + * contiguous slice of the shared `indices` buffer that draws as one + * submesh against a single material. Single-material models still + * produce a `groups` array of length 1, so consumers don't need a + * special case. * @ignore */ function parseOBJ(text) { @@ -47,13 +60,24 @@ function parseOBJ(text) { const texcoords = []; // unified output arrays (built in a single pass) - const vertexMap = new Map(); const vertices = []; const uvs = []; const indices = []; let vertexCount = 0; - // helper: look up or create a unified vertex for a v/vt pair + // Per-material vertex dedup: each material name owns its own + // `vertexMap`, so the same (v, vt) reused across different + // materials produces SEPARATE unified vertices (needed for + // per-vertex color baking in `Mesh`), but the same material + // reappearing in a later `usemtl` block re-uses its existing + // vertex slots. Pre-usemtl faces use the `null` map (the + // "anonymous" group). + const materialMaps = new Map(); + materialMaps.set(null, new Map()); + let vertexMap = materialMaps.get(null); + + // helper: look up or create a unified vertex for a v/vt pair in the + // current material's dedup scope function addVertex(v, vt) { const key = v * VT_KEY_MULTIPLIER + (vt + OBJ_INDEX_OFFSET); let index = vertexMap.get(key); @@ -89,6 +113,43 @@ function parseOBJ(text) { // mtllib reference (if present) let mtllib = null; + // Material grouping. Each `usemtl` switch closes the running group + // (recording its index count) and opens a new one. Models without + // any `usemtl` produce a single group spanning all indices with + // `materialName: null` — consumers can treat that uniformly with + // the multi-material path. Field name `materialName` matches the + // Three.js / glTF convention for "name of the material this + // submesh wants to be drawn with"; renderers / mesh objects look + // it up in their own material table. + const groups = []; + const startGroup = (materialName) => { + // close the previous group if it has any indices + const prev = groups[groups.length - 1]; + if (prev) { + prev.count = indices.length - prev.start; + } else if (indices.length > 0) { + // pre-usemtl indices belong to an anonymous group + groups.push({ + materialName: null, + start: 0, + count: indices.length, + }); + } + groups.push({ materialName, start: indices.length, count: 0 }); + // Swap to this material's vertex dedup scope. Vertices shared + // across materials get separate slots (required for per-vertex + // color baking in `Mesh`), but vertices reused within the same + // material — even across non-contiguous `usemtl` blocks — hit + // the cache and don't get duplicated. Lazily allocated per + // material name on first switch. + let cached = materialMaps.get(materialName); + if (cached === undefined) { + cached = new Map(); + materialMaps.set(materialName, cached); + } + vertexMap = cached; + }; + // parse lines and build geometry in a single pass const lines = text.split("\n"); for (let i = 0; i < lines.length; i++) { @@ -98,9 +159,20 @@ function parseOBJ(text) { } const first = line[0]; - if (first === "m" && line.startsWith("mtllib ")) { - mtllib = line.substring(7).trim(); - continue; + // tokenize once for keyword + argument lookup. The v/vt/f + // paths below also split on `\s+`, so this is just hoisting + // the same parse — handles tabs and multiple-space separators + // consistently across every line type. + if (first === "m" || first === "u") { + const parts = line.split(/\s+/); + if (parts[0] === "mtllib") { + mtllib = parts.slice(1).join(" "); + continue; + } + if (parts[0] === "usemtl") { + startGroup(parts.slice(1).join(" ")); + continue; + } } if (first === VERTEX_PREFIX) { const parts = line.split(/\s+/); @@ -164,12 +236,25 @@ function parseOBJ(text) { } } + // finalize the last open group (or, if no `usemtl` was ever seen, + // emit a single material-less group covering all indices so the + // `groups[]` contract is always non-empty for non-empty OBJs) + if (groups.length === 0) { + if (indices.length > 0) { + groups.push({ materialName: null, start: 0, count: indices.length }); + } + } else { + const last = groups[groups.length - 1]; + last.count = indices.length - last.start; + } + return { vertices: new Float32Array(vertices), uvs: new Float32Array(uvs), indices: new Uint16Array(indices), vertexCount, mtllib, + groups, }; } diff --git a/packages/melonjs/src/renderable/mesh.js b/packages/melonjs/src/renderable/mesh.js index f7ea5348b..5bbcf5a27 100644 --- a/packages/melonjs/src/renderable/mesh.js +++ b/packages/melonjs/src/renderable/mesh.js @@ -1,6 +1,7 @@ import { game } from "../application/application.ts"; import { Polygon } from "../geometries/polygon.ts"; import { getImage, getMTL, getOBJ } from "./../loader/loader.js"; +import { Color } from "../math/color.ts"; import { Matrix3d } from "../math/matrix3d.ts"; import { Vector2d } from "../math/vector2d.ts"; import { @@ -8,6 +9,7 @@ import { normalizeVertices, projectVertices, } from "../math/vertex.ts"; +import Renderer from "./../video/renderer.js"; import { TextureAtlas } from "./../video/texture/atlas.js"; import Renderable from "./renderable.js"; @@ -20,6 +22,61 @@ import Renderable from "./renderable.js"; // reusable matrix for combining projection × model in draw() const _combinedMatrix = new Matrix3d(); +// Resolve any acceptable texture input (TextureAtlas, image / canvas +// object, or asset name) to a cached `TextureAtlas`. Throws if nothing +// resolves — Mesh requires a texture binding for its GL pipeline. +function resolveTextureAtlas(src) { + if (src instanceof TextureAtlas) { + return src; + } + const image = typeof src === "object" ? src : getImage(src); + if (!image) { + throw new Error("Mesh: '" + src + "' image/texture not found!"); + } + return game.renderer.cache.get(image, { + framewidth: image.width, + frameheight: image.height, + }); +} + +/** + * Resolve an OBJ material group into a draw descriptor. Builds the + * group's tint from the MTL's `Kd` (defaults to white if missing) and + * its opacity from `d`. Returns a self-contained record carrying just + * the index slice + color state — per-material textures (`map_Kd`) + * are NOT modeled because the mesh shader uses a single `uSampler` + * binding shared across the whole mesh; only colors are baked + * per-vertex. + * @param {{materialName: string|null, start: number, count: number}} group + * @param {object} materials - MTL material table keyed by material name + * @returns {{materialName: string|null, start: number, count: number, tint: Color, opacity: number}} draw descriptor for this group + * @ignore + */ +function resolveGroupMaterial(group, materials) { + const mat = group.materialName ? materials[group.materialName] : null; + const tint = new Color(255, 255, 255, 1); + let opacity = 1; + if (mat) { + if (mat.Kd) { + tint.setColor( + Math.round(mat.Kd[0] * 255), + Math.round(mat.Kd[1] * 255), + Math.round(mat.Kd[2] * 255), + ); + } + if (typeof mat.d === "number" && mat.d < 1) { + opacity = mat.d; + } + } + return { + materialName: group.materialName, + start: group.start, + count: group.count, + tint, + opacity, + }; +} + /** * A renderable object for displaying textured triangle meshes. * Supports loading from Wavefront OBJ models (via `loader.preload` with type "obj") @@ -70,6 +127,7 @@ export default class Mesh extends Renderable { super(x, y, settings.width, settings.height); // load geometry from OBJ model or raw data + let objGroups = null; if (typeof settings.model === "string") { const objData = getOBJ(settings.model); if (!objData) { @@ -98,6 +156,11 @@ export default class Mesh extends Renderable { * @type {number} */ this.vertexCount = objData.vertexCount; + + // pick up the material-group list emitted by the OBJ parser + // (always non-null for non-empty models thanks to the + // parser's "anonymous group" fallback) + objGroups = objData.groups; } else { this.originalVertices = settings.vertices instanceof Float32Array @@ -129,56 +192,136 @@ export default class Mesh extends Renderable { this.cullBackFaces = settings.cullBackFaces !== undefined ? settings.cullBackFaces : true; - // resolve material (MTL) — applies texture, tint, and opacity + // resolve material (MTL) — applies texture, tint, and opacity. + // Two paths: + // - Single-material: pick the first MTL entry, apply to the whole + // mesh (legacy behavior, unchanged). + // - Multi-material: build a per-group descriptor with each + // group's texture, tint, and opacity resolved from its own MTL + // entry. `draw()` later iterates the groups and swaps state + // per draw. let textureSource = settings.texture; - if (typeof settings.material === "string") { - const materials = getMTL(settings.material); - if (materials) { - // use the first material's properties - const mat = materials[Object.keys(materials)[0]]; - if (mat) { - // auto-resolve texture from map_Kd if no explicit texture - if (!textureSource && mat.map_Kd) { + const materials = + typeof settings.material === "string" ? getMTL(settings.material) : null; + const isMultiMaterial = + materials !== null && + objGroups !== null && + objGroups.length > 1 && + objGroups.some((g) => { + return g.materialName !== null; + }); + + if (isMultiMaterial) { + /** + * Per-material submesh groups, populated when the OBJ + * contains multiple `usemtl` directives AND a matching MTL + * is bound via the `material` setting. Each entry slices + * the shared `indices` buffer; field shape (`start`, + * `count`, `materialName`) matches the Three.js / glTF + * "groups" convention. + * + * Under the per-vertex color baking path (tier 2), the + * `tint` / `opacity` fields here are informational — the + * actual rendered color is baked into `vertexColors` at + * construction time. Mutating `groups[i].tint` after + * construction has no visible effect; use `mesh.tint` for + * runtime color multiplication, or rebuild the Mesh with + * new material settings. + * @type {Array<{materialName: string|null, start: number, + * count: number, tint: Color, opacity: number}>} + */ + this.groups = objGroups.map((g) => { + return resolveGroupMaterial(g, materials); + }); + // `this.tint` stays at its default (white) — every + // material's Kd is already baked into `vertexColors` below, + // so applying the first group's tint globally would double- + // multiply it onto every vertex at render time. The + // renderer-level `setTint` path on top of the baked colors + // is still available for runtime flash / fade / team color. + // Per-material `map_Kd` textures are not switched at draw + // time (mesh shader has a single `uSampler`); pick up the + // first material's `map_Kd` for the shared texture binding + // if any group has one, else fall through to the white- + // pixel fallback further down. + if (!textureSource) { + for (const g of objGroups) { + const mat = g.materialName ? materials[g.materialName] : null; + if (mat && mat.map_Kd) { textureSource = mat.map_Kd; - } - // apply diffuse color as tint - if (mat.Kd) { - this.tint.setColor( - Math.round(mat.Kd[0] * 255), - Math.round(mat.Kd[1] * 255), - Math.round(mat.Kd[2] * 255), - ); - } - // apply opacity - if (mat.d < 1.0) { - this.setOpacity(mat.d); + break; } } } - } - // resolve texture - if (textureSource instanceof TextureAtlas) { /** - * the texture atlas used by this mesh - * @type {TextureAtlas} + * Per-vertex color buffer (one packed Uint32 per vertex) + * populated for multi-material meshes. The mesh batcher + * reads from this when present, pushing the per-vertex + * color as the `aColor` attribute — so multi-material + * rendering needs no extra draw calls per material vs + * single-material rendering (the batcher still chunks + * very large meshes across multiple draws to fit its + * vertex/index buffer limits, same as the single-material + * path). Multiplied at render time by the global + * `mesh.tint`, so runtime tint mutation still works as + * expected (flash, fade, team color, etc.). + * + * Vertices were split per-material at parse time (each + * material has its own dedup scope in the OBJ parser), so + * every vertex belongs to exactly one material group and + * carries that group's color unambiguously. + * @type {Uint32Array} */ - this.texture = textureSource; - } else { - const image = - typeof textureSource === "object" - ? textureSource - : getImage(textureSource); - if (!image) { - throw new Error( - "Mesh: '" + textureSource + "' image/texture not found!", - ); + this.vertexColors = new Uint32Array(this.vertexCount); + for (const g of this.groups) { + const c = g.tint.toUint32(g.opacity); + const end = g.start + g.count; + for (let i = g.start; i < end; i++) { + this.vertexColors[this.indices[i]] = c; + } } - this.texture = game.renderer.cache.get(image, { - framewidth: image.width, - frameheight: image.height, - }); + } else if (materials) { + // Single-material path. Prefer the MTL entry whose name + // matches the OBJ's `usemtl` directive — an OBJ with one + // `usemtl jet_body` referencing a `.mtl` that defines + // `jet_body` alongside other materials should pick + // `jet_body`, not whichever entry happens to be first in + // the MTL parser's output. Fall back to the first entry + // when there's no `usemtl` at all (the parser emits a + // `materialName: null` anonymous group in that case). + const namedMaterial = + objGroups !== null && objGroups[0] && objGroups[0].materialName + ? materials[objGroups[0].materialName] + : null; + const mat = namedMaterial ?? materials[Object.keys(materials)[0]]; + if (mat) { + if (!textureSource && mat.map_Kd) { + textureSource = mat.map_Kd; + } + if (mat.Kd) { + this.tint.setColor( + Math.round(mat.Kd[0] * 255), + Math.round(mat.Kd[1] * 255), + Math.round(mat.Kd[2] * 255), + ); + } + if (mat.d < 1.0) { + this.setOpacity(mat.d); + } + } + } + + // resolve texture. Fall back to the shared 1×1 white pixel when + // no texture source was resolved — covers Kd-only multi-material + // models (Kenney style) AND single-material meshes constructed + // without a `texture:` or a `map_Kd`-bearing `material:` (the + // GPU pipeline still needs something to sample; tint / per- + // vertex color does the actual coloring). + if (!textureSource) { + textureSource = Renderer.getWhitePixel(); } + this.texture = resolveTextureAtlas(textureSource); /** * Projection matrix applied automatically before the model transform in draw(). @@ -236,7 +379,15 @@ export default class Mesh extends Renderable { /** * Draw the mesh (automatically called by melonJS). - * Projects vertices through projectionMatrix × currentTransform and calls renderer.drawMesh(). + * Projects vertices through `projectionMatrix × currentTransform` + * and hands the mesh off to `renderer.drawMesh()`. Multi-material + * meshes need no extra `drawMesh` calls per material vs single- + * material — each material's diffuse color is baked into + * `vertexColors` at construction time and pushed through the + * renderer's per-vertex `aColor` (WebGL) or per-triangle solid- + * fill (Canvas) path. The WebGL batcher may still chunk very + * large meshes across multiple `drawElements` to fit its + * vertex/index buffer limits, same as the single-material path. * @param {CanvasRenderer|WebGLRenderer} renderer - a renderer instance */ draw(renderer) { diff --git a/packages/melonjs/src/video/canvas/canvas_renderer.js b/packages/melonjs/src/video/canvas/canvas_renderer.js index e5428170c..4cf3d9dae 100644 --- a/packages/melonjs/src/video/canvas/canvas_renderer.js +++ b/packages/melonjs/src/video/canvas/canvas_renderer.js @@ -392,12 +392,22 @@ export default class CanvasRenderer extends Renderer { /** * Draw a textured triangle mesh. - * Uses per-triangle affine texture mapping with back-to-front depth sorting - * (painter's algorithm) and optional backface culling. - * Note: the painter's algorithm works well for convex shapes but may produce - * visual artifacts with concave or self-overlapping geometry (e.g. a torus), - * as Canvas 2D has no hardware depth buffer. Use the WebGL renderer for - * correct depth ordering on complex meshes. + * Uses per-triangle affine texture mapping with back-to-front depth + * sorting (painter's algorithm) and optional backface culling. + * Note: the painter's algorithm works well for convex shapes but + * may produce visual artifacts with concave or self-overlapping + * geometry (e.g. a torus), as Canvas 2D has no hardware depth + * buffer. Use the WebGL renderer for correct depth ordering on + * complex meshes. + * + * Multi-material meshes (`mesh.vertexColors` present) render here + * via per-triangle solid fill — each triangle's three vertices + * share one baked material color, so we read `vertexColors[v0]` + * and use it as the triangle's fillStyle, multiplied by + * `currentTint`. The single shared texture (typically the 1×1 + * white-pixel fallback for Kd-only models) is bypassed in that + * path. Canvas can't do per-material textures; if you need that, + * use the WebGL renderer. * @param {Mesh} mesh - a Mesh renderable or compatible object */ drawMesh(mesh) { @@ -408,15 +418,50 @@ export default class CanvasRenderer extends Renderer { const vertices = mesh.vertices; const uvs = mesh.uvs; const indices = mesh.indices; + const vertexColors = mesh.vertexColors; - // apply tint if set + // apply tint if set. When `vertexColors` is present the solid- + // fill path below reads color from the baked buffer per + // triangle and never samples the image, so skip the tint cache + // allocation entirely in that case. let image = mesh.texture.getTexture(); const tint = this.currentTint.toArray(); - if (tint[0] !== 1.0 || tint[1] !== 1.0 || tint[2] !== 1.0) { + if ( + !vertexColors && + (tint[0] !== 1.0 || tint[1] !== 1.0 || tint[2] !== 1.0) + ) { image = this.cache.tint(image, this.currentTint.toRGB()); } const imgW = image.width; const imgH = image.height; + // Solid-fill fast path: kicks in for either (a) per-vertex + // color meshes (multi-material) where each triangle's color + // comes from the baked `vertexColors`, or (b) Kd-only single- + // material meshes where the texture is the 1×1 white-pixel + // fallback. Both bypass Canvas's per-triangle affine drawImage + // (which produces sub-pixel artifacts mapping a 1×1 source + // onto large triangles) and fall back to `fill()`. + const solidFillKd = imgW === 1 && imgH === 1; + const solidFill = solidFillKd || vertexColors !== undefined; + // pre-extract tint as 0..1 floats for per-vertex color modulation + const tintR = tint[0]; + const tintG = tint[1]; + const tintB = tint[2]; + let solidFillStyle = null; + if (solidFillKd && !vertexColors) { + // Single-material 1×1 path — one fill color for the whole + // mesh, sampled from the pre-tinted image. Routes through + // `Renderer.createCanvas` for `OffscreenCanvas` / worker + // safety. + if (!this._meshColorCanvas) { + this._meshColorCanvas = Renderer.createCanvas(1, 1, true); + this._meshColorCtx = this._meshColorCanvas.getContext("2d"); + } + this._meshColorCtx.clearRect(0, 0, 1, 1); + this._meshColorCtx.drawImage(image, 0, 0); + const pixel = this._meshColorCtx.getImageData(0, 0, 1, 1).data; + solidFillStyle = `rgb(${pixel[0]},${pixel[1]},${pixel[2]})`; + } const cullBack = mesh.cullBackFaces === true; const triCount = indices.length / 3; @@ -519,14 +564,38 @@ export default class CanvasRenderer extends Renderer { y2 + (y2 > cy ? 0.5 : y2 < cy ? -0.5 : 0), ); - if (rawDet === 0) { - // degenerate UV triangle — sample a solid color from the texture - // (common with color-palette models where all 3 UVs map to the same point) + if (solidFill) { + // Solid-fill path. Either the texture is 1×1 (Kd-only + // single-material — `solidFillStyle` is pre-computed) or + // the mesh has per-vertex baked colors (multi-material + // — read color from `vertexColors[v0]` since all 3 + // vertices of a triangle share a material color). + context.closePath(); + if (vertexColors) { + // ARGB-packed: A R G B in 4 bytes MSB→LSB. Carries + // the per-material opacity (MTL `d`) baked at + // construction time — emit as `rgba(...)` so it + // reaches the canvas alpha channel rather than + // silently dropping to fully opaque. + const c = vertexColors[indices[j]]; + const cr = Math.round(((c >>> 16) & 0xff) * tintR); + const cg = Math.round(((c >>> 8) & 0xff) * tintG); + const cb = Math.round((c & 0xff) * tintB); + const ca = ((c >>> 24) & 0xff) / 255; + context.fillStyle = `rgba(${cr},${cg},${cb},${ca})`; + } else { + context.fillStyle = solidFillStyle; + } + context.fill(); + } else if (rawDet === 0) { + // degenerate UV triangle — sample a solid color from + // the texture (common with color-palette models where + // all 3 UVs map to the same point). Routes through + // `Renderer.createCanvas` for worker / `OffscreenCanvas` + // safety, matching the `solidFillKd` path above. context.closePath(); if (!this._meshColorCanvas) { - this._meshColorCanvas = document.createElement("canvas"); - this._meshColorCanvas.width = 1; - this._meshColorCanvas.height = 1; + this._meshColorCanvas = Renderer.createCanvas(1, 1, true); this._meshColorCtx = this._meshColorCanvas.getContext("2d"); } const sx = Math.min(Math.max(Math.round(u0), 0), imgW - 1); diff --git a/packages/melonjs/src/video/canvas_factory.js b/packages/melonjs/src/video/canvas_factory.js new file mode 100644 index 000000000..f0a4d4b24 --- /dev/null +++ b/packages/melonjs/src/video/canvas_factory.js @@ -0,0 +1,45 @@ +import * as device from "../system/device.js"; + +/** + * Create and return a new Canvas element (or `OffscreenCanvas` when + * supported and `returnOffscreenCanvas` is true). The single low-level + * allocator used by every renderer-side scratch / fallback / render- + * target canvas in the engine. + * + * Lives in its own module (not on `Renderer` directly) to break the + * circular dependency between `Renderer` and `CanvasRenderTarget` — + * both can import this helper without referencing each other. + * `Renderer.createCanvas` re-exposes it as a static method for the + * public-facing API; internal callers can import it directly. + * + * Gates the OffscreenCanvas path on `device.offscreenCanvas` (the + * vetted capability check — actually instantiates + verifies + * `getContext("2d")` works inside a try/catch, covering historical + * Safari quirks). + * @param {number} width - canvas width in pixels + * @param {number} height - canvas height in pixels + * @param {boolean} [returnOffscreenCanvas=false] - return an + * `OffscreenCanvas` if the platform supports it + * @returns {HTMLCanvasElement|OffscreenCanvas} a new canvas of the given size + * @ignore + */ +export function createCanvas(width, height, returnOffscreenCanvas = false) { + if (width === 0 || height === 0) { + throw new Error( + "width or height was zero, Canvas could not be initialized !", + ); + } + if (returnOffscreenCanvas === true && device.offscreenCanvas === true) { + const c = new globalThis.OffscreenCanvas(width, height); + // stub `style` for compatibility — OffscreenCanvas is detached + // from the DOM but downstream code may read it + if (typeof c.style === "undefined") { + c.style = {}; + } + return c; + } + const c = globalThis.document.createElement("canvas"); + c.width = width; + c.height = height; + return c; +} diff --git a/packages/melonjs/src/video/renderer.js b/packages/melonjs/src/video/renderer.js index 5b4242d20..93fa3e33d 100644 --- a/packages/melonjs/src/video/renderer.js +++ b/packages/melonjs/src/video/renderer.js @@ -3,6 +3,7 @@ import { Color } from "./../math/color.ts"; import { Matrix3d } from "../math/matrix3d.ts"; import { Vector2d } from "../math/vector2d.ts"; import { CANVAS_ONRESIZE, emit } from "../system/event.ts"; +import { createCanvas } from "./canvas_factory.js"; import { Gradient } from "./gradient.js"; import RenderState from "./renderstate.js"; import CanvasRenderTarget from "./rendertarget/canvasrendertarget.js"; @@ -272,6 +273,52 @@ export default class Renderer { return this.renderTarget.canvas; } + /** + * Create and return a new Canvas element (or `OffscreenCanvas` when + * supported and `returnOffscreenCanvas` is true). Centralized + * renderer-side allocator so every scratch / fallback / render- + * target canvas in the engine routes through the same + * `OffscreenCanvas`-aware path, instead of duplicating + * `document.createElement` calls that throw in worker contexts. + * @param {number} width - canvas width in pixels + * @param {number} height - canvas height in pixels + * @param {boolean} [returnOffscreenCanvas=false] - return an + * `OffscreenCanvas` if the platform supports it + * @returns {HTMLCanvasElement|OffscreenCanvas} a new canvas of the given size + */ + static createCanvas(width, height, returnOffscreenCanvas = false) { + return createCanvas(width, height, returnOffscreenCanvas); + } + + /** + * Shared 1×1 fully-white canvas used as a no-op texture fallback. + * Renderers and renderables that need a "blank" texture binding + * (e.g. to satisfy a shader's sampler input when there's no real + * image — Kd-only `Mesh` materials, solid-color quad fills, etc.) + * should use this rather than allocating their own. + * + * Lazily created on first call; shared across every caller; uses + * `OffscreenCanvas` where supported (worker-safe). Static so it's + * accessible without a renderer instance (e.g. from a `Mesh` + * constructor that runs before the active renderer is set). + * @returns {HTMLCanvasElement|OffscreenCanvas} the shared 1×1 white canvas + */ + static getWhitePixel() { + if (Renderer._whitePixel === null) { + const c = Renderer.createCanvas(1, 1, true); + const ctx = c.getContext("2d"); + if (ctx === null) { + throw new Error( + "Renderer.getWhitePixel: 2D context unavailable on the allocated canvas", + ); + } + ctx.fillStyle = "#ffffff"; + ctx.fillRect(0, 0, 1, 1); + Renderer._whitePixel = c; + } + return Renderer._whitePixel; + } + /** * return a reference to the current render target corresponding Context * @returns {CanvasRenderingContext2D|WebGLRenderingContext} @@ -1012,3 +1059,8 @@ export default class Renderer { return this.renderTarget.toDataURL(type, quality); } } + +// Backing field for `Renderer.getWhitePixel()` — declared outside the +// class body so it's initialized to null at module load (static class +// fields aren't universally supported in our transpile target). +Renderer._whitePixel = null; diff --git a/packages/melonjs/src/video/rendertarget/canvasrendertarget.js b/packages/melonjs/src/video/rendertarget/canvasrendertarget.js index 7c0dd0cad..ba07deeea 100644 --- a/packages/melonjs/src/video/rendertarget/canvasrendertarget.js +++ b/packages/melonjs/src/video/rendertarget/canvasrendertarget.js @@ -1,6 +1,6 @@ import { clamp } from "../../math/math.ts"; import { setPrefixed } from "../../utils/agent.ts"; -import { createCanvas } from "../video.js"; +import { createCanvas } from "../canvas_factory.js"; import RenderTarget from "./rendertarget.ts"; /** diff --git a/packages/melonjs/src/video/video.js b/packages/melonjs/src/video/video.js index b9ca5c0b7..5bbfa3fcc 100644 --- a/packages/melonjs/src/video/video.js +++ b/packages/melonjs/src/video/video.js @@ -1,8 +1,9 @@ import { game } from "../application/application.ts"; import { defaultApplicationSettings } from "../application/defaultApplicationSettings.ts"; +import { warning } from "../lang/console.js"; import { initialized } from "../system/bootstrap.ts"; -import * as device from "./../system/device.js"; import { on, VIDEO_INIT } from "../system/event.ts"; +import Renderer from "./renderer.js"; /** * @namespace video @@ -84,37 +85,18 @@ export function init(width, height, options) { } /** - * Create and return a new Canvas element + * Create and return a new Canvas element. * @memberof video * @param {number} width - width * @param {number} height - height * @param {boolean} [returnOffscreenCanvas=false] - will return an OffscreenCanvas if supported * @returns {HTMLCanvasElement|OffscreenCanvas} a new Canvas element of the given size + * @deprecated since 19.7.0 — use {@link Renderer.createCanvas} instead. + * @see Renderer.createCanvas */ export function createCanvas(width, height, returnOffscreenCanvas = false) { - let _canvas; - - if (width === 0 || height === 0) { - throw new Error( - "width or height was zero, Canvas could not be initialized !", - ); - } - - if (device.offscreenCanvas === true && returnOffscreenCanvas === true) { - _canvas = new globalThis.OffscreenCanvas(0, 0); - // stubbing style for compatibility, - // as OffscreenCanvas is detached from the DOM - if (typeof _canvas.style === "undefined") { - _canvas.style = {}; - } - } else { - // "else" create a "standard" canvas - _canvas = globalThis.document.createElement("canvas"); - } - _canvas.width = width; - _canvas.height = height; - - return _canvas; + warning("video.createCanvas", "Renderer.createCanvas", "19.7.0"); + return Renderer.createCanvas(width, height, returnOffscreenCanvas); } /** diff --git a/packages/melonjs/src/video/webgl/batchers/mesh_batcher.js b/packages/melonjs/src/video/webgl/batchers/mesh_batcher.js index 970dff847..087821d55 100644 --- a/packages/melonjs/src/video/webgl/batchers/mesh_batcher.js +++ b/packages/melonjs/src/video/webgl/batchers/mesh_batcher.js @@ -6,6 +6,33 @@ import { MaterialBatcher } from "./material_batcher.js"; // reusable vector for vertex transform const _v = new Vector2d(); +/** + * Per-channel multiply two ARGB-packed Uint32 colors. Used by the + * multi-material mesh path to combine a vertex's baked material color + * (`mesh.vertexColors[i]`) with the runtime `mesh.tint` before + * pushing the result as the vertex's `aColor` attribute. + * Layout (MSB→LSB): A R G B, matching `Color.toUint32`. + * @param {number} a - first ARGB packed Uint32 + * @param {number} b - second ARGB packed Uint32 + * @returns {number} their per-channel product (normalized in 0..255) + * @ignore + */ +function mulPackedARGB(a, b) { + const aa = (a >>> 24) & 0xff; + const ar = (a >>> 16) & 0xff; + const ag = (a >>> 8) & 0xff; + const ab = a & 0xff; + const ba = (b >>> 24) & 0xff; + const br = (b >>> 16) & 0xff; + const bg = (b >>> 8) & 0xff; + const bb = b & 0xff; + const cr = ((ar * br) / 255) | 0; + const cg = ((ag * bg) / 255) | 0; + const cb = ((ab * bb) / 255) | 0; + const ca = ((aa * ba) / 255) | 0; + return ((ca << 24) | (cr << 16) | (cg << 8) | cb) >>> 0; +} + /** * A WebGL Batcher for rendering textured triangle meshes. * Uses indexed drawing to efficiently render arbitrary triangle geometry. @@ -50,7 +77,17 @@ export default class MeshBatcher extends MaterialBatcher { } /** - * Add a textured mesh to the batch + * Add a textured mesh to the batch. When the mesh has a + * `vertexColors` array (multi-material OBJ + bound MTL), each + * vertex's `aColor` attribute comes from that buffer instead of + * the shared `tint` argument — so multi-material rendering needs + * no extra draw calls per material vs single-material (large + * meshes still get chunked across multiple flushes to fit the + * vertex/index buffer limits — same behavior as single-material). + * The shared `tint` is then multiplied into each vertex color + * CPU-side (via `mulPackedARGB`, before `pushMesh`), preserving + * runtime flash / fade / team-color effects — the mesh shader + * itself just does `texture * aColor`, no extra uniform. * @param {object} mesh - a Mesh object with vertices, uvs, indices, and texture properties * @param {number} tint - tint color in UINT32 (argb) format */ @@ -58,6 +95,7 @@ export default class MeshBatcher extends MaterialBatcher { const vertices = mesh.vertices; const uvs = mesh.uvs; const indices = mesh.indices; + const vertexColors = mesh.vertexColors; // upload and activate the texture const unit = this.uploadTexture(mesh.texture); @@ -118,7 +156,16 @@ export default class MeshBatcher extends MaterialBatcher { y = _v.y; } - vertexData.pushMesh(x, y, z, uvs[i2], uvs[i2 + 1], tint); + // per-vertex color when the mesh provides one + // (multi-material baked colors), modulated by the + // runtime `tint` so flash / fade / team color via + // `setTint` still works on top of the baked palette. + // Single-material meshes (no `vertexColors`) fall + // back to the shared `tint` for every vertex. + const vertColor = vertexColors + ? mulPackedARGB(vertexColors[origIdx], tint) + : tint; + vertexData.pushMesh(x, y, z, uvs[i2], uvs[i2 + 1], vertColor); } // absolute index = baseOffset + localIdx chunkIndices.push(baseOffset + localIdx); diff --git a/packages/melonjs/src/video/webgl/webgl_renderer.js b/packages/melonjs/src/video/webgl/webgl_renderer.js index 81a442963..dd3ded432 100644 --- a/packages/melonjs/src/video/webgl/webgl_renderer.js +++ b/packages/melonjs/src/video/webgl/webgl_renderer.js @@ -1217,9 +1217,19 @@ export default class WebGLRenderer extends Renderer { /** * Draw a textured triangle mesh. - * Enables hardware depth testing and backface culling for the duration of the draw, - * then restores the previous GL state. Large meshes are automatically chunked - * across multiple draw calls to fit the vertex/index buffer limits. + * Enables hardware depth testing and backface culling for the + * duration of the draw, then restores the previous GL state. Large + * meshes are automatically chunked across multiple draw calls to + * fit the vertex/index buffer limits. + * + * Multi-material meshes (OBJ files with multiple `usemtl` groups + + * a bound MTL) don't add any per-material draw calls here — the + * per-material colors are baked into `mesh.vertexColors` at + * construction time and pushed through the batcher's per-vertex + * `aColor` attribute. `mesh.tint` still multiplies on top at draw + * time, so flash / fade / team-color via `setTint` work the same + * as single-material. (The chunking-for-buffer-limits behavior + * above still applies to multi-material meshes as well.) * @param {Mesh} mesh - a Mesh renderable or compatible object */ drawMesh(mesh) { diff --git a/packages/melonjs/tests/mesh.spec.js b/packages/melonjs/tests/mesh.spec.js index d2515420c..205e510f6 100644 --- a/packages/melonjs/tests/mesh.spec.js +++ b/packages/melonjs/tests/mesh.spec.js @@ -259,6 +259,128 @@ describe("OBJ Parser", () => { expect(obj.vertexCount).toBeGreaterThan(0); expect(obj.indices.length).toBeGreaterThan(0); }); + + // ── multi-material groups (Three.js / glTF "groups" convention) ────── + + it("emits a single material-less group for OBJs with no usemtl", async () => { + const obj = await parseOBJString(` + v 0 0 0 + v 1 0 0 + v 1 1 0 + f 1 2 3 + `); + expect(Array.isArray(obj.groups)).toBe(true); + expect(obj.groups.length).toBe(1); + expect(obj.groups[0].materialName).toBe(null); + expect(obj.groups[0].start).toBe(0); + expect(obj.groups[0].count).toBe(obj.indices.length); + }); + + it("emits one group per usemtl directive, slicing the index buffer", async () => { + const obj = await parseOBJString(` + v 0 0 0 + v 1 0 0 + v 1 1 0 + v 0 1 0 + v 2 0 0 + v 2 1 0 + usemtl red + f 1 2 3 + usemtl blue + f 1 3 4 + usemtl green + f 2 5 6 + `); + expect(obj.groups.length).toBe(3); + expect(obj.groups[0].materialName).toBe("red"); + expect(obj.groups[0].start).toBe(0); + expect(obj.groups[0].count).toBe(3); + expect(obj.groups[1].materialName).toBe("blue"); + expect(obj.groups[1].start).toBe(3); + expect(obj.groups[1].count).toBe(3); + expect(obj.groups[2].materialName).toBe("green"); + expect(obj.groups[2].start).toBe(6); + expect(obj.groups[2].count).toBe(3); + // every index is covered by exactly one group + const total = obj.groups.reduce((s, g) => { + return s + g.count; + }, 0); + expect(total).toBe(obj.indices.length); + }); + + it("emits an anonymous null-material group for faces declared before any usemtl", async () => { + const obj = await parseOBJString(` + v 0 0 0 + v 1 0 0 + v 1 1 0 + v 0 1 0 + f 1 2 3 + usemtl red + f 1 3 4 + `); + // 2 groups: the anonymous pre-usemtl chunk + the explicit "red" + expect(obj.groups.length).toBe(2); + expect(obj.groups[0].materialName).toBe(null); + expect(obj.groups[0].count).toBe(3); + expect(obj.groups[1].materialName).toBe("red"); + expect(obj.groups[1].count).toBe(3); + }); + + it("handles triangulated quads inside a material group", async () => { + const obj = await parseOBJString(` + v 0 0 0 + v 1 0 0 + v 1 1 0 + v 0 1 0 + usemtl panels + f 1 2 3 4 + `); + // quad → 2 triangles → 6 indices + expect(obj.groups.length).toBe(1); + expect(obj.groups[0].materialName).toBe("panels"); + expect(obj.groups[0].count).toBe(6); + expect(obj.indices.length).toBe(6); + }); + + it("group boundaries survive CW→CCW winding correction", async () => { + // CW winding (negative volume) gets flipped; the per-triangle + // index reorder must not move triangles between groups + const obj = await parseOBJString(` + v -1 -1 -1 + v -1 1 -1 + v 1 -1 -1 + v 1 1 -1 + usemtl front + f 1 3 4 + f 1 4 2 + `); + expect(obj.groups.length).toBe(1); + expect(obj.groups[0].materialName).toBe("front"); + // count is total indices, regardless of winding flip + expect(obj.groups[0].count).toBe(obj.indices.length); + }); + + it("empty OBJ produces an empty groups array (not null)", async () => { + const obj = await parseOBJString(``); + expect(Array.isArray(obj.groups)).toBe(true); + expect(obj.groups.length).toBe(0); + }); + + it("usemtl with no following faces leaves a zero-count group", async () => { + const obj = await parseOBJString(` + v 0 0 0 + v 1 0 0 + v 1 1 0 + usemtl red + f 1 2 3 + usemtl unused + `); + expect(obj.groups.length).toBe(2); + expect(obj.groups[0].materialName).toBe("red"); + expect(obj.groups[0].count).toBe(3); + expect(obj.groups[1].materialName).toBe("unused"); + expect(obj.groups[1].count).toBe(0); + }); }); // ── Matrix3d extensions ─────────────────────────────────────────────────────