From 9db39297502ad115ef990aa3603834f22e73819f Mon Sep 17 00:00:00 2001 From: irrlicht Date: Thu, 16 Jul 2026 19:14:42 +0200 Subject: [PATCH] Collider via collide-Property + Quickhull, OBJ-Pfad entfernt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit col_*-Namenspräfix raus: Rollen steuern nur noch Custom Properties (collide=1 sichtbar+Collider, "proxy" unsichtbar, signal wie gehabt). Collider sind die konvexe Hülle der Vertices — konkave Meshes erlaubt. glb ist der einzige Modell-Pfad; suzanne.obj → suzanne.glb. Co-Authored-By: Claude Fable 5 --- assets/maps/props/suzanne.glb | Bin 0 -> 69712 bytes assets/maps/props/suzanne.mtl | 2 - assets/maps/props/suzanne.obj | 2066 --------------------------------- src/engine/collision.rs | 304 +++-- src/engine/gltf.rs | 15 +- src/engine/mod.rs | 12 +- src/engine/model.rs | 94 +- src/engine/obj.rs | 242 ---- src/engine/trigger.rs | 20 +- src/render/mod.rs | 26 +- src/render/props.rs | 67 +- src/session.rs | 20 +- 12 files changed, 371 insertions(+), 2497 deletions(-) create mode 100644 assets/maps/props/suzanne.glb delete mode 100644 assets/maps/props/suzanne.mtl delete mode 100644 assets/maps/props/suzanne.obj delete mode 100644 src/engine/obj.rs diff --git a/assets/maps/props/suzanne.glb b/assets/maps/props/suzanne.glb new file mode 100644 index 0000000000000000000000000000000000000000..b54b942bdb1b4260fc9043defe30e61fe2cef564 GIT binary patch literal 69712 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 N9G9-_a+KY@{sH2IAjJRx literal 0 HcmV?d00001 diff --git a/assets/maps/props/suzanne.mtl b/assets/maps/props/suzanne.mtl deleted file mode 100644 index 25cbbf5..0000000 --- a/assets/maps/props/suzanne.mtl +++ /dev/null @@ -1,2 +0,0 @@ -# Blender 5.0.0 MTL File: 'None' -# www.blender.org diff --git a/assets/maps/props/suzanne.obj b/assets/maps/props/suzanne.obj deleted file mode 100644 index 975eb5c..0000000 --- a/assets/maps/props/suzanne.obj +++ /dev/null @@ -1,2066 +0,0 @@ -# Blender 5.0.0 -# www.blender.org -mtllib suzanne.mtl -o Suzanne -v 0.437500 0.164062 0.765625 -v -0.437500 0.164062 0.765625 -v 0.500000 0.093750 0.687500 -v -0.500000 0.093750 0.687500 -v 0.546875 0.054688 0.578125 -v -0.546875 0.054688 0.578125 -v 0.351562 -0.023438 0.617188 -v -0.351562 -0.023438 0.617188 -v 0.351562 0.031250 0.718750 -v -0.351562 0.031250 0.718750 -v 0.351562 0.132812 0.781250 -v -0.351562 0.132812 0.781250 -v 0.273438 0.164062 0.796875 -v -0.273438 0.164062 0.796875 -v 0.203125 0.093750 0.742188 -v -0.203125 0.093750 0.742188 -v 0.156250 0.054688 0.648438 -v -0.156250 0.054688 0.648438 -v 0.078125 0.242188 0.656250 -v -0.078125 0.242188 0.656250 -v 0.140625 0.242188 0.742188 -v -0.140625 0.242188 0.742188 -v 0.242188 0.242188 0.796875 -v -0.242188 0.242188 0.796875 -v 0.273438 0.328125 0.796875 -v -0.273438 0.328125 0.796875 -v 0.203125 0.390625 0.742188 -v -0.203125 0.390625 0.742188 -v 0.156250 0.437500 0.648438 -v -0.156250 0.437500 0.648438 -v 0.351562 0.515625 0.617188 -v -0.351562 0.515625 0.617188 -v 0.351562 0.453125 0.718750 -v -0.351562 0.453125 0.718750 -v 0.351562 0.359375 0.781250 -v -0.351562 0.359375 0.781250 -v 0.437500 0.328125 0.765625 -v -0.437500 0.328125 0.765625 -v 0.500000 0.390625 0.687500 -v -0.500000 0.390625 0.687500 -v 0.546875 0.437500 0.578125 -v -0.546875 0.437500 0.578125 -v 0.625000 0.242188 0.562500 -v -0.625000 0.242188 0.562500 -v 0.562500 0.242188 0.671875 -v -0.562500 0.242188 0.671875 -v 0.468750 0.242188 0.757812 -v -0.468750 0.242188 0.757812 -v 0.476562 0.242188 0.773438 -v -0.476562 0.242188 0.773438 -v 0.445312 0.335938 0.781250 -v -0.445312 0.335938 0.781250 -v 0.351562 0.375000 0.804688 -v -0.351562 0.375000 0.804688 -v 0.265625 0.335938 0.820312 -v -0.265625 0.335938 0.820312 -v 0.226562 0.242188 0.820312 -v -0.226562 0.242188 0.820312 -v 0.265625 0.156250 0.820312 -v -0.265625 0.156250 0.820312 -v 0.351562 0.242188 0.828125 -v -0.351562 0.242188 0.828125 -v 0.351562 0.117188 0.804688 -v -0.351562 0.117188 0.804688 -v 0.445312 0.156250 0.781250 -v -0.445312 0.156250 0.781250 -v 0.000000 0.429688 0.742188 -v 0.000000 0.351562 0.820312 -v 0.000000 -0.679688 0.734375 -v 0.000000 -0.320312 0.781250 -v 0.000000 -0.187500 0.796875 -v 0.000000 -0.773438 0.718750 -v 0.000000 0.406250 0.601562 -v 0.000000 0.570312 0.570312 -v 0.000000 0.898438 -0.546875 -v 0.000000 0.562500 -0.851562 -v 0.000000 0.070312 -0.828125 -v 0.000000 -0.382812 -0.351562 -v 0.203125 -0.187500 0.562500 -v -0.203125 -0.187500 0.562500 -v 0.312500 -0.437500 0.570312 -v -0.312500 -0.437500 0.570312 -v 0.351562 -0.695312 0.570312 -v -0.351562 -0.695312 0.570312 -v 0.367188 -0.890625 0.531250 -v -0.367188 -0.890625 0.531250 -v 0.328125 -0.945312 0.523438 -v -0.328125 -0.945312 0.523438 -v 0.179688 -0.968750 0.554688 -v -0.179688 -0.968750 0.554688 -v 0.000000 -0.984375 0.578125 -v 0.437500 -0.140625 0.531250 -v -0.437500 -0.140625 0.531250 -v 0.632812 -0.039062 0.539062 -v -0.632812 -0.039062 0.539062 -v 0.828125 0.148438 0.445312 -v -0.828125 0.148438 0.445312 -v 0.859375 0.429688 0.593750 -v -0.859375 0.429688 0.593750 -v 0.710938 0.484375 0.625000 -v -0.710938 0.484375 0.625000 -v 0.492188 0.601562 0.687500 -v -0.492188 0.601562 0.687500 -v 0.320312 0.757812 0.734375 -v -0.320312 0.757812 0.734375 -v 0.156250 0.718750 0.757812 -v -0.156250 0.718750 0.757812 -v 0.062500 0.492188 0.750000 -v -0.062500 0.492188 0.750000 -v 0.164062 0.414062 0.773438 -v -0.164062 0.414062 0.773438 -v 0.125000 0.304688 0.765625 -v -0.125000 0.304688 0.765625 -v 0.203125 0.093750 0.742188 -v -0.203125 0.093750 0.742188 -v 0.375000 0.015625 0.703125 -v -0.375000 0.015625 0.703125 -v 0.492188 0.062500 0.671875 -v -0.492188 0.062500 0.671875 -v 0.625000 0.187500 0.648438 -v -0.625000 0.187500 0.648438 -v 0.640625 0.296875 0.648438 -v -0.640625 0.296875 0.648438 -v 0.601562 0.375000 0.664062 -v -0.601562 0.375000 0.664062 -v 0.429688 0.437500 0.718750 -v -0.429688 0.437500 0.718750 -v 0.250000 0.468750 0.757812 -v -0.250000 0.468750 0.757812 -v 0.000000 -0.765625 0.734375 -v 0.109375 -0.718750 0.734375 -v -0.109375 -0.718750 0.734375 -v 0.117188 -0.835938 0.710938 -v -0.117188 -0.835938 0.710938 -v 0.062500 -0.882812 0.695312 -v -0.062500 -0.882812 0.695312 -v 0.000000 -0.890625 0.687500 -v 0.000000 -0.195312 0.750000 -v 0.000000 -0.140625 0.742188 -v 0.101562 -0.148438 0.742188 -v -0.101562 -0.148438 0.742188 -v 0.125000 -0.226562 0.750000 -v -0.125000 -0.226562 0.750000 -v 0.085938 -0.289062 0.742188 -v -0.085938 -0.289062 0.742188 -v 0.398438 -0.046875 0.671875 -v -0.398438 -0.046875 0.671875 -v 0.617188 0.054688 0.625000 -v -0.617188 0.054688 0.625000 -v 0.726562 0.203125 0.601562 -v -0.726562 0.203125 0.601562 -v 0.742188 0.375000 0.656250 -v -0.742188 0.375000 0.656250 -v 0.687500 0.414062 0.726562 -v -0.687500 0.414062 0.726562 -v 0.437500 0.546875 0.796875 -v -0.437500 0.546875 0.796875 -v 0.312500 0.640625 0.835938 -v -0.312500 0.640625 0.835938 -v 0.203125 0.617188 0.851562 -v -0.203125 0.617188 0.851562 -v 0.101562 0.429688 0.843750 -v -0.101562 0.429688 0.843750 -v 0.125000 -0.101562 0.812500 -v -0.125000 -0.101562 0.812500 -v 0.210938 -0.445312 0.710938 -v -0.210938 -0.445312 0.710938 -v 0.250000 -0.703125 0.687500 -v -0.250000 -0.703125 0.687500 -v 0.265625 -0.820312 0.664062 -v -0.265625 -0.820312 0.664062 -v 0.234375 -0.914062 0.632812 -v -0.234375 -0.914062 0.632812 -v 0.164062 -0.929688 0.632812 -v -0.164062 -0.929688 0.632812 -v 0.000000 -0.945312 0.640625 -v 0.000000 0.046875 0.726562 -v 0.000000 0.210938 0.765625 -v 0.328125 0.476562 0.742188 -v -0.328125 0.476562 0.742188 -v 0.164062 0.140625 0.750000 -v -0.164062 0.140625 0.750000 -v 0.132812 0.210938 0.757812 -v -0.132812 0.210938 0.757812 -v 0.117188 -0.687500 0.734375 -v -0.117188 -0.687500 0.734375 -v 0.078125 -0.445312 0.750000 -v -0.078125 -0.445312 0.750000 -v 0.000000 -0.445312 0.750000 -v 0.000000 -0.328125 0.742188 -v 0.093750 -0.273438 0.781250 -v -0.093750 -0.273438 0.781250 -v 0.132812 -0.226562 0.796875 -v -0.132812 -0.226562 0.796875 -v 0.109375 -0.132812 0.781250 -v -0.109375 -0.132812 0.781250 -v 0.039062 -0.125000 0.781250 -v -0.039062 -0.125000 0.781250 -v 0.000000 -0.203125 0.828125 -v 0.046875 -0.148438 0.812500 -v -0.046875 -0.148438 0.812500 -v 0.093750 -0.156250 0.812500 -v -0.093750 -0.156250 0.812500 -v 0.109375 -0.226562 0.828125 -v -0.109375 -0.226562 0.828125 -v 0.078125 -0.250000 0.804688 -v -0.078125 -0.250000 0.804688 -v 0.000000 -0.289062 0.804688 -v 0.257812 -0.312500 0.554688 -v -0.257812 -0.312500 0.554688 -v 0.164062 -0.242188 0.710938 -v -0.164062 -0.242188 0.710938 -v 0.179688 -0.312500 0.710938 -v -0.179688 -0.312500 0.710938 -v 0.234375 -0.250000 0.554688 -v -0.234375 -0.250000 0.554688 -v 0.000000 -0.875000 0.687500 -v 0.046875 -0.867188 0.687500 -v -0.046875 -0.867188 0.687500 -v 0.093750 -0.820312 0.710938 -v -0.093750 -0.820312 0.710938 -v 0.093750 -0.742188 0.726562 -v -0.093750 -0.742188 0.726562 -v 0.000000 -0.781250 0.656250 -v 0.093750 -0.750000 0.664062 -v -0.093750 -0.750000 0.664062 -v 0.093750 -0.812500 0.640625 -v -0.093750 -0.812500 0.640625 -v 0.046875 -0.851562 0.632812 -v -0.046875 -0.851562 0.632812 -v 0.000000 -0.859375 0.632812 -v 0.171875 0.218750 0.781250 -v -0.171875 0.218750 0.781250 -v 0.187500 0.156250 0.773438 -v -0.187500 0.156250 0.773438 -v 0.335938 0.429688 0.757812 -v -0.335938 0.429688 0.757812 -v 0.273438 0.421875 0.773438 -v -0.273438 0.421875 0.773438 -v 0.421875 0.398438 0.773438 -v -0.421875 0.398438 0.773438 -v 0.562500 0.351562 0.695312 -v -0.562500 0.351562 0.695312 -v 0.585938 0.289062 0.687500 -v -0.585938 0.289062 0.687500 -v 0.578125 0.195312 0.679688 -v -0.578125 0.195312 0.679688 -v 0.476562 0.101562 0.718750 -v -0.476562 0.101562 0.718750 -v 0.375000 0.062500 0.742188 -v -0.375000 0.062500 0.742188 -v 0.226562 0.109375 0.781250 -v -0.226562 0.109375 0.781250 -v 0.179688 0.296875 0.781250 -v -0.179688 0.296875 0.781250 -v 0.210938 0.375000 0.781250 -v -0.210938 0.375000 0.781250 -v 0.234375 0.359375 0.757812 -v -0.234375 0.359375 0.757812 -v 0.195312 0.296875 0.757812 -v -0.195312 0.296875 0.757812 -v 0.242188 0.125000 0.757812 -v -0.242188 0.125000 0.757812 -v 0.375000 0.085938 0.726562 -v -0.375000 0.085938 0.726562 -v 0.460938 0.117188 0.703125 -v -0.460938 0.117188 0.703125 -v 0.546875 0.210938 0.671875 -v -0.546875 0.210938 0.671875 -v 0.554688 0.281250 0.671875 -v -0.554688 0.281250 0.671875 -v 0.531250 0.335938 0.679688 -v -0.531250 0.335938 0.679688 -v 0.414062 0.390625 0.750000 -v -0.414062 0.390625 0.750000 -v 0.281250 0.398438 0.765625 -v -0.281250 0.398438 0.765625 -v 0.335938 0.406250 0.750000 -v -0.335938 0.406250 0.750000 -v 0.203125 0.171875 0.750000 -v -0.203125 0.171875 0.750000 -v 0.195312 0.226562 0.750000 -v -0.195312 0.226562 0.750000 -v 0.109375 0.460938 0.609375 -v -0.109375 0.460938 0.609375 -v 0.195312 0.664062 0.617188 -v -0.195312 0.664062 0.617188 -v 0.335938 0.687500 0.593750 -v -0.335938 0.687500 0.593750 -v 0.484375 0.554688 0.554688 -v -0.484375 0.554688 0.554688 -v 0.679688 0.453125 0.492188 -v -0.679688 0.453125 0.492188 -v 0.796875 0.406250 0.460938 -v -0.796875 0.406250 0.460938 -v 0.773438 0.164062 0.375000 -v -0.773438 0.164062 0.375000 -v 0.601562 0.000000 0.414062 -v -0.601562 0.000000 0.414062 -v 0.437500 -0.093750 0.468750 -v -0.437500 -0.093750 0.468750 -v 0.000000 0.898438 0.289062 -v 0.000000 0.984375 -0.078125 -v 0.000000 -0.195312 -0.671875 -v 0.000000 -0.460938 0.187500 -v 0.000000 -0.976562 0.460938 -v 0.000000 -0.804688 0.343750 -v 0.000000 -0.570312 0.320312 -v 0.000000 -0.484375 0.281250 -v 0.851562 0.234375 0.054688 -v -0.851562 0.234375 0.054688 -v 0.859375 0.320312 -0.046875 -v -0.859375 0.320312 -0.046875 -v 0.773438 0.265625 -0.437500 -v -0.773438 0.265625 -0.437500 -v 0.460938 0.437500 -0.703125 -v -0.460938 0.437500 -0.703125 -v 0.734375 -0.046875 0.070312 -v -0.734375 -0.046875 0.070312 -v 0.593750 -0.125000 -0.164062 -v -0.593750 -0.125000 -0.164062 -v 0.640625 -0.007812 -0.429688 -v -0.640625 -0.007812 -0.429688 -v 0.335938 0.054688 -0.664062 -v -0.335938 0.054688 -0.664062 -v 0.234375 -0.351562 0.406250 -v -0.234375 -0.351562 0.406250 -v 0.179688 -0.414062 0.257812 -v -0.179688 -0.414062 0.257812 -v 0.289062 -0.710938 0.382812 -v -0.289062 -0.710938 0.382812 -v 0.250000 -0.500000 0.390625 -v -0.250000 -0.500000 0.390625 -v 0.328125 -0.914062 0.398438 -v -0.328125 -0.914062 0.398438 -v 0.140625 -0.757812 0.367188 -v -0.140625 -0.757812 0.367188 -v 0.125000 -0.539062 0.359375 -v -0.125000 -0.539062 0.359375 -v 0.164062 -0.945312 0.437500 -v -0.164062 -0.945312 0.437500 -v 0.218750 -0.281250 0.429688 -v -0.218750 -0.281250 0.429688 -v 0.210938 -0.226562 0.468750 -v -0.210938 -0.226562 0.468750 -v 0.203125 -0.171875 0.500000 -v -0.203125 -0.171875 0.500000 -v 0.210938 -0.390625 0.164062 -v -0.210938 -0.390625 0.164062 -v 0.296875 -0.312500 -0.265625 -v -0.296875 -0.312500 -0.265625 -v 0.343750 -0.148438 -0.539062 -v -0.343750 -0.148438 -0.539062 -v 0.453125 0.867188 -0.382812 -v -0.453125 0.867188 -0.382812 -v 0.453125 0.929688 -0.070312 -v -0.453125 0.929688 -0.070312 -v 0.453125 0.851562 0.234375 -v -0.453125 0.851562 0.234375 -v 0.460938 0.523438 0.429688 -v -0.460938 0.523438 0.429688 -v 0.726562 0.406250 0.335938 -v -0.726562 0.406250 0.335938 -v 0.632812 0.453125 0.281250 -v -0.632812 0.453125 0.281250 -v 0.640625 0.703125 0.054688 -v -0.640625 0.703125 0.054688 -v 0.796875 0.562500 0.125000 -v -0.796875 0.562500 0.125000 -v 0.796875 0.617188 -0.117188 -v -0.796875 0.617188 -0.117188 -v 0.640625 0.750000 -0.195312 -v -0.640625 0.750000 -0.195312 -v 0.640625 0.679688 -0.445312 -v -0.640625 0.679688 -0.445312 -v 0.796875 0.539062 -0.359375 -v -0.796875 0.539062 -0.359375 -v 0.617188 0.328125 -0.585938 -v -0.617188 0.328125 -0.585938 -v 0.484375 0.023438 -0.546875 -v -0.484375 0.023438 -0.546875 -v 0.820312 0.328125 -0.203125 -v -0.820312 0.328125 -0.203125 -v 0.406250 -0.171875 0.148438 -v -0.406250 -0.171875 0.148438 -v 0.429688 -0.195312 -0.210938 -v -0.429688 -0.195312 -0.210938 -v 0.890625 0.406250 -0.234375 -v -0.890625 0.406250 -0.234375 -v 0.773438 -0.140625 -0.125000 -v -0.773438 -0.140625 -0.125000 -v 1.039062 -0.101562 -0.328125 -v -1.039062 -0.101562 -0.328125 -v 1.281250 0.054688 -0.429688 -v -1.281250 0.054688 -0.429688 -v 1.351562 0.320312 -0.421875 -v -1.351562 0.320312 -0.421875 -v 1.234375 0.507812 -0.421875 -v -1.234375 0.507812 -0.421875 -v 1.023438 0.476562 -0.312500 -v -1.023438 0.476562 -0.312500 -v 1.015625 0.414062 -0.289062 -v -1.015625 0.414062 -0.289062 -v 1.187500 0.437500 -0.390625 -v -1.187500 0.437500 -0.390625 -v 1.265625 0.289062 -0.406250 -v -1.265625 0.289062 -0.406250 -v 1.210938 0.078125 -0.406250 -v -1.210938 0.078125 -0.406250 -v 1.031250 -0.039062 -0.304688 -v -1.031250 -0.039062 -0.304688 -v 0.828125 -0.070312 -0.132812 -v -0.828125 -0.070312 -0.132812 -v 0.921875 0.359375 -0.218750 -v -0.921875 0.359375 -0.218750 -v 0.945312 0.304688 -0.289062 -v -0.945312 0.304688 -0.289062 -v 0.882812 -0.023438 -0.210938 -v -0.882812 -0.023438 -0.210938 -v 1.039062 0.000000 -0.367188 -v -1.039062 0.000000 -0.367188 -v 1.187500 0.093750 -0.445312 -v -1.187500 0.093750 -0.445312 -v 1.234375 0.250000 -0.445312 -v -1.234375 0.250000 -0.445312 -v 1.171875 0.359375 -0.437500 -v -1.171875 0.359375 -0.437500 -v 1.023438 0.343750 -0.359375 -v -1.023438 0.343750 -0.359375 -v 0.843750 0.289062 -0.210938 -v -0.843750 0.289062 -0.210938 -v 0.835938 0.171875 -0.273438 -v -0.835938 0.171875 -0.273438 -v 0.757812 0.093750 -0.273438 -v -0.757812 0.093750 -0.273438 -v 0.820312 0.085938 -0.273438 -v -0.820312 0.085938 -0.273438 -v 0.843750 0.015625 -0.273438 -v -0.843750 0.015625 -0.273438 -v 0.812500 -0.015625 -0.273438 -v -0.812500 -0.015625 -0.273438 -v 0.726562 0.000000 -0.070312 -v -0.726562 0.000000 -0.070312 -v 0.718750 -0.023438 -0.171875 -v -0.718750 -0.023438 -0.171875 -v 0.718750 0.039062 -0.187500 -v -0.718750 0.039062 -0.187500 -v 0.796875 0.203125 -0.210938 -v -0.796875 0.203125 -0.210938 -v 0.890625 0.242188 -0.265625 -v -0.890625 0.242188 -0.265625 -v 0.890625 0.234375 -0.320312 -v -0.890625 0.234375 -0.320312 -v 0.812500 -0.015625 -0.320312 -v -0.812500 -0.015625 -0.320312 -v 0.851562 0.015625 -0.320312 -v -0.851562 0.015625 -0.320312 -v 0.828125 0.078125 -0.320312 -v -0.828125 0.078125 -0.320312 -v 0.765625 0.093750 -0.320312 -v -0.765625 0.093750 -0.320312 -v 0.843750 0.171875 -0.320312 -v -0.843750 0.171875 -0.320312 -v 1.039062 0.328125 -0.414062 -v -1.039062 0.328125 -0.414062 -v 1.187500 0.343750 -0.484375 -v -1.187500 0.343750 -0.484375 -v 1.257812 0.242188 -0.492188 -v -1.257812 0.242188 -0.492188 -v 1.210938 0.085938 -0.484375 -v -1.210938 0.085938 -0.484375 -v 1.046875 0.000000 -0.421875 -v -1.046875 0.000000 -0.421875 -v 0.882812 -0.015625 -0.265625 -v -0.882812 -0.015625 -0.265625 -v 0.953125 0.289062 -0.343750 -v -0.953125 0.289062 -0.343750 -v 0.890625 0.109375 -0.328125 -v -0.890625 0.109375 -0.328125 -v 0.937500 0.062500 -0.335938 -v -0.937500 0.062500 -0.335938 -v 1.000000 0.125000 -0.367188 -v -1.000000 0.125000 -0.367188 -v 0.960938 0.171875 -0.351562 -v -0.960938 0.171875 -0.351562 -v 1.015625 0.234375 -0.375000 -v -1.015625 0.234375 -0.375000 -v 1.054688 0.187500 -0.382812 -v -1.054688 0.187500 -0.382812 -v 1.109375 0.210938 -0.390625 -v -1.109375 0.210938 -0.390625 -v 1.085938 0.273438 -0.390625 -v -1.085938 0.273438 -0.390625 -v 1.023438 0.437500 -0.484375 -v -1.023438 0.437500 -0.484375 -v 1.250000 0.468750 -0.546875 -v -1.250000 0.468750 -0.546875 -v 1.367188 0.296875 -0.500000 -v -1.367188 0.296875 -0.500000 -v 1.312500 0.054688 -0.531250 -v -1.312500 0.054688 -0.531250 -v 1.039062 -0.085938 -0.492188 -v -1.039062 -0.085938 -0.492188 -v 0.789062 -0.125000 -0.328125 -v -0.789062 -0.125000 -0.328125 -v 0.859375 0.382812 -0.382812 -v -0.859375 0.382812 -0.382812 -vn 0.6650 -0.2008 0.7194 -vn -0.6650 -0.2008 0.7194 -vn 0.8294 -0.3036 0.4689 -vn -0.8294 -0.3036 0.4689 -vn 0.4155 -0.7933 0.4449 -vn -0.4155 -0.7933 0.4449 -vn 0.3600 -0.5089 0.7820 -vn -0.3600 -0.5089 0.7820 -vn -0.0787 -0.5394 0.8384 -vn 0.0787 -0.5394 0.8384 -vn -0.2696 -0.8413 0.4685 -vn 0.2696 -0.8413 0.4685 -vn -0.7707 -0.3352 0.5420 -vn 0.7707 -0.3352 0.5420 -vn -0.4689 -0.1940 0.8617 -vn 0.4689 -0.1940 0.8617 -vn -0.4767 0.1907 0.8581 -vn 0.4767 0.1907 0.8581 -vn -0.7672 0.3264 0.5521 -vn 0.7672 0.3264 0.5521 -vn -0.2519 0.8173 0.5182 -vn 0.2519 0.8173 0.5182 -vn -0.0949 0.5696 0.8164 -vn 0.0949 0.5696 0.8164 -vn 0.3667 0.5370 0.7597 -vn -0.3667 0.5370 0.7597 -vn 0.4141 0.7672 0.4898 -vn -0.4141 0.7672 0.4898 -vn 0.8277 0.2952 0.4771 -vn -0.8277 0.2952 0.4771 -vn 0.6713 0.1971 0.7145 -vn -0.6713 0.1971 0.7145 -vn 0.8111 0.3244 -0.4867 -vn -0.8111 0.3244 -0.4867 -vn 0.2052 0.8206 -0.5334 -vn -0.2052 0.8206 -0.5334 -vn -0.4223 0.7806 -0.4607 -vn 0.4223 0.7806 -0.4607 -vn -0.8241 0.3225 -0.4658 -vn 0.8241 0.3225 -0.4658 -vn -0.8137 -0.3487 -0.4650 -vn 0.8137 -0.3487 -0.4650 -vn -0.4223 -0.7806 -0.4607 -vn 0.4223 -0.7806 -0.4607 -vn 0.2052 -0.8206 -0.5334 -vn -0.2052 -0.8206 -0.5334 -vn 0.7995 -0.3510 -0.4875 -vn -0.7995 -0.3510 -0.4875 -vn 0.4000 -0.0623 0.9144 -vn -0.4000 -0.0623 0.9144 -vn 0.3069 -0.1754 0.9354 -vn -0.3069 -0.1754 0.9354 -vn 0.0945 -0.1835 0.9785 -vn -0.0945 -0.1835 0.9785 -vn -0.0624 -0.0283 0.9977 -vn 0.0624 -0.0283 0.9977 -vn -0.0624 0.0260 0.9977 -vn 0.0624 0.0260 0.9977 -vn 0.0996 0.1729 0.9799 -vn -0.0996 0.1729 0.9799 -vn 0.3036 0.1656 0.9383 -vn -0.3036 0.1656 0.9383 -vn 0.4002 0.0572 0.9147 -vn -0.4002 0.0572 0.9147 -vn 0.1231 -0.8616 0.4924 -vn -0.1231 -0.8616 0.4924 -vn 0.2190 -0.8647 0.4520 -vn -0.2190 -0.8647 0.4520 -vn 0.5902 -0.4550 0.6668 -vn -0.5902 -0.4550 0.6668 -vn 0.7689 -0.0506 0.6374 -vn -0.7689 -0.0506 0.6374 -vn 0.7796 0.0900 0.6197 -vn -0.7796 0.0900 0.6197 -vn 0.3241 -0.8188 0.4739 -vn -0.3241 -0.8188 0.4739 -vn 0.3857 -0.6629 0.6417 -vn -0.3857 -0.6629 0.6417 -vn 0.6895 -0.4193 0.5906 -vn -0.6895 -0.4193 0.5906 -vn 0.6588 -0.3634 0.6588 -vn -0.6588 -0.3634 0.6588 -vn 0.5465 0.3707 0.7509 -vn -0.5465 0.3707 0.7509 -vn 0.5064 0.6464 0.5706 -vn -0.5064 0.6464 0.5706 -vn 0.6092 0.5167 0.6015 -vn -0.6092 0.5167 0.6015 -vn -0.0441 0.6610 0.7491 -vn 0.0441 0.6610 0.7491 -vn -0.7246 0.3187 0.6110 -vn 0.7246 0.3187 0.6110 -vn -0.5880 0.5554 0.5880 -vn 0.5880 0.5554 0.5880 -vn 0.5361 -0.3909 0.7482 -vn -0.5361 -0.3909 0.7482 -vn 0.2207 -0.4690 0.8552 -vn -0.2207 -0.4690 0.8552 -vn -0.0794 -0.5321 0.8429 -vn 0.0794 -0.5321 0.8429 -vn -0.0825 -0.6575 0.7490 -vn 0.0825 -0.6575 0.7490 -vn 0.0457 -0.5667 0.8226 -vn -0.0457 -0.5667 0.8226 -vn 0.2784 -0.2130 0.9365 -vn -0.2784 -0.2130 0.9365 -vn 0.3813 -0.1824 0.9063 -vn -0.3813 -0.1824 0.9063 -vn 0.3357 -0.2878 0.8969 -vn -0.3357 -0.2878 0.8969 -vn 0.3762 0.0603 0.9246 -vn -0.3762 0.0603 0.9246 -vn -0.1352 0.2680 0.9539 -vn 0.1352 0.2680 0.9539 -vn 0.3961 -0.4321 0.8102 -vn -0.3961 -0.4321 0.8102 -vn 0.1856 -0.2474 0.9510 -vn -0.1856 -0.2474 0.9510 -vn 0.0099 -0.1948 0.9808 -vn -0.0099 -0.1948 0.9808 -vn 0.0721 -0.6966 0.7138 -vn -0.0721 -0.6966 0.7138 -vn 0.1863 -0.5723 0.7986 -vn -0.1863 -0.5723 0.7986 -vn 0.3157 -0.2708 0.9094 -vn -0.3157 -0.2708 0.9094 -vn 0.3063 -0.0265 0.9516 -vn -0.3063 -0.0265 0.9516 -vn 0.3266 -0.1306 0.9361 -vn -0.3266 -0.1306 0.9361 -vn -0.0137 0.0574 0.9983 -vn 0.0137 0.0574 0.9983 -vn -0.0026 -0.0656 0.9978 -vn 0.0026 -0.0656 0.9978 -vn -0.0000 -0.0000 1.0000 -vn 0.8174 -0.5744 -0.0442 -vn -0.8174 -0.5744 -0.0442 -vn 0.9494 0.2297 -0.2144 -vn -0.9494 0.2297 -0.2144 -vn 0.0825 0.9073 -0.4124 -vn -0.0825 0.9073 -0.4124 -vn -0.8836 0.3555 0.3047 -vn 0.8836 0.3555 0.3047 -vn 0.4207 -0.8797 0.2218 -vn -0.4207 -0.8797 0.2218 -vn 0.2873 -0.5747 0.7663 -vn -0.2873 -0.5747 0.7663 -vn -0.6542 0.6019 0.4580 -vn 0.6542 0.6019 0.4580 -vn 0.1052 0.7892 0.6051 -vn -0.1052 0.7892 0.6051 -vn 0.7582 0.2916 0.5832 -vn -0.7582 0.2916 0.5832 -vn 0.3889 -0.7130 0.5834 -vn -0.3889 -0.7130 0.5834 -vn 0.0463 0.2314 0.9718 -vn -0.0463 0.2314 0.9718 -vn 0.0335 -0.4018 0.9151 -vn -0.0335 -0.4018 0.9151 -vn -0.4452 -0.1610 0.8809 -vn 0.4452 -0.1610 0.8809 -vn -0.2182 -0.4364 0.8729 -vn 0.2182 -0.4364 0.8729 -vn 0.4341 -0.1290 0.8916 -vn -0.4341 -0.1290 0.8916 -vn 0.3008 0.0501 0.9524 -vn -0.3008 0.0501 0.9524 -vn 0.8123 0.3010 0.4996 -vn -0.8123 0.3010 0.4996 -vn 0.8753 0.2574 0.4093 -vn -0.8753 0.2574 0.4093 -vn 0.9385 0.1601 0.3060 -vn -0.9385 0.1601 0.3060 -vn 0.2237 -0.6539 0.7227 -vn -0.2237 -0.6539 0.7227 -vn -0.1536 -0.1997 0.9677 -vn 0.1536 -0.1997 0.9677 -vn -0.2733 -0.1025 0.9565 -vn 0.2733 -0.1025 0.9565 -vn -0.0976 0.1952 0.9759 -vn 0.0976 0.1952 0.9759 -vn -0.1582 0.9494 0.2713 -vn 0.1582 0.9494 0.2713 -vn -0.6934 0.7082 0.1328 -vn 0.6934 0.7082 0.1328 -vn -1.0000 -0.0000 -0.0000 -vn 1.0000 -0.0000 -0.0000 -vn 0.3051 -0.9450 0.1181 -vn -0.3051 -0.9450 0.1181 -vn 0.0298 -0.2981 0.9541 -vn -0.0298 -0.2981 0.9541 -vn 0.1353 -0.3479 0.9277 -vn -0.1353 -0.3479 0.9277 -vn -0.5085 -0.2755 0.8158 -vn 0.5085 -0.2755 0.8158 -vn -0.3843 -0.0419 0.9223 -vn 0.3843 -0.0419 0.9223 -vn -0.2083 0.0374 0.9774 -vn 0.2083 0.0374 0.9774 -vn -0.5721 -0.4767 0.6674 -vn 0.5721 -0.4767 0.6674 -vn -0.1369 -0.7531 0.6435 -vn 0.1369 -0.7531 0.6435 -vn 0.4088 -0.6071 0.6814 -vn -0.4088 -0.6071 0.6814 -vn 0.5740 -0.4130 0.7070 -vn -0.5740 -0.4130 0.7070 -vn 0.5665 -0.0968 0.8183 -vn -0.5665 -0.0968 0.8183 -vn 0.5703 0.1180 0.8129 -vn -0.5703 0.1180 0.8129 -vn 0.4823 0.5621 0.6719 -vn -0.4823 0.5621 0.6719 -vn 0.2604 0.6114 0.7473 -vn -0.2604 0.6114 0.7473 -vn 0.1640 0.3607 0.9182 -vn -0.1640 0.3607 0.9182 -vn -0.0178 0.2495 0.9682 -vn 0.0178 0.2495 0.9682 -vn 0.3273 -0.4166 0.8481 -vn -0.3273 -0.4166 0.8481 -vn 0.2811 -0.2610 0.9235 -vn -0.2811 -0.2610 0.9235 -vn -0.2542 -0.6514 0.7149 -vn 0.2542 -0.6514 0.7149 -vn -0.0260 -0.8455 0.5333 -vn 0.0260 -0.8455 0.5333 -vn -0.3518 -0.2606 0.8991 -vn 0.3518 -0.2606 0.8991 -vn -0.3523 -0.0110 0.9358 -vn 0.3523 -0.0110 0.9358 -vn -0.1317 0.4608 0.8777 -vn 0.1317 0.4608 0.8777 -vn -0.0342 0.6159 0.7870 -vn 0.0342 0.6159 0.7870 -vn 0.3603 0.5836 0.7277 -vn -0.3603 0.5836 0.7277 -vn 0.4988 0.5300 0.6858 -vn -0.4988 0.5300 0.6858 -vn 0.6667 -0.3333 0.6667 -vn -0.6667 -0.3333 0.6667 -vn 0.8165 -0.0731 0.5727 -vn -0.8165 -0.0731 0.5727 -vn 0.7840 0.1161 0.6098 -vn -0.7840 0.1161 0.6098 -vn -0.5306 0.8111 -0.2461 -vn 0.5306 0.8111 -0.2461 -vn -0.8511 0.3695 -0.3730 -vn 0.8511 0.3695 -0.3730 -vn -0.2446 0.8675 -0.4331 -vn 0.2446 0.8675 -0.4331 -vn 0.5924 0.7465 -0.3030 -vn -0.5924 0.7465 -0.3030 -vn 0.3685 0.8758 -0.3118 -vn -0.3685 0.8758 -0.3118 -vn 0.2821 0.9151 -0.2880 -vn -0.2821 0.9151 -0.2880 -vn 0.8561 0.1340 -0.4991 -vn -0.8561 0.1340 -0.4991 -vn 0.5342 -0.7233 -0.4376 -vn -0.5342 -0.7233 -0.4376 -vn 0.3849 -0.8131 -0.4368 -vn -0.3849 -0.8131 -0.4368 -vn 0.2335 -0.5806 -0.7800 -vn -0.2335 -0.5806 -0.7800 -vn 0.2449 -0.0583 -0.9678 -vn -0.2449 -0.0583 -0.9678 -vn 0.1163 -0.4535 -0.8837 -vn -0.1163 -0.4535 -0.8837 -vn 0.1152 -0.9836 -0.1388 -vn -0.1152 -0.9836 -0.1388 -vn 0.1184 -0.9669 -0.2260 -vn -0.1184 -0.9669 -0.2260 -vn 0.9597 -0.0085 -0.2808 -vn -0.9597 -0.0085 -0.2808 -vn 0.9319 0.1629 -0.3242 -vn -0.9319 0.1629 -0.3242 -vn 0.1626 0.0207 -0.9865 -vn -0.1626 0.0207 -0.9865 -vn -0.0188 -0.2177 -0.9758 -vn 0.0188 -0.2177 -0.9758 -vn 0.7538 -0.2926 -0.5884 -vn -0.7538 -0.2926 -0.5884 -vn 0.9196 0.1379 -0.3678 -vn -0.9196 0.1379 -0.3678 -vn 0.9297 0.3127 -0.1944 -vn -0.9297 0.3127 -0.1944 -vn 0.9120 0.3376 -0.2329 -vn -0.9120 0.3376 -0.2329 -vn 0.9407 0.3338 -0.0607 -vn -0.9407 0.3338 -0.0607 -vn 0.1761 -0.8805 -0.4402 -vn -0.1761 -0.8805 -0.4402 -vn 0.3708 -0.4733 -0.7991 -vn -0.3708 -0.4733 -0.7991 -vn 0.3107 -0.8284 -0.4660 -vn -0.3107 -0.8284 -0.4660 -vn 0.2793 -0.9515 -0.1287 -vn -0.2793 -0.9515 -0.1287 -vn 0.3139 -0.9321 -0.1807 -vn -0.3139 -0.9321 -0.1807 -vn 0.9762 -0.2083 -0.0609 -vn -0.9762 -0.2083 -0.0609 -vn 0.8267 -0.5066 0.2447 -vn -0.8267 -0.5066 0.2447 -vn 0.3449 -0.1158 -0.9315 -vn -0.3449 -0.1158 -0.9315 -vn 0.1203 0.9644 0.2355 -vn -0.1203 0.9644 0.2355 -vn 0.1275 0.9744 -0.1851 -vn -0.1275 0.9744 -0.1851 -vn 0.3492 0.5947 -0.7241 -vn -0.3492 0.5947 -0.7241 -vn 0.4153 0.8981 -0.1449 -vn -0.4153 0.8981 -0.1449 -vn 0.1845 0.7036 0.6863 -vn -0.1845 0.7036 0.6863 -vn 0.6056 0.7794 0.1608 -vn -0.6056 0.7794 0.1608 -vn 0.7033 0.6806 -0.2053 -vn -0.7033 0.6806 -0.2053 -vn 0.6679 0.2007 -0.7166 -vn -0.6679 0.2007 -0.7166 -vn 0.4948 0.4342 -0.7528 -vn -0.4948 0.4342 -0.7528 -vn 0.6423 0.7459 -0.1761 -vn -0.6423 0.7459 -0.1761 -vn 0.7182 0.6788 0.1530 -vn -0.7182 0.6788 0.1530 -vn 0.7388 0.3972 0.5444 -vn -0.7388 0.3972 0.5444 -vn 0.3428 0.9261 -0.1579 -vn -0.3428 0.9261 -0.1579 -vn 0.2270 0.5740 0.7867 -vn -0.2270 0.5740 0.7867 -vn -0.1722 0.1046 -0.9795 -vn 0.1722 0.1046 -0.9795 -vn 0.0425 0.9150 0.4013 -vn -0.0425 0.9150 0.4013 -vn -0.1616 0.1847 0.9694 -vn 0.1616 0.1847 0.9694 -vn 0.9791 0.1973 0.0483 -vn -0.9791 0.1973 0.0483 -vn 0.9470 0.0918 0.3079 -vn -0.9470 0.0918 0.3079 -vn 0.9794 0.1905 -0.0661 -vn -0.9794 0.1905 -0.0661 -vn 0.9938 0.0312 -0.1070 -vn -0.9938 0.0312 -0.1070 -vn 0.7116 -0.7008 0.0501 -vn -0.7116 -0.7008 0.0501 -vn 0.3722 -0.9243 0.0847 -vn -0.3722 -0.9243 0.0847 -vn 0.4465 -0.8644 0.2310 -vn -0.4465 -0.8644 0.2310 -vn 0.6066 -0.7578 0.2405 -vn -0.6066 -0.7578 0.2405 -vn 0.7325 -0.6368 0.2407 -vn -0.7325 -0.6368 0.2407 -vn 0.2637 -0.4499 0.8533 -vn -0.2637 -0.4499 0.8533 -vn 0.5568 -0.3181 -0.7673 -vn -0.5568 -0.3181 -0.7673 -vn 0.5004 -0.2807 -0.8190 -vn -0.5004 -0.2807 -0.8190 -vn 0.3190 -0.8494 -0.4205 -vn -0.3190 -0.8494 -0.4205 -vn 0.7198 -0.6356 -0.2793 -vn -0.7198 -0.6356 -0.2793 -vn 0.4972 -0.4408 -0.7473 -vn -0.4972 -0.4408 -0.7473 -vn 0.3506 0.3807 0.8557 -vn -0.3506 0.3807 0.8557 -vn 0.4566 0.1715 0.8730 -vn -0.4566 0.1715 0.8730 -vn 0.2583 0.1055 0.9603 -vn -0.2583 0.1055 0.9603 -vn 0.2455 -0.0802 0.9661 -vn -0.2455 -0.0802 0.9661 -vn 0.4643 -0.0599 0.8837 -vn -0.4643 -0.0599 0.8837 -vn 0.6225 -0.3045 0.7210 -vn -0.6225 -0.3045 0.7210 -vn 0.4500 0.6590 0.6027 -vn -0.4500 0.6590 0.6027 -vn -0.2667 0.8309 0.4884 -vn 0.2667 0.8309 0.4884 -vn -0.8284 0.2291 0.5111 -vn 0.8284 0.2291 0.5111 -vn -0.5251 -0.3566 0.7727 -vn 0.5251 -0.3566 0.7727 -vn 0.4546 -0.5665 0.6873 -vn -0.4546 -0.5665 0.6873 -vn 0.6996 -0.4497 0.5552 -vn -0.6996 -0.4497 0.5552 -vn 0.7220 -0.6827 -0.1126 -vn -0.7220 -0.6827 -0.1126 -vn -0.1919 0.2860 0.9388 -vn 0.1919 0.2860 0.9388 -vn 0.9048 -0.3734 -0.2047 -vn -0.9048 -0.3734 -0.2047 -vn 0.1034 0.1551 0.9825 -vn -0.1034 0.1551 0.9825 -vn 0.0841 0.9318 0.3530 -vn -0.0841 0.9318 0.3530 -vn 0.6446 -0.0883 0.7594 -vn -0.6446 -0.0883 0.7594 -vn 0.4309 0.4740 0.7678 -vn -0.4309 0.4740 0.7678 -vn 0.8032 -0.4847 0.3462 -vn -0.8032 -0.4847 0.3462 -vn 0.5811 -0.4128 0.7014 -vn -0.5811 -0.4128 0.7014 -vn 0.5910 -0.4305 0.6822 -vn -0.5910 -0.4305 0.6822 -vn 0.9818 -0.1804 -0.0591 -vn -0.9818 -0.1804 -0.0591 -vn 0.9105 -0.3965 -0.1175 -vn -0.9105 -0.3965 -0.1175 -vn 0.9972 -0.0181 -0.0725 -vn -0.9972 -0.0181 -0.0725 -vn 0.7313 -0.6543 0.1925 -vn -0.7313 -0.6543 0.1925 -vn 0.7867 -0.6079 0.1073 -vn -0.7867 -0.6079 0.1073 -vn 0.7022 -0.7022 0.1170 -vn -0.7022 -0.7022 0.1170 -vn 0.1840 0.9816 -0.0511 -vn -0.1840 0.9816 -0.0511 -vn 0.9352 0.3301 0.1284 -vn -0.9352 0.3301 0.1284 -vn 0.6633 -0.7463 0.0553 -vn -0.6633 -0.7463 0.0553 -vn -0.0085 0.9970 0.0767 -vn 0.0085 0.9970 0.0767 -vn 0.6237 -0.7061 0.3354 -vn -0.6237 -0.7061 0.3354 -vn 0.2733 -0.8925 0.3587 -vn -0.2733 -0.8925 0.3587 -vn -0.8328 -0.5080 -0.2200 -vn 0.8328 -0.5080 -0.2200 -vn -0.8339 0.2377 -0.4981 -vn 0.8339 0.2377 -0.4981 -vn -0.5655 0.7847 -0.2539 -vn 0.5655 0.7847 -0.2539 -vn -0.0560 0.9962 0.0672 -vn 0.0560 0.9962 0.0672 -vn 0.1445 0.0222 0.9893 -vn -0.1445 0.0222 0.9893 -vn 0.3275 0.0645 0.9427 -vn -0.3275 0.0645 0.9427 -vn 0.3127 0.0232 0.9496 -vn -0.3127 0.0232 0.9496 -vn 0.1710 0.0274 0.9849 -vn -0.1710 0.0274 0.9849 -vn 0.3487 0.2849 0.8929 -vn -0.3487 0.2849 0.8929 -vn 0.4006 -0.0343 0.9156 -vn -0.4006 -0.0343 0.9156 -vn 0.2572 -0.0603 0.9645 -vn -0.2572 -0.0603 0.9645 -vn 0.0637 -0.0106 0.9979 -vn -0.0637 -0.0106 0.9979 -vn -0.3637 0.7039 0.6101 -vn 0.3637 0.7039 0.6101 -vn 0.6299 0.0355 0.7759 -vn -0.6299 0.0355 0.7759 -vn 0.4472 -0.2002 0.8717 -vn -0.4472 -0.2002 0.8717 -vn 0.5072 -0.2141 0.8348 -vn -0.5072 -0.2141 0.8348 -vn 0.5258 0.2619 0.8093 -vn -0.5258 0.2619 0.8093 -vn 0.2980 0.5802 0.7580 -vn -0.2980 0.5802 0.7580 -vn 0.0930 -0.9924 -0.0805 -vn -0.0930 -0.9924 -0.0805 -vn 0.5006 -0.8657 0.0080 -vn -0.5006 -0.8657 0.0080 -vn 0.9285 -0.2497 0.2748 -vn -0.9285 -0.2497 0.2748 -vn 0.8393 0.5424 -0.0378 -vn -0.8393 0.5424 -0.0378 -vn -0.2355 0.9367 -0.2589 -vn 0.2355 0.9367 -0.2589 -vn -0.4499 0.8838 -0.1285 -vn 0.4499 0.8838 -0.1285 -vn -0.5384 -0.0098 -0.8427 -vn 0.5384 -0.0098 -0.8427 -vn -0.1910 -0.0241 -0.9813 -vn 0.1910 -0.0241 -0.9813 -vn 0.4046 0.0266 -0.9141 -vn -0.4046 0.0266 -0.9141 -vn -0.7819 0.6231 0.0197 -vn 0.7819 0.6231 0.0197 -vn 0.5428 -0.2063 -0.8142 -vn -0.5428 -0.2063 -0.8142 -vn -0.2474 -0.9231 -0.2945 -vn 0.2474 -0.9231 -0.2945 -vt 0.843250 0.588863 -vt 0.833697 0.611927 -vt 0.794218 0.611927 -vt 0.815334 0.560948 -vt 0.866314 0.937441 -vt 0.866314 0.897962 -vt 0.889378 0.888409 -vt 0.917293 0.916324 -vt 0.746541 0.611927 -vt 0.781622 0.527235 -vt 0.866314 0.985118 -vt 0.951006 0.950037 -vt 0.815334 0.662907 -vt 0.781622 0.696619 -vt 0.781622 0.950037 -vt 0.815334 0.916325 -vt 0.843250 0.634991 -vt 0.843250 0.888409 -vt 0.866314 0.644544 -vt 0.866314 0.684023 -vt 0.794218 0.865345 -vt 0.833697 0.865345 -vt 0.866314 0.731700 -vt 0.746541 0.865345 -vt 0.917293 0.662907 -vt 0.951006 0.696619 -vt 0.781622 0.780653 -vt 0.815334 0.814366 -vt 0.889378 0.634991 -vt 0.843250 0.842281 -vt 0.898931 0.611927 -vt 0.938410 0.611927 -vt 0.866314 0.793249 -vt 0.866314 0.832728 -vt 0.986087 0.611927 -vt 0.866314 0.745572 -vt 0.917294 0.560947 -vt 0.951006 0.527235 -vt 0.951006 0.780653 -vt 0.917294 0.814365 -vt 0.889378 0.588863 -vt 0.889378 0.842281 -vt 0.866314 0.579310 -vt 0.866314 0.539831 -vt 0.938410 0.865345 -vt 0.898931 0.865345 -vt 0.866314 0.492154 -vt 0.986087 0.865345 -vt 0.866314 0.585461 -vt 0.847600 0.593213 -vt 0.892780 0.865345 -vt 0.885028 0.884059 -vt 0.885028 0.593213 -vt 0.885028 0.846631 -vt 0.892780 0.611927 -vt 0.866314 0.838879 -vt 0.885028 0.630641 -vt 0.847600 0.846631 -vt 0.866314 0.638393 -vt 0.839848 0.865345 -vt 0.847600 0.630641 -vt 0.847600 0.884059 -vt 0.839848 0.611927 -vt 0.866314 0.891811 -vt 0.866314 0.611927 -vt 0.866314 0.865345 -vt 0.520711 0.060503 -vt 0.517028 0.068490 -vt 0.500000 0.064527 -vt 0.500000 0.054411 -vt 0.482972 0.068490 -vt 0.479289 0.060503 -vt 0.530457 0.065062 -vt 0.522612 0.070678 -vt 0.477388 0.070678 -vt 0.469543 0.065062 -vt 0.532878 0.067822 -vt 0.526301 0.079422 -vt 0.473699 0.079422 -vt 0.467122 0.067822 -vt 0.544695 0.083023 -vt 0.529228 0.091098 -vt 0.470772 0.091098 -vt 0.455305 0.083023 -vt 0.565647 0.115495 -vt 0.537536 0.127779 -vt 0.462464 0.127779 -vt 0.434353 0.115495 -vt 0.586916 0.172042 -vt 0.626605 0.208159 -vt 0.600470 0.238288 -vt 0.535885 0.215568 -vt 0.399530 0.238288 -vt 0.373395 0.208159 -vt 0.413084 0.172042 -vt 0.464115 0.215568 -vt 0.650908 0.240294 -vt 0.636962 0.261221 -vt 0.363038 0.261221 -vt 0.349092 0.240294 -vt 0.689501 0.268805 -vt 0.655896 0.284413 -vt 0.344104 0.284413 -vt 0.310499 0.268805 -vt 0.684517 0.317795 -vt 0.659507 0.313152 -vt 0.340493 0.313152 -vt 0.315483 0.317795 -vt 0.671357 0.334694 -vt 0.649107 0.322437 -vt 0.350893 0.322437 -vt 0.328643 0.334694 -vt 0.636775 0.370732 -vt 0.613636 0.353108 -vt 0.386364 0.353108 -vt 0.363225 0.370732 -vt 0.602261 0.395588 -vt 0.594178 0.371277 -vt 0.405822 0.371277 -vt 0.397739 0.395588 -vt 0.581044 0.398468 -vt 0.577351 0.373341 -vt 0.422649 0.373341 -vt 0.418956 0.398468 -vt 0.532016 0.387365 -vt 0.539817 0.354339 -vt 0.460183 0.354339 -vt 0.467984 0.387365 -vt 0.500000 0.382860 -vt 0.500000 0.340417 -vt 0.559135 0.334326 -vt 0.579057 0.335646 -vt 0.420943 0.335646 -vt 0.440865 0.334326 -vt 0.592090 0.332885 -vt 0.407910 0.332885 -vt 0.606163 0.325516 -vt 0.393837 0.325516 -vt 0.628251 0.311636 -vt 0.371749 0.311636 -vt 0.635551 0.300507 -vt 0.364449 0.300507 -vt 0.631484 0.283889 -vt 0.368516 0.283889 -vt 0.611331 0.264411 -vt 0.388669 0.264411 -vt 0.592372 0.254897 -vt 0.407628 0.254897 -vt 0.559669 0.267717 -vt 0.440331 0.267717 -vt 0.550878 0.277832 -vt 0.500000 0.252445 -vt 0.449122 0.277832 -vt 0.544001 0.314523 -vt 0.455999 0.314523 -vt 0.500000 0.295691 -vt 0.543637 0.293371 -vt 0.456363 0.293371 -vt 0.506819 0.075382 -vt 0.500000 0.073497 -vt 0.493181 0.075382 -vt 0.511782 0.080633 -vt 0.488218 0.080633 -vt 0.512172 0.093457 -vt 0.487828 0.093457 -vt 0.514257 0.133422 -vt 0.513874 0.097191 -vt 0.486126 0.097191 -vt 0.485743 0.133422 -vt 0.520226 0.164615 -vt 0.500000 0.157864 -vt 0.500000 0.134352 -vt 0.479774 0.164615 -vt 0.500000 0.100063 -vt 0.500000 0.091346 -vt 0.530258 0.180239 -vt 0.521901 0.181560 -vt 0.517666 0.172353 -vt 0.482334 0.172353 -vt 0.478099 0.181560 -vt 0.469742 0.180239 -vt 0.523673 0.198817 -vt 0.518957 0.194728 -vt 0.481043 0.194728 -vt 0.476327 0.198817 -vt 0.500000 0.203867 -vt 0.508211 0.197295 -vt 0.491789 0.197295 -vt 0.500000 0.197278 -vt 0.500000 0.192167 -vt 0.500000 0.165170 -vt 0.513538 0.176949 -vt 0.500000 0.171718 -vt 0.486462 0.176949 -vt 0.500000 0.186182 -vt 0.508818 0.192407 -vt 0.491182 0.192407 -vt 0.515232 0.190709 -vt 0.484768 0.190709 -vt 0.516908 0.181873 -vt 0.483092 0.181873 -vt 0.544209 0.172814 -vt 0.455791 0.172814 -vt 0.541327 0.154899 -vt 0.458673 0.154899 -vt 0.578661 0.139176 -vt 0.421339 0.139176 -vt 0.583245 0.154155 -vt 0.416755 0.154155 -vt 0.500000 0.089977 -vt 0.509911 0.090724 -vt 0.490089 0.090724 -vt 0.509713 0.082236 -vt 0.490287 0.082236 -vt 0.505196 0.077083 -vt 0.494804 0.077083 -vt 0.500000 0.075473 -vt 0.503445 0.080840 -vt 0.500000 0.080320 -vt 0.496555 0.080840 -vt 0.506660 0.083289 -vt 0.493340 0.083289 -vt 0.506822 0.086791 -vt 0.493178 0.086791 -vt 0.500000 0.085637 -vt 0.562361 0.284338 -vt 0.559190 0.294882 -vt 0.437639 0.284338 -vt 0.440810 0.294882 -vt 0.563180 0.307151 -vt 0.436820 0.307151 -vt 0.570458 0.318697 -vt 0.429542 0.318697 -vt 0.569243 0.278245 -vt 0.430757 0.278245 -vt 0.589914 0.271772 -vt 0.410086 0.271772 -vt 0.602190 0.277180 -vt 0.397810 0.277180 -vt 0.617110 0.287175 -vt 0.382890 0.287175 -vt 0.618074 0.299197 -vt 0.381926 0.299197 -vt 0.615871 0.307438 -vt 0.384129 0.307438 -vt 0.598424 0.313725 -vt 0.401576 0.313725 -vt 0.590828 0.319797 -vt 0.409172 0.319797 -vt 0.582130 0.320991 -vt 0.417870 0.320991 -vt 0.578615 0.309394 -vt 0.583998 0.312892 -vt 0.421385 0.309394 -vt 0.416002 0.312892 -vt 0.589656 0.312336 -vt 0.410344 0.312336 -vt 0.596347 0.309249 -vt 0.403653 0.309249 -vt 0.606014 0.302517 -vt 0.393986 0.302517 -vt 0.607735 0.297520 -vt 0.392265 0.297520 -vt 0.606985 0.291216 -vt 0.393015 0.291216 -vt 0.598275 0.283083 -vt 0.401725 0.283083 -vt 0.589866 0.280167 -vt 0.410134 0.280167 -vt 0.576352 0.284624 -vt 0.423648 0.284624 -vt 0.572831 0.289915 -vt 0.427169 0.289915 -vt 0.573307 0.303342 -vt 0.426693 0.303342 -vt 0.572447 0.295716 -vt 0.427553 0.295716 -vt 0.529647 0.428636 -vt 0.500000 0.428636 -vt 0.470353 0.428636 -vt 0.584616 0.428636 -vt 0.415384 0.428636 -vt 0.622301 0.417889 -vt 0.377699 0.417889 -vt 0.666214 0.388954 -vt 0.333786 0.388954 -vt 0.701481 0.344467 -vt 0.298519 0.344467 -vt 0.712163 0.315527 -vt 0.287837 0.315527 -vt 0.704305 0.260225 -vt 0.295695 0.260225 -vt 0.672947 0.224880 -vt 0.327053 0.224880 -vt 0.640497 0.198841 -vt 0.359503 0.198841 -vt 0.603553 0.023452 -vt 0.625128 0.029461 -vt 0.635753 0.082198 -vt 0.598617 0.056841 -vt 0.364247 0.082198 -vt 0.374872 0.029461 -vt 0.396447 0.023452 -vt 0.401383 0.056841 -vt 0.546781 0.009442 -vt 0.556354 0.040906 -vt 0.453219 0.009442 -vt 0.443646 0.040906 -vt 0.500000 0.033223 -vt 0.527159 0.049514 -vt 0.472841 0.049514 -vt 0.537222 0.060794 -vt 0.462778 0.060794 -vt 0.558499 0.065137 -vt 0.441501 0.065137 -vt 0.588127 0.085177 -vt 0.411873 0.085177 -vt 0.607239 0.113940 -vt 0.392761 0.113940 -vt 0.612663 0.133675 -vt 0.605467 0.151503 -vt 0.394533 0.151503 -vt 0.387337 0.133675 -vt 0.600708 0.162985 -vt 0.399292 0.162985 -vt 0.922374 0.064228 -vt 0.859647 0.117994 -vt 0.823287 0.101559 -vt 0.861342 0.027230 -vt 0.176713 0.101559 -vt 0.140353 0.117994 -vt 0.077626 0.064228 -vt 0.138658 0.027230 -vt 0.765863 0.087001 -vt 0.774604 0.010837 -vt 0.234137 0.087001 -vt 0.225396 0.010837 -vt 0.661952 0.087561 -vt 0.647010 0.028716 -vt 0.338048 0.087561 -vt 0.352990 0.028716 -vt 0.743592 0.198572 -vt 0.766410 0.233065 -vt 0.256408 0.198572 -vt 0.233590 0.233065 -vt 0.987633 0.154243 -vt 0.889478 0.177602 -vt 0.110522 0.177602 -vt 0.012367 0.154243 -vt 0.899807 0.420886 -vt 0.844317 0.347370 -vt 0.881169 0.309153 -vt 0.958963 0.357385 -vt 0.118831 0.309153 -vt 0.155683 0.347370 -vt 0.100193 0.420886 -vt 0.041037 0.357385 -vt 0.903302 0.261367 -vt 0.999860 0.253531 -vt 0.096698 0.261367 -vt 0.000140 0.253531 -vt 0.724763 0.340212 -vt 0.744725 0.304629 -vt 0.766113 0.319494 -vt 0.255275 0.304629 -vt 0.275237 0.340212 -vt 0.233887 0.319494 -vt 0.793427 0.280495 -vt 0.826663 0.297810 -vt 0.206573 0.280495 -vt 0.173337 0.297810 -vt 0.823620 0.254612 -vt 0.855331 0.267956 -vt 0.176380 0.254612 -vt 0.144669 0.267956 -vt 0.835860 0.225225 -vt 0.868309 0.233525 -vt 0.164140 0.225225 -vt 0.131691 0.233525 -vt 0.813499 0.190705 -vt 0.851288 0.181145 -vt 0.148712 0.181145 -vt 0.186501 0.190705 -vt 0.772263 0.374010 -vt 0.227737 0.374010 -vt 0.744519 0.390057 -vt 0.255481 0.390057 -vt 0.824765 0.470987 -vt 0.175235 0.470987 -vt 0.792141 0.464714 -vt 0.742181 0.461543 -vt 0.726855 0.432574 -vt 0.273145 0.432574 -vt 0.257819 0.461543 -vt 0.207859 0.464714 -vt 0.795638 0.489991 -vt 0.204362 0.489991 -vt 0.786137 0.227788 -vt 0.213863 0.227788 -vt 0.802950 0.203415 -vt 0.197050 0.203415 -vt 0.769334 0.128771 -vt 0.691484 0.146686 -vt 0.230666 0.128771 -vt 0.308516 0.146686 -vt 0.780113 0.169386 -vt 0.219887 0.169386 -vt 0.803789 0.171181 -vt 0.830917 0.139518 -vt 0.169083 0.139518 -vt 0.196211 0.171181 -vt 0.574628 0.959188 -vt 0.464516 0.959188 -vt 0.477326 0.891852 -vt 0.545439 0.884582 -vt 0.272900 0.891852 -vt 0.285710 0.959188 -vt 0.175598 0.959188 -vt 0.204787 0.884582 -vt 0.605134 0.846978 -vt 0.715894 0.897069 -vt 0.145092 0.846978 -vt 0.034332 0.897069 -vt 0.605360 0.784410 -vt 0.683874 0.757011 -vt 0.144866 0.784410 -vt 0.066352 0.757011 -vt 0.574835 0.744212 -vt 0.617867 0.719156 -vt 0.175391 0.744212 -vt 0.132359 0.719156 -vt 0.534996 0.712528 -vt 0.568728 0.682132 -vt 0.215230 0.712528 -vt 0.181498 0.682132 -vt 0.483006 0.686791 -vt 0.508539 0.641024 -vt 0.267220 0.686791 -vt 0.241687 0.641024 -vt 0.513415 0.736977 -vt 0.478880 0.719593 -vt 0.271346 0.719593 -vt 0.236811 0.736977 -vt 0.541117 0.760754 -vt 0.209109 0.760754 -vt 0.557599 0.789442 -vt 0.192627 0.789442 -vt 0.553841 0.821849 -vt 0.196385 0.821849 -vt 0.524671 0.842380 -vt 0.225555 0.842380 -vt 0.485193 0.845321 -vt 0.265033 0.845321 -vt 0.786150 0.181127 -vt 0.770208 0.191225 -vt 0.213850 0.181127 -vt 0.229792 0.191225 -vt 0.408153 0.587507 -vt 0.409610 0.674022 -vt 0.342073 0.587507 -vt 0.340616 0.674022 -vt 0.400301 0.910556 -vt 0.414155 0.878318 -vt 0.336071 0.878318 -vt 0.349925 0.910556 -vt 0.443630 0.713731 -vt 0.306596 0.713731 -vt 0.429725 0.728622 -vt 0.392812 0.710774 -vt 0.357414 0.710774 -vt 0.320501 0.728622 -vt 0.422979 0.754609 -vt 0.432640 0.746527 -vt 0.327247 0.754609 -vt 0.317586 0.746527 -vt 0.427719 0.796173 -vt 0.391578 0.815187 -vt 0.358648 0.815187 -vt 0.322507 0.796173 -vt 0.448277 0.830976 -vt 0.301949 0.830976 -vt 0.797710 0.195652 -vt 0.802580 0.199758 -vt 0.197420 0.199758 -vt 0.202290 0.195652 -vt 0.789609 0.184671 -vt 0.210391 0.184671 -vt 0.466042 0.804091 -vt 0.490498 0.816075 -vt 0.284184 0.804091 -vt 0.259728 0.816075 -vt 0.448042 0.783921 -vt 0.302184 0.783921 -vt 0.438745 0.764960 -vt 0.311481 0.764960 -vt 0.445216 0.756351 -vt 0.305010 0.756351 -vt 0.449819 0.743458 -vt 0.300407 0.743458 -vt 0.456307 0.732687 -vt 0.293919 0.732687 -vt 0.475471 0.740871 -vt 0.274755 0.740871 -vt 0.514211 0.817769 -vt 0.236015 0.817769 -vt 0.527961 0.808598 -vt 0.222265 0.808598 -vt 0.530079 0.790754 -vt 0.220147 0.790754 -vt 0.518620 0.772385 -vt 0.231606 0.772385 -vt 0.498668 0.755033 -vt 0.251558 0.755033 -vt 0.468029 0.756171 -vt 0.459660 0.770459 -vt 0.282197 0.756171 -vt 0.290566 0.770459 -vt 0.487167 0.769899 -vt 0.477354 0.785393 -vt 0.263059 0.769899 -vt 0.272872 0.785393 -vt 0.504617 0.782999 -vt 0.496545 0.797122 -vt 0.245609 0.782999 -vt 0.253681 0.797122 -vt 0.516137 0.792591 -vt 0.513713 0.804019 -vt 0.234089 0.792591 -vt 0.236513 0.804019 -vt 0.574269 0.607668 -vt 0.613510 0.655816 -vt 0.175957 0.607668 -vt 0.136716 0.655816 -vt 0.644031 0.693270 -vt 0.106195 0.693270 -vt 0.668163 0.712097 -vt 0.082063 0.712097 -vt 0.727131 0.698475 -vt 0.676584 0.686010 -vt 0.023095 0.698475 -vt 0.073642 0.686010 -vt 0.729804 0.624370 -vt 0.667013 0.640198 -vt 0.020422 0.624370 -vt 0.083213 0.640198 -vt 0.721292 0.572934 -vt 0.648152 0.594596 -vt 0.028934 0.572934 -vt 0.102074 0.594596 -vt 0.630034 0.557583 -vt 0.717709 0.538143 -vt 0.032517 0.538143 -vt 0.120192 0.557583 -vt 0.526877 0.539235 -vt 0.223349 0.539235 -s 0 -f 47/1/1 1/2/1 3/3/1 45/4/1 -f 4/5/2 2/6/2 48/7/2 46/8/2 -f 45/4/3 3/3/3 5/9/3 43/10/3 -f 6/11/4 4/5/4 46/8/4 44/12/4 -f 3/3/5 9/13/5 7/14/5 5/9/5 -f 8/15/6 10/16/6 4/5/6 6/11/6 -f 1/2/7 11/17/7 9/13/7 3/3/7 -f 10/16/8 12/18/8 2/6/8 4/5/8 -f 11/17/9 13/19/9 15/20/9 9/13/9 -f 16/21/10 14/22/10 12/18/10 10/16/10 -f 9/13/11 15/20/11 17/23/11 7/14/11 -f 18/24/12 16/21/12 10/16/12 8/15/12 -f 15/20/13 21/25/13 19/26/13 17/23/13 -f 20/27/14 22/28/14 16/21/14 18/24/14 -f 13/19/15 23/29/15 21/25/15 15/20/15 -f 22/28/16 24/30/16 14/22/16 16/21/16 -f 23/29/17 25/31/17 27/32/17 21/25/17 -f 28/33/18 26/34/18 24/30/18 22/28/18 -f 21/25/19 27/32/19 29/35/19 19/26/19 -f 30/36/20 28/33/20 22/28/20 20/27/20 -f 27/32/21 33/37/21 31/38/21 29/35/21 -f 32/39/22 34/40/22 28/33/22 30/36/22 -f 25/31/23 35/41/23 33/37/23 27/32/23 -f 34/40/24 36/42/24 26/34/24 28/33/24 -f 35/41/25 37/43/25 39/44/25 33/37/25 -f 40/45/26 38/46/26 36/42/26 34/40/26 -f 33/37/27 39/44/27 41/47/27 31/38/27 -f 42/48/28 40/45/28 34/40/28 32/39/28 -f 39/44/29 45/4/29 43/10/29 41/47/29 -f 44/12/30 46/8/30 40/45/30 42/48/30 -f 37/43/31 47/1/31 45/4/31 39/44/31 -f 46/8/32 48/7/32 38/46/32 40/45/32 -f 47/1/33 37/43/33 51/49/33 49/50/33 -f 52/51/34 38/46/34 48/7/34 50/52/34 -f 37/43/35 35/41/35 53/53/35 51/49/35 -f 54/54/36 36/42/36 38/46/36 52/51/36 -f 35/41/37 25/31/37 55/55/37 53/53/37 -f 56/56/38 26/34/38 36/42/38 54/54/38 -f 25/31/39 23/29/39 57/57/39 55/55/39 -f 58/58/40 24/30/40 26/34/40 56/56/40 -f 23/29/41 13/19/41 59/59/41 57/57/41 -f 60/60/42 14/22/42 24/30/42 58/58/42 -f 13/19/43 11/17/43 63/61/43 59/59/43 -f 64/62/44 12/18/44 14/22/44 60/60/44 -f 11/17/45 1/2/45 65/63/45 63/61/45 -f 66/64/46 2/6/46 12/18/46 64/62/46 -f 1/2/47 47/1/47 49/50/47 65/63/47 -f 50/52/48 48/7/48 2/6/48 66/64/48 -f 61/65/49 65/63/49 49/50/49 -f 50/52/50 66/64/50 62/66/50 -f 63/61/51 65/63/51 61/65/51 -f 62/66/52 66/64/52 64/62/52 -f 61/65/53 59/59/53 63/61/53 -f 64/62/54 60/60/54 62/66/54 -f 61/65/55 57/57/55 59/59/55 -f 60/60/56 58/58/56 62/66/56 -f 61/65/57 55/55/57 57/57/57 -f 58/58/58 56/56/58 62/66/58 -f 61/65/59 53/53/59 55/55/59 -f 56/56/60 54/54/60 62/66/60 -f 61/65/61 51/49/61 53/53/61 -f 54/54/62 52/51/62 62/66/62 -f 61/65/63 49/50/63 51/49/63 -f 52/51/64 50/52/64 62/66/64 -f 89/67/65 174/68/65 176/69/65 91/70/65 -f 176/69/66 175/71/66 90/72/66 91/70/66 -f 87/73/67 172/74/67 174/68/67 89/67/67 -f 175/71/68 173/75/68 88/76/68 90/72/68 -f 85/77/69 170/78/69 172/74/69 87/73/69 -f 173/75/70 171/79/70 86/80/70 88/76/70 -f 83/81/71 168/82/71 170/78/71 85/77/71 -f 171/79/72 169/83/72 84/84/72 86/80/72 -f 81/85/73 166/86/73 168/82/73 83/81/73 -f 169/83/74 167/87/74 82/88/74 84/84/74 -f 79/89/75 92/90/75 146/91/75 164/92/75 -f 147/93/76 93/94/76 80/95/76 165/96/76 -f 92/90/77 94/97/77 148/98/77 146/91/77 -f 149/99/78 95/100/78 93/94/78 147/93/78 -f 94/97/79 96/101/79 150/102/79 148/98/79 -f 151/103/80 97/104/80 95/100/80 149/99/80 -f 96/101/81 98/105/81 152/106/81 150/102/81 -f 153/107/82 99/108/82 97/104/82 151/103/82 -f 98/105/83 100/109/83 154/110/83 152/106/83 -f 155/111/84 101/112/84 99/108/84 153/107/84 -f 100/109/85 102/113/85 156/114/85 154/110/85 -f 157/115/86 103/116/86 101/112/86 155/111/86 -f 102/113/87 104/117/87 158/118/87 156/114/87 -f 159/119/88 105/120/88 103/116/88 157/115/88 -f 104/117/89 106/121/89 160/122/89 158/118/89 -f 161/123/90 107/124/90 105/120/90 159/119/90 -f 106/121/91 108/125/91 162/126/91 160/122/91 -f 163/127/92 109/128/92 107/124/92 161/123/92 -f 108/125/93 67/129/93 68/130/93 162/126/93 -f 68/130/94 67/129/94 109/128/94 163/127/94 -f 110/131/95 128/132/95 160/122/95 162/126/95 -f 161/123/96 129/133/96 111/134/96 163/127/96 -f 128/132/97 179/135/97 158/118/97 160/122/97 -f 159/119/98 180/136/98 129/133/98 161/123/98 -f 126/137/99 156/114/99 158/118/99 179/135/99 -f 159/119/100 157/115/100 127/138/100 180/136/100 -f 124/139/101 154/110/101 156/114/101 126/137/101 -f 157/115/102 155/111/102 125/140/102 127/138/102 -f 122/141/103 152/106/103 154/110/103 124/139/103 -f 155/111/104 153/107/104 123/142/104 125/140/104 -f 120/143/105 150/102/105 152/106/105 122/141/105 -f 153/107/106 151/103/106 121/144/106 123/142/106 -f 118/145/107 148/98/107 150/102/107 120/143/107 -f 151/103/108 149/99/108 119/146/108 121/144/108 -f 116/147/109 146/91/109 148/98/109 118/145/109 -f 149/99/110 147/93/110 117/148/110 119/146/110 -f 114/149/111 164/92/111 146/91/111 116/147/111 -f 147/93/112 165/96/112 115/150/112 117/148/112 -f 114/149/113 181/151/113 177/152/113 164/92/113 -f 177/152/114 182/153/114 115/150/114 165/96/114 -f 110/131/115 162/126/115 68/130/115 112/154/115 -f 68/130/116 163/127/116 111/134/116 113/155/116 -f 112/154/117 68/130/117 178/156/117 183/157/117 -f 178/156/118 68/130/118 113/155/118 184/158/118 -f 177/152/119 181/151/119 183/157/119 178/156/119 -f 184/158/120 182/153/120 177/152/120 178/156/120 -f 135/159/121 137/160/121 176/69/121 174/68/121 -f 176/69/122 137/160/122 136/161/122 175/71/122 -f 133/162/123 135/159/123 174/68/123 172/74/123 -f 175/71/124 136/161/124 134/163/124 173/75/124 -f 131/164/125 133/162/125 172/74/125 170/78/125 -f 173/75/126 134/163/126 132/165/126 171/79/126 -f 166/86/127 187/166/127 185/167/127 168/82/127 -f 186/168/128 188/169/128 167/87/128 169/83/128 -f 131/164/129 170/78/129 168/82/129 185/167/129 -f 169/83/130 171/79/130 132/165/130 186/168/130 -f 144/170/131 190/171/131 189/172/131 187/166/131 -f 189/172/132 190/171/132 145/173/132 188/169/132 -f 185/167/133 187/166/133 189/172/133 69/174/133 -f 189/172/134 188/169/134 186/168/134 69/174/134 -f 130/175/135 131/164/135 185/167/135 69/174/135 -f 186/168/135 132/165/135 130/175/135 69/174/135 -f 142/176/136 193/177/136 191/178/136 144/170/136 -f 192/179/137 194/180/137 143/181/137 145/173/137 -f 140/182/138 195/183/138 193/177/138 142/176/138 -f 194/180/139 196/184/139 141/185/139 143/181/139 -f 139/186/140 197/187/140 195/183/140 140/182/140 -f 196/184/141 198/188/141 139/186/141 141/185/141 -f 138/189/142 71/190/142 197/187/142 139/186/142 -f 198/188/143 71/190/143 138/189/143 139/186/143 -f 190/171/144 144/170/144 191/178/144 70/191/144 -f 192/179/145 145/173/145 190/171/145 70/191/145 -f 70/191/146 191/178/146 206/192/146 208/193/146 -f 207/194/147 192/179/147 70/191/147 208/193/147 -f 71/190/148 199/195/148 200/196/148 197/187/148 -f 201/197/149 199/195/149 71/190/149 198/188/149 -f 197/187/150 200/196/150 202/198/150 195/183/150 -f 203/199/151 201/197/151 198/188/151 196/184/151 -f 195/183/152 202/198/152 204/200/152 193/177/152 -f 205/201/153 203/199/153 196/184/153 194/180/153 -f 193/177/154 204/200/154 206/192/154 191/178/154 -f 207/194/155 205/201/155 194/180/155 192/179/155 -f 199/195/156 204/200/156 202/198/156 200/196/156 -f 203/199/157 205/201/157 199/195/157 201/197/157 -f 199/195/158 208/193/158 206/192/158 204/200/158 -f 207/194/159 208/193/159 199/195/159 205/201/159 -f 139/186/160 140/182/160 164/92/160 177/152/160 -f 165/96/161 141/185/161 139/186/161 177/152/161 -f 140/182/162 142/176/162 211/202/162 164/92/162 -f 212/203/163 143/181/163 141/185/163 165/96/163 -f 142/176/164 144/170/164 213/204/164 211/202/164 -f 214/205/165 145/173/165 143/181/165 212/203/165 -f 144/170/166 187/166/166 166/86/166 213/204/166 -f 167/87/167 188/169/167 145/173/167 214/205/167 -f 81/85/168 209/206/168 213/204/168 166/86/168 -f 214/205/169 210/207/169 82/88/169 167/87/169 -f 209/206/170 215/208/170 211/202/170 213/204/170 -f 212/203/171 216/209/171 210/207/171 214/205/171 -f 79/89/172 164/92/172 211/202/172 215/208/172 -f 212/203/173 165/96/173 80/95/173 216/209/173 -f 131/164/174 130/175/174 72/210/174 222/211/174 -f 72/210/175 130/175/175 132/165/175 223/212/175 -f 133/162/176 131/164/176 222/211/176 220/213/176 -f 223/212/177 132/165/177 134/163/177 221/214/177 -f 135/159/178 133/162/178 220/213/178 218/215/178 -f 221/214/179 134/163/179 136/161/179 219/216/179 -f 137/160/180 135/159/180 218/215/180 217/217/180 -f 219/216/181 136/161/181 137/160/181 217/217/181 -f 217/217/182 218/215/182 229/218/182 231/219/182 -f 230/220/183 219/216/183 217/217/183 231/219/183 -f 218/215/184 220/213/184 227/221/184 229/218/184 -f 228/222/185 221/214/185 219/216/185 230/220/185 -f 220/213/186 222/211/186 225/223/186 227/221/186 -f 226/224/187 223/212/187 221/214/187 228/222/187 -f 222/211/188 72/210/188 224/225/188 225/223/188 -f 224/225/189 72/210/189 223/212/189 226/224/189 -f 224/225/190 231/219/190 229/218/190 225/223/190 -f 230/220/191 231/219/191 224/225/191 226/224/191 -f 225/223/192 229/218/192 227/221/192 -f 228/222/193 230/220/193 226/224/193 -f 183/157/194 181/151/194 234/226/194 232/227/194 -f 235/228/195 182/153/195 184/158/195 233/229/195 -f 112/154/196 183/157/196 232/227/196 254/230/196 -f 233/229/197 184/158/197 113/155/197 255/231/197 -f 110/131/198 112/154/198 254/230/198 256/232/198 -f 255/231/199 113/155/199 111/134/199 257/233/199 -f 181/151/200 114/149/200 252/234/200 234/226/200 -f 253/235/201 115/150/201 182/153/201 235/228/201 -f 114/149/202 116/147/202 250/236/202 252/234/202 -f 251/237/203 117/148/203 115/150/203 253/235/203 -f 116/147/204 118/145/204 248/238/204 250/236/204 -f 249/239/205 119/146/205 117/148/205 251/237/205 -f 118/145/206 120/143/206 246/240/206 248/238/206 -f 247/241/207 121/144/207 119/146/207 249/239/207 -f 120/143/208 122/141/208 244/242/208 246/240/208 -f 245/243/209 123/142/209 121/144/209 247/241/209 -f 122/141/210 124/139/210 242/244/210 244/242/210 -f 243/245/211 125/140/211 123/142/211 245/243/211 -f 124/139/212 126/137/212 240/246/212 242/244/212 -f 241/247/213 127/138/213 125/140/213 243/245/213 -f 126/137/214 179/135/214 236/248/214 240/246/214 -f 237/249/215 180/136/215 127/138/215 241/247/215 -f 179/135/216 128/132/216 238/250/216 236/248/216 -f 239/251/217 129/133/217 180/136/217 237/249/217 -f 128/132/218 110/131/218 256/232/218 238/250/218 -f 257/233/219 111/134/219 129/133/219 239/251/219 -f 238/250/220 256/232/220 258/252/220 276/253/220 -f 259/254/221 257/233/221 239/251/221 277/255/221 -f 236/248/222 238/250/222 276/253/222 278/256/222 -f 277/255/223 239/251/223 237/249/223 279/257/223 -f 240/246/224 236/248/224 278/256/224 274/258/224 -f 279/257/225 237/249/225 241/247/225 275/259/225 -f 242/244/226 240/246/226 274/258/226 272/260/226 -f 275/259/227 241/247/227 243/245/227 273/261/227 -f 244/242/228 242/244/228 272/260/228 270/262/228 -f 273/261/229 243/245/229 245/243/229 271/263/229 -f 246/240/230 244/242/230 270/262/230 268/264/230 -f 271/263/231 245/243/231 247/241/231 269/265/231 -f 248/238/232 246/240/232 268/264/232 266/266/232 -f 269/265/233 247/241/233 249/239/233 267/267/233 -f 250/236/234 248/238/234 266/266/234 264/268/234 -f 267/267/235 249/239/235 251/237/235 265/269/235 -f 252/234/236 250/236/236 264/268/236 262/270/236 -f 265/269/237 251/237/237 253/235/237 263/271/237 -f 234/226/238 252/234/238 262/270/238 280/272/238 -f 263/271/239 253/235/239 235/228/239 281/273/239 -f 256/232/240 254/230/240 260/274/240 258/252/240 -f 261/275/241 255/231/241 257/233/241 259/254/241 -f 254/230/242 232/227/242 282/276/242 260/274/242 -f 283/277/243 233/229/243 255/231/243 261/275/243 -f 232/227/244 234/226/244 280/272/244 282/276/244 -f 281/273/245 235/228/245 233/229/245 283/277/245 -f 67/129/246 108/125/246 284/278/246 73/279/246 -f 285/280/247 109/128/247 67/129/247 73/279/247 -f 108/125/248 106/121/248 286/281/248 284/278/248 -f 287/282/249 107/124/249 109/128/249 285/280/249 -f 106/121/250 104/117/250 288/283/250 286/281/250 -f 289/284/251 105/120/251 107/124/251 287/282/251 -f 104/117/252 102/113/252 290/285/252 288/283/252 -f 291/286/253 103/116/253 105/120/253 289/284/253 -f 102/113/254 100/109/254 292/287/254 290/285/254 -f 293/288/255 101/112/255 103/116/255 291/286/255 -f 100/109/256 98/105/256 294/289/256 292/287/256 -f 295/290/257 99/108/257 101/112/257 293/288/257 -f 98/105/258 96/101/258 296/291/258 294/289/258 -f 297/292/259 97/104/259 99/108/259 295/290/259 -f 96/101/260 94/97/260 298/293/260 296/291/260 -f 299/294/261 95/100/261 97/104/261 297/292/261 -f 94/97/262 92/90/262 300/295/262 298/293/262 -f 301/296/263 93/94/263 95/100/263 299/294/263 -f 308/297/264 309/298/264 328/299/264 338/300/264 -f 329/301/265 309/302/265 308/303/265 339/304/265 -f 307/305/266 308/297/266 338/300/266 336/306/266 -f 339/304/267 308/303/267 307/307/267 337/308/267 -f 306/309/268 307/305/268 336/306/268 340/310/268 -f 337/308/269 307/307/269 306/309/269 341/311/269 -f 89/67/270 91/70/270 306/309/270 340/310/270 -f 306/309/271 91/70/271 90/72/271 341/311/271 -f 87/73/272 89/67/272 340/310/272 334/312/272 -f 341/311/273 90/72/273 88/76/273 335/313/273 -f 85/77/274 87/73/274 334/312/274 330/314/274 -f 335/313/275 88/76/275 86/80/275 331/315/275 -f 83/81/276 85/77/276 330/314/276 332/316/276 -f 331/315/277 86/80/277 84/84/277 333/317/277 -f 330/314/278 336/306/278 338/300/278 332/316/278 -f 339/304/279 337/308/279 331/315/279 333/317/279 -f 330/314/280 334/312/280 340/310/280 336/306/280 -f 341/311/281 335/313/281 331/315/281 337/308/281 -f 326/318/282 332/316/282 338/300/282 328/299/282 -f 339/304/283 333/317/283 327/319/283 329/301/283 -f 81/85/284 83/81/284 332/316/284 326/318/284 -f 333/317/285 84/84/285 82/88/285 327/319/285 -f 209/206/286 342/320/286 344/321/286 215/208/286 -f 345/322/287 343/323/287 210/207/287 216/209/287 -f 81/85/288 326/318/288 342/320/288 209/206/288 -f 343/323/289 327/319/289 82/88/289 210/207/289 -f 79/89/290 215/208/290 344/321/290 346/324/290 -f 345/322/291 216/209/291 80/95/291 347/325/291 -f 79/89/292 346/324/292 300/295/292 92/90/292 -f 301/296/293 347/325/293 80/95/293 93/94/293 -f 77/326/294 324/327/294 352/328/294 304/329/294 -f 353/330/295 325/331/295 77/332/295 304/333/295 -f 304/329/296 352/328/296 350/334/296 78/335/296 -f 351/336/297 353/330/297 304/333/297 78/337/297 -f 78/335/298 350/334/298 348/338/298 305/339/298 -f 349/340/299 351/336/299 78/337/299 305/341/299 -f 305/339/300 348/338/300 328/299/300 309/298/300 -f 329/301/301 349/340/301 305/341/301 309/302/301 -f 326/318/302 328/299/302 348/338/302 342/320/302 -f 349/340/303 329/301/303 327/319/303 343/323/303 -f 296/291/304 298/293/304 318/342/304 310/343/304 -f 319/344/305 299/294/305 297/292/305 311/345/305 -f 76/346/306 316/347/306 324/327/306 77/326/306 -f 325/331/307 317/348/307 76/349/307 77/332/307 -f 302/350/308 358/351/308 356/352/308 303/353/308 -f 357/354/309 359/355/309 302/356/309 303/357/309 -f 303/353/310 356/352/310 354/358/310 75/359/310 -f 355/360/311 357/354/311 303/357/311 75/361/311 -f 75/359/312 354/358/312 316/347/312 76/346/312 -f 317/348/313 355/360/313 75/361/313 76/349/313 -f 292/362/314 294/289/314 362/363/314 364/364/314 -f 363/365/315 295/290/315 293/366/315 365/367/315 -f 364/364/316 362/363/316 368/368/316 366/369/316 -f 369/370/317 363/365/317 365/367/317 367/371/317 -f 366/369/318 368/368/318 370/372/318 372/373/318 -f 371/374/319 369/370/319 367/371/319 373/375/319 -f 372/373/320 370/372/320 376/376/320 374/377/320 -f 377/378/321 371/374/321 373/375/321 375/379/321 -f 314/380/322 378/381/322 374/377/322 376/376/322 -f 375/379/323 379/382/323 315/383/323 377/378/323 -f 316/347/324 354/358/324 374/377/324 378/381/324 -f 375/379/325 355/360/325 317/348/325 379/382/325 -f 354/358/326 356/352/326 372/373/326 374/377/326 -f 373/375/327 357/354/327 355/360/327 375/379/327 -f 356/352/328 358/351/328 366/369/328 372/373/328 -f 367/371/329 359/355/329 357/354/329 373/375/329 -f 358/351/330 360/384/330 364/364/330 366/369/330 -f 365/367/331 361/385/331 359/355/331 367/371/331 -f 290/386/332 292/362/332 364/364/332 360/384/332 -f 365/367/333 293/366/333 291/387/333 361/385/333 -f 74/388/334 360/384/334 358/351/334 302/350/334 -f 359/355/335 361/385/335 74/389/335 302/356/335 -f 284/390/336 286/391/336 288/392/336 290/386/336 -f 289/393/337 287/394/337 285/395/337 291/387/337 -f 284/390/338 290/386/338 360/384/338 74/388/338 -f 361/385/339 291/387/339 285/395/339 74/389/339 -f 73/396/340 284/390/340 74/388/340 -f 74/389/341 285/395/341 73/397/341 -f 294/289/342 296/291/342 310/343/342 362/363/342 -f 311/345/343 297/292/343 295/290/343 363/365/343 -f 310/343/344 312/398/344 368/368/344 362/363/344 -f 369/370/345 313/399/345 311/345/345 363/365/345 -f 312/398/346 382/400/346 370/372/346 368/368/346 -f 371/374/347 383/401/347 313/399/347 369/370/347 -f 314/380/348 376/376/348 370/372/348 382/400/348 -f 371/374/349 377/378/349 315/383/349 383/401/349 -f 348/338/350 350/334/350 386/402/350 384/403/350 -f 387/404/351 351/336/351 349/340/351 385/405/351 -f 318/342/352 384/403/352 386/402/352 320/406/352 -f 387/404/353 385/405/353 319/344/353 321/407/353 -f 298/293/354 300/295/354 384/403/354 318/342/354 -f 385/405/355 301/296/355 299/294/355 319/344/355 -f 300/295/356 344/321/356 342/320/356 384/403/356 -f 343/323/357 345/322/357 301/296/357 385/405/357 -f 342/320/358 348/338/358 384/403/358 -f 385/405/359 349/340/359 343/323/359 -f 300/295/360 346/324/360 344/321/360 -f 345/322/361 347/325/361 301/296/361 -f 314/380/362 322/408/362 380/409/362 378/381/362 -f 381/410/363 323/411/363 315/383/363 379/382/363 -f 316/347/364 378/381/364 380/409/364 324/327/364 -f 381/410/365 379/382/365 317/348/365 325/331/365 -f 320/406/366 386/402/366 380/409/366 322/408/366 -f 381/410/367 387/404/367 321/407/367 323/411/367 -f 350/334/368 352/328/368 380/409/368 386/402/368 -f 381/410/369 353/330/369 351/336/369 387/404/369 -f 324/327/370 380/409/370 352/328/370 -f 353/330/371 381/410/371 325/331/371 -f 400/412/372 388/413/372 414/414/372 402/415/372 -f 415/416/373 389/417/373 401/418/373 403/419/373 -f 400/412/374 402/415/374 404/420/374 398/421/374 -f 405/422/375 403/419/375 401/418/375 399/423/375 -f 398/421/376 404/420/376 406/424/376 396/425/376 -f 407/426/377 405/422/377 399/423/377 397/427/377 -f 396/425/378 406/424/378 408/428/378 394/429/378 -f 409/430/379 407/426/379 397/427/379 395/431/379 -f 394/429/380 408/428/380 410/432/380 392/433/380 -f 411/434/381 409/430/381 395/431/381 393/435/381 -f 392/433/382 410/432/382 412/436/382 390/437/382 -f 413/438/383 411/434/383 393/435/383 391/439/383 -f 410/432/384 420/440/384 418/441/384 412/436/384 -f 419/442/385 421/443/385 411/434/385 413/438/385 -f 408/428/386 422/444/386 420/440/386 410/432/386 -f 421/443/387 423/445/387 409/430/387 411/434/387 -f 406/424/388 424/446/388 422/444/388 408/428/388 -f 423/445/389 425/447/389 407/426/389 409/430/389 -f 404/420/390 426/448/390 424/446/390 406/424/390 -f 425/447/391 427/449/391 405/422/391 407/426/391 -f 402/415/392 428/450/392 426/448/392 404/420/392 -f 427/449/393 429/451/393 403/419/393 405/422/393 -f 402/415/394 414/414/394 416/452/394 428/450/394 -f 417/453/395 415/416/395 403/419/395 429/451/395 -f 318/342/396 320/406/396 444/454/396 442/455/396 -f 445/456/397 321/407/397 319/344/397 443/457/397 -f 320/458/398 390/437/398 412/436/398 444/459/398 -f 413/438/399 391/439/399 321/460/399 445/461/399 -f 310/343/400 318/342/400 442/455/400 312/398/400 -f 443/457/401 319/344/401 311/345/401 313/399/401 -f 382/462/402 430/463/402 414/414/402 388/413/402 -f 415/416/403 431/464/403 383/465/403 389/417/403 -f 412/436/404 418/441/404 440/466/404 444/459/404 -f 441/467/405 419/442/405 413/438/405 445/461/405 -f 438/468/406 446/469/406 444/459/406 440/466/406 -f 445/461/407 447/470/407 439/471/407 441/467/407 -f 434/472/408 446/469/408 438/468/408 436/473/408 -f 439/471/409 447/470/409 435/474/409 437/475/409 -f 432/476/410 448/477/410 446/469/410 434/472/410 -f 447/470/411 449/478/411 433/479/411 435/474/411 -f 430/463/412 448/477/412 432/476/412 450/480/412 -f 433/479/413 449/478/413 431/464/413 451/481/413 -f 414/414/414 430/463/414 450/480/414 416/452/414 -f 451/481/415 431/464/415 415/416/415 417/453/415 -f 312/398/416 448/482/416 430/483/416 382/400/416 -f 431/484/417 449/485/417 313/399/417 383/401/417 -f 312/398/418 442/455/418 446/486/418 448/482/418 -f 447/487/419 443/457/419 313/399/419 449/485/419 -f 442/455/420 444/454/420 446/486/420 -f 447/487/421 445/456/421 443/457/421 -f 416/452/422 450/480/422 452/488/422 476/489/422 -f 453/490/423 451/481/423 417/453/423 477/491/423 -f 450/480/424 432/476/424 462/492/424 452/488/424 -f 463/493/425 433/479/425 451/481/425 453/490/425 -f 432/476/426 434/472/426 460/494/426 462/492/426 -f 461/495/427 435/474/427 433/479/427 463/493/427 -f 434/472/428 436/473/428 458/496/428 460/494/428 -f 459/497/429 437/475/429 435/474/429 461/495/429 -f 436/473/430 438/468/430 456/498/430 458/496/430 -f 457/499/431 439/471/431 437/475/431 459/497/431 -f 438/468/432 440/466/432 454/500/432 456/498/432 -f 455/501/433 441/467/433 439/471/433 457/499/433 -f 440/466/434 418/441/434 474/502/434 454/500/434 -f 475/503/435 419/442/435 441/467/435 455/501/435 -f 428/450/436 416/452/436 476/489/436 464/504/436 -f 477/491/437 417/453/437 429/451/437 465/505/437 -f 426/448/438 428/450/438 464/504/438 466/506/438 -f 465/505/439 429/451/439 427/449/439 467/507/439 -f 424/446/440 426/448/440 466/506/440 468/508/440 -f 467/507/441 427/449/441 425/447/441 469/509/441 -f 422/444/442 424/446/442 468/508/442 470/510/442 -f 469/509/443 425/447/443 423/445/443 471/511/443 -f 420/440/444 422/444/444 470/510/444 472/512/444 -f 471/511/445 423/445/445 421/443/445 473/513/445 -f 418/441/446 420/440/446 472/512/446 474/502/446 -f 473/513/447 421/443/447 419/442/447 475/503/447 -f 458/496/448 456/498/448 480/514/448 478/515/448 -f 481/516/449 457/499/449 459/497/449 479/517/449 -f 478/515/450 480/514/450 482/518/450 484/519/450 -f 483/520/451 481/516/451 479/517/451 485/521/451 -f 484/519/452 482/518/452 488/522/452 486/523/452 -f 489/524/453 483/520/453 485/521/453 487/525/453 -f 486/523/454 488/522/454 490/526/454 492/527/454 -f 491/528/455 489/524/455 487/525/455 493/529/455 -f 464/504/456 476/489/456 486/523/456 492/527/456 -f 487/525/457 477/491/457 465/505/457 493/529/457 -f 452/488/458 484/519/458 486/523/458 476/489/458 -f 487/525/459 485/521/459 453/490/459 477/491/459 -f 452/488/460 462/492/460 478/515/460 484/519/460 -f 479/517/461 463/493/461 453/490/461 485/521/461 -f 458/496/462 478/515/462 462/492/462 460/494/462 -f 463/493/463 479/517/463 459/497/463 461/495/463 -f 454/500/464 474/502/464 480/514/464 456/498/464 -f 481/516/465 475/503/465 455/501/465 457/499/465 -f 472/512/466 482/518/466 480/514/466 474/502/466 -f 481/516/467 483/520/467 473/513/467 475/503/467 -f 470/510/468 488/522/468 482/518/468 472/512/468 -f 483/520/469 489/524/469 471/511/469 473/513/469 -f 468/508/470 490/526/470 488/522/470 470/510/470 -f 489/524/471 491/528/471 469/509/471 471/511/471 -f 466/506/472 492/527/472 490/526/472 468/508/472 -f 491/528/473 493/529/473 467/507/473 469/509/473 -f 464/504/474 492/527/474 466/506/474 -f 467/507/475 493/529/475 465/505/475 -f 392/433/476 390/437/476 504/530/476 502/531/476 -f 505/532/477 391/439/477 393/435/477 503/533/477 -f 394/429/478 392/433/478 502/531/478 500/534/478 -f 503/533/479 393/435/479 395/431/479 501/535/479 -f 396/425/480 394/429/480 500/534/480 498/536/480 -f 501/535/481 395/431/481 397/427/481 499/537/481 -f 398/538/482 396/425/482 498/536/482 496/539/482 -f 499/537/483 397/427/483 399/540/483 497/541/483 -f 400/542/484 398/538/484 496/539/484 494/543/484 -f 497/541/485 399/540/485 401/544/485 495/545/485 -f 388/546/486 400/542/486 494/543/486 506/547/486 -f 495/545/487 401/544/487 389/548/487 507/549/487 -f 494/543/488 502/531/488 504/530/488 506/547/488 -f 505/532/489 503/533/489 495/545/489 507/549/489 -f 494/543/490 496/539/490 500/534/490 502/531/490 -f 501/535/491 497/541/491 495/545/491 503/533/491 -f 496/539/492 498/536/492 500/534/492 -f 501/535/493 499/537/493 497/541/493 -f 314/550/494 382/551/494 388/546/494 506/547/494 -f 389/548/495 383/552/495 315/553/495 507/549/495 -f 314/550/496 506/547/496 504/530/496 322/554/496 -f 505/532/497 507/549/497 315/553/497 323/555/497 -f 320/458/498 322/554/498 504/530/498 390/437/498 -f 505/532/499 323/555/499 321/460/499 391/439/499 diff --git a/src/engine/collision.rs b/src/engine/collision.rs index 87a85c8..e4436a6 100644 --- a/src/engine/collision.rs +++ b/src/engine/collision.rs @@ -14,20 +14,21 @@ //! sofort wieder im Kontakt steckt. //! //! Zwei Quellen füllen die Welt: Quake-Brushes aus der `.map` ([`build`]) -//! und konvexe `col_*`-Proxies aus Blender-OBJ-Exporten ([`add_model`]). -//! Beide enden als dasselbe Ebenen-Set — die Trace kennt keine Herkunft. -//! Proxy-Ebenen werden aus den Dreiecks-Flächen abgeleitet (koplanare -//! dedupliziert) und um die sechs achsparallelen AABB-Ebenen ergänzt -//! (Quake-„Bevel planes"): die `|n|·half`-Aufblasung ist nur eine -//! konservative Näherung der Minkowski-Summe; die Axial-Ebenen ziehen sie -//! an Kanten schräger Proxies wieder stramm. +//! und Collider-Objekte (`collide`-Property) aus Blender-Modellen +//! ([`add_model`]). Beide enden als dasselbe Ebenen-Set — die Trace kennt +//! keine Herkunft. Modell-Ebenen sind die **konvexe Hülle** der Vertices +//! ([`hull_planes`], inkrementeller Quickhull) — das Mesh selbst darf also +//! konkav sein, kollidiert wird gegen seine Hülle. Ergänzt um die sechs +//! achsparallelen AABB-Ebenen (Quake-„Bevel planes"): die `|n|·half`- +//! Aufblasung ist nur eine konservative Näherung der Minkowski-Summe; die +//! Axial-Ebenen ziehen sie an Kanten schräger Hüllen wieder stramm. //! -//! Headless wie der Rest von `engine`: hängt nur an `map`/`obj` (Quellen +//! Headless wie der Rest von `engine`: hängt nur an `map`/`model` (Quellen //! der Ebenen). Der Player ruft `trace` in `player::step`; gebaut wird die //! Welt einmal vom Frontend aus Map und Modellen. use crate::engine::map::{self, Map}; -use crate::engine::model::Model; +use crate::engine::model::{Model, Object}; /// Mindestabstand (units), den der Box-Mittelpunkt vor einer Fläche hält — /// verhindert Re-Kollision/Jitter im Folgeframe. ~1 cm, unsichtbar. @@ -76,14 +77,15 @@ impl CollisionWorld { Self { brushes: Vec::new() } } - /// Collision-Proxies eines Blender-Modells hinzufügen: jedes Objekt mit - /// `collider = true` wird als konvexer Brush übernommen (siehe - /// [`object_planes`]). + /// Collider eines Blender-Modells hinzufügen: jedes Objekt mit + /// `collider = true` (aus der `collide`-Property) wird als konvexer + /// Brush übernommen (siehe [`object_planes`]). pub fn add_model(&mut self, model: &Model) { for o in &model.objects { if !o.collider { continue; } - if let Some(planes) = object_planes(o) { - self.brushes.push(ConvexBrush { planes }); + match object_planes(o) { + Some(planes) => self.brushes.push(ConvexBrush { planes }), + None => eprintln!("[collision] {}: kein Volumen — ignoriert", o.name), } } } @@ -102,47 +104,173 @@ impl CollisionWorld { } } -/// Das **konvexe** Ebenen-Set eines Modell-Objekts: Ebenen aus den Dreiecks- -/// Flächen (koplanare dedupliziert) plus die sechs achsparallelen AABB-Ebenen -/// (Bevel-Ebenen, siehe Modul-Doc). `None`, wenn kein geschlossenes Volumen -/// entsteht. Konkave Objekte sind Autorinnen-Fehler und ergeben zu große -/// Volumen — die Konvention heißt deshalb `col_*`-*Boxen/Prismen*. -pub(crate) fn object_planes(o: &crate::engine::model::Object) -> Option> { - let mut planes: Vec = Vec::new(); - let (mut bb_min, mut bb_max) = ([f32::MAX; 3], [f32::MIN; 3]); +/// Das konvexe Ebenen-Set eines Modell-Objekts: die **konvexe Hülle** seiner +/// Vertices ([`hull_planes`]) plus die sechs achsparallelen AABB-Ebenen +/// (Bevel-Ebenen, siehe Modul-Doc) — das Mesh darf also konkav sein. Flache +/// Objekte (kein Hüll-Volumen) fallen auf ihre Face-Ebenen zurück, mit den +/// Bevel-Ebenen ergibt das die dünne AABB-Platte (brauchbar für flächige +/// Trigger-Zonen). `None`, wenn gar kein geschlossenes Volumen entsteht. +pub(crate) fn object_planes(o: &Object) -> Option> { + if o.verts.is_empty() { return None; } + let mut planes = hull_planes(&o.verts).unwrap_or_else(|| face_planes(o)); + + // Axiale Bevel-Ebenen aus der AABB (push_plane dedupt achsparallele + // Duplikate weg). + let (mut bb_min, mut bb_max) = (o.verts[0], o.verts[0]); for v in &o.verts { for k in 0..3 { bb_min[k] = bb_min[k].min(v[k]); bb_max[k] = bb_max[k].max(v[k]); } } - for t in &o.tris { - let (a, b, c) = (o.verts[t[0]], o.verts[t[1]], o.verts[t[2]]); - // OBJ-/glTF-Winding ist CCW von außen → cross(b−a, c−a) zeigt nach - // außen. Degenerierte Dreiecke (Länge ~0) überspringen. - let n = cross(sub(b, a), sub(c, a)); - if dot(n, n) < 1e-12 { continue; } - let n = normalize(n); - let d = dot(n, a); - if !planes.iter().any(|p| dot(p.n, n) > 0.999 && (p.d - d).abs() < 1e-4) { - planes.push(Plane { n, d }); - } - } - // Axiale Bevel-Ebenen (dedupe fängt achsparallele Duplikate). for k in 0..3 { let mut n = [0.0f32; 3]; n[k] = 1.0; - if !planes.iter().any(|p| dot(p.n, n) > 0.999 && (p.d - bb_max[k]).abs() < 1e-4) { - planes.push(Plane { n, d: bb_max[k] }); - } + push_plane(&mut planes, n, bb_max[k]); n[k] = -1.0; - if !planes.iter().any(|p| dot(p.n, n) > 0.999 && (p.d + bb_min[k]).abs() < 1e-4) { - planes.push(Plane { n, d: -bb_min[k] }); - } + push_plane(&mut planes, n, -bb_min[k]); } (planes.len() >= 4).then_some(planes) // sonst kein geschlossenes Volumen } +/// Ebene anfügen, wenn keine (nahezu) gleiche schon da ist — Koplanar-Dedupe +/// für Hüll-Dreiecke und Bevel-Ebenen. +fn push_plane(planes: &mut Vec, n: [f32; 3], d: f32) { + if !planes.iter().any(|p| dot(p.n, n) > 0.999 && (p.d - d).abs() < 1e-4) { + planes.push(Plane { n, d }); + } +} + +/// Fallback für degenerierte (flache) Objekte: Ebenen direkt aus den +/// Dreiecks-Flächen (koplanare dedupliziert). OBJ-/glTF-Winding ist CCW von +/// außen → cross(b−a, c−a) zeigt nach außen. +fn face_planes(o: &Object) -> Vec { + let mut planes: Vec = Vec::new(); + for t in &o.tris { + let (a, b, c) = (o.verts[t[0]], o.verts[t[1]], o.verts[t[2]]); + let n = cross(sub(b, a), sub(c, a)); + if dot(n, n) < 1e-12 { continue; } // degeneriert (Länge ~0) + let n = normalize(n); + push_plane(&mut planes, n, dot(n, a)); + } + planes +} + +/// Toleranz der Hüllen-Konstruktion (units): Punkte näher als das an einer +/// Ebene gelten als koplanar. Absolut statt relativ — Maßstab ist fix +/// (1 unit = 1 Blender-Meter), und die Trace arbeitet mit ~SKIN-Auflösung. +const HULL_EPS: f32 = 1e-4; + +/// Ein Hüll-Dreieck während der Konstruktion; `alive = false` = durch einen +/// später eingefügten Punkt ersetzt (Tombstone statt teurem Entfernen). +struct HullFace { + v: [usize; 3], + n: [f32; 3], + d: f32, + alive: bool, +} + +/// Hüll-Dreieck `(x, y, z)`, so orientiert, dass der Referenzpunkt `w` +/// (ein Punkt im Inneren des Start-Simplex) hinter der Ebene liegt. +fn hull_face(pts: &[[f32; 3]], x: usize, y: usize, z: usize, w: usize) -> HullFace { + let n = normalize(cross(sub(pts[y], pts[x]), sub(pts[z], pts[x]))); + let d = dot(n, pts[x]); + if dot(n, pts[w]) > d { + HullFace { v: [x, z, y], n: [-n[0], -n[1], -n[2]], d: -d, alive: true } + } else { + HullFace { v: [x, y, z], n, d, alive: true } + } +} + +/// Konvexe Hülle einer Punktwolke als nach außen orientierte Ebenen +/// (inkrementeller Quickhull: Start-Tetraeder aus Extrempunkten, dann jeden +/// Außenpunkt einfügen — sichtbare Faces sterben, der Horizont wird mit +/// neuen Dreiecken zum Punkt geschlossen). `None` bei degenerierter Eingabe +/// (alle Punkte ~kollinear/koplanar) — kein Volumen zum Aufspannen. +fn hull_planes(pts: &[[f32; 3]]) -> Option> { + if pts.len() < 4 { return None; } + + // Start-Simplex: das fernste Paar unter den sechs Achsen-Extrema, dazu + // der fernste Punkt zur Kante und der fernste zur aufgespannten Ebene. + let mut ext = [0usize; 6]; + for (i, p) in pts.iter().enumerate() { + for k in 0..3 { + if p[k] < pts[ext[k]][k] { ext[k] = i; } + if p[k] > pts[ext[3 + k]][k] { ext[3 + k] = i; } + } + } + let (mut a, mut b, mut best) = (0, 0, -1.0); + for &i in &ext { + for &j in &ext { + let d = sub(pts[i], pts[j]); + let d = dot(d, d); + if d > best { best = d; (a, b) = (i, j); } + } + } + if best < HULL_EPS * HULL_EPS { return None; } // alle Punkte ~identisch + + let ab = sub(pts[b], pts[a]); + let (mut c, mut best) = (0, -1.0); + for (i, p) in pts.iter().enumerate() { + let x = cross(ab, sub(*p, pts[a])); + let x = dot(x, x); + if x > best { best = x; c = i; } + } + // |cross|² = Abstand² · |ab|² → Vergleich gegen EPS²·|ab|². + if best < HULL_EPS * HULL_EPS * dot(ab, ab) { return None; } // kollinear + + let n = normalize(cross(ab, sub(pts[c], pts[a]))); + let (mut e, mut best) = (0, -1.0); + for (i, p) in pts.iter().enumerate() { + let x = (dot(n, *p) - dot(n, pts[a])).abs(); + if x > best { best = x; e = i; } + } + if best < HULL_EPS { return None; } // koplanar + + // Tetraeder-Faces; als Innen-Referenz dient je der vierte Eckpunkt. + let mut faces = vec![ + hull_face(pts, a, b, c, e), + hull_face(pts, a, b, e, c), + hull_face(pts, a, c, e, b), + hull_face(pts, b, c, e, a), + ]; + + for p in 0..pts.len() { + // Faces, die den Punkt sehen (Punkt liegt vor ihrer Ebene). + let visible: Vec = faces.iter().enumerate() + .filter(|(_, f)| f.alive && dot(f.n, pts[p]) > f.d + HULL_EPS) + .map(|(i, _)| i) + .collect(); + if visible.is_empty() { continue; } // schon in der Hülle + + // Horizont: gerichtete Kanten sichtbarer Faces, deren Gegenkante + // nicht selbst zu einem sichtbaren Face gehört. Weil die Windings + // konsistent sind, taucht jede innere Kante genau einmal je Richtung + // auf — der Horizont ist der Rest. + let mut edges: Vec<(usize, usize)> = Vec::new(); + for &fi in &visible { + let [x, y, z] = faces[fi].v; + edges.extend([(x, y), (y, z), (z, x)]); + faces[fi].alive = false; + } + for &(x, y) in &edges { + if edges.contains(&(y, x)) { continue; } // innere Kante + // Horizont-Kante behält ihre Richtung → (x, y, p) zeigt nach + // außen. Degenerierte Dreiecke (p ~auf der Kante) überspringen. + let nn = cross(sub(pts[y], pts[x]), sub(pts[p], pts[x])); + if dot(nn, nn) < 1e-12 { continue; } + let n = normalize(nn); + faces.push(HullFace { v: [x, y, p], n, d: dot(n, pts[x]), alive: true }); + } + } + + let mut planes: Vec = Vec::new(); + for f in faces.iter().filter(|f| f.alive) { + push_plane(&mut planes, f.n, f.d); // koplanare Hüll-Dreiecke dedupen + } + Some(planes) +} + /// Überlappt eine AABB (Mittelpunkt/Halbmaße) das konvexe Volumen? Gleiche /// Minkowski-Aufblasung wie die Trace — „berühren" zählt als drin. pub(crate) fn box_touches(planes: &[Plane], center: [f32; 3], half: [f32; 3]) -> bool { @@ -278,40 +406,22 @@ mod tests { assert!((h.frac - 0.375).abs() < 0.02, "frac={}", h.frac); } - // Einheitswürfel [0,1]³ als col_-Proxy, Quads CCW von außen (wie ein - // Blender-Export), plus ein vis_-Objekt, das keinen Collider ergeben darf. - const PROXY_OBJ: &str = "\ -o col_Box -v 0 0 0 -v 1 0 0 -v 1 1 0 -v 0 1 0 -v 0 0 1 -v 1 0 1 -v 1 1 1 -v 0 1 1 -f 5 6 7 8 -f 1 4 3 2 -f 2 3 7 6 -f 1 5 8 4 -f 4 8 7 3 -f 1 2 6 5 -o vis_Deko -v 5 5 5 -v 6 5 5 -v 6 6 5 -f 9 10 11 -"; - #[test] - fn obj_proxy_becomes_convex_brush() { - use crate::engine::obj; - let model = obj::parse(PROXY_OBJ); + fn collider_object_becomes_convex_brush() { + use crate::engine::model::{test_cube, Model}; + let model = Model { + objects: vec![ + test_cube("Box", [0.0, 0.0, 0.0], Some("proxy"), None), + test_cube("Deko", [5.0, 5.0, 5.0], None, None), + ], + empties: Vec::new(), + materials: vec![String::new()], + }; let mut w = CollisionWorld::empty(); w.add_model(&model); - // Nur col_Box (vis_ trägt nicht bei); 12 Dreiecks-Ebenen dedupen zu 6, - // die axialen Bevel-Ebenen sind mit ihnen identisch (achsparallel). + // Nur Box (Deko hat kein collide); die Hülle des Würfels dedupt auf + // 6 Ebenen, die axialen Bevel-Ebenen sind mit ihnen identisch. assert_eq!(w.brushes.len(), 1); assert_eq!(w.brushes[0].planes.len(), 6); @@ -321,6 +431,58 @@ f 9 10 11 assert!(h.normal[0] < -0.5, "normal sollte -X sein: {:?}", h.normal); } + #[test] + fn hull_of_concave_mesh_spans_the_notch() { + // Konkaves „L": zwei Würfel-Objekte liefern die Punktwolke eines + // L-Prismas — [0,2]×[0,1]×[0,1] plus [0,1]×[1,2]×[0,1]. Die Hülle + // muss die Kerbe (x>1, y>1) mit der Schräge x+y=3 überspannen. + let mut verts = Vec::new(); + for (min, max) in [([0.0f32, 0.0, 0.0], [2.0f32, 1.0, 1.0]), + ([0.0, 1.0, 0.0], [1.0, 2.0, 1.0])] { + for &x in &[min[0], max[0]] { + for &y in &[min[1], max[1]] { + for &z in &[min[2], max[2]] { verts.push([x, y, z]); } + } + } + } + let planes = hull_planes(&verts).unwrap(); + // 6 AABB-Seiten der Gesamtbox + 1 Schräge über der Kerbe. + assert_eq!(planes.len(), 7); + let diag = planes.iter() + .find(|p| p.n[0] > 0.5 && p.n[1] > 0.5) + .expect("Schräge über der Kerbe fehlt"); + assert!((dot(diag.n, [2.0, 1.0, 0.5]) - diag.d).abs() < 1e-3, + "Schräge sollte durch (2,1,·) laufen"); + + // Ein Punkt in der Kerbe, unter der Schräge, liegt in der Hülle. + assert!(box_touches(&planes, [1.2, 1.2, 0.5], PT)); + // Die Ecke der Kerbe (über der Schräge) liegt außerhalb. + assert!(!box_touches(&planes, [1.9, 1.9, 0.5], PT)); + } + + #[test] + fn degenerate_objects_fall_back_or_are_rejected() { + use crate::engine::model::{Object, Props}; + // Flaches Quad in der XY-Ebene: keine Hülle, aber der Face-Ebenen- + // Fallback + Bevels ergeben die dünne Platte. + let flat = Object { + name: "Zone".into(), + verts: vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0], [0.0, 1.0, 0.0]], + uvs: vec![[0.0, 0.0]; 4], + tris: vec![[0, 1, 2], [0, 2, 3]], + tri_mats: vec![0; 2], + visible: true, collider: false, props: Props::new(), + }; + let planes = object_planes(&flat).unwrap(); + assert!(box_touches(&planes, [0.5, 0.5, 0.0], PT)); + assert!(!box_touches(&planes, [0.5, 0.5, 1.0], PT)); + + // Ohne Vertices gibt es nichts. + let empty = Object { verts: Vec::new(), uvs: Vec::new(), tris: Vec::new(), + tri_mats: Vec::new(), ..flat }; + assert!(object_planes(&empty).is_none()); + } + #[test] fn builds_brushes_from_map() { // Ein achsenparalleler Quader-Brush (Quake-Koords). diff --git a/src/engine/gltf.rs b/src/engine/gltf.rs index 0fd7222..8c478b4 100644 --- a/src/engine/gltf.rs +++ b/src/engine/gltf.rs @@ -24,7 +24,7 @@ use serde_json::Value; -use crate::engine::model::{apply_signal_rule, classify, Empty, Model, Object, Props}; +use crate::engine::model::{apply_props, Empty, Model, Object, Props}; pub fn load(path: &str) -> Result { let bytes = std::fs::read(path).map_err(|e| format!("gltf load {path}: {e}"))?; @@ -81,14 +81,14 @@ fn walk_node(doc: &Value, bin: &[u8], idx: usize, parent: M4, model: &mut Model) match node["mesh"].as_u64() { Some(mesh) => { - let (visible, collider) = classify(&name); let mut o = Object { - name, visible, collider, props, + name, props, + visible: true, collider: false, // bis apply_props entscheidet verts: Vec::new(), uvs: Vec::new(), tris: Vec::new(), tri_mats: Vec::new(), }; append_mesh(doc, bin, mesh as usize, world, &mut o, &mut model.materials)?; - apply_signal_rule(&mut o); // Betretens-Zonen werden nie gerendert + apply_props(&mut o); // collide-/signal-Regeln (siehe engine::model) if !o.tris.is_empty() { model.objects.push(o); } } // Node ohne Mesh = Empty (Entity-Marker); Position aus der @@ -352,7 +352,7 @@ mod tests { {"name": "Ding", "mesh": 0, "translation": [10, 0, 0], "extras": {"signal": "tiffany", "hp": 3}, "children": [1]}, - {"name": "col_Kind", "mesh": 0}, + {"name": "Kind", "mesh": 0, "extras": {"collide": "proxy"}}, {"name": "spawn", "translation": [1, 2, 3]} ], "meshes": [{"primitives": [{ @@ -388,9 +388,10 @@ mod tests { assert!(!d.visible && !d.collider); assert_eq!(m.materials, vec!["carpet".to_string()]); - // Kind erbt die Parent-Transform (10, 0, 0). + // Kind erbt die Parent-Transform (10, 0, 0); + // collide="proxy" → unsichtbarer Collider. let k = &m.objects[1]; - assert_eq!(k.name, "col_Kind"); + assert_eq!(k.name, "Kind"); assert!(!k.visible && k.collider); assert_eq!(k.verts[0], [10.0, 0.0, 0.0]); } diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 38569d4..b184a09 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -16,13 +16,12 @@ //! an den Aufrufer lösen (so wie story_ctrl Tags zurückgibt, statt selbst //! signals::dispatch zu rufen). //! -//! `map`, `obj`, `gltf` und `tga` sind reine Decoder (Bytes → owned Daten, -//! hängen an nichts) — die geteilte Heimat für Format-Dekodierung, die jedes +//! `map`, `gltf` und `tga` sind reine Decoder (Bytes → owned Daten, hängen +//! an nichts) — die geteilte Heimat für Format-Dekodierung, die jedes //! Frontend per Pull konsumiert. `map` ist zugleich der Anfang des headless -//! Datenmodells (Phase 2 des Renderer-Plans); `obj` und `gltf` sind der -//! Blender-Pfad (Sichtgeometrie + `col_*`-Collision-Proxies + Custom -//! Properties aus einem Export) und produzieren beide dasselbe neutrale -//! `model::Model`. +//! Datenmodells (Phase 2 des Renderer-Plans); `gltf` ist der Blender-Pfad +//! (Sichtgeometrie, Collider/Trigger via Custom Properties, Empties aus +//! einem Export) und produziert das neutrale `model::Model`. //! //! `player` ist die First-Person-Physik, aus der der Renderer seine View //! ableitet; `collision` (hängt an `map` und `model`) liefert ihr die Welt @@ -36,7 +35,6 @@ pub mod ink; pub mod kv; pub mod map; pub mod model; -pub mod obj; pub mod player; pub mod signals; pub mod story_ctrl; diff --git a/src/engine/model.rs b/src/engine/model.rs index c6f8afa..a8938b7 100644 --- a/src/engine/model.rs +++ b/src/engine/model.rs @@ -1,18 +1,19 @@ -//! Neutrales 3D-Modell — das gemeinsame Ergebnis der Blender-Loader -//! (engine::obj, engine::gltf). Konsumenten (render::props, collision) -//! kennen nur diese Struktur; welcher Parser sie erzeugt hat, ist ihnen -//! egal — so bleibt ein Format-Wechsel ein lokaler Loader-Tausch. +//! Neutrales 3D-Modell — das gemeinsame Ergebnis des Blender-Loaders +//! (engine::gltf). Konsumenten (render::props, collision, trigger) kennen +//! nur diese Struktur; welcher Parser sie erzeugt hat, ist ihnen egal — so +//! bleibt ein Format-Wechsel ein lokaler Loader-Tausch. //! -//! Naming-Konvention (Objekt-Präfix, case-insensitiv): -//! `col_*` → unsichtbar, collidierbar (Collision-Proxy, konvex!) -//! sonst → nur sichtbar (`vis_*` sagt dasselbe explizit) +//! Was ein Objekt *ist*, steuern seine Blender-Custom-Properties (keine +//! Namens-Präfixe — Namen bleiben frei für die Autorin), siehe +//! [`apply_props`]: +//! `collide = true` → sichtbar **und** Collider (konvexe Hülle des +//! Meshes, siehe collision::object_planes) +//! `collide = "proxy"` → unsichtbarer Collider (reines Kollisionsvolumen) +//! `signal = ` → Trigger; mit Collider Klick-Ziel, ohne Collider +//! unsichtbare Betretens-Zone (engine::trigger) +//! nichts davon → nur sichtbar //! -//! Anders als irl3d („ohne Präfix = beides"): dessen Physik kollidierte per -//! Dreieck, unsere per konvexem Ebenen-Schnitt — konkave Sichtgeometrie als -//! Default-Collider ergäbe hier nur ein sinnloses Hüll-Volumen. Kollision -//! ist deshalb immer ein bewusst gebauter `col_*`-Proxy. -//! -//! Objektnamen sind zugleich die künftigen Interact-Keys: Blender-Suffixe +//! Objektnamen sind zugleich die Interact-Keys: Blender-Suffixe //! (`Thing.001`) strippt `signals::signal_key` beim Dispatch, nicht hier — //! die Loader bleiben frei von Signal-Wissen. @@ -56,34 +57,41 @@ pub struct Empty { pub props: Props, } -/// Präfix-Klassifikation (case-insensitiv): `col_` = nur Collider, alles -/// andere nur sichtbar (siehe Modul-Doc, Abweichung von irl3d). -pub fn classify(name: &str) -> (bool, bool) { - let is_col = name.get(..4).is_some_and(|p| p.eq_ignore_ascii_case("col_")); - (!is_col, is_col) -} +/// Custom-Property-Schlüssel, der ein Objekt zum Collider macht (siehe +/// Modul-Doc): truthy (`true`, `1`, …) = sichtbar + Collider, `"proxy"` = +/// unsichtbarer Collider. `0`/`false`/`no`/leer zählen als aus, damit die +/// Autorin die Property abschalten kann, ohne sie zu löschen. +pub const COLLIDE_PROP: &str = "collide"; /// Custom-Property-Schlüssel, der ein Objekt zum Trigger macht (Wert = /// Signal-Name für `signals::dispatch`). Die Collider-Frage entscheidet die /// Semantik — anfassbar = klickbar, durchlaufbar = Betretens-Zone: -/// `col_*` + `signal` → solide **und** klickbar (Point-and-Click) -/// sonst + `signal` → unsichtbare Trigger-Zone, feuert beim Betreten +/// `collide` + `signal` → solide **und** klickbar (Point-and-Click) +/// nur `signal` → unsichtbare Trigger-Zone, feuert beim Betreten /// (einmal je Eintritt; erneut erst nach Verlassen) pub const SIGNAL_PROP: &str = "signal"; -/// Die `signal`-Regel anwenden (nach `classify`, wenn `props` bekannt sind): -/// eine Betretens-Zone ist ein reines Trigger-Volumen und wird nie gerendert. -pub fn apply_signal_rule(o: &mut Object) { +/// Die Property-Regeln anwenden, sobald `props` gelesen sind (Objekte +/// starten sichtbar und ohne Collider): `collide` schaltet den Collider, +/// `"proxy"` und Betretens-Zonen sind reine Volumen und werden nie gerendert. +pub fn apply_props(o: &mut Object) { + if let Some(v) = o.props.get(COLLIDE_PROP) { + let v = v.to_ascii_lowercase(); + if !matches!(v.as_str(), "" | "0" | "false" | "no") { + o.collider = true; + if v == "proxy" { o.visible = false; } + } + } if !o.collider && o.props.contains_key(SIGNAL_PROP) { o.visible = false; } } /// Test-Helfer: Einheitswürfel-Objekt bei `min..min+1` (Quads CCW von außen, -/// Fan-trianguliert wie die Loader) mit optionaler `signal`-Property — -/// geteilt von trigger-/session-Tests. +/// Fan-trianguliert wie der Loader) mit optionalen `collide`-/`signal`- +/// Properties — geteilt von trigger-/session-/collision-Tests. #[cfg(test)] -pub(crate) fn test_cube(name: &str, min: [f32; 3], signal: Option<&str>) -> Object { +pub(crate) fn test_cube(name: &str, min: [f32; 3], collide: Option<&str>, signal: Option<&str>) -> Object { let v = |dx: f32, dy: f32, dz: f32| [min[0] + dx, min[1] + dy, min[2] + dz]; let verts = vec![ v(0.0, 0.0, 0.0), v(1.0, 0.0, 0.0), v(1.0, 1.0, 0.0), v(0.0, 1.0, 0.0), @@ -99,17 +107,17 @@ pub(crate) fn test_cube(name: &str, min: [f32; 3], signal: Option<&str>) -> Obje tris.push([q[0], q[2], q[3]]); } let n = tris.len(); - let (visible, collider) = classify(name); let mut props = Props::new(); + if let Some(c) = collide { props.insert(COLLIDE_PROP.into(), c.into()); } if let Some(s) = signal { props.insert(SIGNAL_PROP.into(), s.into()); } let mut o = Object { name: name.into(), uvs: vec![[0.0, 0.0]; verts.len()], verts, tris, tri_mats: vec![0; n], - visible, collider, props, + visible: true, collider: false, props, }; - apply_signal_rule(&mut o); + apply_props(&mut o); o } @@ -118,11 +126,25 @@ mod tests { use super::*; #[test] - fn classify_is_prefix_and_case_insensitive() { - assert_eq!(classify("col_Wand"), (false, true)); - assert_eq!(classify("COL_Wand"), (false, true)); - assert_eq!(classify("vis_Deko"), (true, false)); - assert_eq!(classify("Suzanne"), (true, false)); - assert_eq!(classify(""), (true, false)); + fn collide_prop_rules() { + // truthy → sichtbar + Collider; "proxy" → unsichtbarer Collider. + let o = test_cube("Kiste", [0.0; 3], Some("true"), None); + assert!(o.visible && o.collider); + let o = test_cube("Wand", [0.0; 3], Some("proxy"), None); + assert!(!o.visible && o.collider); + // Abschaltwerte zählen als aus; ohne Property nur sichtbar. + let o = test_cube("Aus", [0.0; 3], Some("0"), None); + assert!(o.visible && !o.collider); + let o = test_cube("Deko", [0.0; 3], None, None); + assert!(o.visible && !o.collider); + } + + #[test] + fn signal_without_collider_is_invisible_zone() { + let o = test_cube("Zone", [0.0; 3], None, Some("betreten")); + assert!(!o.visible && !o.collider); + // Mit Collider bleibt das Objekt sichtbar (Klick-Ziel). + let o = test_cube("Kiste", [0.0; 3], Some("1"), Some("kiste")); + assert!(o.visible && o.collider); } } diff --git a/src/engine/obj.rs b/src/engine/obj.rs deleted file mode 100644 index c9ef7a4..0000000 --- a/src/engine/obj.rs +++ /dev/null @@ -1,242 +0,0 @@ -//! Wavefront-OBJ-Loader (Blender-Export) → neutrales [`Model`]. -//! -//! Eingebettete Fassung des irl3d-Loaders (../irl3d/src/obj.rs), auf die -//! WDS-Loader-Regeln gebracht: reiner Decoder (Datei → CPU-Struct, kein -//! Textur-/Material-/GPU-Zugriff — Konsumenten laden selbst, wie bei -//! map/tga), f32 statt Fixed-Point, `Result` statt Panic (Blender-Exporte -//! sind Autorinnen-Content), und mit `vt`-UVs (irl3d ignorierte die). -//! -//! Subset: `v`, `vt`, `f`, `o`/`g`, `usemtl`. Alles andere ignoriert. -//! OBJ-Indizes sind 1-basiert und über die ganze Datei global; pro -//! `o`-Block wird lokal re-indexiert (Schlüssel ist das Paar Position/UV, -//! da dieselbe Position je Face verschiedene UVs tragen kann). N-gons -//! werden Fan-trianguliert. Kaputte Zeilen werden übersprungen (gemeldet), -//! nicht fatal. -//! -//! Achsen: Blenders OBJ-Exporter konvertiert per Default auf Forward=-Z, -//! Up=Y — das ist bereits unser Engine-System, kein Swap, Maßstab 1:1 -//! (Blender-Meter = Engine-Unit; `MAP_SCALE` gilt nur für Quake-Maps). -//! -//! Ergebnis ist das format-neutrale [`Model`] (siehe engine::model, dort -//! auch die `col_*`-Namenskonvention). OBJ kann keine Custom Properties und -//! keine Empties tragen — `props` bleiben leer, `empties` gibt es nur im -//! glTF-Pfad (engine::gltf). Faces ohne `vt` bekommen UV [0, 0]. - -use std::collections::HashMap; -use std::fs::read_to_string; - -use crate::engine::model::{classify, Model, Object, Props}; - -pub fn load(path: &str) -> Result { - let src = read_to_string(path).map_err(|e| format!("obj load {path}: {e}"))?; - Ok(parse(&src)) -} - -pub fn parse(src: &str) -> Model { - // Globale Pools (Datei-weite 1-basierte Indizes). - let mut positions: Vec<[f32; 3]> = Vec::new(); - let mut texcoords: Vec<[f32; 2]> = Vec::new(); - - let mut materials: Vec = Vec::new(); - let mut mat_by_name: HashMap = HashMap::new(); - let mut objects: Vec = Vec::new(); - - // Aktueller o-Block. `cur_mat` persistiert über Objektgrenzen (OBJ ist - // zustandsbehaftet); `remap` bildet globale (pos, uv)-Paare auf lokale - // Vertex-Indizes ab. - let mut cur = empty_object(); - let mut cur_mat: Option = None; - let mut remap: HashMap<(usize, Option), usize> = HashMap::new(); - - for line in src.lines() { - let line = line.trim(); - if line.is_empty() || line.starts_with('#') { continue; } - let mut it = line.split_whitespace(); - let Some(kw) = it.next() else { continue; }; - match kw { - "v" => { - if let Some(p) = parse_floats::<3>(&mut it) { - positions.push(p); - } else { - eprintln!("[obj] kaputte v-Zeile übersprungen: {line:?}"); - } - } - "vt" => { - if let Some(t) = parse_floats::<2>(&mut it) { - texcoords.push(t); - } else { - eprintln!("[obj] kaputte vt-Zeile übersprungen: {line:?}"); - } - } - "o" | "g" => { - flush(&mut objects, &mut cur, &mut remap); - cur.name = it.collect::>().join(" "); - } - "usemtl" => { - let name = it.next().unwrap_or("").to_string(); - cur_mat = Some(*mat_by_name.entry(name.clone()).or_insert_with(|| { - materials.push(name); - materials.len() - 1 - })); - } - "f" => { - // Token-Formen: "i", "i/uv", "i//n", "i/uv/n". Wir nehmen - // Position und (falls da) UV. Negative (relative) Indizes - // schreibt Blender nicht — außerhalb des Subsets. - let idxs: Option> = it.map(|tok| { - let mut parts = tok.split('/'); - let vi = parts.next()?.parse::().ok()?.checked_sub(1)?; - if vi >= positions.len() { return None; } - let ti = match parts.next().filter(|s| !s.is_empty()) { - Some(s) => { - let t = s.parse::().ok()?.checked_sub(1)?; - if t >= texcoords.len() { return None; } - Some(t) - } - None => None, - }; - Some(*remap.entry((vi, ti)).or_insert_with(|| { - cur.verts.push(positions[vi]); - cur.uvs.push(ti.map_or([0.0, 0.0], |t| texcoords[t])); - cur.verts.len() - 1 - })) - }).collect(); - let Some(idxs) = idxs else { - eprintln!("[obj] kaputte f-Zeile übersprungen: {line:?}"); - continue; - }; - // Material erst bei der ersten Face festnageln: Faces ohne - // jedes usemtl laufen unter dem ""-Material. - let m = *cur_mat.get_or_insert_with(|| { - *mat_by_name.entry(String::new()).or_insert_with(|| { - materials.push(String::new()); - materials.len() - 1 - }) - }); - // Fan-Triangulierung: (0,1,2), (0,2,3), … - for k in 1..idxs.len().saturating_sub(1) { - cur.tris.push([idxs[0], idxs[k], idxs[k + 1]]); - cur.tri_mats.push(m); - } - } - _ => {} // vn, s, mtllib, … ignorieren - } - } - flush(&mut objects, &mut cur, &mut remap); - - Model { objects, empties: Vec::new(), materials } -} - -fn empty_object() -> Object { - Object { - name: String::new(), - verts: Vec::new(), uvs: Vec::new(), - tris: Vec::new(), tri_mats: Vec::new(), - visible: true, collider: false, - props: Props::new(), - } -} - -/// Fertigen o-Block als Objekt ablegen (leere verwerfen) und den Zustand -/// für den nächsten Block zurücksetzen. -fn flush(objects: &mut Vec, cur: &mut Object, remap: &mut HashMap<(usize, Option), usize>) { - let done = std::mem::replace(cur, empty_object()); - remap.clear(); - if done.tris.is_empty() { return; } - let (visible, collider) = classify(&done.name); - objects.push(Object { visible, collider, ..done }); -} - -fn parse_floats(it: &mut std::str::SplitWhitespace) -> Option<[f32; N]> { - let mut out = [0.0; N]; - for v in &mut out { - *v = it.next()?.parse().ok()?; - } - Some(out) -} - -#[cfg(test)] -mod tests { - use super::*; - - const SAMPLE: &str = "\ -# Kommentar -o Suzanne -v 0 0 0 -v 1 0 0 -v 1 1 0 -v 0 1 0 -vt 0 0 -vt 1 0 -vt 1 1 -vt 0 1 -usemtl skin -f 1/1 2/2 3/3 4/4 -o col_Wand.001 -v 5 0 0 -v 6 0 0 -v 6 1 0 -f 5 6 7 -o VIS_Deko -v 7 7 7 -v 8 7 7 -v 8 8 7 -f 8/1 9/2 10/3 -"; - - #[test] - fn parses_objects_with_local_reindex_and_fan() { - let m = parse(SAMPLE); - assert_eq!(m.objects.len(), 3); - - let s = &m.objects[0]; - assert_eq!(s.name, "Suzanne"); - assert_eq!(s.verts.len(), 4); // lokal re-indexiert - assert_eq!(s.tris, vec![[0, 1, 2], [0, 2, 3]]); // Quad → 2 Dreiecke (Fan) - assert_eq!(s.uvs[2], [1.0, 1.0]); // vt übernommen - assert!(s.visible && !s.collider, "ohne Präfix: nur sichtbar"); - - let w = &m.objects[1]; - assert_eq!(w.name, "col_Wand.001"); - assert_eq!(w.verts[0], [5.0, 0.0, 0.0]); // globale Indizes → lokal - assert_eq!(w.uvs[0], [0.0, 0.0]); // kein vt → Default - } - - #[test] - fn prefix_classification_is_case_insensitive() { - let m = parse(SAMPLE); - let w = &m.objects[1]; - assert!(!w.visible && w.collider, "col_ = nur Collider"); - let d = &m.objects[2]; - assert!(d.visible && !d.collider, "VIS_ = nur sichtbar"); - } - - #[test] - fn materials_are_shared_and_persist_across_objects() { - let m = parse(SAMPLE); - // "skin" (usemtl) — der Zustand persistiert über o-Grenzen, also - // laufen auch Wand und Deko unter "skin"; kein ""-Material nötig. - assert_eq!(m.materials, vec!["skin".to_string()]); - assert!(m.objects.iter().all(|o| o.tri_mats.iter().all(|&i| i == 0))); - } - - #[test] - fn faces_without_usemtl_get_empty_material() { - let m = parse("o A\nv 0 0 0\nv 1 0 0\nv 1 1 0\nf 1 2 3\n"); - assert_eq!(m.materials, vec![String::new()]); - } - - #[test] - fn broken_lines_are_skipped_not_fatal() { - // f verweist auf nicht existierende Indizes, v ist unvollständig — - // beides wird übersprungen, der Rest bleibt nutzbar. - let m = parse("o A\nv 0 0\nv 0 0 0\nv 1 0 0\nv 1 1 0\nf 1 2 99\nf 1 2 3\n"); - assert_eq!(m.objects.len(), 1); - assert_eq!(m.objects[0].tris.len(), 1); - } - - #[test] - fn load_missing_file_is_err() { - assert!(load("/nonexistent/x.obj").is_err()); - } -} diff --git a/src/engine/trigger.rs b/src/engine/trigger.rs index 58d2b64..0317663 100644 --- a/src/engine/trigger.rs +++ b/src/engine/trigger.rs @@ -2,10 +2,10 @@ //! (siehe [`model::SIGNAL_PROP`]), aufgeteilt nach Collider-Frage — //! **anfassbar = klickbar, durchlaufbar = Betretens-Zone**: //! -//! - `col_*` + `signal` → *Klick-Ziel*: das solide Volumen ist zugleich +//! - `collide` + `signal` → *Klick-Ziel*: das solide Volumen ist zugleich //! das Ziel des Point-and-Click-Raycasts (siehe [`Triggers::pick`]). //! Feuern läuft über den `use `-Trichter in `Session::exec`. -//! - sonst + `signal` → *Zone*: unsichtbares konvexes Volumen; Betreten +//! - nur `signal` → *Zone*: unsichtbares konvexes Volumen; Betreten //! feuert das Signal genau einmal je Eintritt ([`Triggers::enter_events`], //! Flanken-Semantik: erneut erst nach Verlassen). „Einmal für immer" //! baut die Autorin über KV-Flags/Ink, nicht hier. @@ -106,9 +106,9 @@ mod tests { #[test] fn splits_by_collider_and_zone_is_invisible() { let m = model(vec![ - cube("col_Kiste", [0.0, 0.0, 0.0], Some("kiste")), - cube("Zone.001", [5.0, 0.0, 0.0], Some("betreten")), - cube("Deko", [9.0, 0.0, 0.0], None), + cube("Kiste", [0.0, 0.0, 0.0], Some("1"), Some("kiste")), + cube("Zone.001", [5.0, 0.0, 0.0], None, Some("betreten")), + cube("Deko", [9.0, 0.0, 0.0], None, None), ]); assert!(!m.objects[1].visible, "Zone darf nicht gerendert werden"); @@ -116,7 +116,7 @@ mod tests { t.add_model(&m); assert_eq!(t.clickables.len(), 1); assert_eq!(t.zones.len(), 1); - assert_eq!(t.signal_for("col_Kiste"), Some("kiste")); + assert_eq!(t.signal_for("Kiste"), Some("kiste")); assert_eq!(t.signal_for("Deko"), None, "ohne signal-Property kein Klick-Ziel"); } @@ -124,12 +124,12 @@ mod tests { fn pick_finds_nearest_clickable() { let mut t = Triggers::new(); t.add_model(&model(vec![ - cube("col_Fern", [4.0, -0.5, -0.5], Some("f")), - cube("col_Nah", [1.0, -0.5, -0.5], Some("n")), + cube("Fern", [4.0, -0.5, -0.5], Some("1"), Some("f")), + cube("Nah", [1.0, -0.5, -0.5], Some("1"), Some("n")), ])); // Strahl entlang +X durch beide Würfel → der nahe gewinnt. let (name, frac) = t.pick([0.0, 0.0, 0.0], [10.0, 0.0, 0.0]).unwrap(); - assert_eq!(name, "col_Nah"); + assert_eq!(name, "Nah"); assert!((frac - 0.1).abs() < 0.01, "frac={frac}"); // Strahl daneben → nichts. assert!(t.pick([0.0, 5.0, 0.0], [10.0, 5.0, 0.0]).is_none()); @@ -138,7 +138,7 @@ mod tests { #[test] fn zone_fires_on_entry_edge_and_rearms_after_exit() { let mut t = Triggers::new(); - t.add_model(&model(vec![cube("Zone", [0.0, 0.0, 0.0], Some("betreten"))])); + t.add_model(&model(vec![cube("Zone", [0.0, 0.0, 0.0], None, Some("betreten"))])); let half = [0.3, 0.9, 0.3]; // Außerhalb: nichts. diff --git a/src/render/mod.rs b/src/render/mod.rs index 7467c93..5bd9012 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -36,7 +36,7 @@ use winit::window::{CursorGrabMode, Window, WindowId}; use crate::engine::player; use crate::engine::tga::Image; -use crate::engine::{assets, gltf, map, model, obj, tga}; +use crate::engine::{assets, gltf, map, model, tga}; use crate::session::{FrameInput, Mode, Session}; use camera::Camera; use gpu::Gpu; @@ -64,7 +64,7 @@ pub fn run(mut session: Session) { // auf die GPU macht später `Gpu` — Decode (CPU) und Upload (GPU) bleiben // getrennt. let world = map::load(&assets::path("assets/maps/test.map")); - // Blender-Modelle: alle .glb/.obj unter assets/maps/props/. Autorinnen- + // Blender-Modelle: alle .glb unter assets/maps/props/. Autorinnen- // Content — ein kaputtes File wird gemeldet und übersprungen, nie fatal. let models = load_models(&assets::path("assets/maps/props")); @@ -81,9 +81,9 @@ pub fn run(mut session: Session) { for (_, m) in &models { mesh.append(props::build(m, &tex_names)); } report_map(&world, &tex_names, &mesh); - // Sim-Welt (Collision-Brushes + Spawn, dann col_*-Proxies der Modelle) in - // die Session einspielen — die Simulation gehört der Session, nicht dem - // Render-Frontend. + // Sim-Welt (Collision-Brushes + Spawn, dann Collider/Trigger der + // Modelle) in die Session einspielen — die Simulation gehört der + // Session, nicht dem Render-Frontend. session.load_world(&world); for (_, m) in &models { session.load_props(m); } @@ -134,25 +134,21 @@ pub fn run(mut session: Session) { event_loop.run_app(&mut app).expect("winit: run"); } -/// Alle Blender-Modelle (`.glb` und `.obj`) eines Verzeichnisses laden -/// (sortiert → deterministische Reihenfolge). Fehlende Verzeichnisse sind -/// kein Fehler (kein Props-Ordner = keine Props); kaputte Dateien werden -/// gemeldet und übersprungen. +/// Alle Blender-Modelle (`.glb`) eines Verzeichnisses laden (sortiert → +/// deterministische Reihenfolge). Fehlende Verzeichnisse sind kein Fehler +/// (kein Props-Ordner = keine Props); kaputte Dateien werden gemeldet und +/// übersprungen. fn load_models(dir: &str) -> Vec<(String, model::Model)> { let Ok(entries) = std::fs::read_dir(dir) else { return Vec::new(); }; let mut paths: Vec = entries.flatten() .map(|e| e.path()) - .filter(|p| matches!(p.extension().and_then(|s| s.to_str()), Some("obj" | "glb"))) + .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("glb")) .collect(); paths.sort(); let mut models = Vec::new(); for p in paths { let path = p.to_string_lossy(); - let loaded = match p.extension().and_then(|s| s.to_str()) { - Some("glb") => gltf::load(&path), - _ => obj::load(&path), - }; - match loaded { + match gltf::load(&path) { Ok(m) => { let name = p.file_stem().map_or_else(String::new, |s| s.to_string_lossy().into_owned()); let tris: usize = m.objects.iter().map(|o| o.tris.len()).sum(); diff --git a/src/render/props.rs b/src/render/props.rs index 05e2572..231c99e 100644 --- a/src/render/props.rs +++ b/src/render/props.rs @@ -1,18 +1,19 @@ //! Blender-Modell → Render-Geometrie. //! -//! Gegenstück zu render::brush für den OBJ-Pfad: nimmt das neutrale -//! [`Model`] aus engine::obj und baut daraus ein [`Mesh`] (Vertices + nach +//! Gegenstück zu render::brush für den Modell-Pfad: nimmt das neutrale +//! [`Model`] aus engine::gltf und baut daraus ein [`Mesh`] (Vertices + nach //! Textur gruppierte Index-Batches), das mit der Brush-Geometrie zu einem //! Buffer verschmilzt (siehe `Mesh::append`). //! -//! Texturen: der `usemtl`-Materialname *ist* der Texturname — aufgelöst wie +//! Texturen: der Blender-Materialname *ist* der Texturname — aufgelöst wie //! bei Brushes zu `assets/textures/{name}.tga` (macht der Aufrufer). Faces //! ohne Material (`""`) laufen unter [`FALLBACK_TEXTURE`]. //! -//! UVs: OBJ hat den V-Ursprung unten links, unsere `tga::Image`s oben links -//! — V wird hier gespiegelt (Achsen-Konvention ist Sache des Konsumenten, -//! siehe engine::tga). Nur `visible`-Objekte werden ausgegeben; reine -//! `col_*`-Proxies sind Sache von engine::collision. +//! UVs: das Modell trägt den V-Ursprung unten links, unsere `tga::Image`s +//! oben links — V wird hier gespiegelt (Achsen-Konvention ist Sache des +//! Konsumenten, siehe engine::tga). Nur `visible`-Objekte werden +//! ausgegeben; Collider-/Zonen-Volumen sind Sache von engine::collision +//! und engine::trigger. use crate::engine::model::Model; use crate::render::scene::{Batch, Mesh, Vertex}; @@ -21,7 +22,7 @@ use crate::render::scene::{Batch, Mesh, Vertex}; pub const FALLBACK_TEXTURE: &str = "placeholder"; /// Distinkte Texturnamen des Modells (sortiert), `""` bereits auf den -/// Fallback gemappt — dasselbe Kontrakt wie `brush::texture_names`. +/// Fallback gemappt — derselbe Kontrakt wie `brush::texture_names`. pub fn texture_names(model: &Model) -> Vec { let mut names: Vec = model.materials.iter() .map(|m| resolve(m).to_string()) @@ -69,43 +70,48 @@ pub fn build(model: &Model, tex_names: &[String]) -> Mesh { #[cfg(test)] mod tests { use super::*; - use crate::engine::obj; + use crate::engine::model::{Model, Object, Props}; - const SAMPLE: &str = "\ -o Ding -v 0 0 0 -v 1 0 0 -v 1 1 0 -vt 0 0 -vt 1 0 -vt 1 1 -usemtl carpet -f 1/1 2/2 3/3 -o col_Proxy -v 5 0 0 -v 6 0 0 -v 6 1 0 -f 4 5 6 -"; + /// Ein Dreieck mit UVs; `visible` steuert, ob es gerendert werden darf. + fn tri(name: &str, visible: bool) -> Object { + Object { + name: name.into(), + verts: vec![[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [1.0, 1.0, 0.0]], + uvs: vec![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0]], + tris: vec![[0, 1, 2]], + tri_mats: vec![0], + visible, collider: !visible, + props: Props::new(), + } + } + + fn sample() -> Model { + Model { + objects: vec![tri("Ding", true), tri("Proxy", false)], + empties: Vec::new(), + materials: vec!["carpet".to_string()], + } + } #[test] fn builds_only_visible_objects_with_v_flip() { - let m = obj::parse(SAMPLE); + let m = sample(); let names = texture_names(&m); assert_eq!(names, vec!["carpet".to_string()]); let mesh = build(&m, &names); - // Nur „Ding" (col_Proxy ist unsichtbar): 3 Vertices, 1 Dreieck. + // Nur „Ding" (der Proxy ist unsichtbar): 3 Vertices, 1 Dreieck. assert_eq!(mesh.verts.len(), 3); assert_eq!(mesh.indices, vec![0, 1, 2]); assert_eq!(mesh.batches.len(), 1); - // vt (1,1) → V-Flip → uv (1,0). + // uv (1,1) → V-Flip → (1,0). assert_eq!(mesh.verts[2].uv, [1.0, 0.0]); } #[test] fn missing_material_maps_to_fallback() { - let m = obj::parse("o A\nv 0 0 0\nv 1 0 0\nv 1 1 0\nf 1 2 3\n"); + let mut m = sample(); + m.materials = vec![String::new()]; // Faces ohne Material assert_eq!(texture_names(&m), vec![FALLBACK_TEXTURE.to_string()]); let mesh = build(&m, &texture_names(&m)); assert_eq!(mesh.batches.len(), 1); @@ -115,9 +121,8 @@ f 4 5 6 fn shares_texture_index_space_with_superset_list() { // Obermengen-Liste (wie beim Merge mit Brush-Texturen): der Batch // zeigt auf den richtigen Index in der geteilten Liste. - let m = obj::parse(SAMPLE); let names = vec!["aaa".to_string(), "carpet".to_string(), "zzz".to_string()]; - let mesh = build(&m, &names); + let mesh = build(&sample(), &names); assert_eq!(mesh.batches[0].texture, 1); } } diff --git a/src/session.rs b/src/session.rs index 3891fd4..0853df8 100644 --- a/src/session.rs +++ b/src/session.rs @@ -104,10 +104,10 @@ impl Session { } /// Sim-Seite eines Blender-Modells in die bestehende Welt einfügen — - /// nach `load_world` rufen, einmal pro Modell: Collision-Proxies - /// (`col_*`) und Trigger (`signal`-Property → Klick-Ziele/Zonen). Die - /// Sichtgeometrie desselben Modells ist Sache des Renderers - /// (render::props). + /// nach `load_world` rufen, einmal pro Modell: Collider (`collide`- + /// Property → konvexe Hülle) und Trigger (`signal`-Property → + /// Klick-Ziele/Zonen). Die Sichtgeometrie desselben Modells ist Sache + /// des Renderers (render::props). pub fn load_props(&mut self, model: &model::Model) { self.collision.add_model(model); self.triggers.add_model(model); @@ -526,9 +526,9 @@ mod tests { crate::engine::model::Model { objects: vec![ crate::engine::model::test_cube( - "Zone.001", [-0.5, 0.0, -3.0], Some("set zone_hit true")), + "Zone.001", [-0.5, 0.0, -3.0], None, Some("set zone_hit true")), crate::engine::model::test_cube( - "col_Kiste", [5.0, 0.0, 5.0], Some("set kiste_geklickt true")), + "Kiste", [5.0, 0.0, 5.0], Some("1"), Some("set kiste_geklickt true")), ], empties: Vec::new(), materials: vec![String::new()], @@ -558,8 +558,8 @@ mod tests { let mut s = empty_session(); s.load_props(&trigger_model()); s.mode = Mode::FirstPerson; - // Klick-Ziel: feuert den Property-Wert, nicht signal_key("col_Kiste"). - s.exec("use col_Kiste"); + // Klick-Ziel: feuert den Property-Wert, nicht signal_key("Kiste"). + s.exec("use Kiste"); assert!(s.game.kv["kiste_geklickt"].coerce_to_bool().unwrap()); // Ohne Property wie gehabt: Name (suffix-gestrippt) als Signal — hier // unbekannt und ohne Table ein stilles No-Op, aber kein Fehler. @@ -571,9 +571,9 @@ mod tests { fn pick_hits_clickable_within_reach() { let mut s = empty_session(); s.load_props(&trigger_model()); - // col_Kiste bei [5..6, 0..1, 5..6]: Strahl von schräg oben davor. + // Kiste bei [5..6, 0..1, 5..6]: Strahl von schräg oben davor. let hit = s.pick([5.5, 0.5, 3.0], [0.0, 0.0, 1.0], INTERACT_REACH); - assert_eq!(hit, Some("col_Kiste")); + assert_eq!(hit, Some("Kiste")); // Außer Reichweite → nichts. assert_eq!(s.pick([5.5, 0.5, 0.0], [0.0, 0.0, 1.0], INTERACT_REACH), None); }