summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cargo/config-nightly-fast-compile.toml16
-rw-r--r--.cargo/config.toml16
-rw-r--r--.clang-format26
-rw-r--r--Cargo.lock2488
-rw-r--r--Cargo.toml19
-rw-r--r--THIRD_PARTY97
-rw-r--r--ecs/Cargo.toml28
-rw-r--r--ecs/examples/with_sole.rs53
-rw-r--r--ecs/src/actions.rs167
-rw-r--r--ecs/src/component.rs368
-rw-r--r--ecs/src/component/local.rs61
-rw-r--r--ecs/src/entity.rs160
-rw-r--r--ecs/src/event.rs1
-rw-r--r--ecs/src/event/component.rs18
-rw-r--r--ecs/src/lib.rs710
-rw-r--r--ecs/src/pair.rs198
-rw-r--r--ecs/src/phase.rs13
-rw-r--r--ecs/src/private.rs2
-rw-r--r--ecs/src/query.rs564
-rw-r--r--ecs/src/query/flexible.rs86
-rw-r--r--ecs/src/query/term.rs122
-rw-r--r--ecs/src/sole.rs176
-rw-r--r--ecs/src/stats.rs8
-rw-r--r--ecs/src/system.rs186
-rw-r--r--ecs/src/system/stateful.rs159
-rw-r--r--ecs/src/uid.rs243
-rw-r--r--ecs/tests/query.rs322
-rw-r--r--engine-ecs-macros/Cargo.toml (renamed from ecs-macros/Cargo.toml)2
-rw-r--r--engine-ecs-macros/src/lib.rs (renamed from ecs-macros/src/lib.rs)312
-rw-r--r--engine-ecs/Cargo.toml45
-rw-r--r--engine-ecs/benches/query.rs (renamed from ecs/benches/query.rs)12
-rw-r--r--engine-ecs/examples/component_changed_event.rs78
-rw-r--r--engine-ecs/examples/component_events.rs64
-rw-r--r--engine-ecs/examples/component_relationship.rs65
-rw-r--r--engine-ecs/examples/component_removed_event.rs46
-rw-r--r--engine-ecs/examples/error_handling.rs79
-rw-r--r--engine-ecs/examples/event_loop.rs (renamed from ecs/examples/event_loop.rs)24
-rw-r--r--engine-ecs/examples/extension.rs (renamed from ecs/examples/extension.rs)10
-rw-r--r--engine-ecs/examples/multiple_queries.rs (renamed from ecs/examples/multiple_queries.rs)14
-rw-r--r--engine-ecs/examples/optional_component.rs (renamed from ecs/examples/optional_component.rs)12
-rw-r--r--engine-ecs/examples/relationship.rs (renamed from ecs/examples/relationship.rs)20
-rw-r--r--engine-ecs/examples/simple.rs (renamed from ecs/examples/simple.rs)8
-rw-r--r--engine-ecs/examples/traversal.rs44
-rw-r--r--engine-ecs/examples/with_local.rs (renamed from ecs/examples/with_local.rs)13
-rw-r--r--engine-ecs/examples/with_sole.rs56
-rw-r--r--engine-ecs/src/actions.rs392
-rw-r--r--engine-ecs/src/component.rs427
-rw-r--r--engine-ecs/src/component/local.rs101
-rw-r--r--engine-ecs/src/component/storage.rs (renamed from ecs/src/component/storage.rs)378
-rw-r--r--engine-ecs/src/component/storage/archetype.rs (renamed from ecs/src/component/storage/archetype.rs)113
-rw-r--r--engine-ecs/src/component/storage/graph.rs (renamed from ecs/src/component/storage/graph.rs)9
-rw-r--r--engine-ecs/src/entity.rs343
-rw-r--r--engine-ecs/src/entity/obtainer.rs29
-rw-r--r--engine-ecs/src/error.rs252
-rw-r--r--engine-ecs/src/event.rs105
-rw-r--r--engine-ecs/src/event/component.rs103
-rw-r--r--engine-ecs/src/extension.rs (renamed from ecs/src/extension.rs)38
-rw-r--r--engine-ecs/src/lib.rs1007
-rw-r--r--engine-ecs/src/lock.rs (renamed from ecs/src/lock.rs)117
-rw-r--r--engine-ecs/src/pair.rs946
-rw-r--r--engine-ecs/src/phase.rs73
-rw-r--r--engine-ecs/src/query.rs603
-rw-r--r--engine-ecs/src/query/flexible.rs101
-rw-r--r--engine-ecs/src/query/term.rs273
-rw-r--r--engine-ecs/src/sole.rs107
-rw-r--r--engine-ecs/src/system.rs170
-rw-r--r--engine-ecs/src/system/initializable.rs131
-rw-r--r--engine-ecs/src/system/observer.rs219
-rw-r--r--engine-ecs/src/system/stateful.rs265
-rw-r--r--engine-ecs/src/time.rs43
-rw-r--r--engine-ecs/src/tuple.rs (renamed from ecs/src/tuple.rs)86
-rw-r--r--engine-ecs/src/uid.rs207
-rw-r--r--engine-ecs/src/util.rs (renamed from ecs/src/util.rs)175
-rw-r--r--engine-ecs/src/util/array_vec.rs (renamed from ecs/src/util/array_vec.rs)58
-rw-r--r--engine-ecs/tests/phase.rs36
-rw-r--r--engine-ecs/tests/query.rs406
-rw-r--r--engine-macros/Cargo.toml13
-rw-r--r--engine-macros/src/lib.rs10
-rw-r--r--engine-macros/src/reflection.rs73
-rw-r--r--engine-macros/src/reflection/default_value.rs42
-rw-r--r--engine-macros/src/reflection/enum_impl.rs485
-rw-r--r--engine-macros/src/reflection/field.rs100
-rw-r--r--engine-macros/src/reflection/options_attr.rs37
-rw-r--r--engine-macros/src/reflection/struct_impl.rs186
-rw-r--r--engine-macros/src/reflection/visibility.rs45
-rw-r--r--engine-macros/src/util.rs65
-rw-r--r--engine-reflection/Cargo.toml8
-rw-r--r--engine-reflection/src/lib.rs647
-rw-r--r--engine/Cargo.toml58
-rw-r--r--engine/benches/file_format/wavefront_obj.rs17
-rw-r--r--engine/benches/res/teapot.obj18362
-rw-r--r--engine/build.rs60
-rw-r--r--engine/res/FiraMono-Regular.ttfbin0 -> 170204 bytes
-rw-r--r--engine/res/imgui_shader.slang54
-rw-r--r--engine/res/main_3d_shader.slang270
-rw-r--r--engine/res/sky_box_shader.slang34
-rw-r--r--engine/res/ui/delete.pngbin0 -> 424 bytes
-rw-r--r--engine/res/ui/edit.pngbin0 -> 581 bytes
-rw-r--r--engine/res/ui/warning.pngbin0 -> 1495 bytes
-rw-r--r--engine/src/asset.rs947
-rw-r--r--engine/src/camera.rs38
-rw-r--r--engine/src/camera/fly.rs146
-rw-r--r--engine/src/collision.rs12
-rw-r--r--engine/src/data_types/color.rs196
-rw-r--r--engine/src/data_types/dimens.rs76
-rw-r--r--engine/src/data_types/matrix.rs284
-rw-r--r--engine/src/data_types/vector.rs361
-rw-r--r--engine/src/delta_time.rs32
-rw-r--r--engine/src/draw_flags.rs20
-rw-r--r--engine/src/file_format/wavefront/common.rs12
-rw-r--r--engine/src/file_format/wavefront/mtl.rs204
-rw-r--r--engine/src/file_format/wavefront/obj.rs693
-rw-r--r--engine/src/image.rs363
-rw-r--r--engine/src/input.rs247
-rw-r--r--engine/src/input/keyboard.rs6
-rw-r--r--engine/src/input/mouse.rs6
-rw-r--r--engine/src/lib.rs92
-rw-r--r--engine/src/lighting.rs80
-rw-r--r--engine/src/material.rs159
-rw-r--r--engine/src/material/asset.rs89
-rw-r--r--engine/src/math.rs6
-rw-r--r--engine/src/mesh.rs217
-rw-r--r--engine/src/mesh/cube.rs480
-rw-r--r--engine/src/mesh/vertex_buffer.rs279
-rw-r--r--engine/src/model.rs192
-rw-r--r--engine/src/model/asset.rs64
-rw-r--r--engine/src/opengl/buffer.rs101
-rw-r--r--engine/src/opengl/debug.rs145
-rw-r--r--engine/src/opengl/glsl.rs616
-rw-r--r--engine/src/opengl/mod.rs128
-rw-r--r--engine/src/opengl/shader.rs247
-rw-r--r--engine/src/opengl/texture.rs240
-rw-r--r--engine/src/opengl/util.rs30
-rw-r--r--engine/src/opengl/vertex_array.rs158
-rw-r--r--engine/src/projection.rs132
-rw-r--r--engine/src/reflection.rs2
-rw-r--r--engine/src/renderer.rs7
-rw-r--r--engine/src/renderer/opengl.rs751
-rw-r--r--engine/src/renderer/opengl/glsl/fragment.glsl73
-rw-r--r--engine/src/renderer/opengl/glsl/light.glsl133
-rw-r--r--engine/src/renderer/opengl/glsl/vertex.glsl24
-rw-r--r--engine/src/renderer/opengl/glsl/vertex_data.glsl11
-rw-r--r--engine/src/renderer/opengl/vertex.rs64
-rw-r--r--engine/src/rendering.rs701
-rw-r--r--engine/src/rendering/backend.rs9
-rw-r--r--engine/src/rendering/backend/opengl.rs2042
-rw-r--r--engine/src/rendering/backend/opengl/glutin_compat.rs264
-rw-r--r--engine/src/rendering/backend/opengl/graphics_mesh.rs285
-rw-r--r--engine/src/rendering/blending.rs89
-rw-r--r--engine/src/rendering/main_render_pass.rs1004
-rw-r--r--engine/src/rendering/object.rs137
-rw-r--r--engine/src/rendering/shader.rs1404
-rw-r--r--engine/src/rendering/shader/cursor.rs372
-rw-r--r--engine/src/scene.rs8
-rw-r--r--engine/src/sky_box.rs11
-rw-r--r--engine/src/texture.rs257
-rw-r--r--engine/src/transform.rs48
-rw-r--r--engine/src/ui.rs2
-rw-r--r--engine/src/ui/dear_imgui.rs1081
-rw-r--r--engine/src/ui/view.rs1
-rw-r--r--engine/src/ui/view/world.rs2314
-rw-r--r--engine/src/util.rs426
-rw-r--r--engine/src/window.rs753
-rw-r--r--engine/src/windowing.rs1169
-rw-r--r--engine/src/windowing/dpi.rs217
-rw-r--r--engine/src/windowing/keyboard.rs826
-rw-r--r--engine/src/windowing/monitor.rs50
-rw-r--r--engine/src/windowing/mouse.rs197
-rw-r--r--engine/src/windowing/window.rs291
-rw-r--r--engine/src/windowing/window/platform.rs12
-rw-r--r--engine/src/work_queue.rs77
-rw-r--r--glfw/Cargo.toml18
-rw-r--r--glfw/build.rs42
-rw-r--r--glfw/glfw.h6
-rw-r--r--glfw/src/ffi.rs9
-rw-r--r--glfw/src/init.rs59
-rw-r--r--glfw/src/lib.rs53
-rw-r--r--glfw/src/util.rs10
-rw-r--r--glfw/src/window.rs863
-rw-r--r--opengl-bindings/Cargo.toml81
-rw-r--r--opengl-bindings/build.rs107
-rw-r--r--opengl-bindings/src/blending.rs180
-rw-r--r--opengl-bindings/src/buffer.rs270
-rw-r--r--opengl-bindings/src/data_types.rs47
-rw-r--r--opengl-bindings/src/debug.rs161
-rw-r--r--opengl-bindings/src/framebuffer.rs206
-rw-r--r--opengl-bindings/src/lib.rs143
-rw-r--r--opengl-bindings/src/misc.rs290
-rw-r--r--opengl-bindings/src/shader.rs395
-rw-r--r--opengl-bindings/src/texture.rs663
-rw-r--r--opengl-bindings/src/vertex_array.rs343
-rw-r--r--res/icon.icobin0 -> 5694 bytes
-rw-r--r--res/sky_box/cubemap_0.pngbin0 -> 1066007 bytes
-rw-r--r--res/sky_box/cubemap_1.pngbin0 -> 1066168 bytes
-rw-r--r--res/sky_box/cubemap_2.pngbin0 -> 1064019 bytes
-rw-r--r--res/sky_box/cubemap_3.pngbin0 -> 1064790 bytes
-rw-r--r--res/sky_box/cubemap_4.pngbin0 -> 1062236 bytes
-rw-r--r--res/sky_box/cubemap_5.pngbin0 -> 1078695 bytes
-rw-r--r--rust-analyzer.json10
-rw-r--r--rustfmt.toml1
-rw-r--r--src/main.rs352
-rw-r--r--xtask/Cargo.toml6
-rw-r--r--xtask/src/main.rs57
203 files changed, 50658 insertions, 11014 deletions
diff --git a/.cargo/config-nightly-fast-compile.toml b/.cargo/config-nightly-fast-compile.toml
new file mode 100644
index 0000000..359e004
--- /dev/null
+++ b/.cargo/config-nightly-fast-compile.toml
@@ -0,0 +1,16 @@
+include = ["config.toml"]
+
+[unstable]
+codegen-backend = true
+
+[profile.dev]
+debug = "line-tables-only"
+codegen-backend = "cranelift"
+
+[build]
+rustflags = ["-Zthreads=16"]
+rustc-wrapper = "sccache"
+
+[target.'cfg(unix)']
+linker = "clang"
+rustflags = ["-Clink-arg=--ld-path=wild"]
diff --git a/.cargo/config.toml b/.cargo/config.toml
new file mode 100644
index 0000000..eed713a
--- /dev/null
+++ b/.cargo/config.toml
@@ -0,0 +1,16 @@
+[alias]
+fcheck = "run -p xtask -- execute cargo +nightly check --config .cargo/config-nightly-fast-compile.toml"
+fbuild = "run -p xtask -- execute cargo +nightly build --config .cargo/config-nightly-fast-compile.toml"
+frun = "run -p xtask -- execute cargo +nightly run --config .cargo/config-nightly-fast-compile.toml"
+
+[env]
+SLANG_USE_PREBUILT = "1"
+
+# This makes unix-like operating systems behave like Windows in that it will search for
+# dynamic libraries in the same directory as the executable. This is needed by the engine
+# crate to be able to load the shader-slang dynamic libraries.
+[target.'cfg(unix)']
+rustflags = ["-Clink-arg=-Wl,-rpath,$ORIGIN"]
+
+[target.'cfg(target_arch = "x86_64")']
+rustflags = ["-C", "target-cpu=x86-64-v2"]
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..6310257
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,26 @@
+# Formatting configuration for shaders
+BasedOnStyle: LLVM
+UseTab: Never
+IndentWidth: 4
+TabWidth: 4
+BreakBeforeBraces: Custom
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterControlStatement: Never
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: true
+ AfterStruct: true
+ AfterUnion: true
+ BeforeElse: false
+ BeforeWhile: false
+ IndentBraces: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+ColumnLimit: 90
+AccessModifierOffset: -4
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: OnePerLine
+BreakAfterOpenBracketFunction: true
+BreakBeforeCloseBracketFunction: true
diff --git a/Cargo.lock b/Cargo.lock
index 625d625..0f35427 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,6 +9,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "getrandom 0.3.3",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
name = "aho-corasick"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -24,6 +37,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
+name = "android-activity"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046"
+dependencies = [
+ "android-properties",
+ "bitflags 2.11.1",
+ "cc",
+ "cesu8",
+ "jni",
+ "jni-sys",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -31,9 +80,27 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
-version = "1.0.10"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "as-raw-xcb-connection"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
+
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
@@ -49,42 +116,42 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bindgen"
-version = "0.68.1"
+version = "0.71.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078"
+checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.11.1",
"cexpr",
"clang-sys",
- "lazy_static",
- "lazycell",
- "peeking_take_while",
+ "itertools",
+ "log",
+ "prettyplease 0.2.31",
"proc-macro2",
"quote",
"regex",
- "rustc-hash 1.1.0",
+ "rustc-hash",
"shlex",
- "syn",
+ "syn 2.0.100",
]
[[package]]
name = "bindgen"
-version = "0.71.1"
+version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3"
+checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
- "bitflags 2.9.0",
+ "bitflags 2.11.1",
"cexpr",
"clang-sys",
"itertools",
"log",
- "prettyplease",
+ "prettyplease 0.2.31",
"proc-macro2",
"quote",
"regex",
- "rustc-hash 2.1.1",
+ "rustc-hash",
"shlex",
- "syn",
+ "syn 2.0.100",
]
[[package]]
@@ -95,9 +162,33 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.9.0"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "build-rs"
+version = "0.3.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe808acca98fccf920154ee7833e791bfb683299be4aae7ec222ddffad8cd4f8"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
+checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
[[package]]
name = "bytemuck"
@@ -106,10 +197,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
[[package]]
-name = "byteorder"
-version = "1.5.0"
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
+[[package]]
+name = "bytes"
+version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
+checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+
+[[package]]
+name = "calloop"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec"
+dependencies = [
+ "bitflags 2.11.1",
+ "log",
+ "polling",
+ "rustix 0.38.44",
+ "slab",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20"
+dependencies = [
+ "calloop",
+ "rustix 0.38.44",
+ "wayland-backend",
+ "wayland-client",
+]
[[package]]
name = "cast"
@@ -118,6 +241,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
+name = "cc"
+version = "1.2.62"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98"
+dependencies = [
+ "find-msvc-tools",
+ "jobserver",
+ "libc",
+ "shlex",
+]
+
+[[package]]
+name = "cesu8"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
+
+[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -128,9 +269,35 @@ dependencies = [
[[package]]
name = "cfg-if"
-version = "1.0.0"
+version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "cfg_aliases"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
+
+[[package]]
+name = "cgl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "chrono"
+version = "0.4.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
+dependencies = [
+ "iana-time-zone",
+ "num-traits",
+ "windows-link",
+]
[[package]]
name = "ciborium"
@@ -160,6 +327,12 @@ dependencies = [
]
[[package]]
+name = "circular-buffer"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f5610398bbf405be68c4daeadee379d4dca31226cdf9076746a875bc58a69e3"
+
+[[package]]
name = "clang-sys"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -172,18 +345,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.5.32"
+version = "4.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6088f3ae8c3608d19260cd7445411865a485688711b78b5be70d78cd96136f83"
+checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.5.32"
+version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22a7ef7f676155edfb82daa97f99441f3ebf4a58d5e32f295a56259f1b6facc8"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstyle",
"clap_lex",
@@ -191,21 +364,74 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.7.4"
+version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
-name = "color_quant"
-version = "1.1.0"
+name = "combine"
+version = "4.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
+[[package]]
+name = "core-foundation-sys"
+version = "0.8.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation",
+ "libc",
+]
[[package]]
name = "crc32fast"
-version = "1.4.2"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
dependencies = [
"cfg-if",
]
@@ -245,58 +471,188 @@ dependencies = [
]
[[package]]
+name = "crossbeam-queue"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
name = "crunchy"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
[[package]]
-name = "ecs"
+name = "cursor-icon"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
+
+[[package]]
+name = "dear-imgui-build-support"
+version = "0.16.0-alpha.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9910bfb7b9c312809c1957efc9bd4c96e275ec08bc4888020472dab9bd2ae576"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "serde",
+ "serde_json",
+ "vcpkg",
+]
+
+[[package]]
+name = "dear-imgui-rs"
+version = "0.16.0-alpha.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4842e82949f15b8b507fd2808529311e6ddacba25721b4e0c4b7c868243174fd"
+dependencies = [
+ "bitflags 2.11.1",
+ "dear-imgui-sys",
+ "parking_lot",
+ "thiserror 2.0.18",
+]
+
+[[package]]
+name = "dear-imgui-sys"
+version = "0.16.0-alpha.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2894ef8941cca0807c4e56f4c80e1f479ccc61100554c803df4e4cc955ec3dd"
+dependencies = [
+ "cc",
+ "dear-imgui-build-support",
+ "mint",
+]
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dispatch2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+]
+
+[[package]]
+name = "dlib"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
+[[package]]
+name = "dpi"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "engine"
+version = "0.1.0"
+dependencies = [
+ "bitflags 2.11.1",
+ "build-rs",
+ "circular-buffer",
+ "criterion",
+ "crossbeam-queue",
+ "dear-imgui-rs",
+ "engine-ecs",
+ "engine-macros",
+ "engine-reflection",
+ "glutin",
+ "image",
+ "intmap",
+ "opengl-bindings",
+ "parking_lot",
+ "paste",
+ "raw-window-handle",
+ "safer-ffi",
+ "seq-macro",
+ "serde",
+ "serde_json",
+ "shader-slang",
+ "thiserror 1.0.69",
+ "tracing",
+ "util-macros",
+ "winit",
+ "zerocopy",
+]
+
+[[package]]
+name = "engine-ecs"
version = "0.1.0"
dependencies = [
+ "anyhow",
"criterion",
- "ecs-macros",
- "hashbrown",
- "linkme",
+ "engine-ecs-macros",
+ "engine-reflection",
+ "hashbrown 0.15.2",
+ "libc",
"parking_lot",
"paste",
"seq-macro",
- "thiserror",
+ "thiserror 1.0.69",
"tracing",
+ "tracing-subscriber",
"util-macros",
"vizoxide",
+ "windows-sys 0.61.2",
]
[[package]]
-name = "ecs-macros"
+name = "engine-ecs-macros"
version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.100",
"toml",
]
[[package]]
-name = "either"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+name = "engine-macros"
+version = "0.1.0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
[[package]]
-name = "engine"
+name = "engine-reflection"
version = "0.1.0"
dependencies = [
- "bitflags 2.9.0",
- "ecs",
- "gl",
- "glfw",
- "image",
- "paste",
- "seq-macro",
- "thiserror",
- "tracing",
- "util-macros",
+ "thiserror 1.0.69",
]
[[package]]
@@ -306,6 +662,51 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
+name = "errno"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18"
+dependencies = [
+ "libc",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "ext-trait"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d772df1c1a777963712fb68e014235e80863d6a91a85c4e06ba2d16243a310e5"
+dependencies = [
+ "ext-trait-proc_macros",
+]
+
+[[package]]
+name = "ext-trait-proc_macros"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ab7934152eaf26aa5aa9f7371408ad5af4c31357073c9e84c3b9d7f11ad639a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "extension-traits"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a296e5a895621edf9fa8329c83aa1cb69a964643e36cf54d8d7a69b789089537"
+dependencies = [
+ "ext-trait",
+]
+
+[[package]]
+name = "extern-c"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320bea982e85d42441eb25c49b41218e7eaa2657e8f90bc4eca7437376751e23"
+
+[[package]]
name = "fdeflate"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -315,10 +716,26 @@ dependencies = [
]
[[package]]
+name = "filetime"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
+dependencies = [
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "find-msvc-tools"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+
+[[package]]
name = "flate2"
-version = "1.1.0"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "11faaf5a5236997af9848be0bef4db95824b1d534ebc64d0f0c6cf3e67bd38dc"
+checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -331,21 +748,97 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "futures-core"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
+
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
name = "game-newest"
version = "0.1.0"
dependencies = [
"engine",
+ "nu-ansi-term",
"tracing",
"tracing-subscriber",
]
[[package]]
-name = "gl"
-version = "0.14.0"
+name = "gethostname"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404"
+checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818"
dependencies = [
- "gl_generator",
+ "libc",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "wasi 0.11.1+wasi-snapshot-preview1",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "r-efi",
+ "wasi 0.14.2+wasi-0.2.4",
]
[[package]]
@@ -360,21 +853,64 @@ dependencies = [
]
[[package]]
-name = "glfw"
-version = "0.1.0"
+name = "glob"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280"
+
+[[package]]
+name = "glutin"
+version = "0.32.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325"
dependencies = [
- "bindgen 0.68.1",
- "bitflags 2.9.0",
- "libc",
- "thiserror",
- "util-macros",
+ "bitflags 2.11.1",
+ "cfg_aliases",
+ "cgl",
+ "dispatch2",
+ "glutin_egl_sys",
+ "glutin_glx_sys",
+ "glutin_wgl_sys",
+ "libloading",
+ "objc2 0.6.4",
+ "objc2-app-kit 0.3.1",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.1",
+ "once_cell",
+ "raw-window-handle",
+ "wayland-sys",
+ "windows-sys 0.52.0",
+ "x11-dl",
]
[[package]]
-name = "glob"
-version = "0.3.2"
+name = "glutin_egl_sys"
+version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2"
+checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2"
+dependencies = [
+ "gl_generator",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "glutin_glx_sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185"
+dependencies = [
+ "gl_generator",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e"
+dependencies = [
+ "gl_generator",
+]
[[package]]
name = "graphviz-sys"
@@ -407,36 +943,89 @@ dependencies = [
]
[[package]]
+name = "hashbrown"
+version = "0.17.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
+
+[[package]]
name = "hermit-abi"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbd780fe5cc30f81464441920d82ac8740e2e46b29a6fad543ddd075229ce37e"
[[package]]
+name = "http"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
+dependencies = [
+ "bytes",
+ "itoa",
+]
+
+[[package]]
+name = "httparse"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+
+[[package]]
+name = "iana-time-zone"
+version = "0.1.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "log",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
+[[package]]
name = "image"
-version = "0.24.9"
+version = "0.25.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
+checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
dependencies = [
"bytemuck",
- "byteorder",
- "color_quant",
- "jpeg-decoder",
+ "byteorder-lite",
+ "moxcms",
"num-traits",
"png",
+ "zune-core",
+ "zune-jpeg",
]
[[package]]
name = "indexmap"
-version = "2.8.0"
+version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058"
+checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
dependencies = [
"equivalent",
- "hashbrown",
+ "hashbrown 0.17.1",
]
[[package]]
+name = "intmap"
+version = "3.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2e611826a1868311677fdcdfbec9e8621d104c732d080f546a854530232f0ee"
+
+[[package]]
name = "is-terminal"
version = "0.4.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -444,7 +1033,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
dependencies = [
"hermit-abi",
"libc",
- "windows-sys",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -458,15 +1047,53 @@ dependencies = [
[[package]]
name = "itoa"
-version = "1.0.15"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
-name = "jpeg-decoder"
-version = "0.3.1"
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni-sys"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
+
+[[package]]
+name = "jobserver"
+version = "0.1.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
+dependencies = [
+ "getrandom 0.3.3",
+ "libc",
+]
+
+[[package]]
+name = "js-sys"
+version = "0.3.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0"
+checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "once_cell",
+ "wasm-bindgen",
+]
[[package]]
name = "khronos_api"
@@ -481,16 +1108,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
-name = "lazycell"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
-
-[[package]]
name = "libc"
-version = "0.2.171"
+version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6"
+checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "libloading"
@@ -499,36 +1120,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [
"cfg-if",
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
-name = "linkme"
-version = "0.3.32"
+name = "libredox"
+version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22d227772b5999ddc0690e733f734f95ca05387e329c4084fe65678c51198ffe"
+checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
- "linkme-impl",
+ "bitflags 2.11.1",
+ "libc",
+ "redox_syscall 0.5.10",
]
[[package]]
-name = "linkme-impl"
-version = "0.3.32"
+name = "linux-raw-sys"
+version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71a98813fa0073a317ed6a8055dcd4722a49d9b862af828ee68449adb799b6be"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "lock_api"
-version = "0.4.12"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
- "autocfg",
"scopeguard",
]
@@ -539,19 +1162,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
+name = "macro_rules_attribute"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf0c9b980bf4f3a37fd7b1c066941dd1b1d0152ce6ee6e8fe8c49b9f6810d862"
+dependencies = [
+ "macro_rules_attribute-proc_macro",
+ "paste",
+]
+
+[[package]]
+name = "macro_rules_attribute-proc_macro"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "58093314a45e00c77d5c508f76e77c3396afbbc0d01506e7fae47b018bac2b1d"
+
+[[package]]
name = "matchers"
-version = "0.1.0"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata 0.1.10",
+ "regex-automata",
]
[[package]]
name = "memchr"
-version = "2.7.4"
+version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+
+[[package]]
+name = "memmap2"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
+dependencies = [
+ "libc",
+]
[[package]]
name = "minimal-lexical"
@@ -570,6 +1218,52 @@ dependencies = [
]
[[package]]
+name = "mint"
+version = "0.5.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff"
+
+[[package]]
+name = "moxcms"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
+[[package]]
+name = "ndk"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4"
+dependencies = [
+ "bitflags 2.11.1",
+ "jni-sys",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
+
+[[package]]
+name = "ndk-sys"
+version = "0.6.0+11769913"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873"
+dependencies = [
+ "jni-sys",
+]
+
+[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -581,12 +1275,11 @@ dependencies = [
[[package]]
name = "nu-ansi-term"
-version = "0.46.0"
+version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
- "overload",
- "winapi",
+ "windows-sys 0.61.2",
]
[[package]]
@@ -599,10 +1292,277 @@ dependencies = [
]
[[package]]
+name = "num_enum"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179"
+dependencies = [
+ "num_enum_derive",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
+dependencies = [
+ "objc2-encode",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "libc",
+ "objc2 0.5.2",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-foundation 0.2.2",
+ "objc2-quartz-core",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+ "objc2-foundation 0.3.1",
+]
+
+[[package]]
+name = "objc2-cloud-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-contacts"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-core-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166"
+dependencies = [
+ "bitflags 2.11.1",
+ "dispatch2",
+ "objc2 0.6.4",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-core-location"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-contacts",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "dispatch",
+ "libc",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-link-presentation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-symbols"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc"
+dependencies = [
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-ui-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-cloud-kit",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+ "objc2-link-presentation",
+ "objc2-quartz-core",
+ "objc2-symbols",
+ "objc2-uniform-type-identifiers",
+ "objc2-user-notifications",
+]
+
+[[package]]
+name = "objc2-uniform-type-identifiers"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe"
+dependencies = [
+ "block2",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-user-notifications"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2",
+ "objc2 0.5.2",
+ "objc2-core-location",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
name = "once_cell"
-version = "1.21.1"
+version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "oorandom"
@@ -611,16 +1571,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
-name = "overload"
-version = "0.1.1"
+name = "opengl-bindings"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bitflags 2.11.1",
+ "gl_generator",
+ "glutin",
+ "safer-ffi",
+ "thiserror 1.0.69",
+ "toml",
+ "util-macros",
+]
+
+[[package]]
+name = "orbclient"
+version = "0.3.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
+checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43"
+dependencies = [
+ "libredox",
+]
[[package]]
name = "parking_lot"
-version = "0.12.3"
+version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
@@ -628,15 +1605,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.10"
+version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.5.10",
"smallvec",
- "windows-targets",
+ "windows-link",
]
[[package]]
@@ -646,10 +1623,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
[[package]]
-name = "peeking_take_while"
-version = "0.1.2"
+name = "percent-encoding"
+version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
+[[package]]
+name = "pin-project"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
+dependencies = [
+ "pin-project-internal",
+]
+
+[[package]]
+name = "pin-project-internal"
+version = "1.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
[[package]]
name = "pin-project-lite"
@@ -658,12 +1655,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
+name = "pkg-config"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
+
+[[package]]
name = "png"
-version = "0.17.16"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
+checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
dependencies = [
- "bitflags 1.3.2",
+ "bitflags 2.11.1",
"crc32fast",
"fdeflate",
"flate2",
@@ -671,13 +1674,56 @@ dependencies = [
]
[[package]]
+name = "polling"
+version = "3.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix 1.1.4",
+ "tracing",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "ppv-lite86"
+version = "0.2.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
+dependencies = [
+ "zerocopy",
+]
+
+[[package]]
+name = "prettyplease"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8646e95016a7a6c4adea95bafa8a16baab64b583356217f2c85db4a39d9a86"
+dependencies = [
+ "proc-macro2",
+ "syn 1.0.109",
+]
+
+[[package]]
name = "prettyplease"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb"
dependencies = [
"proc-macro2",
- "syn",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "proc-macro-crate"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35"
+dependencies = [
+ "toml_edit",
]
[[package]]
@@ -690,60 +1736,111 @@ dependencies = [
]
[[package]]
+name = "pxfm"
+version = "0.1.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d"
+
+[[package]]
+name = "quick-xml"
+version = "0.37.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "quote"
-version = "1.0.40"
+version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
-name = "redox_syscall"
-version = "0.5.10"
+name = "r-efi"
+version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
+checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+
+[[package]]
+name = "rand"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
- "bitflags 2.9.0",
+ "libc",
+ "rand_chacha",
+ "rand_core",
]
[[package]]
-name = "regex"
-version = "1.11.1"
+name = "rand_chacha"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
- "aho-corasick",
- "memchr",
- "regex-automata 0.4.9",
- "regex-syntax 0.8.5",
+ "ppv-lite86",
+ "rand_core",
]
[[package]]
-name = "regex-automata"
-version = "0.1.10"
+name = "rand_core"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
- "regex-syntax 0.6.29",
+ "getrandom 0.2.16",
]
[[package]]
-name = "regex-automata"
-version = "0.4.9"
+name = "raw-window-handle"
+version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
+[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "regex"
+version = "1.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.5",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
-name = "regex-syntax"
-version = "0.6.29"
+name = "regex-automata"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
[[package]]
name = "regex-syntax"
@@ -752,10 +1849,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
-name = "rustc-hash"
-version = "1.1.0"
+name = "ring"
+version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.16",
+ "libc",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
[[package]]
name = "rustc-hash"
@@ -764,10 +1869,111 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
-name = "ryu"
-version = "1.0.20"
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.11.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags 2.11.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.12.1",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "rustls"
+version = "0.23.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
+dependencies = [
+ "log",
+ "once_cell",
+ "ring",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.14.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
+dependencies = [
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-webpki"
+version = "0.103.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
+[[package]]
+name = "rustversion"
+version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
+checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+
+[[package]]
+name = "safer-ffi"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "435fdd58b61a6f1d8545274c1dfa458e905ff68c166e65e294a0130ef5e675bd"
+dependencies = [
+ "extern-c",
+ "libc",
+ "macro_rules_attribute",
+ "paste",
+ "safer_ffi-proc_macros",
+ "scopeguard",
+ "stabby",
+ "uninit",
+ "unwind_safe",
+ "with_builtin_macros",
+]
+
+[[package]]
+name = "safer_ffi-proc_macros"
+version = "0.1.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f25be5ba5f319542edb31925517e0380245ae37df50a9752cdbc05ef948156"
+dependencies = [
+ "macro_rules_attribute",
+ "prettyplease 0.1.25",
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
[[package]]
name = "same-file"
@@ -779,12 +1985,24 @@ dependencies = [
]
[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
+name = "semver"
+version = "1.0.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
+
+[[package]]
name = "seq-macro"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -792,34 +2010,45 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
[[package]]
name = "serde"
-version = "1.0.219"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
-version = "1.0.219"
+version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.100",
]
[[package]]
name = "serde_json"
-version = "1.0.140"
+version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
- "ryu",
"serde",
+ "serde_core",
+ "zmij",
]
[[package]]
@@ -832,6 +2061,33 @@ dependencies = [
]
[[package]]
+name = "sha2-const-stable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9"
+
+[[package]]
+name = "shader-slang"
+version = "0.1.0"
+source = "git+https://github.com/HampusMat/slang-rs?branch=prebuilt-and-artifacts#81ae0e45d8ca6c40c206e3c0fdf7a3a3c5b1301a"
+dependencies = [
+ "build-rs",
+ "shader-slang-sys",
+]
+
+[[package]]
+name = "shader-slang-sys"
+version = "0.1.0"
+source = "git+https://github.com/HampusMat/slang-rs?branch=prebuilt-and-artifacts#81ae0e45d8ca6c40c206e3c0fdf7a3a3c5b1301a"
+dependencies = [
+ "bindgen 0.72.1",
+ "build-rs",
+ "flate2",
+ "tar",
+ "ureq",
+]
+
+[[package]]
name = "sharded-slab"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -853,10 +2109,105 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
+name = "slab"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
name = "smallvec"
-version = "1.14.0"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.19.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
+checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016"
+dependencies = [
+ "bitflags 2.11.1",
+ "calloop",
+ "calloop-wayland-source",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 0.38.44",
+ "thiserror 1.0.69",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols",
+ "wayland-protocols-wlr",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
+[[package]]
+name = "stabby"
+version = "36.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "89b7e94eaf470c2e76b5f15fb2fb49714471a36cc512df5ee231e62e82ec79f8"
+dependencies = [
+ "rustversion",
+ "stabby-abi",
+]
+
+[[package]]
+name = "stabby-abi"
+version = "36.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0dc7a63b8276b54e51bfffe3d85da56e7906b2dcfcb29018a8ab666c06734c1a"
+dependencies = [
+ "rustc_version",
+ "rustversion",
+ "sha2-const-stable",
+ "stabby-macros",
+]
+
+[[package]]
+name = "stabby-macros"
+version = "36.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eecb7ec5611ec93ec79d120fbe55f31bea234dc1bed1001d4a071bb688651615"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "rand",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
+[[package]]
+name = "syn"
+version = "1.0.109"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
[[package]]
name = "syn"
@@ -870,12 +2221,32 @@ dependencies = [
]
[[package]]
+name = "tar"
+version = "0.4.46"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840"
+dependencies = [
+ "filetime",
+ "libc",
+ "xattr",
+]
+
+[[package]]
name = "thiserror"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
dependencies = [
- "thiserror-impl",
+ "thiserror-impl 1.0.69",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl 2.0.18",
]
[[package]]
@@ -886,7 +2257,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
]
[[package]]
@@ -945,9 +2327,9 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.41"
+version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"pin-project-lite",
"tracing-attributes",
@@ -956,46 +2338,122 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.28"
+version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.100",
]
[[package]]
name = "tracing-core"
-version = "0.1.33"
+version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
]
[[package]]
name = "tracing-subscriber"
-version = "0.3.19"
+version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
dependencies = [
+ "chrono",
"matchers",
"nu-ansi-term",
"once_cell",
- "regex",
+ "regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
+ "tracing-log",
]
[[package]]
name = "unicode-ident"
-version = "1.0.18"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[package]]
+name = "unicode-segmentation"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
+
+[[package]]
+name = "uninit"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e130f2ed46ca5d8ec13c7ff95836827f92f5f5f37fd2b2bf16f33c408d98bb6"
+dependencies = [
+ "extension-traits",
+]
+
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
+[[package]]
+name = "unwind_safe"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0976c77def3f1f75c4ef892a292c31c0bbe9e3d0702c63044d7c76db298171a3"
+
+[[package]]
+name = "ureq"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0"
+dependencies = [
+ "base64",
+ "flate2",
+ "log",
+ "percent-encoding",
+ "rustls",
+ "rustls-pki-types",
+ "ureq-proto",
+ "utf8-zero",
+ "webpki-roots",
+]
+
+[[package]]
+name = "ureq-proto"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+checksum = "e994ba84b0bd1b1b0cf92878b7ef898a5c1760108fe7b6010327e274917a808c"
+dependencies = [
+ "base64",
+ "http",
+ "httparse",
+ "log",
+]
+
+[[package]]
+name = "utf8-zero"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8c0a043c9540bae7c578c88f91dda8bd82e59ae27c21baca69c8b191aaf5a6e"
[[package]]
name = "util-macros"
@@ -1003,10 +2461,28 @@ version = "0.1.0"
dependencies = [
"proc-macro2",
"quote",
- "syn",
+ "syn 2.0.100",
]
[[package]]
+name = "valuable"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
name = "vizoxide"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1027,20 +2503,212 @@ dependencies = [
]
[[package]]
-name = "winapi"
-version = "0.3.9"
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "wasi"
+version = "0.14.2+wasi-0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+dependencies = [
+ "wit-bindgen-rt",
+]
+
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8"
dependencies = [
- "winapi-i686-pc-windows-gnu",
- "winapi-x86_64-pc-windows-gnu",
+ "js-sys",
+ "wasm-bindgen",
]
[[package]]
-name = "winapi-i686-pc-windows-gnu"
-version = "0.4.0"
+name = "wasm-bindgen-macro"
+version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.121"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 0.38.44",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61"
+dependencies = [
+ "bitflags 2.11.1",
+ "rustix 0.38.44",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-csd-frame"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
+dependencies = [
+ "bitflags 2.11.1",
+ "cursor-icon",
+ "wayland-backend",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.31.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182"
+dependencies = [
+ "rustix 0.38.44",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615"
+dependencies = [
+ "dlib",
+ "log",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.98"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webpki-roots"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d"
+dependencies = [
+ "rustls-pki-types",
+]
[[package]]
name = "winapi-util"
@@ -1048,14 +2716,85 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
- "windows-sys",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link",
+ "windows-result",
+ "windows-strings",
+]
+
+[[package]]
+name = "windows-implement"
+version = "0.60.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "windows-interface"
+version = "0.59.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-result"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-strings"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
]
[[package]]
-name = "winapi-x86_64-pc-windows-gnu"
-version = "0.4.0"
+name = "windows-sys"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.6",
+]
[[package]]
name = "windows-sys"
@@ -1063,7 +2802,46 @@ version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
+dependencies = [
+ "windows_aarch64_gnullvm 0.42.2",
+ "windows_aarch64_msvc 0.42.2",
+ "windows_i686_gnu 0.42.2",
+ "windows_i686_msvc 0.42.2",
+ "windows_x86_64_gnu 0.42.2",
+ "windows_x86_64_gnullvm 0.42.2",
+ "windows_x86_64_msvc 0.42.2",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
]
[[package]]
@@ -1072,30 +2850,66 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows_aarch64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
@@ -1108,39 +2922,285 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
+version = "0.42.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
+name = "winit"
+version = "0.30.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4"
+dependencies = [
+ "ahash",
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.11.1",
+ "block2",
+ "bytemuck",
+ "calloop",
+ "cfg_aliases",
+ "concurrent-queue",
+ "core-foundation",
+ "core-graphics",
+ "cursor-icon",
+ "dpi",
+ "js-sys",
+ "libc",
+ "memmap2",
+ "ndk",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+ "objc2-ui-kit",
+ "orbclient",
+ "percent-encoding",
+ "pin-project",
+ "raw-window-handle",
+ "redox_syscall 0.4.1",
+ "rustix 0.38.44",
+ "smithay-client-toolkit",
+ "smol_str",
+ "tracing",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols",
+ "wayland-protocols-plasma",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.52.0",
+ "x11-dl",
+ "x11rb",
+ "xkbcommon-dl",
+]
+
+[[package]]
name = "winnow"
-version = "0.7.4"
+version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
+checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
dependencies = [
"memchr",
]
[[package]]
+name = "wit-bindgen-rt"
+version = "0.39.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "with_builtin_macros"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a59d55032495429b87f9d69954c6c8602e4d3f3e0a747a12dea6b0b23de685da"
+dependencies = [
+ "with_builtin_macros-proc_macros",
+]
+
+[[package]]
+name = "with_builtin_macros-proc_macros"
+version = "0.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15bd7679c15e22924f53aee34d4e448c45b674feb6129689af88593e129f8f42"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12"
+dependencies = [
+ "as-raw-xcb-connection",
+ "gethostname",
+ "libc",
+ "libloading",
+ "once_cell",
+ "rustix 0.38.44",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d"
+
+[[package]]
+name = "xattr"
+version = "1.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
+dependencies = [
+ "libc",
+ "rustix 1.1.4",
+]
+
+[[package]]
+name = "xcursor"
+version = "0.3.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61"
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.11.1",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
name = "xml-rs"
version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
+
+[[package]]
+name = "xtask"
+version = "0.1.0"
+
+[[package]]
+name = "zerocopy"
+version = "0.8.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2578b716f8a7a858b7f02d5bd870c14bf4ddbbcf3a4c05414ba6503640505e3"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.8.42"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7e6cc098ea4d3bd6246687de65af3f920c430e236bee1e3bf2e441463f08a02f"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.100",
+]
+
+[[package]]
+name = "zeroize"
+version = "1.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+
+[[package]]
+name = "zmij"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+
+[[package]]
+name = "zune-core"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
+
+[[package]]
+name = "zune-jpeg"
+version = "0.5.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b7a1c0af6e5d8d1363f4994b7a091ccf963d8b694f7da5b0b9cceb82da2c0a6"
+dependencies = [
+ "zune-core",
+]
diff --git a/Cargo.toml b/Cargo.toml
index e14b33c..55fe089 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,13 +4,24 @@ version = "0.1.0"
edition = "2021"
[workspace]
-members = ["glfw", "engine", "ecs", "ecs-macros", "util-macros"]
+members = ["engine*", "util-macros", "opengl-bindings", "xtask"]
+
+[workspace.dependencies]
+engine = { path = "engine" }
+engine-macros = { path = "engine-macros" }
+engine-ecs = { path = "engine-ecs" }
+engine-ecs-macros = { path = "engine-ecs-macros" }
+engine-reflection = { path = "engine-reflection" }
+util-macros= { path = "util-macros" }
+opengl-bindings = { path = "opengl-bindings" }
[dependencies]
engine = { path = "./engine" }
-tracing = { version = "0.1.39", features = ["max_level_debug"] }
+tracing = { version = "0.1.39", features = ["max_level_trace", "release_max_level_warn"] }
[dependencies.tracing-subscriber]
version = "0.3.17"
-default-features = false
-features = ["std", "ansi", "fmt", "smallvec", "env-filter"]
+features = ["env-filter", "chrono", "tracing-log"]
+
+[target.'cfg(windows)'.dependencies]
+nu-ansi-term = "0.50.3"
diff --git a/THIRD_PARTY b/THIRD_PARTY
new file mode 100644
index 0000000..05887c8
--- /dev/null
+++ b/THIRD_PARTY
@@ -0,0 +1,97 @@
+
+Fira Mono
+===============================================================================
+
+Copyright (c) 2012-2013, The Mozilla Corporation and Telefonica S.A.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+https://openfontlicense.org
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/ecs/Cargo.toml b/ecs/Cargo.toml
deleted file mode 100644
index cf35a74..0000000
--- a/ecs/Cargo.toml
+++ /dev/null
@@ -1,28 +0,0 @@
-[package]
-name = "ecs"
-version = "0.1.0"
-edition = "2021"
-
-[features]
-vizoxide = ["dep:vizoxide"]
-
-[dependencies]
-seq-macro = "0.3.5"
-paste = "1.0.14"
-thiserror = "1.0.49"
-tracing = "0.1.39"
-linkme = "0.3.29"
-hashbrown = "0.15.2"
-parking_lot = "0.12.3"
-ecs-macros = { path = "../ecs-macros" }
-util-macros = { path = "../util-macros" }
-vizoxide = { version = "1.0.5", optional = true }
-
-[dev-dependencies.criterion]
-version = "0.5.1"
-default-features = false
-features = ["cargo_bench_support"]
-
-[[bench]]
-name = "query"
-harness = false
diff --git a/ecs/examples/with_sole.rs b/ecs/examples/with_sole.rs
deleted file mode 100644
index c3feaab..0000000
--- a/ecs/examples/with_sole.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-use ecs::pair::{ChildOf, Pair};
-use ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
-use ecs::sole::Single;
-use ecs::{static_entity, Component, Query, Sole, World};
-
-#[derive(Component)]
-struct Ammo
-{
- ammo_left: u32,
-}
-
-#[derive(Sole, Default)]
-struct AmmoCounter
-{
- counter: u32,
-}
-
-fn count_ammo(query: Query<(&Ammo,)>, mut ammo_counter: Single<AmmoCounter>)
-{
- for (ammo,) in &query {
- println!("Found {} ammo", ammo.ammo_left);
-
- ammo_counter.counter += ammo.ammo_left;
- }
-}
-
-fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>)
-{
- println!("Total ammo count: {}", ammo_counter.counter);
-
- assert_eq!(ammo_counter.counter, 19);
-}
-
-static_entity!(
- PRINT_AMMO_COUNT_PHASE,
- (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE))
-);
-
-fn main()
-{
- let mut world = World::new();
-
- world.register_system(*UPDATE_PHASE, count_ammo);
- world.register_system(*PRINT_AMMO_COUNT_PHASE, print_total_ammo_count);
-
- world.create_entity((Ammo { ammo_left: 4 },));
- world.create_entity((Ammo { ammo_left: 7 },));
- world.create_entity((Ammo { ammo_left: 8 },));
-
- world.add_sole(AmmoCounter::default()).unwrap();
-
- world.step();
-}
diff --git a/ecs/src/actions.rs b/ecs/src/actions.rs
deleted file mode 100644
index e0efeda..0000000
--- a/ecs/src/actions.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-use std::marker::PhantomData;
-use std::rc::{Rc, Weak};
-
-use crate::component::{Parts as ComponentParts, Sequence as ComponentSequence};
-use crate::system::{Param as SystemParam, System};
-use crate::uid::{Kind as UidKind, Uid};
-use crate::{ActionQueue, World};
-
-/// Used to to queue up actions for a [`World`] to perform.
-#[derive(Debug)]
-pub struct Actions<'world>
-{
- action_queue: &'world ActionQueue,
- action_queue_weak: Weak<ActionQueue>,
-}
-
-impl<'world> Actions<'world>
-{
- /// Queues up a entity to spawn at the end of the current tick.
- pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps)
- {
- self.action_queue
- .push(Action::Spawn(components.into_parts_array().into()));
- }
-
- /// Queues up despawning a entity at the end of the current tick.
- pub fn despawn(&mut self, entity_uid: Uid)
- {
- debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
- self.action_queue.push(Action::Despawn(entity_uid));
- }
-
- /// Queues up adding component(s) to a entity at the end of the current tick.
- pub fn add_components<Comps>(&mut self, entity_uid: Uid, components: Comps)
- where
- Comps: ComponentSequence,
- {
- debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
- if Comps::COUNT == 0 {
- return;
- }
-
- self.action_queue.push(Action::AddComponents(
- entity_uid,
- components.into_parts_array().into(),
- ));
- }
-
- /// Queues up removing component(s) from a entity at the end of the current tick.
- pub fn remove_components(
- &mut self,
- entity_uid: Uid,
- component_ids: impl IntoIterator<Item = Uid>,
- )
- {
- debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
- let mut component_ids = component_ids.into_iter().peekable();
-
- if component_ids.peek().is_none() {
- return;
- }
-
- self.action_queue.push(Action::RemoveComponents(
- entity_uid,
- component_ids.collect(),
- ));
- }
-
- /// Stops the [`World`]. The world will finish the current tick and that tick will be
- /// the last.
- pub fn stop(&mut self)
- {
- self.action_queue.push(Action::Stop);
- }
-
- /// Returns a struct which holds a weak reference to the [`World`] that `Actions`
- /// references and that can be used to aquire a new `Actions` instance if the
- /// referenced [`World`] is still alive.
- #[must_use]
- pub fn to_weak_ref(&self) -> WeakRef
- {
- WeakRef {
- action_queue: self.action_queue_weak.clone(),
- }
- }
-
- fn new(action_queue: &'world Rc<ActionQueue>) -> Self
- {
- Self {
- action_queue,
- action_queue_weak: Rc::downgrade(action_queue),
- }
- }
-}
-
-impl<'world> SystemParam<'world> for Actions<'world>
-{
- type Input = ();
-
- fn initialize<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self
- {
- Self::new(&world.data.action_queue)
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct WeakRef
-{
- action_queue: Weak<ActionQueue>,
-}
-
-impl WeakRef
-{
- /// Returns a struct which can be used to retrieve a [`Actions`].
- ///
- /// Returns [`None`] if the referenced [`World`] has been dropped.
- #[must_use]
- pub fn access(&self) -> Option<Ref<'_>>
- {
- Some(Ref {
- action_queue: self.action_queue.upgrade()?,
- _pd: PhantomData,
- })
- }
-}
-
-/// Intermediate between [`Actions`] and [`WeakRef`]. Contains a strong reference to
-/// a world which is not allowed direct access to.
-#[derive(Debug, Clone)]
-pub struct Ref<'weak_ref>
-{
- action_queue: Rc<ActionQueue>,
- _pd: PhantomData<&'weak_ref ()>,
-}
-
-impl Ref<'_>
-{
- #[must_use]
- pub fn to_actions(&self) -> Actions<'_>
- {
- Actions::new(&self.action_queue)
- }
-}
-
-/// A action for a [`System`] to perform.
-#[derive(Debug)]
-pub(crate) enum Action
-{
- Spawn(Vec<ComponentParts>),
- Despawn(Uid),
- AddComponents(Uid, Vec<ComponentParts>),
- RemoveComponents(Uid, Vec<Uid>),
- Stop,
-}
diff --git a/ecs/src/component.rs b/ecs/src/component.rs
deleted file mode 100644
index a0ed752..0000000
--- a/ecs/src/component.rs
+++ /dev/null
@@ -1,368 +0,0 @@
-use std::any::{type_name, Any};
-use std::error::Error;
-use std::fmt::Debug;
-use std::ops::{Deref, DerefMut};
-
-use seq_macro::seq;
-
-use crate::lock::{
- Error as LockError,
- MappedReadGuard,
- MappedWriteGuard,
- ReadGuard,
- WriteGuard,
-};
-use crate::system::Input as SystemInput;
-use crate::uid::Uid;
-use crate::util::Array;
-use crate::{EntityComponentRef, World};
-
-pub mod local;
-
-pub(crate) mod storage;
-
-pub trait Component: SystemInput + Any
-{
- type HandleMut<'component>: HandleFromEntityComponentRef<'component>
- where
- Self: Sized;
-
- type Handle<'component>: HandleFromEntityComponentRef<'component>
- where
- Self: Sized;
-
- /// Returns the ID of this component.
- fn id() -> Uid
- where
- Self: Sized;
-
- /// Returns the name of this component.
- fn name(&self) -> &'static str;
-}
-
-impl dyn Component
-{
- pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
- {
- (self as &mut dyn Any).downcast_mut()
- }
-
- pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
- {
- (self as &dyn Any).downcast_ref()
- }
-
- pub fn is<Other: 'static>(&self) -> bool
- {
- (self as &dyn Any).is::<Other>()
- }
-}
-
-impl Debug for dyn Component
-{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter.debug_struct("Component").finish_non_exhaustive()
- }
-}
-
-/// A sequence of components.
-pub trait Sequence
-{
- /// The number of components in this component sequence.
- const COUNT: usize;
-
- type PartsArray: Array<Parts>;
-
- fn into_parts_array(self) -> Self::PartsArray;
-}
-
-/// [`Component`] metadata.
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub struct Metadata
-{
- pub id: Uid,
-}
-
-impl Metadata
-{
- #[must_use]
- pub fn of<ComponentT: Component>() -> Self
- {
- Self { id: ComponentT::id() }
- }
-}
-
-pub trait HandleFromEntityComponentRef<'comp>: Sized
-{
- type Error: Error;
-
- /// Creates a new handle instance from a [`EntityComponentRef`].
- ///
- /// # Errors
- /// See the implementation's [`Self::Error`] type.
- fn from_entity_component_ref(
- entity_component_ref: Option<EntityComponentRef<'comp>>,
- world: &'comp World,
- ) -> Result<Self, Self::Error>;
-}
-
-#[derive(Debug)]
-pub struct Handle<'a, ComponentData: 'static>
-{
- inner: MappedReadGuard<'a, ComponentData>,
-}
-
-impl<'a, ComponentData: 'static> Handle<'a, ComponentData>
-{
- pub(crate) fn new(inner: ReadGuard<'a, Box<dyn Any>>) -> Self
- {
- Self {
- inner: inner.map(|component| {
- component
- .downcast_ref::<ComponentData>()
- .unwrap_or_else(|| {
- panic!(
- "Failed to downcast component to type {}",
- type_name::<ComponentData>()
- );
- })
- }),
- }
- }
-}
-
-impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp>
- for Handle<'comp, ComponentData>
-{
- type Error = HandleError;
-
- fn from_entity_component_ref(
- entity_component_ref: Option<EntityComponentRef<'comp>>,
- _world: &'comp World,
- ) -> Result<Self, Self::Error>
- {
- let entity_comp =
- entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?;
-
- Ok(Self::new(
- entity_comp
- .component()
- .read_nonblock()
- .map_err(AcquireComponentLockFailed)?,
- ))
- }
-}
-
-impl<ComponentData: 'static> Deref for Handle<'_, ComponentData>
-{
- type Target = ComponentData;
-
- fn deref(&self) -> &Self::Target
- {
- &self.inner
- }
-}
-
-#[derive(Debug)]
-pub struct HandleMut<'a, ComponentData: 'static>
-{
- inner: MappedWriteGuard<'a, ComponentData>,
-}
-
-impl<'a, ComponentData: 'static> HandleMut<'a, ComponentData>
-{
- pub(crate) fn new(inner: WriteGuard<'a, Box<dyn Any>>) -> Self
- {
- Self {
- inner: inner.map(|component| {
- component
- .downcast_mut::<ComponentData>()
- .unwrap_or_else(|| {
- panic!(
- "Failed to downcast component to type {}",
- type_name::<ComponentData>()
- );
- })
- }),
- }
- }
-}
-
-impl<'comp, ComponentData: 'static> HandleFromEntityComponentRef<'comp>
- for HandleMut<'comp, ComponentData>
-{
- type Error = HandleError;
-
- fn from_entity_component_ref(
- entity_component_ref: Option<EntityComponentRef<'comp>>,
- _world: &'comp World,
- ) -> Result<Self, Self::Error>
- {
- let entity_comp =
- entity_component_ref.ok_or(HandleError::ComponentDoesNotExist)?;
-
- Ok(Self::new(
- entity_comp
- .component()
- .write_nonblock()
- .map_err(AcquireComponentLockFailed)?,
- ))
- }
-}
-
-impl<ComponentData: 'static> Deref for HandleMut<'_, ComponentData>
-{
- type Target = ComponentData;
-
- fn deref(&self) -> &Self::Target
- {
- &self.inner
- }
-}
-
-impl<ComponentData: 'static> DerefMut for HandleMut<'_, ComponentData>
-{
- fn deref_mut(&mut self) -> &mut Self::Target
- {
- &mut self.inner
- }
-}
-
-#[derive(Debug, thiserror::Error)]
-pub enum HandleError
-{
- #[error(transparent)]
- AcquireComponentLockFailed(#[from] AcquireComponentLockFailed),
-
- #[error("Component does not exist")]
- ComponentDoesNotExist,
-}
-
-#[derive(Debug, thiserror::Error)]
-#[error(transparent)]
-pub struct AcquireComponentLockFailed(LockError);
-
-macro_rules! inner {
- ($c: tt) => {
- seq!(I in 0..=$c {
- impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*)
- {
- const COUNT: usize = $c + 1;
-
- type PartsArray = [Parts; $c + 1];
-
- fn into_parts_array(self) -> Self::PartsArray
- {
- [#({
- self.I.into_parts()
- },)*]
- }
- }
- });
- };
-}
-
-seq!(C in 0..=16 {
- inner!(C);
-});
-
-impl Sequence for ()
-{
- type PartsArray = [Parts; 0];
-
- const COUNT: usize = 0;
-
- fn into_parts_array(self) -> Self::PartsArray
- {
- []
- }
-}
-
-pub trait IntoParts
-{
- fn into_parts(self) -> Parts;
-}
-
-impl<ComponentT> IntoParts for ComponentT
-where
- ComponentT: Component,
-{
- fn into_parts(self) -> Parts
- {
- Parts::builder()
- .name(type_name::<Self>())
- .build(Self::id(), self)
- }
-}
-
-/// The parts of a component.
-#[derive(Debug)]
-#[non_exhaustive]
-pub struct Parts
-{
- id: Uid,
- name: &'static str,
- data: Box<dyn Any>,
-}
-
-impl Parts
-{
- #[must_use]
- pub fn id(&self) -> Uid
- {
- self.id
- }
-
- #[must_use]
- pub fn name(&self) -> &'static str
- {
- self.name
- }
-
- #[must_use]
- pub fn builder() -> PartsBuilder
- {
- PartsBuilder::default()
- }
-
- pub(crate) fn into_data(self) -> Box<dyn Any>
- {
- self.data
- }
-}
-
-#[derive(Debug)]
-pub struct PartsBuilder
-{
- name: &'static str,
-}
-
-impl PartsBuilder
-{
- #[must_use]
- pub fn name(mut self, name: &'static str) -> Self
- {
- self.name = name;
- self
- }
-
- #[must_use]
- pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts
- {
- Parts {
- id,
- name: self.name,
- data: Box::new(data),
- }
- }
-}
-
-impl Default for PartsBuilder
-{
- fn default() -> Self
- {
- Self { name: "(unspecified)" }
- }
-}
diff --git a/ecs/src/component/local.rs b/ecs/src/component/local.rs
deleted file mode 100644
index 0f6f641..0000000
--- a/ecs/src/component/local.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use std::ops::{Deref, DerefMut};
-
-use crate::component::{Component, HandleMut as ComponentHandleMut};
-use crate::system::{Param as SystemParam, System};
-use crate::World;
-
-/// Holds a component which is local to a single system.
-#[derive(Debug)]
-pub struct Local<'world, LocalComponent: Component>
-{
- local_component: ComponentHandleMut<'world, LocalComponent>,
-}
-
-impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent>
-where
- LocalComponent: Component,
-{
- type Input = LocalComponent;
-
- fn initialize<SystemImpl>(
- system: &mut impl System<'world, SystemImpl>,
- input: Self::Input,
- )
- {
- system.set_local_component(input);
- }
-
- fn new<SystemImpl>(
- system: &'world impl System<'world, SystemImpl>,
- _world: &'world World,
- ) -> Self
- {
- let local_component = system
- .get_local_component_mut::<LocalComponent>()
- .expect("Local component is uninitialized");
-
- Self { local_component }
- }
-}
-
-impl<LocalComponent> Deref for Local<'_, LocalComponent>
-where
- LocalComponent: Component,
-{
- type Target = LocalComponent;
-
- fn deref(&self) -> &Self::Target
- {
- &self.local_component
- }
-}
-
-impl<LocalComponent> DerefMut for Local<'_, LocalComponent>
-where
- LocalComponent: Component,
-{
- fn deref_mut(&mut self) -> &mut Self::Target
- {
- &mut self.local_component
- }
-}
diff --git a/ecs/src/entity.rs b/ecs/src/entity.rs
deleted file mode 100644
index 562f7ea..0000000
--- a/ecs/src/entity.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-use std::any::type_name;
-
-use linkme::distributed_slice;
-
-use crate::component::storage::archetype::{
- Archetype,
- Entity as ArchetypeEntity,
- MatchingComponentIter as ArchetypeMatchingComponentIter,
-};
-use crate::component::{
- Component,
- Handle as ComponentHandle,
- HandleFromEntityComponentRef,
- HandleMut as ComponentHandleMut,
-};
-use crate::uid::{Kind as UidKind, Uid};
-use crate::{EntityComponentRef, World};
-
-/// A handle to a entity.
-#[derive(Debug)]
-pub struct Handle<'a>
-{
- world: &'a World,
- archetype: &'a Archetype,
- entity: &'a ArchetypeEntity,
-}
-
-impl<'a> Handle<'a>
-{
- /// Returns the [`Uid`] of this entity.
- #[inline]
- #[must_use]
- pub fn uid(&self) -> Uid
- {
- self.entity.uid()
- }
-
- /// Returns a reference to the specified component in this entity. `None` is
- /// returned if the component isn't found in the entity.
- ///
- /// # Panics
- /// Will panic if:
- /// - The component's ID is not a component ID
- /// - The component is mutably borrowed elsewhere
- #[must_use]
- pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'_, ComponentT>>
- {
- assert_eq!(ComponentT::id().kind(), UidKind::Component);
-
- let component = self.get_matching_components(ComponentT::id()).next()?;
-
- Some(
- ComponentHandle::from_entity_component_ref(Some(component), self.world)
- .unwrap_or_else(|err| {
- panic!(
- "Taking component {} lock failed: {err}",
- type_name::<ComponentT>()
- );
- }),
- )
- }
-
- /// Returns a mutable reference to the specified component in this entity. `None` is
- /// returned if the component isn't found in the entity.
- ///
- /// # Panics
- /// Will panic if:
- /// - The component's ID is not a component ID
- /// - The component is borrowed elsewhere
- #[must_use]
- pub fn get_mut<ComponentT: Component>(
- &self,
- ) -> Option<ComponentHandleMut<'_, ComponentT>>
- {
- assert_eq!(ComponentT::id().kind(), UidKind::Component);
-
- let component = self.get_matching_components(ComponentT::id()).next()?;
-
- Some(
- ComponentHandleMut::from_entity_component_ref(Some(component), self.world)
- .unwrap_or_else(|err| {
- panic!(
- "Taking component {} lock failed: {err}",
- type_name::<ComponentT>()
- );
- }),
- )
- }
-
- #[inline]
- #[must_use]
- pub fn get_matching_components(&self, component_uid: Uid)
- -> MatchingComponentIter<'a>
- {
- MatchingComponentIter {
- inner: self.archetype.get_matching_component_indices(component_uid),
- entity: self.entity,
- }
- }
-
- pub(crate) fn new(
- world: &'a World,
- archetype: &'a Archetype,
- entity: &'a ArchetypeEntity,
- ) -> Self
- {
- Self { world, archetype, entity }
- }
-}
-
-#[derive(Debug)]
-pub struct MatchingComponentIter<'a>
-{
- inner: ArchetypeMatchingComponentIter<'a>,
- entity: &'a ArchetypeEntity,
-}
-
-impl<'a> Iterator for MatchingComponentIter<'a>
-{
- type Item = EntityComponentRef<'a>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- let (matching_component_id, index) = self.inner.next()?;
-
- Some(EntityComponentRef::new(
- matching_component_id,
- self.entity.components().get(index).unwrap(),
- ))
- }
-}
-
-#[allow(clippy::module_name_repetitions)]
-#[macro_export]
-macro_rules! static_entity {
- ($visibility: vis $ident: ident, $components: expr) => {
- $visibility static $ident: ::std::sync::LazyLock<$crate::uid::Uid> =
- ::std::sync::LazyLock::new(|| {
- $crate::uid::Uid::new_unique($crate::uid::Kind::Entity)
- });
-
- $crate::private::paste::paste! {
- mod [<__ecs_ $ident:lower _static_entity_priv>] {
- use super::*;
-
- #[$crate::private::linkme::distributed_slice(
- $crate::entity::CREATE_STATIC_ENTITIES
- )]
- #[linkme(crate=$crate::private::linkme)]
- static CREATE_STATIC_ENTITY: fn(&mut $crate::World) = |world| {
- world.create_entity_with_uid($components, *$ident);
- };
- }
- }
- }
-}
-
-#[distributed_slice]
-#[doc(hidden)]
-pub static CREATE_STATIC_ENTITIES: [fn(&mut World)];
diff --git a/ecs/src/event.rs b/ecs/src/event.rs
deleted file mode 100644
index 9cea807..0000000
--- a/ecs/src/event.rs
+++ /dev/null
@@ -1 +0,0 @@
-pub mod component;
diff --git a/ecs/src/event/component.rs b/ecs/src/event/component.rs
deleted file mode 100644
index ef09480..0000000
--- a/ecs/src/event/component.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//! Component events.
-
-use std::convert::Infallible;
-use std::fmt::Debug;
-
-use crate::Component;
-
-/// Pair relation for events emitted when:
-/// a) A entity with the target component is spawned.
-/// b) The target component is added to a entity.
-#[derive(Debug, Component)]
-pub struct Added(Infallible);
-
-/// Pair relation for events emitted when:
-/// a) The target component is removed from a entity.
-/// b) A entity with the target component is despawned.
-#[derive(Debug, Component)]
-pub struct Removed(Infallible);
diff --git a/ecs/src/lib.rs b/ecs/src/lib.rs
deleted file mode 100644
index 53abc6b..0000000
--- a/ecs/src/lib.rs
+++ /dev/null
@@ -1,710 +0,0 @@
-#![deny(clippy::all, clippy::pedantic)]
-
-use std::any::{type_name, Any, TypeId};
-use std::cell::RefCell;
-use std::fmt::Debug;
-use std::mem::ManuallyDrop;
-use std::rc::Rc;
-use std::sync::atomic::{AtomicBool, Ordering};
-use std::sync::Arc;
-
-use hashbrown::HashMap;
-
-use crate::actions::Action;
-use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent;
-use crate::component::storage::Storage as ComponentStorage;
-use crate::component::{
- Component,
- Parts as ComponentParts,
- Sequence as ComponentSequence,
-};
-use crate::entity::CREATE_STATIC_ENTITIES;
-use crate::event::component::{
- Added as ComponentAddedEvent,
- Removed as ComponentRemovedEvent,
-};
-use crate::extension::{Collector as ExtensionCollector, Extension};
-use crate::lock::Lock;
-use crate::pair::{ChildOf, DependsOn, Pair};
-use crate::phase::{Phase, START as START_PHASE};
-use crate::query::flexible::Query as FlexibleQuery;
-use crate::query::term::Without;
-use crate::query::{
- Iter as QueryIter,
- TermWithFieldTuple as QueryTermWithFieldTuple,
- TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
- Terms as QueryTerms,
- TermsBuilderInterface,
-};
-use crate::sole::Sole;
-use crate::stats::Stats;
-use crate::system::{System, SystemComponent};
-use crate::uid::{Kind as UidKind, Uid, Wildcard};
-
-pub mod actions;
-pub mod component;
-pub mod entity;
-pub mod event;
-pub mod extension;
-pub mod pair;
-pub mod phase;
-pub mod query;
-pub mod sole;
-pub mod stats;
-pub mod system;
-pub mod tuple;
-pub mod uid;
-pub mod util;
-
-#[doc(hidden)]
-pub mod private;
-
-mod lock;
-
-pub use ecs_macros::{Component, Sole};
-
-pub use crate::query::Query;
-
-#[derive(Debug)]
-pub struct World
-{
- data: WorldData,
- stop: AtomicBool,
- is_first_tick: AtomicBool,
-}
-
-impl World
-{
- #[must_use]
- pub fn new() -> Self
- {
- let mut world = Self {
- data: WorldData::default(),
- stop: AtomicBool::new(false),
- is_first_tick: AtomicBool::new(false),
- };
-
- world.add_sole(Stats::default()).ok();
-
- for create_static_entity in CREATE_STATIC_ENTITIES {
- create_static_entity(&mut world);
- }
-
- world
- }
-
- /// Creates a new entity with the given components.
- pub fn create_entity<Comps>(&mut self, components: Comps) -> Uid
- where
- Comps: ComponentSequence,
- {
- let entity_uid = Uid::new_unique(UidKind::Entity);
-
- self.create_entity_with_uid(components, entity_uid);
-
- entity_uid
- }
-
- #[tracing::instrument(skip_all)]
- #[doc(hidden)]
- pub fn create_entity_with_uid<Comps>(&mut self, components: Comps, entity_uid: Uid)
- where
- Comps: ComponentSequence,
- {
- debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
- if let Err(err) = self.data.component_storage.create_entity(entity_uid) {
- tracing::warn!("Failed to create entity: {err}");
- return;
- }
-
- let added_component_ids = Self::add_entity_components(
- entity_uid,
- components.into_parts_array(),
- &mut self.data.component_storage,
- );
-
- for comp_id in added_component_ids {
- self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
- }
- }
-
- /// Adds a globally shared singleton value.
- ///
- /// # Errors
- /// Returns `Err` if this [`Sole`] has already been added.
- pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
- where
- SoleT: Sole,
- {
- self.data.sole_storage.insert(sole)
- }
-
- pub fn register_system<'this, SystemImpl>(
- &'this mut self,
- phase_euid: Uid,
- system: impl System<'this, SystemImpl>,
- )
- {
- self.create_entity((
- SystemComponent { system: system.into_type_erased() },
- Pair::new::<DependsOn>(phase_euid),
- ));
- }
-
- pub fn register_observer_system<'this, SystemImpl>(
- &'this mut self,
- system: impl System<'this, SystemImpl>,
- event: Pair<Uid, Uid>,
- )
- {
- self.create_entity((
- SystemComponent { system: system.into_type_erased() },
- event,
- ));
- }
-
- /// Adds a extensions.
- pub fn add_extension(&mut self, extension: impl Extension)
- {
- let extension_collector = ExtensionCollector::new(self);
-
- extension.collect(extension_collector);
- }
-
- pub fn query<FieldTerms, FieldlessTerms>(&self) -> Query<FieldTerms, FieldlessTerms>
- where
- FieldTerms: QueryTermWithFieldTuple,
- FieldlessTerms: QueryTermWithoutFieldTuple,
- {
- Query::new(self)
- }
-
- pub fn flexible_query<const MAX_TERM_CNT: usize>(
- &self,
- terms: QueryTerms<MAX_TERM_CNT>,
- ) -> FlexibleQuery<'_, MAX_TERM_CNT>
- {
- FlexibleQuery::new(self, terms)
- }
-
- /// Performs a single tick.
- /// # Panics
- /// Will panic if mutable internal lock cannot be acquired.
- pub fn step(&mut self) -> StepResult
- {
- if self.stop.load(Ordering::Relaxed) {
- return StepResult::Stop;
- }
-
- if self
- .is_first_tick
- .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
- .is_ok()
- {
- self.query_and_run_systems(*START_PHASE);
- }
-
- self.perform_phases();
-
- self.data.component_storage.create_imaginary_archetypes();
-
- self.perform_queued_actions();
-
- if self.stop.load(Ordering::Relaxed) {
- return StepResult::Stop;
- }
-
- let mut stats_lock = self
- .data
- .sole_storage
- .get::<Stats>()
- .expect("No stats sole found")
- .write_nonblock()
- .expect("Failed to aquire read-write stats sole lock");
-
- let stats = stats_lock
- .downcast_mut::<Stats>()
- .expect("Casting stats sole to Stats type failed");
-
- stats.current_tick += 1;
-
- StepResult::Continue
- }
-
- /// Starts a loop which calls [`Self::step`] until the world is stopped.
- pub fn start_loop(&mut self)
- {
- while let StepResult::Continue = self.step() {}
- }
-
- #[cfg(feature = "vizoxide")]
- pub fn create_vizoxide_archetype_graph(
- &self,
- name: impl AsRef<str>,
- ) -> Result<vizoxide::Graph, vizoxide::GraphvizError>
- {
- use std::borrow::Cow;
-
- use crate::component::storage::{
- VizoxideArchetypeGraphEdgeKind,
- VizoxideArchetypeGraphParams,
- };
-
- self.data.component_storage.create_vizoxide_archetype_graph(
- name,
- VizoxideArchetypeGraphParams {
- create_node_name: |archetype, _| {
- Cow::Owned(format!(
- "[{}]",
- archetype
- .component_ids_sorted()
- .into_iter()
- .map(|comp_id| comp_id.to_string())
- .collect::<Vec<_>>()
- .join(", ")
- ))
- },
- create_node_cb: |_archetype, archetype_metadata, node_builder| {
- if archetype_metadata.is_imaginary {
- return node_builder.attribute("shape", "ellipse");
- }
-
- node_builder.attribute("shape", "box")
- },
- create_edge_cb: |_, _, edge_kind, edge_builder| {
- edge_builder.attribute(
- "color",
- match edge_kind {
- VizoxideArchetypeGraphEdgeKind::Add => "green",
- VizoxideArchetypeGraphEdgeKind::Remove => "red",
- },
- )
- },
- },
- )
- }
-
- fn query_and_run_systems(&self, phase_euid: Uid)
- {
- let system_query = self.flexible_query(
- QueryTerms::<2>::builder()
- .with_required([
- SystemComponent::id(),
- Pair::new::<DependsOn>(phase_euid).id(),
- ])
- .build(),
- );
-
- for (system_component,) in
- QueryIter::<(&SystemComponent,), _>::new(self, system_query.iter())
- {
- // SAFETY: The world lives long enough
- unsafe {
- system_component.system.run(self);
- }
- }
- }
-
- fn perform_child_phases(&self, parent_phase_euid: Uid)
- {
- let phase_query = self.flexible_query(
- QueryTerms::<2>::builder()
- .with_required([
- Phase::id(),
- Pair::new::<ChildOf>(parent_phase_euid).id(),
- ])
- .build(),
- );
-
- for child_phase_entity in &phase_query {
- self.query_and_run_systems(child_phase_entity.uid());
- self.perform_child_phases(child_phase_entity.uid());
- }
- }
-
- fn perform_phases(&self)
- {
- let phase_query = self.query::<(&Phase,), (Without<Pair<ChildOf, Wildcard>>,)>();
-
- for (phase_entity_id, _) in phase_query.iter_with_euids() {
- if phase_entity_id == *START_PHASE {
- continue;
- }
-
- self.query_and_run_systems(phase_entity_id);
- self.perform_child_phases(phase_entity_id);
- }
- }
-
- #[tracing::instrument(skip_all)]
- fn perform_queued_actions(&mut self)
- {
- let mut active_action_queue = match *self.data.action_queue.active_queue.borrow()
- {
- ActiveActionQueue::A => &self.data.action_queue.queue_a,
- ActiveActionQueue::B => &self.data.action_queue.queue_b,
- }
- .write_nonblock()
- .unwrap_or_else(|err| {
- panic!(
- "Failed to take read-write action queue lock {:?}: {err}",
- self.data.action_queue.active_queue
- );
- });
-
- let mut has_swapped_active_queue = false;
-
- for action in active_action_queue.drain(..) {
- match action {
- Action::Spawn(components) => {
- let new_entity_uid = Uid::new_unique(UidKind::Entity);
-
- if let Err(err) =
- self.data.component_storage.create_entity(new_entity_uid)
- {
- tracing::warn!("Failed to create entity: {err}");
- continue;
- }
-
- let added_component_ids = Self::add_entity_components(
- new_entity_uid,
- components,
- &mut self.data.component_storage,
- );
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for comp_id in added_component_ids {
- self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
- }
- }
- Action::Despawn(entity_uid) => {
- let removed_entity =
- match self.data.component_storage.remove_entity(entity_uid) {
- Ok(components) => components,
- Err(err) => {
- tracing::error!("Failed to despawn entity: {err}");
- return;
- }
- };
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for removed_ent_comp in removed_entity.components() {
- self.emit_event_by_id::<ComponentRemovedEvent>(
- removed_ent_comp.id(),
- );
- }
- }
- Action::AddComponents(entity_uid, components) => {
- let added_component_ids = Self::add_entity_components(
- entity_uid,
- components,
- &mut self.data.component_storage,
- );
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for comp_id in added_component_ids {
- self.emit_event_by_id::<ComponentAddedEvent>(comp_id);
- }
- }
- Action::RemoveComponents(entity_uid, component_ids) => {
- let removed_component_ids = Self::remove_entity_components(
- entity_uid,
- component_ids,
- &mut self.data.component_storage,
- );
-
- if !has_swapped_active_queue {
- self.swap_event_queue(&mut has_swapped_active_queue);
- }
-
- for comp_id in removed_component_ids {
- self.emit_event_by_id::<ComponentRemovedEvent>(comp_id);
- }
- }
- Action::Stop => {
- self.stop.store(true, Ordering::Relaxed);
- }
- }
- }
- }
-
- fn add_entity_components(
- entity_uid: Uid,
- components: impl IntoIterator<Item = ComponentParts>,
- component_storage: &mut ComponentStorage,
- ) -> Vec<Uid>
- {
- let component_iter = components.into_iter();
-
- let mut added_component_ids =
- Vec::<Uid>::with_capacity(component_iter.size_hint().0);
-
- for component_parts in component_iter {
- let comp_id = component_parts.id();
-
- if let Err(err) = component_storage.add_entity_component(
- entity_uid,
- (comp_id, component_parts.name(), component_parts.into_data()),
- ) {
- tracing::error!("Failed to add component to entity: {err}");
- continue;
- }
-
- added_component_ids.push(comp_id);
- }
-
- added_component_ids
- }
-
- fn remove_entity_components(
- entity_uid: Uid,
- component_ids: impl IntoIterator<Item = Uid>,
- component_storage: &mut ComponentStorage,
- ) -> Vec<Uid>
- {
- let component_id_iter = component_ids.into_iter();
-
- let mut removed_component_ids =
- Vec::<Uid>::with_capacity(component_id_iter.size_hint().0);
-
- for component_id in component_id_iter {
- if let Err(err) =
- component_storage.remove_entity_component(entity_uid, component_id)
- {
- tracing::error!("Failed to remove component to entity: {err}");
- continue;
- }
-
- removed_component_ids.push(component_id);
- }
-
- removed_component_ids
- }
-
- fn emit_event_by_id<Event: Component>(&self, target: Uid)
- {
- if target.kind() == UidKind::Pair {
- return;
- }
-
- let query = self.flexible_query(
- QueryTerms::<2>::builder()
- .with_required([SystemComponent::id(), Pair::new::<Event>(target).id()])
- .build(),
- );
-
- for (system,) in QueryIter::<(&SystemComponent,), _>::new(self, query.iter()) {
- unsafe {
- system.system.run(self);
- }
- }
- }
-
- fn swap_event_queue(&self, has_swapped_active_queue: &mut bool)
- {
- let mut active_queue = self.data.action_queue.active_queue.borrow_mut();
-
- *active_queue = match *active_queue {
- ActiveActionQueue::A => ActiveActionQueue::B,
- ActiveActionQueue::B => ActiveActionQueue::A,
- };
-
- *has_swapped_active_queue = true;
- }
-}
-
-impl Default for World
-{
- fn default() -> Self
- {
- Self::new()
- }
-}
-
-/// The result of calling [`World::step`].
-pub enum StepResult
-{
- /// Another step can be made.
- Continue,
-
- /// The world have been stopped so no step can be made again.
- Stop,
-}
-
-#[derive(Debug)]
-pub struct WorldData
-{
- component_storage: ComponentStorage,
- sole_storage: SoleStorage,
- action_queue: Rc<ActionQueue>,
-}
-
-impl Default for WorldData
-{
- fn default() -> Self
- {
- Self {
- component_storage: ComponentStorage::default(),
- sole_storage: SoleStorage::default(),
- action_queue: Rc::new(ActionQueue::default()),
- }
- }
-}
-
-#[derive(Debug)]
-pub struct EntityComponentRef<'a>
-{
- component_id: Uid,
- component: &'a ArchetypeEntityComponent,
-}
-
-impl<'a> EntityComponentRef<'a>
-{
- fn component(&self) -> &'a Lock<Box<dyn Any>>
- {
- self.component.component()
- }
-
- #[must_use]
- pub fn id(&self) -> Uid
- {
- self.component_id
- }
-
- fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent) -> Self
- {
- Self { component_id, component: comp }
- }
-}
-
-#[derive(Debug, Default, Clone, Copy)]
-enum ActiveActionQueue
-{
- #[default]
- A,
- B,
-}
-
-#[derive(Debug)]
-struct ActionQueue
-{
- queue_a: Lock<Vec<Action>>,
- queue_b: Lock<Vec<Action>>,
- active_queue: RefCell<ActiveActionQueue>,
-}
-
-impl ActionQueue
-{
- fn push(&self, action: Action)
- {
- match *self.active_queue.borrow() {
- ActiveActionQueue::A => self
- .queue_a
- .write_nonblock()
- .expect("Failed to aquire read-write action queue A lock")
- .push(action),
- ActiveActionQueue::B => self
- .queue_b
- .write_nonblock()
- .expect("Failed to aquire read-write action queue A lock")
- .push(action),
- }
- }
-}
-
-impl Default for ActionQueue
-{
- fn default() -> Self
- {
- Self {
- queue_a: Lock::new(Vec::new(), type_name::<Vec<Action>>()),
- queue_b: Lock::new(Vec::new(), type_name::<Vec<Action>>()),
- active_queue: RefCell::new(ActiveActionQueue::default()),
- }
- }
-}
-
-#[derive(Debug, thiserror::Error)]
-#[error("Sole {0} already exists")]
-pub struct SoleAlreadyExistsError(pub &'static str);
-
-#[derive(Debug)]
-struct StoredSole
-{
- sole: Arc<Lock<Box<dyn Sole>>>,
- drop_last: bool,
-}
-
-#[derive(Debug, Default)]
-struct SoleStorage
-{
- storage: HashMap<TypeId, ManuallyDrop<StoredSole>>,
-}
-
-impl SoleStorage
-{
- fn get<SoleT: Sole>(&self) -> Option<&Arc<Lock<Box<dyn Sole>>>>
- {
- self.storage
- .get(&TypeId::of::<SoleT>())
- .map(|sole| &sole.sole)
- }
-
- fn insert<SoleT: Sole>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
- {
- let sole_type_id = TypeId::of::<SoleT>();
-
- if self.storage.contains_key(&sole_type_id) {
- return Err(SoleAlreadyExistsError(type_name::<SoleT>()));
- }
-
- let drop_last = sole.drop_last();
-
- // TODO: Reconsider this maybe?
- #[allow(clippy::arc_with_non_send_sync)]
- self.storage.insert(
- sole_type_id,
- ManuallyDrop::new(StoredSole {
- sole: Arc::new(Lock::new(Box::new(sole), type_name::<SoleT>())),
- drop_last,
- }),
- );
-
- Ok(())
- }
-}
-
-impl Drop for SoleStorage
-{
- fn drop(&mut self)
- {
- let mut soles_to_drop_last = Vec::new();
-
- for sole in self.storage.values_mut() {
- if sole.drop_last {
- soles_to_drop_last.push(sole);
- continue;
- }
-
- unsafe {
- ManuallyDrop::drop(sole);
- }
- }
-
- for sole in &mut soles_to_drop_last {
- unsafe {
- ManuallyDrop::drop(sole);
- }
- }
- }
-}
diff --git a/ecs/src/pair.rs b/ecs/src/pair.rs
deleted file mode 100644
index 2055d5e..0000000
--- a/ecs/src/pair.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-use std::convert::Infallible;
-
-use crate::component::{IntoParts as IntoComponentParts, Parts as ComponentParts};
-use crate::entity::{
- Handle as EntityHandle,
- MatchingComponentIter as EntityMatchingComponentIter,
-};
-use crate::query::{
- TermWithField as QueryTermWithField,
- TermsBuilder as QueryTermsBuilder,
- TermsBuilderInterface,
-};
-use crate::uid::{PairParams as UidPairParams, Uid, Wildcard, With as WithUid};
-use crate::{Component, World};
-
-#[derive(Debug)]
-pub struct Pair<RelationElem: Element, TargetElem: Element>
-{
- relation: RelationElem,
- target: TargetElem,
-}
-
-impl Pair<Uid, Uid>
-{
- #[must_use]
- pub fn new<Relation: WithUid>(target: Uid) -> Self
- {
- Self { relation: Relation::uid(), target }
- }
-
- #[must_use]
- pub fn id(&self) -> Uid
- {
- Uid::new_pair(&UidPairParams {
- relation: self.relation,
- target: self.target,
- })
- }
-}
-
-impl IntoComponentParts for Pair<Uid, Uid>
-{
- fn into_parts(self) -> ComponentParts
- {
- ComponentParts::builder().name("Pair").build(self.id(), ())
- }
-}
-
-impl<Relation, Target> QueryTermWithField for Pair<Relation, Target>
-where
- Relation: WithUid,
- Target: WithUid,
-{
- type Field<'a> = Handle<'a>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
- )
- {
- terms_builder.with_required([Self::uid()]);
- }
-
- fn get_field<'world>(
- entity_handle: &EntityHandle<'world>,
- world: &'world World,
- ) -> Self::Field<'world>
- {
- let first_matching_comp = entity_handle
- .get_matching_components(Self::uid())
- .next()
- .expect("Not possible");
-
- Handle {
- world,
- pair_uid: first_matching_comp.id(),
- }
- }
-}
-
-impl<Relation, Target> WithUid for Pair<Relation, Target>
-where
- Relation: WithUid,
- Target: WithUid,
-{
- fn uid() -> Uid
- {
- Uid::new_pair(&UidPairParams {
- relation: Relation::uid(),
- target: Target::uid(),
- })
- }
-}
-
-impl<Relation> QueryTermWithField for &'static [Pair<Relation, Wildcard>]
-where
- Relation: WithUid,
-{
- type Field<'a> = HandleIter<'a>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
- )
- {
- terms_builder.with_required([Pair::<Relation, Wildcard>::uid()]);
- }
-
- fn get_field<'world>(
- entity_handle: &EntityHandle<'world>,
- world: &'world World,
- ) -> Self::Field<'world>
- {
- HandleIter {
- inner: entity_handle
- .get_matching_components(Pair::<Relation, Wildcard>::uid()),
- world,
- }
- }
-}
-
-pub struct Handle<'world>
-{
- world: &'world World,
- pair_uid: Uid,
-}
-
-impl Handle<'_>
-{
- #[must_use]
- pub fn get_target_entity(&self) -> Option<EntityHandle<'_>>
- {
- let archetype = self
- .world
- .data
- .component_storage
- .get_entity_archetype(self.pair_uid.target_entity())?;
-
- let Some(archetype_entity) =
- archetype.get_entity_by_id(self.pair_uid.target_entity())
- else {
- unreachable!();
- };
-
- Some(EntityHandle::new(self.world, archetype, archetype_entity))
- }
-}
-
-pub struct HandleIter<'a>
-{
- inner: EntityMatchingComponentIter<'a>,
- world: &'a World,
-}
-
-impl<'a> Iterator for HandleIter<'a>
-{
- type Item = Handle<'a>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- let matching_comp = self.inner.next()?;
-
- Some(Handle {
- world: self.world,
- pair_uid: matching_comp.id(),
- })
- }
-}
-
-pub trait Element: sealed::Sealed
-{
- type Value;
-}
-
-impl Element for Uid
-{
- type Value = Uid;
-}
-
-impl sealed::Sealed for Uid {}
-
-impl<WithUidT: WithUid> Element for WithUidT
-{
- type Value = Infallible;
-}
-
-impl<WithUidT: WithUid> sealed::Sealed for WithUidT {}
-
-/// Relation denoting a dependency to another entity
-#[derive(Debug, Default, Clone, Copy, Component)]
-pub struct DependsOn;
-
-/// Relation denoting being the child of another entity.
-#[derive(Debug, Default, Clone, Copy, Component)]
-pub struct ChildOf;
-
-mod sealed
-{
- pub trait Sealed {}
-}
diff --git a/ecs/src/phase.rs b/ecs/src/phase.rs
deleted file mode 100644
index 9f47fb8..0000000
--- a/ecs/src/phase.rs
+++ /dev/null
@@ -1,13 +0,0 @@
-use ecs_macros::Component;
-
-use crate::pair::{ChildOf, Pair};
-use crate::static_entity;
-
-#[derive(Debug, Default, Clone, Copy, Component)]
-pub struct Phase;
-
-static_entity!(pub START, (Phase,));
-
-static_entity!(pub PRE_UPDATE, (Phase,));
-
-static_entity!(pub UPDATE, (Phase, Pair::new::<ChildOf>(*PRE_UPDATE)));
diff --git a/ecs/src/private.rs b/ecs/src/private.rs
deleted file mode 100644
index 56a6552..0000000
--- a/ecs/src/private.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-#[doc(hidden)]
-pub use {linkme, paste};
diff --git a/ecs/src/query.rs b/ecs/src/query.rs
deleted file mode 100644
index 7e10c5b..0000000
--- a/ecs/src/query.rs
+++ /dev/null
@@ -1,564 +0,0 @@
-use std::any::type_name;
-use std::marker::PhantomData;
-
-use seq_macro::seq;
-
-use crate::component::{
- Component,
- Handle as ComponentHandle,
- HandleFromEntityComponentRef,
- HandleMut as ComponentHandleMut,
-};
-use crate::entity::Handle as EntityHandle;
-use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery};
-use crate::system::{Param as SystemParam, System};
-use crate::uid::{Kind as UidKind, Uid, With as WithUid};
-use crate::util::array_vec::ArrayVec;
-use crate::util::Array;
-use crate::World;
-
-pub mod flexible;
-pub mod term;
-
-#[derive(Debug)]
-pub struct Query<'world, FieldTerms, FieldlessTerms = ()>
-where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
-{
- world: &'world World,
- // A term tuple type can have a maximum of 17 elements
- inner: FlexibleQuery<'world, 17>,
- _pd: PhantomData<(FieldTerms, FieldlessTerms)>,
-}
-
-impl<'world, FieldTerms, FieldlessTerms> Query<'world, FieldTerms, FieldlessTerms>
-where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
-{
- /// Iterates over the entities matching this query, the iterator item being the entity
- /// components.
- #[must_use]
- pub fn iter<'query>(
- &'query self,
- ) -> Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>
- {
- tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
-
- Iter {
- world: self.world,
- iter: self.inner.iter(),
- comps_pd: PhantomData,
- }
- }
-
- /// Iterates over the entities matching this query, the iterator item being the entity
- /// [`Uid`] and the matching entity components.
- #[must_use]
- pub fn iter_with_euids<'query>(
- &'query self,
- ) -> ComponentAndEuidIter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>
- {
- tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
-
- ComponentAndEuidIter {
- world: self.world,
- iter: self.inner.iter(),
- comps_pd: PhantomData,
- }
- }
-
- /// Iterates over the entities matching this query using the iterator returned by
- /// `func`.
- ///
- /// This function exists so that a custom [`EntityHandle`] iterator can be given to
- /// [`Iter`] without giving the user access to a reference to the [`World`].
- #[must_use]
- pub fn iter_with<'query, OutIter>(
- &'query self,
- func: impl FnOnce(FlexibleQueryIter<'query>) -> OutIter,
- ) -> Iter<'query, 'world, FieldTerms, OutIter>
- where
- OutIter: Iterator<Item = EntityHandle<'query>>,
- {
- tracing::trace!("Searching for {}", std::any::type_name::<FieldTerms>());
-
- Iter {
- world: self.world,
- iter: func(self.inner.iter()),
- comps_pd: PhantomData,
- }
- }
-
- /// Returns the UID of the entity at the given query iteration index.
- #[must_use]
- pub fn get_entity_uid(&self, entity_index: usize) -> Option<Uid>
- {
- Some(self.inner.iter().nth(entity_index)?.uid())
- }
-
- pub(crate) fn new(world: &'world World) -> Self
- {
- let mut terms_builder = Terms::builder();
-
- FieldTerms::apply_terms_to_builder(&mut terms_builder);
- FieldlessTerms::apply_terms_to_builder(&mut terms_builder);
-
- Self {
- world,
- inner: world.flexible_query(terms_builder.build()),
- _pd: PhantomData,
- }
- }
-}
-
-impl<'query, 'world, FieldTerms, FieldlessTerms> IntoIterator
- for &'query Query<'world, FieldTerms, FieldlessTerms>
-where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
-{
- type IntoIter = Iter<'query, 'world, FieldTerms, FlexibleQueryIter<'query>>;
- type Item = FieldTerms::Fields<'query>;
-
- fn into_iter(self) -> Self::IntoIter
- {
- self.iter()
- }
-}
-
-impl<'world, FieldTerms, FieldlessTerms> SystemParam<'world>
- for Query<'world, FieldTerms, FieldlessTerms>
-where
- FieldTerms: TermWithFieldTuple,
- FieldlessTerms: TermWithoutFieldTuple,
-{
- type Input = ();
-
- fn initialize<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self
- {
- Self::new(world)
- }
-}
-
-#[derive(Debug)]
-pub struct Terms<const MAX_TERM_CNT: usize>
-{
- required_components: ArrayVec<Uid, MAX_TERM_CNT>,
- excluded_components: ArrayVec<Uid, MAX_TERM_CNT>,
-}
-
-impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT>
-{
- pub fn builder() -> TermsBuilder<MAX_TERM_CNT>
- {
- TermsBuilder::default()
- }
-}
-
-#[derive(Debug, Default)]
-#[must_use]
-pub struct TermsBuilder<const MAX_TERM_CNT: usize>
-{
- required_components: ArrayVec<Uid, MAX_TERM_CNT>,
- excluded_components: ArrayVec<Uid, MAX_TERM_CNT>,
-}
-
-#[allow(clippy::return_self_not_must_use)]
-pub trait TermsBuilderInterface
-{
- fn with<WithUidT: WithUid>(self) -> Self;
-
- fn without<WithUidT: WithUid>(self) -> Self;
-
- fn with_required(self, ids: impl Array<Uid>) -> Self;
-
- fn without_ids(self, ids: impl Array<Uid>) -> Self;
-}
-
-macro_rules! impl_terms_builder {
- ($($impl_content: tt)*) => {
- impl<const MAX_TERM_CNT: usize>
- TermsBuilderInterface for TermsBuilder<MAX_TERM_CNT>
- {
- $($impl_content)*
- }
-
- impl<const MAX_TERM_CNT: usize>
- TermsBuilderInterface for &mut TermsBuilder<MAX_TERM_CNT>
- {
- $($impl_content)*
- }
- };
-}
-
-impl_terms_builder! {
- #[allow(unused_mut)]
- fn with<WithUidT: WithUid>(mut self) -> Self
- {
- let insert_index = self.required_components
- .partition_point(|id| *id <= WithUidT::uid());
-
- self.required_components
- .insert(insert_index, WithUidT::uid());
-
- self
- }
-
- #[allow(unused_mut)]
- fn without<WithUidT: WithUid>(mut self) -> Self
- {
- let insert_index = self.excluded_components
- .partition_point(|id| *id <= WithUidT::uid());
-
- self.excluded_components
- .insert(insert_index, WithUidT::uid());
-
- self
- }
-
- #[allow(unused_mut)]
- fn with_required(mut self, mut ids: impl Array<Uid>) -> Self
- {
- if !ids.as_ref().is_sorted() {
- ids.as_mut().sort();
- }
-
- if self.required_components.is_empty() {
- self.required_components.extend(ids);
- return self;
- }
-
- let mut id_iter = ids.into_iter();
-
- while let Some(id) = id_iter.next() {
- let insert_index = self.required_components
- .partition_point(|other_id| *other_id <= id);
-
- if insert_index == self.required_components.len() {
- self.required_components.extend([id].into_iter().chain(id_iter));
-
- return self;
- }
-
- self.required_components
- .insert(insert_index, id);
-
- }
-
- self
- }
-
- #[allow(unused_mut)]
- fn without_ids(mut self, mut ids: impl Array<Uid>) -> Self
- {
- if !ids.as_ref().is_sorted() {
- ids.as_mut().sort();
- }
-
- if self.excluded_components.is_empty() {
- self.excluded_components.extend(ids);
- return self;
- }
-
- let mut id_iter = ids.into_iter();
-
- while let Some(id) = id_iter.next() {
- let insert_index = self.excluded_components
- .partition_point(|other_id| *other_id <= id);
-
- if insert_index == self.excluded_components.len() {
- self.excluded_components.extend([id].into_iter().chain(id_iter));
-
- return self;
- }
-
- self.excluded_components
- .insert(insert_index, id);
-
- }
-
- self
- }
-}
-
-impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT>
-{
- #[must_use]
- pub fn build(self) -> Terms<MAX_TERM_CNT>
- {
- debug_assert!(self.required_components.is_sorted());
- debug_assert!(self.excluded_components.is_sorted());
-
- Terms {
- required_components: self.required_components,
- excluded_components: self.excluded_components,
- }
- }
-}
-
-pub trait TermWithoutField
-{
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- );
-}
-
-pub trait TermWithField
-{
- type Field<'a>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- );
-
- fn get_field<'world>(
- entity_handle: &EntityHandle<'world>,
- world: &'world World,
- ) -> Self::Field<'world>;
-}
-
-impl<ComponentT: Component> TermWithField for &ComponentT
-{
- type Field<'a> = ComponentHandle<'a, ComponentT>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- terms_builder.with::<ComponentT>();
- }
-
- fn get_field<'world>(
- entity_handle: &EntityHandle<'world>,
- world: &'world World,
- ) -> Self::Field<'world>
- {
- assert_eq!(ComponentT::id().kind(), UidKind::Component);
-
- Self::Field::from_entity_component_ref(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next(),
- world,
- )
- .unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- })
- }
-}
-
-impl<ComponentT: Component> TermWithField for &mut ComponentT
-{
- type Field<'a> = ComponentHandleMut<'a, ComponentT>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- terms_builder.with::<ComponentT>();
- }
-
- fn get_field<'world>(
- entity_handle: &EntityHandle<'world>,
- world: &'world World,
- ) -> Self::Field<'world>
- {
- assert_eq!(ComponentT::id().kind(), UidKind::Component);
-
- Self::Field::from_entity_component_ref(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next(),
- world,
- )
- .unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- })
- }
-}
-
-pub trait TermWithoutFieldTuple
-{
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- );
-}
-
-pub trait TermWithFieldTuple
-{
- type Fields<'component>;
-
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- );
-
- fn get_fields<'component>(
- entity_handle: &EntityHandle<'component>,
- world: &'component World,
- ) -> Self::Fields<'component>;
-}
-
-pub struct Iter<'query, 'world, FieldTerms, EntityHandleIter>
-where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
-{
- world: &'world World,
- iter: EntityHandleIter,
- comps_pd: PhantomData<FieldTerms>,
-}
-
-impl<'query, 'world, FieldTerms, EntityHandleIter>
- Iter<'query, 'world, FieldTerms, EntityHandleIter>
-where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
- 'world: 'query,
-{
- /// Creates a new iterator from the given entity handle iterator.
- ///
- /// # Important
- /// All of the yielded entities of the entity handle iterator should match the
- /// terms `Terms`. The [`Self::next`] function will panic if it encounters a
- /// entity that does not match the terms `Terms`.
- pub fn new(world: &'world World, iter: EntityHandleIter) -> Self
- {
- Self { world, iter, comps_pd: PhantomData }
- }
-}
-
-impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
- for Iter<'query, 'world, FieldTerms, EntityHandleIter>
-where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
- 'world: 'query,
-{
- type Item = FieldTerms::Fields<'query>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- let entity_handle = self.iter.next()?;
-
- Some(FieldTerms::get_fields(&entity_handle, self.world))
- }
-}
-
-pub struct ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
-where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
-{
- world: &'world World,
- iter: EntityHandleIter,
- comps_pd: PhantomData<FieldTerms>,
-}
-
-impl<'query, 'world, FieldTerms, EntityHandleIter> Iterator
- for ComponentAndEuidIter<'query, 'world, FieldTerms, EntityHandleIter>
-where
- FieldTerms: TermWithFieldTuple,
- EntityHandleIter: Iterator<Item = EntityHandle<'query>>,
- 'world: 'query,
-{
- type Item = (Uid, FieldTerms::Fields<'query>);
-
- fn next(&mut self) -> Option<Self::Item>
- {
- let entity_handle = self.iter.next()?;
-
- Some((
- entity_handle.uid(),
- FieldTerms::get_fields(&entity_handle, self.world),
- ))
- }
-}
-
-macro_rules! impl_term_sequence {
- ($c: tt) => {
- seq!(I in 0..=$c {
- impl<#(Term~I: TermWithoutField,)*> TermWithoutFieldTuple for (#(Term~I,)*)
- {
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>
- )
- {
- #(
- Term~I::apply_to_terms_builder(terms_builder);
- )*
- }
- }
-
- impl<#(Term~I: TermWithField,)*> TermWithFieldTuple for (#(Term~I,)*)
- {
- type Fields<'component> = (#(Term~I::Field<'component>,)*);
-
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>
- )
- {
- #(
- Term~I::apply_to_terms_builder(terms_builder);
- )*
- }
-
- fn get_fields<'component>(
- entity_handle: &EntityHandle<'component>,
- world: &'component World,
- ) -> Self::Fields<'component>
- {
- (#(Term~I::get_field(entity_handle, world),)*)
- }
- }
- });
- };
-}
-
-seq!(C in 0..=16 {
- impl_term_sequence!(C);
-});
-
-impl TermWithoutFieldTuple for ()
-{
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- }
-}
-
-impl TermWithFieldTuple for ()
-{
- type Fields<'component> = ();
-
- fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
- _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- }
-
- fn get_fields<'component>(
- _entity_handle: &EntityHandle<'_>,
- _world: &'component World,
- ) -> Self::Fields<'component>
- {
- }
-}
diff --git a/ecs/src/query/flexible.rs b/ecs/src/query/flexible.rs
deleted file mode 100644
index 6d65ee0..0000000
--- a/ecs/src/query/flexible.rs
+++ /dev/null
@@ -1,86 +0,0 @@
-//! Low-level querying.
-use std::iter::{repeat_n, FlatMap, RepeatN, Zip};
-
-use crate::component::storage::archetype::{Archetype, EntityIter};
-use crate::component::storage::{ArchetypeRefIter, ArchetypeSearchTerms};
-use crate::entity::Handle as EntityHandle;
-use crate::query::Terms;
-use crate::World;
-
-/// Low-level entity query structure.
-#[derive(Debug)]
-pub struct Query<'world, const MAX_TERM_CNT: usize>
-{
- world: &'world World,
- terms: Terms<MAX_TERM_CNT>,
-}
-
-impl<'world, const MAX_TERM_CNT: usize> Query<'world, MAX_TERM_CNT>
-{
- /// Iterates over the entities matching this query.
- #[must_use]
- pub fn iter(&self) -> Iter<'_>
- {
- Iter {
- world: self.world,
- iter: self
- .world
- .data
- .component_storage
- .search_archetypes(ArchetypeSearchTerms {
- required_components: &self.terms.required_components,
- excluded_components: &self.terms.excluded_components,
- })
- .flat_map(
- (|archetype| {
- repeat_n(archetype, archetype.entity_cnt())
- .zip(archetype.entities())
- }) as ComponentIterMapFn,
- ),
- }
- }
-
- pub(crate) fn new(world: &'world World, terms: Terms<MAX_TERM_CNT>) -> Self
- {
- Self { world, terms }
- }
-}
-
-impl<'query, const MAX_TERM_CNT: usize> IntoIterator for &'query Query<'_, MAX_TERM_CNT>
-{
- type IntoIter = Iter<'query>;
- type Item = EntityHandle<'query>;
-
- fn into_iter(self) -> Self::IntoIter
- {
- self.iter()
- }
-}
-
-pub struct Iter<'query>
-{
- world: &'query World,
- iter: QueryEntityIter<'query>,
-}
-
-impl<'query> Iterator for Iter<'query>
-{
- type Item = EntityHandle<'query>;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- let (archetype, entity) = self.iter.next()?;
-
- Some(EntityHandle::new(self.world, archetype, entity))
- }
-}
-
-type ComponentIterMapFnOutput<'a> = Zip<RepeatN<&'a Archetype>, EntityIter<'a>>;
-
-type ComponentIterMapFn = for<'a> fn(&'a Archetype) -> ComponentIterMapFnOutput<'a>;
-
-type QueryEntityIter<'query> = FlatMap<
- ArchetypeRefIter<'query, 'query>,
- ComponentIterMapFnOutput<'query>,
- ComponentIterMapFn,
->;
diff --git a/ecs/src/query/term.rs b/ecs/src/query/term.rs
deleted file mode 100644
index 2e1ecca..0000000
--- a/ecs/src/query/term.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-use std::any::type_name;
-use std::marker::PhantomData;
-
-use crate::component::{
- Component,
- Handle as ComponentHandle,
- HandleFromEntityComponentRef,
- HandleMut as ComponentHandleMut,
-};
-use crate::query::{
- TermWithField,
- TermWithoutField,
- TermsBuilder,
- TermsBuilderInterface,
-};
-use crate::uid::With as WithUid;
-
-pub struct With<WithUidT>
-where
- WithUidT: WithUid,
-{
- _pd: PhantomData<WithUidT>,
-}
-
-impl<WithUidT> TermWithoutField for With<WithUidT>
-where
- WithUidT: WithUid,
-{
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- terms_builder.with::<WithUidT>();
- }
-}
-
-pub struct Without<WithUidT>
-where
- WithUidT: WithUid,
-{
- _pd: PhantomData<WithUidT>,
-}
-
-impl<WithUidT> TermWithoutField for Without<WithUidT>
-where
- WithUidT: WithUid,
-{
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- terms_builder.without::<WithUidT>();
- }
-}
-
-impl<ComponentT: Component> TermWithField for Option<&ComponentT>
-{
- type Field<'a> = Option<ComponentHandle<'a, ComponentT>>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- }
-
- fn get_field<'world>(
- entity_handle: &crate::entity::Handle<'world>,
- world: &'world crate::World,
- ) -> Self::Field<'world>
- {
- Some(
- ComponentHandle::<'world, ComponentT>::from_entity_component_ref(
- Some(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next()?,
- ),
- world,
- )
- .unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- }),
- )
- }
-}
-
-impl<ComponentT: Component> TermWithField for Option<&mut ComponentT>
-{
- type Field<'a> = Option<ComponentHandleMut<'a, ComponentT>>;
-
- fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
- _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
- )
- {
- }
-
- fn get_field<'world>(
- entity_handle: &crate::entity::Handle<'world>,
- world: &'world crate::World,
- ) -> Self::Field<'world>
- {
- Some(
- ComponentHandleMut::<'world, ComponentT>::from_entity_component_ref(
- Some(
- entity_handle
- .get_matching_components(ComponentT::id())
- .next()?,
- ),
- world,
- )
- .unwrap_or_else(|err| {
- panic!(
- "Creating handle to component {} failed: {err}",
- type_name::<ComponentT>()
- );
- }),
- )
- }
-}
diff --git a/ecs/src/sole.rs b/ecs/src/sole.rs
deleted file mode 100644
index 1cce419..0000000
--- a/ecs/src/sole.rs
+++ /dev/null
@@ -1,176 +0,0 @@
-use std::any::{type_name, Any};
-use std::fmt::Debug;
-use std::marker::PhantomData;
-use std::ops::{Deref, DerefMut};
-use std::sync::{Arc, Weak};
-
-use crate::lock::{Lock, WriteGuard};
-use crate::system::{Param as SystemParam, System};
-use crate::World;
-
-/// A type which has a single instance and is shared globally.
-pub trait Sole: Any
-{
- fn drop_last(&self) -> bool;
-
- fn as_any_mut(&mut self) -> &mut dyn Any;
-
- fn as_any(&self) -> &dyn Any;
-}
-
-impl dyn Sole
-{
- pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
- {
- self.as_any_mut().downcast_mut()
- }
-
- pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
- {
- self.as_any().downcast_ref()
- }
-}
-
-impl Debug for dyn Sole
-{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter.debug_struct("Sole").finish_non_exhaustive()
- }
-}
-
-/// Holds a reference to a globally shared singleton value.
-#[derive(Debug)]
-pub struct Single<'world, SoleT: Sole>
-{
- sole: WriteGuard<'world, Box<dyn Sole>>,
- sole_weak: Weak<Lock<Box<dyn Sole>>>,
- _ph: PhantomData<SoleT>,
-}
-
-impl<'world, SoleT> Single<'world, SoleT>
-where
- SoleT: Sole,
-{
- /// Returns a struct which holds a weak reference to the [`World`] that `Single`
- /// references and that can be used to aquire a new `Single` if the referenced
- /// [`World`] is still alive.
- #[must_use]
- pub fn to_weak_ref(&self) -> SingleWeakRef<SoleT>
- {
- SingleWeakRef {
- sole: self.sole_weak.clone(),
- _ph: PhantomData,
- }
- }
-
- fn new(sole: &'world Arc<Lock<Box<dyn Sole>>>) -> Self
- {
- Self {
- sole: sole.write_nonblock().unwrap_or_else(|_| {
- panic!(
- "Failed to aquire read-write lock to single component {}",
- type_name::<SoleT>()
- )
- }),
- sole_weak: Arc::downgrade(sole),
- _ph: PhantomData,
- }
- }
-}
-
-impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT>
-where
- SoleT: Sole,
-{
- type Input = ();
-
- fn initialize<SystemImpl>(
- _system: &mut impl System<'world, SystemImpl>,
- _input: Self::Input,
- )
- {
- }
-
- fn new<SystemImpl>(
- _system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self
- {
- let sole = world.data.sole_storage.get::<SoleT>().unwrap_or_else(|| {
- panic!("Sole {} was not found in world", type_name::<SoleT>())
- });
-
- Self::new(sole)
- }
-}
-
-impl<SoleT> Deref for Single<'_, SoleT>
-where
- SoleT: Sole,
-{
- type Target = SoleT;
-
- fn deref(&self) -> &Self::Target
- {
- self.sole.downcast_ref().unwrap()
- }
-}
-
-impl<SoleT> DerefMut for Single<'_, SoleT>
-where
- SoleT: Sole,
-{
- fn deref_mut(&mut self) -> &mut Self::Target
- {
- self.sole.downcast_mut().unwrap()
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct SingleWeakRef<SoleT>
-where
- SoleT: Sole,
-{
- sole: Weak<Lock<Box<dyn Sole>>>,
- _ph: PhantomData<SoleT>,
-}
-
-impl<SoleT> SingleWeakRef<SoleT>
-where
- SoleT: Sole,
-{
- /// Returns a struct which can be used to retrieve a [`Single`].
- ///
- /// Returns [`None`] if the referenced [`World`] has been dropped.
- #[must_use]
- pub fn access(&self) -> Option<SingleRef<'_, SoleT>>
- {
- Some(SingleRef {
- sole: self.sole.upgrade()?,
- _pd: PhantomData,
- })
- }
-}
-
-/// Intermediate between [`Single`] and [`SingleWeakRef`]. Contains a strong reference to
-/// a world which is not allowed direct access to.
-#[derive(Debug, Clone)]
-pub struct SingleRef<'weak_ref, SoleT>
-where
- SoleT: Sole,
-{
- sole: Arc<Lock<Box<dyn Sole>>>,
- _pd: PhantomData<&'weak_ref SoleT>,
-}
-
-impl<SoleT> SingleRef<'_, SoleT>
-where
- SoleT: Sole,
-{
- #[must_use]
- pub fn to_single(&self) -> Single<'_, SoleT>
- {
- Single::new(&self.sole)
- }
-}
diff --git a/ecs/src/stats.rs b/ecs/src/stats.rs
deleted file mode 100644
index 56a5c32..0000000
--- a/ecs/src/stats.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-use ecs_macros::Sole;
-
-#[derive(Debug, Default, Sole)]
-#[non_exhaustive]
-pub struct Stats
-{
- pub current_tick: u64,
-}
diff --git a/ecs/src/system.rs b/ecs/src/system.rs
deleted file mode 100644
index 603c015..0000000
--- a/ecs/src/system.rs
+++ /dev/null
@@ -1,186 +0,0 @@
-use std::any::Any;
-use std::convert::Infallible;
-use std::fmt::Debug;
-
-use ecs_macros::Component;
-use seq_macro::seq;
-
-use crate::component::{Component, HandleMut as ComponentHandleMut};
-use crate::tuple::{ReduceElement as TupleReduceElement, Tuple};
-use crate::World;
-
-pub mod stateful;
-
-pub trait System<'world, Impl>: 'static
-{
- type Input;
-
- #[must_use]
- fn initialize(self, input: Self::Input) -> Self;
-
- fn run<'this>(&'this self, world: &'world World)
- where
- 'this: 'world;
-
- fn into_type_erased(self) -> TypeErased;
-
- fn get_local_component_mut<LocalComponent: Component>(
- &self,
- ) -> Option<ComponentHandleMut<LocalComponent>>;
-
- fn set_local_component<LocalComponent: Component>(
- &mut self,
- local_component: LocalComponent,
- );
-}
-
-macro_rules! impl_system {
- ($c: tt) => {
- seq!(I in 0..$c {
- impl<'world, Func, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*)>
- for Func
- where
- Func: Fn(#(TParam~I,)*) + Copy + 'static,
- #(TParam~I: Param<'world, Input = ()>,)*
- {
- type Input = Infallible;
-
- fn initialize(self, _input: Self::Input) -> Self
- {
- self
- }
-
- fn run<'this>(&'this self, world: &'world World)
- where
- 'this: 'world
- {
- let func = *self;
-
- func(#({
- TParam~I::new(self, world)
- },)*);
- }
-
- fn into_type_erased(self) -> TypeErased
- {
- TypeErased {
- data: Box::new(self),
- run: Box::new(|data, world| {
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let data = unsafe { &*std::ptr::from_ref(data) };
-
- let me = data
- .downcast_ref::<Func>()
- .expect("Function downcast failed");
-
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let world = unsafe { &*std::ptr::from_ref(world) };
-
- me.run(world);
- }),
- }
- }
-
- fn get_local_component_mut<LocalComponent: Component>(
- &self,
- ) -> Option<ComponentHandleMut<LocalComponent>>
- {
- panic!("System does not have any local components");
- }
-
- fn set_local_component<LocalComponent: Component>(
- &mut self,
- _local_component: LocalComponent,
- ) {
- panic!("System does not have any local components");
- }
- }
- });
- };
-}
-
-seq!(C in 1..16 {
- impl_system!(C);
-});
-
-pub trait Into<Impl>
-{
- type System;
-
- fn into_system(self) -> Self::System;
-}
-
-pub struct TypeErased
-{
- data: Box<dyn Any>,
- run: Box<TypeErasedRunFn>,
-}
-
-impl TypeErased
-{
- /// Runs the system.
- ///
- /// # Safety
- /// `world_data` must live at least as long as the [`World`] the system belongs to.
- pub unsafe fn run(&self, world: &World)
- {
- // You have to dereference for downcasting to work for some reason
- let data = &*self.data;
-
- (self.run)(data, world);
- }
-}
-
-impl Debug for TypeErased
-{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
- {
- formatter.debug_struct("TypeErased").finish_non_exhaustive()
- }
-}
-
-/// Function in [`TypeErased`] used to run the system.
-type TypeErasedRunFn = dyn Fn(&dyn Any, &World);
-
-/// A parameter to a [`System`].
-pub trait Param<'world>
-{
- type Input;
-
- fn initialize<SystemImpl>(
- system: &mut impl System<'world, SystemImpl>,
- input: Self::Input,
- );
-
- fn new<SystemImpl>(
- system: &'world impl System<'world, SystemImpl>,
- world: &'world World,
- ) -> Self;
-}
-
-/// A type which can be used as input to a [`System`].
-pub trait Input: 'static {}
-
-/// Component tuple reducing operation to get the parameters that takes input.
-pub struct ParamWithInputFilter;
-
-impl<InputT: Input, Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter>
- for InputT
-where
- Accumulator: Tuple,
-{
- type Return = Accumulator::WithElementAtEnd<Self>;
-}
-
-impl<Accumulator> TupleReduceElement<Accumulator, ParamWithInputFilter> for ()
-{
- type Return = Accumulator;
-}
-
-#[derive(Debug, Component)]
-pub(crate) struct SystemComponent
-{
- pub(crate) system: TypeErased,
-}
diff --git a/ecs/src/system/stateful.rs b/ecs/src/system/stateful.rs
deleted file mode 100644
index 54f6979..0000000
--- a/ecs/src/system/stateful.rs
+++ /dev/null
@@ -1,159 +0,0 @@
-use std::any::{type_name, Any, TypeId};
-use std::panic::{RefUnwindSafe, UnwindSafe};
-
-use hashbrown::HashMap;
-use seq_macro::seq;
-
-use crate::component::{Component, HandleMut as ComponentHandleMut};
-use crate::lock::Lock;
-use crate::system::{
- Into as IntoSystem,
- Param,
- ParamWithInputFilter,
- System,
- TypeErased,
-};
-use crate::tuple::{
- Reduce as TupleReduce,
- Tuple,
- WithAllElemLtStatic as TupleWithAllElemLtStatic,
-};
-use crate::uid::Uid;
-use crate::World;
-
-/// A stateful system.
-pub struct Stateful<Func>
-{
- func: Func,
- local_components: HashMap<Uid, Lock<Box<dyn Any>>>,
-}
-
-macro_rules! impl_system {
- ($c: tt) => {
- seq!(I in 0..$c {
- impl<'world, Func, #(TParam~I,)*> System<'world, fn(&'world (), #(TParam~I,)*)>
- for Stateful<Func>
- where
- Func: Fn(#(TParam~I,)*) + Copy + RefUnwindSafe + UnwindSafe + 'static,
- #(TParam~I: Param<'world>,)*
- #(TParam~I::Input: 'static,)*
- (#(TParam~I::Input,)*): TupleReduce<
- ParamWithInputFilter,
- Out: Tuple<InOptions: TupleWithAllElemLtStatic>
- >,
- {
- type Input =
- <(#(TParam~I::Input,)*) as TupleReduce<ParamWithInputFilter>>::Out;
-
- fn initialize(mut self, input: Self::Input) -> Self
- {
- let mut option_input = input.into_in_options();
-
- let mut index = 0;
-
- #(
- if TypeId::of::<TParam~I::Input>() !=
- TypeId::of::<()>()
- {
- let input = option_input
- .get_mut::<Option<TParam~I::Input>>(index)
- .expect("Input element index out of range")
- .take()
- .expect("Input element is already taken");
-
- TParam~I::initialize(
- &mut self,
- input
- );
-
- #[allow(unused_assignments)]
- {
- index += 1;
- }
- }
- )*
-
- self
- }
-
- fn run<'this>(&'this self, world: &'world World)
- where
- 'this: 'world
- {
- let func = self.func;
-
- func(#({
- TParam~I::new(self, &world)
- },)*);
- }
-
- fn into_type_erased(self) -> TypeErased
- {
- TypeErased {
- data: Box::new(self),
- run: Box::new(|data, world| {
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let data = unsafe { &*std::ptr::from_ref::<dyn Any>(data) };
-
- let me = data.downcast_ref::<Self>().unwrap();
-
- // SAFETY: The caller of TypeErased::run ensures the lifetime
- // is correct
- let world = unsafe { &*std::ptr::from_ref(world) };
-
- me.run(world);
- }),
- }
- }
-
- fn get_local_component_mut<LocalComponent: Component>(
- &self,
- ) -> Option<ComponentHandleMut<LocalComponent>>
- {
- let local_component = self.local_components
- .get(&LocalComponent::id())?
- .write_nonblock()
- .expect("Failed to aquire read-write local component lock");
-
- Some(ComponentHandleMut::new(local_component))
- }
-
- fn set_local_component<LocalComponent: Component>(
- &mut self,
- local_component: LocalComponent,
- )
- {
- self.local_components
- .insert(
- LocalComponent::id(),
- Lock::new(
- Box::new(local_component),
- type_name::<LocalComponent>()
- )
- );
- }
- }
-
- impl<Func, #(TParam~I,)*> IntoSystem<fn(#(TParam~I,)*)>
- for Func
- where
- Func: Fn(#(TParam~I,)*) + Copy + 'static,
- {
- type System = Stateful<Func>;
-
- fn into_system(self) -> Self::System
- {
- Self::System {
- func: self,
- local_components: HashMap::new(),
- }
- }
- }
- });
- };
-}
-
-seq!(C in 1..16 {
- impl_system!(C);
-});
diff --git a/ecs/src/uid.rs b/ecs/src/uid.rs
deleted file mode 100644
index feed62c..0000000
--- a/ecs/src/uid.rs
+++ /dev/null
@@ -1,243 +0,0 @@
-use std::fmt::{Debug, Display, Formatter};
-use std::mem::transmute;
-use std::sync::atomic::{AtomicU32, Ordering};
-
-use crate::component::Component;
-use crate::util::{gen_mask_64, BitMask, NumberExt};
-
-static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID);
-
-static WILDCARD_ID: u32 = 1;
-
-const ID_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(32..=63));
-const RELATION_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(6..=31));
-const KIND_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(0..=1));
-
-#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
-#[repr(u8)]
-pub enum Kind
-{
- Pair = 3,
- Entity = 2,
- Component = 1,
-}
-
-/// A unique identifier.
-#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
-pub struct Uid
-{
- inner: u64,
-}
-
-impl Uid
-{
- /// The id part of the first unique `Uid`. The ids `0..Uid::FIRST_UNIQUE_ID` are
- /// reserved.
- pub const FIRST_UNIQUE_ID: u32 = 5;
-
- /// Returns a new unique entity/component ID.
- pub fn new_unique(kind: Kind) -> Self
- {
- let id = NEXT.fetch_add(1, Ordering::Relaxed);
-
- Self {
- inner: ID_BITS.field_prep(u64::from(id)) | KIND_BITS.field_prep(kind as u64),
- }
- }
-
- #[must_use]
- pub fn wildcard() -> Self
- {
- Self {
- inner: ID_BITS.field_prep(u64::from(WILDCARD_ID))
- | KIND_BITS.field_prep(Kind::Component as u64),
- }
- }
-
- /// Returns a new pair UID.
- ///
- /// # Panics
- /// Will panic if either the given relation or target is a pair UID.
- #[must_use]
- pub fn new_pair(params: &PairParams) -> Self
- {
- assert_ne!(
- params.relation.kind(),
- Kind::Pair,
- "Pair relation cannot be a pair"
- );
-
- assert_ne!(
- params.target.kind(),
- Kind::Pair,
- "Pair target cannot be a pair"
- );
-
- Self {
- inner: ID_BITS.field_prep(u64::from(params.target.id()))
- | RELATION_BITS.field_prep(u64::from(params.relation.id()))
- | KIND_BITS.field_prep(Kind::Pair as u64),
- }
- }
-
- #[must_use]
- pub fn id(&self) -> u32
- {
- let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else {
- unreachable!("Uid id does not fit in u32");
- };
-
- id
- }
-
- #[must_use]
- pub fn kind(&self) -> Kind
- {
- let Ok(kind) = u8::try_from(self.inner.field_get(KIND_BITS)) else {
- unreachable!("Uid kind does not fit in u8");
- };
-
- // SAFETY: The kind bits cannot be invalid since they are set using the Kind enum
- // in the new_unique function
- unsafe { transmute::<u8, Kind>(kind) }
- }
-
- /// If this `Uid` is a pair, returns the relation as a component `Uid`.
- ///
- /// # Panics
- /// Will panic if this `Uid` is not a pair.
- #[must_use]
- pub fn relation_component(&self) -> Self
- {
- assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
-
- Self {
- inner: ID_BITS.field_prep(u64::from(self.relation()))
- | KIND_BITS.field_prep(Kind::Component as u64),
- }
- }
-
- #[must_use]
- pub fn has_same_relation_as(&self, other: Self) -> bool
- {
- self.relation() == other.relation()
- }
-
- /// If this `Uid` is a pair, returns the relation as a entity `Uid`.
- ///
- /// # Panics
- /// Will panic if this `Uid` is not a pair.
- #[must_use]
- pub fn relation_entity(&self) -> Self
- {
- assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
-
- Self {
- inner: ID_BITS.field_prep(u64::from(self.relation()))
- | KIND_BITS.field_prep(Kind::Entity as u64),
- }
- }
-
- /// If this `Uid` is a pair, returns the target as a component `Uid`.
- ///
- /// # Panics
- /// Will panic if this `Uid` is not a pair.
- #[must_use]
- pub fn target_component(&self) -> Self
- {
- assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
-
- Self {
- inner: ID_BITS.field_prep(u64::from(self.id()))
- | KIND_BITS.field_prep(Kind::Component as u64),
- }
- }
-
- /// If this `Uid` is a pair, returns the target as a entity `Uid`.
- ///
- /// # Panics
- /// Will panic if this `Uid` is not a pair.
- #[must_use]
- pub fn target_entity(&self) -> Self
- {
- assert_eq!(self.kind(), Kind::Pair, "Uid is not a pair");
-
- Self {
- inner: ID_BITS.field_prep(u64::from(self.id()))
- | KIND_BITS.field_prep(Kind::Entity as u64),
- }
- }
-
- fn relation(self) -> u32
- {
- let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else {
- unreachable!("Uid relation does not fit in u32");
- };
-
- relation
- }
-}
-
-impl Debug for Uid
-{
- fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
- {
- formatter
- .debug_struct("Uid")
- .field("id", &self.id())
- .field("kind", &self.kind())
- .finish_non_exhaustive()
- }
-}
-
-impl Display for Uid
-{
- fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
- {
- if self.kind() == Kind::Pair {
- return write!(
- formatter,
- "({}, {})",
- self.relation(),
- self.target_component()
- );
- }
-
- if *self == Uid::wildcard() {
- return write!(formatter, "*");
- }
-
- write!(formatter, "{}", self.id())
- }
-}
-
-#[derive(Debug, Clone)]
-pub struct PairParams
-{
- pub relation: Uid,
- pub target: Uid,
-}
-
-pub trait With: 'static
-{
- fn uid() -> Uid;
-}
-
-impl<ComponentT: Component> With for ComponentT
-{
- fn uid() -> Uid
- {
- Self::id()
- }
-}
-
-#[derive(Debug)]
-pub enum Wildcard {}
-
-impl With for Wildcard
-{
- fn uid() -> Uid
- {
- Uid::wildcard()
- }
-}
diff --git a/ecs/tests/query.rs b/ecs/tests/query.rs
deleted file mode 100644
index 062fd5a..0000000
--- a/ecs/tests/query.rs
+++ /dev/null
@@ -1,322 +0,0 @@
-use ecs::component::Component;
-use ecs::query::term::Without;
-use ecs::query::{
- TermWithFieldTuple as QueryTermWithFieldTuple,
- TermWithoutFieldTuple as QueryTermWithoutFieldTuple,
-};
-use ecs::uid::Uid;
-use ecs::{Component, Query, World};
-use parking_lot::{Mutex, Once};
-
-pub static SETUP: Once = Once::new();
-
-pub static TEST_LOCK: Mutex<()> = Mutex::new(());
-
-#[derive(Component)]
-struct A;
-
-#[derive(Component)]
-struct B;
-
-#[derive(Component)]
-struct C;
-
-#[derive(Component)]
-struct D;
-
-#[derive(Component)]
-struct E;
-
-#[derive(Component)]
-struct F;
-
-#[derive(Component)]
-struct G;
-
-fn setup()
-{
- SETUP.call_once_force(|_| {
- assert_eq!(A::id().id(), Uid::FIRST_UNIQUE_ID);
- assert_eq!(B::id().id(), Uid::FIRST_UNIQUE_ID + 1);
- assert_eq!(C::id().id(), Uid::FIRST_UNIQUE_ID + 2);
- assert_eq!(D::id().id(), Uid::FIRST_UNIQUE_ID + 3);
- assert_eq!(E::id().id(), Uid::FIRST_UNIQUE_ID + 4);
- assert_eq!(F::id().id(), Uid::FIRST_UNIQUE_ID + 5);
- assert_eq!(G::id().id(), Uid::FIRST_UNIQUE_ID + 6);
- });
-}
-
-fn assert_query_finds_ents<QueryFieldTerms, QueryFieldlessTerms>(
- query: Query<'_, QueryFieldTerms, QueryFieldlessTerms>,
- mut expected_ent_ids: Vec<Uid>,
-) where
- QueryFieldTerms: QueryTermWithFieldTuple,
- QueryFieldlessTerms: QueryTermWithoutFieldTuple,
-{
- assert!(
- query.iter_with_euids().all(|(ent_id, _)| {
- let Some(index) = expected_ent_ids
- .iter()
- .position(|expected_id| *expected_id == ent_id)
- else {
- return false;
- };
-
- expected_ent_ids.remove(index);
-
- true
- }),
- "Unexpected entity was found. Expected entities left: {expected_ent_ids:?}"
- );
-
- assert_eq!(
- expected_ent_ids.len(),
- 0,
- concat!(
- "Not all entities expected to be found was found. ",
- "Expected entities left: {:?}"
- ),
- expected_ent_ids
- );
-}
-
-#[test]
-fn query_archetype_exists_with_edges_to_next_archetypes()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, C));
- let ent_2_id = world.create_entity((A, B, C, D, E));
- let ent_3_id = world.create_entity((A, B, C, E));
- let ent_4_id = world.create_entity((A, B, C, G, F));
-
- assert_query_finds_ents(
- world.query::<(&A, &B, &C), ()>(),
- vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id],
- );
-}
-
-#[test]
-fn query_archetype_exists_with_2_comps_diff_to_next_archetype()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, C, D, F));
-
- let ent_2_id = world.create_entity((A, B, F));
-
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
-}
-
-#[test]
-fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, F));
-
- let ent_2_id = world.create_entity((A, B, C, D, F));
-
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
-}
-
-#[test]
-fn query_archetype_exists_with_3_comps_diff_to_next_archetype()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, C, D, E, F));
-
- let ent_2_id = world.create_entity((A, B, F));
-
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
-}
-
-#[test]
-fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, F));
-
- let ent_2_id = world.create_entity((A, B, C, D, E, F));
-
- assert_query_finds_ents(world.query::<(&A, &B, &F), ()>(), vec![ent_1_id, ent_2_id]);
-}
-
-#[test]
-fn query_archetype_exists_with_4_comps_diff_to_next_archetype()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, C, D, E, F, G));
-
- let ent_2_id = world.create_entity((A, B, G));
-
- assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
-}
-
-#[test]
-fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, G));
-
- let ent_2_id = world.create_entity((A, B, C, D, E, F, G));
-
- assert_query_finds_ents(world.query::<(&A, &B, &G), ()>(), vec![ent_1_id, ent_2_id]);
-}
-
-#[test]
-fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, C, D, E, F, G));
-
- let ent_2_id = world.create_entity((A, B, G));
-
- assert_query_finds_ents(
- world.query::<(&A, Option<&E>, &G), ()>(),
- vec![ent_1_id, ent_2_id],
- );
-}
-
-#[test]
-fn query_archetype_nonexistant()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- world.create_entity((A, B, C));
-
- let ent_2_id = world.create_entity((A, B, C, D, E));
- let ent_3_id = world.create_entity((A, B, C, E));
-
- world.create_entity((A, B, C, G, F));
-
- assert_query_finds_ents(world.query::<(&A, &E), ()>(), vec![ent_2_id, ent_3_id]);
-}
-
-#[test]
-fn query_archetype_nonexistant_and_opt_comp()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- world.create_entity((A, B, C));
- let ent_2_id = world.create_entity((A, B, C, D, E));
- let ent_3_id = world.create_entity((A, B, C, E));
- world.create_entity((A, B, C, G, F));
-
- assert_query_finds_ents(
- world.query::<(&A, &E, Option<&D>), ()>(),
- vec![ent_2_id, ent_3_id],
- );
-}
-
-#[test]
-fn query_without_comp_and_archetype_exists()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- let ent_1_id = world.create_entity((A, B, C));
-
- world.create_entity((A, B, C, E));
- world.create_entity((A, B, C, F, E));
-
- let ent_2_id = world.create_entity((A, B, C, G));
- let ent_3_id = world.create_entity((A, B, C, G, F));
-
- assert_query_finds_ents(
- world.query::<(&A, &B, &C), (Without<E>,)>(),
- vec![ent_1_id, ent_2_id, ent_3_id],
- );
-}
-
-#[test]
-fn query_without_required_comp_and_archetype_exists()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- world.create_entity((A, B, C));
-
- world.create_entity((A, B, C, E));
- world.create_entity((A, B, C, F, E));
-
- world.create_entity((A, B, C, G));
- world.create_entity((A, B, C, G, F));
-
- assert_query_finds_ents(world.query::<(&A, &B), (Without<B>,)>(), vec![]);
-}
-
-#[test]
-fn query_without_comp_and_archetype_nonexistant()
-{
- setup();
-
- let _test_lock = TEST_LOCK.lock();
-
- let mut world = World::new();
-
- world.create_entity((A, B, C));
-
- let ent_1_id = world.create_entity((A, B, C, E));
-
- world.create_entity((A, B, C, F, E));
-
- let ent_2_id = world.create_entity((A, B, C, G, E));
- world.create_entity((A, B, C, G, F, E));
-
- assert_query_finds_ents(
- world.query::<(&A, &E), (Without<F>,)>(),
- vec![ent_1_id, ent_2_id],
- );
-}
diff --git a/ecs-macros/Cargo.toml b/engine-ecs-macros/Cargo.toml
index cf323fc..3a2e27b 100644
--- a/ecs-macros/Cargo.toml
+++ b/engine-ecs-macros/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "ecs-macros"
+name = "engine-ecs-macros"
version = "0.1.0"
edition = "2021"
diff --git a/ecs-macros/src/lib.rs b/engine-ecs-macros/src/lib.rs
index aab8dd2..9bc829b 100644
--- a/ecs-macros/src/lib.rs
+++ b/engine-ecs-macros/src/lib.rs
@@ -4,18 +4,7 @@ use std::path::PathBuf as FsPathBuf;
use proc_macro::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::spanned::Spanned;
-use syn::{
- parse,
- Attribute,
- Generics,
- Ident,
- Item,
- ItemEnum,
- ItemStruct,
- ItemUnion,
- Path,
- Type,
-};
+use syn::{parse, Generics, Ident, Item, ItemEnum, ItemStruct, ItemUnion, Path};
use toml::value::{Table as TomlTable, Value as TomlValue};
macro_rules! syn_path {
@@ -49,15 +38,11 @@ macro_rules! syn_path_segment {
/// - Not attributed to a type item
/// - The attributed-to type item is generic
/// - If parsing the user crate's `Cargo.toml` file fails.
-#[proc_macro_derive(Component, attributes(component))]
+#[proc_macro_derive(Component)]
pub fn component_derive(input: TokenStream) -> TokenStream
{
let item: TypeItem = parse::<Item>(input).unwrap().try_into().unwrap();
- let ComponentAttribute { handle_type, handle_mut_type } = item
- .attribute::<ComponentAttribute>("component")
- .unwrap_or_default();
-
let item_ident = item.ident();
let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl();
@@ -73,7 +58,7 @@ pub fn component_derive(input: TokenStream) -> TokenStream
let id_var = quote! {
static #id_var_ident: LazyLock<Uid> = LazyLock::new(|| {
- Uid::new_unique(UidKind::Component)
+ Uid::new_unique()
});
};
@@ -88,11 +73,7 @@ pub fn component_derive(input: TokenStream) -> TokenStream
use ::std::sync::{LazyLock, Mutex};
use #ecs_path::component::Component;
- use #ecs_path::component::{
- Handle as ComponentHandle,
- HandleMut as ComponentHandleMut
- };
- use #ecs_path::uid::{Uid, Kind as UidKind};
+ use #ecs_path::uid::Uid;
use #ecs_path::system::Input as SystemInput;
use super::*;
@@ -102,14 +83,51 @@ pub fn component_derive(input: TokenStream) -> TokenStream
impl #impl_generics Component for #item_ident #type_generics
#where_clause
{
- type HandleMut<'component> = #handle_mut_type;
- type Handle<'component> = #handle_type;
-
fn id() -> Uid
{
*#id_var_ident
}
+ fn type_reflection() -> Option<&'static #ecs_path::reflection::Type>
+ {
+ struct SpecializationTarget<T>(std::marker::PhantomData<T>);
+
+ trait HasReflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>;
+ }
+
+ trait NoReflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>;
+ }
+
+ impl<T> NoReflection for &SpecializationTarget<T>
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>
+ {
+ None
+ }
+ }
+
+ impl<T> HasReflection for SpecializationTarget<T>
+ where
+ T: #ecs_path::reflection::Reflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>
+ {
+ Some(T::type_reflection())
+ }
+ }
+
+ (&SpecializationTarget::<Self>(std::marker::PhantomData))
+ .type_reflection()
+ }
+
fn name(&self) -> &'static str
{
std::any::type_name::<Self>()
@@ -120,6 +138,17 @@ pub fn component_derive(input: TokenStream) -> TokenStream
#where_clause
{
}
+
+ impl #ecs_path::component::IntoParts for #item_ident
+ {
+ fn into_parts(self) -> #ecs_path::component::Parts
+ {
+ #ecs_path::component::Parts::builder()
+ .name(<Self as Component>::name(&self))
+ .type_reflection(<Self as Component>::type_reflection())
+ .build(<Self as Component>::id(), self)
+ }
+ }
}
}
.into()
@@ -128,51 +157,119 @@ pub fn component_derive(input: TokenStream) -> TokenStream
/// Generates a `Sole` implementation.
///
/// # Panics
-/// Will panic if not attributed to a type item or if parsing the user crate's
-/// `Cargo.toml` file fails.
-#[proc_macro_derive(Sole, attributes(sole))]
+/// Will panic if:
+/// - Not attributed to a type item
+/// - The attributed-to type item is generic
+/// - If parsing the user crate's `Cargo.toml` file fails.
+#[proc_macro_derive(Sole)]
pub fn sole_derive(input: TokenStream) -> TokenStream
{
let item: TypeItem = parse::<Item>(input).unwrap().try_into().unwrap();
let item_ident = item.ident();
- let sole_attr = item.attribute::<SoleAttribute>("sole").unwrap_or_default();
+ let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs));
- let drop_last = sole_attr.drop_last;
+ assert!(
+ item.generics().params.is_empty(),
+ "Generic types are not supported as sole components"
+ );
- let (impl_generics, type_generics, where_clause) = item.generics().split_for_impl();
+ let id_var_ident = format_ident!("{}_ID", item_ident.to_string().to_uppercase());
- let ecs_path = find_engine_ecs_crate_path().unwrap_or_else(|| syn_path!(ecs));
+ let id_var = quote! {
+ static #id_var_ident: LazyLock<Uid> = LazyLock::new(|| {
+ Uid::new_unique()
+ });
+ };
+
+ let mod_ident = format_ident!(
+ "__ecs_priv_sole_impl_{}",
+ item_ident.to_string().to_lowercase()
+ );
quote! {
- impl #impl_generics #ecs_path::sole::Sole for #item_ident #type_generics
- #where_clause
- {
- fn drop_last(&self) -> bool
- {
- #drop_last
- }
+ mod #mod_ident {
+ use ::std::any::{Any, TypeId};
+ use ::std::sync::{LazyLock, Mutex};
+
+ use #ecs_path::sole::Sole;
+ use #ecs_path::uid::Uid;
+ use #ecs_path::system::Input as SystemInput;
+
+ use super::*;
+
+ #id_var
- fn as_any_mut(&mut self) -> &mut dyn std::any::Any
+ impl Sole for #item_ident
{
- self
+ fn id() -> Uid
+ {
+ *#id_var_ident
+ }
+
+ fn type_reflection() -> Option<&'static #ecs_path::reflection::Type>
+ {
+ struct SpecializationTarget<T>(std::marker::PhantomData<T>);
+
+ trait HasReflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>;
+ }
+
+ trait NoReflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>;
+ }
+
+ impl<T> NoReflection for &SpecializationTarget<T>
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>
+ {
+ None
+ }
+ }
+
+ impl<T> HasReflection for SpecializationTarget<T>
+ where
+ T: #ecs_path::reflection::Reflection
+ {
+ fn type_reflection(&self)
+ -> Option<&'static #ecs_path::reflection::Type>
+ {
+ Some(T::type_reflection())
+ }
+ }
+
+ (&SpecializationTarget::<Self>(std::marker::PhantomData))
+ .type_reflection()
+ }
+
+ fn name(&self) -> &'static str
+ {
+ std::any::type_name::<Self>()
+ }
}
- fn as_any(&self) -> &dyn std::any::Any
+ impl #ecs_path::component::IntoParts for #item_ident
{
- self
+ fn into_parts(self) -> #ecs_path::component::Parts
+ {
+ #ecs_path::component::Parts::builder()
+ .name(<Self as Sole>::name(&self))
+ .is_sole(true)
+ .type_reflection(<Self as Sole>::type_reflection())
+ .build(<Self as Sole>::id(), self)
+ }
}
}
}
.into()
}
-trait FromAttribute: Sized
-{
- fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>;
-}
-
enum TypeItem
{
Struct(ItemStruct),
@@ -191,27 +288,6 @@ impl TypeItem
}
}
- fn attribute<Attr: FromAttribute>(&self, attr_ident: &str) -> Option<Attr>
- {
- let item_attrs = match &self {
- Self::Struct(struct_item) => &struct_item.attrs,
- &Self::Enum(enum_item) => &enum_item.attrs,
- &Self::Union(union_item) => &union_item.attrs,
- };
-
- let mut attr: Option<&Attribute> = None;
-
- for item_attr in item_attrs {
- assert!(attr.is_none(), "Expected only one {attr_ident} attribute");
-
- if item_attr.path().get_ident()? == attr_ident {
- attr = Some(item_attr);
- }
- }
-
- Some(Attr::from_attribute(attr?).unwrap())
- }
-
fn generics(&self) -> &Generics
{
match self {
@@ -252,36 +328,6 @@ impl ToTokens for TypeItem
}
}
-#[derive(Debug, Default)]
-struct SoleAttribute
-{
- drop_last: bool,
-}
-
-impl FromAttribute for SoleAttribute
-{
- fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>
- {
- let mut drop_last = false;
-
- attribute.parse_nested_meta(|meta| {
- if meta
- .path
- .get_ident()
- .is_some_and(|flag| flag == "drop_last")
- {
- drop_last = true;
-
- return Ok(());
- }
-
- Err(meta.error("Unrecognized token"))
- })?;
-
- Ok(Self { drop_last })
- }
-}
-
fn find_engine_ecs_crate_path() -> Option<Path>
{
let cargo_manifest_dir = FsPathBuf::from(std::env::var("CARGO_MANIFEST_DIR").ok()?);
@@ -289,9 +335,9 @@ fn find_engine_ecs_crate_path() -> Option<Path>
let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
let cargo_pkg_name = std::env::var("CARGO_PKG_NAME").ok()?;
- if cargo_pkg_name == "ecs" && cargo_crate_name != "ecs" {
- // Macro is used by a ecs crate example/test/benchmark
- return Some(syn_path!(ecs));
+ if cargo_pkg_name == "engine-ecs" && cargo_crate_name != "engine_ecs" {
+ // Macro is used by a engine-ecs crate example/test/benchmark
+ return Some(syn_path!(engine_ecs));
}
let crate_manifest = std::fs::read_to_string(cargo_manifest_dir.join("Cargo.toml"))
@@ -309,7 +355,7 @@ fn find_engine_ecs_crate_path() -> Option<Path>
_ => None,
}?;
- if package_name == "ecs" {
+ if package_name == "engine-ecs" {
return Some(syn_path!(crate));
}
@@ -326,61 +372,3 @@ fn find_engine_ecs_crate_path() -> Option<Path>
None
})
}
-
-#[derive(Debug)]
-struct ComponentAttribute
-{
- handle_type: proc_macro2::TokenStream,
- handle_mut_type: proc_macro2::TokenStream,
-}
-
-impl FromAttribute for ComponentAttribute
-{
- fn from_attribute(attribute: &Attribute) -> Result<Self, syn::Error>
- {
- let mut handle_type: Option<Type> = None;
- let mut handle_mut_type: Option<Type> = None;
-
- attribute.parse_nested_meta(|meta| {
- let Some(flag) = meta.path.get_ident() else {
- return Err(meta.error("Not a single identifier"));
- };
-
- if flag == "handle_type" {
- let value = meta.value()?;
-
- handle_type = Some(value.parse::<Type>()?);
-
- return Ok(());
- } else if flag == "handle_mut_type" {
- let value = meta.value()?;
-
- handle_mut_type = Some(value.parse::<Type>()?);
-
- return Ok(());
- }
-
- Err(meta.error("Unrecognized token"))
- })?;
-
- Ok(Self {
- handle_type: handle_type
- .map_or_else(|| Self::default().handle_type, ToTokens::into_token_stream),
- handle_mut_type: handle_mut_type.map_or_else(
- || Self::default().handle_mut_type,
- ToTokens::into_token_stream,
- ),
- })
- }
-}
-
-impl Default for ComponentAttribute
-{
- fn default() -> Self
- {
- Self {
- handle_type: quote! { ComponentHandle<'component, Self> },
- handle_mut_type: quote! { ComponentHandleMut<'component, Self> },
- }
- }
-}
diff --git a/engine-ecs/Cargo.toml b/engine-ecs/Cargo.toml
new file mode 100644
index 0000000..e9ce85c
--- /dev/null
+++ b/engine-ecs/Cargo.toml
@@ -0,0 +1,45 @@
+[package]
+name = "engine-ecs"
+version = "0.1.0"
+edition = "2021"
+
+[features]
+vizoxide = ["dep:vizoxide"]
+
+[dependencies]
+seq-macro = "0.3.5"
+paste = "1.0.14"
+thiserror = "1.0.49"
+tracing = "0.1.39"
+hashbrown = "0.15.2"
+parking_lot = "0.12.3"
+anyhow = "1.0.102"
+engine-ecs-macros = { workspace = true }
+engine-reflection = { workspace = true }
+util-macros = { workspace = true }
+vizoxide = { version = "1.0.5", optional = true }
+
+[target.'cfg(windows)'.dependencies.windows-sys]
+version = "0.61.2"
+features = [
+ "Win32_System_Threading",
+ "Win32_System_Diagnostics_Debug",
+ "Win32_Foundation"
+]
+
+[target.'cfg(target_os = "linux")'.dependencies.libc]
+version = "0.2.186"
+
+[dev-dependencies.criterion]
+version = "0.5.1"
+default-features = false
+features = ["cargo_bench_support"]
+
+[dev-dependencies.tracing-subscriber]
+version = "0.3.17"
+default-features = false
+features = ["std", "ansi", "fmt", "smallvec", "env-filter", "chrono"]
+
+[[bench]]
+name = "query"
+harness = false
diff --git a/ecs/benches/query.rs b/engine-ecs/benches/query.rs
index f14bb06..524719d 100644
--- a/ecs/benches/query.rs
+++ b/engine-ecs/benches/query.rs
@@ -2,7 +2,7 @@ use std::hint::black_box;
use std::path::PathBuf;
use criterion::{criterion_group, criterion_main, Criterion};
-use ecs::{Component, World};
+use engine_ecs::{Component, World};
#[derive(Component)]
struct Foo
@@ -64,7 +64,7 @@ struct EvenMoreText
fn spawn_1000_entities(world: &mut World)
{
for _ in 0..300 {
- world.create_entity((
+ world.spawn((
Bar {
_path: "/dev/zero".into(),
_num: 65789,
@@ -92,7 +92,7 @@ fn spawn_1000_entities(world: &mut World)
}
for _ in 0..700 {
- world.create_entity((
+ world.spawn((
Bar {
_path: "/dev/null".into(),
_num: 65789,
@@ -120,14 +120,14 @@ fn spawn_1000_entities(world: &mut World)
}
}
-fn benchbark(criterion: &mut Criterion)
+fn benchmark(criterion: &mut Criterion)
{
criterion.bench_function("Iterate 1000 entities", |bencher| {
let mut world = World::new();
spawn_1000_entities(&mut world);
- let query = world.query::<(&Bar, &Position, &Foo), ()>();
+ let query = world.query::<(&Bar, &Position, &Foo)>();
bencher.iter(|| {
for comps in query.iter() {
@@ -137,5 +137,5 @@ fn benchbark(criterion: &mut Criterion)
});
}
-criterion_group!(benches, benchbark);
+criterion_group!(benches, benchmark);
criterion_main!(benches);
diff --git a/engine-ecs/examples/component_changed_event.rs b/engine-ecs/examples/component_changed_event.rs
new file mode 100644
index 0000000..3f6b4a8
--- /dev/null
+++ b/engine-ecs/examples/component_changed_event.rs
@@ -0,0 +1,78 @@
+use engine_ecs::event::component::{Changed, EventMatchExt};
+use engine_ecs::pair::Pair;
+use engine_ecs::phase::UPDATE as UPDATE_PHASE;
+use engine_ecs::system::observer::Observe;
+use engine_ecs::{Component, Query, World};
+
+#[derive(Component)]
+struct SomeData
+{
+ num: u64,
+}
+
+#[derive(Component)]
+struct Greeting
+{
+ greeting: String,
+}
+
+fn say_hello(query: Query<(&SomeData, &mut Greeting)>)
+{
+ for (data, mut greeting) in &query {
+ println!("{}: {}", greeting.greeting, data.num);
+
+ if greeting.greeting == "Good evening" {
+ greeting.greeting = "Good morning".to_string();
+ greeting.set_changed();
+ }
+ }
+}
+
+fn print_changed_greetings(observe: Observe<'_, Pair<Changed, Greeting>>)
+{
+ println!("\nChanged greetings:");
+
+ for evt_match in &observe {
+ let greeting = evt_match.get_ent_target_comp();
+
+ println!("A greeting changed to {}", greeting.greeting);
+ }
+
+ println!("");
+}
+
+fn main()
+{
+ let mut world = World::new();
+
+ world.register_system(*UPDATE_PHASE, say_hello);
+
+ world.register_observer(print_changed_greetings);
+
+ world.spawn((
+ SomeData { num: 987_654 },
+ Greeting {
+ greeting: "Good afternoon".to_string(),
+ },
+ ));
+
+ world.spawn((
+ SomeData { num: 345 },
+ Greeting { greeting: "Good evening".to_string() },
+ ));
+
+ world.step();
+
+ world.step();
+
+ for (mut greeting,) in &world.query::<(&mut Greeting,)>() {
+ if greeting.greeting == "Good afternoon" {
+ greeting.greeting = "Yo yo".to_string();
+ greeting.set_changed();
+ }
+ }
+
+ world.step();
+
+ world.step();
+}
diff --git a/engine-ecs/examples/component_events.rs b/engine-ecs/examples/component_events.rs
new file mode 100644
index 0000000..82d9b6c
--- /dev/null
+++ b/engine-ecs/examples/component_events.rs
@@ -0,0 +1,64 @@
+use engine_ecs::actions::Actions;
+use engine_ecs::component::Component;
+use engine_ecs::event::component::{Changed, EventMatchExt, Removed};
+use engine_ecs::pair::Pair;
+use engine_ecs::phase::UPDATE;
+use engine_ecs::system::observer::Observe;
+use engine_ecs::{Component, Query, World};
+
+#[derive(Debug, Component)]
+struct CheeseCrumbs
+{
+ cnt: usize,
+}
+
+#[derive(Debug, Component)]
+struct Cheese
+{
+ name: &'static str,
+}
+
+fn eat_cheese(query: Query<(&Cheese, &mut CheeseCrumbs)>, mut actions: Actions)
+{
+ for (cheese_ent_id, (_, mut cheese_crumbs)) in query.iter_with_euids() {
+ println!("Eating cheese!");
+
+ cheese_crumbs.cnt += 40;
+ cheese_crumbs.set_changed();
+
+ actions.remove_components(cheese_ent_id, [Cheese::id()]);
+ }
+}
+
+fn on_cheese_removed(observe: Observe<Pair<Removed, Cheese>>)
+{
+ for evt_match in &observe {
+ let cheese = evt_match.get_ent_target_comp();
+
+ println!("{} cheese was eaten", cheese.name);
+ }
+}
+
+fn on_cheese_crumbs_changed(observe: Observe<Pair<Changed, CheeseCrumbs>>)
+{
+ for evt_match in &observe {
+ let cheese_crumbs = evt_match.get_ent_target_comp();
+
+ println!("Cheese crumbs count changed to {}", cheese_crumbs.cnt);
+ }
+}
+
+fn main()
+{
+ let mut world = World::new();
+
+ world.register_system(*UPDATE, eat_cheese);
+ world.register_observer(on_cheese_removed);
+ world.register_observer(on_cheese_crumbs_changed);
+
+ world.spawn((Cheese { name: "Brie" }, CheeseCrumbs { cnt: 0 }));
+ world.spawn((Cheese { name: "Parmesan" }, CheeseCrumbs { cnt: 0 }));
+ world.spawn((Cheese { name: "Gouda" }, CheeseCrumbs { cnt: 0 }));
+
+ world.step();
+}
diff --git a/engine-ecs/examples/component_relationship.rs b/engine-ecs/examples/component_relationship.rs
new file mode 100644
index 0000000..a85150e
--- /dev/null
+++ b/engine-ecs/examples/component_relationship.rs
@@ -0,0 +1,65 @@
+use engine_ecs::pair::Pair;
+use engine_ecs::phase::START as START_PHASE;
+use engine_ecs::{Component, Query, World};
+
+#[derive(Component)]
+struct Person
+{
+ name: String,
+}
+
+fn print_dog_likers(query: Query<(&Person, Pair<Likes, &Dogs>)>)
+{
+ for (person, liked_dogs) in &query {
+ println!(
+ "{} likes {} dogs!",
+ person.name,
+ if liked_dogs.large { "large" } else { "small" },
+ );
+ }
+}
+
+#[derive(Component)]
+struct Likes;
+
+#[derive(Component)]
+struct Cats;
+
+#[derive(Component)]
+struct Dogs
+{
+ large: bool,
+}
+
+fn main()
+{
+ let mut world = World::new();
+
+ world.register_system(*START_PHASE, print_dog_likers);
+
+ world.spawn((
+ Person { name: "Irving".to_string() },
+ Pair::builder()
+ .relation::<Likes>()
+ .target_as_data(Dogs { large: true })
+ .build(),
+ ));
+
+ world.spawn((
+ Person { name: "Mark".to_string() },
+ Pair::builder()
+ .relation::<Likes>()
+ .target_as_data(Cats)
+ .build(),
+ ));
+
+ world.spawn((
+ Person { name: "Helena".to_string() },
+ Pair::builder()
+ .relation::<Likes>()
+ .target_as_data(Dogs { large: false })
+ .build(),
+ ));
+
+ world.step();
+}
diff --git a/engine-ecs/examples/component_removed_event.rs b/engine-ecs/examples/component_removed_event.rs
new file mode 100644
index 0000000..23dfc92
--- /dev/null
+++ b/engine-ecs/examples/component_removed_event.rs
@@ -0,0 +1,46 @@
+use engine_ecs::actions::Actions;
+use engine_ecs::component::Component;
+use engine_ecs::event::component::{EventMatchExt, Removed};
+use engine_ecs::pair::Pair;
+use engine_ecs::phase::UPDATE;
+use engine_ecs::system::observer::Observe;
+use engine_ecs::{Component, Query, World};
+
+#[derive(Debug, Component)]
+struct Cheese
+{
+ name: &'static str,
+}
+
+fn eat_cheese(query: Query<(&Cheese,)>, mut actions: Actions)
+{
+ for (cheese_ent_id, (_,)) in query.iter_with_euids() {
+ println!("Eating cheese!");
+
+ actions.remove_components(cheese_ent_id, [Cheese::id()]);
+ }
+}
+
+fn on_cheese_removed(observe: Observe<Pair<Removed, Cheese>>)
+{
+ for evt_match in &observe {
+ let cheese = evt_match.get_ent_target_comp();
+
+ println!("{} cheese was eaten", cheese.name);
+ }
+}
+
+fn main()
+{
+ let mut world = World::new();
+
+ world.register_system(*UPDATE, eat_cheese);
+ world.register_observer(on_cheese_removed);
+
+ world.spawn((Cheese { name: "Brie" },));
+ world.spawn((Cheese { name: "Parmesan" },));
+ world.spawn((Cheese { name: "Gouda" },));
+
+ world.step();
+ world.step();
+}
diff --git a/engine-ecs/examples/error_handling.rs b/engine-ecs/examples/error_handling.rs
new file mode 100644
index 0000000..2025914
--- /dev/null
+++ b/engine-ecs/examples/error_handling.rs
@@ -0,0 +1,79 @@
+use engine_ecs::error::Error;
+use engine_ecs::event::component::{Changed, EventMatchExt};
+use engine_ecs::pair::Pair;
+use engine_ecs::phase::UPDATE;
+use engine_ecs::query::Query;
+use engine_ecs::system::observer::Observe;
+use engine_ecs::{error, Component, World};
+use tracing::level_filters::LevelFilter;
+use tracing_subscriber::fmt::time::ChronoLocal;
+use tracing_subscriber::layer::SubscriberExt;
+use tracing_subscriber::util::SubscriberInitExt;
+use tracing_subscriber::EnvFilter;
+
+#[derive(Component)]
+struct State
+{
+ value: u32,
+}
+
+fn do_something_fallible(query: Query<(&mut State,)>) -> Result<(), Error>
+{
+ for (mut state,) in &query {
+ state.value += 1;
+
+ state.set_changed();
+
+ if state.value > 3 {
+ return Err(error!("Invalid state value {}", state.value));
+ }
+ }
+
+ Ok(())
+}
+
+fn handle_state_changed(observe: Observe<Pair<Changed, State>>) -> Result<(), Error>
+{
+ for evt_match in &observe {
+ let state = evt_match.get_ent_target_comp();
+
+ if state.value > 3 {
+ return Err(error!("Invalid state value {}", state.value));
+ }
+
+ tracing::info!("State has valid value {}", state.value);
+ }
+
+ Ok(())
+}
+
+fn main()
+{
+ tracing_subscriber::registry()
+ .with(
+ tracing_subscriber::fmt::layer()
+ .with_timer(ChronoLocal::new("%T%.6f".to_string())),
+ )
+ .with(
+ EnvFilter::builder()
+ .with_default_directive(LevelFilter::DEBUG.into())
+ .from_env()
+ .unwrap(),
+ )
+ .init();
+
+ let mut world = World::new();
+
+ world.set_err_handler(engine_ecs::error::err_handler_log_error);
+
+ world.spawn((State { value: 0 },));
+
+ world.register_system(*UPDATE, do_something_fallible);
+
+ world.register_observer(handle_state_changed);
+
+ world.step();
+ world.step();
+ world.step();
+ world.step();
+}
diff --git a/ecs/examples/event_loop.rs b/engine-ecs/examples/event_loop.rs
index 61d7ba4..7e215bd 100644
--- a/ecs/examples/event_loop.rs
+++ b/engine-ecs/examples/event_loop.rs
@@ -1,7 +1,7 @@
-use ecs::actions::Actions;
-use ecs::pair::{ChildOf, Pair};
-use ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
-use ecs::{static_entity, Component, Query, World};
+use engine_ecs::actions::Actions;
+use engine_ecs::pair::ChildOf;
+use engine_ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
+use engine_ecs::{declare_entity, pair, Component, Query, World};
#[derive(Component)]
struct Wool
@@ -65,21 +65,25 @@ fn age(query: Query<(&mut Health, &Name)>, mut actions: Actions)
}
}
-static_entity!(SHEER_PHASE, (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE)));
-
-static_entity!(FEED_PHASE, (Phase, Pair::new::<ChildOf>(*SHEER_PHASE)));
-
-static_entity!(AGE_PHASE, (Phase, Pair::new::<ChildOf>(*FEED_PHASE)));
+declare_entity! {
+ SHEER_PHASE: (Phase, pair!(ChildOf, { *UPDATE_PHASE }));
+ FEED_PHASE: (Phase, pair!(ChildOf, { *SHEER_PHASE }));
+ AGE_PHASE: (Phase, pair!(ChildOf, { *FEED_PHASE }));
+}
fn main()
{
let mut world = World::new();
+ world.spawn_declared_entity(&SHEER_PHASE);
+ world.spawn_declared_entity(&FEED_PHASE);
+ world.spawn_declared_entity(&AGE_PHASE);
+
world.register_system(*SHEER_PHASE, sheer);
world.register_system(*FEED_PHASE, feed);
world.register_system(*AGE_PHASE, age);
- world.create_entity((
+ world.spawn((
Wool { remaining: 30 },
Health { health: 3 },
Name { name: "Bessy" },
diff --git a/ecs/examples/extension.rs b/engine-ecs/examples/extension.rs
index f6282e1..a2447f8 100644
--- a/ecs/examples/extension.rs
+++ b/engine-ecs/examples/extension.rs
@@ -1,7 +1,7 @@
-use ecs::actions::Actions;
-use ecs::extension::{Collector as ExtensionCollector, Extension};
-use ecs::phase::UPDATE as UPDATE_PHASE;
-use ecs::{Component, Query, World};
+use engine_ecs::actions::Actions;
+use engine_ecs::extension::{Collector as ExtensionCollector, Extension};
+use engine_ecs::phase::UPDATE as UPDATE_PHASE;
+use engine_ecs::{Component, Query, World};
#[derive(Debug, Component)]
struct Position
@@ -54,7 +54,7 @@ impl Extension for EnemySpawningExtension
{
collector.add_system(*UPDATE_PHASE, spawn_enemies);
- collector.add_entity((Position { x: 187, y: 30 }, EnemySpawnSource));
+ collector.spawn((Position { x: 187, y: 30 }, EnemySpawnSource));
}
}
diff --git a/ecs/examples/multiple_queries.rs b/engine-ecs/examples/multiple_queries.rs
index e0c957f..d04e43f 100644
--- a/ecs/examples/multiple_queries.rs
+++ b/engine-ecs/examples/multiple_queries.rs
@@ -1,7 +1,7 @@
use std::fmt::Display;
-use ecs::phase::START as START_PHASE;
-use ecs::{Component, Query, World};
+use engine_ecs::phase::START as START_PHASE;
+use engine_ecs::{Component, Query, World};
#[derive(Component)]
struct Health
@@ -63,23 +63,23 @@ fn main()
world.register_system(*START_PHASE, do_attacks);
- world.create_entity((
+ world.spawn((
Health { health: 100 },
EnemyName { name: "Big spider".to_string() },
));
- world.create_entity((
+ world.spawn((
Health { health: 30 },
EnemyName { name: "Small goblin".to_string() },
));
- world.create_entity((
+ world.spawn((
Health { health: 30 },
EnemyName { name: "Headcrab".to_string() },
));
- world.create_entity((AttackStrength::Strong,));
- world.create_entity((AttackStrength::Weak,));
+ world.spawn((AttackStrength::Strong,));
+ world.spawn((AttackStrength::Weak,));
world.step();
}
diff --git a/ecs/examples/optional_component.rs b/engine-ecs/examples/optional_component.rs
index ebc9115..e9ea4fb 100644
--- a/ecs/examples/optional_component.rs
+++ b/engine-ecs/examples/optional_component.rs
@@ -1,5 +1,5 @@
-use ecs::phase::UPDATE as UPDATE_PHASE;
-use ecs::{Component, Query, World};
+use engine_ecs::phase::UPDATE as UPDATE_PHASE;
+use engine_ecs::{Component, Query, World};
#[derive(Debug, Component)]
struct PettingCapacity
@@ -54,24 +54,24 @@ fn main()
world.register_system(*UPDATE_PHASE, pet_cats);
- world.create_entity((
+ world.spawn((
CatName { name: "Jasper".to_string() },
Aggressivity::Medium,
PettingCapacity { capacity_left: 5 },
));
- world.create_entity((
+ world.spawn((
CatName { name: "Otto".to_string() },
PettingCapacity { capacity_left: 9 },
));
- world.create_entity((
+ world.spawn((
CatName { name: "Carrie".to_string() },
PettingCapacity { capacity_left: 2 },
Aggressivity::High,
));
- world.create_entity((
+ world.spawn((
CatName { name: "Tommy".to_string() },
PettingCapacity { capacity_left: 1 },
Aggressivity::Low,
diff --git a/ecs/examples/relationship.rs b/engine-ecs/examples/relationship.rs
index b607398..41672ef 100644
--- a/ecs/examples/relationship.rs
+++ b/engine-ecs/examples/relationship.rs
@@ -1,7 +1,6 @@
-use ecs::pair::Pair;
-use ecs::phase::START as START_PHASE;
-use ecs::uid::Wildcard;
-use ecs::{Component, Query, World};
+use engine_ecs::pair::{Pair, Wildcard};
+use engine_ecs::phase::START as START_PHASE;
+use engine_ecs::{Component, Query, World};
#[derive(Component)]
struct Sword
@@ -23,10 +22,10 @@ struct Holding;
fn print_player_stats(player_query: Query<(&Player, &Health, Pair<Holding, Wildcard>)>)
{
- for (_, health, sword_relationship) in &player_query {
+ for (_, health, target_sword) in &player_query {
println!("Player health: {}", health.health);
- if let Some(sword_ent) = sword_relationship.get_target_entity() {
+ if let Some(sword_ent) = target_sword.get_target_ent() {
let sword = sword_ent
.get::<Sword>()
.expect("Sword entity is missing sword component");
@@ -42,12 +41,15 @@ fn main()
world.register_system(*START_PHASE, print_player_stats);
- let sword_uid = world.create_entity((Sword { attack_strength: 17 },));
+ let sword_uid = world.spawn((Sword { attack_strength: 17 },));
- world.create_entity((
+ world.spawn((
Player,
Health { health: 180 },
- Pair::new::<Holding>(sword_uid),
+ Pair::builder()
+ .relation::<Holding>()
+ .target_id(sword_uid)
+ .build(),
));
world.step();
diff --git a/ecs/examples/simple.rs b/engine-ecs/examples/simple.rs
index 0169062..e879ce3 100644
--- a/ecs/examples/simple.rs
+++ b/engine-ecs/examples/simple.rs
@@ -1,5 +1,5 @@
-use ecs::phase::START as START_PHASE;
-use ecs::{Component, Query, World};
+use engine_ecs::phase::START as START_PHASE;
+use engine_ecs::{Component, Query, World};
#[derive(Component)]
struct SomeData
@@ -26,14 +26,14 @@ fn main()
world.register_system(*START_PHASE, say_hello);
- world.create_entity((
+ world.spawn((
SomeData { num: 987_654 },
Greeting {
greeting: "Good afternoon".to_string(),
},
));
- world.create_entity((
+ world.spawn((
SomeData { num: 345 },
Greeting { greeting: "Good evening".to_string() },
));
diff --git a/engine-ecs/examples/traversal.rs b/engine-ecs/examples/traversal.rs
new file mode 100644
index 0000000..23b1d6a
--- /dev/null
+++ b/engine-ecs/examples/traversal.rs
@@ -0,0 +1,44 @@
+use engine_ecs::pair::ChildOf;
+use engine_ecs::phase::START as START_PHASE;
+use engine_ecs::query::term::{Traverse, TraverseSelfOrUp};
+use engine_ecs::{pair, Component, Query, World};
+
+#[derive(Component)]
+struct SomeData
+{
+ num: u64,
+}
+
+#[derive(Component)]
+struct Greeting
+{
+ greeting: String,
+}
+
+fn say_hello(query: Query<(Traverse<(&Greeting,), TraverseSelfOrUp, ChildOf>, &SomeData)>)
+{
+ for (greeting, data) in &query {
+ println!("{}: {}", greeting.greeting, data.num);
+ }
+}
+
+fn main()
+{
+ let mut world = World::new();
+
+ world.register_system(*START_PHASE, say_hello);
+
+ let parent_ent = world.spawn((Greeting {
+ greeting: "Good afternoon".to_string(),
+ },));
+
+ world.spawn((SomeData { num: 345 }, pair!(ChildOf, { parent_ent })));
+
+ world.spawn((
+ SomeData { num: 123 },
+ Greeting { greeting: "Good evening".to_string() },
+ pair!(ChildOf, { parent_ent }),
+ ));
+
+ world.step();
+}
diff --git a/ecs/examples/with_local.rs b/engine-ecs/examples/with_local.rs
index 4658fc0..6d54b7a 100644
--- a/ecs/examples/with_local.rs
+++ b/engine-ecs/examples/with_local.rs
@@ -1,7 +1,8 @@
-use ecs::component::local::Local;
-use ecs::phase::UPDATE as UPDATE_PHASE;
-use ecs::system::{Into, System};
-use ecs::{Component, Query, World};
+use engine_ecs::component::local::Local;
+use engine_ecs::phase::UPDATE as UPDATE_PHASE;
+use engine_ecs::system::initializable::Initializable;
+use engine_ecs::system::Into;
+use engine_ecs::{Component, Query, World};
#[derive(Component)]
struct SomeData
@@ -60,9 +61,9 @@ fn main()
.initialize((SayHelloState { cnt: 0 },)),
);
- world.create_entity((SomeData { num: 987_654 }, Name { name: "Bob".to_string() }));
+ world.spawn((SomeData { num: 987_654 }, Name { name: "Bob".to_string() }));
- world.create_entity((SomeData { num: 345 },));
+ world.spawn((SomeData { num: 345 },));
world.step();
world.step();
diff --git a/engine-ecs/examples/with_sole.rs b/engine-ecs/examples/with_sole.rs
new file mode 100644
index 0000000..69670d1
--- /dev/null
+++ b/engine-ecs/examples/with_sole.rs
@@ -0,0 +1,56 @@
+use engine_ecs::pair::ChildOf;
+use engine_ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
+use engine_ecs::sole::Single;
+use engine_ecs::{declare_entity, pair, Component, Query, Sole, World};
+
+#[derive(Component)]
+struct Ammo
+{
+ ammo_left: u32,
+}
+
+#[derive(Sole, Default)]
+struct AmmoCounter
+{
+ counter: u32,
+}
+
+fn count_ammo(query: Query<(&Ammo,)>, mut ammo_counter: Single<AmmoCounter>)
+{
+ for (ammo,) in &query {
+ println!("Found {} ammo", ammo.ammo_left);
+
+ ammo_counter.get_mut().unwrap().counter += ammo.ammo_left;
+ }
+}
+
+fn print_total_ammo_count(ammo_counter: Single<AmmoCounter>)
+{
+ let ammo_count = ammo_counter.get().unwrap().counter;
+
+ println!("Total ammo count: {}", ammo_count);
+
+ assert_eq!(ammo_count, 19);
+}
+
+declare_entity! {
+ PRINT_AMMO_COUNT_PHASE: (Phase, pair!(ChildOf, { *UPDATE_PHASE }));
+}
+
+fn main()
+{
+ let mut world = World::new();
+
+ world.spawn_declared_entity(&PRINT_AMMO_COUNT_PHASE);
+
+ world.register_system(*UPDATE_PHASE, count_ammo);
+ world.register_system(*PRINT_AMMO_COUNT_PHASE, print_total_ammo_count);
+
+ world.spawn((Ammo { ammo_left: 4 },));
+ world.spawn((Ammo { ammo_left: 7 },));
+ world.spawn((Ammo { ammo_left: 8 },));
+
+ world.add_sole(AmmoCounter::default()).unwrap();
+
+ world.step();
+}
diff --git a/engine-ecs/src/actions.rs b/engine-ecs/src/actions.rs
new file mode 100644
index 0000000..9ca9198
--- /dev/null
+++ b/engine-ecs/src/actions.rs
@@ -0,0 +1,392 @@
+use std::borrow::Cow;
+
+use crate::component::{
+ Component,
+ IntoParts,
+ Parts as ComponentParts,
+ Sequence as ComponentSequence,
+};
+use crate::entity::Name as EntityName;
+use crate::event::component::Removed;
+use crate::pair::Pair;
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::uid::{Uid, WithUidTuple};
+use crate::{ActionQueue, World};
+
+/// Used to to queue up actions for a [`World`] to perform.
+#[derive(Debug)]
+pub struct Actions<'world>
+{
+ action_queue: &'world ActionQueue,
+ world: Option<&'world World>,
+}
+
+impl Actions<'_>
+{
+ /// Queues up a entity to spawn at the end of the current tick, returning the [`Uid`]
+ /// that the entity will have.
+ pub fn spawn<Comps: ComponentSequence>(&mut self, components: Comps) -> Uid
+ {
+ let new_entity_uid = Uid::new_unique();
+
+ let components_parts = components.into_parts_array();
+
+ if let Some(comp_parts) = components_parts
+ .as_ref()
+ .iter()
+ .find(|comp_parts| comp_parts.is_sole)
+ {
+ panic!(
+ "Cannot spawn entity with sole component {}",
+ comp_parts.name
+ );
+ }
+
+ self.action_queue
+ .push(Action::Spawn(new_entity_uid, components_parts.into()));
+
+ new_entity_uid
+ }
+
+ /// Queues up a entity to spawn at the end of the current tick, returning the [`Uid`]
+ /// that the entity will have.
+ ///
+ /// In addition to the given components, the entity will have a [`EntityName`]
+ /// component containing `name`.
+ pub fn spawn_named<Comps: ComponentSequence>(
+ &mut self,
+ name: impl Into<Cow<'static, str>>,
+ components: Comps,
+ ) -> Uid
+ {
+ let new_entity_uid = Uid::new_unique();
+
+ let components_parts = components.into_parts_array();
+
+ if let Some(comp_parts) = components_parts
+ .as_ref()
+ .iter()
+ .find(|comp_parts| comp_parts.is_sole)
+ {
+ panic!(
+ "Cannot spawn entity with sole component {}",
+ comp_parts.name
+ );
+ }
+
+ self.action_queue.push(Action::Spawn(
+ new_entity_uid,
+ components_parts
+ .into_iter()
+ .chain([EntityName { name: name.into() }.into_parts()])
+ .collect(),
+ ));
+
+ new_entity_uid
+ }
+
+ /// Queues up despawning a entity at the end of the current tick.
+ pub fn despawn(&mut self, entity_uid: Uid)
+ {
+ debug_assert!(!entity_uid.is_pair());
+
+ let Some(world) = self.world else {
+ self.action_queue.push(Action::Despawn(entity_uid));
+ return;
+ };
+
+ let Some(ent) = world.get_entity(entity_uid) else {
+ tracing::warn!("Cannot entity that doesn't exist");
+ return;
+ };
+
+ // TODO: Submit all events with a single function call to reduce overhead
+ for comp_id in ent.component_ids() {
+ if comp_id.is_pair() {
+ continue;
+ }
+
+ world.event_submitter().submit_event(
+ &Pair::builder()
+ .relation::<Removed>()
+ .target_id(comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+
+ self.action_queue.push(Action::Despawn(entity_uid));
+ }
+
+ /// Queues up adding component(s) to a entity at the end of the current tick.
+ pub fn add_components<Comps>(&mut self, entity_uid: Uid, components: Comps)
+ where
+ Comps: ComponentSequence,
+ {
+ debug_assert!(!entity_uid.is_pair());
+
+ if components.cnt() == 0 {
+ return;
+ }
+
+ let components_parts = components.into_parts_array();
+
+ if let Some(comp_parts) = components_parts
+ .as_ref()
+ .iter()
+ .find(|comp_parts| comp_parts.is_sole)
+ {
+ panic!("Cannot add sole component {} to an entity", comp_parts.name);
+ }
+
+ self.action_queue
+ .push(Action::AddComponents(entity_uid, components_parts.into()));
+ }
+
+ /// Queues up removing component(s) from a entity at the end of the current tick.
+ #[tracing::instrument(skip(self, component_ids))]
+ pub fn remove_components(
+ &mut self,
+ entity_uid: Uid,
+ component_ids: impl IntoIterator<Item = Uid>,
+ )
+ {
+ debug_assert!(!entity_uid.is_pair());
+
+ let mut component_ids = component_ids.into_iter().peekable();
+
+ if component_ids.peek().is_none() {
+ return;
+ }
+
+ let Some(world) = self.world else {
+ self.action_queue.push(Action::RemoveComponents(
+ entity_uid,
+ component_ids.collect(),
+ ));
+ return;
+ };
+
+ let Some(ent) = world.get_entity(entity_uid) else {
+ tracing::warn!("Cannot remove components from entity that doesn't exist");
+ return;
+ };
+
+ let component_ids = component_ids
+ .filter(|comp_id| ent.has_component(*comp_id))
+ .collect::<Vec<_>>();
+
+ if component_ids.is_empty() {
+ return;
+ }
+
+ // TODO: Submit all events with a single function call to reduce overhead
+ for comp_id in &component_ids {
+ if comp_id.is_pair() {
+ continue;
+ }
+
+ world.event_submitter().submit_event(
+ &Pair::builder()
+ .relation::<Removed>()
+ .target_id(*comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+
+ self.action_queue
+ .push(Action::RemoveComponents(entity_uid, component_ids));
+ }
+
+ /// Queues up removing component(s) from a entity at the end of the current tick.
+ pub fn remove_comps<Ids: WithUidTuple>(&mut self, entity_uid: Uid)
+ {
+ self.remove_components(entity_uid, Ids::uids());
+ }
+
+ /// Queues up setting the target of a entity's pair at the end of the current tick.
+ /// If the pair does not exist in the entity, it is still added to the entity with the
+ /// new target.
+ #[tracing::instrument(
+ skip_all,
+ fields(entity=%entity_id, pair=%pair.id(), new_target=%new_target_id)
+ )]
+ pub fn set_pair_target(
+ &mut self,
+ entity_id: Uid,
+ pair: Pair<Uid, Uid>,
+ new_target_id: Uid,
+ )
+ {
+ debug_assert!(!entity_id.is_pair());
+ debug_assert!(!new_target_id.is_pair());
+
+ if let Some(world) = self.world {
+ if world.get_entity(entity_id).is_none() {
+ tracing::error!("Entity does not exist");
+ return;
+ }
+ }
+
+ let pair_id = pair.id();
+
+ self.action_queue.push(Action::SetPair {
+ entity_id: entity_id,
+ pair_id: pair_id,
+ new_pair: Pair::builder()
+ .relation_id(pair_id.relation())
+ .target_id(new_target_id)
+ .build()
+ .into_parts(),
+ });
+ }
+
+ /// Queues up setting the target of a entity's pair at the end of the current tick.
+ /// If the pair does not exist in the entity, it is still added to the entity with the
+ /// new target.
+ #[tracing::instrument(
+ skip_all,
+ fields(entity=%entity_id, pair=%pair.id(), new_target=%NewTarget::id())
+ )]
+ pub fn set_pair_target_and_data<NewTarget>(
+ &mut self,
+ entity_id: Uid,
+ pair: Pair<Uid, Uid>,
+ new_target: NewTarget,
+ ) where
+ NewTarget: Component,
+ {
+ debug_assert!(!entity_id.is_pair());
+
+ if let Some(world) = self.world {
+ if world.get_entity(entity_id).is_none() {
+ tracing::error!("Entity does not exist");
+ return;
+ }
+ }
+
+ let pair_id = pair.id();
+
+ self.action_queue.push(Action::SetPair {
+ entity_id: entity_id,
+ pair_id: pair_id,
+ new_pair: Pair::builder()
+ .relation_id(pair_id.relation())
+ .target_as_data(new_target)
+ .build()
+ .into_parts(),
+ });
+ }
+
+ /// Queues up setting the relation of a entity's pair at the end of the current tick.
+ /// If the pair does not exist in the entity, it is still added to the entity with the
+ /// new relation.
+ #[tracing::instrument(
+ skip_all,
+ fields(entity=%entity_id, pair=%pair.id(), new_relation=%new_relation_id)
+ )]
+ pub fn set_pair_relation(
+ &mut self,
+ entity_id: Uid,
+ pair: Pair<Uid, Uid>,
+ new_relation_id: Uid,
+ )
+ {
+ debug_assert!(!entity_id.is_pair());
+ debug_assert!(!new_relation_id.is_pair());
+
+ if let Some(world) = self.world {
+ if world.get_entity(entity_id).is_none() {
+ tracing::error!("Entity does not exist");
+ return;
+ }
+ }
+
+ let pair_id = pair.id();
+
+ self.action_queue.push(Action::SetPair {
+ entity_id: entity_id,
+ pair_id: pair_id,
+ new_pair: Pair::builder()
+ .relation_id(new_relation_id)
+ .target_id(pair_id.target())
+ .build()
+ .into_parts(),
+ });
+ }
+
+ /// Queues up setting the relation of a entity's pair at the end of the current tick.
+ /// If the pair does not exist in the entity, it is still added to the entity with the
+ /// new relation.
+ #[tracing::instrument(
+ skip_all,
+ fields(entity=%entity_id, pair=%pair.id(), new_relation=%NewRelation::id())
+ )]
+ pub fn set_pair_relation_and_data<NewRelation>(
+ &mut self,
+ entity_id: Uid,
+ pair: Pair<Uid, Uid>,
+ new_relation: NewRelation,
+ ) where
+ NewRelation: Component,
+ {
+ debug_assert!(!entity_id.is_pair());
+
+ if let Some(world) = self.world {
+ if world.get_entity(entity_id).is_none() {
+ tracing::error!("Entity does not exist");
+ return;
+ }
+ }
+
+ let pair_id = pair.id();
+
+ self.action_queue.push(Action::SetPair {
+ entity_id: entity_id,
+ pair_id: pair_id,
+ new_pair: Pair::builder()
+ .relation_as_data(new_relation)
+ .target_id(pair_id.target())
+ .build()
+ .into_parts(),
+ });
+ }
+
+ /// Stops the [`World`]. The world will finish the current tick and that tick will be
+ /// the last.
+ pub fn stop(&mut self)
+ {
+ self.action_queue.push(Action::Stop);
+ }
+}
+
+impl<'world> SystemParam<'world> for Actions<'world>
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ Self {
+ action_queue: &world.data.action_queue,
+ world: Some(world),
+ }
+ }
+}
+
+/// A action for a [`System`] to perform.
+#[derive(Debug)]
+pub(crate) enum Action
+{
+ Spawn(Uid, Vec<ComponentParts>),
+ Despawn(Uid),
+ AddComponents(Uid, Vec<ComponentParts>),
+ RemoveComponents(Uid, Vec<Uid>),
+ SetPair
+ {
+ entity_id: Uid,
+ pair_id: Uid,
+ new_pair: ComponentParts,
+ },
+ Stop,
+}
diff --git a/engine-ecs/src/component.rs b/engine-ecs/src/component.rs
new file mode 100644
index 0000000..847ecbd
--- /dev/null
+++ b/engine-ecs/src/component.rs
@@ -0,0 +1,427 @@
+use std::any::{Any, TypeId};
+use std::fmt::Debug;
+use std::ops::{Deref, DerefMut};
+
+use engine_ecs_macros::Component;
+use seq_macro::seq;
+
+use crate::event::component::Changed;
+use crate::event::Submitter as EventSubmitter;
+use crate::lock::{
+ Error as LockError,
+ MappedReadGuard,
+ MappedWriteGuard,
+ ReadGuard,
+ WriteGuard,
+};
+use crate::pair::{MemberMetadata as PairMemberMetadata, Pair};
+use crate::reflection::Type as TypeReflection;
+use crate::system::Input as SystemInput;
+use crate::uid::Uid;
+use crate::util::Array;
+use crate::{EntityComponentRef, World};
+
+pub mod local;
+
+pub(crate) mod storage;
+
+pub trait Component: SystemInput + Any + IntoParts
+{
+ /// Returns the ID of this component.
+ fn id() -> Uid
+ where
+ Self: Sized;
+
+ fn type_reflection() -> Option<&'static TypeReflection>
+ where
+ Self: Sized;
+
+ /// Returns the name of this component.
+ fn name(&self) -> &'static str;
+}
+
+impl dyn Component
+{
+ pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
+ {
+ (self as &mut dyn Any).downcast_mut()
+ }
+
+ pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
+ {
+ (self as &dyn Any).downcast_ref()
+ }
+
+ pub fn is<Other: 'static>(&self) -> bool
+ {
+ (self as &dyn Any).is::<Other>()
+ }
+}
+
+impl Debug for dyn Component
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter.debug_struct("Component").finish_non_exhaustive()
+ }
+}
+
+/// A sequence of components.
+pub trait Sequence
+{
+ type PartsArray: Array<Parts>;
+
+ fn cnt(&self) -> usize;
+
+ fn into_parts_array(self) -> Self::PartsArray;
+}
+
+#[derive(Debug)]
+pub struct Handle<'a, DataT: ?Sized + 'static>
+{
+ inner: MappedReadGuard<'a, DataT>,
+}
+
+impl<'comp, DataT: 'static> Handle<'comp, DataT>
+{
+ /// Creates a new handle instance from a [`EntityComponentRef`].
+ ///
+ /// # Errors
+ /// Will return `Err` if acquiring the component's lock fails.
+ pub fn from_entity_component_ref(
+ entity_component_ref: &EntityComponentRef<'comp>,
+ ) -> Result<Self, HandleError>
+ {
+ Self::new(
+ entity_component_ref
+ .component()
+ .read_nonblock()
+ .map_err(AcquireLockError)?,
+ )
+ }
+
+ fn new(inner: ReadGuard<'comp, Box<dyn Any>>) -> Result<Self, HandleError>
+ {
+ Ok(Self {
+ inner: ReadGuard::try_map(inner, |component| {
+ component.downcast_ref::<DataT>()
+ })
+ .map_err(|_| HandleError::IncorrectType)?,
+ })
+ }
+}
+
+impl<'comp> Handle<'comp, dyn Any>
+{
+ pub fn new_any(
+ entity_component_ref: &EntityComponentRef<'comp>,
+ ) -> Result<Self, HandleError>
+ {
+ Ok(Self {
+ inner: ReadGuard::map(
+ entity_component_ref
+ .component()
+ .read_nonblock()
+ .map_err(AcquireLockError)?,
+ |component| &**component,
+ ),
+ })
+ }
+}
+
+impl<DataT: ?Sized + 'static> Deref for Handle<'_, DataT>
+{
+ type Target = DataT;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.inner
+ }
+}
+
+#[derive(Debug)]
+pub struct HandleMut<'a, DataT: ?Sized + 'static>
+{
+ entity_component_ref: EntityComponentRef<'a>,
+ inner: MappedWriteGuard<'a, DataT>,
+ event_submitter: EventSubmitter<'a>,
+}
+
+impl<'comp, DataT: 'static> HandleMut<'comp, DataT>
+{
+ /// Creates a new handle instance from a [`EntityComponentRef`].
+ ///
+ /// # Errors
+ /// Will return `Err` if acquiring the component's lock fails.
+ pub fn from_entity_component_ref(
+ entity_component_ref: &EntityComponentRef<'comp>,
+ world: &'comp World,
+ ) -> Result<Self, HandleError>
+ {
+ let inner = entity_component_ref
+ .component()
+ .write_nonblock()
+ .map_err(AcquireLockError)?;
+
+ Ok(Self {
+ entity_component_ref: entity_component_ref.clone(),
+ inner: WriteGuard::try_map(inner, |component| {
+ component.downcast_mut::<DataT>()
+ })
+ .map_err(|_| HandleError::IncorrectType)?,
+ event_submitter: world.event_submitter(),
+ })
+ }
+}
+
+impl<'comp> HandleMut<'comp, dyn Any>
+{
+ pub fn new_any(
+ entity_component_ref: &EntityComponentRef<'comp>,
+ world: &'comp World,
+ ) -> Result<Self, HandleError>
+ {
+ let inner = entity_component_ref
+ .component()
+ .write_nonblock()
+ .map_err(AcquireLockError)?;
+
+ Ok(Self {
+ entity_component_ref: entity_component_ref.clone(),
+ inner: WriteGuard::map(inner, |component| &mut **component),
+ event_submitter: world.event_submitter(),
+ })
+ }
+}
+
+impl<'comp, DataT: ?Sized + 'static> HandleMut<'comp, DataT>
+{
+ pub fn set_changed(&self)
+ {
+ self.event_submitter.submit_event(
+ &Pair::builder()
+ .relation::<Changed>()
+ .target_id(self.entity_component_ref.id())
+ .build(),
+ self.entity_component_ref.entity_id(),
+ );
+ }
+}
+
+impl<DataT: ?Sized + 'static> Deref for HandleMut<'_, DataT>
+{
+ type Target = DataT;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.inner
+ }
+}
+
+impl<DataT: ?Sized + 'static> DerefMut for HandleMut<'_, DataT>
+{
+ fn deref_mut(&mut self) -> &mut Self::Target
+ {
+ &mut self.inner
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum HandleError
+{
+ #[error(transparent)]
+ AcquireLockFailed(#[from] AcquireLockError),
+
+ #[error("Incorrect component type")]
+ IncorrectType,
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Failed to acquire component lock")]
+pub struct AcquireLockError(#[source] LockError);
+
+macro_rules! inner {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<#(IntoCompParts~I: IntoParts,)*> Sequence for (#(IntoCompParts~I,)*)
+ {
+ type PartsArray = [Parts; $c];
+
+ fn cnt(&self) -> usize
+ {
+ $c
+ }
+
+ fn into_parts_array(self) -> Self::PartsArray
+ {
+ [#({
+ self.I.into_parts()
+ },)*]
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..17 {
+ inner!(C);
+});
+
+impl<const LEN: usize> Sequence for [Parts; LEN]
+{
+ type PartsArray = Self;
+
+ fn cnt(&self) -> usize
+ {
+ self.len()
+ }
+
+ fn into_parts_array(self) -> Self::PartsArray
+ {
+ self
+ }
+}
+
+pub trait IntoParts
+{
+ fn into_parts(self) -> Parts;
+}
+
+/// The parts of a component.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Parts
+{
+ pub id: Uid,
+ pub name: &'static str,
+ pub is_sole: bool,
+ pub type_reflection: Option<&'static TypeReflection>,
+ pub data: Box<dyn Any>,
+ pub pair_relation_metadata: Option<PairMemberMetadata>,
+ pub pair_target_metadata: Option<PairMemberMetadata>,
+}
+
+impl Parts
+{
+ pub fn from_component_info(component_info: &Info, id: Uid, data: Box<dyn Any>)
+ -> Self
+ {
+ assert_eq!((*data).type_id(), component_info.ty_id);
+
+ Self::builder()
+ .type_reflection(component_info.type_reflection)
+ .name(component_info.name)
+ .is_sole(component_info.is_sole)
+ .build_with_any_data(id, data)
+ }
+
+ #[must_use]
+ pub fn builder() -> PartsBuilder
+ {
+ PartsBuilder::default()
+ }
+}
+
+#[derive(Debug)]
+pub struct PartsBuilder
+{
+ name: &'static str,
+ is_sole: bool,
+ type_reflection: Option<&'static TypeReflection>,
+ pair_relation_metadata: Option<PairMemberMetadata>,
+ pair_target_metadata: Option<PairMemberMetadata>,
+}
+
+impl PartsBuilder
+{
+ #[must_use]
+ pub fn name(mut self, name: &'static str) -> Self
+ {
+ self.name = name;
+ self
+ }
+
+ #[must_use]
+ pub fn is_sole(mut self, is_sole: bool) -> Self
+ {
+ self.is_sole = is_sole;
+ self
+ }
+
+ #[must_use]
+ pub fn type_reflection(
+ mut self,
+ type_reflection: Option<&'static TypeReflection>,
+ ) -> Self
+ {
+ self.type_reflection = type_reflection;
+ self
+ }
+
+ #[must_use]
+ pub fn pair_relation_metadata(mut self, metadata: Option<PairMemberMetadata>)
+ -> Self
+ {
+ self.pair_relation_metadata = metadata;
+ self
+ }
+
+ #[must_use]
+ pub fn pair_target_metadata(mut self, metadata: Option<PairMemberMetadata>) -> Self
+ {
+ self.pair_target_metadata = metadata;
+ self
+ }
+
+ #[must_use]
+ pub fn build<Data: 'static>(self, id: Uid, data: Data) -> Parts
+ {
+ Parts {
+ id,
+ name: self.name,
+ is_sole: self.is_sole,
+ type_reflection: self.type_reflection,
+ data: Box::new(data),
+ pair_relation_metadata: self.pair_relation_metadata,
+ pair_target_metadata: self.pair_target_metadata,
+ }
+ }
+
+ #[must_use]
+ pub fn build_with_any_data(self, id: Uid, data: Box<dyn Any>) -> Parts
+ {
+ Parts {
+ id,
+ name: self.name,
+ is_sole: self.is_sole,
+ type_reflection: self.type_reflection,
+ data,
+ pair_relation_metadata: self.pair_relation_metadata,
+ pair_target_metadata: self.pair_target_metadata,
+ }
+ }
+}
+
+impl Default for PartsBuilder
+{
+ fn default() -> Self
+ {
+ Self {
+ name: "(unspecified)",
+ is_sole: false,
+ type_reflection: None,
+ pair_relation_metadata: None,
+ pair_target_metadata: None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Component)]
+#[non_exhaustive]
+pub struct Info
+{
+ pub type_reflection: Option<&'static TypeReflection>,
+ pub ty_id: TypeId,
+ pub name: &'static str,
+ pub is_sole: bool,
+}
diff --git a/engine-ecs/src/component/local.rs b/engine-ecs/src/component/local.rs
new file mode 100644
index 0000000..29afcee
--- /dev/null
+++ b/engine-ecs/src/component/local.rs
@@ -0,0 +1,101 @@
+use std::any::type_name;
+use std::ops::{Deref, DerefMut};
+
+use crate::component::{
+ HandleMut as ComponentHandleMut,
+ IntoParts as _,
+ Parts as ComponentParts,
+};
+use crate::pair::Pair;
+use crate::system::initializable::Param as InitializableParam;
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::{Component, World};
+
+/// Holds a component which is local to a single system.
+#[derive(Debug)]
+pub struct Local<'world, LocalComponent: Component>
+{
+ local_component: ComponentHandleMut<'world, LocalComponent>,
+}
+
+impl<'world, LocalComponent> SystemParam<'world> for Local<'world, LocalComponent>
+where
+ LocalComponent: Component,
+{
+ type Input = LocalComponent;
+
+ fn new(world: &'world World, system_metadata: &SystemMetadata) -> Self
+ {
+ let Some(system_ent) = world.get_entity(system_metadata.ent_id) else {
+ panic!(
+ "System entity with ID {} does not exist",
+ system_metadata.ent_id
+ );
+ };
+
+ let Some(local_component) = system_ent.get_with_id_mut::<LocalComponent>(
+ Pair::builder()
+ .relation::<IsLocalComponent>()
+ .target::<LocalComponent>()
+ .build()
+ .id(),
+ ) else {
+ panic!(
+ "Local component {} of system with ID {} is uninitialized",
+ type_name::<LocalComponent>(),
+ system_metadata.ent_id
+ );
+ };
+
+ Self { local_component }
+ }
+}
+
+impl<'world, LocalComponent, SystemT> InitializableParam<'world, SystemT>
+ for Local<'world, LocalComponent>
+where
+ LocalComponent: Component,
+ SystemT: SystemWithLocalComponents,
+ Self: SystemParam<'world, Input = LocalComponent>,
+{
+ fn initialize(system: &mut SystemT, input: Self::Input)
+ {
+ system.add_local_component(
+ Pair::builder()
+ .relation::<IsLocalComponent>()
+ .target_as_data(input)
+ .build()
+ .into_parts(),
+ );
+ }
+}
+
+impl<LocalComponent> Deref for Local<'_, LocalComponent>
+where
+ LocalComponent: Component,
+{
+ type Target = LocalComponent;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.local_component
+ }
+}
+
+impl<LocalComponent> DerefMut for Local<'_, LocalComponent>
+where
+ LocalComponent: Component,
+{
+ fn deref_mut(&mut self) -> &mut Self::Target
+ {
+ &mut self.local_component
+ }
+}
+
+pub trait SystemWithLocalComponents
+{
+ fn add_local_component(&mut self, component_parts: ComponentParts);
+}
+
+#[derive(Component)]
+struct IsLocalComponent;
diff --git a/ecs/src/component/storage.rs b/engine-ecs/src/component/storage.rs
index b27b552..5746bea 100644
--- a/ecs/src/component/storage.rs
+++ b/engine-ecs/src/component/storage.rs
@@ -1,6 +1,7 @@
use std::any::Any;
use std::array::IntoIter as ArrayIter;
use std::cell::RefCell;
+use std::fmt::Debug;
use std::vec::IntoIter as VecIntoIter;
use hashbrown::HashMap;
@@ -17,7 +18,8 @@ use crate::component::storage::graph::{
ArchetypeEdges,
Graph,
};
-use crate::uid::{Kind as UidKind, Uid};
+use crate::uid::{PairParams, Uid};
+use crate::util::array_vec::ArrayVec;
use crate::util::{BorrowedOrOwned, Either, StreamingIterator, VecExt};
pub mod archetype;
@@ -25,31 +27,94 @@ pub mod archetype;
mod graph;
#[derive(Debug)]
-pub struct ArchetypeSearchTerms<'a>
+pub struct SearchTerms<const TERM_CAP: usize>
{
- pub required_components: &'a [Uid],
- pub excluded_components: &'a [Uid],
+ pub(crate) present: ArrayVec<Uid, TERM_CAP>,
+ pub(crate) absent: ArrayVec<Uid, TERM_CAP>,
+ pub(crate) traverse: ArrayVec<Traversal<TERM_CAP>, TERM_CAP>,
}
-impl ArchetypeSearchTerms<'_>
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct TermMetadata
{
- fn excluded_contains(&self, comp_id: Uid) -> bool
+ pub level: u32,
+ pub index: u32,
+}
+
+#[derive(Clone)]
+pub struct Traversal<const TERM_CAP: usize>
+{
+ pub kind: TraversalKind,
+ pub relation: Uid,
+ pub terms: fn(&Self) -> SearchTerms<TERM_CAP>,
+ pub term_metadata: TermMetadata,
+}
+
+impl<const TERM_CAP: usize> Debug for Traversal<TERM_CAP>
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter
+ .debug_struct("Traversal")
+ .field("kind", &self.kind)
+ .field("relation", &self.relation)
+ .field("terms", &(self.terms)(self))
+ .field("term_metadata", &self.term_metadata)
+ .finish()
+ }
+}
+
+impl<const TERM_CAP: usize> Traversal<TERM_CAP>
+{
+ fn relationship(&self) -> Uid
{
- let comp_id_kind = comp_id.kind();
+ Uid::new_pair(&PairParams {
+ relation: self.relation,
+ target: Uid::wildcard(),
+ })
+ }
+}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum TraversalKind
+{
+ Up,
+ SelfOrUp,
+}
+
+#[derive(Debug)]
+pub struct TraversalResult
+{
+ pub relation: Uid,
+ pub term_metadata: TermMetadata,
+ pub found: FoundByTraversal,
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum FoundByTraversal
+{
+ SelfEntity,
+ OtherEntity(Uid),
+}
+
+impl<const TERM_CAP: usize> SearchTerms<TERM_CAP>
+{
+ fn excluded_contains(&self, comp_id: Uid) -> bool
+ {
debug_assert!(
- comp_id_kind == UidKind::Component
- || (comp_id_kind == UidKind::Pair
- && comp_id.target_component() != Uid::wildcard())
+ !comp_id.is_pair()
+ || (comp_id.is_pair() && comp_id.target() != Uid::wildcard())
);
- let is_found = self.excluded_components.binary_search(&comp_id).is_ok();
+ let is_found = self.absent.binary_search(&comp_id).is_ok();
- if !is_found && comp_id_kind == UidKind::Pair {
- return self.excluded_components.iter().any(|excluded_comp_id| {
- excluded_comp_id.kind() == UidKind::Pair
+ if !is_found && comp_id.is_pair() {
+ return self.absent.iter().any(|excluded_comp_id| {
+ excluded_comp_id.is_pair()
&& excluded_comp_id.has_same_relation_as(comp_id)
- && excluded_comp_id.target_component() == Uid::wildcard()
+ && excluded_comp_id.target() == Uid::wildcard()
});
}
@@ -58,16 +123,14 @@ impl ArchetypeSearchTerms<'_>
fn contains_conflicting(&self) -> bool
{
- self.excluded_components.iter().any(|excluded_comp_id| {
- self.required_components
- .binary_search(excluded_comp_id)
- .is_ok()
- })
+ self.absent
+ .iter()
+ .any(|excluded_comp_id| self.present.binary_search(excluded_comp_id).is_ok())
}
fn archetype_contains_all_required(&self, archetype: &Archetype) -> bool
{
- self.required_components
+ self.present
.iter()
.all(|comp_id| archetype.contains_matching_component(*comp_id))
}
@@ -83,12 +146,12 @@ pub struct Storage
impl Storage
{
- pub fn search_archetypes<'search_terms>(
+ pub fn search_archetypes<'search_terms, const TERM_CAP: usize>(
&self,
- search_terms: ArchetypeSearchTerms<'search_terms>,
- ) -> ArchetypeRefIter<'_, 'search_terms>
+ search_terms: &'search_terms SearchTerms<TERM_CAP>,
+ ) -> ArchetypeRefIter<'_, 'search_terms, TERM_CAP>
{
- let archetype_id = ArchetypeId::new(search_terms.required_components);
+ let archetype_id = ArchetypeId::new(search_terms.present.iter());
if search_terms.contains_conflicting() {
return ArchetypeRefIter {
@@ -105,19 +168,19 @@ impl Storage
self.imaginary_archetypes
.borrow_mut()
.push(ImaginaryArchetype {
- id: ArchetypeId::new(search_terms.required_components.iter().filter(
+ id: ArchetypeId::new(search_terms.present.iter().filter(
|required_comp_id| {
- required_comp_id.kind() != UidKind::Pair
- || required_comp_id.target_component() != Uid::wildcard()
+ !required_comp_id.is_pair()
+ || required_comp_id.target() != Uid::wildcard()
},
)),
component_ids: search_terms
- .required_components
+ .present
.iter()
.copied()
.filter(|required_comp_id| {
- required_comp_id.kind() != UidKind::Pair
- || required_comp_id.target_component() != Uid::wildcard()
+ !required_comp_id.is_pair()
+ || required_comp_id.target() != Uid::wildcard()
})
.collect(),
});
@@ -145,12 +208,12 @@ impl Storage
Some(self.graph.get_node_by_id(id)?.archetype())
}
- pub fn create_entity(&mut self, uid: Uid) -> Result<(), Error>
+ pub fn create_entity(&mut self, uid: Uid) -> Result<(), EntityAlreadyExistsError>
{
- debug_assert_eq!(uid.kind(), UidKind::Entity);
+ debug_assert!(!uid.is_pair());
if self.entity_archetype_lookup.contains_key(&uid) {
- return Err(Error::EntityAlreadyExists(uid));
+ return Err(EntityAlreadyExistsError);
}
let empty_archetype_id = ArchetypeId::new_empty();
@@ -270,7 +333,7 @@ impl Storage
entity.insert_component(
component_id,
- ArchetypeEntityComponent::new(component, component_id, component_name),
+ ArchetypeEntityComponent::new(component, component_name),
add_edge_archetype,
);
@@ -339,7 +402,8 @@ impl Storage
.remove_entity(entity_uid)
.expect("Entity should exist in archetype");
- entity.remove_component(component_id, archetype_node.archetype());
+ let removed_component =
+ entity.remove_component(component_id, archetype_node.archetype());
self.graph
.get_node_by_id_mut(remove_edge_id)
@@ -350,6 +414,13 @@ impl Storage
self.entity_archetype_lookup
.insert(entity_uid, remove_edge_id);
+ tracing::debug!(
+ entity_id = %entity_uid,
+ component_id = %component_id,
+ component_name = removed_component.name(),
+ "Removed component from entity"
+ );
+
Ok(())
}
@@ -365,9 +436,9 @@ impl Storage
}
}
- fn find_all_archetype_with_comps(
+ fn find_all_archetype_with_comps<const TERM_CAP: usize>(
&self,
- search_terms: &ArchetypeSearchTerms<'_>,
+ search_terms: &SearchTerms<TERM_CAP>,
) -> Vec<ArchetypeId>
{
let Some(mut search_iter) =
@@ -401,7 +472,7 @@ impl Storage
.get_node_by_id(node_id)
.expect("Graph node found through DFS doesn't exist");
- if node.archetype().component_cnt() < search_terms.required_components.len() {
+ if node.archetype().component_cnt() < search_terms.present.len() {
continue;
}
@@ -589,29 +660,52 @@ pub enum VizoxideArchetypeGraphEdgeKind
}
#[derive(Debug)]
-pub struct ArchetypeRefIter<'storage, 'search_terms>
+pub struct ArchetypeRefIter<'storage, 'search_terms, const TERM_CAP: usize>
{
storage: &'storage Storage,
pre_iter: Either<ArrayIter<ArchetypeId, 1>, VecIntoIter<ArchetypeId>>,
dfs_iter: ArchetypeAddEdgeDfsIter<'storage>,
- search_terms: ArchetypeSearchTerms<'search_terms>,
+ search_terms: &'search_terms SearchTerms<TERM_CAP>,
}
-impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
+impl<'component_storage, const TERM_CAP: usize> Iterator
+ for ArchetypeRefIter<'component_storage, '_, TERM_CAP>
{
- type Item = &'component_storage Archetype;
+ type Item = (
+ &'component_storage Archetype,
+ ArrayVec<TraversalResult, TERM_CAP>,
+ );
fn next(&mut self) -> Option<Self::Item>
{
- if let Some(pre_iter_archetype_id) = self.pre_iter.next() {
- return Some(
- self.storage
- .get_archetype_by_id(pre_iter_archetype_id)
- .expect("Archetype should exist"),
- );
+ if let Some((pre_iter_archetype, pre_iter_traversal_results)) =
+ self.pre_iter.find_map(|archetype_id| {
+ let archetype = self
+ .storage
+ .get_archetype_by_id(archetype_id)
+ .expect("Archetype should exist");
+
+ let mut traversal_results =
+ ArrayVec::<TraversalResult, TERM_CAP>::default();
+
+ if do_traversals(
+ self.storage,
+ archetype,
+ &self.search_terms.traverse,
+ &mut traversal_results,
+ )
+ .is_none()
+ {
+ return None;
+ };
+
+ Some((archetype, traversal_results))
+ })
+ {
+ return Some((pre_iter_archetype, pre_iter_traversal_results));
}
- let archetype_id = loop {
+ let (archetype, traversal_results) = loop {
match self.dfs_iter.streaming_find(|res| {
matches!(
res,
@@ -628,7 +722,27 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
continue;
}
- break add_edge_archetype_id;
+ let add_edge_archetype = self
+ .storage
+ .get_archetype_by_id(add_edge_archetype_id)
+ .expect("Archetype should exist");
+
+ let mut traversal_results =
+ ArrayVec::<TraversalResult, TERM_CAP>::default();
+
+ if do_traversals(
+ self.storage,
+ add_edge_archetype,
+ &self.search_terms.traverse,
+ &mut traversal_results,
+ )
+ .is_none()
+ {
+ self.dfs_iter.pop();
+ continue;
+ };
+
+ break (add_edge_archetype, traversal_results);
}
ArchetypeAddEdgeDfsIterResult::AddEdgeArchetypeNotFound {
archetype,
@@ -639,13 +753,14 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
continue;
}
- let mut add_edge_archetype_comps =
- archetype.component_ids_sorted().collect::<Vec<_>>();
+ let mut add_edge_archetype_comps = archetype
+ .component_ids_sorted()
+ .collect::<ArrayVec<_, 32>>();
- add_edge_archetype_comps.insert_at_partition_point_by_key(
- add_edge_component_id,
- |comp_id| *comp_id,
- );
+ add_edge_archetype_comps
+ .insert_at_part_pt_by_key(add_edge_component_id, |comp_id| {
+ comp_id
+ });
self.storage.imaginary_archetypes.borrow_mut().push(
ImaginaryArchetype {
@@ -654,13 +769,14 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
},
);
- let found =
- self.find_edges_of_imaginary_archetype(&add_edge_archetype_comps);
+ let found = self.find_edges_of_imaginary_archetype(
+ add_edge_archetype_comps.clone(),
+ );
self.dfs_iter.push((
BorrowedOrOwned::Owned(Archetype::new(
add_edge_archetype_id,
- add_edge_archetype_comps.clone(),
+ add_edge_archetype_comps,
)),
found.into_iter(),
));
@@ -671,25 +787,22 @@ impl<'component_storage> Iterator for ArchetypeRefIter<'component_storage, '_>
}
};
- Some(
- self.storage
- .get_archetype_by_id(archetype_id)
- .expect("Archetype should exist"),
- )
+ Some((archetype, traversal_results))
}
}
-impl ArchetypeRefIter<'_, '_>
+impl<const TERM_CAP: usize> ArchetypeRefIter<'_, '_, TERM_CAP>
{
fn find_edges_of_imaginary_archetype(
&self,
- imaginary_archetype_comps: &[Uid],
+ imaginary_archetype_comps: ArrayVec<Uid, 32>,
) -> Vec<(Uid, ArchetypeEdges)>
{
self.storage
- .find_all_archetype_with_comps(&ArchetypeSearchTerms {
- required_components: imaginary_archetype_comps,
- excluded_components: &[],
+ .find_all_archetype_with_comps(&SearchTerms {
+ present: imaginary_archetype_comps.clone(),
+ absent: ArrayVec::default(),
+ traverse: ArrayVec::default(),
})
.into_iter()
.filter_map(|found_id| {
@@ -712,8 +825,7 @@ impl ArchetypeRefIter<'_, '_>
let mut add_edge_comp_ids = imaginary_archetype_comps.to_vec();
- add_edge_comp_ids
- .insert_at_partition_point_by_key(unique_comp_id, |id| *id);
+ add_edge_comp_ids.insert_at_part_pt_by_key(unique_comp_id, |id| id);
let add_edge = ArchetypeId::new(&add_edge_comp_ids);
@@ -729,30 +841,134 @@ impl ArchetypeRefIter<'_, '_>
#[derive(Debug, thiserror::Error)]
pub enum Error
{
- #[error("Entity with ID {0:?} already exists")]
- EntityAlreadyExists(Uid),
-
- #[error("Entity with ID {0:?} does not exist")]
+ #[error("Entity {0} does not exist")]
EntityDoesNotExist(Uid),
- #[error("Entity with ID {entity:?} already has component with ID {component:?}")]
+ #[error("Entity {entity} already has component {component}")]
ComponentAlreadyInEntity
{
entity: Uid, component: Uid
},
- #[error("Entity with ID {entity:?} does not have component with ID {component:?}")]
+ #[error("Entity {entity} does not have component {component}")]
ComponentNotFoundInEntity
{
entity: Uid, component: Uid
},
}
+#[derive(Debug, thiserror::Error)]
+#[error("Entity already exists")]
+pub struct EntityAlreadyExistsError;
+
#[derive(Debug)]
struct ImaginaryArchetype
{
id: ArchetypeId,
- component_ids: Vec<Uid>,
+ component_ids: ArrayVec<Uid, 32>,
+}
+
+fn do_traversals<const TERM_CAP: usize>(
+ storage: &Storage,
+ archetype: &Archetype,
+ traversals: &[Traversal<TERM_CAP>],
+ results: &mut ArrayVec<TraversalResult, TERM_CAP>,
+) -> Option<()>
+{
+ // TODO: Optimize traversals. Caching is needed especially
+
+ for traversal in traversals {
+ let traversal_terms = (traversal.terms)(traversal);
+
+ match traversal.kind {
+ TraversalKind::Up => {
+ archetype
+ .get_matching_component_ids(traversal.relationship())
+ .find_map(|relationship| {
+ traverse_dfs(
+ storage,
+ relationship.target(),
+ traversal,
+ &traversal_terms,
+ results,
+ )
+ })?;
+ }
+ TraversalKind::SelfOrUp => {
+ if traversal_terms.absent.iter().all(|absent_comp_id| {
+ !archetype.contains_matching_component(*absent_comp_id)
+ }) && traversal_terms.archetype_contains_all_required(archetype)
+ && do_traversals(
+ storage,
+ archetype,
+ &traversal_terms.traverse,
+ results,
+ )
+ .is_some()
+ {
+ results.push(TraversalResult {
+ relation: traversal.relation,
+ term_metadata: traversal.term_metadata,
+ found: FoundByTraversal::SelfEntity,
+ });
+
+ return Some(());
+ }
+
+ archetype
+ .get_matching_component_ids(traversal.relationship())
+ .find_map(|relationship| {
+ traverse_dfs(
+ storage,
+ relationship.target(),
+ traversal,
+ &traversal_terms,
+ results,
+ )
+ })?;
+ }
+ }
+ }
+
+ Some(())
+}
+
+fn traverse_dfs<const TERM_CAP: usize>(
+ storage: &Storage,
+ ent_id: Uid,
+ traversal: &Traversal<TERM_CAP>,
+ terms: &SearchTerms<TERM_CAP>,
+ results: &mut ArrayVec<TraversalResult, TERM_CAP>,
+) -> Option<()>
+{
+ let ent_archetype = storage.get_entity_archetype(ent_id)?;
+
+ if terms
+ .absent
+ .iter()
+ .all(|absent_comp_id| !ent_archetype.contains_matching_component(*absent_comp_id))
+ && terms.archetype_contains_all_required(ent_archetype)
+ && do_traversals(storage, ent_archetype, &terms.traverse, results).is_some()
+ {
+ results.push(TraversalResult {
+ relation: traversal.relation,
+ term_metadata: traversal.term_metadata,
+ found: FoundByTraversal::OtherEntity(ent_id),
+ });
+
+ return Some(());
+ }
+
+ for relationship in ent_archetype.get_matching_component_ids(traversal.relationship())
+ {
+ if traverse_dfs(storage, relationship.target(), traversal, terms, results)
+ .is_some()
+ {
+ return Some(());
+ }
+ }
+
+ None
}
#[cfg(test)]
@@ -760,14 +976,14 @@ mod tests
{
use crate::component::storage::archetype::Id as ArchetypeId;
use crate::component::storage::Storage;
- use crate::uid::{Kind as UidKind, Uid};
+ use crate::uid::Uid;
#[test]
fn create_entity_works()
{
let mut new_storage = Storage::default();
- let uid = Uid::new_unique(UidKind::Entity);
+ let uid = Uid::new_unique();
new_storage.create_entity(uid).expect("Expected Ok");
diff --git a/ecs/src/component/storage/archetype.rs b/engine-ecs/src/component/storage/archetype.rs
index a88e0e8..e72126a 100644
--- a/ecs/src/component/storage/archetype.rs
+++ b/engine-ecs/src/component/storage/archetype.rs
@@ -8,7 +8,7 @@ use std::slice::Iter as SliceIter;
use hashbrown::HashMap;
use crate::lock::Lock;
-use crate::uid::{Kind as UidKind, Uid};
+use crate::uid::Uid;
use crate::util::{Either, HashMapExt};
#[derive(Debug)]
@@ -77,8 +77,6 @@ impl Archetype
pub fn remove_entity(&mut self, entity_uid: Uid) -> Option<Entity>
{
- //debug_assert_eq!(entity_uid.kind(), UidKind::Entity);
-
let entity_index = self.entity_index_lookup.remove(&entity_uid)?;
if self.entities.len() == 1 {
@@ -109,29 +107,24 @@ impl Archetype
EntityIter { iter: self.entities.iter() }
}
- pub fn entity_cnt(&self) -> usize
+ pub fn component_cnt(&self) -> usize
{
- self.entities.len()
+ self.component_index_lookup.len()
}
- pub fn component_cnt(&self) -> usize
+ // Currently only used by a test
+ #[allow(dead_code)]
+ pub fn entity_cnt(&self) -> usize
{
- self.component_index_lookup.len()
+ self.entities.len()
}
pub fn get_matching_component_indices(
&self,
component_id: Uid,
- ) -> MatchingComponentIter
+ ) -> MatchingComponentIter<'_>
{
- assert!(
- component_id.kind() == UidKind::Component
- || component_id.kind() == UidKind::Pair
- );
-
- if component_id.kind() == UidKind::Pair
- && component_id.target_component() == Uid::wildcard()
- {
+ if component_id.is_pair() && component_id.target() == Uid::wildcard() {
return MatchingComponentIter {
inner: Either::A(
self.component_ids
@@ -140,7 +133,7 @@ impl Archetype
.zip(std::iter::repeat_n(component_id, self.component_ids.len()))
.filter(
(|((_, other_comp_id), component_id)| {
- other_comp_id.kind() == UidKind::Pair
+ other_comp_id.is_pair()
&& other_comp_id.has_same_relation_as(*component_id)
})
as MatchingComponentIterFilterFn,
@@ -162,9 +155,8 @@ impl Archetype
pub fn get_index_for_component(&self, component_id: Uid) -> Option<usize>
{
assert!(
- component_id.kind() == UidKind::Component
- || (component_id.kind() == UidKind::Pair
- && component_id.target_component() != Uid::wildcard())
+ !component_id.is_pair()
+ || (component_id.is_pair() && component_id.target() != Uid::wildcard())
);
self.component_index_lookup.get(&component_id).copied()
@@ -182,17 +174,9 @@ impl Archetype
pub fn contains_matching_component(&self, component_id: Uid) -> bool
{
- let component_id_kind = component_id.kind();
-
- debug_assert!(
- component_id_kind == UidKind::Component || component_id_kind == UidKind::Pair
- );
-
- if component_id.kind() == UidKind::Pair
- && component_id.target_component() == Uid::wildcard()
- {
+ if component_id.is_pair() && component_id.target() == Uid::wildcard() {
return self.component_ids.iter().any(|other_comp_id| {
- other_comp_id.kind() == UidKind::Pair
+ other_comp_id.is_pair()
&& other_comp_id.has_same_relation_as(component_id)
});
}
@@ -200,14 +184,38 @@ impl Archetype
self.contains_component_with_exact_id(component_id)
}
- pub fn contains_component_with_exact_id(&self, component_id: Uid) -> bool
+ pub fn get_matching_component_ids(
+ &self,
+ component_id: Uid,
+ ) -> impl Iterator<Item = Uid> + use<'_>
{
- let component_id_kind = component_id.kind();
+ if component_id.is_pair() && component_id.target() == Uid::wildcard() {
+ return Either::A(
+ self.component_ids
+ .iter()
+ .filter(move |other_comp_id| {
+ other_comp_id.is_pair()
+ && other_comp_id.has_same_relation_as(component_id)
+ })
+ .copied(),
+ );
+ }
+
+ Either::B(
+ (if self.contains_component_with_exact_id(component_id) {
+ Some(component_id)
+ } else {
+ None
+ })
+ .into_iter(),
+ )
+ }
+ pub fn contains_component_with_exact_id(&self, component_id: Uid) -> bool
+ {
debug_assert!(
- component_id_kind == UidKind::Component
- || (component_id_kind == UidKind::Pair
- && component_id.target_component() != Uid::wildcard())
+ !component_id.is_pair()
+ || (component_id.is_pair() && component_id.target() != Uid::wildcard())
);
self.component_index_lookup.contains_key(&component_id)
@@ -287,13 +295,17 @@ impl Entity
&self.components
}
- pub fn remove_component(&mut self, component_id: Uid, archetype: &Archetype)
+ pub fn remove_component(
+ &mut self,
+ component_id: Uid,
+ archetype: &Archetype,
+ ) -> EntityComponent
{
let index = archetype
.get_index_for_component(component_id)
.expect("Archetype should contain component");
- self.components.remove(index);
+ self.components.remove(index)
}
pub fn insert_component(
@@ -314,32 +326,28 @@ impl Entity
#[derive(Debug)]
pub struct EntityComponent
{
- id: Uid,
component: Lock<Box<dyn Any>>,
+ name: &'static str,
}
impl EntityComponent
{
- pub fn new(
- component: Box<dyn Any>,
- component_id: Uid,
- component_name: &'static str,
- ) -> Self
+ pub fn new(component: Box<dyn Any>, component_name: &'static str) -> Self
{
Self {
- id: component_id,
component: Lock::new(component, component_name),
+ name: component_name,
}
}
- pub fn id(&self) -> Uid
+ pub fn component(&self) -> &Lock<Box<dyn Any>>
{
- self.id
+ &self.component
}
- pub fn component(&self) -> &Lock<Box<dyn Any>>
+ pub fn name(&self) -> &str
{
- &self.component
+ self.name
}
}
@@ -370,11 +378,10 @@ impl Id
}
for comp_id in component_id_iter {
- if prev_component_id.is_some_and(|prev_comp_id| *comp_id < prev_comp_id) {
- panic!(
- "Cannot create archetype ID from a unsorted component metadata list"
- );
- }
+ assert!(
+ prev_component_id.is_none_or(|prev_comp_id| *comp_id >= prev_comp_id),
+ "Cannot create archetype ID from a unsorted component metadata list"
+ );
prev_component_id = Some(*comp_id);
diff --git a/ecs/src/component/storage/graph.rs b/engine-ecs/src/component/storage/graph.rs
index 29fa937..add25e4 100644
--- a/ecs/src/component/storage/graph.rs
+++ b/engine-ecs/src/component/storage/graph.rs
@@ -3,7 +3,7 @@ use std::vec::IntoIter as VecIntoIter;
use hashbrown::{HashMap, HashSet};
use crate::component::storage::archetype::{Archetype, Id as ArchetypeId};
-use crate::uid::{Kind as UidKind, Uid};
+use crate::uid::Uid;
use crate::util::{BorrowedOrOwned, StreamingIterator};
#[derive(Debug, Default)]
@@ -80,7 +80,7 @@ impl Graph
pub fn dfs_archetype_add_edges(
&self,
archetype_id: ArchetypeId,
- ) -> Option<ArchetypeAddEdgeDfsIter>
+ ) -> Option<ArchetypeAddEdgeDfsIter<'_>>
{
let node = self.get_node_by_id(archetype_id)?;
@@ -250,11 +250,6 @@ impl ArchetypeNode
insert_fn: impl FnOnce() -> ArchetypeEdges,
) -> &mut ArchetypeEdges
{
- debug_assert!(matches!(
- component_id.kind(),
- UidKind::Component | UidKind::Pair
- ));
-
self.edges.entry(component_id).or_insert_with(insert_fn)
}
diff --git a/engine-ecs/src/entity.rs b/engine-ecs/src/entity.rs
new file mode 100644
index 0000000..ba85956
--- /dev/null
+++ b/engine-ecs/src/entity.rs
@@ -0,0 +1,343 @@
+use std::any::{type_name, Any};
+use std::borrow::Cow;
+use std::ops::Deref;
+use std::sync::LazyLock;
+
+use crate::component::storage::archetype::{
+ Archetype,
+ Entity as ArchetypeEntity,
+ MatchingComponentIter as ArchetypeMatchingComponentIter,
+};
+use crate::component::{Handle as ComponentHandle, HandleMut as ComponentHandleMut};
+use crate::pair::{
+ ComponentOrWildcard,
+ MultipleWithWildcard as PairMultipleWithWildcard,
+ Pair,
+ WithWildcard as PairWithWildcard,
+};
+use crate::uid::Uid;
+use crate::{Component, EntityComponentRef, World};
+
+pub mod obtainer;
+
+/// A handle to a entity.
+#[derive(Debug, Clone)]
+pub struct Handle<'a>
+{
+ archetype: &'a Archetype,
+ entity: &'a ArchetypeEntity,
+ world: &'a World,
+}
+
+impl<'a> Handle<'a>
+{
+ /// Returns the [`Uid`] of this entity.
+ #[inline]
+ #[must_use]
+ pub fn uid(&self) -> Uid
+ {
+ self.entity.uid()
+ }
+
+ /// Returns a reference to the [`Name`] component in this entity. `None` is
+ /// returned if the component isn't found in the entity.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component is mutably borrowed elsewhere
+ #[must_use]
+ pub fn get_name(&self) -> Option<ComponentHandle<'a, Name>>
+ {
+ self.get::<Name>()
+ }
+
+ /// Returns a reference to the specified component in this entity. `None` is
+ /// returned if the component isn't found in the entity.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component's ID is pair
+ /// - The component is mutably borrowed elsewhere
+ #[must_use]
+ pub fn get<ComponentT: Component>(&self) -> Option<ComponentHandle<'a, ComponentT>>
+ {
+ assert!(!ComponentT::id().is_pair());
+
+ let component = self.get_matching_components(ComponentT::id()).next()?;
+
+ Some(
+ ComponentHandle::from_entity_component_ref(&component).unwrap_or_else(
+ |err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ },
+ ),
+ )
+ }
+
+ /// Returns a mutable reference to the specified component in this entity. `None` is
+ /// returned if the component isn't found in the entity.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component's ID is a pair
+ /// - The component is borrowed elsewhere
+ #[must_use]
+ pub fn get_mut<ComponentT: Component>(
+ &self,
+ ) -> Option<ComponentHandleMut<'a, ComponentT>>
+ {
+ assert!(!ComponentT::id().is_pair());
+
+ let component = self.get_matching_components(ComponentT::id()).next()?;
+
+ Some(
+ ComponentHandleMut::from_entity_component_ref(&component, self.world)
+ .unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }),
+ )
+ }
+
+ /// Returns a reference to the component with the ID `id` in this entity.
+ /// `None` is returned if the component isn't found.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component is borrowed mutably elsewhere
+ /// - The component type is incorrect
+ #[must_use]
+ pub fn get_with_id<ComponentDataT: 'static>(
+ &self,
+ id: Uid,
+ ) -> Option<ComponentHandle<'a, ComponentDataT>>
+ {
+ let component = self.get_matching_components(id).next()?;
+
+ Some(
+ ComponentHandle::from_entity_component_ref(&component).unwrap_or_else(
+ |err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentDataT>()
+ );
+ },
+ ),
+ )
+ }
+
+ /// Returns a reference to the component with the ID `id` in this entity.
+ /// `None` is returned if the component isn't found.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component is borrowed mutably elsewhere
+ #[must_use]
+ pub fn get_any_by_id(&self, id: Uid) -> Option<ComponentHandle<'a, dyn Any>>
+ {
+ let component = self.get_matching_components(id).next()?;
+
+ Some(ComponentHandle::new_any(&component).unwrap_or_else(|err| {
+ panic!("Creating handle to component with id {id} failed: {err}");
+ }))
+ }
+
+ /// Returns a reference to the component with the ID `id` in this entity.
+ /// `None` is returned if the component isn't found.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component is borrowed elsewhere
+ #[must_use]
+ pub fn get_any_by_id_mut(&self, id: Uid) -> Option<ComponentHandleMut<'a, dyn Any>>
+ {
+ let component = self.get_matching_components(id).next()?;
+
+ Some(
+ ComponentHandleMut::new_any(&component, self.world).unwrap_or_else(|err| {
+ panic!("Creating handle to component with id {id} failed: {err}");
+ }),
+ )
+ }
+
+ /// Returns a mutable reference to the component with the ID `id` in this entity.
+ /// `None` is returned if the component isn't found.
+ ///
+ /// # Panics
+ /// Will panic if:
+ /// - The component is borrowed elsewhere
+ /// - The component type is incorrect
+ #[must_use]
+ pub fn get_with_id_mut<ComponentDataT: 'static>(
+ &self,
+ id: Uid,
+ ) -> Option<ComponentHandleMut<'a, ComponentDataT>>
+ {
+ let component = self.get_matching_components(id).next()?;
+
+ Some(
+ ComponentHandleMut::from_entity_component_ref(&component, self.world)
+ .unwrap_or_else(|err| {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentDataT>()
+ );
+ }),
+ )
+ }
+
+ #[must_use]
+ pub fn get_first_wildcard_pair_match<Relation, Target>(
+ &self,
+ ) -> Option<PairWithWildcard<'a, Relation, Target>>
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ let mut matching_comps = self.get_matching_components(
+ Pair::builder()
+ .relation_id(Relation::uid())
+ .target_id(Target::uid())
+ .build()
+ .id(),
+ );
+
+ Some(PairWithWildcard::new(self.world, matching_comps.next()?))
+ }
+
+ #[must_use]
+ pub fn get_wildcard_pair_matches<Relation, Target>(
+ &self,
+ ) -> PairMultipleWithWildcard<'a, Relation, Target>
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ PairMultipleWithWildcard::new(self.world, self.clone())
+ }
+
+ #[inline]
+ #[must_use]
+ pub fn get_matching_components(&self, component_uid: Uid)
+ -> MatchingComponentIter<'a>
+ {
+ MatchingComponentIter {
+ inner: self.archetype.get_matching_component_indices(component_uid),
+ entity: self.entity,
+ }
+ }
+
+ /// Returns whether or not this entity contains a component with the specified `Uid`.
+ #[must_use]
+ pub fn has_component(&self, component_uid: Uid) -> bool
+ {
+ self.archetype
+ .contains_component_with_exact_id(component_uid)
+ }
+
+ /// Returns the `Uids`s of the components this entity has.
+ pub fn component_ids(&self) -> impl Iterator<Item = Uid> + '_
+ {
+ self.archetype.component_ids_sorted()
+ }
+
+ pub(crate) fn new(
+ archetype: &'a Archetype,
+ entity: &'a ArchetypeEntity,
+ world: &'a World,
+ ) -> Self
+ {
+ Self { archetype, entity, world }
+ }
+}
+
+#[derive(Debug)]
+pub struct MatchingComponentIter<'a>
+{
+ inner: ArchetypeMatchingComponentIter<'a>,
+ entity: &'a ArchetypeEntity,
+}
+
+impl<'a> Iterator for MatchingComponentIter<'a>
+{
+ type Item = EntityComponentRef<'a>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let (matching_component_id, index) = self.inner.next()?;
+
+ Some(EntityComponentRef::new(
+ matching_component_id,
+ self.entity.components().get(index).unwrap(),
+ self.entity.uid(),
+ ))
+ }
+}
+
+/// The data type of a declaration of a entity.
+#[derive(Debug)]
+pub struct Declaration
+{
+ uid: LazyLock<Uid>,
+ spawn_func: fn(&mut World),
+}
+
+impl Declaration
+{
+ pub(crate) fn spawn(&self, world: &mut World)
+ {
+ (self.spawn_func)(world);
+ }
+
+ #[doc(hidden)]
+ pub const fn new(spawn_func: fn(&mut World)) -> Self
+ {
+ Self {
+ uid: LazyLock::new(|| Uid::new_unique()),
+ spawn_func,
+ }
+ }
+}
+
+impl Deref for Declaration
+{
+ type Target = Uid;
+
+ fn deref(&self) -> &Self::Target
+ {
+ &self.uid
+ }
+}
+
+#[allow(clippy::module_name_repetitions)]
+#[macro_export]
+macro_rules! declare_entity {
+ ($($(#[$attr: meta])* $visibility: vis $ident: ident: $components: expr;)+) => {
+ $(
+ $(#[$attr])*
+ $visibility static $ident: $crate::entity::Declaration =
+ $crate::entity::Declaration::new(|world| {
+ use $crate::tuple::Tuple;
+
+ world.spawn_with_uid(
+ *$ident,
+ ($components).with_elem($crate::entity::Name {
+ name: concat!(module_path!(), stringify!($ident)).into()
+ })
+ );
+ });
+ )+
+ }
+}
+
+/// Entity name.
+#[derive(Debug, Clone, Component)]
+pub struct Name
+{
+ pub name: Cow<'static, str>,
+}
diff --git a/engine-ecs/src/entity/obtainer.rs b/engine-ecs/src/entity/obtainer.rs
new file mode 100644
index 0000000..6c2ea96
--- /dev/null
+++ b/engine-ecs/src/entity/obtainer.rs
@@ -0,0 +1,29 @@
+use crate::entity::Handle as EntityHandle;
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::uid::Uid;
+use crate::World;
+
+#[derive(Debug)]
+pub struct Obtainer<'world>
+{
+ world: &'world World,
+}
+
+impl<'world> SystemParam<'world> for Obtainer<'world>
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ Self { world }
+ }
+}
+
+impl Obtainer<'_>
+{
+ #[must_use]
+ pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>>
+ {
+ self.world.get_entity(entity_id)
+ }
+}
diff --git a/engine-ecs/src/error.rs b/engine-ecs/src/error.rs
new file mode 100644
index 0000000..5ce5632
--- /dev/null
+++ b/engine-ecs/src/error.rs
@@ -0,0 +1,252 @@
+use std::fmt::{Debug, Display};
+
+use crate::World;
+
+#[macro_export]
+macro_rules! error {
+ ($lit: literal) => {
+ $crate::error::Error::message($lit)
+ };
+
+ ($lit: literal, $($tt: tt)*) => {
+ $crate::error::Error::message(std::format!($lit, $($tt)*))
+ };
+
+ ($err: expr) => {
+ $crate::error::Error::from($err)
+ };
+}
+
+pub struct Error
+{
+ inner: anyhow::Error,
+}
+
+impl Error
+{
+ pub fn new<Err>(err: Err) -> Self
+ where
+ Err: std::error::Error + Send + Sync + 'static,
+ {
+ Self::from(err)
+ }
+
+ pub fn message<Message>(message: Message) -> Self
+ where
+ Message: Display + Debug + Send + Sync + 'static,
+ {
+ Self { inner: anyhow::Error::msg(message) }
+ }
+
+ pub fn context<Ctx>(self, context: Ctx) -> Error
+ where
+ Ctx: Display + Send + Sync + 'static,
+ {
+ Self { inner: self.inner.context(context) }
+ }
+
+ /// Retrieves the message text of a system-defined Windows Win32 error code and
+ /// returns it as a `Error`. If this fails (for example if the error code is invalid),
+ /// the returned `Error` contains the error code number.
+ #[cfg(windows)]
+ pub fn from_system_defined_win32_error(error: u32) -> Self
+ {
+ use std::ffi::CStr;
+ use std::ptr::null;
+
+ use windows_sys::Win32::System::Diagnostics::Debug::{
+ FormatMessageA,
+ FORMAT_MESSAGE_FROM_SYSTEM,
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ };
+
+ let mut buffer = vec![0u8; 256];
+
+ let len = unsafe {
+ FormatMessageA(
+ FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ null(),
+ error,
+ 0,
+ buffer.as_mut_ptr(),
+ (buffer.len() - 1) as u32,
+ null(),
+ )
+ } as usize;
+
+ if len == 0 {
+ return Self::message(format!("Win32 error {error}"));
+ }
+
+ let buf = unsafe { CStr::from_bytes_with_nul_unchecked(&buffer[..len + 1]) };
+
+ Self::message(buf.to_string_lossy().trim().to_owned())
+ }
+}
+
+impl Debug for Error
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ <anyhow::Error as Debug>::fmt(&self.inner, formatter)
+ }
+}
+
+impl Display for Error
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ <anyhow::Error as Display>::fmt(&self.inner, formatter)
+ }
+}
+
+impl<Err> From<Err> for Error
+where
+ Err: std::error::Error + Send + Sync + 'static,
+{
+ fn from(err: Err) -> Self
+ {
+ Self { inner: err.into() }
+ }
+}
+
+pub trait Context<T>
+{
+ fn context<Ctx>(self, context: Ctx) -> Result<T, Error>
+ where
+ Ctx: Display + Send + Sync + 'static;
+
+ fn with_context<Ctx>(self, context_func: impl FnOnce() -> Ctx) -> Result<T, Error>
+ where
+ Ctx: Display + Send + Sync + 'static;
+}
+
+impl<T, Err> Context<T> for Result<T, Err>
+where
+ Err: std::error::Error + Send + Sync + 'static,
+{
+ fn context<Ctx>(self, context: Ctx) -> Result<T, Error>
+ where
+ Ctx: Display + Send + Sync + 'static,
+ {
+ self.map_err(|err| Error::from(err).context(context))
+ }
+
+ fn with_context<Ctx>(self, context_func: impl FnOnce() -> Ctx) -> Result<T, Error>
+ where
+ Ctx: Display + Send + Sync + 'static,
+ {
+ self.map_err(|err| Error::from(err).context(context_func()))
+ }
+}
+
+impl<T> Context<T> for Result<T, Error>
+{
+ fn context<Ctx>(self, context: Ctx) -> Result<T, Error>
+ where
+ Ctx: Display + Send + Sync + 'static,
+ {
+ self.map_err(|err| err.context(context))
+ }
+
+ fn with_context<Ctx>(self, context_func: impl FnOnce() -> Ctx) -> Result<T, Error>
+ where
+ Ctx: Display + Send + Sync + 'static,
+ {
+ self.map_err(|err| err.context(context_func()))
+ }
+}
+
+pub type HandlerFn = fn(&World, Error, Metadata);
+
+/// Error metadata.
+#[derive(Debug)]
+pub struct Metadata
+{
+ pub source_name: &'static str,
+ pub source_kind: SourceKind,
+}
+
+/// Error source kind.
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum SourceKind
+{
+ System,
+ Observer,
+}
+
+impl Display for SourceKind
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ match self {
+ SourceKind::System => formatter.write_str("system"),
+ SourceKind::Observer => formatter.write_str("observer"),
+ }
+ }
+}
+
+pub fn err_handler_panic(_world: &World, err: Error, err_metadata: Metadata)
+{
+ std::panic::panic_any(err.context(format!(
+ "Error occurred in {} '{}'",
+ err_metadata.source_kind, err_metadata.source_name
+ )));
+}
+
+pub fn err_handler_log_error(_world: &World, err: Error, err_metadata: Metadata)
+{
+ tracing::error!(
+ "Error occurred in {} '{}': {err:#}",
+ err_metadata.source_kind,
+ err_metadata.source_name
+ );
+}
+
+pub(crate) fn set_panic_hook()
+{
+ let previous_panic_hook = std::panic::take_hook();
+
+ std::panic::set_hook(Box::new(move |info| {
+ let Some(err) = info.payload().downcast_ref::<Error>() else {
+ return previous_panic_hook(info);
+ };
+
+ print!(
+ "Thread '{}' ({:?}) panicked",
+ std::thread::current().name().unwrap_or("<unnamed>"),
+ get_current_os_thread_id()
+ );
+
+ if let Some(location) = info.location() {
+ print!(" at {location}");
+ }
+
+ print!("\n{err:?}");
+
+ if err.inner.backtrace().status() == std::backtrace::BacktraceStatus::Disabled {
+ print!(concat!(
+ "\nnote: run with `RUST_BACKTRACE=1` environment variable ",
+ "to display a backtrace"
+ ));
+ }
+
+ println!("");
+ }));
+}
+
+fn get_current_os_thread_id() -> u64
+{
+ cfg_select! {
+ target_os = "linux" => {
+ (unsafe { libc::gettid() }) as u64
+ }
+ windows => {
+ (unsafe { windows_sys::Win32::System::Threading::GetCurrentThreadId() }) as u64
+ }
+ _ => {
+ compile_error!("Unsupported target OS");
+ }
+ }
+}
diff --git a/engine-ecs/src/event.rs b/engine-ecs/src/event.rs
new file mode 100644
index 0000000..cec9269
--- /dev/null
+++ b/engine-ecs/src/event.rs
@@ -0,0 +1,105 @@
+use crate::lock::Lock;
+use crate::pair::Pair;
+use crate::uid::Uid;
+use crate::util::VecExt;
+
+pub mod component;
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Emitted<'a>
+{
+ pub event: Uid,
+ pub match_ids: &'a [Uid],
+}
+
+#[derive(Debug)]
+pub struct Submitter<'world>
+{
+ new_events: &'world Lock<NewEvents>,
+}
+
+impl<'world> Submitter<'world>
+{
+ /// Submits a event to be handled later.
+ ///
+ /// # Panics
+ /// Will panic if unable to acquire a read-write lock to the event store.
+ pub fn submit_event(&self, event: &Pair<Uid, Uid>, match_id: Uid)
+ {
+ let mut new_events_lock = self
+ .new_events
+ .write_nonblock()
+ .expect("Failed to acquire read-write lock to new events");
+
+ new_events_lock.push_event_match(event, match_id);
+ }
+
+ pub(crate) fn new(new_events: &'world Lock<NewEvents>) -> Self
+ {
+ Self { new_events }
+ }
+}
+
+#[derive(Debug, Default)]
+pub(crate) struct NewEvents
+{
+ events: Vec<(Uid, Matches)>,
+}
+
+impl NewEvents
+{
+ pub fn push_event_match(&mut self, event: &Pair<Uid, Uid>, match_id: Uid)
+ {
+ let event_id = event.id();
+
+ assert!(event_id.is_pair());
+
+ if let Ok(event_index) = self
+ .events
+ .binary_search_by_key(&event_id, |(other_event_id, _)| *other_event_id)
+ {
+ let Some((_, matches)) = self.events.get_mut(event_index) else {
+ unreachable!();
+ };
+
+ matches.sorted_push(match_id);
+
+ return;
+ }
+
+ self.events.insert_at_part_pt_by_key(
+ (event_id, Matches { match_ids: Vec::from([match_id]) }),
+ |(other_event_id, _)| other_event_id,
+ );
+ }
+
+ pub fn take(&mut self) -> Vec<(Uid, Matches)>
+ {
+ std::mem::take(&mut self.events)
+ }
+
+ pub fn is_empty(&self) -> bool
+ {
+ self.events.is_empty()
+ }
+}
+
+#[derive(Debug)]
+pub(crate) struct Matches
+{
+ pub match_ids: Vec<Uid>,
+}
+
+impl Matches
+{
+ fn sorted_push(&mut self, match_id: Uid)
+ {
+ if self.match_ids.binary_search(&match_id).is_ok() {
+ return;
+ }
+
+ self.match_ids
+ .insert_at_part_pt_by_key(match_id, |other_match_id| other_match_id);
+ }
+}
diff --git a/engine-ecs/src/event/component.rs b/engine-ecs/src/event/component.rs
new file mode 100644
index 0000000..894fd95
--- /dev/null
+++ b/engine-ecs/src/event/component.rs
@@ -0,0 +1,103 @@
+//! Component events.
+
+use std::convert::Infallible;
+
+use crate::component::{Handle as ComponentHandle, HandleMut as ComponentHandleMut};
+use crate::entity::Handle as EntityHandle;
+use crate::pair::Pair;
+use crate::system::observer::{EventMatch, Observed};
+use crate::Component;
+
+/// Implemented by the relations of component event pairs
+pub trait EventRelation: Component {}
+
+/// Pair relation for events emitted when:
+/// a) A entity with the target component is spawned.
+/// b) The target component is added to a entity.
+#[derive(Debug, Component)]
+pub struct Added(Infallible);
+
+impl EventRelation for Added {}
+
+/// Pair relation for events emitted **before**:
+/// a) The target component is removed from a entity.
+/// b) A entity with the target component is despawned.
+#[derive(Debug, Component)]
+pub struct Removed(Infallible);
+
+impl EventRelation for Removed {}
+
+#[derive(Debug, Component)]
+pub struct Changed(Infallible);
+
+impl EventRelation for Changed {}
+
+/// [`EventMatch`] extension trait for component event matches.
+pub trait EventMatchExt<Target>: sealed::Sealed
+{
+ #[must_use]
+ fn get_entity(&self) -> EntityHandle<'_>;
+
+ #[must_use]
+ fn get_ent_target_comp(&self) -> ComponentHandle<'_, Target>
+ where
+ Target: Component;
+
+ #[must_use]
+ fn get_ent_target_comp_mut(&self) -> ComponentHandleMut<'_, Target>
+ where
+ Target: Component;
+}
+
+impl<ComponentEventRelation: EventRelation, Target> EventMatchExt<Target>
+ for EventMatch<'_, Pair<ComponentEventRelation, Target>>
+where
+ Pair<ComponentEventRelation, Target>: Observed,
+{
+ fn get_entity(&self) -> EntityHandle<'_>
+ {
+ let Some(ent) = self.try_get_entity() else {
+ unreachable!();
+ };
+
+ ent
+ }
+
+ fn get_ent_target_comp(&self) -> ComponentHandle<'_, Target>
+ where
+ Target: Component,
+ {
+ let ent = self.get_entity();
+
+ let Some(comp) = ent.get::<Target>() else {
+ unreachable!();
+ };
+
+ comp
+ }
+
+ fn get_ent_target_comp_mut(&self) -> ComponentHandleMut<'_, Target>
+ where
+ Target: Component,
+ {
+ let ent = self.get_entity();
+
+ let Some(comp) = ent.get_mut::<Target>() else {
+ unreachable!();
+ };
+
+ comp
+ }
+}
+
+impl<ComponentEventRelation: EventRelation, Target> sealed::Sealed
+ for EventMatch<'_, Pair<ComponentEventRelation, Target>>
+where
+ Pair<ComponentEventRelation, Target>: Observed,
+{
+}
+
+mod sealed
+{
+ pub trait Sealed {}
+}
diff --git a/ecs/src/extension.rs b/engine-ecs/src/extension.rs
index 42ebef9..ac989c9 100644
--- a/ecs/src/extension.rs
+++ b/engine-ecs/src/extension.rs
@@ -1,5 +1,9 @@
+use std::borrow::Cow;
+
use crate::component::Sequence as ComponentSequence;
+use crate::entity::Declaration as EntityDeclaration;
use crate::sole::Sole;
+use crate::system::observer::Observer;
use crate::system::System;
use crate::uid::Uid;
use crate::{SoleAlreadyExistsError, World};
@@ -34,18 +38,44 @@ impl<'world> Collector<'world>
self.world.register_system(phase_euid, system);
}
+ /// Adds a observer system to the [`World`].
+ pub fn add_observer<'this, SystemImpl>(
+ &'this mut self,
+ observer: impl Observer<'this, SystemImpl>,
+ )
+ {
+ self.world.register_observer(observer);
+ }
+
/// Adds a entity to the [`World`].
- pub fn add_entity<Comps>(&mut self, components: Comps)
+ pub fn spawn<Comps>(&mut self, components: Comps)
where
Comps: ComponentSequence,
{
- self.world.create_entity(components);
+ self.world.spawn(components);
+ }
+
+ /// Adds a entity to the [`World`].
+ pub fn spawn_named<Comps>(
+ &mut self,
+ name: impl Into<Cow<'static, str>>,
+ components: Comps,
+ ) where
+ Comps: ComponentSequence,
+ {
+ self.world.spawn_named(name, components);
+ }
+
+ /// Adds a declared entity to the [`World`].
+ pub fn spawn_declared_entity(&mut self, entity_decl: &EntityDeclaration)
+ {
+ self.world.spawn_declared_entity(entity_decl);
}
- /// Adds a globally shared singleton value to the [`World`].
+ /// Adds a singleton component to the [`World`].
///
/// # Errors
- /// Returns `Err` if this [`Sole`] has already been added.
+ /// Returns `Err` if the [`Sole`] already exists.
pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
where
SoleT: Sole,
diff --git a/engine-ecs/src/lib.rs b/engine-ecs/src/lib.rs
new file mode 100644
index 0000000..450940a
--- /dev/null
+++ b/engine-ecs/src/lib.rs
@@ -0,0 +1,1007 @@
+#![deny(clippy::all, clippy::pedantic)]
+
+use std::any::Any;
+use std::borrow::Cow;
+use std::fmt::Debug;
+use std::hint::cold_path;
+use std::rc::Rc;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::time::Duration;
+
+use crate::actions::Action;
+use crate::component::storage::archetype::EntityComponent as ArchetypeEntityComponent;
+use crate::component::storage::{
+ EntityAlreadyExistsError,
+ Error as ComponentStorageError,
+ Storage as ComponentStorage,
+};
+use crate::component::{
+ Component,
+ Info as ComponentInfo,
+ IntoParts as IntoComponentParts,
+ Parts as ComponentParts,
+ Sequence as ComponentSequence,
+};
+use crate::entity::{
+ Declaration as EntityDeclaration,
+ Handle as EntityHandle,
+ Name as EntityName,
+};
+use crate::error::{
+ err_handler_panic,
+ HandlerFn as ErrorHandlerFn,
+ Metadata as ErrorMetadata,
+ SourceKind as ErrorSourceKind,
+};
+use crate::event::component::Added;
+use crate::event::{Emitted as EmittedEvent, NewEvents, Submitter as EventSubmitter};
+use crate::extension::{Collector as ExtensionCollector, Extension};
+use crate::lock::Lock;
+use crate::pair::{ChildOf, Pair, Wildcard};
+use crate::phase::{
+ HasSystem as PhaseHasSystem,
+ Phase,
+ INIT as INIT_PHASE,
+ POST_UPDATE as POST_UPDATE_PHASE,
+ PRE_UPDATE as PRE_UPDATE_PHASE,
+ START as START_PHASE,
+ UPDATE as UPDATE_PHASE,
+};
+use crate::query::flexible::Query as FlexibleQuery;
+use crate::query::{
+ TermTuple as QueryTermTuple,
+ Terms as QueryTerms,
+ TermsBuilderInterface,
+ MAX_TERM_CNT as QUERY_MAX_TERM_CNT,
+};
+use crate::sole::{Single, Sole};
+use crate::system::observer::Observer;
+use crate::system::{
+ Callbacks,
+ Metadata as SystemMetadata,
+ Param as SystemParam,
+ System,
+ SystemComponent,
+};
+use crate::uid::Uid;
+
+pub mod actions;
+pub mod component;
+pub mod entity;
+pub mod error;
+pub mod event;
+pub mod extension;
+pub mod pair;
+pub mod phase;
+pub mod query;
+pub mod sole;
+pub mod system;
+pub mod time;
+pub mod tuple;
+pub mod uid;
+pub mod util;
+
+mod lock;
+
+pub use engine_ecs_macros::{Component, Sole};
+
+pub use crate::query::Query;
+
+pub extern crate engine_reflection as reflection;
+
+#[derive(Debug)]
+pub struct World
+{
+ data: WorldData,
+ stop: AtomicBool,
+ is_first_tick: AtomicBool,
+ error_handler: ErrorHandlerFn,
+}
+
+impl World
+{
+ #[must_use]
+ pub fn new() -> Self
+ {
+ let mut world = Self {
+ data: WorldData::default(),
+ stop: AtomicBool::new(false),
+ is_first_tick: AtomicBool::new(false),
+ error_handler: err_handler_panic,
+ };
+
+ let _ = world.add_sole(time::Time {
+ delta_time: Duration::default(),
+ tick_begin: None,
+ });
+
+ crate::phase::spawn_standard_phases(&mut world);
+
+ #[cfg(debug_assertions)]
+ world.register_system(*INIT_PHASE, crate::phase::check_parents);
+
+ crate::error::set_panic_hook();
+
+ world
+ }
+
+ pub fn set_err_handler(&mut self, err_handler: ErrorHandlerFn)
+ {
+ self.error_handler = err_handler;
+ }
+
+ pub fn stop_after_tick(&self)
+ {
+ self.stop.store(true, Ordering::Relaxed);
+ }
+
+ /// Creates a entity with the given components. A new unique [`Uid`] will be generated
+ /// for this entity.
+ pub fn spawn<Comps>(&mut self, components: Comps) -> Uid
+ where
+ Comps: ComponentSequence,
+ {
+ let entity_uid = Uid::new_unique();
+
+ self.spawn_with_uid(entity_uid, components);
+
+ entity_uid
+ }
+
+ /// Creates a entity with the given components. A new unique [`Uid`] will be generated
+ /// for this entity.
+ ///
+ /// In addition to the given components, the entity will have a [`EntityName`]
+ /// component containing `name`.
+ pub fn spawn_named<Comps>(
+ &mut self,
+ name: impl Into<Cow<'static, str>>,
+ components: Comps,
+ ) -> Uid
+ where
+ Comps: ComponentSequence,
+ {
+ let entity_uid = Uid::new_unique();
+
+ self.spawn_named_with_uid(entity_uid, name, components);
+
+ entity_uid
+ }
+
+ /// Creates a entity with the given components. The entity will have the specified
+ /// [`Uid`].
+ #[tracing::instrument(skip_all)]
+ pub fn spawn_with_uid<Comps>(&mut self, entity_uid: Uid, components: Comps)
+ where
+ Comps: ComponentSequence,
+ {
+ let components_parts = components.into_parts_array();
+
+ if let Some(comp_parts) = components_parts
+ .as_ref()
+ .iter()
+ .find(|comp_parts| comp_parts.is_sole)
+ {
+ panic!(
+ "Cannot spawn entity with sole component {}",
+ comp_parts.name
+ );
+ }
+
+ self.create_ent(entity_uid, components_parts);
+ }
+
+ /// Creates a entity with the given components. The entity will have the specified
+ /// [`Uid`].
+ ///
+ /// In addition to the given components, the entity will have a [`EntityName`]
+ /// component containing `name`.
+ #[tracing::instrument(skip_all)]
+ pub fn spawn_named_with_uid<Comps>(
+ &mut self,
+ entity_uid: Uid,
+ name: impl Into<Cow<'static, str>>,
+ components: Comps,
+ ) where
+ Comps: ComponentSequence,
+ {
+ let components_parts = components.into_parts_array();
+
+ if let Some(comp_parts) = components_parts
+ .as_ref()
+ .iter()
+ .find(|comp_parts| comp_parts.is_sole)
+ {
+ panic!(
+ "Cannot spawn entity with sole component {}",
+ comp_parts.name
+ );
+ }
+
+ self.create_ent(
+ entity_uid,
+ components_parts
+ .into_iter()
+ .chain([EntityName { name: name.into() }.into_parts()]),
+ );
+ }
+
+ pub fn add_component(&mut self, entity_id: Uid, component_parts: ComponentParts)
+ {
+ assert!(
+ !component_parts.is_sole,
+ "Cannot add sole component {} to an entity",
+ component_parts.name
+ );
+
+ Self::add_entity_components(
+ entity_id,
+ [component_parts],
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ );
+ }
+
+ pub fn spawn_declared_entity(&mut self, entity_decl: &EntityDeclaration)
+ {
+ entity_decl.spawn(self);
+ }
+
+ /// Adds a singleton component.
+ ///
+ /// # Errors
+ /// Returns `Err` if this [`Sole`] has already been added.
+ pub fn add_sole<SoleT>(&mut self, sole: SoleT) -> Result<(), SoleAlreadyExistsError>
+ where
+ SoleT: Sole,
+ {
+ if self
+ .data
+ .component_storage
+ .get_entity_archetype(SoleT::id())
+ .is_some()
+ {
+ return Err(SoleAlreadyExistsError);
+ }
+
+ let name = sole.name();
+
+ self.create_ent(
+ SoleT::id(),
+ [
+ sole.into_parts(),
+ EntityName { name: name.into() }.into_parts(),
+ ],
+ );
+
+ Ok(())
+ }
+
+ pub fn register_observer<'this, SystemImpl, ObserverT>(
+ &'this mut self,
+ observer: ObserverT,
+ ) where
+ ObserverT: Observer<'this, SystemImpl>,
+ {
+ let (system, mut system_callbacks) = observer.finish_observer();
+
+ let ent_id = Uid::new_unique();
+
+ self.create_ent(
+ ent_id,
+ [SystemComponent { system }.into_parts()].into_iter().chain(
+ ObserverT::observed_events()
+ .into_iter()
+ .map(IntoComponentParts::into_parts),
+ ),
+ );
+
+ system_callbacks.on_created(self, SystemMetadata { ent_id });
+ }
+
+ pub fn register_system<'this, SystemImpl>(
+ &'this mut self,
+ phase_euid: Uid,
+ system: impl System<'this, SystemImpl>,
+ )
+ {
+ let (type_erased_system, mut system_callbacks) = system.finish();
+
+ let system_ent_id = self.spawn((SystemComponent { system: type_erased_system },));
+
+ system_callbacks.on_created(self, SystemMetadata { ent_id: system_ent_id });
+
+ self.spawn_with_uid(
+ phase_euid,
+ (Pair::builder()
+ .relation::<PhaseHasSystem>()
+ .target_id(system_ent_id)
+ .build(),),
+ );
+ }
+
+ /// Adds a extensions.
+ pub fn add_extension(&mut self, extension: impl Extension)
+ {
+ let extension_collector = ExtensionCollector::new(self);
+
+ extension.collect(extension_collector);
+ }
+
+ pub fn query<'this, TermsT>(&'this self) -> Query<'this, TermsT>
+ where
+ TermsT: QueryTermTuple<'this>,
+ {
+ Query::new(self)
+ }
+
+ pub fn flexible_query<const MAX_TERM_CNT: usize>(
+ &self,
+ terms: QueryTerms<MAX_TERM_CNT>,
+ ) -> FlexibleQuery<'_, MAX_TERM_CNT>
+ {
+ FlexibleQuery::new(self, terms)
+ }
+
+ pub fn get_entity(&self, entity_id: Uid) -> Option<EntityHandle<'_>>
+ {
+ let archetype = self
+ .data
+ .component_storage
+ .get_entity_archetype(entity_id)?;
+
+ let Some(entity) = archetype.get_entity_by_id(entity_id) else {
+ unreachable!("Should exist since archetype was found by entity id");
+ };
+
+ Some(EntityHandle::new(archetype, entity, self))
+ }
+
+ pub fn get_sole<SoleT: Sole>(&self) -> Single<'_, SoleT>
+ {
+ Single::<SoleT>::new(self)
+ }
+
+ pub fn event_submitter(&self) -> EventSubmitter<'_>
+ {
+ EventSubmitter::new(&self.data.new_events)
+ }
+
+ /// Performs a single tick.
+ /// # Panics
+ /// Will panic if mutable internal lock cannot be acquired.
+ pub fn step(&mut self) -> StepResult
+ {
+ if self.stop.load(Ordering::Relaxed) {
+ return StepResult::Stop;
+ }
+
+ {
+ let mut time = self.get_sole::<time::Time>();
+
+ let Ok(time) = time.get_mut() else {
+ unreachable!();
+ };
+
+ time::update(&mut *time);
+ }
+
+ if self
+ .is_first_tick
+ .compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
+ .is_ok()
+ {
+ let Some(init_phase_entity) = self.get_entity(*INIT_PHASE) else {
+ unreachable!();
+ };
+
+ self.run_phase_systems(&init_phase_entity);
+
+ let Some(start_phase_entity) = self.get_entity(*START_PHASE) else {
+ unreachable!();
+ };
+
+ self.run_phase_systems(&start_phase_entity);
+ }
+
+ self.perform_phases();
+
+ self.emit_new_events();
+
+ self.data.component_storage.create_imaginary_archetypes();
+
+ self.perform_queued_actions();
+
+ if self.stop.load(Ordering::Relaxed) {
+ return StepResult::Stop;
+ }
+
+ StepResult::Continue
+ }
+
+ /// Starts a loop which calls [`Self::step`] until the world is stopped.
+ pub fn start_loop(&mut self)
+ {
+ while let StepResult::Continue = self.step() {}
+ }
+
+ #[cfg(feature = "vizoxide")]
+ pub fn create_vizoxide_archetype_graph(
+ &self,
+ name: impl AsRef<str>,
+ ) -> Result<vizoxide::Graph, vizoxide::GraphvizError>
+ {
+ use std::borrow::Cow;
+
+ use crate::component::storage::{
+ VizoxideArchetypeGraphEdgeKind,
+ VizoxideArchetypeGraphParams,
+ };
+
+ self.data.component_storage.create_vizoxide_archetype_graph(
+ name,
+ VizoxideArchetypeGraphParams {
+ create_node_name: |archetype, _| {
+ Cow::Owned(format!(
+ "[{}]",
+ archetype
+ .component_ids_sorted()
+ .into_iter()
+ .map(|comp_id| comp_id.to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ ))
+ },
+ create_node_cb: |_archetype, archetype_metadata, node_builder| {
+ if archetype_metadata.is_imaginary {
+ return node_builder.attribute("shape", "ellipse");
+ }
+
+ node_builder.attribute("shape", "box")
+ },
+ create_edge_cb: |_, _, edge_kind, edge_builder| {
+ edge_builder.attribute(
+ "color",
+ match edge_kind {
+ VizoxideArchetypeGraphEdgeKind::Add => "green",
+ VizoxideArchetypeGraphEdgeKind::Remove => "red",
+ },
+ )
+ },
+ },
+ )
+ }
+
+ #[tracing::instrument(skip_all)]
+ fn create_ent(
+ &mut self,
+ entity_uid: Uid,
+ components: impl IntoIterator<Item = ComponentParts>,
+ )
+ {
+ debug_assert!(!entity_uid.is_pair());
+
+ if let Err(EntityAlreadyExistsError) =
+ self.data.component_storage.create_entity(entity_uid)
+ {
+ // This is fine
+ }
+
+ Self::add_entity_components(
+ entity_uid,
+ components,
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ );
+ }
+
+ fn run_phase_systems(&self, phase_entity: &EntityHandle<'_>)
+ {
+ // The phase's systems are retrieved this way so that the order they are
+ // run is the same order as they were registered, even if they have local
+ // components.
+ for system_entity in phase_entity
+ .get_wildcard_pair_matches::<PhaseHasSystem, Wildcard>()
+ .into_iter()
+ .filter_map(|phase_has_system| phase_has_system.get_target_ent())
+ {
+ let Some(system) = system_entity.get::<SystemComponent>() else {
+ cold_path();
+ continue;
+ };
+
+ // SAFETY: The world lives long enough
+ if let Err(err) = unsafe {
+ system.system.run(
+ self,
+ SystemMetadata { ent_id: system_entity.uid() },
+ None,
+ )
+ } {
+ cold_path();
+
+ (self.error_handler)(
+ self,
+ err,
+ ErrorMetadata {
+ source_name: system.system.name(),
+ source_kind: ErrorSourceKind::System,
+ },
+ )
+ }
+ }
+ }
+
+ fn perform_child_phases(&self, parent_phase_euid: Uid)
+ {
+ let phase_query = self.flexible_query(
+ QueryTerms::<2>::builder()
+ .present([
+ Phase::id(),
+ Pair::builder()
+ .relation::<ChildOf>()
+ .target_id(parent_phase_euid)
+ .build()
+ .id(),
+ ])
+ .build(),
+ );
+
+ for child_phase_entity in &phase_query {
+ self.run_phase_systems(&child_phase_entity);
+ self.perform_child_phases(child_phase_entity.uid());
+ }
+ }
+
+ fn perform_single_phase(&self, phase_entity_id: Uid)
+ {
+ let Some(phase_entity) = self.get_entity(phase_entity_id) else {
+ unreachable!();
+ };
+
+ self.run_phase_systems(&phase_entity);
+ self.perform_child_phases(phase_entity_id);
+ }
+
+ fn perform_phases(&self)
+ {
+ self.perform_single_phase(*PRE_UPDATE_PHASE);
+ self.perform_single_phase(*UPDATE_PHASE);
+ self.perform_single_phase(*POST_UPDATE_PHASE);
+ }
+
+ fn emit_new_events(&self)
+ {
+ loop {
+ let new_events = {
+ let mut new_events_lock = self
+ .data
+ .new_events
+ .write_nonblock()
+ .expect("Failed to acquire read-write lock to new events");
+
+ if new_events_lock.is_empty() {
+ break;
+ }
+
+ new_events_lock.take()
+ };
+
+ for (event_id, event_matches) in new_events {
+ self.emit_event_observers(
+ event_id,
+ &EmittedEvent {
+ event: event_id,
+ match_ids: &event_matches.match_ids,
+ },
+ );
+ }
+ }
+ }
+
+ #[tracing::instrument(skip_all)]
+ fn perform_queued_actions(&mut self)
+ {
+ let mut action_queue_lock = self
+ .data
+ .action_queue
+ .queue
+ .write_nonblock()
+ .unwrap_or_else(|err| {
+ panic!("Failed to take read-write action queue lock: {err}",);
+ });
+
+ for action in action_queue_lock.drain(..) {
+ match action {
+ Action::Spawn(entity_id, components) => {
+ if let Err(err) = self.data.component_storage.create_entity(entity_id)
+ {
+ tracing::error!(
+ entity = %entity_id,
+ "Failed to create entity: {err}"
+ );
+
+ continue;
+ }
+
+ Self::add_entity_components(
+ entity_id,
+ components,
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ );
+ }
+ Action::Despawn(entity_id) => {
+ if let Err(err) = self.data.component_storage.remove_entity(entity_id)
+ {
+ tracing::error!(
+ entity = %entity_id,
+ "Failed to despawn entity: {err}"
+ );
+ }
+ }
+ Action::AddComponents(entity_uid, components) => {
+ Self::add_entity_components(
+ entity_uid,
+ components,
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ );
+ }
+ Action::RemoveComponents(entity_uid, component_ids) => {
+ Self::remove_entity_components(
+ entity_uid,
+ component_ids,
+ &mut self.data.component_storage,
+ );
+ }
+ Action::SetPair { entity_id, pair_id, new_pair } => {
+ if self.get_entity(entity_id).is_none() {
+ tracing::error!(
+ entity = %entity_id,
+ pair = %pair_id,
+ new_pair = %new_pair.id,
+ "Cannot set pair of a non-existant entity"
+ );
+ continue;
+ };
+
+ let _ = self
+ .data
+ .component_storage
+ .remove_entity_component(entity_id, pair_id);
+
+ Self::add_entity_components(
+ entity_id,
+ [new_pair],
+ &mut self.data.component_storage,
+ &EventSubmitter::new(&self.data.new_events),
+ );
+ }
+ Action::Stop => {
+ self.stop.store(true, Ordering::Relaxed);
+ }
+ }
+ }
+ }
+
+ fn add_entity_components(
+ entity_uid: Uid,
+ components: impl IntoIterator<Item = ComponentParts>,
+ component_storage: &mut ComponentStorage,
+ event_submitter: &EventSubmitter<'_>,
+ )
+ {
+ let component_iter = components.into_iter();
+
+ for component_parts in component_iter {
+ let comp_id = component_parts.id;
+ let comp_name = component_parts.name;
+ let comp_type_id = (&*component_parts.data).type_id();
+
+ if let Err(err) = component_storage.add_entity_component(
+ entity_uid,
+ (comp_id, comp_name, component_parts.data),
+ ) {
+ let ent_name = Self::get_component_of_entity::<EntityName>(
+ component_storage,
+ entity_uid,
+ );
+
+ tracing::error!(
+ entity = %entity_uid,
+ entity_name = ent_name.as_ref().map(|ent_name| ent_name.name.as_ref()),
+ component = %comp_id,
+ component_name = comp_name,
+ "Failed to add component to entity: {err}"
+ );
+
+ continue;
+ }
+
+ if comp_id.is_pair() {
+ if let Some(pair_relation_metadata) =
+ &component_parts.pair_relation_metadata
+ {
+ Self::create_component_info_entity_if_missing(
+ component_storage,
+ comp_id.relation(),
+ ComponentInfo {
+ type_reflection: pair_relation_metadata.ty,
+ ty_id: pair_relation_metadata.ty_id,
+ name: pair_relation_metadata.name,
+ is_sole: pair_relation_metadata.is_sole,
+ },
+ );
+ }
+
+ if let Some(pair_target_metadata) = &component_parts.pair_target_metadata
+ {
+ Self::create_component_info_entity_if_missing(
+ component_storage,
+ comp_id.target(),
+ ComponentInfo {
+ type_reflection: pair_target_metadata.ty,
+ ty_id: pair_target_metadata.ty_id,
+ name: pair_target_metadata.name,
+ is_sole: pair_target_metadata.is_sole,
+ },
+ );
+ }
+
+ continue;
+ }
+
+ Self::create_component_info_entity_if_missing(
+ component_storage,
+ comp_id,
+ ComponentInfo {
+ type_reflection: component_parts.type_reflection,
+ ty_id: comp_type_id,
+ name: comp_name,
+ is_sole: component_parts.is_sole,
+ },
+ );
+
+ event_submitter.submit_event(
+ &Pair::builder()
+ .relation::<Added>()
+ .target_id(comp_id)
+ .build(),
+ entity_uid,
+ );
+ }
+ }
+
+ fn create_component_info_entity_if_missing(
+ component_storage: &mut ComponentStorage,
+ comp_id: Uid,
+ comp_info: ComponentInfo,
+ )
+ {
+ if let Some(comp_ent_archetype) = component_storage.get_entity_archetype(comp_id)
+ {
+ if comp_ent_archetype.contains_component_with_exact_id(ComponentInfo::id()) {
+ return;
+ }
+ } else {
+ if let Err(EntityAlreadyExistsError) =
+ component_storage.create_entity(comp_id)
+ {
+ unreachable!();
+ }
+ }
+
+ let comp_info_parts = comp_info.into_parts();
+
+ match component_storage.add_entity_component(
+ comp_id,
+ (
+ comp_info_parts.id,
+ comp_info_parts.name,
+ comp_info_parts.data,
+ ),
+ ) {
+ Ok(()) => {}
+ Err(ComponentStorageError::EntityDoesNotExist(_)) => {
+ // Entity is created if it does not exist
+ unreachable!();
+ }
+ Err(ComponentStorageError::ComponentAlreadyInEntity { .. }) => {
+ // This function returns early if ComponentInfo is already present
+ unreachable!();
+ }
+ Err(ComponentStorageError::ComponentNotFoundInEntity { .. }) => {
+ // This error is never returned by add_entity_component
+ unreachable!();
+ }
+ };
+ }
+
+ fn remove_entity_components(
+ entity_id: Uid,
+ component_ids: impl IntoIterator<Item = Uid>,
+ component_storage: &mut ComponentStorage,
+ )
+ {
+ let component_id_iter = component_ids.into_iter();
+
+ for component_id in component_id_iter {
+ if let Err(err) =
+ component_storage.remove_entity_component(entity_id, component_id)
+ {
+ let ent_name = Self::get_component_of_entity::<EntityName>(
+ component_storage,
+ entity_id,
+ );
+
+ let component_info = Self::get_component_of_entity::<ComponentInfo>(
+ component_storage,
+ component_id,
+ );
+
+ tracing::error!(
+ entity = %entity_id,
+ entity_name = ent_name.as_ref().map(|ent_name| ent_name.name.as_ref()),
+ component = %component_id,
+ component_name = component_info
+ .as_ref()
+ .map(|component_info| component_info.name),
+ "Failed to remove component from entity: {err}"
+ );
+ }
+ }
+ }
+
+ pub fn get_component_of_entity<ComponentT: Component>(
+ component_storage: &ComponentStorage,
+ entity_id: Uid,
+ ) -> Option<crate::component::Handle<'_, ComponentT>>
+ {
+ let archetype = component_storage.get_entity_archetype(entity_id)?;
+
+ let Some(entity) = archetype.get_entity_by_id(entity_id) else {
+ unreachable!("Should exist since archetype was found by entity id");
+ };
+
+ let comp_index = archetype.get_index_for_component(ComponentT::id())?;
+
+ let comp = &entity.components()[comp_index];
+
+ crate::component::Handle::from_entity_component_ref(&EntityComponentRef {
+ component_id: ComponentT::id(),
+ component: comp,
+ entity_id,
+ })
+ .ok()
+ }
+
+ fn emit_event_observers(&self, event_id: Uid, emitted_event: &EmittedEvent<'_>)
+ {
+ assert!(event_id.is_pair());
+
+ let query = Query::<(&SystemComponent,)>::from_flexible_query(
+ self.flexible_query(
+ QueryTerms::<QUERY_MAX_TERM_CNT>::builder()
+ .present([SystemComponent::id(), event_id])
+ .build(),
+ ),
+ );
+
+ for (observer_ent_id, (observer,)) in query.iter_with_euids() {
+ if let Err(err) = unsafe {
+ observer.system.run(
+ self,
+ SystemMetadata { ent_id: observer_ent_id },
+ Some(emitted_event.clone()),
+ )
+ } {
+ cold_path();
+
+ (self.error_handler)(
+ self,
+ err,
+ ErrorMetadata {
+ source_name: observer.system.name(),
+ source_kind: ErrorSourceKind::Observer,
+ },
+ )
+ }
+ }
+ }
+}
+
+impl Default for World
+{
+ fn default() -> Self
+ {
+ Self::new()
+ }
+}
+
+impl<'world> SystemParam<'world> for &'world World
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ world
+ }
+}
+
+/// The result of calling [`World::step`].
+pub enum StepResult
+{
+ /// Another step can be made.
+ Continue,
+
+ /// The world have been stopped so no step can be made again.
+ Stop,
+}
+
+#[derive(Debug, Default)]
+struct WorldData
+{
+ component_storage: ComponentStorage,
+ action_queue: Rc<ActionQueue>,
+ new_events: Lock<NewEvents>,
+}
+
+#[derive(Debug, Clone)]
+pub struct EntityComponentRef<'a>
+{
+ component_id: Uid,
+ component: &'a ArchetypeEntityComponent,
+ entity_id: Uid,
+}
+
+impl<'a> EntityComponentRef<'a>
+{
+ fn component(&self) -> &'a Lock<Box<dyn Any>>
+ {
+ self.component.component()
+ }
+
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ self.component_id
+ }
+
+ #[must_use]
+ pub fn entity_id(&self) -> Uid
+ {
+ self.entity_id
+ }
+
+ fn new(component_id: Uid, comp: &'a ArchetypeEntityComponent, entity_id: Uid)
+ -> Self
+ {
+ Self {
+ component_id,
+ component: comp,
+ entity_id,
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct ActionQueue
+{
+ queue: Lock<Vec<Action>>,
+}
+
+impl ActionQueue
+{
+ fn push(&self, action: Action)
+ {
+ self.queue
+ .write_nonblock()
+ .expect("Failed to aquire read-write lock to action queue")
+ .push(action);
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Sole already exists")]
+pub struct SoleAlreadyExistsError;
diff --git a/ecs/src/lock.rs b/engine-ecs/src/lock.rs
index 0b36922..3db29e8 100644
--- a/ecs/src/lock.rs
+++ b/engine-ecs/src/lock.rs
@@ -1,3 +1,4 @@
+use std::any::type_name;
use std::mem::forget;
use std::ops::{Deref, DerefMut};
@@ -30,7 +31,7 @@ impl<Value> Lock<Value>
///
/// # Errors
/// Returns `Err` if unavailable (A mutable handle is hold).
- pub fn read_nonblock(&self) -> Result<ReadGuard<Value>, Error>
+ pub fn read_nonblock(&self) -> Result<ReadGuard<'_, Value>, Error>
{
let guard = self.inner.try_read().ok_or(Error::ReadUnavailable)?;
@@ -46,7 +47,7 @@ impl<Value> Lock<Value>
///
/// # Errors
/// Returns `Err` if unavailable (A mutable or immutable handle is hold).
- pub fn write_nonblock(&self) -> Result<WriteGuard<Value>, Error>
+ pub fn write_nonblock(&self) -> Result<WriteGuard<'_, Value>, Error>
{
let guard = self.inner.try_write().ok_or(Error::WriteUnavailable)?;
@@ -62,6 +63,14 @@ impl<Value> Lock<Value>
}
}
+impl<Value: Default + 'static> Default for Lock<Value>
+{
+ fn default() -> Self
+ {
+ Self::new(Value::default(), type_name::<Value>())
+ }
+}
+
#[derive(Debug, thiserror::Error)]
pub enum Error
{
@@ -73,34 +82,58 @@ pub enum Error
}
#[derive(Debug)]
-pub struct ReadGuard<'guard, Value>
+pub struct ReadGuard<'guard, Value: ?Sized>
{
inner: RwLockReadGuard<'guard, Value>,
value_type_name: &'static str,
}
-impl<'guard, Value> ReadGuard<'guard, Value>
+impl<'guard, Value: ?Sized> ReadGuard<'guard, Value>
{
- pub fn map<NewValue>(
- self,
+ pub fn map<NewValue: ?Sized>(
+ this: Self,
func: impl FnOnce(&Value) -> &NewValue,
) -> MappedReadGuard<'guard, NewValue>
{
- let value_type_name = self.value_type_name;
+ let value_type_name = this.value_type_name;
// The 'inner' field cannot be moved out of ReadGuard in a normal way since
// ReadGuard implements Drop
- let inner = unsafe { std::ptr::read(&self.inner) };
- forget(self);
+ let inner = unsafe { std::ptr::read(&raw const this.inner) };
+
+ forget(this);
MappedReadGuard {
inner: RwLockReadGuard::map(inner, func),
- value_type_name,
+ value_type_name: value_type_name,
+ }
+ }
+
+ pub fn try_map<NewValue>(
+ this: Self,
+ func: impl FnOnce(&Value) -> Option<&NewValue>,
+ ) -> Result<MappedReadGuard<'guard, NewValue>, Self>
+ {
+ let value_type_name = this.value_type_name;
+
+ // The 'inner' field cannot be moved out of ReadGuard in a normal way since
+ // ReadGuard implements Drop
+ let inner = unsafe { std::ptr::read(&raw const this.inner) };
+ forget(this);
+
+ match RwLockReadGuard::try_map(inner, func) {
+ Ok(mapped_guard) => {
+ Ok(MappedReadGuard { inner: mapped_guard, value_type_name })
+ }
+ Err(unmapped_guard) => Err(Self {
+ inner: unmapped_guard,
+ value_type_name,
+ }),
}
}
}
-impl<Value> Deref for ReadGuard<'_, Value>
+impl<Value: ?Sized> Deref for ReadGuard<'_, Value>
{
type Target = Value;
@@ -110,7 +143,7 @@ impl<Value> Deref for ReadGuard<'_, Value>
}
}
-impl<Value> Drop for ReadGuard<'_, Value>
+impl<Value: ?Sized> Drop for ReadGuard<'_, Value>
{
fn drop(&mut self)
{
@@ -119,13 +152,13 @@ impl<Value> Drop for ReadGuard<'_, Value>
}
#[derive(Debug)]
-pub struct MappedReadGuard<'guard, Value>
+pub struct MappedReadGuard<'guard, Value: ?Sized>
{
inner: MappedRwLockReadGuard<'guard, Value>,
value_type_name: &'static str,
}
-impl<Value> Deref for MappedReadGuard<'_, Value>
+impl<Value: ?Sized> Deref for MappedReadGuard<'_, Value>
{
type Target = Value;
@@ -135,7 +168,7 @@ impl<Value> Deref for MappedReadGuard<'_, Value>
}
}
-impl<Value> Drop for MappedReadGuard<'_, Value>
+impl<Value: ?Sized> Drop for MappedReadGuard<'_, Value>
{
fn drop(&mut self)
{
@@ -147,34 +180,58 @@ impl<Value> Drop for MappedReadGuard<'_, Value>
}
#[derive(Debug)]
-pub struct WriteGuard<'guard, Value>
+pub struct WriteGuard<'guard, Value: ?Sized>
{
inner: RwLockWriteGuard<'guard, Value>,
value_type_name: &'static str,
}
-impl<'guard, Value> WriteGuard<'guard, Value>
+impl<'guard, Value: ?Sized> WriteGuard<'guard, Value>
{
- pub fn map<NewValue>(
- self,
+ pub fn map<NewValue: ?Sized>(
+ this: Self,
func: impl FnOnce(&mut Value) -> &mut NewValue,
) -> MappedWriteGuard<'guard, NewValue>
{
- let value_type_name = self.value_type_name;
+ let value_type_name = this.value_type_name;
// The 'inner' field cannot be moved out of ReadGuard in a normal way since
// ReadGuard implements Drop
- let inner = unsafe { std::ptr::read(&self.inner) };
- forget(self);
+ let inner = unsafe { std::ptr::read(&raw const this.inner) };
+
+ forget(this);
MappedWriteGuard {
inner: RwLockWriteGuard::map(inner, func),
- value_type_name,
+ value_type_name: value_type_name,
+ }
+ }
+
+ pub fn try_map<NewValue>(
+ this: Self,
+ func: impl FnOnce(&mut Value) -> Option<&mut NewValue>,
+ ) -> Result<MappedWriteGuard<'guard, NewValue>, Self>
+ {
+ let value_type_name = this.value_type_name;
+
+ // The 'inner' field cannot be moved out of ReadGuard in a normal way since
+ // ReadGuard implements Drop
+ let inner = unsafe { std::ptr::read(&raw const this.inner) };
+ forget(this);
+
+ match RwLockWriteGuard::try_map(inner, func) {
+ Ok(mapped_guard) => {
+ Ok(MappedWriteGuard { inner: mapped_guard, value_type_name })
+ }
+ Err(unmapped_guard) => Err(Self {
+ inner: unmapped_guard,
+ value_type_name,
+ }),
}
}
}
-impl<Value> Deref for WriteGuard<'_, Value>
+impl<Value: ?Sized> Deref for WriteGuard<'_, Value>
{
type Target = Value;
@@ -184,7 +241,7 @@ impl<Value> Deref for WriteGuard<'_, Value>
}
}
-impl<Value> DerefMut for WriteGuard<'_, Value>
+impl<Value: ?Sized> DerefMut for WriteGuard<'_, Value>
{
fn deref_mut(&mut self) -> &mut Self::Target
{
@@ -192,7 +249,7 @@ impl<Value> DerefMut for WriteGuard<'_, Value>
}
}
-impl<Value> Drop for WriteGuard<'_, Value>
+impl<Value: ?Sized> Drop for WriteGuard<'_, Value>
{
fn drop(&mut self)
{
@@ -204,13 +261,13 @@ impl<Value> Drop for WriteGuard<'_, Value>
}
#[derive(Debug)]
-pub struct MappedWriteGuard<'guard, Value>
+pub struct MappedWriteGuard<'guard, Value: ?Sized>
{
inner: MappedRwLockWriteGuard<'guard, Value>,
value_type_name: &'static str,
}
-impl<Value> Deref for MappedWriteGuard<'_, Value>
+impl<Value: ?Sized> Deref for MappedWriteGuard<'_, Value>
{
type Target = Value;
@@ -220,7 +277,7 @@ impl<Value> Deref for MappedWriteGuard<'_, Value>
}
}
-impl<Value> DerefMut for MappedWriteGuard<'_, Value>
+impl<Value: ?Sized> DerefMut for MappedWriteGuard<'_, Value>
{
fn deref_mut(&mut self) -> &mut Self::Target
{
@@ -228,7 +285,7 @@ impl<Value> DerefMut for MappedWriteGuard<'_, Value>
}
}
-impl<Value> Drop for MappedWriteGuard<'_, Value>
+impl<Value: ?Sized> Drop for MappedWriteGuard<'_, Value>
{
fn drop(&mut self)
{
diff --git a/engine-ecs/src/pair.rs b/engine-ecs/src/pair.rs
new file mode 100644
index 0000000..834077c
--- /dev/null
+++ b/engine-ecs/src/pair.rs
@@ -0,0 +1,946 @@
+use std::any::{type_name, TypeId};
+use std::convert::Infallible;
+use std::marker::PhantomData;
+
+use crate::component::{
+ Handle as ComponentHandle,
+ HandleError as ComponentHandleError,
+ HandleMut as ComponentHandleMut,
+ IntoParts as IntoComponentParts,
+ Parts as ComponentParts,
+};
+use crate::entity::{
+ Handle as EntityHandle,
+ MatchingComponentIter as EntityMatchingComponentIter,
+};
+use crate::query::{
+ SearchResult,
+ Term as QueryTerm,
+ TermMetadata as QueryTermMetadata,
+ TermsBuilder as QueryTermsBuilder,
+ TermsBuilderInterface,
+};
+use crate::reflection::Type as TypeReflection;
+use crate::uid::{PairParams as UidPairParams, Uid, With as WithUid};
+use crate::util::impl_multiple;
+use crate::{Component, EntityComponentRef, World};
+
+/// Pair builder.
+#[derive(Debug)]
+pub struct Builder<Relation, Target>
+{
+ relation: Relation,
+ target: Target,
+ relation_metadata: Option<MemberMetadata>,
+ target_metadata: Option<MemberMetadata>,
+}
+
+impl<Relation, Target> Builder<Relation, Target>
+{
+ pub fn relation<NewRelation: Component>(self) -> Builder<Uid, Target>
+ {
+ Builder {
+ relation: NewRelation::id(),
+ target: self.target,
+ relation_metadata: Some(MemberMetadata {
+ ty: NewRelation::type_reflection(),
+ ty_id: TypeId::of::<NewRelation>(),
+ name: type_name::<NewRelation>(),
+ is_sole: false,
+ }),
+ target_metadata: self.target_metadata,
+ }
+ }
+
+ pub fn relation_id(self, id: Uid) -> Builder<Uid, Target>
+ {
+ Builder {
+ relation: id,
+ target: self.target,
+ relation_metadata: None,
+ target_metadata: self.target_metadata,
+ }
+ }
+
+ pub fn target<NewTarget: Component>(self) -> Builder<Relation, Uid>
+ {
+ Builder {
+ relation: self.relation,
+ target: NewTarget::id(),
+ relation_metadata: self.relation_metadata,
+ target_metadata: Some(MemberMetadata {
+ ty: NewTarget::type_reflection(),
+ ty_id: TypeId::of::<NewTarget>(),
+ name: type_name::<NewTarget>(),
+ is_sole: false,
+ }),
+ }
+ }
+
+ pub fn target_id(self, id: Uid) -> Builder<Relation, Uid>
+ {
+ Builder {
+ relation: self.relation,
+ target: id,
+ relation_metadata: self.relation_metadata,
+ target_metadata: None,
+ }
+ }
+}
+
+impl_multiple!(
+ Builder,
+ (impl<Target> _<><Uid, Target>, impl<Target> _<><(), Target>)
+ cb=(type_params=(ty_param_1, ty_param_2)) => {
+ pub fn target_as_data<NewTarget: Component>(
+ self,
+ data: NewTarget,
+ ) -> Builder<$ty_param_1, NewTarget>
+ {
+ Builder {
+ relation: self.relation,
+ target: data,
+ relation_metadata: self.relation_metadata,
+ target_metadata: Some(MemberMetadata {
+ ty: NewTarget::type_reflection(),
+ ty_id: TypeId::of::<NewTarget>(),
+ name: type_name::<NewTarget>(),
+ is_sole: false,
+ }),
+ }
+ }
+ }
+);
+
+impl_multiple!(
+ Builder,
+ (impl<Relation> _<><Relation, Uid>, impl<Relation> _<><Relation, ()>)
+ cb=(type_params=(ty_param_1, ty_param_2)) => {
+ pub fn relation_as_data<NewRelation: Component>(
+ self,
+ data: NewRelation,
+ ) -> Builder<NewRelation, $ty_param_2>
+ {
+ Builder {
+ relation: data,
+ target: self.target,
+ relation_metadata: Some(MemberMetadata {
+ ty: NewRelation::type_reflection(),
+ ty_id: TypeId::of::<NewRelation>(),
+ name: type_name::<NewRelation>(),
+ is_sole: false,
+ }),
+ target_metadata: self.target_metadata,
+ }
+ }
+ }
+);
+
+impl_multiple!(
+ Builder,
+ (
+ impl _<><Uid, Uid>,
+ impl<Relation: Component> _<><Relation, Uid>,
+ impl<Target: Component> _<><Uid, Target>,
+ impl<Relation: Component, Target: Component> _<><Relation, Target>
+ )
+ cb=(type_params=(ty_param_1, ty_param_2)) => {
+ #[must_use]
+ pub fn build(self) -> Pair<$ty_param_1, $ty_param_2>
+ {
+ Pair {
+ relation: self.relation,
+ target: self.target,
+ relation_metadata: self.relation_metadata,
+ target_metadata: self.target_metadata,
+ }
+ }
+ }
+);
+
+impl Default for Builder<(), ()>
+{
+ fn default() -> Self
+ {
+ Self {
+ relation: (),
+ target: (),
+ relation_metadata: None,
+ target_metadata: None,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct Pair<Relation, Target>
+{
+ relation: Relation,
+ target: Target,
+ relation_metadata: Option<MemberMetadata>,
+ target_metadata: Option<MemberMetadata>,
+}
+
+impl Pair<(), ()>
+{
+ #[must_use]
+ pub fn builder() -> Builder<(), ()>
+ {
+ Builder::default()
+ }
+}
+
+impl Pair<Uid, Uid>
+{
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ Uid::new_pair(&UidPairParams {
+ relation: self.relation,
+ target: self.target,
+ })
+ }
+}
+
+impl IntoComponentParts for Pair<Uid, Uid>
+{
+ fn into_parts(self) -> ComponentParts
+ {
+ let id = self.id();
+
+ ComponentParts::builder()
+ .name("Pair")
+ .pair_relation_metadata(self.relation_metadata)
+ .pair_target_metadata(self.target_metadata)
+ .build(id, ())
+ }
+}
+
+impl<Target> IntoComponentParts for Pair<Uid, Target>
+where
+ Target: Component,
+{
+ fn into_parts(self) -> ComponentParts
+ {
+ let id = Uid::new_pair(&UidPairParams {
+ relation: self.relation,
+ target: Target::id(),
+ });
+
+ ComponentParts::builder()
+ .name("Pair")
+ .pair_relation_metadata(self.relation_metadata)
+ .pair_target_metadata(self.target_metadata)
+ .build(id, self.target)
+ }
+}
+
+impl<Relation> IntoComponentParts for Pair<Relation, Uid>
+where
+ Relation: Component,
+{
+ fn into_parts(self) -> ComponentParts
+ {
+ let id = Uid::new_pair(&UidPairParams {
+ relation: Relation::id(),
+ target: self.target,
+ });
+
+ ComponentParts::builder()
+ .name("Pair")
+ .pair_relation_metadata(self.relation_metadata)
+ .pair_target_metadata(self.target_metadata)
+ .build(id, self.relation)
+ }
+}
+
+impl<'query, Relation, Target> QueryTerm<'query> for Pair<Relation, &Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ type Fields = (ComponentHandle<'query, Target>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: QueryTermMetadata,
+ )
+ {
+ terms_builder.present([Pair::<Relation, Target>::uid()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: QueryTermMetadata,
+ ) -> Self::Fields
+ {
+ let target_component = search_result
+ .entity_handle
+ .get_matching_components(Pair::<Relation, Target>::uid())
+ .next()
+ .expect("Not possible");
+
+ let target_component =
+ match ComponentHandle::<Target>::from_entity_component_ref(&target_component)
+ {
+ Ok(target_component) => target_component,
+ Err(err) => {
+ panic!(
+ "Creating handle to target component {} failed: {err}",
+ type_name::<Target>()
+ );
+ }
+ };
+
+ (target_component,)
+ }
+}
+
+impl<'query, Relation, Target> QueryTerm<'query> for Pair<Relation, &mut Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ type Fields = (ComponentHandleMut<'query, Target>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: QueryTermMetadata,
+ )
+ {
+ terms_builder.present([Pair::<Relation, Target>::uid()]);
+ }
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: QueryTermMetadata,
+ ) -> Self::Fields
+ {
+ let target_component = search_result
+ .entity_handle
+ .get_matching_components(Pair::<Relation, Target>::uid())
+ .next()
+ .expect("Not possible");
+
+ let target_component =
+ match ComponentHandleMut::<Target>::from_entity_component_ref(
+ &target_component,
+ world,
+ ) {
+ Ok(target_component) => target_component,
+ Err(err) => {
+ panic!(
+ "Creating mut handle to target component {} failed: {err}",
+ type_name::<Target>()
+ );
+ }
+ };
+
+ (target_component,)
+ }
+}
+
+// TODO: implement QueryTerm for Pair<&Relation, Target> (or equivalent)
+// TODO: implement QueryTerm for Pair<&mut Relation, Target> (or equivalent)
+
+impl<'query, Relation> QueryTerm<'query> for Pair<Relation, Wildcard>
+where
+ Relation: Component,
+{
+ type Fields = (WithWildcard<'query, Relation, Wildcard>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: QueryTermMetadata,
+ )
+ {
+ terms_builder.present([Self::uid()]);
+ }
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: QueryTermMetadata,
+ ) -> Self::Fields
+ {
+ let first_matching_comp = search_result
+ .entity_handle
+ .get_matching_components(Self::uid())
+ .next()
+ .expect("Not possible");
+
+ (WithWildcard {
+ world,
+ component_ref: first_matching_comp,
+ _pd: PhantomData,
+ },)
+ }
+}
+
+impl<Relation, Target> WithUid for Pair<Relation, Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ fn uid() -> Uid
+ {
+ Uid::new_pair(&UidPairParams {
+ relation: Relation::id(),
+ target: Target::id(),
+ })
+ }
+}
+
+impl<Relation> WithUid for Pair<Relation, Wildcard>
+where
+ Relation: Component,
+{
+ fn uid() -> Uid
+ {
+ Uid::new_pair(&UidPairParams {
+ relation: Relation::id(),
+ target: Wildcard::uid(),
+ })
+ }
+}
+
+impl<'query, Relation> QueryTerm<'query> for &[Pair<Relation, Wildcard>]
+where
+ Relation: Component,
+{
+ type Fields = (MultipleWithWildcard<'query, Relation, Wildcard>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ _terms_builder: &mut QueryTermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: QueryTermMetadata,
+ )
+ {
+ }
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: QueryTermMetadata,
+ ) -> Self::Fields
+ {
+ (MultipleWithWildcard {
+ entity_handle: search_result.entity_handle.clone(),
+ world,
+ _pd: PhantomData,
+ },)
+ }
+}
+
+/// Reference to a pair with a wildcard relation/target.
+#[derive(Debug)]
+pub struct WithWildcard<'query, Relation, Target>
+{
+ world: &'query World,
+ component_ref: EntityComponentRef<'query>,
+ _pd: PhantomData<(Relation, Target)>,
+}
+
+impl<'query, Relation, Target> WithWildcard<'query, Relation, Target>
+{
+ /// Returns a new `WithWildcard`.
+ ///
+ /// # Panics
+ /// This function will panic if:
+ /// - The given component's ID is not a pair ID.
+ /// - `Relation::uid()` is not wildcard and does not equal to the relation of the
+ /// given component's ID
+ /// - `Target::uid()` is not wildcard and does not equal to the target of the given
+ /// component's ID
+ /// - Both `Relation::uid()` and `Target::uid()` are wildcards
+ /// - Neither `Relation::uid()` or `Target::uid()` are wildcards
+ pub fn new(world: &'query World, component_ref: EntityComponentRef<'query>) -> Self
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ let component_id = component_ref.id();
+
+ assert!(component_id.is_pair());
+
+ assert!(
+ Relation::uid() == Wildcard::uid()
+ || component_id.relation() == Relation::uid()
+ );
+
+ assert!(
+ Target::uid() == Wildcard::uid() || component_id.target() == Target::uid()
+ );
+
+ assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid());
+
+ assert!(
+ !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid())
+ );
+
+ WithWildcard {
+ world,
+ component_ref,
+ _pd: PhantomData,
+ }
+ }
+
+ /// Returns the [`Uid`] of the pair.
+ #[must_use]
+ pub fn id(&self) -> Uid
+ {
+ self.component_ref.id()
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the `Data`
+ /// type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is mutably borrowed elsewhere.
+ #[must_use]
+ pub fn get_data<Data>(&self) -> Option<ComponentHandle<'_, Data>>
+ where
+ Data: 'static,
+ {
+ ComponentHandle::<Data>::from_entity_component_ref(&self.component_ref)
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Data>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the `Data`
+ /// type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is borrowed elsewhere.
+ #[must_use]
+ pub fn get_data_mut<Data>(&self) -> Option<ComponentHandleMut<'_, Data>>
+ where
+ Data: 'static,
+ {
+ ComponentHandleMut::<Data>::from_entity_component_ref(
+ &self.component_ref,
+ self.world,
+ )
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Data>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+}
+
+impl<'query, Relation> WithWildcard<'query, Relation, Wildcard>
+where
+ Relation: Component,
+{
+ /// Attempts to retrieve the target as a entity, returning `None` if not found.
+ #[must_use]
+ pub fn get_target_ent(&self) -> Option<EntityHandle<'query>>
+ {
+ let archetype = self
+ .world
+ .data
+ .component_storage
+ .get_entity_archetype(self.component_ref.id().target())?;
+
+ let Some(archetype_entity) =
+ archetype.get_entity_by_id(self.component_ref.id().target())
+ else {
+ unreachable!();
+ };
+
+ Some(EntityHandle::new(archetype, archetype_entity, self.world))
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the
+ /// `Relation` type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is mutably borrowed elsewhere.
+ #[must_use]
+ pub fn get_data_as_relation(&self) -> Option<ComponentHandle<'_, Relation>>
+ {
+ ComponentHandle::<Relation>::from_entity_component_ref(&self.component_ref)
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Relation>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+
+ /// Attempts to get the component data of this pair, returning `None` if the
+ /// `Relation` type is incorrect.
+ ///
+ /// # Panics
+ /// Will panic if the component data is borrowed elsewhere.
+ #[must_use]
+ pub fn get_data_as_relation_mut(&self) -> Option<ComponentHandleMut<'_, Relation>>
+ {
+ ComponentHandleMut::<Relation>::from_entity_component_ref(
+ &self.component_ref,
+ self.world,
+ )
+ .map_or_else(
+ |err| match err {
+ ComponentHandleError::IncorrectType => None,
+ err @ ComponentHandleError::AcquireLockFailed(_) => {
+ panic!(
+ "Creating handle to pair data as component {} failed: {err}",
+ type_name::<Relation>()
+ );
+ }
+ },
+ Some,
+ )
+ }
+}
+
+/// Used to access matching pairs in a entity containing zero or more matching pairs.
+#[derive(Debug)]
+pub struct MultipleWithWildcard<'a, Relation, Target>
+{
+ entity_handle: EntityHandle<'a>,
+ world: &'a World,
+ _pd: PhantomData<(Relation, Target)>,
+}
+
+impl<'query, Relation, Target> MultipleWithWildcard<'query, Relation, Target>
+{
+ /// Returns a new `MultipleWithWildcard`.
+ ///
+ /// # Panics
+ /// This function will panic if:
+ /// - Both `Relation::uid()` and `Target::uid()` are wildcards
+ /// - Neither `Relation::uid()` or `Target::uid()` are wildcards
+ pub fn new(world: &'query World, entity_handle: EntityHandle<'query>) -> Self
+ where
+ Relation: ComponentOrWildcard,
+ Target: ComponentOrWildcard,
+ {
+ assert!(Relation::uid() == Wildcard::uid() || Target::uid() == Wildcard::uid());
+
+ assert!(
+ !(Relation::uid() == Wildcard::uid() && Target::uid() == Wildcard::uid())
+ );
+
+ MultipleWithWildcard {
+ entity_handle,
+ world,
+ _pd: PhantomData,
+ }
+ }
+}
+
+impl<'a, Relation: Component> MultipleWithWildcard<'a, Relation, Wildcard>
+{
+ #[must_use]
+ pub fn get_with_target_id(
+ &self,
+ target_id: Uid,
+ ) -> Option<WithWildcard<'a, Relation, Wildcard>>
+ {
+ Some(WithWildcard {
+ world: self.world,
+ component_ref: self
+ .entity_handle
+ .get_matching_components(
+ Pair::builder()
+ .relation::<Relation>()
+ .target_id(target_id)
+ .build()
+ .id(),
+ )
+ .next()?,
+ _pd: PhantomData,
+ })
+ }
+}
+
+impl<'a, Relation: Component> IntoIterator
+ for MultipleWithWildcard<'a, Relation, Wildcard>
+{
+ type IntoIter = WithWildcardIter<'a, Relation, Wildcard>;
+ type Item = <Self::IntoIter as Iterator>::Item;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ WithWildcardIter {
+ inner: self
+ .entity_handle
+ .get_matching_components(Pair::<Relation, Wildcard>::uid()),
+ world: self.world,
+ _pd: PhantomData,
+ }
+ }
+}
+
+/// Iterator of matching pairs in a entity.
+pub struct WithWildcardIter<'a, Relation, Target>
+{
+ inner: EntityMatchingComponentIter<'a>,
+ world: &'a World,
+ _pd: PhantomData<(Relation, Target)>,
+}
+
+impl<'a, Relation, Target> Iterator for WithWildcardIter<'a, Relation, Target>
+{
+ type Item = WithWildcard<'a, Relation, Target>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let matching_comp = self.inner.next()?;
+
+ Some(WithWildcard {
+ world: self.world,
+ component_ref: matching_comp,
+ _pd: PhantomData,
+ })
+ }
+}
+
+/// Relation denoting a dependency to another entity
+#[derive(Debug, Default, Clone, Copy, Component)]
+pub struct DependsOn;
+
+/// Relation denoting being the child of another entity.
+#[derive(Debug, Default, Clone, Copy, Component)]
+pub struct ChildOf;
+
+#[derive(Debug)]
+pub struct Wildcard(Infallible);
+
+impl Wildcard
+{
+ #[must_use]
+ pub fn uid() -> Uid
+ {
+ Uid::wildcard()
+ }
+}
+
+pub trait ComponentOrWildcard: sealed::Sealed
+{
+ fn uid() -> Uid;
+}
+
+impl<ComponentT: Component> ComponentOrWildcard for ComponentT
+{
+ fn uid() -> Uid
+ {
+ ComponentT::id()
+ }
+}
+
+impl<ComponentT: Component> sealed::Sealed for ComponentT {}
+
+impl ComponentOrWildcard for Wildcard
+{
+ fn uid() -> Uid
+ {
+ Wildcard::uid()
+ }
+}
+
+impl sealed::Sealed for Wildcard {}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct MemberMetadata
+{
+ pub ty: Option<&'static TypeReflection>,
+ pub ty_id: TypeId,
+ pub name: &'static str,
+ pub is_sole: bool,
+}
+
+#[macro_export]
+macro_rules! pair {
+
+ (Wildcard, $target: tt) => {
+ {
+ let pair: $crate::pair::Pair<(), ()> = std::compile_error!(
+ "Use shorthand * instead of Wildcard as relation"
+ );
+
+ pair
+ }
+ };
+
+ ($relation: tt, Wildcard) => {
+ {
+ let pair: $crate::pair::Pair<(), ()> = std::compile_error!(
+ "Use shorthand * instead of Wildcard as target"
+ );
+
+ pair
+ }
+ };
+
+ (={ $relation: tt }, ={ $target: tt }) => {
+ {
+ let pair: $crate::pair::Pair<(), ()> = std::compile_error!(
+ "Cannot use both relation and target as pair data"
+ );
+
+ pair
+ }
+ };
+
+ (={ $relation: expr }, $target: tt) => {
+ $crate::pair!(@inner (=, { $relation }), (, $target))
+ };
+
+ ($relation: tt, ={ $target: expr }) => {
+ $crate::pair!(@inner (,$relation), (=, { $target }))
+ };
+
+ ($relation: tt, $target: tt) => {
+ $crate::pair!(@inner (,$relation), (, $target))
+ };
+
+ (
+ @inner
+ (
+ $(=$($relation_eq: literal)?)?,
+ $relation: tt
+ ),
+ (
+ $(=$($target_eq: literal)?)?,
+ $target: tt
+ )
+ ) => {
+ {
+ #[allow(dead_code)]
+ fn get_type_uid<T: $crate::pair::ComponentOrWildcard>()
+ -> $crate::uid::Uid
+ {
+ T::uid()
+ }
+
+ let builder = $crate::pair::Pair::builder();
+
+ $crate::pair!(
+ @set_member_in_builder, builder, relation, $(=$($relation_eq)?)? $relation
+ );
+
+ $crate::pair!(
+ @set_member_in_builder, builder, target, $(=$($target_eq)?)? $target
+ );
+
+ builder.build()
+ }
+ };
+
+ (@set_member_in_builder, $builder: ident, $member: ident, *) => {
+ $crate::pair::__private::paste! {
+ let $builder = $builder.[<$member _id>]($crate::uid::Uid::wildcard());
+ }
+ };
+
+ (@set_member_in_builder, $builder: ident, $member: ident, $src: ty) => {
+ let $builder = $builder.$member::<$src>();
+ };
+
+ (@set_member_in_builder, $builder: ident, $member: ident, { $src: expr }) => {
+ $crate::pair::__private::paste! {
+ let $builder = $builder.[<$member _id>]($src);
+ }
+ };
+
+ (@set_member_in_builder, $builder: ident, $member: ident, ={ $src: expr }) => {
+ $crate::pair::__private::paste! {
+ let $builder = $builder.[<$member _as_data>]($src);
+ }
+ };
+}
+
+#[doc(hidden)]
+pub mod __private
+{
+ pub use paste::paste;
+}
+
+mod sealed
+{
+ pub trait Sealed {}
+}
+
+#[cfg(test)]
+mod tests
+{
+ use crate::pair::ChildOf;
+ use crate::uid::Uid;
+ use crate::Component;
+
+ #[derive(Debug, Component)]
+ struct Foo;
+
+ #[derive(Debug, Component, PartialEq, Eq)]
+ struct Bar
+ {
+ number: u32,
+ }
+
+ #[test]
+ fn macro_works()
+ {
+ let pair_a = pair!(ChildOf, Foo);
+
+ assert_eq!(pair_a.relation, ChildOf::id());
+ assert_eq!(pair_a.target, Foo::id());
+
+ let pair_b = pair!(ChildOf, *);
+
+ assert_eq!(pair_b.relation, ChildOf::id());
+ assert_eq!(pair_b.target, Uid::wildcard());
+
+ let pair_c = pair!(*, Foo);
+
+ assert_eq!(pair_c.relation, Uid::wildcard());
+ assert_eq!(pair_c.target, Foo::id());
+
+ let pair_d = pair!(Foo, ={ Bar { number: 1234 } });
+
+ assert_eq!(pair_d.relation, Foo::id());
+ assert_eq!(pair_d.target, Bar { number: 1234 });
+
+ let pair_e = pair!(={ Bar { number: 789} }, Foo);
+
+ assert_eq!(pair_e.relation, Bar { number: 789 });
+ assert_eq!(pair_e.target, Foo::id());
+
+ let pair_f = pair!(ChildOf, { Foo::id() });
+
+ assert_eq!(pair_f.relation, ChildOf::id());
+ assert_eq!(pair_f.target, Foo::id());
+
+ let pair_g = pair!({ Bar::id() }, Foo);
+
+ assert_eq!(pair_g.relation, Bar::id());
+ assert_eq!(pair_g.target, Foo::id());
+ }
+}
diff --git a/engine-ecs/src/phase.rs b/engine-ecs/src/phase.rs
new file mode 100644
index 0000000..78428c4
--- /dev/null
+++ b/engine-ecs/src/phase.rs
@@ -0,0 +1,73 @@
+use crate::{declare_entity, Component, World};
+
+#[derive(Debug, Default, Clone, Copy, Component)]
+pub struct Phase;
+
+declare_entity! {
+/// Initialization phase that only occurs the first tick, before [`START`].
+/// Should not be used for game logic
+pub INIT: (Phase,);
+
+/// Phase that only occurs the first tick, after [`INIT`].
+pub START: (Phase,);
+
+/// Phase that occurs every tick, before [`UPDATE`].
+pub PRE_UPDATE: (Phase,);
+
+/// Phase that occurs every tick, after [`PRE_UPDATE`].
+pub UPDATE: (Phase,);
+
+/// Phase that occurs every tick, after [`UPDATE`].
+pub POST_UPDATE: (Phase,);
+}
+
+pub(crate) fn spawn_standard_phases(world: &mut World)
+{
+ world.spawn_declared_entity(&INIT);
+ world.spawn_declared_entity(&START);
+ world.spawn_declared_entity(&PRE_UPDATE);
+ world.spawn_declared_entity(&UPDATE);
+ world.spawn_declared_entity(&POST_UPDATE);
+}
+
+#[derive(Debug, Component)]
+pub(crate) struct HasSystem;
+
+#[cfg(debug_assertions)]
+pub(crate) fn check_parents(
+ phase_query: crate::query::Query<(
+ crate::pair::Pair<crate::pair::ChildOf, crate::pair::Wildcard>,
+ Option<&crate::entity::Name>,
+ crate::query::term::With<Phase>,
+ )>,
+)
+{
+ for (child_of, ent_name) in &phase_query {
+ let Some(parent_ent) = child_of.get_target_ent() else {
+ tracing::warn!(
+ phase_entity = ent_name
+ .as_ref()
+ .map(|ent_name| ent_name.name.as_ref())
+ .unwrap_or("<unnamed>"),
+ "Parent entity of phase entity does not exist"
+ );
+ continue;
+ };
+
+ if !parent_ent.has_component(Phase::id()) {
+ let parent_ent_name = parent_ent.get::<crate::entity::Name>();
+
+ tracing::warn!(
+ phase_entity = ent_name
+ .as_ref()
+ .map(|ent_name| ent_name.name.as_ref())
+ .unwrap_or("<unnamed>"),
+ parent_entity = parent_ent_name
+ .as_ref()
+ .map(|parent_ent_name| parent_ent_name.name.as_ref())
+ .unwrap_or("<unnamed>"),
+ "Parent entity of phase entity is not a phase (missing Phase component)"
+ );
+ }
+ }
+}
diff --git a/engine-ecs/src/query.rs b/engine-ecs/src/query.rs
new file mode 100644
index 0000000..c323092
--- /dev/null
+++ b/engine-ecs/src/query.rs
@@ -0,0 +1,603 @@
+use std::any::type_name;
+use std::marker::PhantomData;
+
+use paste::paste;
+use seq_macro::seq;
+use util_macros::sub;
+
+use crate::component::{
+ Component,
+ Handle as ComponentHandle,
+ HandleMut as ComponentHandleMut,
+};
+use crate::entity::Handle as EntityHandle;
+use crate::query::flexible::{Iter as FlexibleQueryIter, Query as FlexibleQuery};
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::tuple::{Concat, Tuple};
+use crate::uid::Uid;
+use crate::util::array_vec::ArrayVec;
+use crate::util::Array;
+use crate::World;
+
+mod reexports
+{
+ pub use crate::component::storage::{
+ FoundByTraversal,
+ SearchTerms as Terms,
+ TermMetadata,
+ Traversal,
+ TraversalKind,
+ TraversalResult,
+ };
+}
+
+pub use reexports::*;
+
+pub mod flexible;
+pub mod term;
+
+// A term tuple type can have a maximum of 17 elements
+pub const MAX_TERM_CNT: usize = 17;
+
+#[derive(Debug)]
+pub struct Query<'world, TermsT>
+{
+ inner: FlexibleQuery<'world, MAX_TERM_CNT>,
+ _pd: PhantomData<TermsT>,
+}
+
+impl<'world, TermsT> Query<'world, TermsT>
+{
+ /// Iterates over the entities matching this query, the iterator item being the entity
+ /// components.
+ #[must_use]
+ pub fn iter<'this>(&'this self) -> Iter<'this, TermsT>
+ where
+ TermsT: TermTuple<'this>,
+ 'world: 'this,
+ {
+ tracing::trace!("Searching for {}", std::any::type_name::<TermsT>());
+
+ Iter {
+ world: self.inner.world(),
+ inner: self.inner.iter(),
+ comps_pd: PhantomData,
+ }
+ }
+
+ /// Iterates over the entities matching this query, the iterator item being the entity
+ /// [`Uid`] and the matching entity components.
+ #[must_use]
+ pub fn iter_with_euids<'this>(&'this self) -> ComponentAndEuidIter<'this, TermsT>
+ where
+ TermsT: TermTuple<'this>,
+ 'world: 'this,
+ {
+ tracing::trace!("Searching for {}", std::any::type_name::<TermsT>());
+
+ ComponentAndEuidIter {
+ world: self.inner.world(),
+ inner: self.inner.iter(),
+ comps_pd: PhantomData,
+ }
+ }
+
+ /// Returns the UID of the entity at the given query iteration index.
+ #[must_use]
+ pub fn get_entity_uid<'this>(&'this self, entity_index: usize) -> Option<Uid>
+ where
+ TermsT: TermTuple<'this>,
+ {
+ Some(self.inner.iter().nth(entity_index)?.uid())
+ }
+
+ /// Returns a new `Query` created from a [`FlexibleQuery`].
+ ///
+ /// # Important notes
+ /// The terms in `TermsT` must be compatible with the terms
+ /// in the given [`FlexibleQuery`], otherwise any method call or iterating might
+ /// panic.
+ #[must_use]
+ pub fn from_flexible_query(
+ flexible_query: FlexibleQuery<'world, MAX_TERM_CNT>,
+ ) -> Self
+ {
+ // TODO: Check compatability of terms
+
+ Self {
+ inner: flexible_query,
+ _pd: PhantomData,
+ }
+ }
+
+ pub fn into_flexible_query(self) -> FlexibleQuery<'world, MAX_TERM_CNT>
+ {
+ self.inner
+ }
+
+ pub(crate) fn new(world: &'world World) -> Self
+ where
+ TermsT: TermTuple<'world>,
+ {
+ let mut terms_builder = Terms::builder();
+
+ TermsT::apply_terms_to_builder(&mut terms_builder, 0);
+
+ Self {
+ inner: world.flexible_query(terms_builder.build()),
+ _pd: PhantomData,
+ }
+ }
+}
+
+impl<'query, 'world, TermsT> IntoIterator for &'query Query<'world, TermsT>
+where
+ TermsT: TermTuple<'query>,
+ 'world: 'query,
+{
+ type IntoIter = Iter<'query, TermsT>;
+ type Item = TermsT::Fields;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ self.iter()
+ }
+}
+
+impl<'world, TermsT> SystemParam<'world> for Query<'world, TermsT>
+where
+ TermsT: TermTuple<'world>,
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ Self::new(world)
+ }
+}
+
+impl<const MAX_TERM_CNT: usize> Terms<MAX_TERM_CNT>
+{
+ pub fn builder() -> TermsBuilder<MAX_TERM_CNT>
+ {
+ TermsBuilder::default()
+ }
+}
+
+#[derive(Debug, Default)]
+#[must_use]
+pub struct TermsBuilder<const TERM_CAP: usize>
+{
+ present: ArrayVec<Uid, TERM_CAP>,
+ absent: ArrayVec<Uid, TERM_CAP>,
+ traverse: ArrayVec<Traversal<TERM_CAP>, TERM_CAP>,
+}
+
+#[allow(clippy::return_self_not_must_use)]
+pub trait TermsBuilderInterface<const TERM_CAP: usize>
+{
+ fn present(self, ids: impl Array<Uid>) -> Self;
+
+ fn absent(self, ids: impl Array<Uid>) -> Self;
+
+ fn traverse(self, traversals: impl Array<Traversal<TERM_CAP>> + Clone) -> Self;
+}
+
+macro_rules! impl_terms_builder {
+ ($($impl_content: tt)*) => {
+ impl<const TERM_CAP: usize>
+ TermsBuilderInterface<TERM_CAP> for TermsBuilder<TERM_CAP>
+ {
+ $($impl_content)*
+ }
+
+ impl<const TERM_CAP: usize>
+ TermsBuilderInterface<TERM_CAP> for &mut TermsBuilder<TERM_CAP>
+ {
+ $($impl_content)*
+ }
+ };
+}
+
+impl_terms_builder! {
+ #[allow(unused_mut)]
+ fn present(mut self, mut ids: impl Array<Uid>) -> Self
+ {
+ if !ids.as_ref().is_sorted() {
+ ids.as_mut().sort();
+ }
+
+ if self.present.is_empty() {
+ self.present.extend(ids);
+ return self;
+ }
+
+ let mut id_iter = ids.into_iter();
+
+ while let Some(id) = id_iter.next() {
+ let insert_index = self.present
+ .partition_point(|other_id| *other_id <= id);
+
+ if insert_index == self.present.len() {
+ self.present.extend([id].into_iter().chain(id_iter));
+
+ return self;
+ }
+
+ self.present
+ .insert(insert_index, id);
+
+ }
+
+ self
+ }
+
+ #[allow(unused_mut)]
+ fn absent(mut self, mut ids: impl Array<Uid>) -> Self
+ {
+ if !ids.as_ref().is_sorted() {
+ ids.as_mut().sort();
+ }
+
+ if self.absent.is_empty() {
+ self.absent.extend(ids);
+ return self;
+ }
+
+ let mut id_iter = ids.into_iter();
+
+ while let Some(id) = id_iter.next() {
+ let insert_index = self.absent
+ .partition_point(|other_id| *other_id <= id);
+
+ if insert_index == self.absent.len() {
+ self.absent.extend([id].into_iter().chain(id_iter));
+
+ return self;
+ }
+
+ self.absent
+ .insert(insert_index, id);
+
+ }
+
+ self
+ }
+
+ #[allow(unused_mut)]
+ fn traverse(mut self, traversals: impl Array<Traversal<TERM_CAP>> + Clone) -> Self
+ {
+ self.traverse.extend(traversals.clone());
+
+ self
+ }
+}
+
+impl<const MAX_TERM_CNT: usize> TermsBuilder<MAX_TERM_CNT>
+{
+ #[must_use]
+ pub fn build(self) -> Terms<MAX_TERM_CNT>
+ {
+ debug_assert!(self.present.is_sorted());
+ debug_assert!(self.absent.is_sorted());
+
+ Terms {
+ present: self.present,
+ absent: self.absent,
+ traverse: self.traverse,
+ }
+ }
+}
+
+pub trait Term<'query>
+{
+ type Fields: Tuple;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ term_metadata: TermMetadata,
+ );
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ term_metadata: TermMetadata,
+ ) -> Self::Fields;
+}
+
+impl<'query, ComponentT: Component> Term<'query> for &ComponentT
+{
+ type Fields = (ComponentHandle<'query, ComponentT>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.present([ComponentT::id()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ assert!(!ComponentT::id().is_pair());
+
+ let Some(component) = search_result
+ .entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()
+ else {
+ panic!(
+ concat!(
+ "Component {} was not found in entity {}. There ",
+ "is most likely a bug in the entity querying"
+ ),
+ type_name::<ComponentT>(),
+ search_result.entity_handle.uid()
+ );
+ };
+
+ let component =
+ match ComponentHandle::<ComponentT>::from_entity_component_ref(&component) {
+ Ok(component) => component,
+ Err(err) => {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ };
+
+ (component,)
+ }
+}
+
+impl<'query, ComponentT: Component> Term<'query> for &mut ComponentT
+{
+ type Fields = (ComponentHandleMut<'query, ComponentT>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.present([ComponentT::id()]);
+ }
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ assert!(!ComponentT::id().is_pair());
+
+ let Some(component) = search_result
+ .entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()
+ else {
+ panic!(
+ concat!(
+ "Component {} was not found in entity {}. There ",
+ "is most likely a bug in the entity querying"
+ ),
+ type_name::<ComponentT>(),
+ search_result.entity_handle.uid()
+ );
+ };
+
+ let component = match ComponentHandleMut::<ComponentT>::from_entity_component_ref(
+ &component, world,
+ ) {
+ Ok(component) => component,
+ Err(err) => {
+ panic!(
+ "Creating mut handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ };
+
+ (component,)
+ }
+}
+
+impl<'query, TermT, FieldAcc> TermFieldSource<'query, FieldAcc> for TermT
+where
+ TermT: Term<'query, Fields: Concat<FieldAcc>>,
+ FieldAcc: Tuple,
+{
+ type NewFieldAcc = <TermT::Fields as Concat<FieldAcc>>::Output;
+
+ fn collect_field(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ term_metadata: TermMetadata,
+ fields: FieldAcc,
+ ) -> Self::NewFieldAcc
+ {
+ <TermT::Fields as Concat<FieldAcc>>::concat(
+ Self::fields(world, search_result, term_metadata),
+ fields,
+ )
+ }
+}
+
+pub trait TermTuple<'query>
+{
+ type Fields: Tuple;
+
+ fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ term_level: u32,
+ );
+
+ fn get_fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ term_level: u32,
+ ) -> Self::Fields;
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct SearchResult<'query, 'temp>
+{
+ pub entity_handle: EntityHandle<'query>,
+ pub traversal_results: &'temp [TraversalResult],
+}
+
+pub struct Iter<'query, TermsT>
+where
+ TermsT: TermTuple<'query>,
+{
+ world: &'query World,
+ inner: FlexibleQueryIter<'query, MAX_TERM_CNT>,
+ comps_pd: PhantomData<TermsT>,
+}
+
+impl<'query, TermsT> Iterator for Iter<'query, TermsT>
+where
+ TermsT: TermTuple<'query>,
+{
+ type Item = TermsT::Fields;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let entity_handle = self.inner.next()?;
+
+ Some(TermsT::get_fields(
+ self.world,
+ &SearchResult {
+ entity_handle,
+ traversal_results: self.inner.traversal_results().unwrap(),
+ },
+ 0,
+ ))
+ }
+}
+
+pub struct ComponentAndEuidIter<'query, TermsT>
+where
+ TermsT: TermTuple<'query>,
+{
+ world: &'query World,
+ inner: FlexibleQueryIter<'query, MAX_TERM_CNT>,
+ comps_pd: PhantomData<TermsT>,
+}
+
+impl<'query, TermsT> Iterator for ComponentAndEuidIter<'query, TermsT>
+where
+ TermsT: TermTuple<'query>,
+{
+ type Item = (Uid, TermsT::Fields);
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let entity_handle = self.inner.next()?;
+
+ Some((
+ entity_handle.uid(),
+ TermsT::get_fields(
+ self.world,
+ &SearchResult {
+ entity_handle,
+ traversal_results: self.inner.traversal_results().unwrap(),
+ },
+ 0,
+ ),
+ ))
+ }
+}
+
+pub trait TermFieldSource<'query, FieldAcc>
+{
+ type NewFieldAcc;
+
+ fn collect_field(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ term_metadata: TermMetadata,
+ fields: FieldAcc,
+ ) -> Self::NewFieldAcc;
+}
+
+macro_rules! term_field_source_new_field_acc {
+ (overflow) => {
+ ()
+ };
+
+ ($index: tt) => {
+ paste! {
+ [<Term $index>]::NewFieldAcc
+ }
+ };
+}
+
+macro_rules! gen_term_tuple_impls {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'query, #(Term~I,)*> TermTuple<'query> for (#(Term~I,)*)
+ where
+ #(
+ Term~I: Term<'query> + TermFieldSource<
+ 'query,
+ sub!(I - 1, term_field_source_new_field_acc),
+ NewFieldAcc: Tuple
+ >,
+ )*
+ {
+ type Fields = sub!($c - 1, term_field_source_new_field_acc);
+
+ #[allow(unused)]
+ fn apply_terms_to_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ term_level: u32,
+ )
+ {
+ #(
+ Term~I::apply_to_terms_builder(
+ terms_builder,
+ TermMetadata {
+ level: term_level,
+ index: I
+ },
+ );
+ )*
+ }
+
+ #[allow(unused)]
+ fn get_fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ term_level: u32,
+ ) -> Self::Fields
+ {
+ let fields = ();
+
+ #(
+ let fields = Term~I::collect_field(
+ world,
+ search_result,
+ TermMetadata {
+ level: term_level,
+ index: I
+ },
+ fields
+ );
+ )*
+
+ fields
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..17 {
+ gen_term_tuple_impls!(C);
+});
diff --git a/engine-ecs/src/query/flexible.rs b/engine-ecs/src/query/flexible.rs
new file mode 100644
index 0000000..0198fb4
--- /dev/null
+++ b/engine-ecs/src/query/flexible.rs
@@ -0,0 +1,101 @@
+//! Low-level querying.
+use crate::component::storage::archetype::{Archetype, EntityIter};
+use crate::component::storage::{ArchetypeRefIter, TraversalResult};
+use crate::entity::Handle as EntityHandle;
+use crate::query::Terms;
+use crate::util::array_vec::ArrayVec;
+use crate::World;
+
+/// Low-level entity query structure.
+#[derive(Debug)]
+pub struct Query<'world, const TERM_CAP: usize>
+{
+ world: &'world World,
+ terms: Terms<TERM_CAP>,
+}
+
+impl<'world, const TERM_CAP: usize> Query<'world, TERM_CAP>
+{
+ /// Iterates over the entities matching this query.
+ #[must_use]
+ pub fn iter<'this>(&'this self) -> Iter<'this, TERM_CAP>
+ where
+ 'world: 'this,
+ {
+ Iter {
+ archetype_iter: self
+ .world
+ .data
+ .component_storage
+ .search_archetypes(&self.terms),
+ current: None,
+ world: self.world,
+ }
+ }
+
+ #[must_use]
+ pub fn world(&self) -> &'world World
+ {
+ self.world
+ }
+
+ pub(crate) fn new(world: &'world World, terms: Terms<TERM_CAP>) -> Self
+ {
+ Self { world, terms }
+ }
+}
+
+impl<'query, 'world, const TERM_CAP: usize> IntoIterator
+ for &'query Query<'world, TERM_CAP>
+where
+ 'world: 'query,
+{
+ type IntoIter = Iter<'query, TERM_CAP>;
+ type Item = EntityHandle<'query>;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ self.iter()
+ }
+}
+
+pub struct Iter<'query, const TERM_CAP: usize>
+{
+ archetype_iter: ArchetypeRefIter<'query, 'query, TERM_CAP>,
+ current: Option<(
+ &'query Archetype,
+ ArrayVec<TraversalResult, TERM_CAP>,
+ EntityIter<'query>,
+ )>,
+ world: &'query World,
+}
+
+impl<'query, const TERM_CAP: usize> Iter<'query, TERM_CAP>
+{
+ pub fn traversal_results(&self) -> Option<&[TraversalResult]>
+ {
+ self.current
+ .as_ref()
+ .map(|(_, traversal_results, _)| traversal_results.as_ref())
+ }
+}
+
+impl<'query, const TERM_CAP: usize> Iterator for Iter<'query, TERM_CAP>
+{
+ type Item = EntityHandle<'query>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ if let Some((archetype, _, entity_iter)) = self.current.as_mut() {
+ if let Some(entity) = entity_iter.next() {
+ return Some(EntityHandle::new(archetype, entity, self.world));
+ }
+ }
+
+ let (archetype, traversal_results) = self.archetype_iter.next()?;
+
+ self.current = Some((archetype, traversal_results, archetype.entities()));
+
+ self.next()
+ }
+}
diff --git a/engine-ecs/src/query/term.rs b/engine-ecs/src/query/term.rs
new file mode 100644
index 0000000..453a750
--- /dev/null
+++ b/engine-ecs/src/query/term.rs
@@ -0,0 +1,273 @@
+use std::any::type_name;
+use std::marker::PhantomData;
+
+use crate::component::{
+ Component,
+ Handle as ComponentHandle,
+ HandleMut as ComponentHandleMut,
+};
+use crate::query::{
+ FoundByTraversal,
+ SearchResult,
+ Term,
+ TermMetadata,
+ TermTuple,
+ TermsBuilder,
+ TermsBuilderInterface,
+ Traversal,
+ TraversalKind,
+};
+use crate::uid::With as WithUid;
+use crate::World;
+
+pub struct With<WithUidT>
+where
+ WithUidT: WithUid,
+{
+ _pd: PhantomData<WithUidT>,
+}
+
+impl<'query, WithUidT> Term<'query> for With<WithUidT>
+where
+ WithUidT: WithUid,
+{
+ type Fields = ();
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.present([WithUidT::uid()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ _search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ ()
+ }
+}
+
+pub struct Without<WithUidT>
+where
+ WithUidT: WithUid,
+{
+ _pd: PhantomData<WithUidT>,
+}
+
+impl<'query, WithUidT> Term<'query> for Without<WithUidT>
+where
+ WithUidT: WithUid,
+{
+ type Fields = ();
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.absent([WithUidT::uid()]);
+ }
+
+ fn fields(
+ _world: &'query World,
+ _search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ ()
+ }
+}
+
+impl<'query, ComponentT: Component> Term<'query> for Option<&ComponentT>
+{
+ type Fields = (Option<ComponentHandle<'query, ComponentT>>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ }
+
+ fn fields(
+ _world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ let component = (|| {
+ let comp_ref = &search_result
+ .entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?;
+
+ match ComponentHandle::<ComponentT>::from_entity_component_ref(comp_ref) {
+ Ok(component) => Some(component),
+ Err(err) => {
+ panic!(
+ "Creating handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ }
+ })();
+
+ (component,)
+ }
+}
+
+impl<'query, ComponentT: Component> Term<'query> for Option<&mut ComponentT>
+{
+ type Fields = (Option<ComponentHandleMut<'query, ComponentT>>,);
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ _terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ _term_metadata: TermMetadata,
+ )
+ {
+ }
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ _term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ let component = (|| {
+ let comp_ref = &search_result
+ .entity_handle
+ .get_matching_components(ComponentT::id())
+ .next()?;
+
+ match ComponentHandleMut::<ComponentT>::from_entity_component_ref(
+ comp_ref, world,
+ ) {
+ Ok(component) => Some(component),
+ Err(err) => {
+ panic!(
+ "Creating mut handle to component {} failed: {err}",
+ type_name::<ComponentT>()
+ );
+ }
+ }
+ })();
+
+ (component,)
+ }
+}
+
+pub struct Traverse<TermsT, Kind, Relation>
+{
+ _pd: PhantomData<(TermsT, Kind, Relation)>,
+}
+
+impl<'query, TermsT, Kind, Relation> Term<'query> for Traverse<TermsT, Kind, Relation>
+where
+ TermsT: TermTuple<'query>,
+ Kind: TraversalKindType,
+ Relation: Component,
+{
+ type Fields = TermsT::Fields;
+
+ fn apply_to_terms_builder<const MAX_TERM_CNT: usize>(
+ terms_builder: &mut TermsBuilder<MAX_TERM_CNT>,
+ term_metadata: TermMetadata,
+ )
+ {
+ terms_builder.traverse([Traversal {
+ kind: Kind::traversal_kind(),
+ relation: Relation::id(),
+ terms: |traversal| {
+ let mut terms_builder = TermsBuilder::default();
+
+ TermsT::apply_terms_to_builder(
+ &mut terms_builder,
+ traversal
+ .term_metadata
+ .level
+ .checked_add(1)
+ .expect("Too large term level. Would overflow"),
+ );
+
+ terms_builder.build()
+ },
+ term_metadata,
+ }]);
+ }
+
+ fn fields(
+ world: &'query World,
+ search_result: &SearchResult<'query, '_>,
+ term_metadata: TermMetadata,
+ ) -> Self::Fields
+ {
+ let Some(traversal_result) = search_result
+ .traversal_results
+ .iter()
+ .find(|traversal_result| traversal_result.term_metadata == term_metadata)
+ else {
+ unreachable!();
+ };
+
+ let ent = match traversal_result.found {
+ FoundByTraversal::SelfEntity => search_result.entity_handle.clone(),
+ FoundByTraversal::OtherEntity(found_ent_id) => {
+ let Some(found_ent) = world.get_entity(found_ent_id) else {
+ unreachable!();
+ };
+
+ found_ent
+ }
+ };
+
+ TermsT::get_fields(
+ world,
+ &SearchResult {
+ entity_handle: ent,
+ traversal_results: search_result.traversal_results,
+ },
+ term_metadata
+ .level
+ .checked_add(1)
+ .expect("Too large term level. Would overflow"),
+ )
+ }
+}
+
+pub trait TraversalKindType: sealed::Sealed
+{
+ fn traversal_kind() -> TraversalKind;
+}
+
+pub struct TraverseUp;
+
+impl TraversalKindType for TraverseUp
+{
+ fn traversal_kind() -> TraversalKind
+ {
+ TraversalKind::Up
+ }
+}
+
+impl sealed::Sealed for TraverseUp {}
+
+pub struct TraverseSelfOrUp;
+
+impl TraversalKindType for TraverseSelfOrUp
+{
+ fn traversal_kind() -> TraversalKind
+ {
+ TraversalKind::SelfOrUp
+ }
+}
+
+impl sealed::Sealed for TraverseSelfOrUp {}
+
+mod sealed
+{
+ pub trait Sealed {}
+}
diff --git a/engine-ecs/src/sole.rs b/engine-ecs/src/sole.rs
new file mode 100644
index 0000000..1e287d3
--- /dev/null
+++ b/engine-ecs/src/sole.rs
@@ -0,0 +1,107 @@
+use std::any::{type_name, Any};
+use std::fmt::Debug;
+
+use crate::component::{
+ HandleMut as ComponentHandleMut,
+ IntoParts as IntoComponentParts,
+};
+use crate::system::{Metadata as SystemMetadata, Param as SystemParam};
+use crate::uid::Uid;
+use crate::World;
+
+/// A component which has a single instance in the [`World`].
+pub trait Sole: Any + IntoComponentParts
+{
+ fn id() -> Uid
+ where
+ Self: Sized;
+
+ fn type_reflection() -> Option<&'static crate::reflection::Type>
+ where
+ Self: Sized;
+
+ /// Returns the name of this component.
+ fn name(&self) -> &'static str;
+}
+
+impl dyn Sole
+{
+ pub fn downcast_mut<Real: 'static>(&mut self) -> Option<&mut Real>
+ {
+ (self as &mut dyn Any).downcast_mut()
+ }
+
+ pub fn downcast_ref<Real: 'static>(&self) -> Option<&Real>
+ {
+ (self as &dyn Any).downcast_ref()
+ }
+
+ pub fn is<Other: 'static>(&self) -> bool
+ {
+ (self as &dyn Any).is::<Other>()
+ }
+}
+
+impl Debug for dyn Sole
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter.debug_struct("Sole").finish_non_exhaustive()
+ }
+}
+
+/// Holds a reference to a singleton component.
+#[derive(Debug)]
+pub struct Single<'world, SoleT: Sole>
+{
+ sole: Option<ComponentHandleMut<'world, SoleT>>,
+}
+
+impl<'world, SoleT> Single<'world, SoleT>
+where
+ SoleT: Sole,
+{
+ /// Returns a reference to `SoleT` if it exists. If not, returns `Err`.
+ pub fn get(&self) -> Result<&SoleT, DoesNotExistError>
+ {
+ self.sole
+ .as_deref()
+ .ok_or_else(|| DoesNotExistError { name: type_name::<SoleT>() })
+ }
+
+ /// Returns a mutable reference to `SoleT` if it exists. If not, returns `Err`.
+ pub fn get_mut(&mut self) -> Result<&mut SoleT, DoesNotExistError>
+ {
+ self.sole
+ .as_deref_mut()
+ .ok_or_else(|| DoesNotExistError { name: type_name::<SoleT>() })
+ }
+
+ pub(crate) fn new(world: &'world World) -> Self
+ {
+ let sole = world
+ .get_entity(SoleT::id())
+ .and_then(|ent| ent.get_with_id_mut::<SoleT>(SoleT::id()));
+
+ Self { sole }
+ }
+}
+
+impl<'world, SoleT> SystemParam<'world> for Single<'world, SoleT>
+where
+ SoleT: Sole,
+{
+ type Input = ();
+
+ fn new(world: &'world World, _system_metadata: &SystemMetadata) -> Self
+ {
+ Self::new(world)
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Sole {name} does not exist")]
+pub struct DoesNotExistError
+{
+ pub name: &'static str,
+}
diff --git a/engine-ecs/src/system.rs b/engine-ecs/src/system.rs
new file mode 100644
index 0000000..5395b14
--- /dev/null
+++ b/engine-ecs/src/system.rs
@@ -0,0 +1,170 @@
+use std::fmt::Debug;
+
+use seq_macro::seq;
+
+use crate::error::Error;
+use crate::event::Emitted as EmittedEvent;
+use crate::uid::Uid;
+use crate::{Component, World};
+
+pub mod initializable;
+pub mod observer;
+pub mod stateful;
+
+/// Metadata of a system.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Metadata
+{
+ pub ent_id: Uid,
+}
+
+pub trait System<'world, Impl>: 'static
+{
+ type Callbacks: Callbacks;
+
+ fn finish(self) -> (TypeErased, Self::Callbacks);
+}
+
+macro_rules! impl_system {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, Func, Ret, #(TParam~I,)*> System<'world, fn(#(TParam~I,)*) -> Ret>
+ for Func
+ where
+ Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world, Input = ()>,)*
+ {
+ type Callbacks = NoCallbacks;
+
+ fn finish(self) -> (TypeErased, Self::Callbacks)
+ {
+ #![allow(unused)]
+
+ crate::util::const_assert!(
+ size_of::<Func>() == 0,
+ "System function is not zero-sized (not function pointer)"
+ );
+
+ let type_erased = TypeErased {
+ run: |world, metadata, _| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ let func = unsafe { std::mem::zeroed::<Func>() };
+
+ func(#({
+ TParam~I::new(world, &metadata)
+ },)*).into_result()
+ },
+ name: std::any::type_name::<Self>()
+ };
+
+ (type_erased, NoCallbacks)
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..16 {
+ impl_system!(C);
+});
+
+pub trait Into<'world, Impl>
+{
+ type System;
+
+ fn into_system(self) -> Self::System;
+}
+
+pub struct TypeErased
+{
+ pub run: TypeErasedRunFn,
+ pub name: &'static str,
+}
+
+impl TypeErased
+{
+ /// Runs the system.
+ ///
+ /// # Safety
+ /// `world_data` must live at least as long as the [`World`] the system belongs to.
+ pub unsafe fn run(
+ &self,
+ world: &World,
+ metadata: Metadata,
+ evt: Option<EmittedEvent<'_>>,
+ ) -> Result<(), Error>
+ {
+ (self.run)(world, metadata, evt)
+ }
+
+ pub fn name(&self) -> &'static str
+ {
+ self.name
+ }
+}
+
+impl Debug for TypeErased
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter.debug_struct("TypeErased").finish_non_exhaustive()
+ }
+}
+
+pub trait ReturnValue
+{
+ fn into_result(self) -> Result<(), Error>;
+}
+
+impl ReturnValue for ()
+{
+ fn into_result(self) -> Result<(), Error>
+ {
+ Ok(())
+ }
+}
+
+impl ReturnValue for Result<(), Error>
+{
+ fn into_result(self) -> Result<(), Error>
+ {
+ self
+ }
+}
+
+/// A parameter to a [`System`].
+pub trait Param<'world>
+{
+ type Input;
+
+ fn new(world: &'world World, system_metadata: &Metadata) -> Self;
+}
+
+/// A type which can be used as input to a [`System`].
+pub trait Input: 'static {}
+
+pub trait Callbacks
+{
+ fn on_created(&mut self, world: &mut World, metadata: Metadata);
+}
+
+pub struct NoCallbacks;
+
+impl Callbacks for NoCallbacks
+{
+ fn on_created(&mut self, _world: &mut World, _metadata: Metadata) {}
+}
+
+#[derive(Debug, Component)]
+pub(crate) struct SystemComponent
+{
+ pub(crate) system: TypeErased,
+}
+
+type TypeErasedRunFn =
+ fn(&World, Metadata, Option<EmittedEvent<'_>>) -> Result<(), Error>;
diff --git a/engine-ecs/src/system/initializable.rs b/engine-ecs/src/system/initializable.rs
new file mode 100644
index 0000000..fa925e0
--- /dev/null
+++ b/engine-ecs/src/system/initializable.rs
@@ -0,0 +1,131 @@
+use std::marker::PhantomData;
+
+use seq_macro::seq;
+
+use crate::system::{Input, Param as SystemParam};
+use crate::tuple::{Reduce as TupleReduce, ReduceElement as TupleReduceElement, Tuple};
+
+/// A initializable system.
+pub trait Initializable<'world, Impl>
+{
+ type Inputs;
+
+ #[must_use]
+ fn initialize(self, inputs: Self::Inputs) -> Self;
+}
+
+pub trait Param<'world, SystemT>: SystemParam<'world>
+{
+ fn initialize(system: &mut SystemT, input: Self::Input);
+}
+
+pub struct ParamTupleFilter<'world, SystemT>
+{
+ _pd: PhantomData<(&'world (), SystemT)>,
+}
+
+impl<'world, SystemT, ParamT, Accumulator>
+ TupleReduceElement<Accumulator, ParamTupleFilter<'world, SystemT>> for ParamT
+where
+ ParamT: SystemParam<
+ 'world,
+ Input: AppendInitializableParam<'world, Accumulator, ParamT, SystemT>,
+ >,
+ Accumulator: Tuple,
+{
+ type Return = <ParamT::Input as AppendInitializableParam<
+ 'world,
+ Accumulator,
+ ParamT,
+ SystemT,
+ >>::Return;
+}
+
+pub trait AppendInitializableParam<'world, Accumulator, ParamT, SystemT>
+{
+ type Return;
+}
+
+impl<'world, InputT, ParamT, Accumulator, SystemT>
+ AppendInitializableParam<'world, Accumulator, ParamT, SystemT> for InputT
+where
+ InputT: Input,
+ Accumulator: Tuple,
+ ParamT: Param<'world, SystemT>,
+{
+ type Return = Accumulator::WithElementAtEnd<ParamT>;
+}
+
+impl<ParamT, Accumulator, SystemT>
+ AppendInitializableParam<'_, Accumulator, ParamT, SystemT> for ()
+where
+ Accumulator: Tuple,
+{
+ type Return = Accumulator;
+}
+
+pub trait ParamTuple<'world, SystemT>
+{
+ type Inputs;
+
+ fn initialize_all(system: &mut SystemT, inputs: Self::Inputs);
+}
+
+macro_rules! impl_initializable_param_tuple {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, SystemT, #(Param~I,)*> ParamTuple<'world, SystemT>
+ for (#(Param~I,)*)
+ where
+ #(Param~I: Param<'world, SystemT>,)*
+ {
+ type Inputs = (#(Param~I::Input,)*);
+
+ fn initialize_all(
+ system: &mut SystemT,
+ inputs: Self::Inputs,
+ ) {
+ #(
+ <Param~I as Param<'world, SystemT>>::initialize(
+ system,
+ inputs.I
+ );
+ )*
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 1..16 {
+ impl_initializable_param_tuple!(C);
+});
+
+impl<SystemT> ParamTuple<'_, SystemT> for ()
+{
+ type Inputs = ();
+
+ fn initialize_all(_system: &mut SystemT, _inputs: Self::Inputs) {}
+}
+
+/// A tuple of system parameters that may or may not be initializable.
+pub trait MaybeInitializableParamTuple<'world, SystemT>
+{
+ /// A tuple of the inputs of the initializable system parameters in this tuple.
+ type Inputs;
+
+ fn init_initializable(system: &mut SystemT, inputs: Self::Inputs);
+}
+
+impl<'world, SystemT, Params> MaybeInitializableParamTuple<'world, SystemT> for Params
+where
+ Params:
+ TupleReduce<ParamTupleFilter<'world, SystemT>, Out: ParamTuple<'world, SystemT>>,
+{
+ type Inputs = <Params::Out as ParamTuple<'world, SystemT>>::Inputs;
+
+ fn init_initializable(system: &mut SystemT, inputs: Self::Inputs)
+ {
+ Params::Out::initialize_all(system, inputs);
+ }
+}
diff --git a/engine-ecs/src/system/observer.rs b/engine-ecs/src/system/observer.rs
new file mode 100644
index 0000000..e5345cd
--- /dev/null
+++ b/engine-ecs/src/system/observer.rs
@@ -0,0 +1,219 @@
+use std::any::type_name;
+use std::fmt::Debug;
+use std::marker::PhantomData;
+use std::mem::transmute;
+use std::slice::Iter as SliceIter;
+
+use seq_macro::seq;
+
+use crate::entity::Handle as EntityHandle;
+use crate::event::Emitted as EmittedEvent;
+use crate::pair::Pair;
+use crate::system::{
+ Callbacks,
+ NoCallbacks,
+ Param,
+ ReturnValue as SystemReturnValue,
+ TypeErased as TypeErasedSystem,
+};
+use crate::uid::Uid;
+use crate::util::Array;
+use crate::{Component, World};
+
+pub trait Observed
+{
+ type Events: Array<Pair<Uid, Uid>>;
+
+ fn events() -> Self::Events;
+}
+
+impl<Relation, Target> Observed for Pair<Relation, Target>
+where
+ Relation: Component,
+ Target: Component,
+{
+ type Events = [Pair<Uid, Uid>; 1];
+
+ fn events() -> Self::Events
+ {
+ [Pair::builder()
+ .relation::<Relation>()
+ .target::<Target>()
+ .build()]
+ }
+}
+
+/// Observer system.
+pub trait Observer<'world, Impl>
+{
+ type Callbacks: Callbacks;
+ type ObservedEvents: Array<Pair<Uid, Uid>>;
+
+ fn observed_events() -> Self::ObservedEvents;
+
+ fn finish_observer(self) -> (TypeErasedSystem, Self::Callbacks);
+}
+
+pub struct Observe<'world, ObservedT: Observed>
+{
+ _pd: PhantomData<ObservedT>,
+ world: &'world World,
+ emitted_event: EmittedEvent<'world>,
+}
+
+impl<'world, ObservedT: Observed> Observe<'world, ObservedT>
+{
+ pub fn new(world: &'world World, emitted_event: EmittedEvent<'world>) -> Self
+ {
+ Self {
+ _pd: PhantomData,
+ world,
+ emitted_event,
+ }
+ }
+
+ #[must_use]
+ pub fn event(&self) -> Uid
+ {
+ self.emitted_event.event
+ }
+}
+
+impl<ObservedT: Observed> Observe<'_, ObservedT>
+{
+ #[must_use]
+ pub fn iter(&self) -> ObserveIter<'_, ObservedT>
+ {
+ ObserveIter {
+ world: self.world,
+ inner: self.emitted_event.match_ids.iter(),
+ _pd: PhantomData,
+ }
+ }
+}
+
+impl<'a, ObservedT: Observed> IntoIterator for &'a Observe<'_, ObservedT>
+{
+ type IntoIter = ObserveIter<'a, ObservedT>;
+ type Item = <Self::IntoIter as Iterator>::Item;
+
+ fn into_iter(self) -> Self::IntoIter
+ {
+ self.iter()
+ }
+}
+
+pub struct ObserveIter<'observe, ObservedT: Observed>
+{
+ world: &'observe World,
+ inner: SliceIter<'observe, Uid>,
+ _pd: PhantomData<ObservedT>,
+}
+
+impl<'observe, ObservedT: Observed> Iterator for ObserveIter<'observe, ObservedT>
+{
+ type Item = EventMatch<'observe, ObservedT>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let match_id = *self.inner.next()?;
+
+ Some(EventMatch {
+ world: self.world,
+ id: match_id,
+ _pd: PhantomData,
+ })
+ }
+}
+
+/// A event match.
+#[derive(Debug)]
+pub struct EventMatch<'world, ObservedT: Observed>
+{
+ world: &'world World,
+ id: Uid,
+ _pd: PhantomData<ObservedT>,
+}
+
+impl<'world, ObservedT: Observed> EventMatch<'world, ObservedT>
+{
+ #[must_use]
+ pub fn entity_id(&self) -> Uid
+ {
+ self.id
+ }
+
+ /// Attempts to get the entity with the id of this match.
+ #[must_use]
+ pub fn try_get_entity(&self) -> Option<EntityHandle<'world>>
+ {
+ self.world.get_entity(self.id)
+ }
+}
+
+macro_rules! impl_observer {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret
+ > for Func
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: SystemReturnValue,
+ #(TParam~I: Param<'world, Input = ()>,)*
+ {
+ type Callbacks = NoCallbacks;
+ type ObservedEvents = ObservedT::Events;
+
+ fn observed_events() -> Self::ObservedEvents
+ {
+ ObservedT::events()
+ }
+
+ fn finish_observer(self) -> (TypeErasedSystem, NoCallbacks)
+ {
+ #![allow(unused)]
+
+ crate::util::const_assert!(
+ size_of::<Func>() == 0,
+ "System function is not zero-sized (not function pointer)"
+ );
+
+ let system = TypeErasedSystem {
+ run: |world, metadata, emitted_event| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ let emitted_event =
+ emitted_event.expect("No event info passed to observer");
+
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let emitted_event = unsafe {
+ transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(
+ emitted_event
+ )
+ };
+
+ let func = unsafe { std::mem::zeroed::<Func>() };
+
+ func(Observe::new(world, emitted_event), #({
+ TParam~I::new(world, &metadata)
+ },)*).into_result()
+ },
+ name: type_name::<Func>()
+ };
+
+ (system, NoCallbacks)
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..16 {
+ impl_observer!(C);
+});
diff --git a/engine-ecs/src/system/stateful.rs b/engine-ecs/src/system/stateful.rs
new file mode 100644
index 0000000..c9354c3
--- /dev/null
+++ b/engine-ecs/src/system/stateful.rs
@@ -0,0 +1,265 @@
+use std::any::type_name;
+use std::marker::PhantomData;
+use std::mem::transmute;
+
+use seq_macro::seq;
+
+use crate::component::local::SystemWithLocalComponents;
+use crate::component::Parts as ComponentParts;
+use crate::event::Emitted as EmittedEvent;
+use crate::system::initializable::{Initializable, MaybeInitializableParamTuple};
+use crate::system::observer::{Observe, Observed, Observer};
+use crate::system::{
+ Into as IntoSystem,
+ Metadata,
+ Param,
+ ReturnValue,
+ System,
+ TypeErased,
+};
+use crate::World;
+
+/// A stateful system.
+pub struct Stateful<Func: Copy + 'static>
+{
+ local_components: Vec<ComponentParts>,
+ _pd: PhantomData<Func>,
+}
+
+macro_rules! impl_system {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, Func, Ret, #(TParam~I,)*>
+ System<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func>
+ where
+ Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world, Input: 'static>,)*
+ {
+ type Callbacks = Callbacks;
+
+ fn finish(self) -> (TypeErased, Self::Callbacks)
+ {
+ crate::util::const_assert!(
+ size_of::<Func>() == 0,
+ "System function is not zero-sized (not function pointer)"
+ );
+
+ let Self { local_components, .. } = self;
+
+ let callbacks = Callbacks { local_components };
+
+ let type_erased = TypeErased {
+ run: |world, metadata, _| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ let func = unsafe { std::mem::zeroed::<Func>() };
+
+ func(#({
+ TParam~I::new(&world, &metadata)
+ },)*);
+
+ Ok(())
+ },
+ name: type_name::<Func>()
+ };
+
+
+ (type_erased, callbacks)
+ }
+ }
+
+ impl<'world, Func, Ret, #(TParam~I,)*>
+ Initializable<'world, fn(#(TParam~I,)*) -> Ret> for Stateful<Func>
+ where
+ Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world, Input: 'static>,)*
+ (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self>
+ {
+ type Inputs = <
+ (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self>
+ >::Inputs;
+
+ fn initialize(mut self, inputs: Self::Inputs) -> Self
+ {
+ init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs);
+
+ self
+ }
+ }
+
+ impl<'world, Func, Ret, #(TParam~I,)*>
+ IntoSystem<'world, fn(#(TParam~I,)*) -> Ret> for Func
+ where
+ Func: Fn(#(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world>,)*
+ {
+ type System = Stateful<Func>;
+
+ fn into_system(self) -> Self::System
+ {
+ Self::System {
+ local_components: Vec::new(), // TODO: Use Vec::with_capacity
+ _pd: PhantomData
+ }
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 1..16 {
+ impl_system!(C);
+});
+
+impl<Func: Copy + 'static> SystemWithLocalComponents for Stateful<Func>
+{
+ fn add_local_component(&mut self, component_parts: ComponentParts)
+ {
+ self.local_components.push(component_parts);
+ }
+}
+
+#[derive(Debug)]
+pub struct Callbacks
+{
+ local_components: Vec<ComponentParts>,
+}
+
+impl crate::system::Callbacks for Callbacks
+{
+ fn on_created(&mut self, world: &mut World, metadata: Metadata)
+ {
+ for local_comp_parts in self.local_components.drain(..) {
+ world.add_component(metadata.ent_id, local_comp_parts);
+ }
+ }
+}
+
+fn init_initializable_params<'world, SystemT, Params>(
+ system: &mut SystemT,
+ inputs: Params::Inputs,
+) where
+ Params: MaybeInitializableParamTuple<'world, SystemT>,
+{
+ Params::init_initializable(system, inputs);
+}
+
+macro_rules! impl_observer {
+ ($c: tt) => {
+ seq!(I in 0..$c {
+ impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Initializable<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret
+ > for Stateful<Func>
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world>,)*
+ (#(TParam~I,)*): MaybeInitializableParamTuple<'world, Self>
+ {
+ type Inputs = <
+ (#(TParam~I,)*) as MaybeInitializableParamTuple<'world, Self>
+ >::Inputs;
+
+ fn initialize(mut self, inputs: Self::Inputs) -> Self
+ {
+ init_initializable_params::<_, (#(TParam~I,)*)>(&mut self, inputs);
+
+ self
+ }
+ }
+
+ impl<'world, ObservedT, Func, Ret, #(TParam~I,)*> Observer<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret
+ > for Stateful<Func>
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world>,)*
+ {
+ type Callbacks = Callbacks;
+ type ObservedEvents = ObservedT::Events;
+
+ fn observed_events() -> Self::ObservedEvents
+ {
+ ObservedT::events()
+ }
+
+ fn finish_observer(self) -> (TypeErased, Callbacks)
+ {
+ #![allow(unused)]
+
+ crate::util::const_assert!(
+ size_of::<Func>() == 0,
+ "System function is not zero-sized (not function pointer)"
+ );
+
+ let Self { local_components, .. } = self;
+
+ let callbacks = Callbacks { local_components };
+
+ let system = TypeErased {
+ run: |world, metadata, emitted_event| {
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let world = unsafe { &*std::ptr::from_ref(world) };
+
+ let emitted_event =
+ emitted_event.expect("No event info passed to observer");
+
+ // SAFETY: The caller of TypeErased::run ensures the lifetime
+ // is correct
+ let emitted_event = unsafe {
+ transmute::<EmittedEvent<'_>, EmittedEvent<'_>>(
+ emitted_event
+ )
+ };
+
+ let func = unsafe { std::mem::zeroed::<Func>() };
+
+ func(Observe::new(world, emitted_event), #({
+ TParam~I::new(world, &metadata)
+ },)*).into_result()
+ },
+ name: type_name::<Func>()
+ };
+
+ (system, callbacks)
+ }
+ }
+
+ impl<'world, Func, Ret, ObservedT, #(TParam~I,)*> IntoSystem<
+ 'world,
+ fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret
+ > for Func
+ where
+ ObservedT: Observed,
+ Func: Fn(Observe<'world, ObservedT>, #(TParam~I,)*) -> Ret + Copy + 'static,
+ Ret: ReturnValue,
+ #(TParam~I: Param<'world>,)*
+ {
+ type System = Stateful<Func>;
+
+ fn into_system(self) -> Stateful<Func>
+ {
+ Stateful {
+ local_components: Vec::new(), // TODO: Use Vec::with_capacity
+ _pd: PhantomData
+ }
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..16 {
+ impl_observer!(C);
+});
diff --git a/engine-ecs/src/time.rs b/engine-ecs/src/time.rs
new file mode 100644
index 0000000..3f888cb
--- /dev/null
+++ b/engine-ecs/src/time.rs
@@ -0,0 +1,43 @@
+use std::ops::Deref;
+use std::time::{Duration, Instant};
+
+use engine_ecs_macros::Sole;
+
+use crate::sole::Single;
+
+#[derive(Debug, Sole)]
+#[non_exhaustive]
+pub struct Time
+{
+ /// Time passed since the beginning of the previous tick.
+ pub delta_time: Duration,
+
+ /// Time at the beginning of the current tick.
+ pub tick_begin: Option<Instant>,
+}
+
+impl Deref for Single<'_, Time>
+{
+ type Target = Time;
+
+ fn deref(&self) -> &Self::Target
+ {
+ let Ok(time) = self.get() else {
+ // Time is always created by World::new
+ unreachable!();
+ };
+
+ time
+ }
+}
+
+pub(crate) fn update(time: &mut Time)
+{
+ let current_time = Instant::now();
+
+ let tick_begin = time.tick_begin.get_or_insert(current_time);
+
+ time.delta_time = current_time.duration_since(*tick_begin);
+
+ time.tick_begin = Some(current_time);
+}
diff --git a/ecs/src/tuple.rs b/engine-ecs/src/tuple.rs
index def25a0..a2073b1 100644
--- a/ecs/src/tuple.rs
+++ b/engine-ecs/src/tuple.rs
@@ -1,4 +1,5 @@
use std::any::TypeId;
+use std::convert::Infallible;
use paste::paste;
use seq_macro::seq;
@@ -11,9 +12,9 @@ pub trait Tuple: sealed::Sealed
/// `(String, i32, u8)::WithElementAtEnd<Path> = (String, i32, u8, Path)`
///
/// # Important note
- /// If `Self` has 16 elements, this will be `()`. The reason for this is that the
- /// `Tuple` trait is only implemented for tuples with up to and including 16 elements.
- type WithElementAtEnd<NewElem>: Tuple;
+ /// If `Self` has 16 elements, this will be [`Infallible`]. The reason for this is
+ /// that the `Tuple` trait is only implemented for tuples with 0-16 elements.
+ type WithElementAtEnd<NewElem>;
/// `Self` without the last element.
///
@@ -26,6 +27,8 @@ pub trait Tuple: sealed::Sealed
/// Self with all elements wrapped in [`Option`].
type InOptions: Tuple;
+ fn with_elem<NewElem>(self, new_elem: NewElem) -> Self::WithElementAtEnd<NewElem>;
+
/// Pops the last element from this tuple, returning the new tuple and the popped
/// element.
fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement);
@@ -53,6 +56,32 @@ pub trait ReduceElement<Accumulator, Operation>
type Return;
}
+pub trait ElementConcat<Acc>
+{
+ type Output;
+
+ fn add_to(self, acc: Acc) -> Self::Output;
+}
+
+impl<Elem, Acc> ElementConcat<Acc> for Elem
+where
+ Acc: Tuple,
+{
+ type Output = Acc::WithElementAtEnd<Elem>;
+
+ fn add_to(self, acc: Acc) -> Self::Output
+ {
+ acc.with_elem(self)
+ }
+}
+
+pub trait Concat<Left>
+{
+ type Output;
+
+ fn concat(self, left: Left) -> Self::Output;
+}
+
macro_rules! tuple_reduce_elem_tuple {
(overflow) => {
()
@@ -65,6 +94,18 @@ macro_rules! tuple_reduce_elem_tuple {
};
}
+macro_rules! tuple_concat_elem_ouput {
+ (overflow) => {
+ Left
+ };
+
+ ($index: tt) => {
+ paste! {
+ [<Elem $index>]::Output
+ }
+ };
+}
+
macro_rules! elem_type_by_index {
(overflow) => {
()
@@ -126,6 +167,11 @@ macro_rules! impl_tuple_traits {
type InOptions = (#(Option<Elem~I>,)*);
+ fn with_elem<NewElem>(self, new_elem: NewElem) -> Self::WithElementAtEnd<NewElem>
+ {
+ (#(self.I,)* new_elem,)
+ }
+
fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement)
{
(
@@ -141,6 +187,31 @@ macro_rules! impl_tuple_traits {
}
}
+ paste! {
+ impl<Left, #(Elem~I,)*> Concat<Left> for (#(Elem~I,)*)
+ where
+ Left: Tuple,
+ #(
+ Elem~I: ElementConcat<
+ sub!(I - 1, tuple_concat_elem_ouput),
+ >,
+ )*
+ {
+ type Output = sub!($cnt - 1, tuple_concat_elem_ouput);
+
+ fn concat(self, left: Left) -> Self::Output
+ {
+ let out = left;
+
+ #(
+ let out = self.I.add_to(out);
+ )*
+
+ out
+ }
+ }
+ }
+
impl<#(Elem~I: 'static,)*> WithAllElemLtStatic for (#(Elem~I,)*)
{
fn get_mut<Element: 'static>(&mut self, index: usize) -> Option<&mut Element>
@@ -186,7 +257,7 @@ seq!(N in 0..16 {
seq!(I in 0..16 {
impl<#(Elem~I,)*> Tuple for (#(Elem~I,)*)
{
- type WithElementAtEnd<NewElem> = ();
+ type WithElementAtEnd<NewElem> = Infallible;
type WithoutLastElement = all_except_last!(start #(Elem~I)*);
@@ -194,6 +265,13 @@ seq!(I in 0..16 {
type InOptions = (#(Option<Elem~I>,)*);
+ fn with_elem<NewElem>(self, _: NewElem) -> Self::WithElementAtEnd<NewElem>
+ {
+ const {
+ panic!("Cannot add element to tuple with 16 elements");
+ };
+ }
+
fn pop_last(self) -> (Self::WithoutLastElement, Self::LastElement)
{
(
diff --git a/engine-ecs/src/uid.rs b/engine-ecs/src/uid.rs
new file mode 100644
index 0000000..78ca2ad
--- /dev/null
+++ b/engine-ecs/src/uid.rs
@@ -0,0 +1,207 @@
+use std::fmt::{Debug, Display, Formatter};
+use std::sync::atomic::{AtomicU32, Ordering};
+
+use seq_macro::seq;
+
+use crate::component::Component;
+use crate::util::{gen_mask_64, Array, BitMask, NumberExt};
+
+static NEXT: AtomicU32 = AtomicU32::new(Uid::FIRST_UNIQUE_ID);
+
+static WILDCARD_ID: u32 = 1;
+
+const ID_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(32..=63));
+const RELATION_BITS: BitMask<u64> = BitMask::new(gen_mask_64!(6..=31));
+const IS_PAIR_BITS: BitMask<u64> = BitMask::new(1 << 0);
+
+/// A unique identifier.
+#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub struct Uid
+{
+ inner: u64,
+}
+
+impl Uid
+{
+ /// The id part of the first unique `Uid`. The ids `0..Uid::FIRST_UNIQUE_ID` are
+ /// reserved.
+ pub const FIRST_UNIQUE_ID: u32 = 5;
+
+ /// Returns a new unique entity/component ID.
+ pub fn new_unique() -> Self
+ {
+ let id = NEXT.fetch_add(1, Ordering::Relaxed);
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(id)) | IS_PAIR_BITS.field_prep(0),
+ }
+ }
+
+ #[must_use]
+ pub fn wildcard() -> Self
+ {
+ Self {
+ inner: ID_BITS.field_prep(u64::from(WILDCARD_ID))
+ | IS_PAIR_BITS.field_prep(0),
+ }
+ }
+
+ /// Returns a new pair UID.
+ ///
+ /// # Panics
+ /// Will panic if either the given relation or target is a pair UID.
+ #[must_use]
+ pub fn new_pair(params: &PairParams) -> Self
+ {
+ assert!(!params.relation.is_pair(), "Pair relation cannot be a pair");
+ assert!(!params.target.is_pair(), "Pair target cannot be a pair");
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(params.target.id()))
+ | RELATION_BITS.field_prep(u64::from(params.relation.id()))
+ | IS_PAIR_BITS.field_prep(1),
+ }
+ }
+
+ #[must_use]
+ pub fn id(&self) -> u32
+ {
+ let Ok(id) = u32::try_from(self.inner.field_get(ID_BITS)) else {
+ unreachable!("Uid id does not fit in u32");
+ };
+
+ id
+ }
+
+ #[must_use]
+ pub fn is_pair(&self) -> bool
+ {
+ let Ok(is_pair) = u8::try_from(self.inner.field_get(IS_PAIR_BITS)) else {
+ unreachable!();
+ };
+
+ is_pair == 1
+ }
+
+ /// If this `Uid` is a pair, returns the relation `Uid`.
+ ///
+ /// # Panics
+ /// Will panic if this `Uid` is not a pair.
+ #[must_use]
+ pub fn relation(&self) -> Self
+ {
+ assert!(self.is_pair(), "Uid is not a pair");
+
+ let Ok(relation) = u32::try_from(self.inner.field_get(RELATION_BITS)) else {
+ unreachable!("Uid relation does not fit in u32");
+ };
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(relation)) | IS_PAIR_BITS.field_prep(0),
+ }
+ }
+
+ /// Compares the relation of this pair `Uid` with the relation of another pair `Uid`.
+ ///
+ /// # Panics
+ /// Will panic if any of the `Uid`s are not a pair.
+ #[must_use]
+ pub fn has_same_relation_as(&self, other: Self) -> bool
+ {
+ self.relation() == other.relation()
+ }
+
+ /// If this `Uid` is a pair, returns the target `Uid`.
+ ///
+ /// # Panics
+ /// Will panic if this `Uid` is not a pair.
+ #[must_use]
+ pub fn target(&self) -> Self
+ {
+ assert!(self.is_pair(), "Uid is not a pair");
+
+ Self {
+ inner: ID_BITS.field_prep(u64::from(self.id())) | IS_PAIR_BITS.field_prep(0),
+ }
+ }
+}
+
+impl Debug for Uid
+{
+ fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
+ {
+ let mut debug_struct = formatter.debug_struct("Uid");
+
+ if self.is_pair() {
+ return debug_struct
+ .field("relation", &self.relation())
+ .field("target", &self.target())
+ .finish();
+ }
+
+ debug_struct.field("id", &self.id()).finish()
+ }
+}
+
+impl Display for Uid
+{
+ fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
+ {
+ if self.is_pair() {
+ return write!(formatter, "({}, {})", self.relation(), self.target());
+ }
+
+ if *self == Uid::wildcard() {
+ return write!(formatter, "*");
+ }
+
+ write!(formatter, "◇{}", self.id())
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct PairParams
+{
+ pub relation: Uid,
+ pub target: Uid,
+}
+
+pub trait With: 'static
+{
+ fn uid() -> Uid;
+}
+
+impl<ComponentT: Component> With for ComponentT
+{
+ fn uid() -> Uid
+ {
+ Self::id()
+ }
+}
+
+pub trait WithUidTuple
+{
+ type UidsArray: Array<Uid>;
+
+ fn uids() -> Self::UidsArray;
+}
+
+macro_rules! impl_with_uid_tuple {
+ ($c: tt) => {
+ seq!(I in 0..=$c {
+ impl<#(WithUid~I: With,)*> WithUidTuple for (#(WithUid~I,)*)
+ {
+ type UidsArray = [Uid; $c + 1];
+
+ fn uids() -> Self::UidsArray
+ {
+ [#(WithUid~I::uid(),)*]
+ }
+ }
+ });
+ };
+}
+
+seq!(C in 0..=16 {
+ impl_with_uid_tuple!(C);
+});
diff --git a/ecs/src/util.rs b/engine-ecs/src/util.rs
index 9ab4dc6..304ba21 100644
--- a/ecs/src/util.rs
+++ b/engine-ecs/src/util.rs
@@ -1,27 +1,45 @@
+use std::fmt::Display;
use std::hash::Hash;
use std::mem::transmute;
use std::ops::{BitAnd, Deref};
use hashbrown::HashMap;
-pub(crate) mod array_vec;
+pub mod array_vec;
pub trait VecExt<Item>
{
- fn insert_at_partition_point_by_key<Key>(
+ fn insert_at_part_pt_by_key<Key>(
&mut self,
item: Item,
- func: impl FnMut(&Item) -> Key,
+ func: impl FnMut(&Item) -> &Key,
) where
Key: Ord;
}
impl<Item> VecExt<Item> for Vec<Item>
{
- fn insert_at_partition_point_by_key<Key>(
+ fn insert_at_part_pt_by_key<Key>(
&mut self,
item: Item,
- mut func: impl FnMut(&Item) -> Key,
+ mut func: impl FnMut(&Item) -> &Key,
+ ) where
+ Key: Ord,
+ {
+ let key = func(&item);
+
+ let insert_index = self.partition_point(|other_item| func(other_item) <= key);
+
+ self.insert(insert_index, item);
+ }
+}
+
+impl<Item, const CAPACITY: usize> VecExt<Item> for ArrayVec<Item, CAPACITY>
+{
+ fn insert_at_part_pt_by_key<Key>(
+ &mut self,
+ item: Item,
+ mut func: impl FnMut(&Item) -> &Key,
) where
Key: Ord,
{
@@ -121,6 +139,27 @@ pub enum Either<A, B>
B(B),
}
+impl<A, B> Either<A, B>
+{
+ #[inline]
+ pub fn as_a(&self) -> Option<&A>
+ {
+ match self {
+ Self::A(a) => Some(a),
+ Self::B(_) => None,
+ }
+ }
+
+ #[inline]
+ pub fn as_b(&self) -> Option<&B>
+ {
+ match self {
+ Self::A(_) => None,
+ Self::B(b) => Some(b),
+ }
+ }
+}
+
impl<A, B> Iterator for Either<A, B>
where
A: Iterator,
@@ -137,6 +176,20 @@ where
}
}
+impl<A, B> Display for Either<A, B>
+where
+ A: Display,
+ B: Display,
+{
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ match self {
+ Self::A(a) => a.fmt(f),
+ Self::B(b) => b.fmt(f),
+ }
+ }
+}
+
pub trait HashMapExt<Key, Value>
{
/// Returns true if the keys are a subset of another [`HashMap`]'s keys, i.e., `other`
@@ -170,14 +223,21 @@ where
pub trait Array<Item>:
AsRef<[Item]>
+ AsMut<[Item]>
- + IntoIterator<Item = Item>
+ + IntoIterator<Item = Item, IntoIter: ExactSizeIterator>
+ Into<Vec<Item>>
+ Sortable<Item = Item>
+ sealed::Sealed
{
+ fn map<T>(self, func: impl FnMut(Item) -> T) -> impl Array<T>;
}
-impl<Item, const CNT: usize> Array<Item> for [Item; CNT] {}
+impl<Item, const CNT: usize> Array<Item> for [Item; CNT]
+{
+ fn map<T>(self, func: impl FnMut(Item) -> T) -> impl Array<T>
+ {
+ self.map(func)
+ }
+}
impl<Item, const CNT: usize> sealed::Sealed for [Item; CNT] {}
@@ -300,6 +360,107 @@ macro_rules! gen_mask_64 {
pub(crate) use gen_mask_64;
+macro_rules! impl_multiple {
+ (
+ $type: ident,
+ ($(
+ impl$(<$($generic: tt$(: $bound: ident)?),*>)?
+ _<$($lt_param: lifetime),*><$($type_param: ty),*>
+ $(($($extra_cb_arg: expr),*))?
+ ),*)
+ cb=(
+ type_params=($($ty_param_matcher: ident),*)
+ $(, $($extra_matcher: ident),+)?
+ ) => {
+ $($item_tt: tt)*
+ }
+ ) => {
+ const _: () = {
+ $crate::util::impl_multiple!(
+ @(make_gen_item_macro)
+ _gen_multiple_impl_item,
+ ($($ty_param_matcher),*),
+ ($($($extra_matcher),+)?),
+ ($($item_tt)*)
+ );
+
+ $(
+ impl $(<$($generic$(: $bound)?,)*>)? $type<$($lt_param,)* $($type_param),*>
+ {
+ _gen_multiple_impl_item!(
+ type_params=($($type_param),*),
+ $($($extra_cb_arg),*)?
+ );
+ }
+ )*
+ };
+ };
+
+ (
+ @(make_gen_item_macro)
+ $name: ident,
+ ($($ty_param_matcher: ident),*),
+ ($($extra_matcher: ident),*),
+ ($($transcriber: tt)*)
+ ) => {
+ $crate::util::impl_multiple!(
+ @(make_gen_item_macro)
+ ($),
+ $name,
+ ($($ty_param_matcher),*),
+ ($($extra_matcher),*),
+ ($($transcriber)*)
+ );
+ };
+
+ (
+ @(make_gen_item_macro)
+ ($dollar: tt),
+ $name: ident,
+ ($($ty_param_matcher: ident),*),
+ ($($extra_matcher: ident),*),
+ ($($transcriber: tt)*)
+ ) => {
+ $crate::util::impl_multiple!(
+ @(make_gen_item_macro)
+ $name,
+ (
+ type_params=($($dollar$ty_param_matcher: ty),*),
+ $($dollar$extra_matcher: expr),*
+ ) => {
+ $($transcriber)*
+ }
+ );
+ };
+
+ (@(make_gen_item_macro) $name: ident, $($rule: tt)*) => {
+ macro_rules! $name {
+ $($rule)*
+ }
+ };
+}
+
+pub(crate) use impl_multiple;
+
+macro_rules! const_assert {
+ ($expr: expr, $message: literal) => {
+ const {
+ if !($expr) {
+ panic!(concat!(
+ "Const assertion failed: ",
+ stringify!($expr),
+ ": ",
+ $message
+ ))
+ }
+ }
+ };
+}
+
+pub(crate) use const_assert;
+
+use crate::util::array_vec::ArrayVec;
+
mod sealed
{
pub trait Sealed {}
diff --git a/ecs/src/util/array_vec.rs b/engine-ecs/src/util/array_vec.rs
index 13a0349..3e3344c 100644
--- a/ecs/src/util/array_vec.rs
+++ b/engine-ecs/src/util/array_vec.rs
@@ -1,7 +1,7 @@
+use std::fmt::Debug;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};
-#[derive(Debug)]
pub struct ArrayVec<Item, const CAPACITY: usize>
{
items: [MaybeUninit<Item>; CAPACITY],
@@ -45,8 +45,8 @@ impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY>
unsafe {
std::ptr::copy(
- &self.items[index],
- &mut self.items[index + 1],
+ &raw const self.items[index],
+ &raw mut self.items[index + 1],
self.len - index,
);
}
@@ -57,9 +57,23 @@ impl<Item, const CAPACITY: usize> ArrayVec<Item, CAPACITY>
}
}
+impl<Item, const CAPACITY: usize> FromIterator<Item> for ArrayVec<Item, CAPACITY>
+{
+ fn from_iter<IterT: IntoIterator<Item = Item>>(iter: IterT) -> Self
+ {
+ let mut new = Self::default();
+
+ for item in iter {
+ new.push(item);
+ }
+
+ new
+ }
+}
+
impl<Item, const CAPACITY: usize> Extend<Item> for ArrayVec<Item, CAPACITY>
{
- fn extend<IntoIter: IntoIterator<Item = Item>>(&mut self, iter: IntoIter)
+ fn extend<Iter: IntoIterator<Item = Item>>(&mut self, iter: Iter)
{
for item in iter {
self.push(item);
@@ -115,3 +129,39 @@ impl<Item, const CAPACITY: usize> Default for ArrayVec<Item, CAPACITY>
}
}
}
+
+impl<Item, const CAPACITY: usize> Clone for ArrayVec<Item, CAPACITY>
+where
+ Item: Clone,
+{
+ fn clone(&self) -> Self
+ {
+ Self::from_iter(self.iter().cloned())
+ }
+}
+
+impl<Item, const CAPACITY: usize> Drop for ArrayVec<Item, CAPACITY>
+{
+ fn drop(&mut self)
+ {
+ for item in &mut self.items[..self.len] {
+ // SAFETY: The items from index 0 to the length index will always be
+ // initialized and satisfy all the invariants of the Item type.
+ unsafe {
+ item.assume_init_drop();
+ }
+ }
+ }
+}
+
+impl<Item: Debug, const CAPACITY: usize> Debug for ArrayVec<Item, CAPACITY>
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter
+ .debug_struct("ArrayVec")
+ .field("items", &self.as_ref())
+ .field("len", &self.len)
+ .finish()
+ }
+}
diff --git a/engine-ecs/tests/phase.rs b/engine-ecs/tests/phase.rs
new file mode 100644
index 0000000..c2469fd
--- /dev/null
+++ b/engine-ecs/tests/phase.rs
@@ -0,0 +1,36 @@
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+use engine_ecs::component::local::Local;
+use engine_ecs::phase::UPDATE;
+use engine_ecs::system::initializable::Initializable;
+use engine_ecs::system::Into;
+use engine_ecs::{Component, World};
+
+#[derive(Component)]
+struct Thing;
+
+#[test]
+fn system_run_order_correct_when_one_has_local_comp()
+{
+ static COUNTER: AtomicUsize = AtomicUsize::new(0);
+
+ fn first_system(_thing: Local<Thing>)
+ {
+ assert_eq!(COUNTER.fetch_add(1, Ordering::Relaxed), 0);
+ }
+
+ fn second_system()
+ {
+ assert_eq!(COUNTER.fetch_add(1, Ordering::Relaxed), 1);
+ }
+
+ let mut world = World::new();
+
+ world.register_system(*UPDATE, first_system.into_system().initialize((Thing,)));
+
+ world.register_system(*UPDATE, second_system);
+
+ world.step();
+
+ assert_eq!(COUNTER.load(Ordering::Relaxed), 2);
+}
diff --git a/engine-ecs/tests/query.rs b/engine-ecs/tests/query.rs
new file mode 100644
index 0000000..d6dd8b7
--- /dev/null
+++ b/engine-ecs/tests/query.rs
@@ -0,0 +1,406 @@
+use engine_ecs::component::Component;
+use engine_ecs::pair::{Pair, Wildcard};
+use engine_ecs::query::term::Without;
+use engine_ecs::query::{Query, TermTuple as QueryTermTuple};
+use engine_ecs::uid::Uid;
+use engine_ecs::{Component, World};
+use parking_lot::{Mutex, Once};
+
+pub static SETUP: Once = Once::new();
+
+pub static TEST_LOCK: Mutex<()> = Mutex::new(());
+
+#[derive(Component)]
+struct A;
+
+#[derive(Component)]
+struct B;
+
+#[derive(Component)]
+struct C;
+
+#[derive(Component)]
+struct D;
+
+#[derive(Component)]
+struct E;
+
+#[derive(Component)]
+struct F;
+
+#[derive(Component)]
+struct G;
+
+fn setup()
+{
+ SETUP.call_once_force(|_| {
+ assert_eq!(A::id().id(), Uid::FIRST_UNIQUE_ID);
+ assert_eq!(B::id().id(), Uid::FIRST_UNIQUE_ID + 1);
+ assert_eq!(C::id().id(), Uid::FIRST_UNIQUE_ID + 2);
+ assert_eq!(D::id().id(), Uid::FIRST_UNIQUE_ID + 3);
+ assert_eq!(E::id().id(), Uid::FIRST_UNIQUE_ID + 4);
+ assert_eq!(F::id().id(), Uid::FIRST_UNIQUE_ID + 5);
+ assert_eq!(G::id().id(), Uid::FIRST_UNIQUE_ID + 6);
+ });
+}
+
+fn assert_query_finds_ents<'world, QueryTerms>(
+ query: Query<'world, QueryTerms>,
+ mut expected_ent_ids: Vec<Uid>,
+) where
+ for<'query> QueryTerms: QueryTermTuple<'query>,
+{
+ assert!(
+ query.iter_with_euids().all(|(ent_id, _)| {
+ let Some(index) = expected_ent_ids
+ .iter()
+ .position(|expected_id| *expected_id == ent_id)
+ else {
+ return false;
+ };
+
+ expected_ent_ids.remove(index);
+
+ true
+ }),
+ "Unexpected entity was found. Expected entities left: {expected_ent_ids:?}"
+ );
+
+ assert_eq!(
+ expected_ent_ids.len(),
+ 0,
+ concat!(
+ "Not all entities expected to be found was found. ",
+ "Expected entities left: {:?}"
+ ),
+ expected_ent_ids
+ );
+}
+
+#[test]
+fn query_archetype_exists_with_edges_to_next_archetypes()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, C));
+ let ent_2_id = world.spawn((A, B, C, D, E));
+ let ent_3_id = world.spawn((A, B, C, E));
+ let ent_4_id = world.spawn((A, B, C, G, F));
+
+ assert_query_finds_ents(
+ world.query::<(&A, &B, &C)>(),
+ vec![ent_1_id, ent_2_id, ent_3_id, ent_4_id],
+ );
+}
+
+#[test]
+fn query_archetype_exists_with_2_comps_diff_to_next_archetype()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, C, D, F));
+
+ let ent_2_id = world.spawn((A, B, F));
+
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
+}
+
+#[test]
+fn query_archetype_exists_with_2_comps_diff_to_next_archetype_rev()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, F));
+
+ let ent_2_id = world.spawn((A, B, C, D, F));
+
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
+}
+
+#[test]
+fn query_archetype_exists_with_3_comps_diff_to_next_archetype()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, C, D, E, F));
+
+ let ent_2_id = world.spawn((A, B, F));
+
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
+}
+
+#[test]
+fn query_archetype_exists_with_3_comps_diff_to_next_archetype_rev()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, F));
+
+ let ent_2_id = world.spawn((A, B, C, D, E, F));
+
+ assert_query_finds_ents(world.query::<(&A, &B, &F)>(), vec![ent_1_id, ent_2_id]);
+}
+
+#[test]
+fn query_archetype_exists_with_4_comps_diff_to_next_archetype()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, C, D, E, F, G));
+
+ let ent_2_id = world.spawn((A, B, G));
+
+ assert_query_finds_ents(world.query::<(&A, &B, &G)>(), vec![ent_1_id, ent_2_id]);
+}
+
+#[test]
+fn query_archetype_exists_with_4_comps_diff_to_next_archetype_rev()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, G));
+
+ let ent_2_id = world.spawn((A, B, C, D, E, F, G));
+
+ assert_query_finds_ents(world.query::<(&A, &B, &G)>(), vec![ent_1_id, ent_2_id]);
+}
+
+#[test]
+fn query_archetype_exists_with_4_comps_diff_to_next_archetype_and_opt_comp()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, C, D, E, F, G));
+
+ let ent_2_id = world.spawn((A, B, G));
+
+ assert_query_finds_ents(
+ world.query::<(&A, Option<&E>, &G)>(),
+ vec![ent_1_id, ent_2_id],
+ );
+}
+
+#[test]
+fn query_archetype_nonexistant()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ world.spawn((A, B, C));
+
+ let ent_2_id = world.spawn((A, B, C, D, E));
+ let ent_3_id = world.spawn((A, B, C, E));
+
+ world.spawn((A, B, C, G, F));
+
+ assert_query_finds_ents(world.query::<(&A, &E)>(), vec![ent_2_id, ent_3_id]);
+}
+
+#[test]
+fn query_archetype_nonexistant_and_opt_comp()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ world.spawn((A, B, C));
+ let ent_2_id = world.spawn((A, B, C, D, E));
+ let ent_3_id = world.spawn((A, B, C, E));
+ world.spawn((A, B, C, G, F));
+
+ assert_query_finds_ents(
+ world.query::<(&A, &E, Option<&D>)>(),
+ vec![ent_2_id, ent_3_id],
+ );
+}
+
+#[test]
+fn query_without_comp_and_archetype_exists()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, B, C));
+
+ world.spawn((A, B, C, E));
+ world.spawn((A, B, C, F, E));
+
+ let ent_2_id = world.spawn((A, B, C, G));
+ let ent_3_id = world.spawn((A, B, C, G, F));
+
+ assert_query_finds_ents(
+ world.query::<(&A, &B, &C, Without<E>)>(),
+ vec![ent_1_id, ent_2_id, ent_3_id],
+ );
+}
+
+#[test]
+fn query_without_required_comp_and_archetype_exists()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ world.spawn((A, B, C));
+
+ world.spawn((A, B, C, E));
+ world.spawn((A, B, C, F, E));
+
+ world.spawn((A, B, C, G));
+ world.spawn((A, B, C, G, F));
+
+ assert_query_finds_ents(world.query::<(&A, &B, Without<B>)>(), vec![]);
+}
+
+#[test]
+fn query_without_comp_and_archetype_nonexistant()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ world.spawn((A, B, C));
+
+ let ent_1_id = world.spawn((A, B, C, E));
+
+ world.spawn((A, B, C, F, E));
+
+ let ent_2_id = world.spawn((A, B, C, G, E));
+ world.spawn((A, B, C, G, F, E));
+
+ assert_query_finds_ents(
+ world.query::<(&A, &E, Without<F>)>(),
+ vec![ent_1_id, ent_2_id],
+ );
+}
+
+#[test]
+fn query_with_wildcard_target_pair()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, C));
+
+ world.spawn((B,));
+
+ let ent_2_id = world.spawn((
+ B,
+ Pair::builder().relation::<G>().target_id(ent_1_id).build(),
+ ));
+
+ world.spawn((
+ B,
+ Pair::builder().relation::<F>().target_id(ent_1_id).build(),
+ ));
+ world.spawn((
+ B,
+ A,
+ C,
+ Pair::builder().relation::<F>().target_id(ent_1_id).build(),
+ ));
+
+ let ent_3_id = world.spawn((
+ B,
+ Pair::builder().relation::<G>().target_id(ent_2_id).build(),
+ ));
+
+ let ent_4_id = world.spawn((
+ B,
+ E,
+ Pair::builder().relation::<G>().target_as_data(D).build(),
+ ));
+
+ assert_query_finds_ents(
+ world.query::<(&B, Pair<G, Wildcard>)>(),
+ vec![ent_2_id, ent_3_id, ent_4_id],
+ );
+}
+
+#[test]
+fn query_with_component_target_pair()
+{
+ setup();
+
+ let _test_lock = TEST_LOCK.lock();
+
+ let mut world = World::new();
+
+ let ent_1_id = world.spawn((A, C));
+
+ world.spawn((B,));
+
+ world.spawn((
+ B,
+ Pair::builder().relation::<G>().target_id(ent_1_id).build(),
+ ));
+
+ world.spawn((
+ B,
+ Pair::builder().relation::<F>().target_id(ent_1_id).build(),
+ ));
+ world.spawn((
+ B,
+ A,
+ C,
+ Pair::builder().relation::<F>().target_id(ent_1_id).build(),
+ ));
+
+ let ent_2_id =
+ world.spawn((B, Pair::builder().relation::<G>().target_as_data(F).build()));
+
+ let ent_3_id = world.spawn((
+ B,
+ E,
+ Pair::builder().relation::<G>().target_as_data(F).build(),
+ ));
+
+ assert_query_finds_ents(world.query::<(&B, Pair<G, &F>)>(), vec![ent_2_id, ent_3_id]);
+}
diff --git a/engine-macros/Cargo.toml b/engine-macros/Cargo.toml
new file mode 100644
index 0000000..11e3c4d
--- /dev/null
+++ b/engine-macros/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "engine-macros"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+proc-macro = true
+
+[dependencies]
+quote = "1.0.35"
+syn = { version = "2.0.51", features = ["full", "extra-traits" ] }
+proc-macro2 = "1.0.78"
+
diff --git a/engine-macros/src/lib.rs b/engine-macros/src/lib.rs
new file mode 100644
index 0000000..deddd15
--- /dev/null
+++ b/engine-macros/src/lib.rs
@@ -0,0 +1,10 @@
+#![deny(clippy::all, clippy::pedantic)]
+
+mod reflection;
+mod util;
+
+#[proc_macro_derive(Reflection, attributes(reflection))]
+pub fn reflection_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream
+{
+ reflection::derive(input.into()).into()
+}
diff --git a/engine-macros/src/reflection.rs b/engine-macros/src/reflection.rs
new file mode 100644
index 0000000..2180084
--- /dev/null
+++ b/engine-macros/src/reflection.rs
@@ -0,0 +1,73 @@
+use crate::reflection::options_attr::OptionsAttr;
+
+mod default_value;
+mod enum_impl;
+mod field;
+mod options_attr;
+mod struct_impl;
+mod visibility;
+
+pub fn derive(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream
+{
+ let input = syn::parse2::<syn::Item>(input).unwrap();
+
+ let options = get_options(&input);
+
+ match input {
+ syn::Item::Struct(input) => struct_impl::generate(input, options),
+ syn::Item::Enum(input) => enum_impl::generate(input, options),
+ _ => panic!("Invalid input"),
+ }
+}
+
+pub fn get_options(input: &syn::Item) -> OptionsAttr
+{
+ let (attrs, generic_params) = match &input {
+ syn::Item::Struct(input) => (input.attrs.as_slice(), &input.generics.params),
+ syn::Item::Enum(input) => (input.attrs.as_slice(), &input.generics.params),
+ _ => (
+ const { &[] }.as_slice(),
+ &syn::punctuated::Punctuated::<syn::GenericParam, syn::Token![,]>::new(),
+ ),
+ };
+
+ if attrs
+ .iter()
+ .filter(|attr| attr.path().is_ident("reflection"))
+ .count()
+ > 1
+ {
+ panic!("Multiple 'reflection' attributes are not allowed");
+ }
+
+ let options = match attrs
+ .iter()
+ .find_map(|attr| {
+ if attr.path().is_ident("reflection") {
+ return Some(attr.parse_args::<OptionsAttr>());
+ }
+
+ None
+ })
+ .transpose()
+ {
+ Ok(attr) => attr.unwrap_or_default(),
+ Err(err) => {
+ panic!("{err}");
+ }
+ };
+
+ if options.impl_with_generics.is_empty() && !generic_params.is_empty() {
+ panic!(concat!(
+ "Generic types deriving Reflection must specify which ",
+ "generics to use in the generated impl(s) using the 'reflection' attribute\n",
+ "For example: #[reflection(impl_with_generics(<u32, String>, <u8, u16>))]"
+ ));
+ }
+
+ if !options.impl_with_generics.is_empty() && generic_params.is_empty() {
+ panic!("Specifying 'impl_with_generics' for non-generic types is not allowed");
+ }
+
+ options
+}
diff --git a/engine-macros/src/reflection/default_value.rs b/engine-macros/src/reflection/default_value.rs
new file mode 100644
index 0000000..6784129
--- /dev/null
+++ b/engine-macros/src/reflection/default_value.rs
@@ -0,0 +1,42 @@
+use quote::quote;
+
+pub fn gen_get_default_value_fn(
+ type_ident: &proc_macro2::Ident,
+ generic_args: Option<&syn::AngleBracketedGenericArguments>,
+) -> proc_macro2::TokenStream
+{
+ quote! {
+ struct SpecializationTarget<T>(std::marker::PhantomData<T>);
+
+ trait HasDefaultValue
+ {
+ fn default_value_fn(&self) -> Option<fn() -> Box<dyn std::any::Any>>;
+ }
+
+ trait NoDefaultValue
+ {
+ fn default_value_fn(&self) -> Option<fn() -> Box<dyn std::any::Any>>;
+ }
+
+ impl<T> NoDefaultValue for &SpecializationTarget<T>
+ {
+ fn default_value_fn(&self) -> Option<fn() -> Box<dyn std::any::Any>>
+ {
+ None
+ }
+ }
+
+ impl<T> HasDefaultValue for SpecializationTarget<T>
+ where
+ T: std::default::Default + 'static
+ {
+ fn default_value_fn(&self) -> Option<fn() -> Box<dyn std::any::Any>>
+ {
+ Some(|| Box::new(T::default()))
+ }
+ }
+
+ (&SpecializationTarget::<#type_ident #generic_args>(std::marker::PhantomData))
+ .default_value_fn()
+ }
+}
diff --git a/engine-macros/src/reflection/enum_impl.rs b/engine-macros/src/reflection/enum_impl.rs
new file mode 100644
index 0000000..14d91a7
--- /dev/null
+++ b/engine-macros/src/reflection/enum_impl.rs
@@ -0,0 +1,485 @@
+use quote::{format_ident, quote};
+
+use crate::reflection::default_value::gen_get_default_value_fn;
+use crate::reflection::options_attr::OptionsAttr;
+use crate::util::find_engine_crate_path;
+
+pub fn generate(input: syn::ItemEnum, options: OptionsAttr) -> proc_macro2::TokenStream
+{
+ let engine_crate_path = find_engine_crate_path().unwrap();
+
+ let variant_lookup_match_arms = input
+ .variants
+ .iter()
+ .enumerate()
+ .map(|(index, variant)| {
+ let variant_ident = &variant.ident;
+
+ let pattern = match variant.fields {
+ syn::Fields::Unit => quote! { Self::#variant_ident },
+ syn::Fields::Named(_) => quote! { Self::#variant_ident { .. } },
+ syn::Fields::Unnamed(_) => quote! { Self::#variant_ident(..) },
+ };
+
+ quote! {
+ #pattern => &enum_reflection.variants[#index]
+ }
+ })
+ .collect::<Vec<_>>();
+
+ let is_unit_only = input
+ .variants
+ .iter()
+ .all(|variant| matches!(variant.fields, syn::Fields::Unit));
+
+ let impls: &mut dyn Iterator<Item = proc_macro2::TokenStream> =
+ if input.generics.params.is_empty() {
+ &mut [generate_impls(
+ &input,
+ None,
+ is_unit_only,
+ &variant_lookup_match_arms,
+ &engine_crate_path,
+ )]
+ .into_iter()
+ } else {
+ &mut options.impl_with_generics.iter().map(|generic_args| {
+ generate_impls(
+ &input,
+ Some(generic_args),
+ is_unit_only,
+ &variant_lookup_match_arms,
+ &engine_crate_path,
+ )
+ })
+ };
+
+ quote! {
+ #(#impls)*
+ }
+}
+
+fn generate_impls(
+ input: &syn::ItemEnum,
+ generic_args: Option<&syn::AngleBracketedGenericArguments>,
+ is_unit_only: bool,
+ variant_lookup_match_arms: &[proc_macro2::TokenStream],
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args);
+
+ let variants = generate_variants(&input.variants, &engine_crate_path);
+
+ let generics_type_aliases = input
+ .generics
+ .type_params()
+ .zip(
+ generic_args
+ .iter()
+ .map(|generic_args| &generic_args.args)
+ .flatten()
+ .flat_map(|generic_arg| match generic_arg {
+ syn::GenericArgument::Type(ty) => Some(ty),
+ _ => None,
+ }),
+ )
+ .map(|(type_param, generic_arg_type)| {
+ let type_param_ident = &type_param.ident;
+
+ quote! {
+ type #type_param_ident = #generic_arg_type;
+ }
+ });
+
+ let input_ident = &input.ident;
+
+ quote! {
+ unsafe impl #engine_crate_path::reflection::Reflection for
+ #input_ident #generic_args
+ {
+ const TYPE_REFLECTION: &#engine_crate_path::reflection::Type =
+ &const {
+ #(#generics_type_aliases)*
+
+ #engine_crate_path::reflection::Type::Enum(
+ #engine_crate_path::reflection::Enum {
+ variants: &[#(#variants),*],
+ is_unit_only: #is_unit_only,
+ get_default_value: || {
+ #get_default_value_fn
+ },
+ cast_dyn_any: |ptr| ptr.cast::<Self>(),
+ get_variant_index: |target| {
+ use std::any::Any;
+ use #engine_crate_path::reflection::Reflection;
+ use #engine_crate_path::reflection::EnumReflectionExt;
+
+ let target = target.downcast_ref::<Self>()?;
+
+ let enum_reflection = unsafe {
+ <Self as Reflection>::TYPE_REFLECTION
+ .as_enum()
+ .unwrap_unchecked()
+ };
+
+ Some(enum_reflection.variants
+ .element_offset(
+ match target {
+ #(#variant_lookup_match_arms),*
+ }
+ )
+ .unwrap())
+ }
+ }
+ )
+ };
+ }
+
+ unsafe impl #engine_crate_path::reflection::EnumReflectionExt for
+ #input_ident #generic_args
+ {
+ fn get_variant_reflection(&self)
+ -> &'static #engine_crate_path::reflection::EnumVariant
+ {
+ let enum_reflection = unsafe {
+ <Self as #engine_crate_path::reflection::Reflection>::TYPE_REFLECTION
+ .as_enum()
+ .unwrap_unchecked()
+ };
+
+ match self {
+ #(#variant_lookup_match_arms),*
+ }
+ }
+ }
+ }
+}
+
+fn generate_variants<'a>(
+ input_variants: &'a syn::punctuated::Punctuated<syn::Variant, syn::Token![,]>,
+ engine_crate_path: &'a syn::Path,
+) -> impl Iterator<Item = proc_macro2::TokenStream> + use<'a>
+{
+ input_variants.iter().map(move |variant| {
+ let variant_name =
+ syn::LitStr::new(&variant.ident.to_string(), variant.ident.span());
+
+ let fields = match &variant.fields {
+ syn::Fields::Unit => quote! { None },
+ syn::Fields::Named(named_fields) => {
+ let fields = named_fields.named.iter().enumerate().map(
+ |(variant_field_index, variant_field)| {
+ generate_enum_variant_field(
+ variant_field,
+ variant_field_index,
+ &engine_crate_path,
+ )
+ },
+ );
+
+ quote! {
+ Some(#engine_crate_path::reflection::EnumVariantFields::Named {
+ fields: &[#(#fields),*]
+ })
+ }
+ }
+ syn::Fields::Unnamed(unnamed_fields) => {
+ let fields = unnamed_fields.unnamed.iter().enumerate().map(
+ |(variant_field_index, variant_field)| {
+ generate_enum_variant_field(
+ variant_field,
+ variant_field_index,
+ &engine_crate_path,
+ )
+ },
+ );
+
+ quote! {
+ Some(#engine_crate_path::reflection::EnumVariantFields::Unnamed {
+ fields: &[#(#fields),*]
+ })
+ }
+ }
+ };
+
+ let variant_field_vars = variant.fields.iter().enumerate().map(
+ |(variant_field_index, variant_field)| {
+ let var_ident = match variant_field.ident.as_ref() {
+ Some(field_ident) => format_ident!("field_{field_ident}"),
+ None => format_ident!("field_{}", variant_field_index),
+ };
+
+ let field_type = &variant_field.ty;
+
+ quote! {
+ let #var_ident = fields
+ .next()
+ .ok_or(EnumVariantWriteNewToError::TooFewFields)?
+ .downcast::<#field_type>()
+ .map_err(|_| EnumVariantWriteNewToError::WrongFieldType)?;
+ }
+ },
+ );
+
+ let variant_construction =
+ match &variant.fields {
+ syn::Fields::Unit => {
+ let variant_ident = &variant.ident;
+
+ quote! {
+ Self::#variant_ident
+ }
+ }
+ syn::Fields::Named(named_fields) => {
+ let variant_ident = &variant.ident;
+
+ let fields = named_fields.named.iter().map(|field| {
+ let Some(field_ident) = field.ident.as_ref() else {
+ unreachable!();
+ };
+
+ let field_var_ident = format_ident!("field_{field_ident}");
+
+ quote! {
+ #field_ident: *#field_var_ident
+ }
+ });
+
+ quote! {
+ Self::#variant_ident {
+ #(#fields),*
+ }
+ }
+ }
+ syn::Fields::Unnamed(unnamed_fields) => {
+ let variant_ident = &variant.ident;
+
+ let fields = unnamed_fields.unnamed.iter().enumerate().map(
+ |(field_index, _)| {
+ let field_var_ident = format_ident!("field_{field_index}");
+
+ quote! {
+ *#field_var_ident
+ }
+ },
+ );
+
+ quote! {
+ Self::#variant_ident(
+ #(#fields),*
+ )
+ }
+ }
+ };
+
+ let variant_ident = &variant.ident;
+
+ let variant_pattern = match &variant.fields {
+ syn::Fields::Unit => quote! { Self::#variant_ident },
+ syn::Fields::Named(syn::FieldsNamed { named: named_fields, .. }) => {
+ let field_idents = named_fields.iter().map(|field| {
+ let Some(field_ident) = &field.ident else {
+ unreachable!();
+ };
+
+ field_ident
+ });
+
+ quote! { Self::#variant_ident { #(#field_idents),* } }
+ }
+ syn::Fields::Unnamed(syn::FieldsUnnamed {
+ unnamed: unnamed_fields, ..
+ }) => {
+ let field_idents = unnamed_fields
+ .iter()
+ .enumerate()
+ .map(|(field_index, _)| format_ident!("field_{field_index}"));
+
+ quote! { Self::#variant_ident(#(#field_idents),*) }
+ }
+ };
+
+ let field_index_match_arms = match &variant.fields {
+ syn::Fields::Unit => quote! {},
+ syn::Fields::Named(syn::FieldsNamed { named: named_fields, .. }) => {
+ let match_arms =
+ named_fields.iter().enumerate().map(|(field_index, field)| {
+ let Some(field_ident) = &field.ident else {
+ unreachable!();
+ };
+
+ quote! { #field_index => { #field_ident } }
+ });
+
+ quote! { #(#match_arms)* }
+ }
+ syn::Fields::Unnamed(syn::FieldsUnnamed {
+ unnamed: unnamed_fields, ..
+ }) => {
+ let match_arms =
+ unnamed_fields.iter().enumerate().map(|(field_index, _)| {
+ let field_ident = format_ident!("field_{field_index}");
+
+ quote! { #field_index => { #field_ident } }
+ });
+
+ quote! { #(#match_arms)* }
+ }
+ };
+
+ quote! {
+ #engine_crate_path::reflection::EnumVariant {
+ name: #variant_name,
+ fields: #fields,
+ try_write_new_to: |dst, fields| {
+ use std::any::Any;
+ use #engine_crate_path::reflection::EnumVariantWriteNewToError;
+
+ let dst = dst
+ .downcast_mut()
+ .ok_or(EnumVariantWriteNewToError::WrongDstType)?;
+
+ #(#variant_field_vars)*;
+
+ let created = #variant_construction;
+
+ if fields.next().is_some() {
+ return Err(EnumVariantWriteNewToError::TooManyFields);
+ }
+
+ *dst = created;
+
+ Ok(())
+ },
+ try_get_field: |target, field_index| { {
+ #![allow(unreachable_code)]
+
+ use std::any::Any;
+ use #engine_crate_path::reflection::GetError;
+
+ let target = target
+ .downcast_ref::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let #variant_pattern = target else {
+ return Err(GetError::WrongTargetEnumVariant);
+ };
+
+ let field: &dyn Any = match field_index {
+ #field_index_match_arms
+ _ => {
+ return Err(GetError::IndexOutOfBounds);
+ }
+ };
+
+ Ok(field)
+ } },
+ try_get_field_mut: |target, field_index| { {
+ #![allow(unreachable_code)]
+
+ use std::any::Any;
+ use #engine_crate_path::reflection::GetError;
+
+ let target = target
+ .downcast_mut::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let #variant_pattern = target else {
+ return Err(GetError::WrongTargetEnumVariant);
+ };
+
+ let field: &mut dyn Any = match field_index {
+ #field_index_match_arms
+ _ => {
+ return Err(GetError::IndexOutOfBounds);
+ }
+ };
+
+ Ok(field)
+ } },
+ }
+ }
+ })
+}
+
+pub fn generate_enum_variant_field(
+ field: &syn::Field,
+ field_index: usize,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ let field_ident = &field.ident;
+
+ let field_type = &field.ty;
+
+ let field_name = if let Some(field_ident) = field_ident {
+ let field_name = syn::LitStr::new(&field_ident.to_string(), field_ident.span());
+
+ quote! { Some(#field_name) }
+ } else {
+ quote! { None }
+ };
+
+ let get_type_fn_body =
+ { gen_get_optional_type_reflection(field_type, engine_crate_path) };
+
+ quote! {
+ #engine_crate_path::reflection::EnumVariantField {
+ name: #field_name,
+ index: #field_index,
+ type_id: std::any::TypeId::of::<#field_type>(),
+ get_type_name: #engine_crate_path::reflection::FnWithDebug::new(|| {
+ std::any::type_name::<#field_type>()
+ }),
+ get_type: #engine_crate_path::reflection::FnWithDebug::new(|| {
+ #get_type_fn_body
+ }),
+ }
+ }
+}
+
+fn gen_get_optional_type_reflection(
+ field_type: &syn::Type,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ quote! {
+ struct SpecializationTarget<Field>(std::marker::PhantomData<Field>);
+
+ trait FieldHasReflection
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>;
+ }
+
+ trait FieldDoesNotHaveReflection
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>;
+ }
+
+ impl<Field> FieldDoesNotHaveReflection for &SpecializationTarget<Field>
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>
+ {
+ None
+ }
+ }
+
+ impl<Field> FieldHasReflection for SpecializationTarget<Field>
+ where
+ Field: #engine_crate_path::reflection::Reflection
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>
+ {
+ Some(Field::type_reflection())
+ }
+ }
+
+ (&SpecializationTarget::<#field_type>(std::marker::PhantomData))
+ .field_type_reflection()
+ }
+}
diff --git a/engine-macros/src/reflection/field.rs b/engine-macros/src/reflection/field.rs
new file mode 100644
index 0000000..7c50f47
--- /dev/null
+++ b/engine-macros/src/reflection/field.rs
@@ -0,0 +1,100 @@
+use quote::quote;
+
+use crate::reflection::visibility::generate as generate_visibility;
+
+pub struct ReflectionFieldGenOptions<'a>
+{
+ pub field_vis_override: Option<syn::Visibility>,
+ pub gen_get_byte_offset: &'a dyn Fn(&syn::Field) -> proc_macro2::TokenStream,
+}
+
+pub fn generate(
+ field: &syn::Field,
+ field_index: usize,
+ engine_crate_path: &syn::Path,
+ options: ReflectionFieldGenOptions<'_>,
+) -> proc_macro2::TokenStream
+{
+ let field_ident = &field.ident;
+
+ let field_type = &field.ty;
+
+ let field_name = if let Some(field_ident) = field_ident {
+ let field_name = syn::LitStr::new(&field_ident.to_string(), field_ident.span());
+
+ quote! { Some(#field_name) }
+ } else {
+ quote! { None }
+ };
+
+ let field_byte_offset = (options.gen_get_byte_offset)(field);
+
+ let field_vis = options.field_vis_override.as_ref().unwrap_or(&field.vis);
+
+ let field_reflection_vis = generate_visibility(field_vis, &engine_crate_path);
+
+ let get_type_fn_body =
+ { gen_get_optional_type_reflection(field_type, engine_crate_path) };
+
+ quote! {
+ #engine_crate_path::reflection::Field {
+ name: #field_name,
+ index: #field_index,
+ layout: std::alloc::Layout::new::<#field_type>(),
+ byte_offset: #field_byte_offset,
+ type_id: std::any::TypeId::of::<#field_type>(),
+ get_type_name: #engine_crate_path::reflection::FnWithDebug::new(|| {
+ std::any::type_name::<#field_type>()
+ }),
+ get_type: #engine_crate_path::reflection::FnWithDebug::new(|| {
+ #get_type_fn_body
+ }),
+ visibility: #field_reflection_vis
+ }
+ }
+}
+
+fn gen_get_optional_type_reflection(
+ field_type: &syn::Type,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ quote! {
+ struct SpecializationTarget<Field>(std::marker::PhantomData<Field>);
+
+ trait FieldHasReflection
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>;
+ }
+
+ trait FieldDoesNotHaveReflection
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>;
+ }
+
+ impl<Field> FieldDoesNotHaveReflection for &SpecializationTarget<Field>
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>
+ {
+ None
+ }
+ }
+
+ impl<Field> FieldHasReflection for SpecializationTarget<Field>
+ where
+ Field: #engine_crate_path::reflection::Reflection
+ {
+ fn field_type_reflection(&self)
+ -> Option<&'static #engine_crate_path::reflection::Type>
+ {
+ Some(Field::type_reflection())
+ }
+ }
+
+ (&SpecializationTarget::<#field_type>(std::marker::PhantomData))
+ .field_type_reflection()
+ }
+}
diff --git a/engine-macros/src/reflection/options_attr.rs b/engine-macros/src/reflection/options_attr.rs
new file mode 100644
index 0000000..231b969
--- /dev/null
+++ b/engine-macros/src/reflection/options_attr.rs
@@ -0,0 +1,37 @@
+#[derive(Debug, Default)]
+pub struct OptionsAttr
+{
+ pub impl_with_generics:
+ syn::punctuated::Punctuated<syn::AngleBracketedGenericArguments, syn::Token![,]>,
+}
+
+impl syn::parse::Parse for OptionsAttr
+{
+ fn parse(input: syn::parse::ParseStream) -> syn::Result<Self>
+ {
+ let params =
+ input.parse_terminated(|param| param.parse::<syn::Meta>(), syn::Token![,])?;
+
+ let mut impl_with_generics = syn::punctuated::Punctuated::new();
+
+ for param in params {
+ let param = param.require_list()?;
+
+ if !param.path.is_ident("impl_with_generics") {
+ return Err(syn::Error::new_spanned(
+ param,
+ "Unrecognized attribute parameter",
+ ));
+ }
+
+ impl_with_generics = param.parse_args_with(
+ syn::punctuated::Punctuated::<
+ syn::AngleBracketedGenericArguments,
+ syn::Token![,],
+ >::parse_terminated,
+ )?;
+ }
+
+ Ok(Self { impl_with_generics })
+ }
+}
diff --git a/engine-macros/src/reflection/struct_impl.rs b/engine-macros/src/reflection/struct_impl.rs
new file mode 100644
index 0000000..0ba426d
--- /dev/null
+++ b/engine-macros/src/reflection/struct_impl.rs
@@ -0,0 +1,186 @@
+use quote::{format_ident, quote};
+
+use crate::reflection::default_value::gen_get_default_value_fn;
+use crate::reflection::field::{generate as generate_field, ReflectionFieldGenOptions};
+use crate::reflection::options_attr::OptionsAttr;
+use crate::util::find_engine_crate_path;
+
+pub fn generate(input: syn::ItemStruct, options: OptionsAttr)
+ -> proc_macro2::TokenStream
+{
+ let engine_crate_path = find_engine_crate_path().unwrap();
+
+ if input.generics.params.is_empty() {
+ return gen_impl(&input, None, &engine_crate_path);
+ }
+
+ let impls = options
+ .impl_with_generics
+ .into_iter()
+ .map(|generic_args| gen_impl(&input, Some(&generic_args), &engine_crate_path));
+
+ quote! {
+ #(#impls)*
+ }
+}
+
+fn gen_impl(
+ input: &syn::ItemStruct,
+ generic_args: Option<&syn::AngleBracketedGenericArguments>,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ let fields = input.fields.iter().enumerate().map(|(field_index, field)| {
+ generate_field(
+ &field,
+ field_index,
+ &engine_crate_path,
+ ReflectionFieldGenOptions {
+ field_vis_override: None,
+ gen_get_byte_offset: &|field| {
+ if let Some(field_ident) = &field.ident {
+ quote! { std::mem::offset_of!(Self, #field_ident) }
+ } else {
+ quote! { std::mem::offset_of!(Self, #field_index) }
+ }
+ },
+ },
+ )
+ });
+
+ let get_default_value_fn = gen_get_default_value_fn(&input.ident, generic_args);
+
+ let input_ident = &input.ident;
+
+ let generics_type_aliases = input
+ .generics
+ .type_params()
+ .zip(
+ generic_args
+ .iter()
+ .map(|generic_args| &generic_args.args)
+ .flatten()
+ .flat_map(|generic_arg| match generic_arg {
+ syn::GenericArgument::Type(ty) => Some(ty),
+ _ => None,
+ }),
+ )
+ .map(|(type_param, generic_arg_type)| {
+ let type_param_ident = &type_param.ident;
+
+ quote! {
+ type #type_param_ident = #generic_arg_type;
+ }
+ });
+
+ let struct_pattern = match &input.fields {
+ syn::Fields::Unit => quote! { Self },
+ syn::Fields::Named(syn::FieldsNamed { named: named_fields, .. }) => {
+ let field_idents = named_fields.iter().map(|field| {
+ let Some(field_ident) = &field.ident else {
+ unreachable!();
+ };
+
+ field_ident
+ });
+
+ quote! { Self { #(#field_idents),* } }
+ }
+ syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: unnamed_fields, .. }) => {
+ let field_idents = unnamed_fields
+ .iter()
+ .enumerate()
+ .map(|(field_index, _)| format_ident!("field_{field_index}"));
+
+ quote! { Self(#(#field_idents),*) }
+ }
+ };
+
+ let field_index_match_arms = match &input.fields {
+ syn::Fields::Unit => quote! {},
+ syn::Fields::Named(syn::FieldsNamed { named: named_fields, .. }) => {
+ let match_arms =
+ named_fields.iter().enumerate().map(|(field_index, field)| {
+ let Some(field_ident) = &field.ident else {
+ unreachable!();
+ };
+
+ quote! { #field_index => { #field_ident } }
+ });
+
+ quote! { #(#match_arms)* }
+ }
+ syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed: unnamed_fields, .. }) => {
+ let match_arms = unnamed_fields.iter().enumerate().map(|(field_index, _)| {
+ let field_ident = format_ident!("field_{field_index}");
+
+ quote! { #field_index => { #field_ident } }
+ });
+
+ quote! { #(#match_arms)* }
+ }
+ };
+
+ quote! {
+ unsafe impl #engine_crate_path::reflection::Reflection for
+ #input_ident #generic_args
+ {
+ const TYPE_REFLECTION: &#engine_crate_path::reflection::Type =
+ &const {
+ use std::any::Any;
+
+ use #engine_crate_path::reflection::GetError;
+
+ #(#generics_type_aliases)*
+
+ #engine_crate_path::reflection::Type::Struct(
+ #engine_crate_path::reflection::Struct {
+ fields: &[
+ #(#fields),*
+ ],
+ get_default_value: || {
+ #get_default_value_fn
+ },
+ cast_dyn_any: |ptr| ptr.cast::<Self>(),
+ try_get_field: |target, field_index| { {
+ #![allow(unreachable_code)]
+
+ let target = target
+ .downcast_ref::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let #struct_pattern = target;
+
+ let field: &dyn Any = match field_index {
+ #field_index_match_arms
+ _ => {
+ return Err(GetError::IndexOutOfBounds);
+ }
+ };
+
+ Ok(field)
+ } },
+ try_get_field_mut: |target, field_index| { {
+ #![allow(unreachable_code)]
+
+ let target = target
+ .downcast_mut::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let #struct_pattern = target;
+
+ let field: &mut dyn Any = match field_index {
+ #field_index_match_arms
+ _ => {
+ return Err(GetError::IndexOutOfBounds);
+ }
+ };
+
+ Ok(field)
+ } },
+ }
+ )
+ };
+ }
+ }
+}
diff --git a/engine-macros/src/reflection/visibility.rs b/engine-macros/src/reflection/visibility.rs
new file mode 100644
index 0000000..c9e8524
--- /dev/null
+++ b/engine-macros/src/reflection/visibility.rs
@@ -0,0 +1,45 @@
+use quote::quote;
+
+use crate::util::syn_path_to_string;
+
+pub fn generate(
+ visibility: &syn::Visibility,
+ engine_crate_path: &syn::Path,
+) -> proc_macro2::TokenStream
+{
+ match visibility {
+ syn::Visibility::Public(_) => {
+ quote! { #engine_crate_path::reflection::Visibility::Pub }
+ }
+ syn::Visibility::Restricted(vis_restricted) => {
+ let vis_scope = if vis_restricted.in_token.is_some() {
+ let in_path = syn_path_to_string(&vis_restricted.path);
+
+ quote! {
+ #engine_crate_path::reflection::VisibilityScope::In(
+ std::borrow::Cow::Borrowed(#in_path)
+ )
+ }
+ } else {
+ let Some(scope) = vis_restricted.path.get_ident() else {
+ unreachable!();
+ };
+
+ if scope == "crate" {
+ quote! { #engine_crate_path::reflection::VisibilityScope::Crate }
+ } else if scope == "super" {
+ quote! { #engine_crate_path::reflection::VisibilityScope::Super }
+ } else if scope == "self" {
+ quote! { #engine_crate_path::reflection::VisibilityScope::SelfModule }
+ } else {
+ unreachable!();
+ }
+ };
+
+ quote! { #engine_crate_path::reflection::Visibility::PubScoped(#vis_scope) }
+ }
+ syn::Visibility::Inherited => {
+ quote! { #engine_crate_path::reflection::Visibility::Private }
+ }
+ }
+}
diff --git a/engine-macros/src/util.rs b/engine-macros/src/util.rs
new file mode 100644
index 0000000..515b066
--- /dev/null
+++ b/engine-macros/src/util.rs
@@ -0,0 +1,65 @@
+use std::fmt::Write;
+
+use quote::ToTokens;
+
+macro_rules! syn_path {
+ ($first_segment: ident $(::$segment: ident)*) => {
+ ::syn::Path {
+ leading_colon: None,
+ segments: ::syn::punctuated::Punctuated::from_iter([
+ syn_path_segment!($first_segment),
+ $(syn_path_segment!($segment),)*
+ ])
+ }
+ };
+}
+
+macro_rules! syn_path_segment {
+ ($segment: ident) => {
+ ::syn::PathSegment {
+ ident: ::proc_macro2::Ident::new(
+ stringify!($segment),
+ ::proc_macro2::Span::call_site(),
+ ),
+ arguments: ::syn::PathArguments::None,
+ }
+ };
+}
+
+pub fn find_engine_crate_path() -> Option<syn::Path>
+{
+ let cargo_crate_name = std::env::var("CARGO_CRATE_NAME").ok()?;
+ let cargo_pkg_name = std::env::var("CARGO_PKG_NAME").ok()?;
+
+ if cargo_pkg_name == "engine" && cargo_crate_name != "engine" {
+ // Macro is used by a crate example/test/benchmark
+ return Some(syn_path!(engine));
+ }
+
+ if cargo_crate_name == "engine" {
+ return Some(syn_path!(crate));
+ }
+
+ Some(syn_path!(engine))
+}
+
+pub fn syn_path_to_string(path: &syn::Path) -> String
+{
+ let mut output = String::with_capacity(2 + path.segments.len() * 8);
+
+ if let Some(leading_colon) = path.leading_colon {
+ write!(output, "{}", leading_colon.to_token_stream()).unwrap();
+ }
+
+ for (segment, punct) in path.segments.pairs().map(syn::punctuated::Pair::into_tuple) {
+ let segment_ident = &segment.ident;
+
+ write!(output, "{segment_ident}",).unwrap();
+
+ if let Some(punct) = punct {
+ write!(output, "{}", punct.to_token_stream()).unwrap();
+ }
+ }
+
+ output
+}
diff --git a/engine-reflection/Cargo.toml b/engine-reflection/Cargo.toml
new file mode 100644
index 0000000..2f90841
--- /dev/null
+++ b/engine-reflection/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "engine-reflection"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+thiserror = "1.0.49"
+
diff --git a/engine-reflection/src/lib.rs b/engine-reflection/src/lib.rs
new file mode 100644
index 0000000..dedfee7
--- /dev/null
+++ b/engine-reflection/src/lib.rs
@@ -0,0 +1,647 @@
+use std::alloc::Layout;
+use std::any::{type_name, Any, TypeId};
+use std::borrow::Cow;
+use std::ffi::c_void;
+use std::fmt::Debug;
+
+/// Trait implemented by types that support runtime reflection on them.
+///
+/// # Safety
+/// Implementors of this trait must provide accurate reflection information in the
+/// `TYPE_REFLECTION` associated constant and the `type_reflection` and
+/// `get_type_reflection` methods.
+pub unsafe trait Reflection: 'static
+{
+ const TYPE_REFLECTION: &Type;
+
+ fn type_reflection() -> &'static Type
+ where
+ Self: Sized,
+ {
+ Self::TYPE_REFLECTION
+ }
+
+ fn get_type_reflection(&self) -> &'static Type
+ {
+ Self::TYPE_REFLECTION
+ }
+}
+
+/// Trait implemented by enums that support runtime reflection on them.
+///
+/// # Safety
+/// Implementors of this trait must provide accurate reflection information in the
+/// `get_variant_reflection` method.
+pub unsafe trait EnumReflectionExt: Reflection
+{
+ fn get_variant_reflection(&self) -> &'static EnumVariant;
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub enum Type
+{
+ Struct(Struct),
+ Enum(Enum),
+ Array(Array),
+ Slice(Slice),
+ Literal(Literal),
+ Reference(Reference),
+}
+
+impl Type
+{
+ pub const fn as_struct(&self) -> Option<&Struct>
+ {
+ match self {
+ Self::Struct(struct_reflection) => Some(struct_reflection),
+ _ => None,
+ }
+ }
+
+ pub const fn as_enum(&self) -> Option<&Enum>
+ {
+ match self {
+ Self::Enum(enum_reflection) => Some(enum_reflection),
+ _ => None,
+ }
+ }
+
+ #[inline]
+ pub fn default_value(&self) -> Option<Box<dyn Any>>
+ {
+ match self {
+ Self::Struct(struct_type) => struct_type.default_value(),
+ Self::Enum(enum_type) => enum_type.default_value(),
+ Self::Literal(literal_type) => literal_type.default_value(),
+ Self::Array(_) | Self::Slice(_) | Self::Reference(_) => None,
+ }
+ }
+
+ #[inline]
+ pub fn has_default_value(&self) -> bool
+ {
+ match self {
+ Self::Struct(struct_type) => struct_type.has_default_value(),
+ Self::Enum(enum_type) => enum_type.has_default_value(),
+ Self::Literal(literal_type) => literal_type.has_default_value(),
+ Self::Array(_) | Self::Slice(_) | Self::Reference(_) => false,
+ }
+ }
+
+ #[inline]
+ pub fn cast_dyn_any(&self, ptr: *mut c_void) -> Option<*mut dyn Any>
+ {
+ match self {
+ Self::Struct(struct_type) => Some((struct_type.cast_dyn_any)(ptr)),
+ Self::Enum(enum_type) => Some((enum_type.cast_dyn_any)(ptr)),
+ Self::Literal(literal_type) => Some((literal_type.cast_dyn_any)(ptr)),
+ Self::Array(array_type) => Some((array_type.cast_dyn_any)(ptr)),
+ Self::Reference(ref_type) => Some((ref_type.cast_dyn_any)(ptr)),
+ Self::Slice(_) => None,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Struct
+{
+ pub fields: &'static [Field],
+ pub get_default_value: fn() -> Option<DefaultValueFn>,
+ pub cast_dyn_any: CastDynAnyFn,
+ pub try_get_field:
+ fn(target: &dyn Any, field_index: usize) -> Result<&dyn Any, GetError>,
+ pub try_get_field_mut:
+ fn(target: &mut dyn Any, field_index: usize) -> Result<&mut dyn Any, GetError>,
+}
+
+impl Struct
+{
+ #[inline]
+ pub fn default_value(&self) -> Option<Box<dyn Any>>
+ {
+ Some((self.get_default_value)()?())
+ }
+
+ #[inline]
+ pub fn has_default_value(&self) -> bool
+ {
+ (self.get_default_value)().is_some()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Enum
+{
+ /// Enum variants in the same order as in the enum definition.
+ pub variants: &'static [EnumVariant],
+
+ /// The enum only contains unit variants.
+ pub is_unit_only: bool,
+
+ pub get_default_value: fn() -> Option<DefaultValueFn>,
+ pub cast_dyn_any: CastDynAnyFn,
+
+ pub get_variant_index: fn(&dyn Any) -> Option<usize>,
+}
+
+impl Enum
+{
+ #[inline]
+ pub fn default_value(&self) -> Option<Box<dyn Any>>
+ {
+ Some((self.get_default_value)()?())
+ }
+
+ #[inline]
+ pub fn has_default_value(&self) -> bool
+ {
+ (self.get_default_value)().is_some()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct EnumVariant
+{
+ pub name: &'static str,
+
+ /// The fields of this variant. If `None`, this variant is a unit variant.
+ pub fields: Option<EnumVariantFields>,
+
+ pub try_write_new_to: fn(
+ dst: &mut dyn Any,
+ fields: &mut dyn Iterator<Item = Box<dyn Any>>,
+ ) -> Result<(), EnumVariantWriteNewToError>,
+
+ pub try_get_field:
+ fn(target: &dyn Any, field_index: usize) -> Result<&dyn Any, GetError>,
+
+ pub try_get_field_mut:
+ fn(target: &mut dyn Any, field_index: usize) -> Result<&mut dyn Any, GetError>,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum EnumVariantWriteNewToError
+{
+ #[error("Destination must have the same type as the variant's enum")]
+ WrongDstType,
+
+ #[error("Too few fields are specified")]
+ TooFewFields,
+
+ #[error("Too many fields are specified")]
+ TooManyFields,
+
+ #[error("A field has the wrong type")]
+ WrongFieldType,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum GetError
+{
+ #[error("Target is not the correct type")]
+ WrongTargetType,
+
+ #[error("Target is not the correct enum variant")]
+ WrongTargetEnumVariant,
+
+ #[error("Index is out of bounds")]
+ IndexOutOfBounds,
+}
+
+#[derive(Debug, Clone)]
+pub enum EnumVariantFields
+{
+ Named
+ {
+ fields: &'static [EnumVariantField]
+ },
+ Unnamed
+ {
+ fields: &'static [EnumVariantField]
+ },
+}
+
+impl EnumVariantFields
+{
+ pub fn fields(&self) -> &'static [EnumVariantField]
+ {
+ match self {
+ Self::Named { fields } => fields,
+ Self::Unnamed { fields } => fields,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct EnumVariantField
+{
+ pub name: Option<&'static str>,
+ pub index: usize,
+ pub type_id: TypeId,
+ pub get_type_name: FnWithDebug<&'static str>,
+ pub get_type: FnWithDebug<Option<&'static Type>>,
+}
+
+impl EnumVariantField
+{
+ pub fn type_name(&self) -> &'static str
+ {
+ self.get_type_name.get()
+ }
+
+ pub fn type_reflection(&self) -> Option<&'static Type>
+ {
+ self.get_type.get()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Field
+{
+ pub name: Option<&'static str>,
+ pub index: usize,
+ pub layout: Layout,
+
+ /// Byte offset to the field relative to the start of the type containing it.
+ pub byte_offset: usize,
+
+ pub type_id: TypeId,
+ pub get_type_name: FnWithDebug<&'static str>,
+ pub get_type: FnWithDebug<Option<&'static Type>>,
+ pub visibility: Visibility,
+}
+
+impl Field
+{
+ pub fn type_name(&self) -> &'static str
+ {
+ self.get_type_name.get()
+ }
+
+ pub fn type_reflection(&self) -> Option<&'static Type>
+ {
+ self.get_type.get()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Array
+{
+ pub item_type: &'static Type,
+ pub item_layout: Layout,
+ pub get_item_type_name: FnWithDebug<&'static str>,
+ pub length: usize,
+ pub cast_dyn_any: CastDynAnyFn,
+ pub try_get_item: fn(target: &dyn Any, index: usize) -> Result<&dyn Any, GetError>,
+ pub try_get_item_mut:
+ fn(target: &mut dyn Any, index: usize) -> Result<&mut dyn Any, GetError>,
+}
+
+impl Array
+{
+ pub fn item_type_name(&self) -> &'static str
+ {
+ self.get_item_type_name.get()
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Slice
+{
+ pub item_type: &'static Type,
+ pub item_layout: Layout,
+ pub get_item_type_name: FnWithDebug<&'static str>,
+ pub try_get_item: fn(target: &dyn Any, index: usize) -> Result<&dyn Any, GetError>,
+ pub try_get_len: fn(target: &dyn Any) -> Option<usize>,
+}
+
+impl Slice
+{
+ pub fn item_type_name(&self) -> &'static str
+ {
+ self.get_item_type_name.get()
+ }
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Literal
+{
+ pub layout: Layout,
+ pub type_id: TypeId,
+ pub ty: LiteralType,
+ pub type_name: fn() -> &'static str,
+ pub get_default_value: fn() -> Option<DefaultValueFn>,
+ pub cast_dyn_any: CastDynAnyFn,
+}
+
+impl Literal
+{
+ #[inline]
+ pub fn default_value(&self) -> Option<Box<dyn Any>>
+ {
+ Some((self.get_default_value)()?())
+ }
+
+ #[inline]
+ pub fn has_default_value(&self) -> bool
+ {
+ (self.get_default_value)().is_some()
+ }
+}
+
+#[derive(Debug)]
+pub struct Reference
+{
+ pub ty: &'static Type,
+ pub cast_dyn_any: CastDynAnyFn,
+ pub try_deref: fn(target: &dyn Any) -> Option<&dyn Any>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum LiteralType
+{
+ U8,
+ I8,
+ U16,
+ I16,
+ U32,
+ I32,
+ U64,
+ I64,
+ U128,
+ I128,
+ F32,
+ F64,
+ Usize,
+ Isize,
+ Bool,
+ Str,
+}
+
+#[derive(Debug, Clone)]
+pub enum Visibility
+{
+ Pub,
+ PubScoped(VisibilityScope),
+ Private,
+}
+
+#[derive(Debug, Clone)]
+pub enum VisibilityScope
+{
+ Crate,
+ Super,
+ SelfModule,
+ In(Cow<'static, str>),
+}
+
+macro_rules! impl_reflection_for_literals {
+ ($(($literal_type: ident, $literal: ty)),*) => {
+ $(
+ unsafe impl Reflection for $literal
+ {
+ const TYPE_REFLECTION: &Type = &Type::Literal(Literal {
+ layout: Layout::new::<$literal>(),
+ type_id: TypeId::of::<$literal>(),
+ ty: LiteralType::$literal_type,
+ type_name: || type_name::<$literal>(),
+ get_default_value: || Some(|| Box::new(Self::default())),
+ cast_dyn_any: |ptr| ptr.cast::<Self>()
+ });
+ }
+ )*
+ };
+}
+
+impl_reflection_for_literals!(
+ (U8, u8),
+ (I8, i8),
+ (U16, u16),
+ (I16, i16),
+ (U32, u32),
+ (I32, i32),
+ (U64, u64),
+ (I64, i64),
+ (U128, u128),
+ (I128, i128),
+ (F32, f32),
+ (F64, f64),
+ (Usize, usize),
+ (Isize, isize),
+ (Bool, bool),
+ (Str, &'static str)
+);
+
+unsafe impl<T: Reflection, const LEN: usize> Reflection for [T; LEN]
+{
+ const TYPE_REFLECTION: &Type = &Type::Array(Array {
+ item_type: T::TYPE_REFLECTION,
+ item_layout: Layout::new::<T>(),
+ get_item_type_name: FnWithDebug::new(|| type_name::<T>()),
+ length: LEN,
+ cast_dyn_any: |ptr| ptr.cast::<Self>(),
+ try_get_item: |target, index| {
+ let target = target
+ .downcast_ref::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let item = target.get(index).ok_or(GetError::IndexOutOfBounds)?;
+
+ Ok(item)
+ },
+ try_get_item_mut: |target, index| {
+ let target = target
+ .downcast_mut::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let item = target.get_mut(index).ok_or(GetError::IndexOutOfBounds)?;
+
+ Ok(item)
+ },
+ });
+}
+
+unsafe impl<T: Reflection> Reflection for &'static [T]
+{
+ const TYPE_REFLECTION: &Type = &Type::Slice(Slice {
+ item_type: T::TYPE_REFLECTION,
+ item_layout: Layout::new::<T>(),
+ get_item_type_name: FnWithDebug::new(|| type_name::<T>()),
+ try_get_item: |target, index| {
+ let target = target
+ .downcast_ref::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let item = target.get(index).ok_or(GetError::IndexOutOfBounds)?;
+
+ Ok(item)
+ },
+ try_get_len: |target| {
+ let target = target.downcast_ref::<Self>()?;
+
+ Some(target.len())
+ },
+ });
+}
+
+unsafe impl<T: Reflection> Reflection for &'static T
+{
+ const TYPE_REFLECTION: &Type = &Type::Reference(Reference {
+ ty: T::TYPE_REFLECTION,
+ cast_dyn_any: |ptr| ptr.cast::<Self>(),
+ try_deref: |target| {
+ let target = target.downcast_ref::<Self>()?;
+
+ Some(*target)
+ },
+ });
+}
+
+unsafe impl<T: Reflection> Reflection for Option<T>
+{
+ const TYPE_REFLECTION: &Type = &Type::Enum(Enum {
+ variants: &[
+ EnumVariant {
+ name: "Some",
+ fields: Some(EnumVariantFields::Unnamed {
+ fields: &[EnumVariantField {
+ name: None,
+ index: 0,
+ type_id: TypeId::of::<T>(),
+ get_type_name: FnWithDebug::new(|| type_name::<T>()),
+ get_type: FnWithDebug::new(|| Some(T::type_reflection())),
+ }],
+ }),
+ try_write_new_to: |dst, fields| {
+ let dst = dst
+ .downcast_mut::<Self>()
+ .ok_or(EnumVariantWriteNewToError::WrongDstType)?;
+
+ let value_field = fields
+ .next()
+ .ok_or(EnumVariantWriteNewToError::TooFewFields)?
+ .downcast::<T>()
+ .map_err(|_| EnumVariantWriteNewToError::WrongFieldType)?;
+
+ if fields.next().is_some() {
+ return Err(EnumVariantWriteNewToError::TooManyFields);
+ }
+
+ *dst = Some(*value_field);
+
+ Ok(())
+ },
+ try_get_field: |target, field_index| {
+ if field_index != 0 {
+ return Err(GetError::IndexOutOfBounds);
+ }
+
+ let target = target
+ .downcast_ref::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let Some(value_field) = target else {
+ return Err(GetError::WrongTargetEnumVariant);
+ };
+
+ Ok(value_field)
+ },
+ try_get_field_mut: |target, field_index| {
+ if field_index != 0 {
+ return Err(GetError::IndexOutOfBounds);
+ }
+
+ let target = target
+ .downcast_mut::<Self>()
+ .ok_or(GetError::WrongTargetType)?;
+
+ let Some(value_field) = target else {
+ return Err(GetError::WrongTargetEnumVariant);
+ };
+
+ Ok(value_field)
+ },
+ },
+ EnumVariant {
+ name: "None",
+ fields: None,
+ try_write_new_to: |dst, fields| {
+ let dst = dst
+ .downcast_mut::<Self>()
+ .ok_or(EnumVariantWriteNewToError::WrongDstType)?;
+
+ if fields.next().is_some() {
+ return Err(EnumVariantWriteNewToError::TooManyFields);
+ }
+
+ *dst = None;
+
+ Ok(())
+ },
+ try_get_field: |_, _| Err(GetError::IndexOutOfBounds),
+ try_get_field_mut: |_, _| Err(GetError::IndexOutOfBounds),
+ },
+ ],
+ is_unit_only: false,
+ get_default_value: || Some(|| Box::new(Option::<T>::None)),
+ cast_dyn_any: |ptr| ptr.cast::<Self>(),
+ get_variant_index: |target| {
+ let target = target.downcast_ref::<Self>()?;
+
+ Some(match target {
+ Some(_) => 0,
+ None => 1,
+ })
+ },
+ });
+}
+
+unsafe impl<T: Reflection> EnumReflectionExt for Option<T>
+{
+ fn get_variant_reflection(&self) -> &'static EnumVariant
+ {
+ let ty = unsafe { Self::type_reflection().as_enum().unwrap_unchecked() };
+
+ match self {
+ Some(_) => &ty.variants[0],
+ None => &ty.variants[1],
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct FnWithDebug<Value>
+{
+ func: fn() -> Value,
+}
+
+impl<Value> FnWithDebug<Value>
+{
+ pub const fn new(func: fn() -> Value) -> Self
+ {
+ Self { func }
+ }
+
+ pub fn get(&self) -> Value
+ {
+ (self.func)()
+ }
+}
+
+impl<Value: Debug> Debug for FnWithDebug<Value>
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter
+ .debug_tuple("FnWithDebug")
+ .field(&self.get())
+ .finish()
+ }
+}
+
+type DefaultValueFn = fn() -> Box<dyn Any>;
+
+type CastDynAnyFn = fn(*mut c_void) -> *mut dyn Any;
diff --git a/engine/Cargo.toml b/engine/Cargo.toml
index f6cd5cf..322e6c3 100644
--- a/engine/Cargo.toml
+++ b/engine/Cargo.toml
@@ -4,17 +4,61 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-glfw = { path = "../glfw", features = ["opengl"] }
+glutin = "0.32.3"
+raw-window-handle = "0.6.2"
thiserror = "1.0.49"
-gl = "0.14.0"
bitflags = "2.4.0"
tracing = "0.1.39"
seq-macro = "0.3.5"
paste = "1.0.14"
-ecs = { path = "../ecs" }
-util-macros = { path = "../util-macros" }
+safer-ffi = "0.1.13"
+crossbeam-queue = "0.3.12"
+parking_lot = "0.12.3"
+circular-buffer = "2.0.0"
+intmap = "3.1.3"
+engine-macros = { workspace = true }
+engine-ecs = { workspace = true }
+engine-reflection = { workspace = true }
+util-macros = { workspace = true }
+opengl-bindings = { workspace = true }
-[dependencies.image]
-version = "0.24.7"
+[dependencies.winit]
+version = "0.30.11"
default-features = false
-features = ["png", "jpeg"]
+features = ["rwh_06", "wayland", "wayland-dlopen", "x11"]
+
+[dependencies.image_rs]
+version = "0.25.10"
+default-features = false
+features = ["png", "jpeg", "ico"]
+package = "image"
+
+[dependencies.zerocopy]
+version = "0.8.42"
+default-features = false
+features = ["derive"]
+
+[dependencies.shader-slang]
+git = "https://github.com/HampusMat/slang-rs"
+branch = "prebuilt-and-artifacts"
+
+[dependencies.dear-imgui-rs]
+# This alpha release has a fix for linking with libstdc++ on non-msvc Windows targets.
+# When a stable release with this fix is available, it should be used instead
+version = "=0.16.0-alpha.1"
+features = ["freetype"]
+
+[build-dependencies]
+build-rs = "0.3.4"
+serde = { version = "1.0.228", features = ["derive"] }
+serde_json = "1.0.149"
+
+[dev-dependencies.criterion]
+version = "0.5.1"
+default-features = false
+features = ["cargo_bench_support"]
+
+[[bench]]
+name = "wavefront_obj"
+path = "benches/file_format/wavefront_obj.rs"
+harness = false
diff --git a/engine/benches/file_format/wavefront_obj.rs b/engine/benches/file_format/wavefront_obj.rs
new file mode 100644
index 0000000..a6b34b2
--- /dev/null
+++ b/engine/benches/file_format/wavefront_obj.rs
@@ -0,0 +1,17 @@
+use std::hint::black_box;
+
+use criterion::{criterion_group, criterion_main, Criterion};
+
+fn bench_parsing(criterion: &mut Criterion)
+{
+ criterion.bench_function("Parsing", |bencher| {
+ bencher.iter_with_large_drop(|| {
+ engine::file_format::wavefront::obj::parse(black_box(include_str!(
+ "../res/teapot.obj"
+ )))
+ })
+ });
+}
+
+criterion_group!(benches, bench_parsing);
+criterion_main!(benches);
diff --git a/engine/benches/res/teapot.obj b/engine/benches/res/teapot.obj
new file mode 100644
index 0000000..bc2eb67
--- /dev/null
+++ b/engine/benches/res/teapot.obj
@@ -0,0 +1,18362 @@
+mtllib res/teapot.mtl
+o teapot
+v 0.000000 2.400000 -1.400000
+v 0.229712 2.400000 -1.381970
+v 0.227403 2.435440 -1.368070
+v 0.000000 2.435440 -1.385930
+v 0.446656 2.400000 -1.329660
+v 0.442166 2.435440 -1.316300
+v 0.648144 2.400000 -1.245780
+v 0.641628 2.435440 -1.233250
+v 0.831488 2.400000 -1.132990
+v 0.823129 2.435440 -1.121600
+v 0.994000 2.400000 -0.994000
+v 0.984007 2.435440 -0.984007
+v 1.132990 2.400000 -0.831488
+v 1.121600 2.435440 -0.823129
+v 1.245780 2.400000 -0.648144
+v 1.233250 2.435440 -0.641628
+v 1.329660 2.400000 -0.446656
+v 1.316300 2.435440 -0.442166
+v 1.381970 2.400000 -0.229712
+v 1.368070 2.435440 -0.227403
+v 1.400000 2.400000 -0.000000
+v 1.385930 2.435440 -0.000000
+v 1.381970 2.400000 0.229712
+v 1.368070 2.435440 0.227403
+v 1.329660 2.400000 0.446656
+v 1.316300 2.435440 0.442166
+v 1.245780 2.400000 0.648144
+v 1.233250 2.435440 0.641628
+v 1.132990 2.400000 0.831488
+v 1.121600 2.435440 0.823129
+v 0.994000 2.400000 0.994000
+v 0.984007 2.435440 0.984007
+v 0.831488 2.400000 1.132990
+v 0.823129 2.435440 1.121600
+v 0.648144 2.400000 1.245780
+v 0.641628 2.435440 1.233250
+v 0.446656 2.400000 1.329660
+v 0.442166 2.435440 1.316300
+v 0.229712 2.400000 1.381970
+v 0.227403 2.435440 1.368070
+v 0.000000 2.400000 1.400000
+v 0.000000 2.435440 1.385930
+v -0.229712 2.400000 1.381970
+v -0.227403 2.435440 1.368070
+v -0.446656 2.400000 1.329660
+v -0.442166 2.435440 1.316300
+v -0.648144 2.400000 1.245780
+v -0.641628 2.435440 1.233250
+v -0.831488 2.400000 1.132990
+v -0.823129 2.435440 1.121600
+v -0.994000 2.400000 0.994000
+v -0.984007 2.435440 0.984007
+v -1.132990 2.400000 0.831488
+v -1.121600 2.435440 0.823129
+v -1.245780 2.400000 0.648144
+v -1.233250 2.435440 0.641628
+v -1.329660 2.400000 0.446656
+v -1.316300 2.435440 0.442166
+v -1.381970 2.400000 0.229712
+v -1.368070 2.435440 0.227403
+v -1.400000 2.400000 -0.000000
+v -1.385930 2.435440 -0.000000
+v -1.381970 2.400000 -0.229712
+v -1.368070 2.435440 -0.227403
+v -1.329660 2.400000 -0.446656
+v -1.316300 2.435440 -0.442166
+v -1.245780 2.400000 -0.648144
+v -1.233250 2.435440 -0.641628
+v -1.132990 2.400000 -0.831488
+v -1.121600 2.435440 -0.823129
+v -0.994000 2.400000 -0.994000
+v -0.984007 2.435440 -0.984007
+v -0.831488 2.400000 -1.132990
+v -0.823129 2.435440 -1.121600
+v -0.648144 2.400000 -1.245780
+v -0.641628 2.435440 -1.233250
+v -0.446656 2.400000 -1.329660
+v -0.442166 2.435440 -1.316300
+v -0.229712 2.400000 -1.381970
+v -0.227403 2.435440 -1.368070
+v 0.000000 2.463000 -1.380400
+v -0.226496 2.463000 -1.362620
+v 0.226496 2.463000 -1.362620
+v 0.440403 2.463000 -1.311050
+v 0.639070 2.463000 -1.228340
+v 0.819847 2.463000 -1.117130
+v 0.980084 2.463000 -0.980084
+v 1.117130 2.463000 -0.819847
+v 1.228340 2.463000 -0.639070
+v 1.311050 2.463000 -0.440403
+v 1.362620 2.463000 -0.226496
+v 1.380400 2.463000 -0.000000
+v 1.362620 2.463000 0.226496
+v 1.311050 2.463000 0.440403
+v 1.228340 2.463000 0.639070
+v 1.117130 2.463000 0.819847
+v 0.980084 2.463000 0.980084
+v 0.819847 2.463000 1.117130
+v 0.639070 2.463000 1.228340
+v 0.440403 2.463000 1.311050
+v 0.226496 2.463000 1.362620
+v 0.000000 2.463000 1.380400
+v -0.226496 2.463000 1.362620
+v -0.440403 2.463000 1.311050
+v -0.639070 2.463000 1.228340
+v -0.819847 2.463000 1.117130
+v -0.980084 2.463000 0.980084
+v -1.117130 2.463000 0.819847
+v -1.228340 2.463000 0.639070
+v -1.311050 2.463000 0.440403
+v -1.362620 2.463000 0.226496
+v -1.380400 2.463000 -0.000000
+v -1.362620 2.463000 -0.226496
+v -1.311050 2.463000 -0.440403
+v -1.228340 2.463000 -0.639070
+v -1.117130 2.463000 -0.819847
+v -0.980084 2.463000 -0.980084
+v -0.819847 2.463000 -1.117130
+v -0.639070 2.463000 -1.228340
+v -0.440403 2.463000 -1.311050
+v -0.226795 2.482690 -1.364420
+v -0.440985 2.482690 -1.312780
+v 0.000000 2.482690 -1.382220
+v 0.226795 2.482690 -1.364420
+v 0.440985 2.482690 -1.312780
+v 0.639915 2.482690 -1.229960
+v 0.820931 2.482690 -1.118610
+v 0.981380 2.482690 -0.981380
+v 1.118610 2.482690 -0.820931
+v 1.229960 2.482690 -0.639915
+v 1.312780 2.482690 -0.440985
+v 1.364420 2.482690 -0.226795
+v 1.382220 2.482690 -0.000000
+v 1.364420 2.482690 0.226795
+v 1.312780 2.482690 0.440985
+v 1.229960 2.482690 0.639915
+v 1.118610 2.482690 0.820931
+v 0.981380 2.482690 0.981380
+v 0.820931 2.482690 1.118610
+v 0.639915 2.482690 1.229960
+v 0.440985 2.482690 1.312780
+v 0.226795 2.482690 1.364420
+v 0.000000 2.482690 1.382220
+v -0.226795 2.482690 1.364420
+v -0.440985 2.482690 1.312780
+v -0.639915 2.482690 1.229960
+v -0.820931 2.482690 1.118610
+v -0.981380 2.482690 0.981380
+v -1.118610 2.482690 0.820931
+v -1.229960 2.482690 0.639915
+v -1.312780 2.482690 0.440985
+v -1.364420 2.482690 0.226795
+v -1.382220 2.482690 -0.000000
+v -1.364420 2.482690 -0.226795
+v -1.312780 2.482690 -0.440985
+v -1.229960 2.482690 -0.639915
+v -1.118610 2.482690 -0.820931
+v -0.981380 2.482690 -0.981380
+v -0.820931 2.482690 -1.118610
+v -0.639915 2.482690 -1.229960
+v -0.443529 2.494500 -1.320360
+v -0.643607 2.494500 -1.237060
+v -0.228104 2.494500 -1.372290
+v 0.000000 2.494500 -1.390200
+v 0.228104 2.494500 -1.372290
+v 0.443529 2.494500 -1.320360
+v 0.643607 2.494500 -1.237060
+v 0.825668 2.494500 -1.125060
+v 0.987042 2.494500 -0.987042
+v 1.125060 2.494500 -0.825668
+v 1.237060 2.494500 -0.643607
+v 1.320360 2.494500 -0.443529
+v 1.372290 2.494500 -0.228104
+v 1.390200 2.494500 -0.000000
+v 1.372290 2.494500 0.228104
+v 1.320360 2.494500 0.443529
+v 1.237060 2.494500 0.643607
+v 1.125060 2.494500 0.825668
+v 0.987042 2.494500 0.987042
+v 0.825668 2.494500 1.125060
+v 0.643607 2.494500 1.237060
+v 0.443529 2.494500 1.320360
+v 0.228104 2.494500 1.372290
+v 0.000000 2.494500 1.390200
+v -0.228104 2.494500 1.372290
+v -0.443529 2.494500 1.320360
+v -0.643607 2.494500 1.237060
+v -0.825668 2.494500 1.125060
+v -0.987042 2.494500 0.987042
+v -1.125060 2.494500 0.825668
+v -1.237060 2.494500 0.643607
+v -1.320360 2.494500 0.443529
+v -1.372290 2.494500 0.228104
+v -1.390200 2.494500 -0.000000
+v -1.372290 2.494500 -0.228104
+v -1.320360 2.494500 -0.443529
+v -1.237060 2.494500 -0.643607
+v -1.125060 2.494500 -0.825668
+v -0.987042 2.494500 -0.987042
+v -0.825668 2.494500 -1.125060
+v -0.649591 2.498440 -1.248560
+v -0.833344 2.498440 -1.135520
+v -0.447653 2.498440 -1.332630
+v -0.230225 2.498440 -1.385050
+v 0.000000 2.498440 -1.403120
+v 0.230225 2.498440 -1.385050
+v 0.447653 2.498440 -1.332630
+v 0.649591 2.498440 -1.248560
+v 0.833344 2.498440 -1.135520
+v 0.996219 2.498440 -0.996219
+v 1.135520 2.498440 -0.833344
+v 1.248560 2.498440 -0.649591
+v 1.332630 2.498440 -0.447653
+v 1.385050 2.498440 -0.230225
+v 1.403120 2.498440 -0.000000
+v 1.385050 2.498440 0.230225
+v 1.332630 2.498440 0.447653
+v 1.248560 2.498440 0.649591
+v 1.135520 2.498440 0.833344
+v 0.996219 2.498440 0.996219
+v 0.833344 2.498440 1.135520
+v 0.649591 2.498440 1.248560
+v 0.447653 2.498440 1.332630
+v 0.230225 2.498440 1.385050
+v 0.000000 2.498440 1.403120
+v -0.230225 2.498440 1.385050
+v -0.447653 2.498440 1.332630
+v -0.649591 2.498440 1.248560
+v -0.833344 2.498440 1.135520
+v -0.996219 2.498440 0.996219
+v -1.135520 2.498440 0.833344
+v -1.248560 2.498440 0.649591
+v -1.332630 2.498440 0.447653
+v -1.385050 2.498440 0.230225
+v -1.403120 2.498440 -0.000000
+v -1.385050 2.498440 -0.230225
+v -1.332630 2.498440 -0.447653
+v -1.248560 2.498440 -0.649591
+v -1.135520 2.498440 -0.833344
+v -0.996219 2.498440 -0.996219
+v -0.843248 2.494500 -1.149020
+v -1.008060 2.494500 -1.008060
+v -0.657311 2.494500 -1.263390
+v -0.452973 2.494500 -1.348470
+v -0.232961 2.494500 -1.401510
+v 0.000000 2.494500 -1.419800
+v 0.232961 2.494500 -1.401510
+v 0.452973 2.494500 -1.348470
+v 0.657311 2.494500 -1.263390
+v 0.843248 2.494500 -1.149020
+v 1.008060 2.494500 -1.008060
+v 1.149020 2.494500 -0.843248
+v 1.263390 2.494500 -0.657311
+v 1.348470 2.494500 -0.452973
+v 1.401510 2.494500 -0.232961
+v 1.419800 2.494500 -0.000000
+v 1.401510 2.494500 0.232961
+v 1.348470 2.494500 0.452973
+v 1.263390 2.494500 0.657311
+v 1.149020 2.494500 0.843248
+v 1.008060 2.494500 1.008060
+v 0.843248 2.494500 1.149020
+v 0.657311 2.494500 1.263390
+v 0.452973 2.494500 1.348470
+v 0.232961 2.494500 1.401510
+v 0.000000 2.494500 1.419800
+v -0.232961 2.494500 1.401510
+v -0.452973 2.494500 1.348470
+v -0.657311 2.494500 1.263390
+v -0.843248 2.494500 1.149020
+v -1.008060 2.494500 1.008060
+v -1.149020 2.494500 0.843248
+v -1.263390 2.494500 0.657311
+v -1.348470 2.494500 0.452973
+v -1.401510 2.494500 0.232961
+v -1.419800 2.494500 -0.000000
+v -1.401510 2.494500 -0.232961
+v -1.348470 2.494500 -0.452973
+v -1.263390 2.494500 -0.657311
+v -1.149020 2.494500 -0.843248
+v -1.021710 2.482690 -1.021710
+v -1.164570 2.482690 -0.854666
+v -0.854666 2.482690 -1.164570
+v -0.666211 2.482690 -1.280500
+v -0.459107 2.482690 -1.366730
+v -0.236115 2.482690 -1.420490
+v 0.000000 2.482690 -1.439020
+v 0.236115 2.482690 -1.420490
+v 0.459107 2.482690 -1.366730
+v 0.666211 2.482690 -1.280500
+v 0.854666 2.482690 -1.164570
+v 1.021710 2.482690 -1.021710
+v 1.164570 2.482690 -0.854666
+v 1.280500 2.482690 -0.666211
+v 1.366730 2.482690 -0.459107
+v 1.420490 2.482690 -0.236115
+v 1.439020 2.482690 -0.000000
+v 1.420490 2.482690 0.236115
+v 1.366730 2.482690 0.459107
+v 1.280500 2.482690 0.666211
+v 1.164570 2.482690 0.854666
+v 1.021710 2.482690 1.021710
+v 0.854666 2.482690 1.164570
+v 0.666211 2.482690 1.280500
+v 0.459107 2.482690 1.366730
+v 0.236115 2.482690 1.420490
+v 0.000000 2.482690 1.439020
+v -0.236115 2.482690 1.420490
+v -0.459107 2.482690 1.366730
+v -0.666211 2.482690 1.280500
+v -0.854666 2.482690 1.164570
+v -1.021710 2.482690 1.021710
+v -1.164570 2.482690 0.854666
+v -1.280500 2.482690 0.666211
+v -1.366730 2.482690 0.459107
+v -1.420490 2.482690 0.236115
+v -1.439020 2.482690 -0.000000
+v -1.420490 2.482690 -0.236115
+v -1.366730 2.482690 -0.459107
+v -1.280500 2.482690 -0.666211
+v -1.181230 2.463000 -0.866886
+v -1.298810 2.463000 -0.675736
+v -1.036320 2.463000 -1.036320
+v -0.866886 2.463000 -1.181230
+v -0.675736 2.463000 -1.298810
+v -0.465671 2.463000 -1.386270
+v -0.239491 2.463000 -1.440800
+v 0.000000 2.463000 -1.459600
+v 0.239491 2.463000 -1.440800
+v 0.465671 2.463000 -1.386270
+v 0.675736 2.463000 -1.298810
+v 0.866886 2.463000 -1.181230
+v 1.036320 2.463000 -1.036320
+v 1.181230 2.463000 -0.866886
+v 1.298810 2.463000 -0.675736
+v 1.386270 2.463000 -0.465671
+v 1.440800 2.463000 -0.239491
+v 1.459600 2.463000 -0.000000
+v 1.440800 2.463000 0.239491
+v 1.386270 2.463000 0.465671
+v 1.298810 2.463000 0.675736
+v 1.181230 2.463000 0.866886
+v 1.036320 2.463000 1.036320
+v 0.866886 2.463000 1.181230
+v 0.675736 2.463000 1.298810
+v 0.465671 2.463000 1.386270
+v 0.239491 2.463000 1.440800
+v 0.000000 2.463000 1.459600
+v -0.239491 2.463000 1.440800
+v -0.465671 2.463000 1.386270
+v -0.675736 2.463000 1.298810
+v -0.866886 2.463000 1.181230
+v -1.036320 2.463000 1.036320
+v -1.181230 2.463000 0.866886
+v -1.298810 2.463000 0.675736
+v -1.386270 2.463000 0.465671
+v -1.440800 2.463000 0.239491
+v -1.459600 2.463000 -0.000000
+v -1.440800 2.463000 -0.239491
+v -1.386270 2.463000 -0.465671
+v -1.317250 2.435440 -0.685331
+v -1.405950 2.435440 -0.472283
+v -1.198000 2.435440 -0.879195
+v -1.051030 2.435440 -1.051030
+v -0.879195 2.435440 -1.198000
+v -0.685331 2.435440 -1.317250
+v -0.472283 2.435440 -1.405950
+v -0.242892 2.435440 -1.461260
+v 0.000000 2.435440 -1.480330
+v 0.242892 2.435440 -1.461260
+v 0.472283 2.435440 -1.405950
+v 0.685331 2.435440 -1.317250
+v 0.879195 2.435440 -1.198000
+v 1.051030 2.435440 -1.051030
+v 1.198000 2.435440 -0.879195
+v 1.317250 2.435440 -0.685331
+v 1.405950 2.435440 -0.472283
+v 1.461260 2.435440 -0.242892
+v 1.480330 2.435440 -0.000000
+v 1.461260 2.435440 0.242892
+v 1.405950 2.435440 0.472283
+v 1.317250 2.435440 0.685331
+v 1.198000 2.435440 0.879195
+v 1.051030 2.435440 1.051030
+v 0.879195 2.435440 1.198000
+v 0.685331 2.435440 1.317250
+v 0.472283 2.435440 1.405950
+v 0.242892 2.435440 1.461260
+v 0.000000 2.435440 1.480330
+v -0.242892 2.435440 1.461260
+v -0.472283 2.435440 1.405950
+v -0.685331 2.435440 1.317250
+v -0.879195 2.435440 1.198000
+v -1.051030 2.435440 1.051030
+v -1.198000 2.435440 0.879195
+v -1.317250 2.435440 0.685331
+v -1.405950 2.435440 0.472283
+v -1.461260 2.435440 0.242892
+v -1.480330 2.435440 -0.000000
+v -1.461260 2.435440 -0.242892
+v -1.424640 2.400000 -0.478560
+v -1.480680 2.400000 -0.246120
+v -1.334760 2.400000 -0.694440
+v -1.213920 2.400000 -0.890880
+v -1.065000 2.400000 -1.065000
+v -0.890880 2.400000 -1.213920
+v -0.694440 2.400000 -1.334760
+v -0.478560 2.400000 -1.424640
+v -0.246120 2.400000 -1.480680
+v 0.000000 2.400000 -1.500000
+v 0.246120 2.400000 -1.480680
+v 0.478560 2.400000 -1.424640
+v 0.694440 2.400000 -1.334760
+v 0.890880 2.400000 -1.213920
+v 1.065000 2.400000 -1.065000
+v 1.213920 2.400000 -0.890880
+v 1.334760 2.400000 -0.694440
+v 1.424640 2.400000 -0.478560
+v 1.480680 2.400000 -0.246120
+v 1.500000 2.400000 -0.000000
+v 1.480680 2.400000 0.246120
+v 1.424640 2.400000 0.478560
+v 1.334760 2.400000 0.694440
+v 1.213920 2.400000 0.890880
+v 1.065000 2.400000 1.065000
+v 0.890880 2.400000 1.213920
+v 0.694440 2.400000 1.334760
+v 0.478560 2.400000 1.424640
+v 0.246120 2.400000 1.480680
+v 0.000000 2.400000 1.500000
+v -0.246120 2.400000 1.480680
+v -0.478560 2.400000 1.424640
+v -0.694440 2.400000 1.334760
+v -0.890880 2.400000 1.213920
+v -1.065000 2.400000 1.065000
+v -1.213920 2.400000 0.890880
+v -1.334760 2.400000 0.694440
+v -1.424640 2.400000 0.478560
+v -1.480680 2.400000 0.246120
+v -1.500000 2.400000 -0.000000
+v -1.554470 2.242580 -0.258385
+v -1.574750 2.242580 -0.000000
+v -1.495630 2.242580 -0.502408
+v -1.401280 2.242570 -0.729046
+v -1.274410 2.242580 -0.935276
+v -1.118070 2.242580 -1.118070
+v -0.935276 2.242580 -1.274410
+v -0.729046 2.242580 -1.401280
+v -0.502408 2.242580 -1.495630
+v -0.258385 2.242580 -1.554470
+v 0.000000 2.242580 -1.574750
+v 0.258385 2.242580 -1.554470
+v 0.502408 2.242580 -1.495630
+v 0.729046 2.242570 -1.401280
+v 0.935276 2.242580 -1.274410
+v 1.118070 2.242580 -1.118070
+v 1.274410 2.242580 -0.935276
+v 1.401280 2.242580 -0.729046
+v 1.495630 2.242580 -0.502408
+v 1.554470 2.242580 -0.258385
+v 1.574750 2.242580 -0.000000
+v 1.554470 2.242580 0.258385
+v 1.495630 2.242580 0.502408
+v 1.401280 2.242570 0.729046
+v 1.274410 2.242580 0.935276
+v 1.118070 2.242580 1.118070
+v 0.935276 2.242580 1.274410
+v 0.729046 2.242580 1.401280
+v 0.502408 2.242580 1.495630
+v 0.258385 2.242580 1.554470
+v 0.000000 2.242580 1.574750
+v -0.258385 2.242580 1.554470
+v -0.502408 2.242580 1.495630
+v -0.729046 2.242570 1.401280
+v -0.935276 2.242580 1.274410
+v -1.118070 2.242580 1.118070
+v -1.274410 2.242580 0.935276
+v -1.401280 2.242580 0.729046
+v -1.495630 2.242580 0.502408
+v -1.554470 2.242580 0.258385
+v -1.648000 2.085600 -0.000000
+v -1.626770 2.085600 0.270404
+v -1.626770 2.085600 -0.270404
+v -1.565200 2.085600 -0.525778
+v -1.466460 2.085600 -0.762958
+v -1.333690 2.085600 -0.978780
+v -1.170080 2.085600 -1.170080
+v -0.978780 2.085600 -1.333690
+v -0.762958 2.085600 -1.466460
+v -0.525778 2.085600 -1.565200
+v -0.270404 2.085600 -1.626770
+v 0.000000 2.085600 -1.648000
+v 0.270404 2.085600 -1.626770
+v 0.525778 2.085600 -1.565200
+v 0.762958 2.085600 -1.466460
+v 0.978780 2.085600 -1.333690
+v 1.170080 2.085600 -1.170080
+v 1.333690 2.085600 -0.978780
+v 1.466460 2.085600 -0.762958
+v 1.565200 2.085600 -0.525778
+v 1.626770 2.085600 -0.270404
+v 1.648000 2.085600 -0.000000
+v 1.626770 2.085600 0.270404
+v 1.565200 2.085600 0.525778
+v 1.466460 2.085600 0.762958
+v 1.333690 2.085600 0.978780
+v 1.170080 2.085600 1.170080
+v 0.978780 2.085600 1.333690
+v 0.762958 2.085600 1.466460
+v 0.525778 2.085600 1.565200
+v 0.270404 2.085600 1.626770
+v 0.000000 2.085600 1.648000
+v -0.270404 2.085600 1.626770
+v -0.525778 2.085600 1.565200
+v -0.762958 2.085600 1.466460
+v -0.978780 2.085600 1.333690
+v -1.170080 2.085600 1.170080
+v -1.333690 2.085600 0.978780
+v -1.466460 2.085600 0.762958
+v -1.565200 2.085600 0.525778
+v -1.696120 1.929520 0.281930
+v -1.631930 1.929520 0.548190
+v -1.718250 1.929520 -0.000000
+v -1.696120 1.929520 -0.281930
+v -1.631930 1.929530 -0.548190
+v -1.528970 1.929520 -0.795481
+v -1.390550 1.929530 -1.020500
+v -1.219960 1.929520 -1.219960
+v -1.020500 1.929520 -1.390550
+v -0.795481 1.929520 -1.528970
+v -0.548190 1.929520 -1.631930
+v -0.281930 1.929520 -1.696120
+v 0.000000 1.929520 -1.718250
+v 0.281930 1.929520 -1.696120
+v 0.548190 1.929530 -1.631930
+v 0.795481 1.929520 -1.528970
+v 1.020500 1.929530 -1.390550
+v 1.219960 1.929520 -1.219960
+v 1.390550 1.929520 -1.020500
+v 1.528970 1.929520 -0.795481
+v 1.631930 1.929520 -0.548190
+v 1.696120 1.929520 -0.281930
+v 1.718250 1.929520 -0.000000
+v 1.696120 1.929520 0.281930
+v 1.631930 1.929530 0.548190
+v 1.528970 1.929520 0.795481
+v 1.390550 1.929530 1.020500
+v 1.219960 1.929520 1.219960
+v 1.020500 1.929520 1.390550
+v 0.795481 1.929520 1.528970
+v 0.548190 1.929520 1.631930
+v 0.281930 1.929520 1.696120
+v 0.000000 1.929520 1.718250
+v -0.281930 1.929520 1.696120
+v -0.548190 1.929530 1.631930
+v -0.795481 1.929520 1.528970
+v -1.020500 1.929530 1.390550
+v -1.219960 1.929520 1.219960
+v -1.390550 1.929520 1.020500
+v -1.528970 1.929520 0.795481
+v -1.694370 1.774800 0.569167
+v -1.587470 1.774800 0.825921
+v -1.761020 1.774800 0.292719
+v -1.784000 1.774800 -0.000000
+v -1.761020 1.774800 -0.292719
+v -1.694370 1.774800 -0.569167
+v -1.587470 1.774800 -0.825921
+v -1.443760 1.774800 -1.059550
+v -1.266640 1.774800 -1.266640
+v -1.059550 1.774800 -1.443760
+v -0.825921 1.774800 -1.587470
+v -0.569167 1.774800 -1.694370
+v -0.292719 1.774800 -1.761020
+v 0.000000 1.774800 -1.784000
+v 0.292719 1.774800 -1.761020
+v 0.569167 1.774800 -1.694370
+v 0.825921 1.774800 -1.587470
+v 1.059550 1.774800 -1.443760
+v 1.266640 1.774800 -1.266640
+v 1.443760 1.774800 -1.059550
+v 1.587470 1.774800 -0.825921
+v 1.694370 1.774800 -0.569167
+v 1.761020 1.774800 -0.292719
+v 1.784000 1.774800 -0.000000
+v 1.761020 1.774800 0.292719
+v 1.694370 1.774800 0.569167
+v 1.587470 1.774800 0.825921
+v 1.443760 1.774800 1.059550
+v 1.266640 1.774800 1.266640
+v 1.059550 1.774800 1.443760
+v 0.825921 1.774800 1.587470
+v 0.569167 1.774800 1.694370
+v 0.292719 1.774800 1.761020
+v 0.000000 1.774800 1.784000
+v -0.292719 1.774800 1.761020
+v -0.569167 1.774800 1.694370
+v -0.825921 1.774800 1.587470
+v -1.059550 1.774800 1.443760
+v -1.266640 1.774800 1.266640
+v -1.443760 1.774800 1.059550
+v -1.640640 1.621870 0.853583
+v -1.492110 1.621870 1.095040
+v -1.751120 1.621880 0.588230
+v -1.820000 1.621880 0.302523
+v -1.843750 1.621880 -0.000000
+v -1.820000 1.621870 -0.302522
+v -1.751120 1.621880 -0.588230
+v -1.640640 1.621870 -0.853582
+v -1.492110 1.621870 -1.095040
+v -1.309060 1.621880 -1.309060
+v -1.095040 1.621870 -1.492110
+v -0.853583 1.621870 -1.640640
+v -0.588230 1.621880 -1.751120
+v -0.302523 1.621880 -1.820000
+v 0.000000 1.621880 -1.843750
+v 0.302523 1.621870 -1.820000
+v 0.588230 1.621880 -1.751120
+v 0.853583 1.621870 -1.640640
+v 1.095040 1.621870 -1.492110
+v 1.309060 1.621880 -1.309060
+v 1.492110 1.621870 -1.095040
+v 1.640640 1.621870 -0.853583
+v 1.751120 1.621880 -0.588230
+v 1.820000 1.621880 -0.302522
+v 1.843750 1.621880 -0.000000
+v 1.820000 1.621870 0.302522
+v 1.751120 1.621880 0.588230
+v 1.640640 1.621870 0.853582
+v 1.492110 1.621870 1.095040
+v 1.309060 1.621880 1.309060
+v 1.095040 1.621870 1.492110
+v 0.853583 1.621870 1.640640
+v 0.588230 1.621880 1.751120
+v 0.302523 1.621880 1.820000
+v 0.000000 1.621880 1.843750
+v -0.302522 1.621870 1.820000
+v -0.588230 1.621880 1.751120
+v -0.853582 1.621870 1.640640
+v -1.095040 1.621870 1.492110
+v -1.309060 1.621880 1.309060
+v -1.534390 1.471200 1.126070
+v -1.346160 1.471200 1.346160
+v -1.687140 1.471200 0.877772
+v -1.800740 1.471200 0.604900
+v -1.871580 1.471200 0.311096
+v -1.896000 1.471200 -0.000000
+v -1.871580 1.471200 -0.311096
+v -1.800740 1.471200 -0.604900
+v -1.687140 1.471200 -0.877772
+v -1.534390 1.471200 -1.126070
+v -1.346160 1.471200 -1.346160
+v -1.126070 1.471200 -1.534390
+v -0.877772 1.471200 -1.687140
+v -0.604900 1.471200 -1.800740
+v -0.311096 1.471200 -1.871580
+v 0.000000 1.471200 -1.896000
+v 0.311096 1.471200 -1.871580
+v 0.604900 1.471200 -1.800740
+v 0.877772 1.471200 -1.687140
+v 1.126070 1.471200 -1.534390
+v 1.346160 1.471200 -1.346160
+v 1.534390 1.471200 -1.126070
+v 1.687140 1.471200 -0.877772
+v 1.800740 1.471200 -0.604900
+v 1.871580 1.471200 -0.311096
+v 1.896000 1.471200 -0.000000
+v 1.871580 1.471200 0.311096
+v 1.800740 1.471200 0.604900
+v 1.687140 1.471200 0.877772
+v 1.534390 1.471200 1.126070
+v 1.346160 1.471200 1.346160
+v 1.126070 1.471200 1.534390
+v 0.877772 1.471200 1.687140
+v 0.604900 1.471200 1.800740
+v 0.311096 1.471200 1.871580
+v 0.000000 1.471200 1.896000
+v -0.311096 1.471200 1.871580
+v -0.604900 1.471200 1.800740
+v -0.877772 1.471200 1.687140
+v -1.126070 1.471200 1.534390
+v -1.376870 1.323220 1.376870
+v -1.151760 1.323230 1.569400
+v -1.569400 1.323230 1.151760
+v -1.725620 1.323220 0.897795
+v -1.841820 1.323230 0.618698
+v -1.914270 1.323230 0.318192
+v -1.939250 1.323230 -0.000000
+v -1.914270 1.323230 -0.318192
+v -1.841820 1.323230 -0.618698
+v -1.725620 1.323220 -0.897795
+v -1.569400 1.323230 -1.151760
+v -1.376870 1.323220 -1.376870
+v -1.151760 1.323230 -1.569400
+v -0.897795 1.323220 -1.725620
+v -0.618698 1.323230 -1.841820
+v -0.318192 1.323230 -1.914270
+v 0.000000 1.323230 -1.939250
+v 0.318192 1.323230 -1.914270
+v 0.618698 1.323230 -1.841820
+v 0.897795 1.323220 -1.725620
+v 1.151760 1.323230 -1.569400
+v 1.376870 1.323220 -1.376870
+v 1.569400 1.323230 -1.151760
+v 1.725620 1.323220 -0.897795
+v 1.841820 1.323230 -0.618698
+v 1.914270 1.323230 -0.318192
+v 1.939250 1.323230 -0.000000
+v 1.914270 1.323230 0.318192
+v 1.841820 1.323230 0.618698
+v 1.725620 1.323220 0.897795
+v 1.569400 1.323230 1.151760
+v 1.376870 1.323220 1.376870
+v 1.151760 1.323230 1.569400
+v 0.897795 1.323220 1.725620
+v 0.618698 1.323230 1.841820
+v 0.318192 1.323230 1.914270
+v 0.000000 1.323230 1.939250
+v -0.318192 1.323230 1.914270
+v -0.618698 1.323230 1.841820
+v -0.897795 1.323220 1.725620
+v -1.171210 1.178400 1.595900
+v -0.912957 1.178400 1.754760
+v -1.400120 1.178400 1.400120
+v -1.595900 1.178400 1.171210
+v -1.754760 1.178400 0.912957
+v -1.872930 1.178400 0.629147
+v -1.946600 1.178400 0.323566
+v -1.972000 1.178400 -0.000000
+v -1.946600 1.178400 -0.323566
+v -1.872930 1.178400 -0.629147
+v -1.754760 1.178400 -0.912957
+v -1.595900 1.178400 -1.171210
+v -1.400120 1.178400 -1.400120
+v -1.171210 1.178400 -1.595900
+v -0.912957 1.178400 -1.754760
+v -0.629147 1.178400 -1.872930
+v -0.323566 1.178400 -1.946600
+v 0.000000 1.178400 -1.972000
+v 0.323566 1.178400 -1.946600
+v 0.629147 1.178400 -1.872930
+v 0.912957 1.178400 -1.754760
+v 1.171210 1.178400 -1.595900
+v 1.400120 1.178400 -1.400120
+v 1.595900 1.178400 -1.171210
+v 1.754760 1.178400 -0.912957
+v 1.872930 1.178400 -0.629147
+v 1.946600 1.178400 -0.323566
+v 1.972000 1.178400 -0.000000
+v 1.946600 1.178400 0.323566
+v 1.872930 1.178400 0.629147
+v 1.754760 1.178400 0.912957
+v 1.595900 1.178400 1.171210
+v 1.400120 1.178400 1.400120
+v 1.171210 1.178400 1.595900
+v 0.912957 1.178400 1.754760
+v 0.629147 1.178400 1.872930
+v 0.323566 1.178400 1.946600
+v 0.000000 1.178400 1.972000
+v -0.323566 1.178400 1.946600
+v -0.629147 1.178400 1.872930
+v -0.922564 1.037170 1.773230
+v -0.635767 1.037180 1.892630
+v -1.183530 1.037170 1.612690
+v -1.414850 1.037180 1.414850
+v -1.612690 1.037180 1.183530
+v -1.773230 1.037170 0.922564
+v -1.892630 1.037170 0.635767
+v -1.967080 1.037180 0.326970
+v -1.992750 1.037170 -0.000000
+v -1.967080 1.037180 -0.326970
+v -1.892630 1.037180 -0.635767
+v -1.773230 1.037170 -0.922564
+v -1.612690 1.037170 -1.183530
+v -1.414850 1.037180 -1.414850
+v -1.183530 1.037180 -1.612690
+v -0.922564 1.037170 -1.773230
+v -0.635767 1.037170 -1.892630
+v -0.326970 1.037180 -1.967080
+v 0.000000 1.037170 -1.992750
+v 0.326970 1.037180 -1.967080
+v 0.635767 1.037180 -1.892630
+v 0.922564 1.037170 -1.773230
+v 1.183530 1.037170 -1.612690
+v 1.414850 1.037180 -1.414850
+v 1.612690 1.037180 -1.183530
+v 1.773230 1.037170 -0.922564
+v 1.892630 1.037170 -0.635767
+v 1.967080 1.037180 -0.326970
+v 1.992750 1.037170 -0.000000
+v 1.967080 1.037180 0.326970
+v 1.892630 1.037180 0.635767
+v 1.773230 1.037170 0.922564
+v 1.612690 1.037170 1.183530
+v 1.414850 1.037180 1.414850
+v 1.183530 1.037180 1.612690
+v 0.922564 1.037170 1.773230
+v 0.635767 1.037170 1.892630
+v 0.326970 1.037180 1.967080
+v 0.000000 1.037170 1.992750
+v -0.326970 1.037180 1.967080
+v -0.638080 0.900000 1.899520
+v -0.328160 0.900000 1.974240
+v -0.925920 0.900000 1.779680
+v -1.187840 0.900000 1.618560
+v -1.420000 0.900000 1.420000
+v -1.618560 0.900000 1.187840
+v -1.779680 0.900000 0.925920
+v -1.899520 0.900000 0.638080
+v -1.974240 0.900000 0.328160
+v -2.000000 0.900000 -0.000000
+v -1.974240 0.900000 -0.328160
+v -1.899520 0.900000 -0.638080
+v -1.779680 0.900000 -0.925920
+v -1.618560 0.900000 -1.187840
+v -1.420000 0.900000 -1.420000
+v -1.187840 0.900000 -1.618560
+v -0.925920 0.900000 -1.779680
+v -0.638080 0.900000 -1.899520
+v -0.328160 0.900000 -1.974240
+v 0.000000 0.900000 -2.000000
+v 0.328160 0.900000 -1.974240
+v 0.638080 0.900000 -1.899520
+v 0.925920 0.900000 -1.779680
+v 1.187840 0.900000 -1.618560
+v 1.420000 0.900000 -1.420000
+v 1.618560 0.900000 -1.187840
+v 1.779680 0.900000 -0.925920
+v 1.899520 0.900000 -0.638080
+v 1.974240 0.900000 -0.328160
+v 2.000000 0.900000 -0.000000
+v 1.974240 0.900000 0.328160
+v 1.899520 0.900000 0.638080
+v 1.779680 0.900000 0.925920
+v 1.618560 0.900000 1.187840
+v 1.420000 0.900000 1.420000
+v 1.187840 0.900000 1.618560
+v 0.925920 0.900000 1.779680
+v 0.638080 0.900000 1.899520
+v 0.328160 0.900000 1.974240
+v 0.000000 0.900000 2.000000
+v -0.325863 0.771675 1.960420
+v 0.000000 0.771675 1.986000
+v -0.633613 0.771675 1.886220
+v -0.919439 0.771675 1.767220
+v -1.179530 0.771675 1.607230
+v -1.410060 0.771675 1.410060
+v -1.607230 0.771675 1.179530
+v -1.767220 0.771675 0.919439
+v -1.886220 0.771675 0.633613
+v -1.960420 0.771675 0.325863
+v -1.986000 0.771675 -0.000000
+v -1.960420 0.771675 -0.325863
+v -1.886220 0.771675 -0.633613
+v -1.767220 0.771675 -0.919439
+v -1.607230 0.771675 -1.179530
+v -1.410060 0.771675 -1.410060
+v -1.179530 0.771675 -1.607230
+v -0.919439 0.771675 -1.767220
+v -0.633613 0.771675 -1.886220
+v -0.325863 0.771675 -1.960420
+v 0.000000 0.771675 -1.986000
+v 0.325863 0.771675 -1.960420
+v 0.633613 0.771675 -1.886220
+v 0.919439 0.771675 -1.767220
+v 1.179530 0.771675 -1.607230
+v 1.410060 0.771675 -1.410060
+v 1.607230 0.771675 -1.179530
+v 1.767220 0.771675 -0.919439
+v 1.886220 0.771675 -0.633613
+v 1.960420 0.771675 -0.325863
+v 1.986000 0.771675 -0.000000
+v 1.960420 0.771675 0.325863
+v 1.886220 0.771675 0.633613
+v 1.767220 0.771675 0.919439
+v 1.607230 0.771675 1.179530
+v 1.410060 0.771675 1.410060
+v 1.179530 0.771675 1.607230
+v 0.919439 0.771675 1.767220
+v 0.633613 0.771675 1.886220
+v 0.325863 0.771675 1.960420
+v 0.000000 0.656400 1.948000
+v 0.319628 0.656400 1.922910
+v -0.319628 0.656400 1.922910
+v -0.621490 0.656400 1.850130
+v -0.901846 0.656400 1.733410
+v -1.156960 0.656400 1.576480
+v -1.383080 0.656400 1.383080
+v -1.576480 0.656400 1.156960
+v -1.733410 0.656400 0.901846
+v -1.850130 0.656400 0.621490
+v -1.922910 0.656400 0.319628
+v -1.948000 0.656400 -0.000000
+v -1.922910 0.656400 -0.319628
+v -1.850130 0.656400 -0.621490
+v -1.733410 0.656400 -0.901846
+v -1.576480 0.656400 -1.156960
+v -1.383080 0.656400 -1.383080
+v -1.156960 0.656400 -1.576480
+v -0.901846 0.656400 -1.733410
+v -0.621490 0.656400 -1.850130
+v -0.319628 0.656400 -1.922910
+v 0.000000 0.656400 -1.948000
+v 0.319628 0.656400 -1.922910
+v 0.621490 0.656400 -1.850130
+v 0.901846 0.656400 -1.733410
+v 1.156960 0.656400 -1.576480
+v 1.383080 0.656400 -1.383080
+v 1.576480 0.656400 -1.156960
+v 1.733410 0.656400 -0.901846
+v 1.850130 0.656400 -0.621490
+v 1.922910 0.656400 -0.319628
+v 1.948000 0.656400 -0.000000
+v 1.922910 0.656400 0.319628
+v 1.850130 0.656400 0.621490
+v 1.733410 0.656400 0.901846
+v 1.576480 0.656400 1.156960
+v 1.383080 0.656400 1.383080
+v 1.156960 0.656400 1.576480
+v 0.901846 0.656400 1.733410
+v 0.621490 0.656400 1.850130
+v 0.310439 0.553725 1.867630
+v 0.603624 0.553725 1.796950
+v 0.000000 0.553725 1.892000
+v -0.310439 0.553725 1.867630
+v -0.603624 0.553725 1.796950
+v -0.875920 0.553725 1.683580
+v -1.123700 0.553725 1.531160
+v -1.343320 0.553725 1.343320
+v -1.531160 0.553725 1.123700
+v -1.683580 0.553725 0.875920
+v -1.796950 0.553725 0.603624
+v -1.867630 0.553725 0.310439
+v -1.892000 0.553725 -0.000000
+v -1.867630 0.553725 -0.310439
+v -1.796950 0.553725 -0.603624
+v -1.683580 0.553725 -0.875920
+v -1.531160 0.553725 -1.123700
+v -1.343320 0.553725 -1.343320
+v -1.123700 0.553725 -1.531160
+v -0.875920 0.553725 -1.683580
+v -0.603624 0.553725 -1.796950
+v -0.310439 0.553725 -1.867630
+v 0.000000 0.553725 -1.892000
+v 0.310439 0.553725 -1.867630
+v 0.603624 0.553725 -1.796950
+v 0.875920 0.553725 -1.683580
+v 1.123700 0.553725 -1.531160
+v 1.343320 0.553725 -1.343320
+v 1.531160 0.553725 -1.123700
+v 1.683580 0.553725 -0.875920
+v 1.796950 0.553725 -0.603624
+v 1.867630 0.553725 -0.310439
+v 1.892000 0.553725 -0.000000
+v 1.867630 0.553725 0.310439
+v 1.796950 0.553725 0.603624
+v 1.683580 0.553725 0.875920
+v 1.531160 0.553725 1.123700
+v 1.343320 0.553725 1.343320
+v 1.123700 0.553725 1.531160
+v 0.875920 0.553725 1.683580
+v 0.581929 0.463200 1.732360
+v 0.844439 0.463200 1.623070
+v 0.299282 0.463200 1.800510
+v 0.000000 0.463200 1.824000
+v -0.299282 0.463200 1.800510
+v -0.581929 0.463200 1.732360
+v -0.844439 0.463200 1.623070
+v -1.083310 0.463200 1.476130
+v -1.295040 0.463200 1.295040
+v -1.476130 0.463200 1.083310
+v -1.623070 0.463200 0.844439
+v -1.732360 0.463200 0.581929
+v -1.800510 0.463200 0.299282
+v -1.824000 0.463200 -0.000000
+v -1.800510 0.463200 -0.299282
+v -1.732360 0.463200 -0.581929
+v -1.623070 0.463200 -0.844439
+v -1.476130 0.463200 -1.083310
+v -1.295040 0.463200 -1.295040
+v -1.083310 0.463200 -1.476130
+v -0.844439 0.463200 -1.623070
+v -0.581929 0.463200 -1.732360
+v -0.299282 0.463200 -1.800510
+v 0.000000 0.463200 -1.824000
+v 0.299282 0.463200 -1.800510
+v 0.581929 0.463200 -1.732360
+v 0.844439 0.463200 -1.623070
+v 1.083310 0.463200 -1.476130
+v 1.295040 0.463200 -1.295040
+v 1.476130 0.463200 -1.083310
+v 1.623070 0.463200 -0.844439
+v 1.732360 0.463200 -0.581929
+v 1.800510 0.463200 -0.299282
+v 1.824000 0.463200 -0.000000
+v 1.800510 0.463200 0.299282
+v 1.732360 0.463200 0.581929
+v 1.623070 0.463200 0.844439
+v 1.476130 0.463200 1.083310
+v 1.295040 0.463200 1.295040
+v 1.083310 0.463200 1.476130
+v 0.810180 0.384375 1.557220
+v 1.039360 0.384375 1.416240
+v 0.558320 0.384375 1.662080
+v 0.287140 0.384375 1.727460
+v 0.000000 0.384375 1.750000
+v -0.287140 0.384375 1.727460
+v -0.558320 0.384375 1.662080
+v -0.810180 0.384375 1.557220
+v -1.039360 0.384375 1.416240
+v -1.242500 0.384375 1.242500
+v -1.416240 0.384375 1.039360
+v -1.557220 0.384375 0.810180
+v -1.662080 0.384375 0.558320
+v -1.727460 0.384375 0.287140
+v -1.750000 0.384375 -0.000000
+v -1.727460 0.384375 -0.287140
+v -1.662080 0.384375 -0.558320
+v -1.557220 0.384375 -0.810180
+v -1.416240 0.384375 -1.039360
+v -1.242500 0.384375 -1.242500
+v -1.039360 0.384375 -1.416240
+v -0.810180 0.384375 -1.557220
+v -0.558320 0.384375 -1.662080
+v -0.287140 0.384375 -1.727460
+v 0.000000 0.384375 -1.750000
+v 0.287140 0.384375 -1.727460
+v 0.558320 0.384375 -1.662080
+v 0.810180 0.384375 -1.557220
+v 1.039360 0.384375 -1.416240
+v 1.242500 0.384375 -1.242500
+v 1.416240 0.384375 -1.039360
+v 1.557220 0.384375 -0.810180
+v 1.662080 0.384375 -0.558320
+v 1.727460 0.384375 -0.287140
+v 1.750000 0.384375 -0.000000
+v 1.727460 0.384375 0.287140
+v 1.662080 0.384375 0.558320
+v 1.557220 0.384375 0.810180
+v 1.416240 0.384375 1.039360
+v 1.242500 0.384375 1.242500
+v 0.995410 0.316800 1.356350
+v 1.189960 0.316800 1.189960
+v 0.775921 0.316800 1.491370
+v 0.534711 0.316800 1.591800
+v 0.274998 0.316800 1.654410
+v 0.000000 0.316800 1.676000
+v -0.274998 0.316800 1.654410
+v -0.534711 0.316800 1.591800
+v -0.775921 0.316800 1.491370
+v -0.995410 0.316800 1.356350
+v -1.189960 0.316800 1.189960
+v -1.356350 0.316800 0.995410
+v -1.491370 0.316800 0.775921
+v -1.591800 0.316800 0.534711
+v -1.654410 0.316800 0.274998
+v -1.676000 0.316800 -0.000000
+v -1.654410 0.316800 -0.274998
+v -1.591800 0.316800 -0.534711
+v -1.491370 0.316800 -0.775921
+v -1.356350 0.316800 -0.995410
+v -1.189960 0.316800 -1.189960
+v -0.995410 0.316800 -1.356350
+v -0.775921 0.316800 -1.491370
+v -0.534711 0.316800 -1.591800
+v -0.274998 0.316800 -1.654410
+v 0.000000 0.316800 -1.676000
+v 0.274998 0.316800 -1.654410
+v 0.534711 0.316800 -1.591800
+v 0.775921 0.316800 -1.491370
+v 0.995410 0.316800 -1.356350
+v 1.189960 0.316800 -1.189960
+v 1.356350 0.316800 -0.995410
+v 1.491370 0.316800 -0.775921
+v 1.591800 0.316800 -0.534711
+v 1.654410 0.316800 -0.274998
+v 1.676000 0.316800 -0.000000
+v 1.654410 0.316800 0.274998
+v 1.591800 0.316800 0.534711
+v 1.491370 0.316800 0.775921
+v 1.356350 0.316800 0.995410
+v 1.141680 0.260025 1.141680
+v 1.301320 0.260025 0.955023
+v 0.955023 0.260025 1.301320
+v 0.744440 0.260025 1.430860
+v 0.513016 0.260025 1.527210
+v 0.263841 0.260025 1.587290
+v 0.000000 0.260025 1.608000
+v -0.263841 0.260025 1.587290
+v -0.513016 0.260025 1.527210
+v -0.744440 0.260025 1.430860
+v -0.955023 0.260025 1.301320
+v -1.141680 0.260025 1.141680
+v -1.301320 0.260025 0.955023
+v -1.430860 0.260025 0.744440
+v -1.527210 0.260025 0.513016
+v -1.587290 0.260025 0.263841
+v -1.608000 0.260025 -0.000000
+v -1.587290 0.260025 -0.263841
+v -1.527210 0.260025 -0.513016
+v -1.430860 0.260025 -0.744440
+v -1.301320 0.260025 -0.955023
+v -1.141680 0.260025 -1.141680
+v -0.955023 0.260025 -1.301320
+v -0.744440 0.260025 -1.430860
+v -0.513016 0.260025 -1.527210
+v -0.263841 0.260025 -1.587290
+v 0.000000 0.260025 -1.608000
+v 0.263841 0.260025 -1.587290
+v 0.513016 0.260025 -1.527210
+v 0.744440 0.260025 -1.430860
+v 0.955023 0.260025 -1.301320
+v 1.141680 0.260025 -1.141680
+v 1.301320 0.260025 -0.955023
+v 1.430860 0.260025 -0.744440
+v 1.527210 0.260025 -0.513016
+v 1.587290 0.260025 -0.263841
+v 1.608000 0.260025 -0.000000
+v 1.587290 0.260025 0.263841
+v 1.527210 0.260025 0.513016
+v 1.430860 0.260025 0.744440
+v 1.256000 0.213600 0.921764
+v 1.381030 0.213600 0.718514
+v 1.101920 0.213600 1.101920
+v 0.921764 0.213600 1.256000
+v 0.718514 0.213600 1.381030
+v 0.495150 0.213600 1.474030
+v 0.254652 0.213600 1.532010
+v 0.000000 0.213600 1.552000
+v -0.254652 0.213600 1.532010
+v -0.495150 0.213600 1.474030
+v -0.718514 0.213600 1.381030
+v -0.921764 0.213600 1.256000
+v -1.101920 0.213600 1.101920
+v -1.256000 0.213600 0.921764
+v -1.381030 0.213600 0.718514
+v -1.474030 0.213600 0.495150
+v -1.532010 0.213600 0.254652
+v -1.552000 0.213600 -0.000000
+v -1.532010 0.213600 -0.254652
+v -1.474030 0.213600 -0.495150
+v -1.381030 0.213600 -0.718514
+v -1.256000 0.213600 -0.921764
+v -1.101920 0.213600 -1.101920
+v -0.921764 0.213600 -1.256000
+v -0.718514 0.213600 -1.381030
+v -0.495150 0.213600 -1.474030
+v -0.254652 0.213600 -1.532010
+v 0.000000 0.213600 -1.552000
+v 0.254652 0.213600 -1.532010
+v 0.495150 0.213600 -1.474030
+v 0.718514 0.213600 -1.381030
+v 0.921764 0.213600 -1.256000
+v 1.101920 0.213600 -1.101920
+v 1.256000 0.213600 -0.921764
+v 1.381030 0.213600 -0.718514
+v 1.474030 0.213600 -0.495150
+v 1.532010 0.213600 -0.254652
+v 1.552000 0.213600 -0.000000
+v 1.532010 0.213600 0.254652
+v 1.474030 0.213600 0.495150
+v 1.347220 0.177075 0.700921
+v 1.437940 0.177075 0.483027
+v 1.225250 0.177075 0.899195
+v 1.074940 0.177075 1.074940
+v 0.899195 0.177075 1.225250
+v 0.700921 0.177075 1.347220
+v 0.483027 0.177075 1.437940
+v 0.248417 0.177075 1.494500
+v 0.000000 0.177075 1.514000
+v -0.248417 0.177075 1.494500
+v -0.483027 0.177075 1.437940
+v -0.700921 0.177075 1.347220
+v -0.899195 0.177075 1.225250
+v -1.074940 0.177075 1.074940
+v -1.225250 0.177075 0.899195
+v -1.347220 0.177075 0.700921
+v -1.437940 0.177075 0.483027
+v -1.494500 0.177075 0.248417
+v -1.514000 0.177075 -0.000000
+v -1.494500 0.177075 -0.248417
+v -1.437940 0.177075 -0.483027
+v -1.347220 0.177075 -0.700921
+v -1.225250 0.177075 -0.899195
+v -1.074940 0.177075 -1.074940
+v -0.899195 0.177075 -1.225250
+v -0.700921 0.177075 -1.347220
+v -0.483027 0.177075 -1.437940
+v -0.248417 0.177075 -1.494500
+v 0.000000 0.177075 -1.514000
+v 0.248417 0.177075 -1.494500
+v 0.483027 0.177075 -1.437940
+v 0.700921 0.177075 -1.347220
+v 0.899195 0.177075 -1.225250
+v 1.074940 0.177075 -1.074940
+v 1.225250 0.177075 -0.899195
+v 1.347220 0.177075 -0.700921
+v 1.437940 0.177075 -0.483027
+v 1.494500 0.177075 -0.248417
+v 1.514000 0.177075 -0.000000
+v 1.494500 0.177075 0.248417
+v 1.424640 0.150000 0.478560
+v 1.480680 0.150000 0.246120
+v 1.334760 0.150000 0.694440
+v 1.213920 0.150000 0.890880
+v 1.065000 0.150000 1.065000
+v 0.890880 0.150000 1.213920
+v 0.694440 0.150000 1.334760
+v 0.478560 0.150000 1.424640
+v 0.246120 0.150000 1.480680
+v 0.000000 0.150000 1.500000
+v -0.246120 0.150000 1.480680
+v -0.478560 0.150000 1.424640
+v -0.694440 0.150000 1.334760
+v -0.890880 0.150000 1.213920
+v -1.065000 0.150000 1.065000
+v -1.213920 0.150000 0.890880
+v -1.334760 0.150000 0.694440
+v -1.424640 0.150000 0.478560
+v -1.480680 0.150000 0.246120
+v -1.500000 0.150000 -0.000000
+v -1.480680 0.150000 -0.246120
+v -1.424640 0.150000 -0.478560
+v -1.334760 0.150000 -0.694440
+v -1.213920 0.150000 -0.890880
+v -1.065000 0.150000 -1.065000
+v -0.890880 0.150000 -1.213920
+v -0.694440 0.150000 -1.334760
+v -0.478560 0.150000 -1.424640
+v -0.246120 0.150000 -1.480680
+v 0.000000 0.150000 -1.500000
+v 0.246120 0.150000 -1.480680
+v 0.478560 0.150000 -1.424640
+v 0.694440 0.150000 -1.334760
+v 0.890880 0.150000 -1.213920
+v 1.065000 0.150000 -1.065000
+v 1.213920 0.150000 -0.890880
+v 1.334760 0.150000 -0.694440
+v 1.424640 0.150000 -0.478560
+v 1.480680 0.150000 -0.246120
+v 1.500000 0.150000 -0.000000
+v 1.477200 0.127575 0.245542
+v 1.496470 0.127575 -0.000000
+v 1.461730 0.105600 0.242970
+v 1.480800 0.105600 -0.000000
+v 1.426710 0.084525 0.237149
+v 1.445320 0.084525 -0.000000
+v 1.364590 0.064800 0.226824
+v 1.382400 0.064800 -0.000000
+v 1.267830 0.046875 0.210740
+v 1.284380 0.046875 -0.000000
+v 1.128870 0.031200 0.187642
+v 1.143600 0.031200 -0.000000
+v 0.940158 0.018225 0.156274
+v 0.952425 0.018225 -0.000000
+v 0.694143 0.008400 0.115381
+v 0.703200 0.008400 -0.000000
+v 0.383274 0.002175 0.063708
+v 0.388275 0.002175 -0.000000
+v 0.000000 0.000000 0.000000
+v 0.383274 0.002175 -0.063708
+v 0.368768 0.002175 -0.123875
+v 0.345503 0.002175 -0.179756
+v 0.314223 0.002175 -0.230604
+v 0.275675 0.002175 -0.275675
+v 0.230604 0.002175 -0.314223
+v 0.179756 0.002175 -0.345503
+v 0.123875 0.002175 -0.368768
+v 0.063708 0.002175 -0.383274
+v 0.000000 0.002175 -0.388275
+v -0.063708 0.002175 -0.383274
+v -0.123875 0.002175 -0.368768
+v -0.179756 0.002175 -0.345503
+v -0.230604 0.002175 -0.314223
+v -0.275675 0.002175 -0.275675
+v -0.314223 0.002175 -0.230604
+v -0.345503 0.002175 -0.179756
+v -0.368768 0.002175 -0.123875
+v -0.383274 0.002175 -0.063708
+v -0.388275 0.002175 -0.000000
+v -0.383274 0.002175 0.063708
+v -0.368768 0.002175 0.123875
+v -0.345503 0.002175 0.179756
+v -0.314223 0.002175 0.230604
+v -0.275675 0.002175 0.275675
+v -0.230604 0.002175 0.314223
+v -0.179756 0.002175 0.345503
+v -0.123875 0.002175 0.368768
+v -0.063708 0.002175 0.383274
+v 0.000000 0.002175 0.388275
+v 0.063708 0.002175 0.383274
+v 0.123875 0.002175 0.368768
+v 0.179756 0.002175 0.345503
+v 0.230604 0.002175 0.314223
+v 0.275675 0.002175 0.275675
+v 0.314223 0.002175 0.230604
+v 0.345503 0.002175 0.179756
+v 0.368768 0.002175 0.123875
+v 0.667871 0.008400 0.224349
+v 0.904575 0.018225 0.303862
+v 1.086150 0.031200 0.364854
+v 1.219850 0.046875 0.409767
+v 1.312950 0.064800 0.441041
+v 1.372710 0.084525 0.461116
+v 1.406400 0.105600 0.472434
+v 1.421290 0.127575 0.477435
+v 1.331620 0.127575 0.692808
+v 1.317680 0.105600 0.685551
+v 1.286110 0.084525 0.669128
+v 1.230110 0.064800 0.639996
+v 1.142890 0.046875 0.594614
+v 1.017620 0.031200 0.529441
+v 0.847506 0.018225 0.440935
+v 0.625735 0.008400 0.325553
+v 0.569086 0.008400 0.417645
+v 0.770779 0.018225 0.565664
+v 0.925493 0.031200 0.679207
+v 1.039420 0.046875 0.762816
+v 1.118750 0.064800 0.821035
+v 1.169670 0.084525 0.858407
+v 1.198380 0.105600 0.879477
+v 1.211070 0.127575 0.888786
+v 1.062500 0.127575 1.062500
+v 1.051370 0.105600 1.051370
+v 1.026180 0.084525 1.026180
+v 0.981504 0.064800 0.981504
+v 0.911906 0.046875 0.911906
+v 0.811956 0.031200 0.811956
+v 0.676222 0.018225 0.676222
+v 0.499272 0.008400 0.499272
+v 0.417645 0.008400 0.569086
+v 0.565664 0.018225 0.770779
+v 0.679207 0.031200 0.925493
+v 0.762816 0.046875 1.039420
+v 0.821035 0.064800 1.118750
+v 0.858407 0.084525 1.169670
+v 0.879477 0.105600 1.198380
+v 0.888786 0.127575 1.211070
+v 0.692808 0.127575 1.331620
+v 0.685551 0.105600 1.317680
+v 0.669128 0.084525 1.286110
+v 0.639996 0.064800 1.230110
+v 0.594614 0.046875 1.142890
+v 0.529441 0.031200 1.017620
+v 0.440935 0.018225 0.847506
+v 0.325553 0.008400 0.625735
+v 0.224349 0.008400 0.667871
+v 0.303862 0.018225 0.904575
+v 0.364854 0.031200 1.086150
+v 0.409767 0.046875 1.219850
+v 0.441041 0.064800 1.312950
+v 0.461116 0.084525 1.372710
+v 0.472434 0.105600 1.406400
+v 0.477435 0.127575 1.421290
+v 0.245542 0.127575 1.477200
+v 0.242970 0.105600 1.461730
+v 0.237149 0.084525 1.426710
+v 0.226824 0.064800 1.364590
+v 0.210740 0.046875 1.267830
+v 0.187642 0.031200 1.128870
+v 0.156274 0.018225 0.940158
+v 0.115381 0.008400 0.694143
+v 0.000000 0.008400 0.703200
+v 0.000000 0.018225 0.952425
+v 0.000000 0.031200 1.143600
+v 0.000000 0.046875 1.284380
+v 0.000000 0.064800 1.382400
+v 0.000000 0.084525 1.445320
+v 0.000000 0.105600 1.480800
+v 0.000000 0.127575 1.496470
+v -0.245542 0.127575 1.477200
+v -0.242970 0.105600 1.461730
+v -0.237149 0.084525 1.426710
+v -0.226824 0.064800 1.364590
+v -0.210740 0.046875 1.267830
+v -0.187642 0.031200 1.128870
+v -0.156274 0.018225 0.940158
+v -0.115381 0.008400 0.694143
+v -0.224349 0.008400 0.667871
+v -0.303862 0.018225 0.904575
+v -0.364854 0.031200 1.086150
+v -0.409767 0.046875 1.219850
+v -0.441041 0.064800 1.312950
+v -0.461116 0.084525 1.372710
+v -0.472434 0.105600 1.406400
+v -0.477435 0.127575 1.421290
+v -0.692808 0.127575 1.331620
+v -0.685551 0.105600 1.317680
+v -0.669128 0.084525 1.286110
+v -0.639996 0.064800 1.230110
+v -0.594614 0.046875 1.142890
+v -0.529441 0.031200 1.017620
+v -0.440935 0.018225 0.847506
+v -0.325553 0.008400 0.625735
+v -0.417645 0.008400 0.569086
+v -0.565664 0.018225 0.770779
+v -0.679207 0.031200 0.925493
+v -0.762816 0.046875 1.039420
+v -0.821035 0.064800 1.118750
+v -0.858407 0.084525 1.169670
+v -0.879477 0.105600 1.198380
+v -0.888786 0.127575 1.211070
+v -1.062500 0.127575 1.062500
+v -1.051370 0.105600 1.051370
+v -1.026180 0.084525 1.026180
+v -0.981504 0.064800 0.981504
+v -0.911906 0.046875 0.911906
+v -0.811956 0.031200 0.811956
+v -0.676222 0.018225 0.676222
+v -0.499272 0.008400 0.499272
+v -0.569086 0.008400 0.417645
+v -0.770779 0.018225 0.565664
+v -0.925493 0.031200 0.679207
+v -1.039420 0.046875 0.762816
+v -1.118750 0.064800 0.821035
+v -1.169670 0.084525 0.858407
+v -1.198380 0.105600 0.879477
+v -1.211070 0.127575 0.888786
+v -1.331620 0.127575 0.692808
+v -1.317680 0.105600 0.685551
+v -1.286110 0.084525 0.669128
+v -1.230110 0.064800 0.639996
+v -1.142890 0.046875 0.594614
+v -1.017620 0.031200 0.529441
+v -0.847506 0.018225 0.440935
+v -0.625735 0.008400 0.325553
+v -0.667871 0.008400 0.224349
+v -0.904575 0.018225 0.303862
+v -1.086150 0.031200 0.364854
+v -1.219850 0.046875 0.409767
+v -1.312950 0.064800 0.441041
+v -1.372710 0.084525 0.461116
+v -1.406400 0.105600 0.472434
+v -1.421290 0.127575 0.477435
+v -1.477200 0.127575 0.245542
+v -1.461730 0.105600 0.242970
+v -1.426710 0.084525 0.237149
+v -1.364590 0.064800 0.226824
+v -1.267830 0.046875 0.210740
+v -1.128870 0.031200 0.187642
+v -0.940158 0.018225 0.156274
+v -0.694143 0.008400 0.115381
+v -0.703200 0.008400 -0.000000
+v -0.952425 0.018225 -0.000000
+v -1.143600 0.031200 -0.000000
+v -1.284380 0.046875 -0.000000
+v -1.382400 0.064800 -0.000000
+v -1.445320 0.084525 -0.000000
+v -1.480800 0.105600 -0.000000
+v -1.496470 0.127575 -0.000000
+v -1.477200 0.127575 -0.245542
+v -1.461730 0.105600 -0.242970
+v -1.426710 0.084525 -0.237149
+v -1.364590 0.064800 -0.226824
+v -1.267830 0.046875 -0.210740
+v -1.128870 0.031200 -0.187642
+v -0.940158 0.018225 -0.156274
+v -0.694143 0.008400 -0.115381
+v -0.667871 0.008400 -0.224349
+v -0.904575 0.018225 -0.303862
+v -1.086150 0.031200 -0.364854
+v -1.219850 0.046875 -0.409767
+v -1.312950 0.064800 -0.441041
+v -1.372710 0.084525 -0.461116
+v -1.406400 0.105600 -0.472434
+v -1.421290 0.127575 -0.477435
+v -1.331620 0.127575 -0.692808
+v -1.317680 0.105600 -0.685551
+v -1.286110 0.084525 -0.669128
+v -1.230110 0.064800 -0.639996
+v -1.142890 0.046875 -0.594614
+v -1.017620 0.031200 -0.529441
+v -0.847506 0.018225 -0.440935
+v -0.625735 0.008400 -0.325553
+v -0.569086 0.008400 -0.417645
+v -0.770779 0.018225 -0.565664
+v -0.925493 0.031200 -0.679207
+v -1.039420 0.046875 -0.762816
+v -1.118750 0.064800 -0.821035
+v -1.169670 0.084525 -0.858407
+v -1.198380 0.105600 -0.879477
+v -1.211070 0.127575 -0.888786
+v -1.062500 0.127575 -1.062500
+v -1.051370 0.105600 -1.051370
+v -1.026180 0.084525 -1.026180
+v -0.981504 0.064800 -0.981504
+v -0.911906 0.046875 -0.911906
+v -0.811956 0.031200 -0.811956
+v -0.676222 0.018225 -0.676222
+v -0.499272 0.008400 -0.499272
+v -0.417645 0.008400 -0.569086
+v -0.565664 0.018225 -0.770779
+v -0.679207 0.031200 -0.925493
+v -0.762816 0.046875 -1.039420
+v -0.821035 0.064800 -1.118750
+v -0.858407 0.084525 -1.169670
+v -0.879477 0.105600 -1.198380
+v -0.888786 0.127575 -1.211070
+v -0.692808 0.127575 -1.331620
+v -0.685551 0.105600 -1.317680
+v -0.669128 0.084525 -1.286110
+v -0.639996 0.064800 -1.230110
+v -0.594614 0.046875 -1.142890
+v -0.529441 0.031200 -1.017620
+v -0.440935 0.018225 -0.847506
+v -0.325553 0.008400 -0.625735
+v -0.224349 0.008400 -0.667871
+v -0.303862 0.018225 -0.904575
+v -0.364854 0.031200 -1.086150
+v -0.409767 0.046875 -1.219850
+v -0.441041 0.064800 -1.312950
+v -0.461116 0.084525 -1.372710
+v -0.472434 0.105600 -1.406400
+v -0.477435 0.127575 -1.421290
+v -0.245542 0.127575 -1.477200
+v -0.242970 0.105600 -1.461730
+v -0.237149 0.084525 -1.426710
+v -0.226824 0.064800 -1.364590
+v -0.210740 0.046875 -1.267830
+v -0.187642 0.031200 -1.128870
+v -0.156274 0.018225 -0.940158
+v -0.115381 0.008400 -0.694143
+v 0.000000 0.008400 -0.703200
+v 0.000000 0.018225 -0.952425
+v 0.000000 0.031200 -1.143600
+v 0.000000 0.046875 -1.284380
+v 0.000000 0.064800 -1.382400
+v 0.000000 0.084525 -1.445320
+v 0.000000 0.105600 -1.480800
+v 0.000000 0.127575 -1.496470
+v 0.245542 0.127575 -1.477200
+v 0.242970 0.105600 -1.461730
+v 0.237149 0.084525 -1.426710
+v 0.226824 0.064800 -1.364590
+v 0.210740 0.046875 -1.267830
+v 0.187642 0.031200 -1.128870
+v 0.156274 0.018225 -0.940158
+v 0.115381 0.008400 -0.694143
+v 0.224349 0.008400 -0.667871
+v 0.303862 0.018225 -0.904575
+v 0.364854 0.031200 -1.086150
+v 0.409767 0.046875 -1.219850
+v 0.441041 0.064800 -1.312950
+v 0.461116 0.084525 -1.372710
+v 0.472434 0.105600 -1.406400
+v 0.477435 0.127575 -1.421290
+v 0.692808 0.127575 -1.331620
+v 0.685551 0.105600 -1.317680
+v 0.669128 0.084525 -1.286110
+v 0.639996 0.064800 -1.230110
+v 0.594614 0.046875 -1.142890
+v 0.529441 0.031200 -1.017620
+v 0.440935 0.018225 -0.847506
+v 0.325553 0.008400 -0.625735
+v 0.417645 0.008400 -0.569086
+v 0.565664 0.018225 -0.770779
+v 0.679207 0.031200 -0.925493
+v 0.762816 0.046875 -1.039420
+v 0.821035 0.064800 -1.118750
+v 0.858407 0.084525 -1.169670
+v 0.879477 0.105600 -1.198380
+v 0.888786 0.127575 -1.211070
+v 1.062500 0.127575 -1.062500
+v 1.051370 0.105600 -1.051370
+v 1.026180 0.084525 -1.026180
+v 0.981504 0.064800 -0.981504
+v 0.911906 0.046875 -0.911906
+v 0.811956 0.031200 -0.811956
+v 0.676222 0.018225 -0.676222
+v 0.499272 0.008400 -0.499272
+v 0.569086 0.008400 -0.417645
+v 0.770779 0.018225 -0.565664
+v 0.925493 0.031200 -0.679207
+v 1.039420 0.046875 -0.762816
+v 1.118750 0.064800 -0.821035
+v 1.169670 0.084525 -0.858407
+v 1.198380 0.105600 -0.879477
+v 1.211070 0.127575 -0.888786
+v 1.331620 0.127575 -0.692808
+v 1.317680 0.105600 -0.685551
+v 1.286110 0.084525 -0.669128
+v 1.230110 0.064800 -0.639996
+v 1.142890 0.046875 -0.594614
+v 1.017620 0.031200 -0.529441
+v 0.847506 0.018225 -0.440935
+v 0.625735 0.008400 -0.325553
+v 0.667871 0.008400 -0.224349
+v 0.904575 0.018225 -0.303862
+v 1.086150 0.031200 -0.364854
+v 1.219850 0.046875 -0.409767
+v 1.312950 0.064800 -0.441041
+v 1.372710 0.084525 -0.461116
+v 1.406400 0.105600 -0.472434
+v 1.421290 0.127575 -0.477435
+v 1.477200 0.127575 -0.245542
+v 1.461730 0.105600 -0.242970
+v 1.426710 0.084525 -0.237149
+v 1.364590 0.064800 -0.226824
+v 1.267830 0.046875 -0.210740
+v 1.128870 0.031200 -0.187642
+v 0.940158 0.018225 -0.156274
+v 0.694143 0.008400 -0.115381
+v 0.000000 3.150000 -0.000000
+v 0.032048 3.141450 -0.192107
+v 0.000000 3.141450 -0.194600
+v 0.062272 3.141450 -0.184870
+v 0.090306 3.141450 -0.173255
+v 0.115787 3.141450 -0.157626
+v 0.138348 3.141450 -0.138348
+v 0.157626 3.141450 -0.115787
+v 0.173255 3.141450 -0.090307
+v 0.184870 3.141450 -0.062272
+v 0.192107 3.141450 -0.032048
+v 0.194600 3.141450 -0.000000
+v 0.192107 3.141450 0.032048
+v 0.184870 3.141450 0.062272
+v 0.173255 3.141450 0.090306
+v 0.157626 3.141450 0.115787
+v 0.138348 3.141450 0.138348
+v 0.115787 3.141450 0.157626
+v 0.090306 3.141450 0.173255
+v 0.062272 3.141450 0.184870
+v 0.032048 3.141450 0.192107
+v 0.000000 3.141450 0.194600
+v -0.032048 3.141450 0.192107
+v -0.062272 3.141450 0.184870
+v -0.090306 3.141450 0.173255
+v -0.115787 3.141450 0.157626
+v -0.138348 3.141450 0.138348
+v -0.157626 3.141450 0.115787
+v -0.173255 3.141450 0.090306
+v -0.184870 3.141450 0.062272
+v -0.192107 3.141450 0.032048
+v -0.194600 3.141450 -0.000000
+v -0.192107 3.141450 -0.032048
+v -0.184870 3.141450 -0.062272
+v -0.173255 3.141450 -0.090307
+v -0.157626 3.141450 -0.115787
+v -0.138348 3.141450 -0.138348
+v -0.115787 3.141450 -0.157626
+v -0.090306 3.141450 -0.173255
+v -0.062272 3.141450 -0.184870
+v -0.032048 3.141450 -0.192107
+v 0.000000 3.117600 -0.308800
+v -0.050855 3.117600 -0.304843
+v 0.050855 3.117600 -0.304843
+v 0.098815 3.117600 -0.293360
+v 0.143301 3.117600 -0.274928
+v 0.183734 3.117600 -0.250127
+v 0.219536 3.117600 -0.219536
+v 0.250127 3.117600 -0.183734
+v 0.274928 3.117600 -0.143301
+v 0.293360 3.117600 -0.098815
+v 0.304843 3.117600 -0.050855
+v 0.308800 3.117600 -0.000000
+v 0.304843 3.117600 0.050854
+v 0.293360 3.117600 0.098814
+v 0.274928 3.117600 0.143301
+v 0.250127 3.117600 0.183734
+v 0.219536 3.117600 0.219536
+v 0.183734 3.117600 0.250127
+v 0.143301 3.117600 0.274928
+v 0.098815 3.117600 0.293360
+v 0.050855 3.117600 0.304843
+v 0.000000 3.117600 0.308800
+v -0.050855 3.117600 0.304843
+v -0.098815 3.117600 0.293360
+v -0.143301 3.117600 0.274928
+v -0.183734 3.117600 0.250127
+v -0.219536 3.117600 0.219536
+v -0.250127 3.117600 0.183734
+v -0.274928 3.117600 0.143301
+v -0.293360 3.117600 0.098814
+v -0.304843 3.117600 0.050854
+v -0.308800 3.117600 -0.000000
+v -0.304843 3.117600 -0.050855
+v -0.293360 3.117600 -0.098815
+v -0.274928 3.117600 -0.143301
+v -0.250127 3.117600 -0.183734
+v -0.219536 3.117600 -0.219536
+v -0.183734 3.117600 -0.250127
+v -0.143301 3.117600 -0.274928
+v -0.098815 3.117600 -0.293360
+v -0.058988 3.081150 -0.353610
+v -0.114619 3.081150 -0.340289
+v 0.000000 3.081150 -0.358200
+v 0.058988 3.081150 -0.353610
+v 0.114619 3.081150 -0.340289
+v 0.166221 3.081150 -0.318907
+v 0.213123 3.081150 -0.290138
+v 0.254653 3.081150 -0.254653
+v 0.290138 3.081150 -0.213123
+v 0.318907 3.081150 -0.166221
+v 0.340289 3.081150 -0.114619
+v 0.353610 3.081150 -0.058988
+v 0.358200 3.081150 -0.000000
+v 0.353610 3.081150 0.058988
+v 0.340289 3.081150 0.114619
+v 0.318907 3.081150 0.166221
+v 0.290138 3.081150 0.213123
+v 0.254653 3.081150 0.254653
+v 0.213123 3.081150 0.290138
+v 0.166221 3.081150 0.318907
+v 0.114619 3.081150 0.340289
+v 0.058988 3.081150 0.353610
+v 0.000000 3.081150 0.358200
+v -0.058988 3.081150 0.353610
+v -0.114619 3.081150 0.340289
+v -0.166221 3.081150 0.318907
+v -0.213123 3.081150 0.290138
+v -0.254653 3.081150 0.254653
+v -0.290138 3.081150 0.213123
+v -0.318907 3.081150 0.166221
+v -0.340289 3.081150 0.114619
+v -0.353610 3.081150 0.058988
+v -0.358200 3.081150 -0.000000
+v -0.353610 3.081150 -0.058988
+v -0.340289 3.081150 -0.114619
+v -0.318907 3.081150 -0.166221
+v -0.290138 3.081150 -0.213123
+v -0.254653 3.081150 -0.254653
+v -0.213123 3.081150 -0.290138
+v -0.166221 3.081150 -0.318907
+v -0.114676 3.034800 -0.340477
+v -0.166306 3.034800 -0.319082
+v -0.059016 3.034800 -0.353807
+v 0.000000 3.034800 -0.358400
+v 0.059016 3.034800 -0.353807
+v 0.114676 3.034800 -0.340477
+v 0.166306 3.034800 -0.319082
+v 0.213234 3.034800 -0.290295
+v 0.254788 3.034800 -0.254788
+v 0.290295 3.034800 -0.213234
+v 0.319082 3.034800 -0.166306
+v 0.340477 3.034800 -0.114676
+v 0.353807 3.034800 -0.059016
+v 0.358400 3.034800 -0.000000
+v 0.353807 3.034800 0.059016
+v 0.340477 3.034800 0.114676
+v 0.319082 3.034800 0.166306
+v 0.290295 3.034800 0.213234
+v 0.254788 3.034800 0.254788
+v 0.213234 3.034800 0.290295
+v 0.166306 3.034800 0.319082
+v 0.114676 3.034800 0.340477
+v 0.059016 3.034800 0.353807
+v 0.000000 3.034800 0.358400
+v -0.059016 3.034800 0.353807
+v -0.114676 3.034800 0.340477
+v -0.166306 3.034800 0.319082
+v -0.213234 3.034800 0.290295
+v -0.254788 3.034800 0.254788
+v -0.290295 3.034800 0.213234
+v -0.319082 3.034800 0.166306
+v -0.340477 3.034800 0.114676
+v -0.353807 3.034800 0.059016
+v -0.358400 3.034800 -0.000000
+v -0.353807 3.034800 -0.059016
+v -0.340477 3.034800 -0.114676
+v -0.319082 3.034800 -0.166306
+v -0.290295 3.034800 -0.213234
+v -0.254788 3.034800 -0.254788
+v -0.213234 3.034800 -0.290295
+v -0.150793 2.981250 -0.289340
+v -0.193348 2.981250 -0.263232
+v -0.103976 2.981250 -0.308744
+v -0.053508 2.981250 -0.320834
+v 0.000000 2.981250 -0.325000
+v 0.053508 2.981250 -0.320834
+v 0.103976 2.981250 -0.308744
+v 0.150793 2.981250 -0.289340
+v 0.193348 2.981250 -0.263232
+v 0.231031 2.981250 -0.231031
+v 0.263232 2.981250 -0.193348
+v 0.289340 2.981250 -0.150793
+v 0.308744 2.981250 -0.103976
+v 0.320834 2.981250 -0.053508
+v 0.325000 2.981250 -0.000000
+v 0.320834 2.981250 0.053508
+v 0.308744 2.981250 0.103976
+v 0.289340 2.981250 0.150793
+v 0.263232 2.981250 0.193348
+v 0.231031 2.981250 0.231031
+v 0.193348 2.981250 0.263232
+v 0.150793 2.981250 0.289340
+v 0.103976 2.981250 0.308744
+v 0.053508 2.981250 0.320834
+v 0.000000 2.981250 0.325000
+v -0.053508 2.981250 0.320834
+v -0.103976 2.981250 0.308744
+v -0.150793 2.981250 0.289340
+v -0.193348 2.981250 0.263232
+v -0.231031 2.981250 0.231031
+v -0.263232 2.981250 0.193348
+v -0.289340 2.981250 0.150793
+v -0.308744 2.981250 0.103976
+v -0.320834 2.981250 0.053508
+v -0.325000 2.981250 -0.000000
+v -0.320834 2.981250 -0.053508
+v -0.308744 2.981250 -0.103976
+v -0.289340 2.981250 -0.150793
+v -0.263232 2.981250 -0.193348
+v -0.231031 2.981250 -0.231031
+v -0.162745 2.923200 -0.221585
+v -0.194472 2.923200 -0.194472
+v -0.126920 2.923200 -0.243569
+v -0.087510 2.923200 -0.259910
+v -0.045032 2.923200 -0.270092
+v 0.000000 2.923200 -0.273600
+v 0.045032 2.923200 -0.270092
+v 0.087510 2.923200 -0.259910
+v 0.126920 2.923200 -0.243569
+v 0.162745 2.923200 -0.221585
+v 0.194472 2.923200 -0.194472
+v 0.221585 2.923200 -0.162745
+v 0.243569 2.923200 -0.126920
+v 0.259910 2.923200 -0.087511
+v 0.270092 2.923200 -0.045033
+v 0.273600 2.923200 -0.000000
+v 0.270092 2.923200 0.045032
+v 0.259910 2.923200 0.087510
+v 0.243569 2.923200 0.126920
+v 0.221585 2.923200 0.162745
+v 0.194472 2.923200 0.194472
+v 0.162745 2.923200 0.221585
+v 0.126920 2.923200 0.243569
+v 0.087510 2.923200 0.259910
+v 0.045032 2.923200 0.270092
+v 0.000000 2.923200 0.273600
+v -0.045032 2.923200 0.270092
+v -0.087510 2.923200 0.259910
+v -0.126920 2.923200 0.243569
+v -0.162745 2.923200 0.221585
+v -0.194472 2.923200 0.194472
+v -0.221585 2.923200 0.162745
+v -0.243569 2.923200 0.126920
+v -0.259910 2.923200 0.087510
+v -0.270092 2.923200 0.045032
+v -0.273600 2.923200 -0.000000
+v -0.270092 2.923200 -0.045033
+v -0.259910 2.923200 -0.087511
+v -0.243569 2.923200 -0.126920
+v -0.221585 2.923200 -0.162745
+v -0.156200 2.863350 -0.156200
+v -0.177989 2.863350 -0.130707
+v -0.130707 2.863350 -0.177989
+v -0.101925 2.863350 -0.195658
+v -0.070270 2.863350 -0.208794
+v -0.036157 2.863350 -0.216979
+v 0.000000 2.863350 -0.219800
+v 0.036157 2.863350 -0.216979
+v 0.070270 2.863350 -0.208794
+v 0.101925 2.863350 -0.195658
+v 0.130707 2.863350 -0.177989
+v 0.156200 2.863350 -0.156200
+v 0.177989 2.863350 -0.130707
+v 0.195658 2.863350 -0.101925
+v 0.208794 2.863350 -0.070270
+v 0.216979 2.863350 -0.036157
+v 0.219800 2.863350 -0.000000
+v 0.216979 2.863350 0.036156
+v 0.208794 2.863350 0.070270
+v 0.195658 2.863350 0.101925
+v 0.177989 2.863350 0.130707
+v 0.156200 2.863350 0.156200
+v 0.130707 2.863350 0.177989
+v 0.101925 2.863350 0.195658
+v 0.070270 2.863350 0.208794
+v 0.036157 2.863350 0.216979
+v 0.000000 2.863350 0.219800
+v -0.036157 2.863350 0.216979
+v -0.070270 2.863350 0.208794
+v -0.101925 2.863350 0.195658
+v -0.130707 2.863350 0.177989
+v -0.156200 2.863350 0.156200
+v -0.177989 2.863350 0.130707
+v -0.195658 2.863350 0.101925
+v -0.208794 2.863350 0.070270
+v -0.216979 2.863350 0.036156
+v -0.219800 2.863350 -0.000000
+v -0.216979 2.863350 -0.036157
+v -0.208794 2.863350 -0.070270
+v -0.195658 2.863350 -0.101925
+v -0.145078 2.804400 -0.106513
+v -0.159496 2.804400 -0.083047
+v -0.127304 2.804400 -0.127304
+v -0.106513 2.804400 -0.145078
+v -0.083047 2.804400 -0.159496
+v -0.057246 2.804400 -0.170215
+v -0.029450 2.804400 -0.176897
+v 0.000000 2.804400 -0.179200
+v 0.029450 2.804400 -0.176897
+v 0.057246 2.804400 -0.170215
+v 0.083047 2.804400 -0.159496
+v 0.106513 2.804400 -0.145078
+v 0.127304 2.804400 -0.127304
+v 0.145078 2.804400 -0.106513
+v 0.159496 2.804400 -0.083047
+v 0.170215 2.804400 -0.057246
+v 0.176897 2.804400 -0.029450
+v 0.179200 2.804400 -0.000000
+v 0.176897 2.804400 0.029450
+v 0.170215 2.804400 0.057245
+v 0.159496 2.804400 0.083047
+v 0.145078 2.804400 0.106513
+v 0.127304 2.804400 0.127304
+v 0.106513 2.804400 0.145078
+v 0.083047 2.804400 0.159496
+v 0.057246 2.804400 0.170215
+v 0.029450 2.804400 0.176897
+v 0.000000 2.804400 0.179200
+v -0.029450 2.804400 0.176897
+v -0.057246 2.804400 0.170215
+v -0.083047 2.804400 0.159496
+v -0.106513 2.804400 0.145078
+v -0.127304 2.804400 0.127304
+v -0.145078 2.804400 0.106513
+v -0.159496 2.804400 0.083047
+v -0.170215 2.804400 0.057245
+v -0.176897 2.804400 0.029450
+v -0.179200 2.804400 -0.000000
+v -0.176897 2.804400 -0.029450
+v -0.170215 2.804400 -0.057246
+v -0.148969 2.749050 -0.077524
+v -0.158995 2.749050 -0.053428
+v -0.135489 2.749050 -0.099446
+v -0.118874 2.749050 -0.118874
+v -0.099445 2.749050 -0.135489
+v -0.077523 2.749050 -0.148969
+v -0.053428 2.749050 -0.158995
+v -0.027480 2.749050 -0.165245
+v 0.000000 2.749050 -0.167400
+v 0.027480 2.749050 -0.165245
+v 0.053428 2.749050 -0.158995
+v 0.077523 2.749050 -0.148969
+v 0.099445 2.749050 -0.135489
+v 0.118874 2.749050 -0.118874
+v 0.135489 2.749050 -0.099446
+v 0.148969 2.749050 -0.077524
+v 0.158995 2.749050 -0.053428
+v 0.165245 2.749050 -0.027480
+v 0.167400 2.749050 -0.000000
+v 0.165245 2.749050 0.027480
+v 0.158995 2.749050 0.053428
+v 0.148969 2.749050 0.077523
+v 0.135489 2.749050 0.099445
+v 0.118874 2.749050 0.118874
+v 0.099445 2.749050 0.135489
+v 0.077523 2.749050 0.148969
+v 0.053428 2.749050 0.158995
+v 0.027480 2.749050 0.165245
+v 0.000000 2.749050 0.167400
+v -0.027480 2.749050 0.165245
+v -0.053428 2.749050 0.158995
+v -0.077523 2.749050 0.148969
+v -0.099445 2.749050 0.135489
+v -0.118874 2.749050 0.118874
+v -0.135489 2.749050 0.099445
+v -0.148969 2.749050 0.077523
+v -0.158995 2.749050 0.053428
+v -0.165245 2.749050 0.027480
+v -0.167400 2.749050 -0.000000
+v -0.165245 2.749050 -0.027480
+v -0.189952 2.700000 -0.063808
+v -0.197424 2.700000 -0.032816
+v -0.177968 2.700000 -0.092592
+v -0.161856 2.700000 -0.118784
+v -0.142000 2.700000 -0.142000
+v -0.118784 2.700000 -0.161856
+v -0.092592 2.700000 -0.177968
+v -0.063808 2.700000 -0.189952
+v -0.032816 2.700000 -0.197424
+v 0.000000 2.700000 -0.200000
+v 0.032816 2.700000 -0.197424
+v 0.063808 2.700000 -0.189952
+v 0.092592 2.700000 -0.177968
+v 0.118784 2.700000 -0.161856
+v 0.142000 2.700000 -0.142000
+v 0.161856 2.700000 -0.118784
+v 0.177968 2.700000 -0.092592
+v 0.189952 2.700000 -0.063808
+v 0.197424 2.700000 -0.032816
+v 0.200000 2.700000 -0.000000
+v 0.197424 2.700000 0.032816
+v 0.189952 2.700000 0.063808
+v 0.177968 2.700000 0.092592
+v 0.161856 2.700000 0.118784
+v 0.142000 2.700000 0.142000
+v 0.118784 2.700000 0.161856
+v 0.092592 2.700000 0.177968
+v 0.063808 2.700000 0.189952
+v 0.032816 2.700000 0.197424
+v 0.000000 2.700000 0.200000
+v -0.032816 2.700000 0.197424
+v -0.063808 2.700000 0.189952
+v -0.092592 2.700000 0.177968
+v -0.118784 2.700000 0.161856
+v -0.142000 2.700000 0.142000
+v -0.161856 2.700000 0.118784
+v -0.177968 2.700000 0.092592
+v -0.189952 2.700000 0.063808
+v -0.197424 2.700000 0.032816
+v -0.200000 2.700000 -0.000000
+v -0.275801 2.659200 -0.045844
+v -0.279400 2.659200 -0.000000
+v -0.265363 2.659200 -0.089140
+v -0.248621 2.659200 -0.129351
+v -0.226113 2.659200 -0.165941
+v -0.198374 2.659200 -0.198374
+v -0.165941 2.659200 -0.226113
+v -0.129351 2.659200 -0.248621
+v -0.089140 2.659200 -0.265363
+v -0.045844 2.659200 -0.275801
+v 0.000000 2.659200 -0.279400
+v 0.045844 2.659200 -0.275801
+v 0.089140 2.659200 -0.265363
+v 0.129351 2.659200 -0.248621
+v 0.165941 2.659200 -0.226113
+v 0.198374 2.659200 -0.198374
+v 0.226113 2.659200 -0.165941
+v 0.248621 2.659200 -0.129351
+v 0.265363 2.659200 -0.089140
+v 0.275801 2.659200 -0.045844
+v 0.279400 2.659200 -0.000000
+v 0.275801 2.659200 0.045844
+v 0.265363 2.659200 0.089140
+v 0.248621 2.659200 0.129351
+v 0.226113 2.659200 0.165941
+v 0.198374 2.659200 0.198374
+v 0.165941 2.659200 0.226113
+v 0.129351 2.659200 0.248621
+v 0.089140 2.659200 0.265363
+v 0.045844 2.659200 0.275801
+v 0.000000 2.659200 0.279400
+v -0.045844 2.659200 0.275801
+v -0.089140 2.659200 0.265363
+v -0.129351 2.659200 0.248621
+v -0.165941 2.659200 0.226113
+v -0.198374 2.659200 0.198374
+v -0.226113 2.659200 0.165941
+v -0.248621 2.659200 0.129351
+v -0.265363 2.659200 0.089140
+v -0.275801 2.659200 0.045844
+v -0.391200 2.625600 -0.000000
+v -0.386161 2.625600 0.064188
+v -0.386161 2.625600 -0.064188
+v -0.371546 2.625600 -0.124808
+v -0.348105 2.625600 -0.181110
+v -0.316590 2.625600 -0.232342
+v -0.277752 2.625600 -0.277752
+v -0.232342 2.625600 -0.316590
+v -0.181110 2.625600 -0.348105
+v -0.124808 2.625600 -0.371546
+v -0.064188 2.625600 -0.386161
+v 0.000000 2.625600 -0.391200
+v 0.064188 2.625600 -0.386161
+v 0.124808 2.625600 -0.371546
+v 0.181110 2.625600 -0.348105
+v 0.232342 2.625600 -0.316590
+v 0.277752 2.625600 -0.277752
+v 0.316590 2.625600 -0.232342
+v 0.348105 2.625600 -0.181110
+v 0.371546 2.625600 -0.124808
+v 0.386161 2.625600 -0.064188
+v 0.391200 2.625600 -0.000000
+v 0.386161 2.625600 0.064188
+v 0.371546 2.625600 0.124808
+v 0.348105 2.625600 0.181110
+v 0.316590 2.625600 0.232342
+v 0.277752 2.625600 0.277752
+v 0.232342 2.625600 0.316590
+v 0.181110 2.625600 0.348105
+v 0.124808 2.625600 0.371546
+v 0.064188 2.625600 0.386161
+v 0.000000 2.625600 0.391200
+v -0.064188 2.625600 0.386161
+v -0.124808 2.625600 0.371546
+v -0.181110 2.625600 0.348105
+v -0.232342 2.625600 0.316590
+v -0.277752 2.625600 0.277752
+v -0.316590 2.625600 0.232342
+v -0.348105 2.625600 0.181110
+v -0.371546 2.625600 0.124808
+v -0.519028 2.597400 0.086273
+v -0.499384 2.597400 0.167751
+v -0.525800 2.597400 -0.000000
+v -0.519028 2.597400 -0.086273
+v -0.499384 2.597400 -0.167751
+v -0.467878 2.597400 -0.243424
+v -0.425519 2.597400 -0.312283
+v -0.373318 2.597400 -0.373318
+v -0.312283 2.597400 -0.425519
+v -0.243424 2.597400 -0.467878
+v -0.167751 2.597400 -0.499384
+v -0.086273 2.597400 -0.519028
+v 0.000000 2.597400 -0.525800
+v 0.086273 2.597400 -0.519028
+v 0.167751 2.597400 -0.499384
+v 0.243424 2.597400 -0.467878
+v 0.312283 2.597400 -0.425519
+v 0.373318 2.597400 -0.373318
+v 0.425519 2.597400 -0.312283
+v 0.467878 2.597400 -0.243424
+v 0.499384 2.597400 -0.167751
+v 0.519028 2.597400 -0.086273
+v 0.525800 2.597400 -0.000000
+v 0.519028 2.597400 0.086273
+v 0.499384 2.597400 0.167751
+v 0.467878 2.597400 0.243424
+v 0.425519 2.597400 0.312283
+v 0.373318 2.597400 0.373318
+v 0.312283 2.597400 0.425519
+v 0.243424 2.597400 0.467878
+v 0.167751 2.597400 0.499384
+v 0.086273 2.597400 0.519028
+v 0.000000 2.597400 0.525800
+v -0.086273 2.597400 0.519028
+v -0.167751 2.597400 0.499384
+v -0.243424 2.597400 0.467878
+v -0.312283 2.597400 0.425519
+v -0.373318 2.597400 0.373318
+v -0.425519 2.597400 0.312283
+v -0.467878 2.597400 0.243424
+v -0.639758 2.572800 0.214905
+v -0.599396 2.572800 0.311850
+v -0.664924 2.572800 0.110524
+v -0.673600 2.572800 -0.000000
+v -0.664924 2.572800 -0.110524
+v -0.639758 2.572800 -0.214905
+v -0.599396 2.572800 -0.311850
+v -0.545131 2.572800 -0.400065
+v -0.478256 2.572800 -0.478256
+v -0.400065 2.572800 -0.545131
+v -0.311850 2.572800 -0.599396
+v -0.214905 2.572800 -0.639758
+v -0.110524 2.572800 -0.664924
+v 0.000000 2.572800 -0.673600
+v 0.110524 2.572800 -0.664924
+v 0.214905 2.572800 -0.639758
+v 0.311850 2.572800 -0.599396
+v 0.400065 2.572800 -0.545131
+v 0.478256 2.572800 -0.478256
+v 0.545131 2.572800 -0.400065
+v 0.599396 2.572800 -0.311850
+v 0.639758 2.572800 -0.214905
+v 0.664924 2.572800 -0.110524
+v 0.673600 2.572800 -0.000000
+v 0.664924 2.572800 0.110524
+v 0.639758 2.572800 0.214905
+v 0.599396 2.572800 0.311850
+v 0.545131 2.572800 0.400065
+v 0.478256 2.572800 0.478256
+v 0.400065 2.572800 0.545131
+v 0.311850 2.572800 0.599396
+v 0.214905 2.572800 0.639758
+v 0.110524 2.572800 0.664924
+v 0.000000 2.572800 0.673600
+v -0.110524 2.572800 0.664924
+v -0.214905 2.572800 0.639758
+v -0.311850 2.572800 0.599396
+v -0.400065 2.572800 0.545131
+v -0.478256 2.572800 0.478256
+v -0.545131 2.572800 0.400065
+v -0.734118 2.550000 0.381942
+v -0.667656 2.550000 0.489984
+v -0.783552 2.550000 0.263208
+v -0.814374 2.550000 0.135366
+v -0.825000 2.550000 -0.000000
+v -0.814374 2.550000 -0.135366
+v -0.783552 2.550000 -0.263208
+v -0.734118 2.550000 -0.381942
+v -0.667656 2.550000 -0.489984
+v -0.585750 2.550000 -0.585750
+v -0.489984 2.550000 -0.667656
+v -0.381942 2.550000 -0.734118
+v -0.263208 2.550000 -0.783552
+v -0.135366 2.550000 -0.814374
+v 0.000000 2.550000 -0.825000
+v 0.135366 2.550000 -0.814374
+v 0.263208 2.550000 -0.783552
+v 0.381942 2.550000 -0.734118
+v 0.489984 2.550000 -0.667656
+v 0.585750 2.550000 -0.585750
+v 0.667656 2.550000 -0.489984
+v 0.734118 2.550000 -0.381942
+v 0.783552 2.550000 -0.263208
+v 0.814374 2.550000 -0.135366
+v 0.825000 2.550000 -0.000000
+v 0.814374 2.550000 0.135366
+v 0.783552 2.550000 0.263208
+v 0.734118 2.550000 0.381942
+v 0.667656 2.550000 0.489984
+v 0.585750 2.550000 0.585750
+v 0.489984 2.550000 0.667656
+v 0.381942 2.550000 0.734118
+v 0.263208 2.550000 0.783552
+v 0.135366 2.550000 0.814374
+v 0.000000 2.550000 0.825000
+v -0.135366 2.550000 0.814374
+v -0.263208 2.550000 0.783552
+v -0.381942 2.550000 0.734118
+v -0.489984 2.550000 0.667656
+v -0.585750 2.550000 0.585750
+v -0.785325 2.527200 0.576340
+v -0.688984 2.527200 0.688984
+v -0.863501 2.527200 0.449256
+v -0.921647 2.527200 0.309596
+v -0.957901 2.527200 0.159223
+v -0.970400 2.527200 -0.000000
+v -0.957901 2.527200 -0.159223
+v -0.921647 2.527200 -0.309596
+v -0.863501 2.527200 -0.449256
+v -0.785325 2.527200 -0.576340
+v -0.688984 2.527200 -0.688984
+v -0.576340 2.527200 -0.785325
+v -0.449256 2.527200 -0.863501
+v -0.309596 2.527200 -0.921647
+v -0.159223 2.527200 -0.957901
+v 0.000000 2.527200 -0.970400
+v 0.159223 2.527200 -0.957901
+v 0.309596 2.527200 -0.921647
+v 0.449256 2.527200 -0.863501
+v 0.576340 2.527200 -0.785325
+v 0.688984 2.527200 -0.688984
+v 0.785325 2.527200 -0.576340
+v 0.863501 2.527200 -0.449256
+v 0.921647 2.527200 -0.309596
+v 0.957901 2.527200 -0.159223
+v 0.970400 2.527200 -0.000000
+v 0.957901 2.527200 0.159223
+v 0.921647 2.527200 0.309596
+v 0.863501 2.527200 0.449256
+v 0.785325 2.527200 0.576340
+v 0.688984 2.527200 0.688984
+v 0.576340 2.527200 0.785325
+v 0.449256 2.527200 0.863501
+v 0.309596 2.527200 0.921647
+v 0.159223 2.527200 0.957901
+v 0.000000 2.527200 0.970400
+v -0.159223 2.527200 0.957901
+v -0.309596 2.527200 0.921647
+v -0.449256 2.527200 0.863501
+v -0.576340 2.527200 0.785325
+v -0.781142 2.502600 0.781142
+v -0.653431 2.502600 0.890370
+v -0.890370 2.502600 0.653431
+v -0.979002 2.502600 0.509349
+v -1.044930 2.502600 0.351008
+v -1.086030 2.502600 0.180521
+v -1.100200 2.502600 -0.000000
+v -1.086030 2.502600 -0.180521
+v -1.044930 2.502600 -0.351008
+v -0.979002 2.502600 -0.509349
+v -0.890370 2.502600 -0.653431
+v -0.781142 2.502600 -0.781142
+v -0.653431 2.502600 -0.890370
+v -0.509349 2.502600 -0.979002
+v -0.351008 2.502600 -1.044930
+v -0.180521 2.502600 -1.086030
+v 0.000000 2.502600 -1.100200
+v 0.180521 2.502600 -1.086030
+v 0.351008 2.502600 -1.044930
+v 0.509349 2.502600 -0.979002
+v 0.653431 2.502600 -0.890370
+v 0.781142 2.502600 -0.781142
+v 0.890370 2.502600 -0.653431
+v 0.979002 2.502600 -0.509349
+v 1.044930 2.502600 -0.351008
+v 1.086030 2.502600 -0.180521
+v 1.100200 2.502600 -0.000000
+v 1.086030 2.502600 0.180521
+v 1.044930 2.502600 0.351008
+v 0.979002 2.502600 0.509349
+v 0.890370 2.502600 0.653431
+v 0.781142 2.502600 0.781142
+v 0.653431 2.502600 0.890370
+v 0.509349 2.502600 0.979002
+v 0.351008 2.502600 1.044930
+v 0.180521 2.502600 1.086030
+v 0.000000 2.502600 1.100200
+v -0.180521 2.502600 1.086030
+v -0.351008 2.502600 1.044930
+v -0.509349 2.502600 0.979002
+v -0.715555 2.474400 0.975021
+v -0.557774 2.474400 1.072080
+v -0.855408 2.474400 0.855408
+v -0.975021 2.474400 0.715555
+v -1.072080 2.474400 0.557774
+v -1.144270 2.474400 0.384379
+v -1.189280 2.474400 0.197684
+v -1.204800 2.474400 -0.000000
+v -1.189280 2.474400 -0.197684
+v -1.144270 2.474400 -0.384379
+v -1.072080 2.474400 -0.557774
+v -0.975021 2.474400 -0.715555
+v -0.855408 2.474400 -0.855408
+v -0.715555 2.474400 -0.975021
+v -0.557774 2.474400 -1.072080
+v -0.384379 2.474400 -1.144270
+v -0.197684 2.474400 -1.189280
+v 0.000000 2.474400 -1.204800
+v 0.197684 2.474400 -1.189280
+v 0.384379 2.474400 -1.144270
+v 0.557774 2.474400 -1.072080
+v 0.715555 2.474400 -0.975021
+v 0.855408 2.474400 -0.855408
+v 0.975021 2.474400 -0.715555
+v 1.072080 2.474400 -0.557774
+v 1.144270 2.474400 -0.384379
+v 1.189280 2.474400 -0.197684
+v 1.204800 2.474400 -0.000000
+v 1.189280 2.474400 0.197684
+v 1.144270 2.474400 0.384379
+v 1.072080 2.474400 0.557774
+v 0.975021 2.474400 0.715555
+v 0.855408 2.474400 0.855408
+v 0.715555 2.474400 0.975021
+v 0.557774 2.474400 1.072080
+v 0.384379 2.474400 1.144270
+v 0.197684 2.474400 1.189280
+v 0.000000 2.474400 1.204800
+v -0.197684 2.474400 1.189280
+v -0.384379 2.474400 1.144270
+v -0.590089 2.440800 1.134190
+v -0.406648 2.440800 1.210560
+v -0.757010 2.440800 1.031510
+v -0.904966 2.440800 0.904966
+v -1.031510 2.440800 0.757010
+v -1.134190 2.440800 0.590089
+v -1.210560 2.440800 0.406648
+v -1.258180 2.440800 0.209136
+v -1.274600 2.440800 -0.000000
+v -1.258180 2.440800 -0.209136
+v -1.210560 2.440800 -0.406648
+v -1.134190 2.440800 -0.590089
+v -1.031510 2.440800 -0.757010
+v -0.904966 2.440800 -0.904966
+v -0.757010 2.440800 -1.031510
+v -0.590089 2.440800 -1.134190
+v -0.406648 2.440800 -1.210560
+v -0.209136 2.440800 -1.258180
+v 0.000000 2.440800 -1.274600
+v 0.209136 2.440800 -1.258180
+v 0.406648 2.440800 -1.210560
+v 0.590089 2.440800 -1.134190
+v 0.757010 2.440800 -1.031510
+v 0.904966 2.440800 -0.904966
+v 1.031510 2.440800 -0.757010
+v 1.134190 2.440800 -0.590089
+v 1.210560 2.440800 -0.406648
+v 1.258180 2.440800 -0.209136
+v 1.274600 2.440800 -0.000000
+v 1.258180 2.440800 0.209136
+v 1.210560 2.440800 0.406648
+v 1.134190 2.440800 0.590089
+v 1.031510 2.440800 0.757010
+v 0.904966 2.440800 0.904966
+v 0.757010 2.440800 1.031510
+v 0.590089 2.440800 1.134190
+v 0.406648 2.440800 1.210560
+v 0.209136 2.440800 1.258180
+v 0.000000 2.440800 1.274600
+v -0.209136 2.440800 1.258180
+v -0.414752 2.400000 1.234690
+v -0.213304 2.400000 1.283260
+v -0.601848 2.400000 1.156790
+v -0.772096 2.400000 1.052060
+v -0.923000 2.400000 0.923000
+v -1.052060 2.400000 0.772096
+v -1.156790 2.400000 0.601848
+v -1.234690 2.400000 0.414752
+v -1.283260 2.400000 0.213304
+v -1.300000 2.400000 -0.000000
+v -1.283260 2.400000 -0.213304
+v -1.234690 2.400000 -0.414752
+v -1.156790 2.400000 -0.601848
+v -1.052060 2.400000 -0.772096
+v -0.923000 2.400000 -0.923000
+v -0.772096 2.400000 -1.052060
+v -0.601848 2.400000 -1.156790
+v -0.414752 2.400000 -1.234690
+v -0.213304 2.400000 -1.283260
+v 0.000000 2.400000 -1.300000
+v 0.213304 2.400000 -1.283260
+v 0.414752 2.400000 -1.234690
+v 0.601848 2.400000 -1.156790
+v 0.772096 2.400000 -1.052060
+v 0.923000 2.400000 -0.923000
+v 1.052060 2.400000 -0.772096
+v 1.156790 2.400000 -0.601848
+v 1.234690 2.400000 -0.414752
+v 1.283260 2.400000 -0.213304
+v 1.300000 2.400000 -0.000000
+v 1.283260 2.400000 0.213304
+v 1.234690 2.400000 0.414752
+v 1.156790 2.400000 0.601848
+v 1.052060 2.400000 0.772096
+v 0.923000 2.400000 0.923000
+v 0.772096 2.400000 1.052060
+v 0.601848 2.400000 1.156790
+v 0.414752 2.400000 1.234690
+v 0.213304 2.400000 1.283260
+v 0.000000 2.400000 1.300000
+v 0.000000 2.025000 1.600000
+v 0.081000 2.031300 1.597200
+v 0.081000 2.031070 1.800450
+v 0.000000 2.024770 1.800900
+v 0.144000 2.048400 1.589600
+v 0.144000 2.048150 1.799250
+v 0.189000 2.073600 1.578400
+v 0.189000 2.073330 1.797470
+v 0.216000 2.104200 1.564800
+v 0.216000 2.103900 1.795300
+v 0.225000 2.137500 1.550000
+v 0.225000 2.137160 1.792950
+v 0.216000 2.170800 1.535200
+v 0.216000 2.170430 1.790600
+v 0.189000 2.201400 1.521600
+v 0.189000 2.201000 1.788430
+v 0.144000 2.226600 1.510400
+v 0.144000 2.226170 1.786650
+v 0.081000 2.243700 1.502800
+v 0.081000 2.243260 1.785450
+v 0.000000 2.250000 1.500000
+v 0.000000 2.249550 1.785000
+v -0.081000 2.243260 1.785450
+v -0.081000 2.243700 1.502800
+v -0.081000 2.240150 2.038410
+v 0.000000 2.246400 2.040000
+v -0.081000 2.231720 2.261680
+v 0.000000 2.237850 2.265000
+v -0.081000 2.215300 2.455230
+v 0.000000 2.221200 2.460000
+v -0.081000 2.188240 2.619050
+v 0.000000 2.193750 2.625000
+v -0.081000 2.147860 2.753120
+v 0.000000 2.152800 2.760000
+v -0.081000 2.091510 2.857430
+v 0.000000 2.095650 2.865000
+v -0.081000 2.016530 2.931960
+v 0.000000 2.019600 2.940000
+v -0.081000 1.920240 2.976690
+v 0.000000 1.921950 2.985000
+v -0.081000 1.800000 2.991600
+v 0.000000 1.800000 3.000000
+v -0.081000 1.667840 2.981170
+v 0.000000 1.666160 2.989450
+v -0.081000 1.537790 2.949690
+v 0.000000 1.534800 2.957600
+v -0.081000 1.410130 2.896850
+v 0.000000 1.406140 2.904150
+v -0.081000 1.285170 2.822330
+v 0.000000 1.280400 2.828800
+v -0.081000 1.163190 2.725830
+v 0.000000 1.157810 2.731250
+v -0.081000 1.044500 2.607030
+v 0.000000 1.038600 2.611200
+v -0.081000 0.929375 2.465640
+v 0.000000 0.922988 2.468350
+v -0.081000 0.818122 2.301350
+v 0.000000 0.811200 2.302400
+v -0.081000 0.711032 2.113840
+v 0.000000 0.703463 2.113050
+v -0.081000 0.608400 1.902800
+v 0.000000 0.600000 1.900000
+v 0.081000 0.711032 2.113840
+v 0.081000 0.608400 1.902800
+v 0.081000 0.818122 2.301350
+v 0.081000 0.929375 2.465640
+v 0.081000 1.044500 2.607030
+v 0.081000 1.163190 2.725830
+v 0.081000 1.285170 2.822330
+v 0.081000 1.410130 2.896850
+v 0.081000 1.537790 2.949690
+v 0.081000 1.667840 2.981170
+v 0.081000 1.800000 2.991600
+v 0.081000 1.920240 2.976690
+v 0.081000 2.016530 2.931960
+v 0.081000 2.091510 2.857430
+v 0.081000 2.147860 2.753120
+v 0.081000 2.188240 2.619050
+v 0.081000 2.215300 2.455230
+v 0.081000 2.231720 2.261680
+v 0.081000 2.240150 2.038410
+v 0.144000 2.223190 2.034090
+v 0.144000 2.215080 2.252660
+v 0.144000 2.199300 2.442280
+v 0.144000 2.173280 2.602900
+v 0.144000 2.134450 2.734460
+v 0.144000 2.080280 2.836890
+v 0.144000 2.008180 2.910130
+v 0.144000 1.915610 2.954120
+v 0.144000 1.800000 2.968800
+v 0.144000 1.672410 2.958710
+v 0.144000 1.545910 2.928230
+v 0.144000 1.420980 2.877020
+v 0.144000 1.298120 2.804760
+v 0.144000 1.177800 2.711100
+v 0.144000 1.060500 2.595720
+v 0.144000 0.946711 2.458300
+v 0.144000 0.836909 2.298490
+v 0.144000 0.731578 2.115970
+v 0.144000 0.631200 1.910400
+v 0.189000 0.761855 2.119110
+v 0.189000 0.664800 1.921600
+v 0.189000 0.864595 2.294280
+v 0.189000 0.972260 2.447470
+v 0.189000 1.084090 2.579060
+v 0.189000 1.199320 2.689400
+v 0.189000 1.317210 2.778860
+v 0.189000 1.436970 2.847810
+v 0.189000 1.557870 2.896600
+v 0.189000 1.679130 2.925610
+v 0.189000 1.800000 2.935200
+v 0.189000 1.908780 2.920870
+v 0.189000 1.995880 2.877960
+v 0.189000 2.063720 2.806620
+v 0.189000 2.114700 2.706950
+v 0.189000 2.151230 2.579100
+v 0.189000 2.175710 2.423190
+v 0.189000 2.190560 2.239360
+v 0.189000 2.198190 2.027730
+v 0.216000 2.167830 2.020010
+v 0.216000 2.160790 2.223220
+v 0.216000 2.147070 2.400020
+v 0.216000 2.124450 2.550200
+v 0.216000 2.090710 2.673550
+v 0.216000 2.043620 2.769850
+v 0.216000 1.980950 2.838910
+v 0.216000 1.900490 2.880490
+v 0.216000 1.800000 2.894400
+v 0.216000 1.687300 2.885420
+v 0.216000 1.572390 2.858200
+v 0.216000 1.456390 2.812330
+v 0.216000 1.340380 2.747420
+v 0.216000 1.225460 2.663050
+v 0.216000 1.112730 2.558820
+v 0.216000 1.003280 2.434330
+v 0.216000 0.898214 2.289160
+v 0.216000 0.798621 2.122920
+v 0.216000 0.705600 1.935200
+v 0.225000 0.838631 2.127080
+v 0.225000 0.750000 1.950000
+v 0.225000 0.934800 2.283600
+v 0.225000 1.037040 2.420020
+v 0.225000 1.143900 2.536800
+v 0.225000 1.253910 2.634370
+v 0.225000 1.365600 2.713200
+v 0.225000 1.477520 2.773730
+v 0.225000 1.588200 2.816400
+v 0.225000 1.696180 2.841680
+v 0.225000 1.800000 2.850000
+v 0.225000 1.891460 2.836550
+v 0.225000 1.964700 2.796400
+v 0.225000 2.021740 2.729850
+v 0.225000 2.064600 2.637200
+v 0.225000 2.095310 2.518750
+v 0.225000 2.115900 2.374800
+v 0.225000 2.128390 2.205650
+v 0.225000 2.134800 2.011600
+v 0.216000 2.101770 2.003190
+v 0.216000 2.095990 2.188080
+v 0.216000 2.084730 2.349580
+v 0.216000 2.066180 2.487300
+v 0.216000 2.038490 2.600850
+v 0.216000 1.999860 2.689850
+v 0.216000 1.948450 2.753890
+v 0.216000 1.882440 2.792610
+v 0.216000 1.800000 2.805600
+v 0.216000 1.705070 2.797930
+v 0.216000 1.604010 2.774600
+v 0.216000 1.498650 2.735120
+v 0.216000 1.390820 2.678980
+v 0.216000 1.282350 2.605700
+v 0.216000 1.175070 2.514780
+v 0.216000 1.070800 2.405720
+v 0.216000 0.971386 2.278040
+v 0.216000 0.878641 2.131230
+v 0.216000 0.794400 1.964800
+v 0.189000 0.915407 2.135040
+v 0.189000 0.835200 1.978400
+v 0.189000 1.005000 2.272920
+v 0.189000 1.101830 2.392580
+v 0.189000 1.203710 2.494540
+v 0.189000 1.308490 2.579350
+v 0.189000 1.413990 2.647540
+v 0.189000 1.518060 2.699640
+v 0.189000 1.618530 2.736200
+v 0.189000 1.713230 2.757740
+v 0.189000 1.800000 2.764800
+v 0.189000 1.874150 2.752230
+v 0.189000 1.933520 2.714840
+v 0.189000 1.979760 2.653080
+v 0.189000 2.014500 2.567450
+v 0.189000 2.039400 2.458400
+v 0.189000 2.056090 2.326410
+v 0.189000 2.066210 2.171940
+v 0.189000 2.071410 1.995470
+v 0.144000 2.046410 1.989110
+v 0.144000 2.041690 2.158640
+v 0.144000 2.032500 2.307320
+v 0.144000 2.017350 2.434600
+v 0.144000 1.994750 2.539940
+v 0.144000 1.963200 2.622810
+v 0.144000 1.921220 2.682670
+v 0.144000 1.867320 2.718980
+v 0.144000 1.800000 2.731200
+v 0.144000 1.719960 2.724640
+v 0.144000 1.630490 2.704570
+v 0.144000 1.534050 2.670430
+v 0.144000 1.433080 2.621640
+v 0.144000 1.330010 2.557650
+v 0.144000 1.227300 2.477880
+v 0.144000 1.127380 2.381750
+v 0.144000 1.032690 2.268710
+v 0.144000 0.945685 2.138180
+v 0.144000 0.868800 1.989600
+v 0.081000 0.966231 2.140310
+v 0.081000 0.891600 1.997200
+v 0.081000 1.051480 2.265850
+v 0.081000 1.144710 2.374410
+v 0.081000 1.243300 2.466570
+v 0.081000 1.344620 2.542930
+v 0.081000 1.446030 2.604070
+v 0.081000 1.544900 2.650600
+v 0.081000 1.638610 2.683110
+v 0.081000 1.724520 2.702180
+v 0.081000 1.800000 2.708400
+v 0.081000 1.862680 2.696410
+v 0.081000 1.912870 2.660840
+v 0.081000 1.951960 2.602270
+v 0.081000 1.981340 2.521280
+v 0.081000 2.002390 2.418450
+v 0.081000 2.016500 2.294370
+v 0.081000 2.025050 2.149620
+v 0.081000 2.029450 1.984790
+v 0.000000 2.023200 1.983200
+v 0.000000 2.018920 2.146300
+v 0.000000 2.010600 2.289600
+v 0.000000 1.996870 2.412500
+v 0.000000 1.976400 2.514400
+v 0.000000 1.947820 2.594700
+v 0.000000 1.909800 2.652800
+v 0.000000 1.860980 2.688100
+v 0.000000 1.800000 2.700000
+v 0.000000 1.726200 2.693900
+v 0.000000 1.641600 2.675200
+v 0.000000 1.548900 2.643300
+v 0.000000 1.450800 2.597600
+v 0.000000 1.350000 2.537500
+v 0.000000 1.249200 2.462400
+v 0.000000 1.151100 2.371700
+v 0.000000 1.058400 2.264800
+v 0.000000 0.973800 2.141100
+v -0.081000 0.891600 1.997200
+v -0.081000 0.966231 2.140310
+v -0.144000 0.868800 1.989600
+v -0.144000 0.945685 2.138180
+v -0.189000 0.835200 1.978400
+v -0.189000 0.915407 2.135040
+v -0.216000 0.794400 1.964800
+v -0.216000 0.878641 2.131230
+v -0.225000 0.750000 1.950000
+v -0.225000 0.838631 2.127080
+v -0.216000 0.705600 1.935200
+v -0.216000 0.798621 2.122920
+v -0.189000 0.664800 1.921600
+v -0.189000 0.761855 2.119110
+v -0.144000 0.631200 1.910400
+v -0.144000 0.731578 2.115970
+v -0.144000 0.836909 2.298490
+v -0.144000 0.946711 2.458300
+v -0.144000 1.060500 2.595720
+v -0.144000 1.177800 2.711100
+v -0.144000 1.298120 2.804760
+v -0.144000 1.420980 2.877020
+v -0.144000 1.545910 2.928230
+v -0.144000 1.672410 2.958710
+v -0.144000 1.800000 2.968800
+v -0.144000 1.915610 2.954120
+v -0.144000 2.008180 2.910130
+v -0.144000 2.080280 2.836890
+v -0.144000 2.134450 2.734460
+v -0.144000 2.173280 2.602900
+v -0.144000 2.199300 2.442280
+v -0.144000 2.215080 2.252660
+v -0.144000 2.223190 2.034090
+v -0.144000 2.226170 1.786650
+v -0.144000 2.226600 1.510400
+v -0.189000 2.201000 1.788430
+v -0.189000 2.201400 1.521600
+v -0.189000 2.198190 2.027730
+v -0.189000 2.190560 2.239360
+v -0.189000 2.175710 2.423190
+v -0.189000 2.151230 2.579100
+v -0.189000 2.114700 2.706950
+v -0.189000 2.063720 2.806620
+v -0.189000 1.995880 2.877960
+v -0.189000 1.908780 2.920870
+v -0.189000 1.800000 2.935200
+v -0.189000 1.679130 2.925610
+v -0.189000 1.557870 2.896600
+v -0.189000 1.436970 2.847810
+v -0.189000 1.317210 2.778860
+v -0.189000 1.199320 2.689400
+v -0.189000 1.084090 2.579060
+v -0.189000 0.972260 2.447470
+v -0.189000 0.864595 2.294280
+v -0.216000 0.898214 2.289160
+v -0.216000 1.003280 2.434330
+v -0.216000 1.112730 2.558820
+v -0.216000 1.225460 2.663050
+v -0.216000 1.340380 2.747420
+v -0.216000 1.456390 2.812330
+v -0.216000 1.572390 2.858200
+v -0.216000 1.687300 2.885420
+v -0.216000 1.800000 2.894400
+v -0.216000 1.900490 2.880490
+v -0.216000 1.980950 2.838910
+v -0.216000 2.043620 2.769850
+v -0.216000 2.090710 2.673550
+v -0.216000 2.124450 2.550200
+v -0.216000 2.147070 2.400020
+v -0.216000 2.160790 2.223220
+v -0.216000 2.167830 2.020010
+v -0.216000 2.170430 1.790600
+v -0.216000 2.170800 1.535200
+v -0.225000 2.137160 1.792950
+v -0.225000 2.137500 1.550000
+v -0.225000 2.134800 2.011600
+v -0.225000 2.128390 2.205650
+v -0.225000 2.115900 2.374800
+v -0.225000 2.095310 2.518750
+v -0.225000 2.064600 2.637200
+v -0.225000 2.021740 2.729850
+v -0.225000 1.964700 2.796400
+v -0.225000 1.891460 2.836550
+v -0.225000 1.800000 2.850000
+v -0.225000 1.696180 2.841680
+v -0.225000 1.588200 2.816400
+v -0.225000 1.477520 2.773730
+v -0.225000 1.365600 2.713200
+v -0.225000 1.253910 2.634370
+v -0.225000 1.143900 2.536800
+v -0.225000 1.037040 2.420020
+v -0.225000 0.934800 2.283600
+v -0.216000 0.971386 2.278040
+v -0.216000 1.070800 2.405720
+v -0.216000 1.175070 2.514780
+v -0.216000 1.282350 2.605700
+v -0.216000 1.390820 2.678980
+v -0.216000 1.498650 2.735120
+v -0.216000 1.604010 2.774600
+v -0.216000 1.705070 2.797930
+v -0.216000 1.800000 2.805600
+v -0.216000 1.882440 2.792610
+v -0.216000 1.948450 2.753890
+v -0.216000 1.999860 2.689850
+v -0.216000 2.038490 2.600850
+v -0.216000 2.066180 2.487300
+v -0.216000 2.084730 2.349580
+v -0.216000 2.095990 2.188080
+v -0.216000 2.101770 2.003190
+v -0.216000 2.103900 1.795300
+v -0.216000 2.104200 1.564800
+v -0.189000 2.073330 1.797470
+v -0.189000 2.073600 1.578400
+v -0.189000 2.071410 1.995470
+v -0.189000 2.066210 2.171940
+v -0.189000 2.056090 2.326410
+v -0.189000 2.039400 2.458400
+v -0.189000 2.014500 2.567450
+v -0.189000 1.979760 2.653080
+v -0.189000 1.933520 2.714840
+v -0.189000 1.874150 2.752230
+v -0.189000 1.800000 2.764800
+v -0.189000 1.713230 2.757740
+v -0.189000 1.618530 2.736200
+v -0.189000 1.518060 2.699640
+v -0.189000 1.413990 2.647540
+v -0.189000 1.308490 2.579350
+v -0.189000 1.203710 2.494540
+v -0.189000 1.101830 2.392580
+v -0.189000 1.005000 2.272920
+v -0.144000 1.032690 2.268710
+v -0.144000 1.127380 2.381750
+v -0.144000 1.227300 2.477880
+v -0.144000 1.330010 2.557650
+v -0.144000 1.433080 2.621640
+v -0.144000 1.534050 2.670430
+v -0.144000 1.630490 2.704570
+v -0.144000 1.719960 2.724640
+v -0.144000 1.800000 2.731200
+v -0.144000 1.867320 2.718980
+v -0.144000 1.921220 2.682670
+v -0.144000 1.963200 2.622810
+v -0.144000 1.994750 2.539940
+v -0.144000 2.017350 2.434600
+v -0.144000 2.032500 2.307320
+v -0.144000 2.041690 2.158640
+v -0.144000 2.046410 1.989110
+v -0.144000 2.048150 1.799250
+v -0.144000 2.048400 1.589600
+v -0.081000 2.031070 1.800450
+v -0.081000 2.031300 1.597200
+v -0.081000 2.029450 1.984790
+v -0.081000 2.025050 2.149620
+v -0.081000 2.016500 2.294370
+v -0.081000 2.002390 2.418450
+v -0.081000 1.981340 2.521280
+v -0.081000 1.951960 2.602270
+v -0.081000 1.912870 2.660840
+v -0.081000 1.862680 2.696410
+v -0.081000 1.800000 2.708400
+v -0.081000 1.724520 2.702180
+v -0.081000 1.638610 2.683110
+v -0.081000 1.544900 2.650600
+v -0.081000 1.446030 2.604070
+v -0.081000 1.344620 2.542930
+v -0.081000 1.243300 2.466570
+v -0.081000 1.144710 2.374410
+v -0.081000 1.051480 2.265850
+v 0.000000 1.425000 -1.700000
+v 0.178200 1.401900 -1.700000
+v 0.175100 1.423220 -1.939390
+v 0.311290 1.366280 -1.948880
+v 0.316800 1.339200 -1.700000
+v 0.408568 1.282360 -1.962860
+v 0.415800 1.246800 -1.700000
+v 0.466934 1.180460 -1.979830
+v 0.475200 1.134600 -1.700000
+v 0.486390 1.069570 -1.998300
+v 0.495000 1.012500 -1.700000
+v 0.475200 0.890400 -1.700000
+v 0.466934 0.958686 -2.016770
+v 0.408568 0.856788 -2.033740
+v 0.415800 0.778200 -1.700000
+v 0.311290 0.772872 -2.047720
+v 0.316800 0.685800 -1.700000
+v 0.175100 0.715929 -2.057210
+v 0.178200 0.623100 -1.700000
+v 0.000000 0.600000 -1.700000
+v 0.000000 0.694950 -2.060700
+v -0.175100 0.715929 -2.057210
+v -0.178200 0.623100 -1.700000
+v -0.166687 0.856080 -2.311820
+v 0.000000 0.837600 -2.317600
+v -0.154289 1.032880 -2.485740
+v 0.000000 1.017150 -2.492900
+v -0.139234 1.235650 -2.600870
+v 0.000000 1.222800 -2.608800
+v -0.122850 1.453730 -2.679100
+v 0.000000 1.443750 -2.687500
+v -0.106466 1.676420 -2.742330
+v 0.000000 1.669200 -2.751200
+v -0.091411 1.893070 -2.812460
+v 0.000000 1.888350 -2.822100
+v -0.079013 2.093000 -2.911380
+v 0.000000 2.090400 -2.922400
+v -0.070600 2.265540 -3.060990
+v 0.000000 2.264550 -3.074300
+v -0.067500 2.400000 -3.283200
+v 0.000000 2.400000 -3.300000
+v -0.066744 2.425660 -3.341020
+v 0.000000 2.425820 -3.358630
+v -0.064692 2.446500 -3.382040
+v 0.000000 2.446800 -3.400000
+v -0.061668 2.462180 -3.406950
+v 0.000000 2.462610 -3.424870
+v -0.057996 2.472370 -3.416450
+v 0.000000 2.472900 -3.434000
+v -0.054000 2.476750 -3.411240
+v 0.000000 2.477340 -3.428130
+v -0.050004 2.475000 -3.392010
+v 0.000000 2.475600 -3.408000
+v -0.046332 2.466770 -3.359450
+v 0.000000 2.467330 -3.374370
+v -0.043308 2.451750 -3.314270
+v 0.000000 2.452200 -3.328000
+v -0.041256 2.429600 -3.257150
+v 0.000000 2.429870 -3.269630
+v -0.040500 2.400000 -3.188800
+v 0.000000 2.400000 -3.200000
+v 0.041256 2.429600 -3.257150
+v 0.040500 2.400000 -3.188800
+v 0.043308 2.451750 -3.314270
+v 0.046332 2.466770 -3.359450
+v 0.050004 2.475000 -3.392010
+v 0.054000 2.476750 -3.411240
+v 0.057996 2.472370 -3.416450
+v 0.061668 2.462180 -3.406950
+v 0.064692 2.446500 -3.382040
+v 0.066744 2.425660 -3.341020
+v 0.067500 2.400000 -3.283200
+v 0.070600 2.265540 -3.060990
+v 0.079013 2.093000 -2.911380
+v 0.091411 1.893070 -2.812460
+v 0.106466 1.676420 -2.742330
+v 0.122850 1.453730 -2.679100
+v 0.139234 1.235650 -2.600870
+v 0.154289 1.032880 -2.485740
+v 0.166687 0.856080 -2.311820
+v 0.296333 0.906240 -2.296130
+v 0.274291 1.075570 -2.466320
+v 0.247526 1.270540 -2.579350
+v 0.218400 1.480800 -2.656300
+v 0.189274 1.696030 -2.718250
+v 0.162509 1.905900 -2.786280
+v 0.140467 2.100070 -2.881470
+v 0.125510 2.268220 -3.024880
+v 0.120000 2.400000 -3.237600
+v 0.118656 2.425240 -3.293230
+v 0.115008 2.445680 -3.333270
+v 0.109632 2.461010 -3.358290
+v 0.103104 2.470930 -3.368810
+v 0.096000 2.475150 -3.365400
+v 0.088896 2.473350 -3.348600
+v 0.082368 2.465240 -3.318950
+v 0.076992 2.450520 -3.277000
+v 0.073344 2.428870 -3.223300
+v 0.072000 2.400000 -3.158400
+v 0.096264 2.427790 -3.173410
+v 0.094500 2.400000 -3.113600
+v 0.101052 2.448700 -3.222070
+v 0.108108 2.462990 -3.259250
+v 0.116676 2.470930 -3.284620
+v 0.126000 2.472790 -3.297850
+v 0.135324 2.468820 -3.298610
+v 0.143892 2.459290 -3.286580
+v 0.150948 2.444470 -3.261410
+v 0.155736 2.424620 -3.222800
+v 0.157500 2.400000 -3.170400
+v 0.164732 2.272160 -2.971660
+v 0.184363 2.110490 -2.837380
+v 0.213293 1.924800 -2.747710
+v 0.248422 1.724930 -2.682770
+v 0.286650 1.520700 -2.622700
+v 0.324878 1.321940 -2.547630
+v 0.360007 1.138490 -2.437690
+v 0.388937 0.980160 -2.273020
+v 0.444499 1.069920 -2.244950
+v 0.411437 1.214890 -2.402930
+v 0.371290 1.384370 -2.509110
+v 0.327600 1.569150 -2.581900
+v 0.283910 1.760020 -2.639690
+v 0.243763 1.947750 -2.700870
+v 0.210701 2.123140 -2.783850
+v 0.188266 2.276960 -2.907030
+v 0.180000 2.400000 -3.088800
+v 0.177984 2.423860 -3.137280
+v 0.172512 2.443000 -3.174160
+v 0.164448 2.457200 -3.199500
+v 0.154656 2.466250 -3.213370
+v 0.144000 2.469920 -3.215830
+v 0.133344 2.468000 -3.206940
+v 0.123552 2.460260 -3.186770
+v 0.115488 2.446500 -3.155380
+v 0.110016 2.426480 -3.112840
+v 0.108000 2.400000 -3.059200
+v 0.114600 2.425060 -3.046910
+v 0.112500 2.400000 -3.000000
+v 0.120300 2.444100 -3.082800
+v 0.128700 2.457290 -3.107890
+v 0.138900 2.464800 -3.122400
+v 0.150000 2.466800 -3.126560
+v 0.161100 2.463450 -3.120600
+v 0.171300 2.454930 -3.104740
+v 0.179700 2.441400 -3.079200
+v 0.185400 2.423030 -3.044210
+v 0.187500 2.400000 -3.000000
+v 0.196110 2.282180 -2.836700
+v 0.219480 2.136900 -2.725600
+v 0.253920 1.972720 -2.649900
+v 0.295740 1.798200 -2.592800
+v 0.341250 1.621870 -2.537500
+v 0.386760 1.452300 -2.467200
+v 0.428580 1.298020 -2.365100
+v 0.463020 1.167600 -2.214400
+v 0.444499 1.265280 -2.183850
+v 0.411437 1.381160 -2.327270
+v 0.371290 1.520230 -2.425290
+v 0.327600 1.674600 -2.493100
+v 0.283910 1.836380 -2.545910
+v 0.243763 1.997700 -2.598930
+v 0.210701 2.150660 -2.667350
+v 0.188266 2.287390 -2.766370
+v 0.180000 2.400000 -2.911200
+v 0.177984 2.422210 -2.951150
+v 0.172512 2.439800 -2.984240
+v 0.164448 2.452660 -3.009980
+v 0.154656 2.460650 -3.027830
+v 0.144000 2.463670 -3.037300
+v 0.133344 2.461600 -3.037860
+v 0.123552 2.454320 -3.029010
+v 0.115488 2.441700 -3.010220
+v 0.110016 2.423640 -2.980990
+v 0.108000 2.400000 -2.940800
+v 0.096264 2.422330 -2.920410
+v 0.094500 2.400000 -2.886400
+v 0.101052 2.439500 -2.943530
+v 0.108108 2.451590 -2.956520
+v 0.116676 2.458670 -2.960180
+v 0.126000 2.460810 -2.955270
+v 0.135324 2.458080 -2.942590
+v 0.143892 2.450570 -2.922900
+v 0.150948 2.438330 -2.896990
+v 0.155736 2.421450 -2.865630
+v 0.157500 2.400000 -2.829600
+v 0.164732 2.292190 -2.701740
+v 0.184363 2.163310 -2.613820
+v 0.213293 2.020650 -2.552090
+v 0.248422 1.871470 -2.502830
+v 0.286650 1.723050 -2.452300
+v 0.324878 1.582660 -2.386770
+v 0.360007 1.457560 -2.292510
+v 0.388937 1.355040 -2.155780
+v 0.296333 1.428960 -2.132670
+v 0.274291 1.520480 -2.263880
+v 0.247526 1.634060 -2.355050
+v 0.218400 1.762950 -2.418700
+v 0.189274 1.900370 -2.467350
+v 0.162509 2.039550 -2.513520
+v 0.140467 2.173730 -2.569730
+v 0.125510 2.296130 -2.648520
+v 0.120000 2.400000 -2.762400
+v 0.118656 2.420830 -2.795200
+v 0.115008 2.437120 -2.825130
+v 0.109632 2.448850 -2.851190
+v 0.103104 2.455970 -2.872390
+v 0.096000 2.458440 -2.887730
+v 0.088896 2.456250 -2.896200
+v 0.082368 2.449340 -2.896830
+v 0.076992 2.437680 -2.888600
+v 0.073344 2.421250 -2.870520
+v 0.072000 2.400000 -2.841600
+v 0.041256 2.420520 -2.836670
+v 0.040500 2.400000 -2.811200
+v 0.043308 2.436450 -2.851330
+v 0.046332 2.447810 -2.856320
+v 0.050004 2.454600 -2.852790
+v 0.054000 2.456840 -2.841890
+v 0.057996 2.454530 -2.824750
+v 0.061668 2.447680 -2.802530
+v 0.064692 2.436300 -2.776360
+v 0.066744 2.420410 -2.747410
+v 0.067500 2.400000 -2.716800
+v 0.070600 2.298810 -2.612410
+v 0.079013 2.180800 -2.539820
+v 0.091411 2.052380 -2.487340
+v 0.106466 1.919980 -2.443270
+v 0.122850 1.790030 -2.395900
+v 0.139234 1.668950 -2.333530
+v 0.154289 1.563170 -2.244460
+v 0.166687 1.479120 -2.116980
+v 0.000000 1.444200 -1.935900
+v 0.000000 1.497600 -2.111200
+v -0.178200 1.401900 -1.700000
+v -0.175100 1.423220 -1.939390
+v -0.166687 1.479120 -2.116980
+v 0.000000 1.578900 -2.237300
+v -0.154289 1.563170 -2.244460
+v 0.000000 1.681800 -2.325600
+v -0.139234 1.668950 -2.333530
+v 0.000000 1.800000 -2.387500
+v -0.122850 1.790030 -2.395900
+v 0.000000 1.927200 -2.434400
+v -0.106466 1.919980 -2.443270
+v 0.000000 2.057100 -2.477700
+v -0.091411 2.052380 -2.487340
+v 0.000000 2.183400 -2.528800
+v -0.079013 2.180800 -2.539820
+v 0.000000 2.299800 -2.599100
+v -0.070600 2.298810 -2.612410
+v 0.000000 2.400000 -2.700000
+v -0.067500 2.400000 -2.716800
+v 0.000000 2.420250 -2.729800
+v -0.066744 2.420410 -2.747410
+v 0.000000 2.436000 -2.758400
+v -0.064692 2.436300 -2.776360
+v 0.000000 2.447250 -2.784600
+v -0.061668 2.447680 -2.802530
+v 0.000000 2.454000 -2.807200
+v -0.057996 2.454530 -2.824750
+v 0.000000 2.456250 -2.825000
+v -0.054000 2.456840 -2.841890
+v 0.000000 2.454000 -2.836800
+v -0.050004 2.454600 -2.852790
+v 0.000000 2.447250 -2.841400
+v -0.046332 2.447810 -2.856320
+v 0.000000 2.436000 -2.837600
+v -0.043308 2.436450 -2.851330
+v 0.000000 2.420250 -2.824200
+v -0.041256 2.420520 -2.836670
+v 0.000000 2.400000 -2.800000
+v -0.040500 2.400000 -2.811200
+v -0.072000 2.400000 -2.841600
+v -0.073344 2.421250 -2.870520
+v -0.096264 2.422330 -2.920410
+v -0.094500 2.400000 -2.886400
+v -0.108000 2.400000 -2.940800
+v -0.110016 2.423640 -2.980990
+v -0.112500 2.400000 -3.000000
+v -0.114600 2.425060 -3.046910
+v -0.108000 2.400000 -3.059200
+v -0.110016 2.426480 -3.112840
+v -0.094500 2.400000 -3.113600
+v -0.096264 2.427790 -3.173410
+v -0.072000 2.400000 -3.158400
+v -0.073344 2.428870 -3.223300
+v -0.076992 2.450520 -3.277000
+v -0.082368 2.465240 -3.318950
+v -0.088896 2.473350 -3.348600
+v -0.096000 2.475150 -3.365400
+v -0.103104 2.470930 -3.368810
+v -0.109632 2.461010 -3.358290
+v -0.115008 2.445680 -3.333270
+v -0.118656 2.425240 -3.293230
+v -0.120000 2.400000 -3.237600
+v -0.125510 2.268220 -3.024880
+v -0.140467 2.100070 -2.881470
+v -0.162509 1.905900 -2.786280
+v -0.189274 1.696030 -2.718250
+v -0.218400 1.480800 -2.656300
+v -0.247526 1.270540 -2.579350
+v -0.274291 1.075570 -2.466320
+v -0.296333 0.906240 -2.296130
+v -0.311290 0.772872 -2.047720
+v -0.316800 0.685800 -1.700000
+v -0.408568 0.856788 -2.033740
+v -0.388937 0.980160 -2.273020
+v -0.360007 1.138490 -2.437690
+v -0.324878 1.321940 -2.547630
+v -0.286650 1.520700 -2.622700
+v -0.248422 1.724930 -2.682770
+v -0.213293 1.924800 -2.747710
+v -0.184363 2.110490 -2.837380
+v -0.164732 2.272160 -2.971660
+v -0.157500 2.400000 -3.170400
+v -0.155736 2.424620 -3.222800
+v -0.150948 2.444470 -3.261410
+v -0.143892 2.459290 -3.286580
+v -0.135324 2.468820 -3.298610
+v -0.126000 2.472790 -3.297850
+v -0.116676 2.470930 -3.284620
+v -0.108108 2.462990 -3.259250
+v -0.101052 2.448700 -3.222070
+v -0.115488 2.446500 -3.155380
+v -0.123552 2.460260 -3.186770
+v -0.133344 2.468000 -3.206940
+v -0.144000 2.469920 -3.215830
+v -0.154656 2.466250 -3.213370
+v -0.164448 2.457200 -3.199500
+v -0.172512 2.443000 -3.174160
+v -0.177984 2.423860 -3.137280
+v -0.180000 2.400000 -3.088800
+v -0.188266 2.276960 -2.907030
+v -0.210701 2.123140 -2.783850
+v -0.243763 1.947750 -2.700870
+v -0.283910 1.760020 -2.639690
+v -0.327600 1.569150 -2.581900
+v -0.371290 1.384370 -2.509110
+v -0.411437 1.214890 -2.402930
+v -0.444499 1.069920 -2.244950
+v -0.466934 0.958686 -2.016770
+v -0.415800 0.778200 -1.700000
+v -0.475200 0.890400 -1.700000
+v -0.486390 1.069570 -1.998300
+v -0.495000 1.012500 -1.700000
+v -0.463020 1.167600 -2.214400
+v -0.428580 1.298020 -2.365100
+v -0.386760 1.452300 -2.467200
+v -0.341250 1.621870 -2.537500
+v -0.295740 1.798200 -2.592800
+v -0.253920 1.972720 -2.649900
+v -0.219480 2.136900 -2.725600
+v -0.196110 2.282180 -2.836700
+v -0.187500 2.400000 -3.000000
+v -0.185400 2.423030 -3.044210
+v -0.179700 2.441400 -3.079200
+v -0.171300 2.454930 -3.104740
+v -0.161100 2.463450 -3.120600
+v -0.150000 2.466800 -3.126560
+v -0.138900 2.464800 -3.122400
+v -0.128700 2.457290 -3.107890
+v -0.120300 2.444100 -3.082800
+v -0.115488 2.441700 -3.010220
+v -0.123552 2.454320 -3.029010
+v -0.133344 2.461600 -3.037860
+v -0.144000 2.463670 -3.037300
+v -0.154656 2.460650 -3.027830
+v -0.164448 2.452660 -3.009980
+v -0.172512 2.439800 -2.984240
+v -0.177984 2.422210 -2.951150
+v -0.180000 2.400000 -2.911200
+v -0.188266 2.287390 -2.766370
+v -0.210701 2.150660 -2.667350
+v -0.243763 1.997700 -2.598930
+v -0.283910 1.836380 -2.545910
+v -0.327600 1.674600 -2.493100
+v -0.371290 1.520230 -2.425290
+v -0.411437 1.381160 -2.327270
+v -0.444499 1.265280 -2.183850
+v -0.466934 1.180460 -1.979830
+v -0.475200 1.134600 -1.700000
+v -0.408568 1.282360 -1.962860
+v -0.388937 1.355040 -2.155780
+v -0.360007 1.457560 -2.292510
+v -0.324878 1.582660 -2.386770
+v -0.286650 1.723050 -2.452300
+v -0.248422 1.871470 -2.502830
+v -0.213293 2.020650 -2.552090
+v -0.184363 2.163310 -2.613820
+v -0.164732 2.292190 -2.701740
+v -0.157500 2.400000 -2.829600
+v -0.155736 2.421450 -2.865630
+v -0.150948 2.438330 -2.896990
+v -0.143892 2.450570 -2.922900
+v -0.135324 2.458080 -2.942590
+v -0.126000 2.460810 -2.955270
+v -0.116676 2.458670 -2.960180
+v -0.108108 2.451590 -2.956520
+v -0.101052 2.439500 -2.943530
+v -0.076992 2.437680 -2.888600
+v -0.082368 2.449340 -2.896830
+v -0.088896 2.456250 -2.896200
+v -0.096000 2.458440 -2.887730
+v -0.103104 2.455970 -2.872390
+v -0.109632 2.448850 -2.851190
+v -0.115008 2.437120 -2.825130
+v -0.118656 2.420830 -2.795200
+v -0.120000 2.400000 -2.762400
+v -0.125510 2.296130 -2.648520
+v -0.140467 2.173730 -2.569730
+v -0.162509 2.039550 -2.513520
+v -0.189274 1.900370 -2.467350
+v -0.218400 1.762950 -2.418700
+v -0.247526 1.634060 -2.355050
+v -0.274291 1.520480 -2.263880
+v -0.296333 1.428960 -2.132670
+v -0.311290 1.366280 -1.948880
+v -0.415800 1.246800 -1.700000
+v -0.316800 1.339200 -1.700000
+vn -0.0000 -0.3691 0.9294
+vn -0.1503 -0.2842 0.9469
+vn -0.0000 -0.2840 0.9588
+vn -0.1457 -0.3693 0.9178
+vn -0.2975 -0.2845 0.9114
+vn -0.2883 -0.3697 0.8833
+vn -0.4367 -0.2848 0.8533
+vn -0.4232 -0.3701 0.8270
+vn -0.5643 -0.2850 0.7748
+vn -0.5469 -0.3703 0.7508
+vn -0.6778 -0.2850 0.6778
+vn -0.7508 -0.3703 0.5469
+vn -0.6568 -0.3704 0.6568
+vn -0.8270 -0.3701 0.4232
+vn -0.7748 -0.2850 0.5644
+vn -0.8833 -0.3697 0.2884
+vn -0.8533 -0.2848 0.4367
+vn -0.9178 -0.3693 0.1457
+vn -0.9113 -0.2845 0.2975
+vn -0.9294 -0.3691 -0.0000
+vn -0.9469 -0.2842 0.1504
+vn -0.9469 -0.2842 -0.1503
+vn -0.9588 -0.2840 -0.0000
+vn -0.9178 -0.3693 -0.1457
+vn -0.9114 -0.2845 -0.2975
+vn -0.8833 -0.3697 -0.2883
+vn -0.8533 -0.2848 -0.4367
+vn -0.8270 -0.3701 -0.4232
+vn -0.7748 -0.2850 -0.5644
+vn -0.7508 -0.3703 -0.5469
+vn -0.6778 -0.2850 -0.6778
+vn -0.7748 -0.2850 -0.5643
+vn -0.5469 -0.3703 -0.7508
+vn -0.6568 -0.3704 -0.6568
+vn -0.4232 -0.3701 -0.8270
+vn -0.5644 -0.2850 -0.7748
+vn -0.5469 -0.3704 -0.7508
+vn -0.2884 -0.3697 -0.8833
+vn -0.4367 -0.2848 -0.8533
+vn -0.1457 -0.3693 -0.9178
+vn -0.2975 -0.2845 -0.9113
+vn -0.0000 -0.3691 -0.9294
+vn -0.1504 -0.2842 -0.9469
+vn 0.1503 -0.2842 -0.9469
+vn -0.0000 -0.2840 -0.9588
+vn 0.1457 -0.3693 -0.9178
+vn 0.2975 -0.2845 -0.9114
+vn 0.2883 -0.3697 -0.8833
+vn 0.4367 -0.2848 -0.8533
+vn 0.4232 -0.3701 -0.8270
+vn 0.5643 -0.2850 -0.7748
+vn 0.5469 -0.3703 -0.7508
+vn 0.6778 -0.2850 -0.6778
+vn 0.7508 -0.3703 -0.5469
+vn 0.6568 -0.3704 -0.6568
+vn 0.8270 -0.3701 -0.4232
+vn 0.7748 -0.2850 -0.5644
+vn 0.8833 -0.3697 -0.2884
+vn 0.8533 -0.2848 -0.4367
+vn 0.9178 -0.3693 -0.1457
+vn 0.9113 -0.2845 -0.2975
+vn 0.9294 -0.3691 -0.0000
+vn 0.9469 -0.2842 -0.1504
+vn 0.9469 -0.2842 0.1503
+vn 0.9588 -0.2840 -0.0000
+vn 0.9178 -0.3693 0.1457
+vn 0.9114 -0.2845 0.2975
+vn 0.8833 -0.3697 0.2883
+vn 0.8533 -0.2848 0.4367
+vn 0.8270 -0.3701 0.4232
+vn 0.7748 -0.2850 0.5644
+vn 0.7508 -0.3703 0.5469
+vn 0.6778 -0.2850 0.6778
+vn 0.7748 -0.2850 0.5643
+vn 0.5469 -0.3703 0.7508
+vn 0.6568 -0.3704 0.6568
+vn 0.4232 -0.3701 0.8270
+vn 0.5644 -0.2850 0.7748
+vn 0.5469 -0.3704 0.7508
+vn 0.2884 -0.3697 0.8833
+vn 0.4367 -0.2848 0.8533
+vn 0.1457 -0.3693 0.9178
+vn 0.2975 -0.2845 0.9113
+vn 0.1504 -0.2842 0.9469
+vn 0.1566 -0.0527 0.9863
+vn 0.1503 -0.2842 0.9469
+vn -0.1566 -0.0527 0.9863
+vn -0.0000 -0.0527 0.9986
+vn -0.1504 -0.2842 0.9469
+vn -0.3099 -0.0528 0.9493
+vn -0.2975 -0.2845 0.9113
+vn -0.4549 -0.0528 0.8890
+vn -0.4367 -0.2848 0.8534
+vn -0.5879 -0.0528 0.8072
+vn -0.5644 -0.2850 0.7748
+vn -0.7061 -0.0529 0.7061
+vn -0.7748 -0.2850 0.5643
+vn -0.6778 -0.2851 0.6778
+vn -0.8072 -0.0529 0.5879
+vn -0.9114 -0.2845 0.2975
+vn -0.8890 -0.0528 0.4549
+vn -0.9493 -0.0528 0.3099
+vn -0.9863 -0.0527 0.1566
+vn -0.9469 -0.2842 0.1503
+vn -0.9863 -0.0527 -0.1566
+vn -0.9986 -0.0527 -0.0000
+vn -0.9493 -0.0528 -0.3099
+vn -0.9113 -0.2845 -0.2975
+vn -0.8890 -0.0528 -0.4549
+vn -0.8534 -0.2848 -0.4367
+vn -0.8072 -0.0528 -0.5879
+vn -0.7061 -0.0529 -0.7061
+vn -0.5643 -0.2850 -0.7748
+vn -0.6778 -0.2851 -0.6778
+vn -0.5879 -0.0529 -0.8072
+vn -0.2975 -0.2845 -0.9114
+vn -0.4549 -0.0529 -0.8890
+vn -0.3099 -0.0528 -0.9493
+vn -0.1566 -0.0527 -0.9863
+vn -0.1503 -0.2842 -0.9469
+vn 0.1566 -0.0527 -0.9863
+vn -0.0000 -0.0527 -0.9986
+vn 0.1504 -0.2842 -0.9469
+vn 0.3099 -0.0528 -0.9493
+vn 0.2975 -0.2845 -0.9113
+vn 0.4549 -0.0528 -0.8890
+vn 0.4367 -0.2848 -0.8534
+vn 0.5879 -0.0528 -0.8072
+vn 0.5644 -0.2850 -0.7748
+vn 0.7061 -0.0529 -0.7061
+vn 0.7748 -0.2850 -0.5643
+vn 0.6778 -0.2851 -0.6778
+vn 0.8072 -0.0529 -0.5879
+vn 0.9114 -0.2845 -0.2975
+vn 0.8890 -0.0528 -0.4549
+vn 0.9493 -0.0528 -0.3099
+vn 0.9863 -0.0527 -0.1566
+vn 0.9469 -0.2842 -0.1503
+vn 0.9863 -0.0527 0.1566
+vn 0.9986 -0.0527 -0.0000
+vn 0.9493 -0.0528 0.3099
+vn 0.9113 -0.2845 0.2975
+vn 0.8890 -0.0528 0.4549
+vn 0.8534 -0.2848 0.4367
+vn 0.8072 -0.0528 0.5879
+vn 0.7061 -0.0529 0.7061
+vn 0.5643 -0.2850 0.7748
+vn 0.6778 -0.2851 0.6778
+vn 0.5879 -0.0529 0.8072
+vn 0.2975 -0.2845 0.9114
+vn 0.4549 -0.0528 0.8890
+vn 0.3099 -0.0528 0.9493
+vn 0.1477 0.3367 0.9299
+vn 0.2922 0.3371 0.8950
+vn -0.0000 0.3366 0.9417
+vn 0.1476 0.3367 0.9300
+vn -0.1477 0.3367 0.9300
+vn -0.2921 0.3371 0.8950
+vn -0.5879 -0.0529 0.8072
+vn -0.4288 0.3373 0.8380
+vn -0.5542 0.3376 0.7609
+vn -0.7609 0.3376 0.5542
+vn -0.6656 0.3376 0.6656
+vn -0.8381 0.3373 0.4288
+vn -0.8890 -0.0529 0.4549
+vn -0.8950 0.3371 0.2922
+vn -0.8380 0.3373 0.4289
+vn -0.9300 0.3367 0.1477
+vn -0.9417 0.3365 -0.0000
+vn -0.9300 0.3367 0.1476
+vn -0.9300 0.3367 -0.1477
+vn -0.8950 0.3371 -0.2921
+vn -0.8072 -0.0529 -0.5879
+vn -0.8380 0.3373 -0.4289
+vn -0.7609 0.3376 -0.5542
+vn -0.5542 0.3376 -0.7609
+vn -0.6656 0.3376 -0.6656
+vn -0.4288 0.3374 -0.8380
+vn -0.2922 0.3371 -0.8950
+vn -0.4289 0.3373 -0.8380
+vn -0.1477 0.3367 -0.9299
+vn -0.0000 0.3366 -0.9417
+vn -0.1476 0.3367 -0.9300
+vn 0.1477 0.3367 -0.9300
+vn 0.2921 0.3371 -0.8950
+vn 0.5879 -0.0529 -0.8072
+vn 0.4288 0.3373 -0.8380
+vn 0.5542 0.3376 -0.7609
+vn 0.7609 0.3376 -0.5542
+vn 0.6656 0.3376 -0.6656
+vn 0.8381 0.3373 -0.4288
+vn 0.8890 -0.0529 -0.4549
+vn 0.8950 0.3371 -0.2922
+vn 0.8380 0.3373 -0.4289
+vn 0.9300 0.3367 -0.1477
+vn 0.9417 0.3365 -0.0000
+vn 0.9300 0.3367 -0.1476
+vn 0.9300 0.3367 0.1477
+vn 0.8950 0.3371 0.2921
+vn 0.8072 -0.0529 0.5879
+vn 0.8380 0.3373 0.4289
+vn 0.7609 0.3376 0.5542
+vn 0.5542 0.3376 0.7609
+vn 0.6656 0.3376 0.6656
+vn 0.4288 0.3374 0.8380
+vn 0.4549 -0.0529 0.8890
+vn 0.4289 0.3373 0.8380
+vn 0.1841 0.8049 0.5642
+vn 0.2702 0.8051 0.5280
+vn 0.0931 0.8046 0.5864
+vn 0.1841 0.8049 0.5641
+vn -0.0000 0.8045 0.5940
+vn -0.1476 0.3367 0.9300
+vn -0.0000 0.3365 0.9417
+vn -0.2921 0.3370 0.8950
+vn -0.0931 0.8046 0.5865
+vn -0.4289 0.3374 0.8380
+vn -0.1842 0.8049 0.5641
+vn -0.2922 0.3371 0.8950
+vn -0.2702 0.8052 0.5279
+vn -0.4289 0.3373 0.8380
+vn -0.3490 0.8053 0.4792
+vn -0.4792 0.8053 0.3490
+vn -0.4192 0.8053 0.4192
+vn -0.5280 0.8051 0.2702
+vn -0.5642 0.8049 0.1841
+vn -0.5865 0.8046 0.0931
+vn -0.5641 0.8049 0.1841
+vn -0.5940 0.8045 -0.0000
+vn -0.5864 0.8046 0.0931
+vn -0.8950 0.3370 -0.2921
+vn -0.5865 0.8046 -0.0931
+vn -0.9300 0.3367 -0.1476
+vn -0.8380 0.3374 -0.4289
+vn -0.5641 0.8049 -0.1841
+vn -0.8950 0.3371 -0.2922
+vn -0.5279 0.8051 -0.2702
+vn -0.4792 0.8053 -0.3490
+vn -0.3490 0.8053 -0.4792
+vn -0.4191 0.8054 -0.4192
+vn -0.5542 0.3375 -0.7609
+vn -0.2702 0.8051 -0.5280
+vn -0.1841 0.8049 -0.5642
+vn -0.0931 0.8046 -0.5864
+vn -0.1841 0.8049 -0.5641
+vn -0.0000 0.8045 -0.5940
+vn 0.1476 0.3367 -0.9300
+vn -0.0000 0.3365 -0.9417
+vn 0.2921 0.3370 -0.8950
+vn 0.0931 0.8046 -0.5865
+vn 0.4289 0.3374 -0.8380
+vn 0.1842 0.8049 -0.5641
+vn 0.2922 0.3371 -0.8950
+vn 0.2702 0.8052 -0.5279
+vn 0.4289 0.3373 -0.8380
+vn 0.3490 0.8053 -0.4792
+vn 0.4792 0.8053 -0.3490
+vn 0.4192 0.8053 -0.4192
+vn 0.5280 0.8051 -0.2702
+vn 0.5642 0.8049 -0.1841
+vn 0.5865 0.8046 -0.0931
+vn 0.5641 0.8049 -0.1841
+vn 0.5940 0.8045 -0.0000
+vn 0.5864 0.8046 -0.0931
+vn 0.8950 0.3370 0.2921
+vn 0.5865 0.8046 0.0931
+vn 0.9300 0.3367 0.1476
+vn 0.8380 0.3374 0.4289
+vn 0.5641 0.8049 0.1841
+vn 0.8950 0.3371 0.2922
+vn 0.5279 0.8051 0.2702
+vn 0.4792 0.8053 0.3490
+vn 0.3490 0.8053 0.4792
+vn 0.4191 0.8054 0.4192
+vn 0.5542 0.3375 0.7609
+vn 0.0145 0.9995 0.0283
+vn 0.0187 0.9995 0.0257
+vn 0.0099 0.9995 0.0303
+vn 0.1842 0.8049 0.5641
+vn 0.0050 0.9995 0.0315
+vn 0.0099 0.9995 0.0302
+vn 0.0931 0.8046 0.5865
+vn -0.0000 0.9995 0.0319
+vn -0.0050 0.9995 0.0315
+vn -0.2702 0.8051 0.5280
+vn -0.0099 0.9995 0.0303
+vn -0.3491 0.8053 0.4792
+vn -0.0145 0.9995 0.0284
+vn -0.4192 0.8054 0.4191
+vn -0.0187 0.9995 0.0257
+vn -0.4191 0.8054 0.4191
+vn -0.0257 0.9995 0.0187
+vn -0.0225 0.9995 0.0225
+vn -0.0283 0.9995 0.0145
+vn -0.0303 0.9995 0.0099
+vn -0.0315 0.9995 0.0050
+vn -0.0319 0.9995 -0.0000
+vn -0.0315 0.9995 -0.0050
+vn -0.5864 0.8046 -0.0931
+vn -0.5280 0.8051 -0.2702
+vn -0.0303 0.9995 -0.0099
+vn -0.4792 0.8053 -0.3491
+vn -0.0283 0.9995 -0.0145
+vn -0.0257 0.9995 -0.0187
+vn -0.4192 0.8054 -0.4191
+vn -0.0187 0.9995 -0.0257
+vn -0.0225 0.9995 -0.0225
+vn -0.0145 0.9995 -0.0283
+vn -0.0099 0.9995 -0.0303
+vn -0.1842 0.8049 -0.5641
+vn -0.0050 0.9995 -0.0315
+vn -0.0099 0.9995 -0.0302
+vn -0.0931 0.8046 -0.5865
+vn -0.0000 0.9995 -0.0319
+vn 0.0050 0.9995 -0.0315
+vn 0.2702 0.8051 -0.5280
+vn 0.0099 0.9995 -0.0303
+vn 0.3491 0.8053 -0.4792
+vn 0.0145 0.9995 -0.0284
+vn 0.4192 0.8054 -0.4191
+vn 0.0187 0.9995 -0.0257
+vn 0.4191 0.8054 -0.4191
+vn 0.0257 0.9995 -0.0187
+vn 0.0225 0.9995 -0.0225
+vn 0.0283 0.9995 -0.0145
+vn 0.0303 0.9995 -0.0099
+vn 0.0315 0.9995 -0.0050
+vn 0.0319 0.9995 -0.0000
+vn 0.0315 0.9995 0.0050
+vn 0.5864 0.8046 0.0931
+vn 0.5280 0.8051 0.2702
+vn 0.0303 0.9995 0.0099
+vn 0.4792 0.8053 0.3491
+vn 0.0283 0.9995 0.0145
+vn 0.0257 0.9995 0.0187
+vn 0.4192 0.8054 0.4191
+vn 0.0225 0.9995 0.0225
+vn 0.0224 0.9995 0.0225
+vn -0.2239 0.9249 -0.3074
+vn -0.2688 0.9249 -0.2688
+vn -0.1733 0.9248 -0.3387
+vn -0.1182 0.9246 -0.3621
+vn -0.1734 0.9247 -0.3388
+vn -0.0598 0.9245 -0.3765
+vn -0.1182 0.9246 -0.3620
+vn -0.0000 0.9244 -0.3814
+vn -0.0050 0.9995 0.0314
+vn -0.0000 0.9244 -0.3815
+vn -0.0099 0.9995 0.0302
+vn 0.0598 0.9245 -0.3766
+vn -0.0145 0.9995 0.0283
+vn 0.1182 0.9246 -0.3621
+vn 0.1733 0.9248 -0.3387
+vn -0.0224 0.9995 0.0225
+vn 0.2239 0.9249 -0.3074
+vn 0.3074 0.9249 -0.2239
+vn 0.2688 0.9249 -0.2689
+vn 0.3387 0.9248 -0.1733
+vn 0.3621 0.9246 -0.1182
+vn 0.3388 0.9247 -0.1734
+vn 0.3765 0.9245 -0.0598
+vn 0.3815 0.9244 -0.0000
+vn -0.0302 0.9995 -0.0099
+vn 0.3766 0.9245 0.0598
+vn 0.3620 0.9247 0.1182
+vn 0.3387 0.9248 0.1733
+vn 0.3074 0.9249 0.2239
+vn -0.0224 0.9995 -0.0225
+vn 0.2239 0.9249 0.3074
+vn 0.2688 0.9249 0.2688
+vn 0.1733 0.9248 0.3387
+vn 0.1182 0.9246 0.3621
+vn 0.1734 0.9247 0.3388
+vn 0.0598 0.9245 0.3765
+vn 0.1182 0.9246 0.3620
+vn -0.0000 0.9244 0.3814
+vn 0.0050 0.9995 -0.0314
+vn -0.0000 0.9244 0.3815
+vn 0.0099 0.9995 -0.0302
+vn -0.0598 0.9245 0.3766
+vn 0.0145 0.9995 -0.0283
+vn -0.1182 0.9246 0.3621
+vn -0.1733 0.9248 0.3387
+vn 0.0224 0.9995 -0.0225
+vn -0.2239 0.9249 0.3074
+vn -0.3074 0.9249 0.2239
+vn -0.2688 0.9249 0.2689
+vn -0.3387 0.9248 0.1733
+vn -0.3621 0.9246 0.1182
+vn -0.3388 0.9247 0.1734
+vn -0.3765 0.9245 0.0598
+vn -0.3815 0.9244 -0.0000
+vn 0.0302 0.9995 0.0099
+vn -0.3766 0.9245 -0.0598
+vn -0.3620 0.9247 -0.1182
+vn -0.3387 0.9248 -0.1733
+vn -0.3074 0.9249 -0.2239
+vn -0.4926 0.7929 -0.3588
+vn -0.2689 0.9249 -0.2688
+vn -0.3588 0.7929 -0.4926
+vn -0.4309 0.7929 -0.4309
+vn -0.2777 0.7926 -0.5428
+vn -0.1893 0.7923 -0.5800
+vn -0.2777 0.7926 -0.5427
+vn -0.0957 0.7919 -0.6031
+vn -0.0598 0.9245 -0.3766
+vn -0.0000 0.7918 -0.6108
+vn 0.0598 0.9245 -0.3765
+vn 0.0957 0.7920 -0.6030
+vn 0.1893 0.7923 -0.5801
+vn 0.2777 0.7926 -0.5428
+vn 0.2688 0.9249 -0.2688
+vn 0.3588 0.7929 -0.4926
+vn 0.4926 0.7929 -0.3588
+vn 0.4309 0.7929 -0.4309
+vn 0.5428 0.7926 -0.2778
+vn 0.4926 0.7928 -0.3588
+vn 0.5800 0.7923 -0.1893
+vn 0.5428 0.7926 -0.2777
+vn 0.6031 0.7919 -0.0957
+vn 0.3766 0.9245 -0.0598
+vn 0.6108 0.7918 -0.0000
+vn 0.3765 0.9245 0.0598
+vn 0.3621 0.9246 0.1182
+vn 0.6030 0.7920 0.0957
+vn 0.5801 0.7923 0.1893
+vn 0.5428 0.7926 0.2777
+vn 0.4926 0.7929 0.3588
+vn 0.2689 0.9249 0.2688
+vn 0.3588 0.7929 0.4926
+vn 0.4309 0.7929 0.4309
+vn 0.2777 0.7926 0.5428
+vn 0.1893 0.7923 0.5800
+vn 0.2777 0.7926 0.5427
+vn 0.0957 0.7919 0.6031
+vn 0.0598 0.9245 0.3766
+vn -0.0000 0.7918 0.6108
+vn -0.0598 0.9245 0.3765
+vn -0.0957 0.7920 0.6030
+vn -0.1893 0.7923 0.5801
+vn -0.2777 0.7926 0.5428
+vn -0.2688 0.9249 0.2688
+vn -0.3588 0.7929 0.4926
+vn -0.4926 0.7929 0.3588
+vn -0.4309 0.7929 0.4309
+vn -0.5428 0.7926 0.2778
+vn -0.4926 0.7928 0.3588
+vn -0.5800 0.7923 0.1893
+vn -0.5428 0.7926 0.2777
+vn -0.6031 0.7919 0.0957
+vn -0.3766 0.9245 0.0598
+vn -0.6108 0.7918 -0.0000
+vn -0.3765 0.9245 -0.0598
+vn -0.3621 0.9246 -0.1182
+vn -0.6030 0.7920 -0.0957
+vn -0.5801 0.7923 -0.1893
+vn -0.5428 0.7926 -0.2777
+vn -0.4926 0.7928 -0.3588
+vn -0.6648 0.6651 -0.3402
+vn -0.4308 0.7930 -0.4308
+vn -0.6034 0.6654 -0.4395
+vn -0.4309 0.7929 -0.4308
+vn -0.4395 0.6654 -0.6034
+vn -0.5278 0.6655 -0.5278
+vn -0.3588 0.7928 -0.4926
+vn -0.3402 0.6651 -0.6648
+vn -0.4395 0.6653 -0.6034
+vn -0.2319 0.6646 -0.7103
+vn -0.1172 0.6642 -0.7383
+vn -0.0000 0.6640 -0.7478
+vn 0.0957 0.7919 -0.6031
+vn 0.1893 0.7923 -0.5800
+vn 0.1172 0.6642 -0.7383
+vn 0.2778 0.7926 -0.5427
+vn 0.2318 0.6646 -0.7103
+vn 0.3588 0.7928 -0.4926
+vn 0.3402 0.6651 -0.6648
+vn 0.4308 0.7930 -0.4308
+vn 0.4395 0.6654 -0.6034
+vn 0.4308 0.7929 -0.4309
+vn 0.6034 0.6654 -0.4395
+vn 0.5278 0.6654 -0.5278
+vn 0.6648 0.6651 -0.3402
+vn 0.6034 0.6653 -0.4395
+vn 0.7103 0.6647 -0.2319
+vn 0.7383 0.6642 -0.1172
+vn 0.7103 0.6646 -0.2319
+vn 0.7478 0.6640 -0.0000
+vn 0.6031 0.7919 0.0957
+vn 0.5800 0.7923 0.1893
+vn 0.7383 0.6642 0.1172
+vn 0.5427 0.7926 0.2778
+vn 0.7103 0.6646 0.2318
+vn 0.4926 0.7928 0.3588
+vn 0.6648 0.6651 0.3402
+vn 0.4308 0.7930 0.4308
+vn 0.6034 0.6654 0.4395
+vn 0.4308 0.7929 0.4308
+vn 0.4395 0.6654 0.6034
+vn 0.5278 0.6655 0.5278
+vn 0.3588 0.7928 0.4926
+vn 0.3402 0.6651 0.6648
+vn 0.4395 0.6653 0.6034
+vn 0.2319 0.6646 0.7103
+vn 0.1172 0.6642 0.7383
+vn -0.0000 0.6640 0.7478
+vn -0.0957 0.7919 0.6031
+vn -0.1893 0.7923 0.5800
+vn -0.1172 0.6642 0.7383
+vn -0.2778 0.7926 0.5427
+vn -0.2318 0.6646 0.7103
+vn -0.3588 0.7928 0.4926
+vn -0.3402 0.6651 0.6648
+vn -0.4308 0.7930 0.4308
+vn -0.4395 0.6654 0.6034
+vn -0.4308 0.7929 0.4309
+vn -0.6034 0.6654 0.4395
+vn -0.5278 0.6654 0.5278
+vn -0.6648 0.6651 0.3402
+vn -0.6034 0.6653 0.4395
+vn -0.7103 0.6647 0.2319
+vn -0.7383 0.6642 0.1172
+vn -0.7103 0.6646 0.2319
+vn -0.7478 0.6640 -0.0000
+vn -0.6031 0.7919 -0.0957
+vn -0.5800 0.7923 -0.1893
+vn -0.7383 0.6642 -0.1172
+vn -0.5427 0.7926 -0.2778
+vn -0.7103 0.6646 -0.2318
+vn -0.7969 0.5452 -0.2601
+vn -0.7102 0.6647 -0.2319
+vn -0.6034 0.6653 -0.4395
+vn -0.7460 0.5457 -0.3818
+vn -0.6772 0.5459 -0.4933
+vn -0.4933 0.5460 -0.6772
+vn -0.5924 0.5460 -0.5924
+vn -0.3818 0.5457 -0.7460
+vn -0.4933 0.5459 -0.6772
+vn -0.2601 0.5452 -0.7969
+vn -0.3817 0.5457 -0.7460
+vn -0.2318 0.6646 -0.7103
+vn -0.1315 0.5447 -0.8282
+vn -0.0000 0.5445 -0.8388
+vn -0.1315 0.5448 -0.8282
+vn -0.0000 0.6639 -0.7478
+vn 0.2319 0.6646 -0.7103
+vn 0.1315 0.5448 -0.8282
+vn 0.2601 0.5452 -0.7969
+vn 0.2319 0.6647 -0.7103
+vn 0.3818 0.5457 -0.7460
+vn 0.5278 0.6655 -0.5278
+vn 0.4933 0.5459 -0.6772
+vn 0.6772 0.5460 -0.4933
+vn 0.5924 0.5460 -0.5924
+vn 0.7460 0.5457 -0.3818
+vn 0.6772 0.5459 -0.4933
+vn 0.7969 0.5452 -0.2601
+vn 0.7460 0.5457 -0.3817
+vn 0.7103 0.6646 -0.2318
+vn 0.8282 0.5448 -0.1315
+vn 0.8388 0.5445 -0.0000
+vn 0.7478 0.6639 -0.0000
+vn 0.8282 0.5447 0.1315
+vn 0.7969 0.5452 0.2601
+vn 0.7102 0.6647 0.2319
+vn 0.6034 0.6653 0.4395
+vn 0.7460 0.5457 0.3818
+vn 0.6772 0.5459 0.4933
+vn 0.4933 0.5460 0.6772
+vn 0.5924 0.5460 0.5924
+vn 0.3818 0.5457 0.7460
+vn 0.4933 0.5459 0.6772
+vn 0.2601 0.5452 0.7969
+vn 0.3817 0.5457 0.7460
+vn 0.2318 0.6646 0.7103
+vn 0.1315 0.5447 0.8282
+vn -0.0000 0.5445 0.8388
+vn 0.1315 0.5448 0.8282
+vn -0.2319 0.6646 0.7103
+vn -0.1315 0.5448 0.8282
+vn -0.2601 0.5452 0.7969
+vn -0.2319 0.6647 0.7103
+vn -0.3818 0.5457 0.7460
+vn -0.5278 0.6655 0.5278
+vn -0.4933 0.5459 0.6772
+vn -0.6772 0.5460 0.4933
+vn -0.5924 0.5460 0.5924
+vn -0.7460 0.5457 0.3818
+vn -0.6772 0.5459 0.4933
+vn -0.7969 0.5452 0.2601
+vn -0.7460 0.5457 0.3817
+vn -0.7103 0.6646 0.2318
+vn -0.8282 0.5448 0.1315
+vn -0.8388 0.5445 -0.0000
+vn -0.7478 0.6639 -0.0000
+vn -0.8282 0.5447 -0.1315
+vn -0.7969 0.5452 -0.2602
+vn -0.8781 0.4576 -0.1394
+vn -0.7461 0.5456 -0.3817
+vn -0.8450 0.4581 -0.2759
+vn -0.6772 0.5459 -0.4932
+vn -0.7911 0.4585 -0.4048
+vn -0.7460 0.5457 -0.3817
+vn -0.7182 0.4588 -0.5231
+vn -0.6772 0.5460 -0.4932
+vn -0.5231 0.4588 -0.7182
+vn -0.6282 0.4589 -0.6283
+vn -0.4048 0.4585 -0.7911
+vn -0.3817 0.5456 -0.7461
+vn -0.2759 0.4581 -0.8450
+vn -0.4049 0.4585 -0.7911
+vn -0.1394 0.4576 -0.8781
+vn -0.1315 0.5447 -0.8283
+vn -0.0000 0.4574 -0.8893
+vn 0.1315 0.5447 -0.8283
+vn 0.1394 0.4576 -0.8781
+vn 0.1315 0.5447 -0.8282
+vn 0.3818 0.5456 -0.7460
+vn 0.2759 0.4581 -0.8450
+vn 0.4932 0.5459 -0.6772
+vn 0.4048 0.4585 -0.7911
+vn 0.3817 0.5457 -0.7460
+vn 0.5231 0.4588 -0.7182
+vn 0.4932 0.5460 -0.6772
+vn 0.7182 0.4588 -0.5231
+vn 0.6283 0.4589 -0.6282
+vn 0.7911 0.4585 -0.4048
+vn 0.7460 0.5456 -0.3817
+vn 0.8450 0.4581 -0.2759
+vn 0.8782 0.4576 -0.1394
+vn 0.8283 0.5447 -0.1315
+vn 0.8893 0.4574 -0.0000
+vn 0.8781 0.4576 -0.1394
+vn 0.8283 0.5447 0.1315
+vn 0.7969 0.5452 0.2602
+vn 0.8781 0.4576 0.1394
+vn 0.7461 0.5456 0.3817
+vn 0.8450 0.4581 0.2759
+vn 0.6772 0.5459 0.4932
+vn 0.7911 0.4585 0.4048
+vn 0.7460 0.5457 0.3817
+vn 0.7182 0.4588 0.5231
+vn 0.6772 0.5460 0.4932
+vn 0.5231 0.4588 0.7182
+vn 0.6282 0.4589 0.6283
+vn 0.4048 0.4585 0.7911
+vn 0.3817 0.5456 0.7461
+vn 0.2759 0.4581 0.8450
+vn 0.4049 0.4585 0.7911
+vn 0.1394 0.4576 0.8781
+vn 0.1315 0.5447 0.8283
+vn -0.0000 0.4574 0.8893
+vn -0.1315 0.5447 0.8283
+vn -0.1394 0.4576 0.8781
+vn -0.1315 0.5447 0.8282
+vn -0.3818 0.5456 0.7460
+vn -0.2759 0.4581 0.8450
+vn -0.4932 0.5459 0.6772
+vn -0.4048 0.4585 0.7911
+vn -0.3817 0.5457 0.7460
+vn -0.5231 0.4588 0.7182
+vn -0.4932 0.5460 0.6772
+vn -0.7182 0.4588 0.5231
+vn -0.6283 0.4589 0.6282
+vn -0.7911 0.4585 0.4048
+vn -0.7460 0.5456 0.3817
+vn -0.8450 0.4581 0.2759
+vn -0.8782 0.4576 0.1394
+vn -0.8283 0.5447 0.1315
+vn -0.8893 0.4574 -0.0000
+vn -0.8781 0.4576 0.1394
+vn -0.8283 0.5447 -0.1315
+vn -0.8782 0.4576 -0.1394
+vn -0.9048 0.4259 -0.0000
+vn -0.8935 0.4261 -0.1419
+vn -0.8598 0.4266 -0.2807
+vn -0.8450 0.4581 -0.2758
+vn -0.8050 0.4270 -0.4119
+vn -0.6283 0.4589 -0.6283
+vn -0.7308 0.4272 -0.5323
+vn -0.6283 0.4589 -0.6282
+vn -0.5323 0.4272 -0.7308
+vn -0.6393 0.4273 -0.6393
+vn -0.4119 0.4270 -0.8050
+vn -0.2807 0.4265 -0.8598
+vn -0.1419 0.4261 -0.8935
+vn -0.2807 0.4266 -0.8598
+vn -0.1394 0.4576 -0.8782
+vn -0.0000 0.4259 -0.9048
+vn 0.1394 0.4576 -0.8782
+vn 0.1419 0.4261 -0.8935
+vn 0.2807 0.4265 -0.8598
+vn 0.2758 0.4581 -0.8450
+vn 0.4119 0.4270 -0.8050
+vn 0.6283 0.4589 -0.6283
+vn 0.5323 0.4272 -0.7308
+vn 0.6282 0.4589 -0.6283
+vn 0.7308 0.4272 -0.5323
+vn 0.6393 0.4273 -0.6393
+vn 0.8050 0.4270 -0.4119
+vn 0.7911 0.4585 -0.4049
+vn 0.8598 0.4265 -0.2807
+vn 0.8935 0.4261 -0.1419
+vn 0.8598 0.4266 -0.2807
+vn 0.9048 0.4259 -0.0000
+vn 0.8782 0.4576 0.1394
+vn 0.8935 0.4261 0.1419
+vn 0.8598 0.4266 0.2807
+vn 0.8450 0.4581 0.2758
+vn 0.8050 0.4270 0.4119
+vn 0.6283 0.4589 0.6283
+vn 0.7308 0.4272 0.5323
+vn 0.6283 0.4589 0.6282
+vn 0.5323 0.4272 0.7308
+vn 0.6393 0.4273 0.6393
+vn 0.4119 0.4270 0.8050
+vn 0.2807 0.4265 0.8598
+vn 0.1419 0.4261 0.8935
+vn 0.2807 0.4266 0.8598
+vn 0.1394 0.4576 0.8782
+vn -0.0000 0.4259 0.9048
+vn -0.1394 0.4576 0.8782
+vn -0.1419 0.4261 0.8935
+vn -0.2807 0.4265 0.8598
+vn -0.2758 0.4581 0.8450
+vn -0.4119 0.4270 0.8050
+vn -0.6283 0.4589 0.6283
+vn -0.5323 0.4272 0.7308
+vn -0.6282 0.4589 0.6283
+vn -0.7308 0.4272 0.5323
+vn -0.6393 0.4273 0.6393
+vn -0.8050 0.4270 0.4119
+vn -0.7911 0.4585 0.4049
+vn -0.8598 0.4265 0.2807
+vn -0.8935 0.4261 0.1419
+vn -0.8598 0.4266 0.2807
+vn -0.9091 0.4167 -0.0000
+vn -0.8977 0.4169 0.1425
+vn -0.8935 0.4261 -0.1418
+vn -0.8977 0.4169 -0.1426
+vn -0.8050 0.4270 -0.4120
+vn -0.8639 0.4173 -0.2820
+vn -0.8088 0.4177 -0.4139
+vn -0.7343 0.4180 -0.5348
+vn -0.5348 0.4180 -0.7343
+vn -0.6423 0.4181 -0.6424
+vn -0.4139 0.4177 -0.8088
+vn -0.5349 0.4180 -0.7343
+vn -0.4120 0.4270 -0.8050
+vn -0.2820 0.4173 -0.8639
+vn -0.2806 0.4266 -0.8598
+vn -0.1425 0.4169 -0.8977
+vn -0.0000 0.4167 -0.9091
+vn 0.1418 0.4261 -0.8935
+vn 0.2807 0.4266 -0.8598
+vn 0.1425 0.4169 -0.8977
+vn 0.4120 0.4270 -0.8050
+vn 0.2820 0.4173 -0.8639
+vn 0.4139 0.4177 -0.8088
+vn 0.5348 0.4180 -0.7343
+vn 0.7343 0.4180 -0.5348
+vn 0.6424 0.4181 -0.6423
+vn 0.8088 0.4177 -0.4139
+vn 0.7343 0.4180 -0.5349
+vn 0.8050 0.4270 -0.4120
+vn 0.8639 0.4173 -0.2820
+vn 0.8598 0.4265 -0.2806
+vn 0.8977 0.4169 -0.1425
+vn 0.9091 0.4167 -0.0000
+vn 0.8935 0.4261 0.1418
+vn 0.8977 0.4169 0.1426
+vn 0.8050 0.4270 0.4120
+vn 0.8639 0.4173 0.2820
+vn 0.8088 0.4177 0.4139
+vn 0.7343 0.4180 0.5348
+vn 0.5348 0.4180 0.7343
+vn 0.6423 0.4181 0.6424
+vn 0.4139 0.4177 0.8088
+vn 0.5349 0.4180 0.7343
+vn 0.2820 0.4173 0.8639
+vn 0.2806 0.4266 0.8598
+vn 0.1425 0.4169 0.8977
+vn -0.0000 0.4167 0.9091
+vn -0.1418 0.4261 0.8935
+vn -0.2807 0.4266 0.8598
+vn -0.1425 0.4169 0.8977
+vn -0.4120 0.4270 0.8050
+vn -0.2820 0.4173 0.8639
+vn -0.4139 0.4177 0.8088
+vn -0.5348 0.4180 0.7343
+vn -0.7343 0.4180 0.5348
+vn -0.6424 0.4181 0.6423
+vn -0.8088 0.4177 0.4139
+vn -0.7343 0.4180 0.5349
+vn -0.8050 0.4270 0.4120
+vn -0.8639 0.4173 0.2820
+vn -0.8598 0.4265 0.2806
+vn -0.9047 0.4010 0.1436
+vn -0.8707 0.4014 0.2842
+vn -0.9162 0.4008 -0.0000
+vn -0.8977 0.4169 -0.1425
+vn -0.9047 0.4010 -0.1437
+vn -0.8707 0.4014 -0.2842
+vn -0.7343 0.4180 -0.5349
+vn -0.8152 0.4018 -0.4172
+vn -0.6424 0.4181 -0.6423
+vn -0.7401 0.4021 -0.5390
+vn -0.5391 0.4021 -0.7401
+vn -0.6474 0.4022 -0.6474
+vn -0.4171 0.4018 -0.8152
+vn -0.2842 0.4014 -0.8707
+vn -0.4172 0.4018 -0.8152
+vn -0.1436 0.4010 -0.9047
+vn -0.0000 0.4008 -0.9162
+vn 0.1436 0.4010 -0.9047
+vn 0.1426 0.4169 -0.8977
+vn 0.2842 0.4014 -0.8707
+vn 0.5349 0.4180 -0.7343
+vn 0.4172 0.4018 -0.8152
+vn 0.6423 0.4181 -0.6424
+vn 0.5391 0.4021 -0.7401
+vn 0.7401 0.4021 -0.5391
+vn 0.6474 0.4022 -0.6474
+vn 0.8152 0.4018 -0.4171
+vn 0.8707 0.4014 -0.2842
+vn 0.8152 0.4018 -0.4172
+vn 0.9047 0.4010 -0.1436
+vn 0.9162 0.4008 -0.0000
+vn 0.8977 0.4169 0.1425
+vn 0.9047 0.4010 0.1437
+vn 0.8707 0.4014 0.2842
+vn 0.7343 0.4180 0.5349
+vn 0.8152 0.4018 0.4172
+vn 0.6424 0.4181 0.6423
+vn 0.7401 0.4021 0.5390
+vn 0.5391 0.4021 0.7401
+vn 0.6474 0.4022 0.6474
+vn 0.4171 0.4018 0.8152
+vn 0.2842 0.4014 0.8707
+vn 0.4172 0.4018 0.8152
+vn 0.1436 0.4010 0.9047
+vn -0.0000 0.4008 0.9162
+vn -0.1436 0.4010 0.9047
+vn -0.1426 0.4169 0.8977
+vn -0.2842 0.4014 0.8707
+vn -0.5349 0.4180 0.7343
+vn -0.4172 0.4018 0.8152
+vn -0.6423 0.4181 0.6424
+vn -0.5391 0.4021 0.7401
+vn -0.7401 0.4021 0.5391
+vn -0.6474 0.4022 0.6474
+vn -0.8152 0.4018 0.4171
+vn -0.8152 0.4018 0.4172
+vn -0.8800 0.3782 0.2873
+vn -0.8240 0.3785 0.4216
+vn -0.9144 0.3778 0.1452
+vn -0.9047 0.4010 0.1437
+vn -0.9260 0.3776 -0.0000
+vn -0.9047 0.4010 -0.1436
+vn -0.9144 0.3778 -0.1452
+vn -0.8800 0.3782 -0.2873
+vn -0.7401 0.4021 -0.5391
+vn -0.8240 0.3785 -0.4216
+vn -0.7481 0.3788 -0.5449
+vn -0.5449 0.3788 -0.7481
+vn -0.6544 0.3789 -0.6544
+vn -0.5390 0.4021 -0.7401
+vn -0.4216 0.3785 -0.8240
+vn -0.2873 0.3782 -0.8800
+vn -0.1452 0.3778 -0.9144
+vn -0.1437 0.4010 -0.9047
+vn -0.0000 0.3776 -0.9260
+vn 0.1452 0.3778 -0.9144
+vn 0.2873 0.3782 -0.8800
+vn 0.4216 0.3785 -0.8240
+vn 0.4171 0.4018 -0.8152
+vn 0.5449 0.3788 -0.7481
+vn 0.7481 0.3788 -0.5449
+vn 0.6544 0.3789 -0.6544
+vn 0.7401 0.4021 -0.5390
+vn 0.8240 0.3785 -0.4216
+vn 0.7481 0.3788 -0.5448
+vn 0.8800 0.3782 -0.2873
+vn 0.9144 0.3778 -0.1452
+vn 0.9047 0.4010 -0.1437
+vn 0.9260 0.3776 -0.0000
+vn 0.9047 0.4010 0.1436
+vn 0.9144 0.3778 0.1452
+vn 0.8800 0.3782 0.2873
+vn 0.7401 0.4021 0.5391
+vn 0.8240 0.3785 0.4216
+vn 0.8152 0.4018 0.4171
+vn 0.7481 0.3788 0.5449
+vn 0.5449 0.3788 0.7481
+vn 0.6544 0.3789 0.6544
+vn 0.5390 0.4021 0.7401
+vn 0.4216 0.3785 0.8240
+vn 0.2873 0.3782 0.8800
+vn 0.1452 0.3778 0.9144
+vn 0.1437 0.4010 0.9047
+vn -0.0000 0.3776 0.9260
+vn -0.1452 0.3778 0.9144
+vn -0.2873 0.3782 0.8800
+vn -0.4216 0.3785 0.8240
+vn -0.4171 0.4018 0.8152
+vn -0.5449 0.3788 0.7481
+vn -0.7481 0.3788 0.5449
+vn -0.6544 0.3789 0.6544
+vn -0.7401 0.4021 0.5390
+vn -0.7481 0.3788 0.5448
+vn -0.8350 0.3468 0.4273
+vn -0.7581 0.3470 0.5522
+vn -0.8240 0.3785 0.4217
+vn -0.8918 0.3464 0.2911
+vn -0.9266 0.3460 0.1471
+vn -0.9383 0.3459 -0.0000
+vn -0.9383 0.3458 -0.0000
+vn -0.9266 0.3460 -0.1471
+vn -0.8240 0.3785 -0.4217
+vn -0.8918 0.3464 -0.2911
+vn -0.8350 0.3468 -0.4273
+vn -0.7581 0.3470 -0.5522
+vn -0.5522 0.3470 -0.7581
+vn -0.6631 0.3471 -0.6632
+vn -0.4273 0.3468 -0.8350
+vn -0.4217 0.3785 -0.8240
+vn -0.2911 0.3464 -0.8918
+vn -0.1471 0.3460 -0.9266
+vn -0.0000 0.3459 -0.9383
+vn -0.0000 0.3458 -0.9383
+vn 0.1471 0.3460 -0.9266
+vn 0.4217 0.3785 -0.8240
+vn 0.2911 0.3464 -0.8918
+vn 0.4273 0.3468 -0.8350
+vn 0.5522 0.3470 -0.7581
+vn 0.7581 0.3470 -0.5522
+vn 0.6632 0.3471 -0.6631
+vn 0.8350 0.3468 -0.4273
+vn 0.8240 0.3785 -0.4217
+vn 0.8918 0.3464 -0.2911
+vn 0.9266 0.3460 -0.1471
+vn 0.9383 0.3459 -0.0000
+vn 0.9383 0.3458 -0.0000
+vn 0.9266 0.3460 0.1471
+vn 0.8240 0.3785 0.4217
+vn 0.8918 0.3464 0.2911
+vn 0.8350 0.3468 0.4273
+vn 0.7581 0.3470 0.5522
+vn 0.5522 0.3470 0.7581
+vn 0.6631 0.3471 0.6632
+vn 0.4273 0.3468 0.8350
+vn 0.4217 0.3785 0.8240
+vn 0.2911 0.3464 0.8918
+vn 0.1471 0.3460 0.9266
+vn -0.0000 0.3459 0.9383
+vn -0.0000 0.3458 0.9383
+vn -0.1471 0.3460 0.9266
+vn -0.4217 0.3785 0.8240
+vn -0.2911 0.3464 0.8918
+vn -0.4273 0.3468 0.8350
+vn -0.5522 0.3470 0.7581
+vn -0.6632 0.3471 0.6631
+vn -0.6631 0.3471 0.6632
+vn -0.7697 0.3052 0.5607
+vn -0.6733 0.3053 0.6733
+vn -0.8478 0.3050 0.4339
+vn -0.7698 0.3052 0.5606
+vn -0.9054 0.3047 0.2956
+vn -0.9408 0.3044 0.1494
+vn -0.9526 0.3042 -0.0000
+vn -0.9408 0.3044 0.1493
+vn -0.9408 0.3044 -0.1493
+vn -0.9054 0.3047 -0.2956
+vn -0.8478 0.3050 -0.4338
+vn -0.7697 0.3052 -0.5607
+vn -0.6632 0.3471 -0.6631
+vn -0.5607 0.3052 -0.7697
+vn -0.6733 0.3053 -0.6734
+vn -0.4339 0.3050 -0.8478
+vn -0.5606 0.3052 -0.7698
+vn -0.2956 0.3047 -0.9054
+vn -0.1494 0.3044 -0.9408
+vn -0.0000 0.3042 -0.9526
+vn -0.1493 0.3044 -0.9408
+vn 0.1493 0.3044 -0.9408
+vn 0.2956 0.3047 -0.9054
+vn 0.4338 0.3050 -0.8478
+vn 0.5607 0.3052 -0.7697
+vn 0.6631 0.3471 -0.6632
+vn 0.7697 0.3052 -0.5607
+vn 0.6733 0.3053 -0.6733
+vn 0.8478 0.3050 -0.4339
+vn 0.7698 0.3052 -0.5606
+vn 0.9054 0.3047 -0.2956
+vn 0.9408 0.3044 -0.1494
+vn 0.9526 0.3042 -0.0000
+vn 0.9408 0.3044 -0.1493
+vn 0.9408 0.3044 0.1493
+vn 0.9054 0.3047 0.2956
+vn 0.8478 0.3050 0.4338
+vn 0.7697 0.3052 0.5607
+vn 0.6632 0.3471 0.6631
+vn 0.5607 0.3052 0.7697
+vn 0.6733 0.3053 0.6734
+vn 0.4339 0.3050 0.8478
+vn 0.5606 0.3052 0.7698
+vn 0.2956 0.3047 0.9054
+vn 0.1494 0.3044 0.9408
+vn -0.0000 0.3042 0.9526
+vn 0.1493 0.3044 0.9408
+vn -0.1493 0.3043 0.9408
+vn -0.2956 0.3047 0.9054
+vn -0.4338 0.3050 0.8478
+vn -0.5607 0.3052 0.7697
+vn -0.5698 0.2516 0.7823
+vn -0.5606 0.3052 0.7698
+vn -0.6733 0.3053 0.6734
+vn -0.7823 0.2516 0.5698
+vn -0.6843 0.2516 0.6844
+vn -0.8616 0.2514 0.4409
+vn -0.8478 0.3050 0.4338
+vn -0.9202 0.2511 0.3004
+vn -0.9561 0.2508 0.1518
+vn -0.9681 0.2507 -0.0000
+vn -0.9408 0.3044 -0.1494
+vn -0.9561 0.2508 -0.1518
+vn -0.9408 0.3043 -0.1494
+vn -0.9202 0.2511 -0.3004
+vn -0.8616 0.2514 -0.4409
+vn -0.6733 0.3053 -0.6733
+vn -0.7823 0.2516 -0.5698
+vn -0.7698 0.3052 -0.5606
+vn -0.6734 0.3053 -0.6733
+vn -0.5698 0.2516 -0.7823
+vn -0.6844 0.2516 -0.6843
+vn -0.4409 0.2514 -0.8616
+vn -0.4338 0.3050 -0.8478
+vn -0.3004 0.2511 -0.9202
+vn -0.1518 0.2508 -0.9561
+vn -0.0000 0.2507 -0.9681
+vn 0.1494 0.3044 -0.9408
+vn 0.1518 0.2508 -0.9561
+vn 0.1494 0.3043 -0.9408
+vn 0.3004 0.2511 -0.9202
+vn 0.4409 0.2514 -0.8616
+vn 0.5698 0.2516 -0.7823
+vn 0.5606 0.3052 -0.7698
+vn 0.6733 0.3053 -0.6734
+vn 0.7823 0.2516 -0.5698
+vn 0.6843 0.2516 -0.6844
+vn 0.8616 0.2514 -0.4409
+vn 0.8478 0.3050 -0.4338
+vn 0.9202 0.2511 -0.3004
+vn 0.9561 0.2508 -0.1518
+vn 0.9681 0.2507 -0.0000
+vn 0.9408 0.3044 0.1494
+vn 0.9561 0.2508 0.1518
+vn 0.9408 0.3043 0.1494
+vn 0.9202 0.2511 0.3004
+vn 0.8616 0.2514 0.4409
+vn 0.6733 0.3053 0.6733
+vn 0.7823 0.2516 0.5698
+vn 0.7698 0.3052 0.5606
+vn 0.6734 0.3053 0.6733
+vn 0.5698 0.2516 0.7823
+vn 0.6844 0.2516 0.6843
+vn 0.4409 0.2514 0.8616
+vn 0.4338 0.3050 0.8478
+vn 0.3004 0.2511 0.9202
+vn 0.1518 0.2508 0.9561
+vn -0.0000 0.2507 0.9681
+vn -0.1494 0.3044 0.9408
+vn -0.1518 0.2508 0.9561
+vn -0.1494 0.3043 0.9408
+vn -0.3004 0.2511 0.9202
+vn -0.4409 0.2514 0.8616
+vn -0.4478 0.1837 0.8751
+vn -0.5787 0.1838 0.7945
+vn -0.6844 0.2516 0.6843
+vn -0.7945 0.1838 0.5787
+vn -0.6951 0.1838 0.6950
+vn -0.8751 0.1836 0.4478
+vn -0.9345 0.1834 0.3051
+vn -0.9709 0.1832 0.1541
+vn -0.9831 0.1831 -0.0000
+vn -0.9709 0.1832 -0.1541
+vn -0.9345 0.1834 -0.3051
+vn -0.8751 0.1837 -0.4478
+vn -0.7945 0.1838 -0.5787
+vn -0.6843 0.2516 -0.6844
+vn -0.5787 0.1838 -0.7945
+vn -0.6950 0.1838 -0.6951
+vn -0.4478 0.1836 -0.8751
+vn -0.3051 0.1834 -0.9345
+vn -0.1541 0.1832 -0.9709
+vn -0.0000 0.1831 -0.9831
+vn 0.1541 0.1832 -0.9709
+vn 0.3051 0.1834 -0.9345
+vn 0.4478 0.1837 -0.8751
+vn 0.5787 0.1838 -0.7945
+vn 0.6844 0.2516 -0.6843
+vn 0.7945 0.1838 -0.5787
+vn 0.6951 0.1838 -0.6950
+vn 0.8751 0.1836 -0.4478
+vn 0.9345 0.1834 -0.3051
+vn 0.9709 0.1832 -0.1541
+vn 0.9831 0.1831 -0.0000
+vn 0.9709 0.1832 0.1541
+vn 0.9345 0.1834 0.3051
+vn 0.8751 0.1837 0.4478
+vn 0.7945 0.1838 0.5787
+vn 0.6843 0.2516 0.6844
+vn 0.5787 0.1838 0.7945
+vn 0.6950 0.1838 0.6951
+vn 0.4478 0.1836 0.8751
+vn 0.3051 0.1834 0.9345
+vn 0.1541 0.1832 0.9709
+vn -0.0000 0.1831 0.9831
+vn -0.1541 0.1832 0.9709
+vn -0.3051 0.1834 0.9345
+vn -0.4478 0.1836 0.8751
+vn -0.3088 0.0994 0.9459
+vn -0.5787 0.1838 0.7946
+vn -0.4533 0.0995 0.8858
+vn -0.6950 0.1838 0.6951
+vn -0.5858 0.0996 0.8043
+vn -0.6951 0.1838 0.6951
+vn -0.8043 0.0996 0.5858
+vn -0.7036 0.0996 0.7036
+vn -0.7946 0.1838 0.5787
+vn -0.8858 0.0995 0.4533
+vn -0.9459 0.0994 0.3088
+vn -0.9828 0.0992 0.1560
+vn -0.9951 0.0992 -0.0000
+vn -0.9827 0.0992 0.1561
+vn -0.9828 0.0993 -0.1560
+vn -0.8751 0.1836 -0.4478
+vn -0.9459 0.0994 -0.3088
+vn -0.7946 0.1838 -0.5787
+vn -0.8858 0.0995 -0.4533
+vn -0.6951 0.1838 -0.6950
+vn -0.8043 0.0996 -0.5858
+vn -0.6951 0.1838 -0.6951
+vn -0.5858 0.0996 -0.8043
+vn -0.7036 0.0996 -0.7036
+vn -0.5787 0.1838 -0.7946
+vn -0.4533 0.0995 -0.8858
+vn -0.3088 0.0994 -0.9459
+vn -0.1560 0.0992 -0.9828
+vn -0.0000 0.0992 -0.9951
+vn -0.1561 0.0992 -0.9827
+vn 0.1560 0.0993 -0.9828
+vn 0.4478 0.1836 -0.8751
+vn 0.3088 0.0994 -0.9459
+vn 0.5787 0.1838 -0.7946
+vn 0.4533 0.0995 -0.8858
+vn 0.6950 0.1838 -0.6951
+vn 0.5858 0.0996 -0.8043
+vn 0.6951 0.1838 -0.6951
+vn 0.8043 0.0996 -0.5858
+vn 0.7036 0.0996 -0.7036
+vn 0.7946 0.1838 -0.5787
+vn 0.8858 0.0995 -0.4533
+vn 0.9459 0.0994 -0.3088
+vn 0.9828 0.0992 -0.1560
+vn 0.9951 0.0992 -0.0000
+vn 0.9827 0.0992 -0.1561
+vn 0.9828 0.0993 0.1560
+vn 0.8751 0.1836 0.4478
+vn 0.9459 0.0994 0.3088
+vn 0.7946 0.1838 0.5787
+vn 0.8858 0.0995 0.4533
+vn 0.6951 0.1838 0.6950
+vn 0.8043 0.0996 0.5858
+vn 0.6951 0.1838 0.6951
+vn 0.5858 0.0996 0.8043
+vn 0.7036 0.0996 0.7036
+vn 0.5787 0.1838 0.7946
+vn 0.4533 0.0995 0.8858
+vn 0.3088 0.0994 0.9459
+vn 0.1560 0.0992 0.9828
+vn -0.0000 0.0992 0.9951
+vn 0.1561 0.0992 0.9827
+vn -0.1560 0.0993 0.9828
+vn -0.1567 -0.0280 0.9872
+vn -0.1560 0.0992 0.9828
+vn -0.3102 -0.0280 0.9503
+vn -0.4554 -0.0280 0.8899
+vn -0.5885 -0.0280 0.8080
+vn -0.8080 -0.0281 0.5885
+vn -0.7068 -0.0280 0.7068
+vn -0.8898 -0.0280 0.4554
+vn -0.8080 -0.0280 0.5885
+vn -0.9503 -0.0280 0.3102
+vn -0.8898 -0.0281 0.4554
+vn -0.9872 -0.0280 0.1567
+vn -0.9996 -0.0279 -0.0000
+vn -0.9872 -0.0280 0.1568
+vn -0.9872 -0.0280 -0.1567
+vn -0.9828 0.0992 -0.1560
+vn -0.9503 -0.0280 -0.3102
+vn -0.8899 -0.0280 -0.4554
+vn -0.8080 -0.0280 -0.5885
+vn -0.5885 -0.0281 -0.8080
+vn -0.7068 -0.0280 -0.7068
+vn -0.4554 -0.0280 -0.8898
+vn -0.5885 -0.0280 -0.8080
+vn -0.3102 -0.0280 -0.9503
+vn -0.4554 -0.0281 -0.8898
+vn -0.1567 -0.0280 -0.9872
+vn -0.1560 0.0993 -0.9828
+vn -0.0000 -0.0279 -0.9996
+vn -0.1568 -0.0279 -0.9872
+vn 0.1560 0.0992 -0.9828
+vn 0.1567 -0.0280 -0.9872
+vn 0.3102 -0.0280 -0.9503
+vn 0.4554 -0.0280 -0.8899
+vn 0.5885 -0.0280 -0.8080
+vn 0.8080 -0.0281 -0.5885
+vn 0.7068 -0.0280 -0.7068
+vn 0.8898 -0.0280 -0.4554
+vn 0.8080 -0.0280 -0.5885
+vn 0.9503 -0.0280 -0.3102
+vn 0.8898 -0.0281 -0.4554
+vn 0.9872 -0.0280 -0.1567
+vn 0.9996 -0.0279 -0.0000
+vn 0.9872 -0.0280 -0.1568
+vn 0.9872 -0.0280 0.1567
+vn 0.9828 0.0992 0.1560
+vn 0.9503 -0.0280 0.3102
+vn 0.8899 -0.0280 0.4554
+vn 0.8080 -0.0280 0.5885
+vn 0.5885 -0.0281 0.8080
+vn 0.7068 -0.0280 0.7068
+vn 0.4554 -0.0280 0.8898
+vn 0.5885 -0.0280 0.8080
+vn 0.3102 -0.0280 0.9503
+vn 0.4554 -0.0281 0.8898
+vn 0.1567 -0.0280 0.9872
+vn 0.1560 0.0993 0.9828
+vn -0.0000 -0.0279 0.9996
+vn 0.1568 -0.0279 0.9872
+vn -0.1532 -0.2121 0.9652
+vn -0.0000 -0.2119 0.9773
+vn -0.1567 -0.0279 0.9873
+vn -0.3033 -0.2123 0.9290
+vn -0.4452 -0.2126 0.8698
+vn -0.3033 -0.2123 0.9289
+vn -0.5753 -0.2127 0.7898
+vn -0.4451 -0.2126 0.8699
+vn -0.6909 -0.2127 0.6909
+vn -0.6909 -0.2128 0.6909
+vn -0.7068 -0.0281 0.7068
+vn -0.8899 -0.0280 0.4554
+vn -0.7898 -0.2127 0.5753
+vn -0.8699 -0.2126 0.4451
+vn -0.9872 -0.0279 0.1568
+vn -0.9289 -0.2123 0.3033
+vn -0.9652 -0.2121 0.1532
+vn -0.9872 -0.0279 0.1567
+vn -0.9652 -0.2121 -0.1532
+vn -0.9773 -0.2119 -0.0000
+vn -0.9873 -0.0279 -0.1567
+vn -0.9290 -0.2123 -0.3033
+vn -0.8698 -0.2126 -0.4452
+vn -0.7898 -0.2127 -0.5753
+vn -0.8699 -0.2126 -0.4451
+vn -0.6909 -0.2127 -0.6909
+vn -0.6909 -0.2128 -0.6909
+vn -0.7068 -0.0281 -0.7068
+vn -0.4554 -0.0280 -0.8899
+vn -0.5753 -0.2127 -0.7898
+vn -0.4451 -0.2126 -0.8699
+vn -0.3033 -0.2123 -0.9289
+vn -0.1532 -0.2121 -0.9652
+vn -0.1567 -0.0279 -0.9872
+vn 0.1532 -0.2121 -0.9652
+vn -0.0000 -0.2119 -0.9773
+vn 0.1567 -0.0279 -0.9873
+vn 0.3033 -0.2123 -0.9290
+vn 0.4452 -0.2126 -0.8698
+vn 0.5753 -0.2127 -0.7898
+vn 0.4451 -0.2126 -0.8699
+vn 0.6909 -0.2127 -0.6909
+vn 0.6909 -0.2128 -0.6909
+vn 0.7068 -0.0281 -0.7068
+vn 0.8899 -0.0280 -0.4554
+vn 0.7898 -0.2127 -0.5753
+vn 0.8699 -0.2126 -0.4451
+vn 0.9872 -0.0279 -0.1568
+vn 0.9289 -0.2123 -0.3033
+vn 0.9652 -0.2121 -0.1532
+vn 0.9872 -0.0279 -0.1567
+vn 0.9652 -0.2121 0.1532
+vn 0.9773 -0.2119 -0.0000
+vn 0.9873 -0.0279 0.1567
+vn 0.9290 -0.2123 0.3033
+vn 0.8698 -0.2126 0.4452
+vn 0.7898 -0.2127 0.5753
+vn 0.8699 -0.2126 0.4451
+vn 0.6909 -0.2127 0.6909
+vn 0.6909 -0.2128 0.6909
+vn 0.7068 -0.0281 0.7068
+vn 0.4554 -0.0280 0.8899
+vn 0.5753 -0.2127 0.7898
+vn 0.4451 -0.2126 0.8699
+vn 0.3033 -0.2123 0.9289
+vn 0.1532 -0.2121 0.9652
+vn 0.1567 -0.0279 0.9872
+vn 0.1439 -0.3978 0.9061
+vn 0.1533 -0.2121 0.9652
+vn -0.1439 -0.3978 0.9061
+vn -0.0000 -0.3976 0.9176
+vn -0.1532 -0.2120 0.9652
+vn -0.2847 -0.3982 0.8720
+vn -0.3032 -0.2123 0.9290
+vn -0.4178 -0.3986 0.8164
+vn -0.2846 -0.3982 0.8720
+vn -0.5399 -0.3988 0.7413
+vn -0.6484 -0.3990 0.6484
+vn -0.5399 -0.3989 0.7412
+vn -0.6484 -0.3989 0.6484
+vn -0.8698 -0.2126 0.4452
+vn -0.7413 -0.3988 0.5399
+vn -0.9290 -0.2123 0.3032
+vn -0.8164 -0.3986 0.4178
+vn -0.8699 -0.2125 0.4451
+vn -0.8720 -0.3982 0.2847
+vn -0.9290 -0.2123 0.3033
+vn -0.9061 -0.3978 0.1439
+vn -0.9652 -0.2121 0.1533
+vn -0.9061 -0.3978 -0.1439
+vn -0.9176 -0.3976 -0.0000
+vn -0.8720 -0.3982 -0.2847
+vn -0.9290 -0.2123 -0.3032
+vn -0.8164 -0.3986 -0.4178
+vn -0.8720 -0.3982 -0.2846
+vn -0.7413 -0.3988 -0.5399
+vn -0.6484 -0.3989 -0.6484
+vn -0.7412 -0.3989 -0.5399
+vn -0.6484 -0.3990 -0.6484
+vn -0.4452 -0.2126 -0.8698
+vn -0.5399 -0.3988 -0.7413
+vn -0.3032 -0.2123 -0.9290
+vn -0.4178 -0.3986 -0.8164
+vn -0.4451 -0.2125 -0.8699
+vn -0.1532 -0.2120 -0.9652
+vn -0.2847 -0.3982 -0.8720
+vn -0.3033 -0.2123 -0.9290
+vn -0.1439 -0.3978 -0.9061
+vn -0.1533 -0.2121 -0.9652
+vn 0.1439 -0.3978 -0.9061
+vn -0.0000 -0.3976 -0.9176
+vn 0.1532 -0.2120 -0.9652
+vn 0.2847 -0.3982 -0.8720
+vn 0.3032 -0.2123 -0.9290
+vn 0.4178 -0.3986 -0.8164
+vn 0.2846 -0.3982 -0.8720
+vn 0.5399 -0.3988 -0.7413
+vn 0.6484 -0.3990 -0.6484
+vn 0.5399 -0.3989 -0.7412
+vn 0.6484 -0.3989 -0.6484
+vn 0.8698 -0.2126 -0.4452
+vn 0.7413 -0.3988 -0.5399
+vn 0.9290 -0.2123 -0.3032
+vn 0.8164 -0.3986 -0.4178
+vn 0.8699 -0.2125 -0.4451
+vn 0.8720 -0.3982 -0.2847
+vn 0.9290 -0.2123 -0.3033
+vn 0.9061 -0.3978 -0.1439
+vn 0.9652 -0.2121 -0.1533
+vn 0.9061 -0.3978 0.1439
+vn 0.9176 -0.3976 -0.0000
+vn 0.8720 -0.3982 0.2847
+vn 0.9290 -0.2123 0.3032
+vn 0.8164 -0.3986 0.4178
+vn 0.8720 -0.3982 0.2846
+vn 0.7413 -0.3988 0.5399
+vn 0.6484 -0.3989 0.6484
+vn 0.7412 -0.3989 0.5399
+vn 0.6484 -0.3990 0.6484
+vn 0.4452 -0.2126 0.8698
+vn 0.5399 -0.3988 0.7413
+vn 0.3032 -0.2123 0.9290
+vn 0.4178 -0.3986 0.8164
+vn 0.4451 -0.2125 0.8699
+vn 0.1532 -0.2120 0.9652
+vn 0.2847 -0.3982 0.8720
+vn 0.3033 -0.2123 0.9290
+vn 0.1438 -0.3978 0.9061
+vn 0.2608 -0.5418 0.7990
+vn 0.1318 -0.5414 0.8304
+vn -0.1318 -0.5414 0.8304
+vn -0.0000 -0.5411 0.8409
+vn -0.2608 -0.5419 0.7990
+vn -0.3827 -0.5423 0.7480
+vn -0.2608 -0.5418 0.7990
+vn -0.4946 -0.5426 0.6790
+vn -0.3828 -0.5423 0.7479
+vn -0.5399 -0.3988 0.7412
+vn -0.5939 -0.5427 0.5939
+vn -0.7412 -0.3988 0.5399
+vn -0.6790 -0.5426 0.4946
+vn -0.7412 -0.3989 0.5399
+vn -0.7479 -0.5423 0.3827
+vn -0.9061 -0.3978 0.1438
+vn -0.7990 -0.5418 0.2608
+vn -0.8304 -0.5414 0.1318
+vn -0.8304 -0.5414 -0.1318
+vn -0.8409 -0.5411 -0.0000
+vn -0.7990 -0.5419 -0.2608
+vn -0.7480 -0.5423 -0.3827
+vn -0.7990 -0.5418 -0.2608
+vn -0.6790 -0.5426 -0.4946
+vn -0.7479 -0.5423 -0.3828
+vn -0.7412 -0.3988 -0.5399
+vn -0.5939 -0.5427 -0.5939
+vn -0.5399 -0.3988 -0.7412
+vn -0.4946 -0.5426 -0.6790
+vn -0.5399 -0.3989 -0.7412
+vn -0.3827 -0.5423 -0.7479
+vn -0.1438 -0.3978 -0.9061
+vn -0.2608 -0.5418 -0.7990
+vn -0.1318 -0.5414 -0.8304
+vn 0.1318 -0.5414 -0.8304
+vn -0.0000 -0.5411 -0.8409
+vn 0.2608 -0.5419 -0.7990
+vn 0.3827 -0.5423 -0.7480
+vn 0.2608 -0.5418 -0.7990
+vn 0.4946 -0.5426 -0.6790
+vn 0.3828 -0.5423 -0.7479
+vn 0.5399 -0.3988 -0.7412
+vn 0.5939 -0.5427 -0.5939
+vn 0.7412 -0.3988 -0.5399
+vn 0.6790 -0.5426 -0.4946
+vn 0.7412 -0.3989 -0.5399
+vn 0.7479 -0.5423 -0.3827
+vn 0.9061 -0.3978 -0.1438
+vn 0.7990 -0.5418 -0.2608
+vn 0.8304 -0.5414 -0.1318
+vn 0.8304 -0.5414 0.1318
+vn 0.8409 -0.5411 -0.0000
+vn 0.7990 -0.5419 0.2608
+vn 0.7480 -0.5423 0.3827
+vn 0.7990 -0.5418 0.2608
+vn 0.6790 -0.5426 0.4946
+vn 0.7479 -0.5423 0.3828
+vn 0.7412 -0.3988 0.5399
+vn 0.5939 -0.5427 0.5939
+vn 0.5399 -0.3988 0.7412
+vn 0.4946 -0.5426 0.6790
+vn 0.5399 -0.3989 0.7412
+vn 0.3827 -0.5423 0.7479
+vn 0.2608 -0.5419 0.7990
+vn 0.3483 -0.6446 0.6806
+vn 0.3828 -0.5423 0.7479
+vn 0.2374 -0.6442 0.7271
+vn -0.0000 -0.5412 0.8409
+vn 0.1200 -0.6437 0.7558
+vn -0.1200 -0.6437 0.7558
+vn -0.0000 -0.6435 0.7655
+vn -0.2373 -0.6442 0.7271
+vn -0.3483 -0.6446 0.6806
+vn -0.3827 -0.5423 0.7479
+vn -0.4499 -0.6449 0.6178
+vn -0.5403 -0.6450 0.5403
+vn -0.5404 -0.6450 0.5403
+vn -0.6178 -0.6449 0.4499
+vn -0.6790 -0.5426 0.4945
+vn -0.7990 -0.5419 0.2608
+vn -0.6806 -0.6446 0.3483
+vn -0.7479 -0.5423 0.3828
+vn -0.7271 -0.6442 0.2374
+vn -0.8409 -0.5412 -0.0000
+vn -0.7558 -0.6437 0.1200
+vn -0.7558 -0.6437 -0.1200
+vn -0.7654 -0.6435 -0.0000
+vn -0.7271 -0.6442 -0.2373
+vn -0.6805 -0.6446 -0.3483
+vn -0.6178 -0.6449 -0.4499
+vn -0.6806 -0.6446 -0.3483
+vn -0.5403 -0.6450 -0.5403
+vn -0.5403 -0.6450 -0.5404
+vn -0.4499 -0.6449 -0.6178
+vn -0.4945 -0.5426 -0.6790
+vn -0.2608 -0.5419 -0.7990
+vn -0.3483 -0.6446 -0.6806
+vn -0.3828 -0.5423 -0.7479
+vn -0.2374 -0.6442 -0.7271
+vn -0.0000 -0.5412 -0.8409
+vn -0.1200 -0.6437 -0.7558
+vn 0.1200 -0.6437 -0.7558
+vn -0.0000 -0.6435 -0.7655
+vn 0.2373 -0.6442 -0.7271
+vn 0.3483 -0.6446 -0.6805
+vn 0.3827 -0.5423 -0.7479
+vn 0.4499 -0.6449 -0.6178
+vn 0.3483 -0.6446 -0.6806
+vn 0.5403 -0.6450 -0.5403
+vn 0.5404 -0.6450 -0.5403
+vn 0.6178 -0.6449 -0.4499
+vn 0.6790 -0.5426 -0.4945
+vn 0.7990 -0.5419 -0.2608
+vn 0.6806 -0.6446 -0.3483
+vn 0.7479 -0.5423 -0.3828
+vn 0.7271 -0.6442 -0.2374
+vn 0.8409 -0.5412 -0.0000
+vn 0.7558 -0.6437 -0.1200
+vn 0.7558 -0.6437 0.1200
+vn 0.7654 -0.6435 -0.0000
+vn 0.7271 -0.6442 0.2373
+vn 0.6805 -0.6446 0.3483
+vn 0.6178 -0.6449 0.4499
+vn 0.6806 -0.6446 0.3483
+vn 0.5403 -0.6450 0.5403
+vn 0.5403 -0.6450 0.5404
+vn 0.4499 -0.6449 0.6178
+vn 0.4945 -0.5426 0.6790
+vn 0.4126 -0.7133 0.5665
+vn 0.3194 -0.7130 0.6241
+vn 0.3482 -0.6446 0.6806
+vn 0.2177 -0.7126 0.6669
+vn -0.0000 -0.6435 0.7654
+vn 0.1101 -0.7122 0.6933
+vn -0.1101 -0.7122 0.6933
+vn -0.0000 -0.7120 0.7022
+vn -0.2177 -0.7126 0.6669
+vn -0.2374 -0.6442 0.7271
+vn -0.3194 -0.7130 0.6242
+vn -0.4126 -0.7133 0.5665
+vn -0.4500 -0.6449 0.6177
+vn -0.4955 -0.7134 0.4955
+vn -0.6178 -0.6449 0.4500
+vn -0.5403 -0.6450 0.5404
+vn -0.5665 -0.7133 0.4126
+vn -0.6241 -0.7130 0.3194
+vn -0.6806 -0.6446 0.3482
+vn -0.6669 -0.7126 0.2177
+vn -0.6933 -0.7122 0.1101
+vn -0.6933 -0.7122 -0.1101
+vn -0.7022 -0.7120 -0.0000
+vn -0.6669 -0.7126 -0.2177
+vn -0.7271 -0.6442 -0.2374
+vn -0.6242 -0.7130 -0.3194
+vn -0.5665 -0.7133 -0.4126
+vn -0.6177 -0.6449 -0.4500
+vn -0.4955 -0.7134 -0.4955
+vn -0.4500 -0.6449 -0.6178
+vn -0.5404 -0.6450 -0.5403
+vn -0.4126 -0.7133 -0.5665
+vn -0.3194 -0.7130 -0.6241
+vn -0.3482 -0.6446 -0.6806
+vn -0.2177 -0.7126 -0.6669
+vn -0.0000 -0.6435 -0.7654
+vn -0.1101 -0.7122 -0.6933
+vn 0.1101 -0.7122 -0.6933
+vn -0.0000 -0.7120 -0.7022
+vn 0.2177 -0.7126 -0.6669
+vn 0.2374 -0.6442 -0.7271
+vn 0.3194 -0.7130 -0.6242
+vn 0.4126 -0.7133 -0.5665
+vn 0.4500 -0.6449 -0.6177
+vn 0.4955 -0.7134 -0.4955
+vn 0.6178 -0.6449 -0.4500
+vn 0.5403 -0.6450 -0.5404
+vn 0.5665 -0.7133 -0.4126
+vn 0.6241 -0.7130 -0.3194
+vn 0.6806 -0.6446 -0.3482
+vn 0.6669 -0.7126 -0.2177
+vn 0.6933 -0.7122 -0.1101
+vn 0.6933 -0.7122 0.1101
+vn 0.7022 -0.7120 -0.0000
+vn 0.6669 -0.7126 0.2177
+vn 0.7271 -0.6442 0.2374
+vn 0.6242 -0.7130 0.3194
+vn 0.5665 -0.7133 0.4126
+vn 0.6177 -0.6449 0.4500
+vn 0.4955 -0.7134 0.4955
+vn 0.4500 -0.6449 0.6178
+vn 0.5404 -0.6450 0.5403
+vn 0.4640 -0.7545 0.4641
+vn 0.3864 -0.7545 0.5305
+vn 0.2991 -0.7542 0.5846
+vn 0.3194 -0.7130 0.6242
+vn 0.1100 -0.7122 0.6933
+vn 0.2039 -0.7538 0.6246
+vn 0.1031 -0.7534 0.6494
+vn -0.1031 -0.7534 0.6494
+vn -0.0000 -0.7532 0.6578
+vn -0.2039 -0.7538 0.6246
+vn -0.2991 -0.7542 0.5846
+vn -0.3194 -0.7130 0.6241
+vn -0.3864 -0.7545 0.5305
+vn -0.4640 -0.7545 0.4641
+vn -0.3864 -0.7545 0.5306
+vn -0.5305 -0.7545 0.3864
+vn -0.5846 -0.7542 0.2991
+vn -0.6242 -0.7130 0.3194
+vn -0.6933 -0.7122 0.1100
+vn -0.6246 -0.7538 0.2039
+vn -0.6494 -0.7534 0.1031
+vn -0.6494 -0.7534 -0.1031
+vn -0.6578 -0.7532 -0.0000
+vn -0.6246 -0.7538 -0.2039
+vn -0.5846 -0.7542 -0.2991
+vn -0.5305 -0.7545 -0.3864
+vn -0.4640 -0.7545 -0.4641
+vn -0.5306 -0.7545 -0.3864
+vn -0.3864 -0.7545 -0.5305
+vn -0.2991 -0.7542 -0.5846
+vn -0.3194 -0.7130 -0.6242
+vn -0.1100 -0.7122 -0.6933
+vn -0.2039 -0.7538 -0.6246
+vn -0.1031 -0.7534 -0.6494
+vn 0.1031 -0.7534 -0.6494
+vn -0.0000 -0.7532 -0.6578
+vn 0.2039 -0.7538 -0.6246
+vn 0.2991 -0.7542 -0.5846
+vn 0.3194 -0.7130 -0.6241
+vn 0.3864 -0.7545 -0.5305
+vn 0.4640 -0.7545 -0.4641
+vn 0.3864 -0.7545 -0.5306
+vn 0.5305 -0.7545 -0.3864
+vn 0.5846 -0.7542 -0.2991
+vn 0.6242 -0.7130 -0.3194
+vn 0.6933 -0.7122 -0.1100
+vn 0.6246 -0.7538 -0.2039
+vn 0.6494 -0.7534 -0.1031
+vn 0.6494 -0.7534 0.1031
+vn 0.6578 -0.7532 -0.0000
+vn 0.6246 -0.7538 0.2039
+vn 0.5846 -0.7542 0.2991
+vn 0.5305 -0.7545 0.3864
+vn 0.5306 -0.7545 0.3864
+vn 0.4512 -0.7700 0.4511
+vn 0.5158 -0.7699 0.3757
+vn 0.4511 -0.7700 0.4512
+vn 0.4640 -0.7545 0.4640
+vn 0.2991 -0.7542 0.5845
+vn 0.3757 -0.7699 0.5158
+vn 0.2908 -0.7697 0.5683
+vn 0.1982 -0.7693 0.6073
+vn 0.1003 -0.7689 0.6314
+vn -0.1003 -0.7689 0.6314
+vn -0.0000 -0.7687 0.6396
+vn -0.1983 -0.7693 0.6073
+vn -0.2908 -0.7697 0.5683
+vn -0.1982 -0.7693 0.6073
+vn -0.2991 -0.7542 0.5845
+vn -0.3757 -0.7699 0.5158
+vn -0.4511 -0.7700 0.4512
+vn -0.4512 -0.7700 0.4511
+vn -0.4640 -0.7545 0.4640
+vn -0.5845 -0.7542 0.2991
+vn -0.5158 -0.7699 0.3757
+vn -0.5683 -0.7697 0.2908
+vn -0.6073 -0.7693 0.1982
+vn -0.6314 -0.7689 0.1003
+vn -0.6314 -0.7689 -0.1003
+vn -0.6396 -0.7687 -0.0000
+vn -0.6073 -0.7693 -0.1983
+vn -0.5683 -0.7697 -0.2908
+vn -0.6073 -0.7693 -0.1982
+vn -0.5845 -0.7542 -0.2991
+vn -0.5158 -0.7699 -0.3757
+vn -0.4512 -0.7700 -0.4511
+vn -0.4511 -0.7700 -0.4512
+vn -0.4640 -0.7545 -0.4640
+vn -0.2991 -0.7542 -0.5845
+vn -0.3757 -0.7699 -0.5158
+vn -0.2908 -0.7697 -0.5683
+vn -0.1982 -0.7693 -0.6073
+vn -0.1003 -0.7689 -0.6314
+vn 0.1003 -0.7689 -0.6314
+vn -0.0000 -0.7687 -0.6396
+vn 0.1983 -0.7693 -0.6073
+vn 0.2908 -0.7697 -0.5683
+vn 0.1982 -0.7693 -0.6073
+vn 0.2991 -0.7542 -0.5845
+vn 0.3757 -0.7699 -0.5158
+vn 0.4511 -0.7700 -0.4512
+vn 0.4512 -0.7700 -0.4511
+vn 0.4640 -0.7545 -0.4640
+vn 0.5845 -0.7542 -0.2991
+vn 0.5158 -0.7699 -0.3757
+vn 0.5683 -0.7697 -0.2908
+vn 0.6073 -0.7693 -0.1982
+vn 0.6314 -0.7689 -0.1003
+vn 0.6314 -0.7689 0.1003
+vn 0.6396 -0.7687 -0.0000
+vn 0.6073 -0.7693 0.1983
+vn 0.5683 -0.7697 0.2908
+vn 0.6073 -0.7693 0.1982
+vn 0.5845 -0.7542 0.2991
+vn 0.5372 -0.7472 0.3913
+vn 0.5919 -0.7469 0.3029
+vn 0.4699 -0.7473 0.4699
+vn 0.4511 -0.7700 0.4511
+vn 0.3913 -0.7472 0.5372
+vn 0.1983 -0.7693 0.6073
+vn 0.3029 -0.7469 0.5919
+vn 0.2064 -0.7465 0.6325
+vn 0.1044 -0.7461 0.6576
+vn 0.1002 -0.7689 0.6314
+vn -0.1044 -0.7461 0.6576
+vn -0.0000 -0.7459 0.6660
+vn -0.1002 -0.7689 0.6314
+vn -0.2065 -0.7465 0.6325
+vn -0.3029 -0.7469 0.5919
+vn -0.3913 -0.7472 0.5372
+vn -0.4699 -0.7473 0.4699
+vn -0.4511 -0.7700 0.4511
+vn -0.5372 -0.7472 0.3913
+vn -0.6073 -0.7693 0.1983
+vn -0.5919 -0.7469 0.3029
+vn -0.6325 -0.7465 0.2064
+vn -0.6576 -0.7461 0.1044
+vn -0.6314 -0.7689 0.1002
+vn -0.6576 -0.7461 -0.1044
+vn -0.6660 -0.7459 -0.0000
+vn -0.6314 -0.7689 -0.1002
+vn -0.6325 -0.7465 -0.2065
+vn -0.5919 -0.7469 -0.3029
+vn -0.5372 -0.7472 -0.3913
+vn -0.4699 -0.7473 -0.4699
+vn -0.4511 -0.7700 -0.4511
+vn -0.3913 -0.7472 -0.5372
+vn -0.1983 -0.7693 -0.6073
+vn -0.3029 -0.7469 -0.5919
+vn -0.2064 -0.7465 -0.6325
+vn -0.1044 -0.7461 -0.6576
+vn -0.1002 -0.7689 -0.6314
+vn 0.1044 -0.7461 -0.6576
+vn -0.0000 -0.7459 -0.6660
+vn 0.1002 -0.7689 -0.6314
+vn 0.2065 -0.7465 -0.6325
+vn 0.3029 -0.7469 -0.5919
+vn 0.3913 -0.7472 -0.5372
+vn 0.4699 -0.7473 -0.4699
+vn 0.4511 -0.7700 -0.4511
+vn 0.5372 -0.7472 -0.3913
+vn 0.6073 -0.7693 -0.1983
+vn 0.5919 -0.7469 -0.3029
+vn 0.6325 -0.7465 -0.2064
+vn 0.6576 -0.7461 -0.1044
+vn 0.6314 -0.7689 -0.1002
+vn 0.6576 -0.7461 0.1044
+vn 0.6660 -0.7459 -0.0000
+vn 0.6314 -0.7689 0.1002
+vn 0.6325 -0.7465 0.2065
+vn 0.7126 -0.5993 0.3647
+vn 0.7613 -0.5989 0.2485
+vn 0.6469 -0.5996 0.4712
+vn 0.5658 -0.5997 0.5659
+vn 0.5658 -0.5997 0.5658
+vn 0.4712 -0.5996 0.6469
+vn 0.2065 -0.7465 0.6325
+vn 0.3647 -0.5993 0.7126
+vn 0.2485 -0.5989 0.7613
+vn 0.1256 -0.5984 0.7913
+vn -0.1256 -0.5984 0.7913
+vn -0.0000 -0.5982 0.8013
+vn -0.2485 -0.5989 0.7613
+vn -0.1256 -0.5985 0.7912
+vn -0.3647 -0.5993 0.7126
+vn -0.4712 -0.5996 0.6469
+vn -0.5659 -0.5997 0.5658
+vn -0.5658 -0.5997 0.5658
+vn -0.6469 -0.5996 0.4712
+vn -0.6325 -0.7465 0.2065
+vn -0.7126 -0.5993 0.3647
+vn -0.7613 -0.5989 0.2485
+vn -0.7913 -0.5984 0.1256
+vn -0.7913 -0.5984 -0.1256
+vn -0.8013 -0.5982 -0.0000
+vn -0.7613 -0.5989 -0.2485
+vn -0.7912 -0.5985 -0.1256
+vn -0.7126 -0.5993 -0.3647
+vn -0.6469 -0.5996 -0.4712
+vn -0.5658 -0.5997 -0.5659
+vn -0.5658 -0.5997 -0.5658
+vn -0.4712 -0.5996 -0.6469
+vn -0.2065 -0.7465 -0.6325
+vn -0.3647 -0.5993 -0.7126
+vn -0.2485 -0.5989 -0.7613
+vn -0.1256 -0.5984 -0.7913
+vn 0.1256 -0.5984 -0.7913
+vn -0.0000 -0.5982 -0.8013
+vn 0.2485 -0.5989 -0.7613
+vn 0.1256 -0.5985 -0.7912
+vn 0.3647 -0.5993 -0.7126
+vn 0.4712 -0.5996 -0.6469
+vn 0.5659 -0.5997 -0.5658
+vn 0.5658 -0.5997 -0.5658
+vn 0.6469 -0.5996 -0.4712
+vn 0.6325 -0.7465 -0.2065
+vn 0.7126 -0.5993 -0.3647
+vn 0.7613 -0.5989 -0.2485
+vn 0.7913 -0.5984 -0.1256
+vn 0.7913 -0.5984 0.1256
+vn 0.8013 -0.5982 -0.0000
+vn 0.7912 -0.5985 0.1256
+vn 0.9032 -0.3118 0.2948
+vn 0.9385 -0.3115 0.1490
+vn 0.8457 -0.3122 0.4328
+vn 0.9032 -0.3119 0.2948
+vn 0.7126 -0.5994 0.3647
+vn 0.7679 -0.3124 0.5593
+vn 0.6717 -0.3124 0.6717
+vn 0.5593 -0.3124 0.7679
+vn 0.4328 -0.3122 0.8457
+vn 0.1256 -0.5985 0.7913
+vn 0.2949 -0.3118 0.9032
+vn 0.1490 -0.3115 0.9385
+vn 0.1256 -0.5985 0.7912
+vn -0.1490 -0.3115 0.9385
+vn -0.0000 -0.3114 0.9503
+vn -0.2948 -0.3118 0.9032
+vn -0.4328 -0.3122 0.8457
+vn -0.2948 -0.3119 0.9032
+vn -0.5593 -0.3124 0.7679
+vn -0.4711 -0.5996 0.6469
+vn -0.6717 -0.3124 0.6717
+vn -0.7679 -0.3123 0.5593
+vn -0.8457 -0.3122 0.4328
+vn -0.7913 -0.5985 0.1256
+vn -0.9032 -0.3118 0.2949
+vn -0.9385 -0.3115 0.1490
+vn -0.7912 -0.5985 0.1256
+vn -0.9385 -0.3115 -0.1490
+vn -0.9503 -0.3114 -0.0000
+vn -0.9032 -0.3118 -0.2948
+vn -0.8457 -0.3122 -0.4328
+vn -0.9032 -0.3119 -0.2948
+vn -0.7126 -0.5994 -0.3647
+vn -0.7679 -0.3124 -0.5593
+vn -0.6717 -0.3124 -0.6717
+vn -0.5593 -0.3124 -0.7679
+vn -0.4328 -0.3122 -0.8457
+vn -0.1256 -0.5985 -0.7913
+vn -0.2949 -0.3118 -0.9032
+vn -0.1490 -0.3115 -0.9385
+vn -0.1256 -0.5985 -0.7912
+vn 0.1490 -0.3115 -0.9385
+vn -0.0000 -0.3114 -0.9503
+vn 0.2948 -0.3118 -0.9032
+vn 0.4328 -0.3122 -0.8457
+vn 0.2948 -0.3119 -0.9032
+vn 0.5593 -0.3124 -0.7679
+vn 0.4711 -0.5996 -0.6469
+vn 0.6717 -0.3124 -0.6717
+vn 0.7679 -0.3123 -0.5593
+vn 0.8457 -0.3122 -0.4328
+vn 0.7913 -0.5985 -0.1256
+vn 0.9032 -0.3118 -0.2949
+vn 0.9385 -0.3115 -0.1490
+vn 0.7912 -0.5985 -0.1256
+vn 0.9503 -0.3114 -0.0000
+vn 0.9142 -0.3784 0.1451
+vn 0.9503 -0.3113 -0.0000
+vn 0.6683 -0.7363 0.1061
+vn 0.9257 -0.3783 -0.0000
+vn 0.4024 -0.9132 0.0639
+vn 0.6769 -0.7361 -0.0000
+vn 0.2368 -0.9708 0.0376
+vn 0.4076 -0.9131 -0.0000
+vn 0.1435 -0.9894 0.0228
+vn 0.2399 -0.9708 -0.0000
+vn 0.0881 -0.9960 0.0140
+vn 0.1454 -0.9894 -0.0000
+vn 0.0529 -0.9986 0.0084
+vn 0.0892 -0.9960 -0.0000
+vn 0.0880 -0.9960 0.0140
+vn 0.0292 -0.9996 0.0046
+vn 0.0536 -0.9986 -0.0000
+vn 0.0125 -0.9999 0.0020
+vn 0.0296 -0.9996 -0.0000
+vn -0.0000 -1.0000 -0.0000
+vn 0.0127 -0.9999 -0.0000
+vn 0.0125 -0.9999 -0.0020
+vn 0.0120 -0.9999 -0.0039
+vn 0.0112 -0.9999 -0.0057
+vn 0.0102 -0.9999 -0.0074
+vn 0.0113 -0.9999 -0.0058
+vn 0.0090 -0.9999 -0.0090
+vn 0.0074 -0.9999 -0.0102
+vn 0.0058 -0.9999 -0.0113
+vn 0.0039 -0.9999 -0.0120
+vn 0.0057 -0.9999 -0.0112
+vn 0.0020 -0.9999 -0.0125
+vn -0.0000 -0.9999 -0.0127
+vn -0.0020 -0.9999 -0.0125
+vn -0.0039 -0.9999 -0.0120
+vn -0.0057 -0.9999 -0.0112
+vn -0.0074 -0.9999 -0.0102
+vn -0.0058 -0.9999 -0.0113
+vn -0.0090 -0.9999 -0.0090
+vn -0.0102 -0.9999 -0.0074
+vn -0.0113 -0.9999 -0.0058
+vn -0.0120 -0.9999 -0.0039
+vn -0.0112 -0.9999 -0.0057
+vn -0.0125 -0.9999 -0.0020
+vn -0.0127 -0.9999 -0.0000
+vn -0.0125 -0.9999 0.0020
+vn -0.0120 -0.9999 0.0039
+vn -0.0112 -0.9999 0.0057
+vn -0.0102 -0.9999 0.0074
+vn -0.0113 -0.9999 0.0058
+vn -0.0090 -0.9999 0.0090
+vn -0.0074 -0.9999 0.0102
+vn -0.0058 -0.9999 0.0113
+vn -0.0039 -0.9999 0.0120
+vn -0.0057 -0.9999 0.0112
+vn -0.0020 -0.9999 0.0125
+vn -0.0000 -0.9999 0.0127
+vn 0.0020 -0.9999 0.0125
+vn 0.0039 -0.9999 0.0120
+vn 0.0057 -0.9999 0.0112
+vn 0.0074 -0.9999 0.0102
+vn 0.0058 -0.9999 0.0113
+vn 0.0090 -0.9999 0.0090
+vn 0.0102 -0.9999 0.0074
+vn 0.0113 -0.9999 0.0058
+vn 0.0120 -0.9999 0.0039
+vn 0.0112 -0.9999 0.0057
+vn 0.0281 -0.9996 0.0092
+vn 0.0508 -0.9986 0.0166
+vn 0.0847 -0.9960 0.0276
+vn 0.1379 -0.9894 0.0450
+vn 0.2277 -0.9709 0.0743
+vn 0.3870 -0.9134 0.1263
+vn 0.3869 -0.9134 0.1263
+vn 0.6428 -0.7367 0.2098
+vn 0.6429 -0.7367 0.2099
+vn 0.9142 -0.3784 0.1452
+vn 0.8798 -0.3787 0.2872
+vn 0.9032 -0.3119 0.2949
+vn 0.8237 -0.3791 0.4216
+vn 0.6017 -0.7370 0.3079
+vn 0.8798 -0.3788 0.2872
+vn 0.8238 -0.3791 0.4216
+vn 0.3620 -0.9136 0.1852
+vn 0.6428 -0.7367 0.2099
+vn 0.6016 -0.7370 0.3079
+vn 0.2130 -0.9710 0.1090
+vn 0.3620 -0.9136 0.1853
+vn 0.1290 -0.9894 0.0660
+vn 0.2130 -0.9709 0.1090
+vn 0.0792 -0.9960 0.0405
+vn 0.0475 -0.9986 0.0243
+vn 0.0846 -0.9960 0.0276
+vn 0.0262 -0.9996 0.0134
+vn 0.0263 -0.9996 0.0134
+vn 0.0238 -0.9996 0.0174
+vn 0.0238 -0.9996 0.0173
+vn 0.0431 -0.9986 0.0314
+vn 0.0718 -0.9960 0.0523
+vn 0.0719 -0.9960 0.0523
+vn 0.1170 -0.9895 0.0852
+vn 0.1170 -0.9895 0.0853
+vn 0.1932 -0.9710 0.1407
+vn 0.1933 -0.9710 0.1408
+vn 0.3285 -0.9137 0.2393
+vn 0.5461 -0.7373 0.3978
+vn 0.5461 -0.7373 0.3977
+vn 0.8238 -0.3791 0.4215
+vn 0.7479 -0.3793 0.5447
+vn 0.8457 -0.3121 0.4328
+vn 0.6542 -0.3794 0.6542
+vn 0.4776 -0.7374 0.4776
+vn 0.7479 -0.3793 0.5448
+vn 0.2873 -0.9138 0.2873
+vn 0.1690 -0.9710 0.1690
+vn 0.2873 -0.9137 0.2873
+vn 0.1024 -0.9895 0.1024
+vn 0.1932 -0.9710 0.1408
+vn 0.0628 -0.9960 0.0628
+vn 0.1171 -0.9894 0.0853
+vn 0.0377 -0.9986 0.0377
+vn 0.0628 -0.9961 0.0628
+vn 0.0208 -0.9996 0.0208
+vn 0.0090 -0.9999 0.0089
+vn 0.0089 -0.9999 0.0090
+vn 0.0174 -0.9996 0.0238
+vn 0.0075 -0.9999 0.0102
+vn 0.0314 -0.9986 0.0431
+vn 0.0173 -0.9996 0.0238
+vn 0.0523 -0.9960 0.0718
+vn 0.0853 -0.9894 0.1171
+vn 0.1407 -0.9710 0.1932
+vn 0.0852 -0.9895 0.1171
+vn 0.2393 -0.9137 0.3285
+vn 0.1407 -0.9710 0.1933
+vn 0.3978 -0.7373 0.5461
+vn 0.4777 -0.7373 0.4777
+vn 0.5448 -0.3793 0.7479
+vn 0.3977 -0.7373 0.5461
+vn 0.5593 -0.3123 0.7679
+vn 0.5447 -0.3793 0.7479
+vn 0.4215 -0.3791 0.8238
+vn 0.4216 -0.3791 0.8238
+vn 0.3079 -0.7371 0.6016
+vn 0.1852 -0.9136 0.3620
+vn 0.1408 -0.9710 0.1933
+vn 0.1090 -0.9710 0.2130
+vn 0.0853 -0.9895 0.1171
+vn 0.0660 -0.9894 0.1290
+vn 0.0523 -0.9960 0.0719
+vn 0.0405 -0.9960 0.0792
+vn 0.0243 -0.9986 0.0475
+vn 0.0134 -0.9996 0.0262
+vn 0.0134 -0.9996 0.0263
+vn 0.0092 -0.9996 0.0281
+vn 0.0166 -0.9986 0.0508
+vn 0.0276 -0.9960 0.0846
+vn 0.0450 -0.9894 0.1379
+vn 0.0276 -0.9960 0.0847
+vn 0.0743 -0.9709 0.2277
+vn 0.1263 -0.9134 0.3870
+vn 0.2098 -0.7367 0.6428
+vn 0.1263 -0.9134 0.3869
+vn 0.3079 -0.7370 0.6017
+vn 0.2872 -0.3788 0.8798
+vn 0.2949 -0.3119 0.9032
+vn 0.2872 -0.3787 0.8798
+vn 0.1451 -0.3784 0.9142
+vn 0.2099 -0.7367 0.6428
+vn 0.1452 -0.3784 0.9142
+vn 0.1061 -0.7363 0.6683
+vn 0.0639 -0.9132 0.4024
+vn 0.0376 -0.9708 0.2368
+vn 0.0228 -0.9894 0.1435
+vn 0.0140 -0.9960 0.0881
+vn 0.0084 -0.9986 0.0529
+vn 0.0046 -0.9996 0.0292
+vn -0.0000 -0.9996 0.0296
+vn -0.0000 -0.9986 0.0536
+vn -0.0000 -0.9960 0.0892
+vn -0.0000 -0.9894 0.1454
+vn -0.0000 -0.9708 0.2399
+vn 0.0376 -0.9708 0.2369
+vn -0.0000 -0.9131 0.4077
+vn -0.0000 -0.7361 0.6769
+vn -0.0000 -0.9131 0.4076
+vn -0.0000 -0.3783 0.9257
+vn -0.0000 -0.3113 0.9503
+vn -0.0000 -0.3782 0.9257
+vn -0.1451 -0.3784 0.9142
+vn -0.1061 -0.7363 0.6683
+vn -0.0639 -0.9132 0.4024
+vn -0.0376 -0.9708 0.2368
+vn -0.0228 -0.9894 0.1435
+vn -0.0140 -0.9960 0.0881
+vn -0.0084 -0.9986 0.0529
+vn -0.0140 -0.9960 0.0880
+vn -0.0046 -0.9996 0.0292
+vn -0.0092 -0.9996 0.0281
+vn -0.0166 -0.9986 0.0508
+vn -0.0276 -0.9960 0.0847
+vn -0.0450 -0.9894 0.1379
+vn -0.0743 -0.9709 0.2277
+vn -0.1263 -0.9134 0.3870
+vn -0.1263 -0.9134 0.3869
+vn -0.2098 -0.7367 0.6428
+vn -0.2099 -0.7367 0.6428
+vn -0.1452 -0.3784 0.9142
+vn -0.2872 -0.3787 0.8798
+vn -0.2949 -0.3119 0.9032
+vn -0.4216 -0.3791 0.8238
+vn -0.3079 -0.7370 0.6017
+vn -0.2872 -0.3788 0.8798
+vn -0.1852 -0.9136 0.3620
+vn -0.1090 -0.9710 0.2130
+vn -0.1853 -0.9136 0.3620
+vn -0.0660 -0.9894 0.1290
+vn -0.1090 -0.9709 0.2130
+vn -0.0405 -0.9960 0.0792
+vn -0.0243 -0.9986 0.0475
+vn -0.0276 -0.9960 0.0846
+vn -0.0134 -0.9996 0.0262
+vn -0.0134 -0.9996 0.0263
+vn -0.0174 -0.9996 0.0238
+vn -0.0173 -0.9996 0.0238
+vn -0.0314 -0.9986 0.0431
+vn -0.0523 -0.9960 0.0718
+vn -0.0523 -0.9960 0.0719
+vn -0.0852 -0.9895 0.1170
+vn -0.0853 -0.9895 0.1170
+vn -0.1407 -0.9710 0.1932
+vn -0.1408 -0.9710 0.1933
+vn -0.2393 -0.9137 0.3285
+vn -0.3079 -0.7370 0.6016
+vn -0.3978 -0.7373 0.5461
+vn -0.3977 -0.7373 0.5461
+vn -0.4215 -0.3791 0.8238
+vn -0.5447 -0.3793 0.7479
+vn -0.6542 -0.3794 0.6542
+vn -0.4776 -0.7374 0.4776
+vn -0.5448 -0.3793 0.7479
+vn -0.2873 -0.9138 0.2873
+vn -0.1690 -0.9710 0.1690
+vn -0.2873 -0.9137 0.2873
+vn -0.1024 -0.9895 0.1024
+vn -0.1408 -0.9710 0.1932
+vn -0.0628 -0.9960 0.0628
+vn -0.0853 -0.9894 0.1171
+vn -0.0377 -0.9986 0.0377
+vn -0.0208 -0.9996 0.0208
+vn -0.0089 -0.9999 0.0090
+vn -0.0090 -0.9999 0.0089
+vn -0.0238 -0.9996 0.0174
+vn -0.0102 -0.9999 0.0075
+vn -0.0431 -0.9986 0.0314
+vn -0.0238 -0.9996 0.0173
+vn -0.0718 -0.9960 0.0523
+vn -0.1171 -0.9894 0.0853
+vn -0.1932 -0.9710 0.1408
+vn -0.1171 -0.9895 0.0852
+vn -0.3285 -0.9137 0.2393
+vn -0.1933 -0.9710 0.1407
+vn -0.5461 -0.7373 0.3978
+vn -0.4777 -0.7373 0.4777
+vn -0.7479 -0.3793 0.5448
+vn -0.5461 -0.7373 0.3977
+vn -0.7479 -0.3793 0.5447
+vn -0.8238 -0.3791 0.4215
+vn -0.8237 -0.3791 0.4216
+vn -0.6016 -0.7371 0.3079
+vn -0.3620 -0.9136 0.1852
+vn -0.1933 -0.9710 0.1408
+vn -0.2130 -0.9710 0.1090
+vn -0.1171 -0.9895 0.0853
+vn -0.1290 -0.9894 0.0660
+vn -0.0719 -0.9960 0.0523
+vn -0.0792 -0.9960 0.0405
+vn -0.0475 -0.9986 0.0243
+vn -0.0262 -0.9996 0.0134
+vn -0.0263 -0.9996 0.0134
+vn -0.0281 -0.9996 0.0092
+vn -0.0508 -0.9986 0.0166
+vn -0.0846 -0.9960 0.0276
+vn -0.1379 -0.9894 0.0450
+vn -0.0847 -0.9960 0.0276
+vn -0.2277 -0.9709 0.0743
+vn -0.3870 -0.9134 0.1263
+vn -0.6428 -0.7367 0.2098
+vn -0.3869 -0.9134 0.1263
+vn -0.6017 -0.7370 0.3079
+vn -0.8798 -0.3788 0.2872
+vn -0.6429 -0.7367 0.2098
+vn -0.8798 -0.3787 0.2872
+vn -0.9142 -0.3784 0.1451
+vn -0.9142 -0.3784 0.1452
+vn -0.6683 -0.7363 0.1061
+vn -0.4024 -0.9132 0.0639
+vn -0.2368 -0.9708 0.0376
+vn -0.1435 -0.9894 0.0228
+vn -0.0881 -0.9960 0.0140
+vn -0.0529 -0.9986 0.0084
+vn -0.0292 -0.9996 0.0046
+vn -0.0296 -0.9996 -0.0000
+vn -0.0536 -0.9986 -0.0000
+vn -0.0892 -0.9960 -0.0000
+vn -0.1454 -0.9894 -0.0000
+vn -0.2399 -0.9708 -0.0000
+vn -0.2369 -0.9708 0.0376
+vn -0.4077 -0.9131 -0.0000
+vn -0.6769 -0.7361 -0.0000
+vn -0.4076 -0.9131 -0.0000
+vn -0.9257 -0.3783 -0.0000
+vn -0.9503 -0.3113 -0.0000
+vn -0.9257 -0.3782 -0.0000
+vn -0.9142 -0.3784 -0.1451
+vn -0.6683 -0.7363 -0.1061
+vn -0.4024 -0.9132 -0.0639
+vn -0.2368 -0.9708 -0.0376
+vn -0.1435 -0.9894 -0.0228
+vn -0.0881 -0.9960 -0.0140
+vn -0.0529 -0.9986 -0.0084
+vn -0.0880 -0.9960 -0.0140
+vn -0.0292 -0.9996 -0.0046
+vn -0.0281 -0.9996 -0.0092
+vn -0.0508 -0.9986 -0.0166
+vn -0.0847 -0.9960 -0.0276
+vn -0.1379 -0.9894 -0.0450
+vn -0.2277 -0.9709 -0.0743
+vn -0.3870 -0.9134 -0.1263
+vn -0.3869 -0.9134 -0.1263
+vn -0.6428 -0.7367 -0.2098
+vn -0.6429 -0.7367 -0.2099
+vn -0.9142 -0.3784 -0.1452
+vn -0.8798 -0.3787 -0.2872
+vn -0.9032 -0.3119 -0.2949
+vn -0.8237 -0.3791 -0.4216
+vn -0.6017 -0.7370 -0.3079
+vn -0.8798 -0.3788 -0.2872
+vn -0.8238 -0.3791 -0.4216
+vn -0.3620 -0.9136 -0.1852
+vn -0.6428 -0.7367 -0.2099
+vn -0.6016 -0.7370 -0.3079
+vn -0.2130 -0.9710 -0.1090
+vn -0.3620 -0.9136 -0.1853
+vn -0.1290 -0.9894 -0.0660
+vn -0.2130 -0.9709 -0.1090
+vn -0.0792 -0.9960 -0.0405
+vn -0.0475 -0.9986 -0.0243
+vn -0.0846 -0.9960 -0.0276
+vn -0.0262 -0.9996 -0.0134
+vn -0.0263 -0.9996 -0.0134
+vn -0.0238 -0.9996 -0.0174
+vn -0.0238 -0.9996 -0.0173
+vn -0.0431 -0.9986 -0.0314
+vn -0.0718 -0.9960 -0.0523
+vn -0.0719 -0.9960 -0.0523
+vn -0.1170 -0.9895 -0.0852
+vn -0.1170 -0.9895 -0.0853
+vn -0.1932 -0.9710 -0.1407
+vn -0.1933 -0.9710 -0.1408
+vn -0.3285 -0.9137 -0.2393
+vn -0.5461 -0.7373 -0.3978
+vn -0.5461 -0.7373 -0.3977
+vn -0.8238 -0.3791 -0.4215
+vn -0.7479 -0.3793 -0.5447
+vn -0.8457 -0.3121 -0.4328
+vn -0.6542 -0.3794 -0.6542
+vn -0.4776 -0.7374 -0.4776
+vn -0.7479 -0.3793 -0.5448
+vn -0.2873 -0.9138 -0.2873
+vn -0.1690 -0.9710 -0.1690
+vn -0.2873 -0.9137 -0.2873
+vn -0.1024 -0.9895 -0.1024
+vn -0.1932 -0.9710 -0.1408
+vn -0.0628 -0.9960 -0.0628
+vn -0.1171 -0.9894 -0.0853
+vn -0.0377 -0.9986 -0.0377
+vn -0.0628 -0.9961 -0.0628
+vn -0.0208 -0.9996 -0.0208
+vn -0.0090 -0.9999 -0.0089
+vn -0.0089 -0.9999 -0.0090
+vn -0.0174 -0.9996 -0.0238
+vn -0.0075 -0.9999 -0.0102
+vn -0.0314 -0.9986 -0.0431
+vn -0.0173 -0.9996 -0.0238
+vn -0.0523 -0.9960 -0.0718
+vn -0.0853 -0.9894 -0.1171
+vn -0.1407 -0.9710 -0.1932
+vn -0.0852 -0.9895 -0.1171
+vn -0.2393 -0.9137 -0.3285
+vn -0.1407 -0.9710 -0.1933
+vn -0.3978 -0.7373 -0.5461
+vn -0.4777 -0.7373 -0.4777
+vn -0.5448 -0.3793 -0.7479
+vn -0.3977 -0.7373 -0.5461
+vn -0.5593 -0.3123 -0.7679
+vn -0.5447 -0.3793 -0.7479
+vn -0.4215 -0.3791 -0.8238
+vn -0.4216 -0.3791 -0.8238
+vn -0.3079 -0.7371 -0.6016
+vn -0.1852 -0.9136 -0.3620
+vn -0.1408 -0.9710 -0.1933
+vn -0.1090 -0.9710 -0.2130
+vn -0.0853 -0.9895 -0.1171
+vn -0.0660 -0.9894 -0.1290
+vn -0.0523 -0.9960 -0.0719
+vn -0.0405 -0.9960 -0.0792
+vn -0.0243 -0.9986 -0.0475
+vn -0.0134 -0.9996 -0.0262
+vn -0.0134 -0.9996 -0.0263
+vn -0.0092 -0.9996 -0.0281
+vn -0.0166 -0.9986 -0.0508
+vn -0.0276 -0.9960 -0.0846
+vn -0.0450 -0.9894 -0.1379
+vn -0.0276 -0.9960 -0.0847
+vn -0.0743 -0.9709 -0.2277
+vn -0.1263 -0.9134 -0.3870
+vn -0.2098 -0.7367 -0.6428
+vn -0.1263 -0.9134 -0.3869
+vn -0.3079 -0.7370 -0.6017
+vn -0.2872 -0.3788 -0.8798
+vn -0.2949 -0.3119 -0.9032
+vn -0.2872 -0.3787 -0.8798
+vn -0.1451 -0.3784 -0.9142
+vn -0.2099 -0.7367 -0.6428
+vn -0.1452 -0.3784 -0.9142
+vn -0.1061 -0.7363 -0.6683
+vn -0.0639 -0.9132 -0.4024
+vn -0.0376 -0.9708 -0.2368
+vn -0.0228 -0.9894 -0.1435
+vn -0.0140 -0.9960 -0.0881
+vn -0.0140 -0.9960 -0.0880
+vn -0.0084 -0.9986 -0.0529
+vn -0.0046 -0.9996 -0.0292
+vn -0.0000 -0.9996 -0.0296
+vn -0.0000 -0.9986 -0.0536
+vn -0.0000 -0.9960 -0.0892
+vn -0.0000 -0.9894 -0.1454
+vn -0.0000 -0.9708 -0.2399
+vn -0.0376 -0.9708 -0.2369
+vn -0.0000 -0.9131 -0.4077
+vn -0.0000 -0.7361 -0.6769
+vn -0.0000 -0.9131 -0.4076
+vn -0.0000 -0.3783 -0.9257
+vn -0.0000 -0.3113 -0.9503
+vn -0.0000 -0.3782 -0.9257
+vn 0.1451 -0.3784 -0.9142
+vn 0.1061 -0.7363 -0.6683
+vn 0.0639 -0.9132 -0.4024
+vn 0.0376 -0.9708 -0.2368
+vn 0.0228 -0.9894 -0.1435
+vn 0.0140 -0.9960 -0.0881
+vn 0.0084 -0.9986 -0.0529
+vn 0.0140 -0.9960 -0.0880
+vn 0.0046 -0.9996 -0.0292
+vn 0.0092 -0.9996 -0.0281
+vn 0.0166 -0.9986 -0.0508
+vn 0.0276 -0.9960 -0.0847
+vn 0.0450 -0.9894 -0.1379
+vn 0.0743 -0.9709 -0.2277
+vn 0.1263 -0.9134 -0.3870
+vn 0.1263 -0.9134 -0.3869
+vn 0.2098 -0.7367 -0.6428
+vn 0.2099 -0.7367 -0.6428
+vn 0.1452 -0.3784 -0.9142
+vn 0.2872 -0.3787 -0.8798
+vn 0.2949 -0.3119 -0.9032
+vn 0.4216 -0.3791 -0.8238
+vn 0.3079 -0.7370 -0.6017
+vn 0.2872 -0.3788 -0.8798
+vn 0.1852 -0.9136 -0.3620
+vn 0.1090 -0.9710 -0.2130
+vn 0.1853 -0.9136 -0.3620
+vn 0.0660 -0.9894 -0.1290
+vn 0.1090 -0.9709 -0.2130
+vn 0.0405 -0.9960 -0.0792
+vn 0.0243 -0.9986 -0.0475
+vn 0.0276 -0.9960 -0.0846
+vn 0.0134 -0.9996 -0.0262
+vn 0.0134 -0.9996 -0.0263
+vn 0.0174 -0.9996 -0.0238
+vn 0.0173 -0.9996 -0.0238
+vn 0.0314 -0.9986 -0.0431
+vn 0.0523 -0.9960 -0.0718
+vn 0.0523 -0.9960 -0.0719
+vn 0.0852 -0.9895 -0.1170
+vn 0.0853 -0.9895 -0.1170
+vn 0.1407 -0.9710 -0.1932
+vn 0.1408 -0.9710 -0.1933
+vn 0.2393 -0.9137 -0.3285
+vn 0.3079 -0.7370 -0.6016
+vn 0.3978 -0.7373 -0.5461
+vn 0.3977 -0.7373 -0.5461
+vn 0.4215 -0.3791 -0.8238
+vn 0.5447 -0.3793 -0.7479
+vn 0.6542 -0.3794 -0.6542
+vn 0.4776 -0.7374 -0.4776
+vn 0.5448 -0.3793 -0.7479
+vn 0.2873 -0.9138 -0.2873
+vn 0.1690 -0.9710 -0.1690
+vn 0.2873 -0.9137 -0.2873
+vn 0.1024 -0.9895 -0.1024
+vn 0.1408 -0.9710 -0.1932
+vn 0.0628 -0.9960 -0.0628
+vn 0.0853 -0.9894 -0.1171
+vn 0.0377 -0.9986 -0.0377
+vn 0.0208 -0.9996 -0.0208
+vn 0.0089 -0.9999 -0.0090
+vn 0.0090 -0.9999 -0.0089
+vn 0.0238 -0.9996 -0.0174
+vn 0.0102 -0.9999 -0.0075
+vn 0.0431 -0.9986 -0.0314
+vn 0.0238 -0.9996 -0.0173
+vn 0.0718 -0.9960 -0.0523
+vn 0.1171 -0.9894 -0.0853
+vn 0.1932 -0.9710 -0.1407
+vn 0.1171 -0.9895 -0.0852
+vn 0.3285 -0.9137 -0.2393
+vn 0.1933 -0.9710 -0.1407
+vn 0.5461 -0.7373 -0.3978
+vn 0.4777 -0.7373 -0.4777
+vn 0.7479 -0.3793 -0.5448
+vn 0.5461 -0.7373 -0.3977
+vn 0.7479 -0.3793 -0.5447
+vn 0.8238 -0.3791 -0.4215
+vn 0.8237 -0.3791 -0.4216
+vn 0.6016 -0.7371 -0.3079
+vn 0.3620 -0.9136 -0.1852
+vn 0.1933 -0.9710 -0.1408
+vn 0.2130 -0.9710 -0.1090
+vn 0.1171 -0.9895 -0.0853
+vn 0.1290 -0.9894 -0.0660
+vn 0.0719 -0.9960 -0.0523
+vn 0.0792 -0.9960 -0.0405
+vn 0.0475 -0.9986 -0.0243
+vn 0.0262 -0.9996 -0.0134
+vn 0.0263 -0.9996 -0.0134
+vn 0.0281 -0.9996 -0.0092
+vn 0.0508 -0.9986 -0.0166
+vn 0.0846 -0.9960 -0.0276
+vn 0.1379 -0.9894 -0.0450
+vn 0.0847 -0.9960 -0.0276
+vn 0.2277 -0.9709 -0.0743
+vn 0.3870 -0.9134 -0.1263
+vn 0.6428 -0.7367 -0.2098
+vn 0.3869 -0.9134 -0.1263
+vn 0.6017 -0.7370 -0.3079
+vn 0.8798 -0.3788 -0.2872
+vn 0.6429 -0.7367 -0.2098
+vn 0.8798 -0.3787 -0.2872
+vn 0.9142 -0.3784 -0.1451
+vn 0.9142 -0.3784 -0.1452
+vn 0.6683 -0.7363 -0.1061
+vn 0.4024 -0.9132 -0.0639
+vn 0.2368 -0.9708 -0.0376
+vn 0.1435 -0.9894 -0.0228
+vn 0.0881 -0.9960 -0.0140
+vn 0.0529 -0.9986 -0.0084
+vn 0.0292 -0.9996 -0.0046
+vn 0.2369 -0.9708 -0.0376
+vn 0.4077 -0.9131 -0.0000
+vn 0.9257 -0.3782 -0.0000
+vn -0.0000 1.0000 -0.0000
+vn 0.0194 0.9922 -0.1230
+vn -0.0000 0.9922 -0.1246
+vn 0.0384 0.9922 -0.1182
+vn 0.0194 0.9922 -0.1229
+vn 0.0564 0.9923 -0.1106
+vn 0.0729 0.9923 -0.1003
+vn 0.0876 0.9923 -0.0876
+vn 0.1003 0.9923 -0.0729
+vn 0.1106 0.9923 -0.0564
+vn 0.1182 0.9922 -0.0384
+vn 0.1229 0.9922 -0.0194
+vn 0.1246 0.9922 -0.0000
+vn 0.1230 0.9922 -0.0194
+vn 0.1230 0.9922 0.0194
+vn 0.1182 0.9922 0.0384
+vn 0.1229 0.9922 0.0194
+vn 0.1106 0.9923 0.0564
+vn 0.1003 0.9923 0.0729
+vn 0.0876 0.9923 0.0876
+vn 0.0729 0.9923 0.1003
+vn 0.0564 0.9923 0.1106
+vn 0.0384 0.9922 0.1182
+vn 0.0194 0.9922 0.1229
+vn -0.0000 0.9922 0.1246
+vn 0.0194 0.9922 0.1230
+vn -0.0194 0.9922 0.1230
+vn -0.0384 0.9922 0.1182
+vn -0.0194 0.9922 0.1229
+vn -0.0564 0.9923 0.1106
+vn -0.0729 0.9923 0.1003
+vn -0.0876 0.9923 0.0876
+vn -0.1003 0.9923 0.0729
+vn -0.1106 0.9923 0.0564
+vn -0.1182 0.9922 0.0384
+vn -0.1229 0.9922 0.0194
+vn -0.1246 0.9922 -0.0000
+vn -0.1230 0.9922 0.0194
+vn -0.1230 0.9922 -0.0194
+vn -0.1182 0.9922 -0.0384
+vn -0.1229 0.9922 -0.0194
+vn -0.1106 0.9923 -0.0564
+vn -0.1003 0.9923 -0.0729
+vn -0.0876 0.9923 -0.0876
+vn -0.0729 0.9923 -0.1003
+vn -0.0564 0.9923 -0.1106
+vn -0.0384 0.9922 -0.1182
+vn -0.0194 0.9922 -0.1229
+vn -0.0194 0.9922 -0.1230
+vn -0.0000 0.9128 -0.4084
+vn -0.0635 0.9129 -0.4032
+vn 0.0635 0.9129 -0.4032
+vn 0.1259 0.9131 -0.3877
+vn 0.1850 0.9133 -0.3628
+vn 0.2392 0.9135 -0.3291
+vn 0.0729 0.9923 -0.1002
+vn 0.3291 0.9135 -0.2392
+vn 0.2876 0.9135 -0.2876
+vn 0.3628 0.9133 -0.1849
+vn 0.3877 0.9131 -0.1259
+vn 0.4032 0.9129 -0.0636
+vn 0.4084 0.9128 -0.0000
+vn 0.4032 0.9129 -0.0635
+vn 0.4032 0.9129 0.0635
+vn 0.1106 0.9923 0.0563
+vn 0.3877 0.9131 0.1259
+vn 0.3628 0.9133 0.1850
+vn 0.3291 0.9135 0.2392
+vn 0.2392 0.9135 0.3291
+vn 0.2876 0.9135 0.2876
+vn 0.1850 0.9133 0.3628
+vn 0.1259 0.9131 0.3877
+vn 0.1849 0.9133 0.3628
+vn 0.0635 0.9129 0.4032
+vn -0.0000 0.9128 0.4084
+vn -0.0635 0.9129 0.4032
+vn -0.1259 0.9131 0.3877
+vn -0.1850 0.9133 0.3628
+vn -0.2392 0.9135 0.3291
+vn -0.0729 0.9923 0.1002
+vn -0.3291 0.9135 0.2392
+vn -0.2876 0.9135 0.2876
+vn -0.3628 0.9133 0.1849
+vn -0.3877 0.9131 0.1259
+vn -0.4032 0.9129 0.0636
+vn -0.4084 0.9128 -0.0000
+vn -0.4032 0.9129 0.0635
+vn -0.4032 0.9129 -0.0635
+vn -0.1106 0.9923 -0.0563
+vn -0.3877 0.9131 -0.1259
+vn -0.3628 0.9133 -0.1850
+vn -0.3291 0.9135 -0.2392
+vn -0.2392 0.9135 -0.3291
+vn -0.2876 0.9135 -0.2876
+vn -0.1850 0.9133 -0.3628
+vn -0.1259 0.9131 -0.3877
+vn -0.1849 0.9133 -0.3628
+vn -0.1259 0.9131 -0.3878
+vn -0.1388 0.4532 -0.8805
+vn -0.2752 0.4536 -0.8477
+vn -0.0000 0.4531 -0.8915
+vn -0.1388 0.4533 -0.8805
+vn 0.1388 0.4532 -0.8805
+vn 0.1849 0.9134 -0.3627
+vn 0.2752 0.4536 -0.8477
+vn 0.2393 0.9135 -0.3291
+vn 0.4047 0.4539 -0.7938
+vn 0.1849 0.9133 -0.3628
+vn 0.2876 0.9136 -0.2876
+vn 0.5239 0.4540 -0.7207
+vn 0.7207 0.4540 -0.5239
+vn 0.6300 0.4541 -0.6300
+vn 0.7938 0.4539 -0.4048
+vn 0.7206 0.4541 -0.5239
+vn 0.3628 0.9134 -0.1849
+vn 0.8477 0.4535 -0.2752
+vn 0.7939 0.4538 -0.4048
+vn 0.3878 0.9131 -0.1259
+vn 0.8805 0.4532 -0.1388
+vn 0.8477 0.4536 -0.2752
+vn 0.8915 0.4531 -0.0000
+vn 0.8805 0.4532 0.1388
+vn 0.3627 0.9134 0.1849
+vn 0.8477 0.4536 0.2752
+vn 0.3877 0.9131 0.1258
+vn 0.3291 0.9135 0.2393
+vn 0.7938 0.4539 0.4047
+vn 0.3628 0.9133 0.1849
+vn 0.2876 0.9136 0.2876
+vn 0.7207 0.4540 0.5239
+vn 0.5239 0.4540 0.7207
+vn 0.6300 0.4541 0.6300
+vn 0.4048 0.4539 0.7938
+vn 0.5239 0.4541 0.7206
+vn 0.1849 0.9134 0.3628
+vn 0.2752 0.4535 0.8477
+vn 0.4048 0.4538 0.7939
+vn 0.1259 0.9131 0.3878
+vn 0.1388 0.4532 0.8805
+vn 0.2752 0.4536 0.8477
+vn -0.0000 0.4531 0.8915
+vn 0.1388 0.4533 0.8805
+vn -0.1388 0.4532 0.8805
+vn -0.1849 0.9134 0.3627
+vn -0.2752 0.4536 0.8477
+vn -0.2393 0.9135 0.3291
+vn -0.4047 0.4539 0.7938
+vn -0.1849 0.9133 0.3628
+vn -0.2876 0.9136 0.2876
+vn -0.5239 0.4540 0.7207
+vn -0.7207 0.4540 0.5239
+vn -0.6300 0.4541 0.6300
+vn -0.7938 0.4539 0.4048
+vn -0.7206 0.4541 0.5239
+vn -0.3628 0.9134 0.1849
+vn -0.8477 0.4535 0.2752
+vn -0.7939 0.4538 0.4048
+vn -0.3878 0.9131 0.1259
+vn -0.8805 0.4532 0.1388
+vn -0.8477 0.4536 0.2752
+vn -0.8915 0.4531 -0.0000
+vn -0.8805 0.4532 -0.1388
+vn -0.3627 0.9134 -0.1849
+vn -0.8477 0.4536 -0.2752
+vn -0.3877 0.9131 -0.1258
+vn -0.3291 0.9135 -0.2393
+vn -0.7938 0.4539 -0.4047
+vn -0.3628 0.9133 -0.1849
+vn -0.2876 0.9136 -0.2876
+vn -0.7207 0.4540 -0.5239
+vn -0.5239 0.4540 -0.7207
+vn -0.6300 0.4541 -0.6300
+vn -0.4048 0.4539 -0.7938
+vn -0.5239 0.4541 -0.7206
+vn -0.1849 0.9134 -0.3628
+vn -0.2752 0.4535 -0.8477
+vn -0.4048 0.4538 -0.7939
+vn -0.4368 -0.2743 -0.8567
+vn -0.4047 0.4538 -0.7939
+vn -0.2970 -0.2739 -0.9148
+vn -0.1498 -0.2735 -0.9501
+vn 0.1498 -0.2735 -0.9501
+vn -0.0000 -0.2733 -0.9619
+vn 0.2970 -0.2739 -0.9148
+vn 0.4368 -0.2742 -0.8567
+vn 0.2970 -0.2739 -0.9147
+vn 0.4048 0.4539 -0.7938
+vn 0.5655 -0.2745 -0.7777
+vn 0.4368 -0.2743 -0.8567
+vn 0.5239 0.4541 -0.7207
+vn 0.6799 -0.2746 -0.6799
+vn 0.7939 0.4538 -0.4047
+vn 0.7777 -0.2746 -0.5655
+vn 0.8567 -0.2743 -0.4368
+vn 0.9148 -0.2739 -0.2970
+vn 0.9501 -0.2735 -0.1498
+vn 0.9501 -0.2735 0.1498
+vn 0.9619 -0.2733 -0.0000
+vn 0.9148 -0.2739 0.2970
+vn 0.8567 -0.2742 0.4368
+vn 0.9148 -0.2738 0.2970
+vn 0.7938 0.4539 0.4048
+vn 0.7777 -0.2745 0.5655
+vn 0.8567 -0.2743 0.4368
+vn 0.7207 0.4541 0.5239
+vn 0.6799 -0.2746 0.6799
+vn 0.4047 0.4538 0.7939
+vn 0.5655 -0.2746 0.7777
+vn 0.4368 -0.2743 0.8567
+vn 0.2970 -0.2739 0.9148
+vn 0.1498 -0.2735 0.9501
+vn -0.1498 -0.2735 0.9501
+vn -0.0000 -0.2733 0.9619
+vn -0.2970 -0.2738 0.9148
+vn -0.4368 -0.2742 0.8567
+vn -0.2970 -0.2739 0.9147
+vn -0.4048 0.4539 0.7938
+vn -0.5655 -0.2745 0.7777
+vn -0.4368 -0.2743 0.8567
+vn -0.5239 0.4541 0.7207
+vn -0.6799 -0.2746 0.6799
+vn -0.7939 0.4538 0.4047
+vn -0.7777 -0.2746 0.5655
+vn -0.8567 -0.2743 0.4368
+vn -0.9148 -0.2739 0.2970
+vn -0.9501 -0.2735 0.1498
+vn -0.9501 -0.2735 -0.1498
+vn -0.9619 -0.2733 -0.0000
+vn -0.9148 -0.2739 -0.2970
+vn -0.8567 -0.2742 -0.4368
+vn -0.9148 -0.2738 -0.2970
+vn -0.7938 0.4539 -0.4048
+vn -0.7777 -0.2745 -0.5655
+vn -0.8567 -0.2743 -0.4368
+vn -0.7207 0.4541 -0.5239
+vn -0.6799 -0.2746 -0.6799
+vn -0.5655 -0.2746 -0.7777
+vn -0.4704 -0.6002 -0.6469
+vn -0.5655 -0.2745 -0.7777
+vn -0.2970 -0.2739 -0.9147
+vn -0.3635 -0.5998 -0.7128
+vn -0.4368 -0.2742 -0.8567
+vn -0.2473 -0.5991 -0.7615
+vn -0.1248 -0.5985 -0.7914
+vn 0.1248 -0.5985 -0.7914
+vn -0.0000 -0.5982 -0.8014
+vn 0.2473 -0.5991 -0.7615
+vn 0.1248 -0.5985 -0.7913
+vn 0.2970 -0.2738 -0.9148
+vn 0.3635 -0.5998 -0.7128
+vn 0.2473 -0.5992 -0.7615
+vn 0.4369 -0.2743 -0.8567
+vn 0.4704 -0.6002 -0.6469
+vn 0.5655 -0.6004 -0.5655
+vn 0.7777 -0.2745 -0.5655
+vn 0.6799 -0.2747 -0.6799
+vn 0.6469 -0.6002 -0.4704
+vn 0.9147 -0.2739 -0.2970
+vn 0.7128 -0.5998 -0.3635
+vn 0.8567 -0.2742 -0.4368
+vn 0.7615 -0.5991 -0.2473
+vn 0.7914 -0.5985 -0.1248
+vn 0.7914 -0.5985 0.1248
+vn 0.8014 -0.5982 -0.0000
+vn 0.7615 -0.5991 0.2473
+vn 0.7913 -0.5985 0.1248
+vn 0.7128 -0.5998 0.3635
+vn 0.7615 -0.5992 0.2473
+vn 0.8567 -0.2743 0.4369
+vn 0.6469 -0.6002 0.4704
+vn 0.5655 -0.6004 0.5655
+vn 0.5655 -0.2745 0.7777
+vn 0.6799 -0.2747 0.6799
+vn 0.4704 -0.6002 0.6469
+vn 0.2970 -0.2739 0.9147
+vn 0.3635 -0.5998 0.7128
+vn 0.4368 -0.2742 0.8567
+vn 0.2473 -0.5991 0.7615
+vn 0.1248 -0.5985 0.7914
+vn -0.1248 -0.5985 0.7914
+vn -0.0000 -0.5982 0.8014
+vn -0.1498 -0.2734 0.9502
+vn -0.2473 -0.5991 0.7615
+vn -0.1248 -0.5985 0.7913
+vn -0.3635 -0.5998 0.7128
+vn -0.2473 -0.5992 0.7615
+vn -0.4369 -0.2743 0.8567
+vn -0.4704 -0.6002 0.6469
+vn -0.5655 -0.6004 0.5655
+vn -0.7777 -0.2745 0.5655
+vn -0.6799 -0.2747 0.6799
+vn -0.6469 -0.6002 0.4704
+vn -0.9147 -0.2739 0.2970
+vn -0.7128 -0.5998 0.3635
+vn -0.8567 -0.2742 0.4368
+vn -0.7615 -0.5991 0.2473
+vn -0.7914 -0.5985 0.1248
+vn -0.7914 -0.5985 -0.1248
+vn -0.8014 -0.5982 -0.0000
+vn -0.7615 -0.5991 -0.2473
+vn -0.7913 -0.5985 -0.1248
+vn -0.7128 -0.5998 -0.3635
+vn -0.7615 -0.5992 -0.2473
+vn -0.8567 -0.2743 -0.4369
+vn -0.6469 -0.6002 -0.4704
+vn -0.5655 -0.6004 -0.5655
+vn -0.6799 -0.2747 -0.6799
+vn -0.5262 -0.6679 -0.5263
+vn -0.4378 -0.6678 -0.6020
+vn -0.3384 -0.6673 -0.6634
+vn -0.2303 -0.6667 -0.7089
+vn -0.2473 -0.5992 -0.7615
+vn -0.1162 -0.6661 -0.7368
+vn -0.1248 -0.5985 -0.7913
+vn 0.1163 -0.6661 -0.7368
+vn -0.0000 -0.6657 -0.7462
+vn 0.2303 -0.6667 -0.7089
+vn 0.1163 -0.6660 -0.7368
+vn 0.3384 -0.6673 -0.6634
+vn 0.2303 -0.6667 -0.7088
+vn 0.4378 -0.6678 -0.6020
+vn 0.5263 -0.6679 -0.5262
+vn 0.6020 -0.6678 -0.4378
+vn 0.6634 -0.6673 -0.3384
+vn 0.7089 -0.6667 -0.2303
+vn 0.7615 -0.5992 -0.2473
+vn 0.7368 -0.6661 -0.1162
+vn 0.7913 -0.5985 -0.1248
+vn 0.7368 -0.6661 0.1163
+vn 0.7462 -0.6657 -0.0000
+vn 0.7089 -0.6667 0.2303
+vn 0.7368 -0.6660 0.1163
+vn 0.6634 -0.6673 0.3385
+vn 0.7088 -0.6667 0.2304
+vn 0.6020 -0.6678 0.4378
+vn 0.6634 -0.6673 0.3384
+vn 0.5262 -0.6679 0.5263
+vn 0.4378 -0.6678 0.6020
+vn 0.3384 -0.6673 0.6634
+vn 0.2303 -0.6667 0.7089
+vn 0.2473 -0.5992 0.7615
+vn 0.1162 -0.6661 0.7368
+vn 0.1248 -0.5985 0.7913
+vn -0.1163 -0.6661 0.7368
+vn -0.0000 -0.6657 0.7462
+vn -0.2303 -0.6667 0.7089
+vn -0.1163 -0.6660 0.7368
+vn -0.3384 -0.6673 0.6634
+vn -0.2303 -0.6667 0.7088
+vn -0.4378 -0.6678 0.6020
+vn -0.5263 -0.6679 0.5262
+vn -0.6020 -0.6678 0.4378
+vn -0.6634 -0.6673 0.3384
+vn -0.7089 -0.6667 0.2303
+vn -0.7615 -0.5992 0.2473
+vn -0.7368 -0.6661 0.1162
+vn -0.7913 -0.5985 0.1248
+vn -0.7368 -0.6661 -0.1163
+vn -0.7462 -0.6657 -0.0000
+vn -0.7089 -0.6667 -0.2303
+vn -0.7368 -0.6660 -0.1163
+vn -0.6634 -0.6673 -0.3385
+vn -0.7088 -0.6667 -0.2304
+vn -0.6020 -0.6678 -0.4378
+vn -0.6634 -0.6673 -0.3384
+vn -0.5539 -0.6215 -0.5539
+vn -0.6336 -0.6214 -0.4609
+vn -0.5540 -0.6215 -0.5539
+vn -0.5263 -0.6679 -0.5262
+vn -0.4609 -0.6214 -0.6336
+vn -0.3564 -0.6209 -0.6982
+vn -0.1163 -0.6660 -0.7368
+vn -0.2426 -0.6202 -0.7460
+vn -0.1225 -0.6195 -0.7754
+vn -0.1163 -0.6661 -0.7368
+vn 0.1225 -0.6195 -0.7754
+vn -0.0000 -0.6192 -0.7852
+vn 0.1162 -0.6660 -0.7368
+vn 0.2426 -0.6202 -0.7460
+vn 0.3564 -0.6209 -0.6982
+vn 0.3385 -0.6673 -0.6634
+vn 0.4609 -0.6214 -0.6336
+vn 0.5539 -0.6215 -0.5539
+vn 0.5539 -0.6215 -0.5540
+vn 0.5262 -0.6679 -0.5263
+vn 0.6336 -0.6214 -0.4609
+vn 0.6982 -0.6209 -0.3564
+vn 0.7368 -0.6660 -0.1163
+vn 0.7460 -0.6202 -0.2426
+vn 0.7754 -0.6195 -0.1225
+vn 0.7368 -0.6661 -0.1163
+vn 0.7754 -0.6195 0.1225
+vn 0.7852 -0.6192 -0.0000
+vn 0.7368 -0.6660 0.1162
+vn 0.7460 -0.6202 0.2426
+vn 0.6982 -0.6209 0.3564
+vn 0.6336 -0.6214 0.4609
+vn 0.5539 -0.6215 0.5539
+vn 0.5540 -0.6215 0.5539
+vn 0.5263 -0.6679 0.5262
+vn 0.4609 -0.6214 0.6336
+vn 0.3564 -0.6209 0.6982
+vn 0.1163 -0.6660 0.7368
+vn 0.2426 -0.6202 0.7460
+vn 0.1225 -0.6195 0.7754
+vn 0.1163 -0.6661 0.7368
+vn -0.1225 -0.6195 0.7754
+vn -0.0000 -0.6192 0.7852
+vn -0.1162 -0.6660 0.7368
+vn -0.2426 -0.6202 0.7460
+vn -0.3564 -0.6209 0.6982
+vn -0.3385 -0.6673 0.6634
+vn -0.4609 -0.6214 0.6336
+vn -0.5539 -0.6215 0.5539
+vn -0.5539 -0.6215 0.5540
+vn -0.5262 -0.6679 0.5263
+vn -0.6336 -0.6214 0.4609
+vn -0.6982 -0.6209 0.3564
+vn -0.7368 -0.6660 0.1163
+vn -0.7460 -0.6202 0.2426
+vn -0.7754 -0.6195 0.1225
+vn -0.7368 -0.6661 0.1163
+vn -0.7754 -0.6195 -0.1225
+vn -0.7852 -0.6192 -0.0000
+vn -0.7368 -0.6660 -0.1162
+vn -0.7460 -0.6202 -0.2426
+vn -0.6982 -0.6209 -0.3564
+vn -0.7418 -0.3977 -0.5399
+vn -0.8172 -0.3972 -0.4176
+vn -0.6487 -0.3979 -0.6487
+vn -0.6488 -0.3979 -0.6487
+vn -0.5539 -0.6215 -0.5540
+vn -0.5399 -0.3977 -0.7418
+vn -0.4609 -0.6213 -0.6336
+vn -0.4176 -0.3972 -0.8172
+vn -0.2842 -0.3966 -0.8729
+vn -0.1436 -0.3959 -0.9070
+vn 0.1435 -0.3959 -0.9070
+vn -0.0000 -0.3956 -0.9184
+vn 0.2842 -0.3966 -0.8729
+vn 0.1436 -0.3959 -0.9070
+vn 0.4176 -0.3972 -0.8172
+vn 0.2843 -0.3966 -0.8729
+vn 0.5399 -0.3977 -0.7418
+vn 0.6487 -0.3979 -0.6487
+vn 0.6487 -0.3979 -0.6488
+vn 0.5540 -0.6215 -0.5539
+vn 0.7418 -0.3977 -0.5399
+vn 0.6336 -0.6213 -0.4609
+vn 0.8172 -0.3972 -0.4176
+vn 0.8729 -0.3966 -0.2843
+vn 0.9070 -0.3959 -0.1436
+vn 0.9070 -0.3959 0.1435
+vn 0.9184 -0.3956 -0.0000
+vn 0.8729 -0.3966 0.2842
+vn 0.9070 -0.3959 0.1436
+vn 0.8172 -0.3972 0.4176
+vn 0.8729 -0.3966 0.2843
+vn 0.7418 -0.3977 0.5399
+vn 0.6487 -0.3979 0.6487
+vn 0.6488 -0.3979 0.6487
+vn 0.5539 -0.6215 0.5540
+vn 0.5399 -0.3977 0.7418
+vn 0.4609 -0.6213 0.6336
+vn 0.4176 -0.3972 0.8172
+vn 0.2842 -0.3966 0.8729
+vn 0.1436 -0.3959 0.9070
+vn -0.1435 -0.3959 0.9070
+vn -0.0000 -0.3956 0.9184
+vn -0.2842 -0.3966 0.8729
+vn -0.1436 -0.3959 0.9070
+vn -0.4176 -0.3972 0.8172
+vn -0.2843 -0.3966 0.8729
+vn -0.5399 -0.3977 0.7418
+vn -0.6487 -0.3979 0.6487
+vn -0.6487 -0.3979 0.6488
+vn -0.5540 -0.6215 0.5539
+vn -0.7418 -0.3977 0.5399
+vn -0.6336 -0.6213 0.4609
+vn -0.8172 -0.3972 0.4176
+vn -0.8729 -0.3966 0.2843
+vn -0.9070 -0.3959 0.1436
+vn -0.9070 -0.3959 -0.1435
+vn -0.9184 -0.3956 -0.0000
+vn -0.8729 -0.3966 -0.2842
+vn -0.9070 -0.3959 -0.1436
+vn -0.8729 -0.3966 -0.2843
+vn -0.8746 0.1871 -0.4473
+vn -0.9339 0.1873 -0.3045
+vn -0.8172 -0.3973 -0.4175
+vn -0.7942 0.1871 -0.5782
+vn -0.8746 0.1872 -0.4473
+vn -0.7419 -0.3977 -0.5399
+vn -0.6946 0.1870 -0.6946
+vn -0.5399 -0.3977 -0.7419
+vn -0.6946 0.1871 -0.6946
+vn -0.4175 -0.3972 -0.8172
+vn -0.5782 0.1871 -0.7941
+vn -0.2843 -0.3966 -0.8729
+vn -0.4473 0.1872 -0.8746
+vn -0.4175 -0.3973 -0.8172
+vn -0.3045 0.1872 -0.9339
+vn -0.1538 0.1873 -0.9702
+vn 0.1538 0.1873 -0.9702
+vn -0.0000 0.1873 -0.9823
+vn 0.3046 0.1872 -0.9339
+vn 0.4473 0.1871 -0.8746
+vn 0.3045 0.1873 -0.9339
+vn 0.4175 -0.3973 -0.8172
+vn 0.5782 0.1871 -0.7942
+vn 0.4473 0.1872 -0.8746
+vn 0.5399 -0.3977 -0.7419
+vn 0.6946 0.1870 -0.6946
+vn 0.7419 -0.3977 -0.5399
+vn 0.6946 0.1871 -0.6946
+vn 0.8172 -0.3972 -0.4175
+vn 0.7941 0.1871 -0.5782
+vn 0.8746 0.1872 -0.4473
+vn 0.8172 -0.3973 -0.4175
+vn 0.9339 0.1872 -0.3045
+vn 0.9702 0.1873 -0.1538
+vn 0.9702 0.1873 0.1538
+vn 0.9823 0.1873 -0.0000
+vn 0.9339 0.1872 0.3046
+vn 0.8746 0.1871 0.4473
+vn 0.9339 0.1873 0.3045
+vn 0.8172 -0.3973 0.4175
+vn 0.7942 0.1871 0.5782
+vn 0.8746 0.1872 0.4473
+vn 0.7419 -0.3977 0.5399
+vn 0.6946 0.1870 0.6946
+vn 0.5399 -0.3977 0.7419
+vn 0.6946 0.1871 0.6946
+vn 0.4175 -0.3972 0.8172
+vn 0.5782 0.1871 0.7941
+vn 0.2843 -0.3966 0.8729
+vn 0.4473 0.1872 0.8746
+vn 0.4175 -0.3973 0.8172
+vn 0.3045 0.1872 0.9339
+vn 0.1538 0.1873 0.9702
+vn -0.1538 0.1873 0.9702
+vn -0.0000 0.1873 0.9823
+vn -0.3046 0.1872 0.9339
+vn -0.4473 0.1871 0.8746
+vn -0.3045 0.1873 0.9339
+vn -0.4175 -0.3973 0.8172
+vn -0.5782 0.1871 0.7942
+vn -0.4473 0.1872 0.8746
+vn -0.5399 -0.3977 0.7419
+vn -0.6946 0.1870 0.6946
+vn -0.7419 -0.3977 0.5399
+vn -0.6946 0.1871 0.6946
+vn -0.8172 -0.3972 0.4175
+vn -0.7941 0.1871 0.5782
+vn -0.8746 0.1872 0.4473
+vn -0.8172 -0.3973 0.4175
+vn -0.9339 0.1872 0.3045
+vn -0.9702 0.1873 0.1538
+vn -0.9702 0.1873 -0.1538
+vn -0.9823 0.1873 -0.0000
+vn -0.9339 0.1872 -0.3046
+vn -0.9339 0.1872 -0.3045
+vn -0.6578 0.7459 -0.1044
+vn -0.6329 0.7462 -0.2065
+vn -0.7941 0.1871 -0.5782
+vn -0.5924 0.7465 -0.3031
+vn -0.8746 0.1872 -0.4472
+vn -0.6947 0.1870 -0.6946
+vn -0.5377 0.7467 -0.3916
+vn -0.6946 0.1870 -0.6947
+vn -0.3917 0.7466 -0.5377
+vn -0.4703 0.7467 -0.4703
+vn -0.3031 0.7465 -0.5924
+vn -0.3916 0.7467 -0.5377
+vn -0.4472 0.1871 -0.8746
+vn -0.2066 0.7462 -0.6329
+vn -0.3046 0.1873 -0.9339
+vn -0.1044 0.7458 -0.6579
+vn -0.0000 0.7457 -0.6663
+vn -0.1044 0.7459 -0.6579
+vn 0.1044 0.7459 -0.6578
+vn 0.2065 0.7462 -0.6329
+vn 0.5782 0.1871 -0.7941
+vn 0.3031 0.7465 -0.5924
+vn 0.4472 0.1872 -0.8746
+vn 0.6946 0.1870 -0.6947
+vn 0.3916 0.7467 -0.5377
+vn 0.6947 0.1870 -0.6946
+vn 0.5377 0.7466 -0.3916
+vn 0.4703 0.7467 -0.4703
+vn 0.5924 0.7465 -0.3031
+vn 0.5377 0.7467 -0.3916
+vn 0.8746 0.1872 -0.4472
+vn 0.6329 0.7462 -0.2066
+vn 0.6579 0.7458 -0.1044
+vn 0.6663 0.7456 -0.0000
+vn 0.6579 0.7459 -0.1044
+vn 0.6663 0.7457 -0.0000
+vn 0.9339 0.1872 0.3045
+vn 0.6578 0.7459 0.1044
+vn 0.6329 0.7462 0.2065
+vn 0.7941 0.1871 0.5782
+vn 0.5924 0.7465 0.3031
+vn 0.8746 0.1872 0.4472
+vn 0.6946 0.1870 0.6947
+vn 0.5377 0.7467 0.3916
+vn 0.3917 0.7466 0.5377
+vn 0.4703 0.7467 0.4703
+vn 0.3031 0.7465 0.5924
+vn 0.3916 0.7467 0.5377
+vn 0.4472 0.1871 0.8746
+vn 0.2066 0.7462 0.6329
+vn 0.3046 0.1873 0.9339
+vn 0.1044 0.7458 0.6579
+vn -0.0000 0.7457 0.6663
+vn 0.1044 0.7459 0.6579
+vn -0.1044 0.7459 0.6578
+vn -0.2065 0.7462 0.6329
+vn -0.5782 0.1871 0.7941
+vn -0.3031 0.7465 0.5924
+vn -0.4472 0.1872 0.8746
+vn -0.6946 0.1870 0.6947
+vn -0.3916 0.7467 0.5377
+vn -0.6947 0.1870 0.6946
+vn -0.5377 0.7466 0.3916
+vn -0.4703 0.7467 0.4703
+vn -0.5924 0.7465 0.3031
+vn -0.5377 0.7467 0.3916
+vn -0.8746 0.1872 0.4472
+vn -0.6329 0.7462 0.2066
+vn -0.6579 0.7458 0.1044
+vn -0.6663 0.7456 -0.0000
+vn -0.6579 0.7459 0.1044
+vn -0.6663 0.7457 -0.0000
+vn -0.6579 0.7458 -0.1044
+vn -0.3740 0.9274 -0.0000
+vn -0.6329 0.7462 -0.2066
+vn -0.3691 0.9275 -0.0586
+vn -0.3550 0.9277 -0.1159
+vn -0.3321 0.9278 -0.1699
+vn -0.3013 0.9279 -0.2195
+vn -0.2195 0.9279 -0.3013
+vn -0.2635 0.9280 -0.2635
+vn -0.1699 0.9278 -0.3321
+vn -0.1159 0.9277 -0.3550
+vn -0.0586 0.9275 -0.3691
+vn -0.0000 0.9274 -0.3740
+vn 0.1044 0.7458 -0.6579
+vn 0.2066 0.7462 -0.6329
+vn 0.0586 0.9275 -0.3691
+vn 0.1159 0.9277 -0.3550
+vn 0.1699 0.9278 -0.3321
+vn 0.2195 0.9279 -0.3013
+vn 0.3013 0.9279 -0.2195
+vn 0.2635 0.9280 -0.2635
+vn 0.3321 0.9278 -0.1699
+vn 0.3550 0.9277 -0.1159
+vn 0.3691 0.9275 -0.0586
+vn 0.3740 0.9274 -0.0000
+vn 0.6579 0.7458 0.1044
+vn 0.6329 0.7462 0.2066
+vn 0.3691 0.9275 0.0586
+vn 0.3550 0.9277 0.1159
+vn 0.3321 0.9278 0.1699
+vn 0.3013 0.9279 0.2195
+vn 0.2195 0.9279 0.3013
+vn 0.2635 0.9280 0.2635
+vn 0.1699 0.9278 0.3321
+vn 0.1159 0.9277 0.3550
+vn 0.0586 0.9275 0.3691
+vn -0.0000 0.9274 0.3740
+vn -0.1044 0.7458 0.6579
+vn -0.2066 0.7462 0.6329
+vn -0.0586 0.9275 0.3691
+vn -0.1159 0.9277 0.3550
+vn -0.1699 0.9278 0.3321
+vn -0.2195 0.9279 0.3013
+vn -0.3013 0.9279 0.2195
+vn -0.2635 0.9280 0.2635
+vn -0.3321 0.9278 0.1699
+vn -0.3550 0.9277 0.1159
+vn -0.3691 0.9275 0.0586
+vn -0.3692 0.9275 0.0586
+vn -0.2467 0.9691 -0.0000
+vn -0.2435 0.9691 0.0386
+vn -0.3692 0.9275 -0.0586
+vn -0.3549 0.9277 -0.1159
+vn -0.2435 0.9691 -0.0386
+vn -0.2341 0.9692 -0.0764
+vn -0.3550 0.9277 -0.1158
+vn -0.3013 0.9279 -0.2194
+vn -0.2190 0.9693 -0.1120
+vn -0.3320 0.9279 -0.1699
+vn -0.1987 0.9693 -0.1447
+vn -0.1447 0.9693 -0.1987
+vn -0.1738 0.9693 -0.1737
+vn -0.1120 0.9693 -0.2190
+vn -0.0764 0.9692 -0.2341
+vn -0.0386 0.9691 -0.2435
+vn -0.0586 0.9275 -0.3692
+vn -0.0000 0.9691 -0.2467
+vn 0.0586 0.9275 -0.3692
+vn 0.1159 0.9277 -0.3549
+vn 0.0386 0.9691 -0.2435
+vn 0.0764 0.9692 -0.2341
+vn 0.1120 0.9693 -0.2190
+vn 0.1699 0.9279 -0.3320
+vn 0.1447 0.9693 -0.1987
+vn 0.1987 0.9693 -0.1447
+vn 0.1737 0.9693 -0.1738
+vn 0.2190 0.9693 -0.1120
+vn 0.2341 0.9692 -0.0764
+vn 0.3549 0.9277 -0.1159
+vn 0.2435 0.9691 -0.0386
+vn 0.3692 0.9275 -0.0586
+vn 0.2467 0.9691 -0.0000
+vn 0.3692 0.9275 0.0586
+vn 0.3549 0.9277 0.1159
+vn 0.2435 0.9691 0.0386
+vn 0.2341 0.9692 0.0764
+vn 0.3550 0.9277 0.1158
+vn 0.3013 0.9279 0.2194
+vn 0.2190 0.9693 0.1120
+vn 0.3320 0.9279 0.1699
+vn 0.1987 0.9693 0.1447
+vn 0.1447 0.9693 0.1987
+vn 0.1738 0.9693 0.1737
+vn 0.1120 0.9693 0.2190
+vn 0.0764 0.9692 0.2341
+vn 0.0386 0.9691 0.2435
+vn 0.0586 0.9275 0.3692
+vn -0.0000 0.9691 0.2467
+vn -0.0586 0.9275 0.3692
+vn -0.1159 0.9277 0.3549
+vn -0.0386 0.9691 0.2435
+vn -0.0764 0.9692 0.2341
+vn -0.1120 0.9693 0.2190
+vn -0.1699 0.9279 0.3320
+vn -0.1447 0.9693 0.1987
+vn -0.1987 0.9693 0.1447
+vn -0.1737 0.9693 0.1738
+vn -0.2190 0.9693 0.1120
+vn -0.2341 0.9692 0.0764
+vn -0.3549 0.9277 0.1159
+vn -0.1823 0.9828 0.0289
+vn -0.1753 0.9829 0.0572
+vn -0.2434 0.9692 0.0386
+vn -0.1846 0.9828 -0.0000
+vn -0.1823 0.9828 -0.0289
+vn -0.2190 0.9693 -0.1121
+vn -0.1752 0.9829 -0.0572
+vn -0.1639 0.9829 -0.0839
+vn -0.1737 0.9693 -0.1738
+vn -0.1487 0.9829 -0.1083
+vn -0.1738 0.9693 -0.1738
+vn -0.1083 0.9829 -0.1487
+vn -0.1301 0.9829 -0.1301
+vn -0.0839 0.9829 -0.1639
+vn -0.1121 0.9693 -0.2190
+vn -0.0572 0.9829 -0.1752
+vn -0.0289 0.9828 -0.1823
+vn -0.0386 0.9692 -0.2434
+vn -0.0000 0.9828 -0.1846
+vn 0.0386 0.9692 -0.2434
+vn 0.0289 0.9828 -0.1823
+vn 0.1121 0.9693 -0.2190
+vn 0.0572 0.9829 -0.1752
+vn 0.0839 0.9829 -0.1639
+vn 0.1738 0.9693 -0.1738
+vn 0.1083 0.9829 -0.1487
+vn 0.1487 0.9829 -0.1083
+vn 0.1301 0.9829 -0.1301
+vn 0.1639 0.9829 -0.0839
+vn 0.2190 0.9693 -0.1121
+vn 0.1752 0.9829 -0.0572
+vn 0.1823 0.9828 -0.0289
+vn 0.1753 0.9829 -0.0572
+vn 0.2434 0.9692 -0.0386
+vn 0.1846 0.9828 -0.0000
+vn 0.1823 0.9828 0.0289
+vn 0.2190 0.9693 0.1121
+vn 0.1752 0.9829 0.0572
+vn 0.1639 0.9829 0.0839
+vn 0.1737 0.9693 0.1738
+vn 0.1487 0.9829 0.1083
+vn 0.1738 0.9693 0.1738
+vn 0.1083 0.9829 0.1487
+vn 0.1301 0.9829 0.1301
+vn 0.0839 0.9829 0.1639
+vn 0.1121 0.9693 0.2190
+vn 0.0572 0.9829 0.1752
+vn 0.0289 0.9828 0.1823
+vn 0.0386 0.9692 0.2434
+vn -0.0000 0.9828 0.1846
+vn -0.0386 0.9692 0.2434
+vn -0.0289 0.9828 0.1823
+vn -0.1121 0.9693 0.2190
+vn -0.0572 0.9829 0.1752
+vn -0.0839 0.9829 0.1639
+vn -0.1738 0.9693 0.1738
+vn -0.1083 0.9829 0.1487
+vn -0.1487 0.9829 0.1083
+vn -0.1301 0.9829 0.1301
+vn -0.1639 0.9829 0.0839
+vn -0.2190 0.9693 0.1121
+vn -0.1752 0.9829 0.0572
+vn -0.1486 0.9877 0.0485
+vn -0.1390 0.9877 0.0711
+vn -0.1545 0.9877 0.0245
+vn -0.1566 0.9877 -0.0000
+vn -0.1545 0.9877 -0.0245
+vn -0.1486 0.9877 -0.0485
+vn -0.1390 0.9877 -0.0711
+vn -0.1301 0.9829 -0.1300
+vn -0.1261 0.9878 -0.0918
+vn -0.1300 0.9829 -0.1301
+vn -0.0918 0.9878 -0.1261
+vn -0.1103 0.9878 -0.1103
+vn -0.0711 0.9877 -0.1390
+vn -0.0485 0.9877 -0.1486
+vn -0.0245 0.9877 -0.1545
+vn -0.0000 0.9877 -0.1566
+vn 0.0245 0.9877 -0.1545
+vn 0.0485 0.9877 -0.1486
+vn 0.0711 0.9877 -0.1390
+vn 0.0918 0.9878 -0.1261
+vn 0.1301 0.9829 -0.1300
+vn 0.1261 0.9878 -0.0918
+vn 0.1103 0.9878 -0.1103
+vn 0.1390 0.9877 -0.0711
+vn 0.1486 0.9877 -0.0485
+vn 0.1545 0.9877 -0.0245
+vn 0.1566 0.9877 -0.0000
+vn 0.1545 0.9877 0.0245
+vn 0.1486 0.9877 0.0485
+vn 0.1390 0.9877 0.0711
+vn 0.1301 0.9829 0.1300
+vn 0.1261 0.9878 0.0918
+vn 0.1300 0.9829 0.1301
+vn 0.0918 0.9878 0.1261
+vn 0.1103 0.9878 0.1103
+vn 0.0711 0.9877 0.1390
+vn 0.0485 0.9877 0.1486
+vn 0.0245 0.9877 0.1545
+vn -0.0000 0.9877 0.1566
+vn -0.0245 0.9877 0.1545
+vn -0.0485 0.9877 0.1486
+vn -0.0711 0.9877 0.1390
+vn -0.0918 0.9878 0.1261
+vn -0.1301 0.9829 0.1300
+vn -0.1261 0.9878 0.0918
+vn -0.1103 0.9878 0.1103
+vn -0.1348 0.9885 0.0690
+vn -0.1223 0.9885 0.0891
+vn -0.1442 0.9884 0.0470
+vn -0.1499 0.9884 0.0238
+vn -0.1442 0.9884 0.0471
+vn -0.1519 0.9884 -0.0000
+vn -0.1565 0.9877 -0.0000
+vn -0.1499 0.9884 -0.0238
+vn -0.1442 0.9884 -0.0470
+vn -0.1348 0.9885 -0.0690
+vn -0.1223 0.9885 -0.0891
+vn -0.0891 0.9885 -0.1223
+vn -0.1070 0.9885 -0.1070
+vn -0.0690 0.9885 -0.1348
+vn -0.0470 0.9884 -0.1442
+vn -0.0485 0.9877 -0.1485
+vn -0.0238 0.9884 -0.1499
+vn -0.0471 0.9884 -0.1442
+vn -0.0000 0.9884 -0.1519
+vn -0.0000 0.9877 -0.1565
+vn 0.0238 0.9884 -0.1499
+vn 0.0470 0.9884 -0.1442
+vn 0.0485 0.9877 -0.1485
+vn 0.0690 0.9885 -0.1348
+vn 0.0891 0.9885 -0.1223
+vn 0.1223 0.9885 -0.0891
+vn 0.1070 0.9885 -0.1070
+vn 0.1348 0.9885 -0.0690
+vn 0.1442 0.9884 -0.0470
+vn 0.1499 0.9884 -0.0238
+vn 0.1442 0.9884 -0.0471
+vn 0.1519 0.9884 -0.0000
+vn 0.1565 0.9877 -0.0000
+vn 0.1499 0.9884 0.0238
+vn 0.1442 0.9884 0.0470
+vn 0.1348 0.9885 0.0690
+vn 0.1223 0.9885 0.0891
+vn 0.0891 0.9885 0.1223
+vn 0.1070 0.9885 0.1070
+vn 0.0690 0.9885 0.1348
+vn 0.0470 0.9884 0.1442
+vn 0.0485 0.9877 0.1485
+vn 0.0238 0.9884 0.1499
+vn 0.0471 0.9884 0.1442
+vn -0.0000 0.9884 0.1519
+vn -0.0000 0.9877 0.1565
+vn -0.0238 0.9884 0.1499
+vn -0.0470 0.9884 0.1442
+vn -0.0485 0.9877 0.1485
+vn -0.0690 0.9885 0.1348
+vn -0.0891 0.9885 0.1223
+vn -0.1070 0.9885 0.1070
+vn -0.1374 0.9854 0.1001
+vn -0.1201 0.9855 0.1202
+vn -0.1514 0.9854 0.0775
+vn -0.1374 0.9855 0.1000
+vn -0.1349 0.9885 0.0690
+vn -0.1619 0.9854 0.0528
+vn -0.1684 0.9854 0.0267
+vn -0.1500 0.9884 0.0238
+vn -0.1706 0.9853 -0.0000
+vn -0.1684 0.9854 -0.0267
+vn -0.1619 0.9854 -0.0528
+vn -0.1442 0.9884 -0.0471
+vn -0.1514 0.9854 -0.0775
+vn -0.1374 0.9854 -0.1001
+vn -0.1001 0.9854 -0.1374
+vn -0.1202 0.9855 -0.1201
+vn -0.0775 0.9854 -0.1514
+vn -0.1000 0.9855 -0.1374
+vn -0.0528 0.9854 -0.1619
+vn -0.0267 0.9854 -0.1684
+vn -0.0238 0.9884 -0.1500
+vn -0.0000 0.9853 -0.1706
+vn 0.0267 0.9854 -0.1684
+vn 0.0690 0.9885 -0.1349
+vn 0.0528 0.9854 -0.1618
+vn 0.0471 0.9884 -0.1442
+vn 0.0775 0.9854 -0.1514
+vn 0.1001 0.9854 -0.1374
+vn 0.1374 0.9854 -0.1001
+vn 0.1201 0.9855 -0.1202
+vn 0.1514 0.9854 -0.0775
+vn 0.1374 0.9855 -0.1000
+vn 0.1349 0.9885 -0.0690
+vn 0.1619 0.9854 -0.0528
+vn 0.1684 0.9854 -0.0267
+vn 0.1500 0.9884 -0.0238
+vn 0.1706 0.9853 -0.0000
+vn 0.1684 0.9854 0.0267
+vn 0.1619 0.9854 0.0528
+vn 0.1442 0.9884 0.0471
+vn 0.1514 0.9854 0.0775
+vn 0.1374 0.9854 0.1001
+vn 0.1001 0.9854 0.1374
+vn 0.1202 0.9855 0.1201
+vn 0.0775 0.9854 0.1514
+vn 0.1000 0.9855 0.1374
+vn 0.0528 0.9854 0.1619
+vn 0.0267 0.9854 0.1684
+vn 0.0238 0.9884 0.1500
+vn -0.0000 0.9853 0.1706
+vn -0.0267 0.9854 0.1684
+vn -0.0690 0.9885 0.1349
+vn -0.0528 0.9854 0.1618
+vn -0.0471 0.9884 0.1442
+vn -0.0775 0.9854 0.1514
+vn -0.1001 0.9854 0.1374
+vn -0.1311 0.9749 0.1799
+vn -0.1202 0.9855 0.1201
+vn -0.1799 0.9749 0.1311
+vn -0.1574 0.9749 0.1574
+vn -0.1983 0.9749 0.1015
+vn -0.1799 0.9749 0.1310
+vn -0.2120 0.9748 0.0692
+vn -0.2205 0.9748 0.0350
+vn -0.2234 0.9747 -0.0000
+vn -0.2205 0.9748 -0.0350
+vn -0.2120 0.9748 -0.0692
+vn -0.1983 0.9749 -0.1015
+vn -0.1799 0.9749 -0.1311
+vn -0.1201 0.9855 -0.1202
+vn -0.1311 0.9749 -0.1799
+vn -0.1574 0.9749 -0.1574
+vn -0.1015 0.9749 -0.1983
+vn -0.1310 0.9749 -0.1799
+vn -0.0692 0.9748 -0.2120
+vn -0.0350 0.9748 -0.2205
+vn -0.0000 0.9747 -0.2234
+vn 0.0528 0.9854 -0.1619
+vn 0.0350 0.9748 -0.2205
+vn 0.0692 0.9748 -0.2120
+vn 0.1015 0.9749 -0.1983
+vn 0.1311 0.9749 -0.1799
+vn 0.1202 0.9855 -0.1201
+vn 0.1799 0.9749 -0.1311
+vn 0.1574 0.9749 -0.1574
+vn 0.1983 0.9749 -0.1015
+vn 0.1799 0.9749 -0.1310
+vn 0.2120 0.9748 -0.0692
+vn 0.2205 0.9748 -0.0350
+vn 0.2234 0.9747 -0.0000
+vn 0.2205 0.9748 0.0350
+vn 0.2120 0.9748 0.0692
+vn 0.1983 0.9749 0.1015
+vn 0.1799 0.9749 0.1311
+vn 0.1201 0.9855 0.1202
+vn 0.1311 0.9749 0.1799
+vn 0.1574 0.9749 0.1574
+vn 0.1015 0.9749 0.1983
+vn 0.1310 0.9749 0.1799
+vn 0.0692 0.9748 0.2120
+vn 0.0350 0.9748 0.2205
+vn -0.0000 0.9747 0.2234
+vn -0.0528 0.9854 0.1619
+vn -0.0350 0.9748 0.2205
+vn -0.0692 0.9748 0.2120
+vn -0.1015 0.9749 0.1983
+vn -0.1583 0.9376 0.3094
+vn -0.2045 0.9377 0.2808
+vn -0.2808 0.9377 0.2045
+vn -0.2456 0.9377 0.2456
+vn -0.3094 0.9376 0.1583
+vn -0.3308 0.9375 0.1080
+vn -0.3094 0.9377 0.1583
+vn -0.3440 0.9374 0.0546
+vn -0.3485 0.9373 -0.0000
+vn -0.3440 0.9374 -0.0546
+vn -0.3308 0.9375 -0.1080
+vn -0.3094 0.9376 -0.1583
+vn -0.2808 0.9377 -0.2045
+vn -0.2045 0.9377 -0.2808
+vn -0.2456 0.9377 -0.2456
+vn -0.1583 0.9376 -0.3094
+vn -0.1080 0.9375 -0.3308
+vn -0.1583 0.9377 -0.3094
+vn -0.0546 0.9374 -0.3440
+vn -0.0000 0.9373 -0.3485
+vn 0.0546 0.9374 -0.3440
+vn 0.1080 0.9375 -0.3308
+vn 0.1583 0.9376 -0.3094
+vn 0.2045 0.9377 -0.2808
+vn 0.2808 0.9377 -0.2045
+vn 0.2456 0.9377 -0.2456
+vn 0.3094 0.9376 -0.1583
+vn 0.3308 0.9375 -0.1080
+vn 0.3094 0.9377 -0.1583
+vn 0.3440 0.9374 -0.0546
+vn 0.3485 0.9373 -0.0000
+vn 0.3440 0.9374 0.0546
+vn 0.3308 0.9375 0.1080
+vn 0.3094 0.9376 0.1583
+vn 0.2808 0.9377 0.2045
+vn 0.2045 0.9377 0.2808
+vn 0.2456 0.9377 0.2456
+vn 0.1583 0.9376 0.3094
+vn 0.1080 0.9375 0.3308
+vn 0.1583 0.9377 0.3094
+vn 0.0546 0.9374 0.3440
+vn -0.0000 0.9373 0.3485
+vn -0.0546 0.9374 0.3440
+vn -0.1080 0.9375 0.3308
+vn -0.2070 0.7450 0.6341
+vn -0.3037 0.7453 0.5935
+vn -0.1583 0.9377 0.3094
+vn -0.3924 0.7455 0.5387
+vn -0.5387 0.7455 0.3924
+vn -0.4712 0.7456 0.4712
+vn -0.5935 0.7453 0.3037
+vn -0.6341 0.7450 0.2070
+vn -0.6592 0.7446 0.1046
+vn -0.6342 0.7450 0.2070
+vn -0.6677 0.7445 -0.0000
+vn -0.6592 0.7447 0.1046
+vn -0.6592 0.7446 -0.1046
+vn -0.6341 0.7450 -0.2070
+vn -0.5935 0.7453 -0.3037
+vn -0.3094 0.9377 -0.1583
+vn -0.5387 0.7455 -0.3924
+vn -0.3924 0.7455 -0.5387
+vn -0.4712 0.7456 -0.4712
+vn -0.3037 0.7453 -0.5935
+vn -0.2070 0.7450 -0.6341
+vn -0.1046 0.7446 -0.6592
+vn -0.2070 0.7450 -0.6342
+vn -0.0000 0.7445 -0.6677
+vn -0.1046 0.7447 -0.6592
+vn 0.1046 0.7446 -0.6592
+vn 0.2070 0.7450 -0.6341
+vn 0.3037 0.7453 -0.5935
+vn 0.1583 0.9377 -0.3094
+vn 0.3924 0.7455 -0.5387
+vn 0.5387 0.7455 -0.3924
+vn 0.4712 0.7456 -0.4712
+vn 0.5935 0.7453 -0.3037
+vn 0.6341 0.7450 -0.2070
+vn 0.6592 0.7446 -0.1046
+vn 0.6342 0.7450 -0.2070
+vn 0.6677 0.7445 -0.0000
+vn 0.6592 0.7447 -0.1046
+vn 0.6592 0.7446 0.1046
+vn 0.6341 0.7450 0.2070
+vn 0.5935 0.7453 0.3037
+vn 0.3094 0.9377 0.1583
+vn 0.5387 0.7455 0.3924
+vn 0.3924 0.7455 0.5387
+vn 0.4712 0.7456 0.4712
+vn 0.3037 0.7453 0.5935
+vn 0.2070 0.7450 0.6341
+vn 0.1046 0.7446 0.6592
+vn 0.2070 0.7450 0.6342
+vn -0.0000 0.7445 0.6677
+vn 0.1046 0.7447 0.6592
+vn -0.1046 0.7446 0.6592
+vn -0.1331 0.5287 0.8383
+vn -0.1047 0.7446 0.6592
+vn -0.2633 0.5292 0.8066
+vn -0.3924 0.7456 0.5387
+vn -0.3864 0.5297 0.7551
+vn -0.4993 0.5300 0.6855
+vn -0.6855 0.5299 0.4993
+vn -0.5996 0.5301 0.5996
+vn -0.7551 0.5297 0.3864
+vn -0.6855 0.5300 0.4993
+vn -0.8066 0.5292 0.2633
+vn -0.8383 0.5287 0.1331
+vn -0.6592 0.7447 0.1047
+vn -0.8489 0.5285 -0.0000
+vn -0.8383 0.5288 0.1331
+vn -0.6676 0.7445 -0.0000
+vn -0.8383 0.5287 -0.1331
+vn -0.6592 0.7446 -0.1047
+vn -0.8066 0.5292 -0.2633
+vn -0.5387 0.7456 -0.3924
+vn -0.7551 0.5297 -0.3864
+vn -0.6855 0.5300 -0.4993
+vn -0.4993 0.5299 -0.6855
+vn -0.5996 0.5301 -0.5996
+vn -0.3864 0.5297 -0.7551
+vn -0.4993 0.5300 -0.6855
+vn -0.2633 0.5292 -0.8066
+vn -0.1331 0.5287 -0.8383
+vn -0.0000 0.5285 -0.8489
+vn 0.1046 0.7447 -0.6592
+vn -0.0000 0.7445 -0.6676
+vn 0.1331 0.5287 -0.8383
+vn 0.1047 0.7446 -0.6592
+vn 0.2633 0.5292 -0.8066
+vn 0.3924 0.7456 -0.5387
+vn 0.3864 0.5297 -0.7551
+vn 0.4993 0.5300 -0.6855
+vn 0.6855 0.5299 -0.4993
+vn 0.5996 0.5301 -0.5996
+vn 0.7551 0.5297 -0.3864
+vn 0.6855 0.5300 -0.4993
+vn 0.8066 0.5292 -0.2633
+vn 0.8383 0.5287 -0.1331
+vn 0.6592 0.7447 -0.1047
+vn 0.8489 0.5285 -0.0000
+vn 0.8383 0.5288 -0.1331
+vn 0.6676 0.7445 -0.0000
+vn 0.8383 0.5287 0.1331
+vn 0.6592 0.7446 0.1047
+vn 0.8066 0.5292 0.2633
+vn 0.5387 0.7456 0.3924
+vn 0.7551 0.5297 0.3864
+vn 0.6855 0.5300 0.4993
+vn 0.4993 0.5299 0.6855
+vn 0.5996 0.5301 0.5996
+vn 0.3864 0.5297 0.7551
+vn 0.4993 0.5300 0.6855
+vn 0.2633 0.5292 0.8066
+vn 0.1331 0.5287 0.8383
+vn -0.0000 0.5285 0.8489
+vn -0.1046 0.7447 0.6592
+vn -0.0000 0.7445 0.6676
+vn -0.0000 -1.0000 -0.0011
+vn 0.1701 -0.9854 -0.0049
+vn -0.0000 -1.0000 -0.0049
+vn 0.1704 -0.9854 -0.0011
+vn 0.3774 -0.9260 -0.0048
+vn 0.3779 -0.9259 -0.0011
+vn 0.6274 -0.7787 -0.0042
+vn 0.6278 -0.7784 -0.0009
+vn 0.8803 -0.4744 -0.0027
+vn 0.6273 -0.7787 -0.0043
+vn 0.8806 -0.4739 -0.0006
+vn 1.0000 -0.0000 -0.0000
+vn 0.8803 0.4744 0.0030
+vn 0.8805 0.4740 0.0007
+vn 0.6274 0.7787 0.0052
+vn 0.6278 0.7783 0.0012
+vn 0.3774 0.9260 0.0063
+vn 0.3778 0.9259 0.0014
+vn 0.1701 0.9854 0.0068
+vn 0.1704 0.9854 0.0015
+vn -0.0000 1.0000 0.0069
+vn -0.1704 0.9854 0.0015
+vn -0.1701 0.9854 0.0068
+vn -0.1699 0.9852 0.0246
+vn -0.1702 0.9854 0.0068
+vn -0.1707 0.9835 0.0601
+vn -0.0000 0.9997 0.0251
+vn -0.1736 0.9773 0.1216
+vn -0.0000 0.9981 0.0613
+vn -0.1706 0.9835 0.0601
+vn -0.1804 0.9583 0.2218
+vn -0.0000 0.9922 0.1243
+vn -0.1927 0.9066 0.3755
+vn -0.0000 0.9739 0.2269
+vn -0.2340 0.7632 0.6024
+vn -0.0000 0.9229 0.3850
+vn -0.1926 0.9066 0.3755
+vn -0.2208 0.5580 0.7999
+vn -0.0000 0.7999 0.6001
+vn -0.2340 0.7631 0.6024
+vn -0.2263 0.2691 0.9362
+vn -0.0000 0.5703 0.8214
+vn -0.2209 0.5580 0.7999
+vn -0.2242 0.0219 0.9743
+vn -0.0000 0.2745 0.9616
+vn -0.2262 0.2692 0.9361
+vn -0.2240 -0.1534 0.9624
+vn -0.0000 0.0221 0.9998
+vn -0.2242 0.0220 0.9743
+vn -0.2237 -0.3017 0.9268
+vn -0.0000 -0.1575 0.9875
+vn -0.2206 -0.4369 0.8720
+vn -0.0000 -0.3102 0.9507
+vn -0.2352 -0.5607 0.7939
+vn -0.0000 -0.4493 0.8934
+vn -0.2223 -0.6560 0.7213
+vn -0.0000 -0.5686 0.8226
+vn -0.2095 -0.7318 0.6485
+vn -0.0000 -0.6662 0.7458
+vn -0.2223 -0.6559 0.7213
+vn -0.1982 -0.7912 0.5786
+vn -0.0000 -0.7434 0.6688
+vn -0.1723 -0.8361 0.5208
+vn -0.0000 -0.8033 0.5955
+vn -0.1982 -0.7912 0.5785
+vn -0.1725 -0.8708 0.4603
+vn -0.0000 -0.8494 0.5278
+vn -0.1729 -0.8856 0.4310
+vn -0.0000 -0.8846 0.4664
+vn 0.1729 -0.8856 0.4310
+vn 0.1725 -0.8708 0.4603
+vn 0.1943 -0.8280 0.5260
+vn 0.2051 -0.7798 0.5916
+vn 0.2051 -0.7797 0.5916
+vn 0.2171 -0.7174 0.6620
+vn 0.2293 -0.6383 0.7348
+vn 0.2158 -0.5542 0.8040
+vn 0.2158 -0.5541 0.8040
+vn 0.2206 -0.4369 0.8720
+vn 0.2237 -0.3017 0.9268
+vn 0.2240 -0.1534 0.9624
+vn 0.2242 0.0220 0.9743
+vn 0.2242 0.0219 0.9743
+vn 0.2262 0.2692 0.9361
+vn 0.2263 0.2691 0.9361
+vn 0.2449 0.5832 0.7745
+vn 0.2113 0.7832 0.5847
+vn 0.2114 0.7832 0.5847
+vn 0.1926 0.9066 0.3755
+vn 0.1927 0.9066 0.3755
+vn 0.1804 0.9583 0.2218
+vn 0.1736 0.9773 0.1216
+vn 0.1706 0.9835 0.0601
+vn 0.1707 0.9835 0.0601
+vn 0.1699 0.9852 0.0246
+vn 0.1702 0.9854 0.0068
+vn 0.3770 0.9259 0.0227
+vn 0.3775 0.9260 0.0063
+vn 0.3783 0.9240 0.0555
+vn 0.3836 0.9167 0.1120
+vn 0.3961 0.8954 0.2032
+vn 0.1804 0.9583 0.2217
+vn 0.4215 0.8398 0.3421
+vn 0.4492 0.7196 0.5296
+vn 0.4215 0.8398 0.3420
+vn 0.4963 0.5290 0.6883
+vn 0.5049 0.2687 0.8203
+vn 0.4964 0.5290 0.6883
+vn 0.2262 0.2691 0.9362
+vn 0.4758 0.0209 0.8793
+vn 0.4754 -0.1381 0.8689
+vn 0.4759 0.0209 0.8793
+vn 0.2239 -0.1534 0.9625
+vn 0.4750 -0.2707 0.8374
+vn 0.4716 -0.3924 0.7897
+vn 0.4749 -0.2707 0.8374
+vn 0.4600 -0.5005 0.7334
+vn 0.4417 -0.5919 0.6742
+vn 0.4417 -0.5918 0.6742
+vn 0.4212 -0.6685 0.6129
+vn 0.4211 -0.6685 0.6129
+vn 0.4211 -0.6685 0.6130
+vn 0.2050 -0.7798 0.5916
+vn 0.4011 -0.7311 0.5519
+vn 0.4011 -0.7311 0.5520
+vn 0.2050 -0.7797 0.5916
+vn 0.4099 -0.7671 0.4936
+vn 0.4038 -0.8044 0.4357
+vn 0.3824 -0.8302 0.4056
+vn 0.4037 -0.8045 0.4357
+vn 0.6323 -0.6953 0.3416
+vn 0.6543 -0.6630 0.3638
+vn 0.6346 -0.6505 0.4172
+vn 0.4098 -0.7671 0.4936
+vn 0.4037 -0.8044 0.4357
+vn 0.6511 -0.6013 0.4632
+vn 0.6726 -0.5401 0.5058
+vn 0.6939 -0.4696 0.5458
+vn 0.6850 -0.4128 0.6004
+vn 0.6968 -0.3251 0.6393
+vn 0.6849 -0.4128 0.6004
+vn 0.6967 -0.3252 0.6394
+vn 0.4750 -0.2707 0.8373
+vn 0.6968 -0.3252 0.6393
+vn 0.7273 -0.2090 0.6537
+vn 0.7278 -0.1072 0.6774
+vn 0.7277 -0.1072 0.6774
+vn 0.7283 0.0176 0.6850
+vn 0.7283 0.0177 0.6850
+vn 0.7295 0.1998 0.6541
+vn 0.7222 0.4063 0.5597
+vn 0.7222 0.4063 0.5598
+vn 0.6997 0.5808 0.4161
+vn 0.6997 0.5808 0.4160
+vn 0.4492 0.7195 0.5296
+vn 0.6488 0.7012 0.2955
+vn 0.6467 0.7449 0.1640
+vn 0.6467 0.7449 0.1639
+vn 0.6336 0.7683 0.0912
+vn 0.6280 0.7769 0.0454
+vn 0.6268 0.7790 0.0186
+vn 0.6273 0.7787 0.0052
+vn 0.8800 0.4749 0.0110
+vn 0.8803 0.4744 0.0031
+vn 0.8805 0.4733 0.0268
+vn 0.8800 0.4748 0.0110
+vn 0.6280 0.7769 0.0453
+vn 0.8830 0.4664 0.0535
+vn 0.6336 0.7682 0.0912
+vn 0.8889 0.4481 0.0952
+vn 0.6468 0.7449 0.1639
+vn 0.8808 0.4383 0.1793
+vn 0.8889 0.4481 0.0951
+vn 0.8808 0.4383 0.1792
+vn 0.8946 0.3605 0.2641
+vn 0.9209 0.2391 0.3079
+vn 0.7221 0.4064 0.5598
+vn 0.9257 0.1133 0.3609
+vn 0.9246 0.0107 0.3808
+vn 0.9257 0.1134 0.3609
+vn 0.9244 -0.0593 0.3769
+vn 0.9246 0.0107 0.3807
+vn 0.9242 -0.1150 0.3642
+vn 0.9244 -0.0593 0.3768
+vn 0.9222 -0.1669 0.3489
+vn 0.9242 -0.1150 0.3643
+vn 0.8999 -0.2439 0.3616
+vn 0.8998 -0.2440 0.3616
+vn 0.8896 -0.2972 0.3469
+vn 0.6727 -0.5401 0.5058
+vn 0.8779 -0.3484 0.3285
+vn 0.8661 -0.3952 0.3061
+vn 0.8577 -0.4327 0.2777
+vn 0.6543 -0.6629 0.3639
+vn 0.8796 -0.4173 0.2284
+vn 0.8824 -0.4217 0.2086
+vn 1.0000 0.0001 -0.0007
+vn 1.0000 -0.0000 -0.0013
+vn 1.0000 -0.0005 -0.0021
+vn 1.0000 -0.0004 -0.0021
+vn 1.0000 -0.0011 -0.0023
+vn 1.0000 -0.0010 -0.0023
+vn 1.0000 -0.0015 -0.0021
+vn 0.8896 -0.2971 0.3469
+vn 1.0000 -0.0017 -0.0016
+vn 0.8999 -0.2440 0.3616
+vn 1.0000 -0.0015 -0.0011
+vn 1.0000 -0.0011 -0.0006
+vn 1.0000 -0.0005 -0.0002
+vn 0.9243 -0.0594 0.3769
+vn 1.0000 -0.0006 -0.0002
+vn 1.0000 -0.0001 -0.0000
+vn 1.0000 -0.0004 -0.0000
+vn 1.0000 -0.0004 0.0001
+vn 1.0000 -0.0018 0.0005
+vn 1.0000 -0.0029 0.0012
+vn 1.0000 -0.0029 0.0017
+vn 1.0000 -0.0029 0.0016
+vn 1.0000 -0.0021 0.0015
+vn 1.0000 -0.0021 0.0016
+vn 1.0000 -0.0012 0.0012
+vn 1.0000 -0.0005 0.0007
+vn 1.0000 -0.0006 0.0007
+vn 1.0000 -0.0002 0.0004
+vn 1.0000 -0.0002 0.0003
+vn 1.0000 -0.0001 0.0001
+vn 1.0000 -0.0000 0.0001
+vn 0.8798 -0.4752 -0.0098
+vn 0.8799 -0.4746 -0.0237
+vn 0.8799 -0.4751 -0.0098
+vn 0.8814 -0.4700 -0.0473
+vn 0.8799 -0.4745 -0.0236
+vn 0.8875 -0.4528 -0.0851
+vn 0.8814 -0.4699 -0.0473
+vn 0.8735 -0.4654 -0.1425
+vn 0.8875 -0.4529 -0.0851
+vn 0.8878 -0.4043 -0.2199
+vn 0.9023 -0.3014 -0.3083
+vn 0.9092 -0.1635 -0.3830
+vn 0.9248 -0.0148 -0.3803
+vn 0.9243 0.0583 -0.3772
+vn 0.9247 -0.0148 -0.3803
+vn 1.0000 -0.0001 -0.0001
+vn 0.9240 0.1099 -0.3663
+vn 0.9229 0.1568 -0.3516
+vn 0.9025 0.2139 -0.3738
+vn 0.8945 0.2639 -0.3609
+vn 0.8835 0.3153 -0.3465
+vn 0.8709 0.3665 -0.3274
+vn 0.8601 0.4125 -0.3000
+vn 0.8555 0.4463 -0.2627
+vn 0.8602 0.4125 -0.2999
+vn 0.8810 0.4219 -0.2140
+vn 0.8554 0.4463 -0.2627
+vn 0.8554 0.4464 -0.2627
+vn 0.6277 0.6928 -0.3549
+vn 0.5927 0.6912 -0.4135
+vn 0.6242 0.6303 -0.4616
+vn 0.6421 0.5682 -0.5147
+vn 0.8602 0.4125 -0.3000
+vn 0.6421 0.5681 -0.5147
+vn 0.6666 0.4971 -0.5554
+vn 0.6666 0.4971 -0.5555
+vn 0.6904 0.4228 -0.5870
+vn 0.7093 0.3479 -0.6131
+vn 0.9025 0.2139 -0.3737
+vn 0.7453 0.2714 -0.6090
+vn 0.9229 0.1569 -0.3516
+vn 0.9240 0.1099 -0.3662
+vn 0.7453 0.2713 -0.6090
+vn 0.7266 0.1939 -0.6591
+vn 0.7266 0.1940 -0.6591
+vn 0.7275 0.1041 -0.6782
+vn 0.7288 -0.0298 -0.6841
+vn 0.7013 -0.2904 -0.6511
+vn 0.7109 -0.4859 -0.5085
+vn 0.6804 -0.6418 -0.3538
+vn 0.8878 -0.4044 -0.2198
+vn 0.6531 -0.7234 -0.2240
+vn 0.8878 -0.4044 -0.2199
+vn 0.6653 -0.7337 -0.1380
+vn 0.6654 -0.7337 -0.1380
+vn 0.6284 -0.7744 -0.0741
+vn 0.6261 -0.7789 -0.0369
+vn 0.8799 -0.4751 -0.0097
+vn 0.6263 -0.7794 -0.0152
+vn 0.6264 -0.7794 -0.0152
+vn 0.6273 -0.7788 -0.0042
+vn 0.3765 -0.9263 -0.0172
+vn 0.3760 -0.9257 -0.0416
+vn 0.3772 -0.9223 -0.0836
+vn 0.3759 -0.9257 -0.0416
+vn 0.4068 -0.8995 -0.1594
+vn 0.4216 -0.8650 -0.2720
+vn 0.4215 -0.8650 -0.2720
+vn 0.4216 -0.8019 -0.4233
+vn 0.7109 -0.4859 -0.5084
+vn 0.4536 -0.6281 -0.6322
+vn 0.4537 -0.6281 -0.6322
+vn 0.4451 -0.3786 -0.8115
+vn 0.7013 -0.2903 -0.6511
+vn 0.4766 -0.0427 -0.8781
+vn 0.7288 -0.0299 -0.6841
+vn 0.4750 0.1326 -0.8699
+vn 0.4739 0.2440 -0.8461
+vn 0.4935 0.3454 -0.7982
+vn 0.4934 0.3454 -0.7983
+vn 0.4537 0.4294 -0.7809
+vn 0.4536 0.4293 -0.7809
+vn 0.4337 0.5146 -0.7397
+vn 0.4100 0.5965 -0.6900
+vn 0.3871 0.6736 -0.6297
+vn 0.3716 0.7425 -0.5573
+vn 0.3715 0.7425 -0.5573
+vn 0.3462 0.8017 -0.4872
+vn 0.3767 0.8228 -0.4255
+vn 0.3461 0.8017 -0.4872
+vn 0.1694 0.8741 -0.4553
+vn 0.1647 0.8466 -0.5061
+vn 0.1823 0.7881 -0.5880
+vn 0.1823 0.7880 -0.5880
+vn 0.1902 0.7169 -0.6707
+vn 0.2034 0.6389 -0.7419
+vn 0.2181 0.5560 -0.8021
+vn 0.2181 0.5560 -0.8020
+vn 0.2312 0.4690 -0.8524
+vn 0.4537 0.4293 -0.7809
+vn 0.2417 0.3772 -0.8940
+vn 0.4536 0.4294 -0.7809
+vn 0.4934 0.3454 -0.7982
+vn 0.2230 0.2664 -0.9377
+vn 0.2237 0.1461 -0.9636
+vn 0.2247 -0.0513 -0.9731
+vn 0.2209 -0.3831 -0.8969
+vn 0.2323 -0.6708 -0.7043
+vn 0.2322 -0.6708 -0.7043
+vn 0.4452 -0.3786 -0.8115
+vn 0.2120 -0.8601 -0.4639
+vn 0.2323 -0.6709 -0.7042
+vn 0.1991 -0.9384 -0.2824
+vn 0.4216 -0.8020 -0.4233
+vn 0.2120 -0.8602 -0.4639
+vn 0.4216 -0.8020 -0.4232
+vn 0.1720 -0.9727 -0.1557
+vn 0.3773 -0.9223 -0.0836
+vn 0.1696 -0.9818 -0.0854
+vn 0.1692 -0.9847 -0.0425
+vn 0.1696 -0.9854 -0.0176
+vn 0.1702 -0.9854 -0.0049
+vn -0.0000 -0.9998 -0.0176
+vn -0.0000 -0.9991 -0.0425
+vn -0.0000 -0.9964 -0.0853
+vn -0.0000 -0.9878 -0.1555
+vn -0.0000 -0.9631 -0.2691
+vn -0.0000 -0.8941 -0.4479
+vn -0.0000 -0.9631 -0.2692
+vn -0.0000 -0.7202 -0.6938
+vn -0.0000 -0.3950 -0.9187
+vn -0.0000 -0.7201 -0.6938
+vn -0.0000 -0.0542 -0.9985
+vn -0.0000 0.1496 -0.9887
+vn 0.2237 0.1461 -0.9637
+vn -0.0000 0.2718 -0.9623
+vn -0.0000 0.3758 -0.9267
+vn -0.0000 0.2719 -0.9623
+vn -0.0000 0.4698 -0.8828
+vn -0.0000 0.5577 -0.8300
+vn -0.0000 0.6412 -0.7674
+vn -0.0000 0.7201 -0.6939
+vn -0.0000 0.7930 -0.6092
+vn -0.0000 0.8578 -0.5139
+vn 0.1648 0.8465 -0.5062
+vn -0.0000 0.8864 -0.4629
+vn -0.1849 0.8386 -0.5124
+vn -0.1694 0.8741 -0.4553
+vn -0.3932 0.7881 -0.4736
+vn -0.3767 0.8228 -0.4255
+vn -0.6476 0.6568 -0.3865
+vn -0.6277 0.6928 -0.3549
+vn -0.8776 0.4169 -0.2365
+vn -0.6476 0.6567 -0.3865
+vn -0.8810 0.4219 -0.2140
+vn -1.0000 -0.0000 -0.0013
+vn -0.8777 0.4169 -0.2365
+vn -1.0000 0.0001 -0.0007
+vn -0.8585 -0.4480 0.2496
+vn -0.8824 -0.4217 0.2086
+vn -0.6018 -0.7007 0.3831
+vn -0.6323 -0.6953 0.3416
+vn -0.3788 -0.8173 0.4342
+vn -0.3824 -0.8302 0.4056
+vn -0.3825 -0.7814 0.4930
+vn -0.3976 -0.7317 0.5536
+vn -0.4176 -0.6686 0.6153
+vn -0.4176 -0.6686 0.6154
+vn -0.2096 -0.7318 0.6485
+vn -0.4175 -0.6686 0.6154
+vn -0.4386 -0.5909 0.6771
+vn -0.4809 -0.5003 0.7200
+vn -0.4809 -0.5004 0.7200
+vn -0.4942 -0.3959 0.7740
+vn -0.4749 -0.2707 0.8374
+vn -0.4750 -0.2707 0.8374
+vn -0.2239 -0.1534 0.9625
+vn -0.4754 -0.1381 0.8689
+vn -0.4758 0.0209 0.8793
+vn -0.2262 0.2691 0.9362
+vn -0.4771 0.2463 0.8436
+vn -0.4714 0.5149 0.7160
+vn -0.4759 0.6971 0.5363
+vn -0.4759 0.6970 0.5363
+vn -0.4456 0.8235 0.3513
+vn -0.4455 0.8235 0.3513
+vn -0.1804 0.9583 0.2217
+vn -0.3961 0.8954 0.2032
+vn -0.3836 0.9167 0.1120
+vn -0.3783 0.9240 0.0555
+vn -0.3770 0.9259 0.0227
+vn -0.3775 0.9260 0.0063
+vn -0.3778 0.9259 0.0014
+vn -0.3774 0.9260 0.0063
+vn -0.6278 0.7783 0.0012
+vn -0.6274 0.7787 0.0052
+vn -0.6268 0.7790 0.0186
+vn -0.6280 0.7769 0.0454
+vn -0.6336 0.7683 0.0912
+vn -0.6467 0.7449 0.1639
+vn -0.6988 0.6622 0.2706
+vn -0.6996 0.5878 0.4062
+vn -0.6988 0.6622 0.2705
+vn -0.6973 0.4477 0.5597
+vn -0.6973 0.4478 0.5597
+vn -0.7069 0.2292 0.6691
+vn -0.7069 0.2293 0.6692
+vn -0.7283 0.0177 0.6850
+vn -0.7069 0.2292 0.6692
+vn -0.7277 -0.1072 0.6774
+vn -0.7283 0.0176 0.6850
+vn -0.7273 -0.2090 0.6537
+vn -0.7278 -0.1072 0.6774
+vn -0.7238 -0.3015 0.6206
+vn -0.4750 -0.2707 0.8373
+vn -0.7124 -0.3854 0.5864
+vn -0.7239 -0.3014 0.6206
+vn -0.6937 -0.4661 0.5492
+vn -0.7124 -0.3854 0.5865
+vn -0.6721 -0.5379 0.5089
+vn -0.6937 -0.4660 0.5492
+vn -0.6720 -0.5379 0.5089
+vn -0.6503 -0.6002 0.4658
+vn -0.6720 -0.5379 0.5090
+vn -0.6340 -0.6499 0.4190
+vn -0.6341 -0.6499 0.4190
+vn -0.8632 -0.4190 0.2818
+vn -0.8731 -0.3781 0.3077
+vn -0.8848 -0.3302 0.3288
+vn -0.8848 -0.3303 0.3288
+vn -0.8952 -0.2794 0.3472
+vn -0.9190 -0.2136 0.3314
+vn -0.9222 -0.1669 0.3489
+vn -0.9242 -0.1150 0.3643
+vn -0.9242 -0.1150 0.3642
+vn -0.9244 -0.0593 0.3768
+vn -0.9244 -0.0593 0.3769
+vn -0.9246 0.0107 0.3807
+vn -0.9246 0.0107 0.3808
+vn -0.9257 0.1134 0.3609
+vn -0.9257 0.1133 0.3609
+vn -0.9053 0.2738 0.3248
+vn -0.8932 0.3819 0.2372
+vn -0.8933 0.3819 0.2372
+vn -0.9002 0.4079 0.1526
+vn -0.9002 0.4079 0.1525
+vn -0.6468 0.7449 0.1639
+vn -0.8889 0.4481 0.0951
+vn -0.8889 0.4481 0.0952
+vn -0.6336 0.7682 0.0912
+vn -0.8830 0.4664 0.0535
+vn -0.6280 0.7769 0.0453
+vn -0.8805 0.4733 0.0268
+vn -0.8800 0.4748 0.0110
+vn -0.8800 0.4749 0.0110
+vn -0.6273 0.7787 0.0052
+vn -0.8803 0.4744 0.0031
+vn -0.8805 0.4740 0.0007
+vn -0.8803 0.4744 0.0030
+vn -1.0000 -0.0000 -0.0000
+vn -1.0000 -0.0000 0.0001
+vn -1.0000 -0.0002 0.0003
+vn -1.0000 -0.0001 0.0001
+vn -1.0000 -0.0006 0.0007
+vn -1.0000 -0.0002 0.0004
+vn -1.0000 -0.0012 0.0012
+vn -1.0000 -0.0005 0.0007
+vn -1.0000 -0.0021 0.0016
+vn -1.0000 -0.0029 0.0016
+vn -1.0000 -0.0021 0.0015
+vn -1.0000 -0.0029 0.0012
+vn -1.0000 -0.0029 0.0017
+vn -1.0000 -0.0018 0.0005
+vn -1.0000 -0.0004 0.0001
+vn -1.0000 -0.0001 -0.0000
+vn -1.0000 -0.0004 -0.0000
+vn -1.0000 -0.0006 -0.0002
+vn -0.9243 -0.0594 0.3769
+vn -1.0000 -0.0011 -0.0006
+vn -1.0000 -0.0005 -0.0002
+vn -1.0000 -0.0015 -0.0011
+vn -1.0000 -0.0017 -0.0016
+vn -1.0000 -0.0015 -0.0021
+vn -1.0000 -0.0010 -0.0023
+vn -1.0000 -0.0004 -0.0021
+vn -1.0000 -0.0011 -0.0023
+vn -1.0000 -0.0005 -0.0021
+vn -0.8545 0.4284 -0.2938
+vn -0.8632 0.3858 -0.3256
+vn -0.8633 0.3857 -0.3256
+vn -0.8760 0.3346 -0.3474
+vn -0.8884 0.2815 -0.3626
+vn -0.8983 0.2295 -0.3748
+vn -0.9053 0.1778 -0.3857
+vn -0.9053 0.1778 -0.3858
+vn -0.9240 0.1099 -0.3663
+vn -1.0000 -0.0001 -0.0001
+vn -0.9243 0.0583 -0.3772
+vn -0.9247 -0.0148 -0.3803
+vn -0.9248 -0.0148 -0.3803
+vn -0.9241 -0.1292 -0.3598
+vn -0.9241 -0.1292 -0.3597
+vn -0.9029 -0.2682 -0.3360
+vn -0.8887 -0.3863 -0.2468
+vn -0.8737 -0.4586 -0.1626
+vn -0.8737 -0.4585 -0.1626
+vn -0.8641 -0.4936 -0.0982
+vn -0.8641 -0.4936 -0.0981
+vn -0.8814 -0.4699 -0.0473
+vn -0.8814 -0.4700 -0.0473
+vn -0.8799 -0.4745 -0.0236
+vn -0.8799 -0.4746 -0.0237
+vn -0.8799 -0.4751 -0.0098
+vn -0.8798 -0.4752 -0.0098
+vn -0.8803 -0.4744 -0.0027
+vn -0.8806 -0.4739 -0.0006
+vn -0.6278 -0.7784 -0.0009
+vn -0.6273 -0.7787 -0.0043
+vn -0.6264 -0.7794 -0.0152
+vn -0.6274 -0.7787 -0.0042
+vn -0.6261 -0.7789 -0.0369
+vn -0.8799 -0.4751 -0.0097
+vn -0.6263 -0.7794 -0.0152
+vn -0.6284 -0.7744 -0.0741
+vn -0.6114 -0.7781 -0.1438
+vn -0.6525 -0.7226 -0.2282
+vn -0.6114 -0.7781 -0.1439
+vn -0.6525 -0.7227 -0.2282
+vn -0.8736 -0.4586 -0.1626
+vn -0.8641 -0.4937 -0.0982
+vn -0.6799 -0.6379 -0.3617
+vn -0.6799 -0.6379 -0.3616
+vn -0.7111 -0.4738 -0.5194
+vn -0.7501 -0.2545 -0.6104
+vn -0.7111 -0.4738 -0.5195
+vn -0.9240 -0.1292 -0.3598
+vn -0.7288 -0.0298 -0.6841
+vn -0.7501 -0.2544 -0.6104
+vn -0.7275 0.1041 -0.6782
+vn -0.7266 0.1940 -0.6591
+vn -0.6969 0.2902 -0.6559
+vn -0.9240 0.1099 -0.3662
+vn -0.7266 0.1939 -0.6591
+vn -0.7092 0.3510 -0.6114
+vn -0.6968 0.2902 -0.6559
+vn -0.6907 0.4257 -0.5846
+vn -0.6674 0.4998 -0.5521
+vn -0.6435 0.5705 -0.5104
+vn -0.6257 0.6325 -0.4566
+vn -0.3933 0.7881 -0.4736
+vn -0.6476 0.6567 -0.3864
+vn -0.3751 0.7433 -0.5539
+vn -0.6434 0.5705 -0.5104
+vn -0.3917 0.6739 -0.6265
+vn -0.4145 0.5967 -0.6871
+vn -0.4146 0.5967 -0.6871
+vn -0.6907 0.4258 -0.5846
+vn -0.4375 0.5149 -0.7373
+vn -0.4375 0.5148 -0.7373
+vn -0.4563 0.4298 -0.7791
+vn -0.4427 0.3532 -0.8242
+vn -0.4426 0.3532 -0.8242
+vn -0.4739 0.2440 -0.8461
+vn -0.4750 0.1326 -0.8699
+vn -0.7288 -0.0299 -0.6841
+vn -0.4766 -0.0427 -0.8781
+vn -0.4989 -0.3535 -0.7913
+vn -0.4534 -0.6223 -0.6381
+vn -0.7112 -0.4738 -0.5194
+vn -0.4208 -0.8010 -0.4257
+vn -0.4208 -0.8011 -0.4257
+vn -0.6524 -0.7227 -0.2282
+vn -0.3973 -0.8801 -0.2598
+vn -0.3973 -0.8802 -0.2598
+vn -0.3850 -0.9106 -0.1505
+vn -0.3772 -0.9223 -0.0836
+vn -0.3759 -0.9257 -0.0416
+vn -0.3760 -0.9257 -0.0416
+vn -0.3765 -0.9263 -0.0172
+vn -0.6273 -0.7788 -0.0042
+vn -0.3774 -0.9260 -0.0048
+vn -0.3779 -0.9259 -0.0011
+vn -0.1704 -0.9854 -0.0011
+vn -0.1701 -0.9854 -0.0049
+vn -0.1696 -0.9854 -0.0176
+vn -0.1702 -0.9854 -0.0049
+vn -0.1692 -0.9847 -0.0425
+vn -0.1696 -0.9818 -0.0854
+vn -0.1720 -0.9727 -0.1557
+vn -0.3773 -0.9223 -0.0836
+vn -0.1805 -0.9467 -0.2669
+vn -0.2116 -0.8819 -0.4213
+vn -0.2315 -0.7202 -0.6540
+vn -0.2116 -0.8818 -0.4214
+vn -0.2465 -0.4138 -0.8764
+vn -0.2247 -0.0513 -0.9731
+vn -0.2237 0.1461 -0.9636
+vn -0.2230 0.2664 -0.9377
+vn -0.2211 0.3680 -0.9032
+vn -0.2378 0.4505 -0.8605
+vn -0.2211 0.3679 -0.9032
+vn -0.2267 0.5383 -0.8117
+vn -0.2130 0.6220 -0.7535
+vn -0.4375 0.5149 -0.7372
+vn -0.1992 0.7013 -0.6845
+vn -0.1992 0.7012 -0.6845
+vn -0.1886 0.7742 -0.6041
+vn -0.1886 0.7743 -0.6041
+vn -0.2268 0.5383 -0.8117
+vn -0.2237 0.1461 -0.9637
+vn -0.2465 -0.4137 -0.8764
+vn 0.0403 0.9957 0.0830
+vn 0.3120 0.9463 0.0850
+vn 0.2567 0.9495 0.1803
+vn 0.3121 0.9462 0.0850
+vn 0.5314 0.8330 0.1539
+vn 0.5889 0.8043 0.0797
+vn 0.5889 0.8042 0.0797
+vn 0.7765 0.6218 0.1017
+vn 0.8203 0.5689 0.0583
+vn 0.9418 0.3353 0.0242
+vn 0.9627 0.2699 0.0175
+vn 0.9974 0.0122 -0.0705
+vn 0.9996 -0.0006 -0.0293
+vn 0.9364 -0.3099 -0.1645
+vn 0.9221 -0.3722 -0.1059
+vn 0.7918 -0.5655 -0.2306
+vn 0.7442 -0.6459 -0.1705
+vn 0.5537 -0.7778 -0.2974
+vn 0.4924 -0.8420 -0.2205
+vn 0.2836 -0.8980 -0.3364
+vn 0.2643 -0.9327 -0.2452
+vn -0.0000 -0.9281 -0.3724
+vn -0.3069 -0.9209 -0.2405
+vn -0.2527 -0.8971 -0.3625
+vn -0.0000 -0.7946 -0.6071
+vn -0.2526 -0.8971 -0.3625
+vn -0.2348 -0.7688 -0.5948
+vn -0.0000 -0.5990 -0.8008
+vn -0.2311 -0.5536 -0.8001
+vn -0.0000 -0.4132 -0.9106
+vn -0.2158 -0.3774 -0.9006
+vn -0.0197 -0.2958 -0.9551
+vn -0.2185 -0.2750 -0.9363
+vn -0.2196 -0.2671 -0.9383
+vn -0.0197 -0.2958 -0.9550
+vn -0.0047 -0.2880 -0.9576
+vn -0.0196 -0.2958 -0.9551
+vn -0.2577 -0.3465 -0.9020
+vn -0.2197 -0.2671 -0.9383
+vn -0.0065 -0.3748 -0.9271
+vn -0.2903 -0.5159 -0.8060
+vn -0.0056 -0.5532 -0.8330
+vn -0.2791 -0.7291 -0.6249
+vn -0.0002 -0.7655 -0.6434
+vn -0.2217 -0.8576 -0.4640
+vn -0.2217 -0.8577 -0.4640
+vn 0.0221 -0.8812 -0.4723
+vn -0.0002 -0.7655 -0.6435
+vn -0.2448 -0.8717 -0.4246
+vn -0.2965 -0.8179 -0.4931
+vn -0.0000 -0.9028 -0.4301
+vn -0.4670 -0.5905 -0.6582
+vn -0.0000 -0.8669 -0.4984
+vn -0.5221 0.2351 -0.8198
+vn -0.0000 -0.7546 -0.6562
+vn -0.1357 0.9710 -0.1968
+vn -0.0001 0.1250 -0.9922
+vn -0.5220 0.2351 -0.8199
+vn -0.0000 0.9866 0.1631
+vn -0.1356 0.9710 -0.1969
+vn 0.0842 0.9840 0.1570
+vn -0.0000 0.9616 0.2745
+vn 0.0842 0.9840 0.1571
+vn 0.1603 0.9512 0.2637
+vn -0.0000 0.9425 0.3341
+vn 0.2026 0.9246 0.3226
+vn -0.0000 0.9264 0.3766
+vn 0.2291 0.9022 0.3654
+vn -0.0000 0.9186 0.3953
+vn 0.2391 0.8917 0.3844
+vn -0.2291 0.9022 0.3654
+vn -0.2391 0.8917 0.3844
+vn -0.2024 0.9248 0.3221
+vn -0.1603 0.9512 0.2637
+vn -0.2026 0.9246 0.3226
+vn -0.0842 0.9840 0.1571
+vn 0.1356 0.9710 -0.1969
+vn -0.0842 0.9840 0.1570
+vn 0.0001 0.1250 -0.9922
+vn 0.1357 0.9710 -0.1969
+vn 0.5817 0.1070 -0.8063
+vn 0.5816 0.1070 -0.8064
+vn 0.3946 -0.6616 -0.6376
+vn 0.2965 -0.8179 -0.4931
+vn 0.2448 -0.8717 -0.4246
+vn 0.2742 -0.8440 -0.4609
+vn 0.2791 -0.7299 -0.6240
+vn 0.2793 -0.5199 -0.8073
+vn 0.2444 -0.3499 -0.9043
+vn 0.2096 -0.2689 -0.9401
+vn 0.2096 -0.2690 -0.9401
+vn 0.1912 -0.2861 -0.9389
+vn 0.2151 -0.4085 -0.8871
+vn 0.2103 -0.5793 -0.7875
+vn 0.2151 -0.4084 -0.8871
+vn 0.2348 -0.7688 -0.5948
+vn 0.4895 -0.6785 -0.5477
+vn 0.4483 -0.5054 -0.7373
+vn 0.4145 -0.3368 -0.8454
+vn 0.4146 -0.3368 -0.8454
+vn 0.4145 -0.3369 -0.8454
+vn 0.4113 -0.2304 -0.8819
+vn 0.4474 -0.2066 -0.8701
+vn 0.5130 -0.2679 -0.8155
+vn 0.5743 -0.4090 -0.7092
+vn 0.5739 -0.6080 -0.5486
+vn 0.5561 -0.7212 -0.4131
+vn 0.2742 -0.8440 -0.4610
+vn 0.5324 -0.7496 -0.3932
+vn 0.6268 -0.6369 -0.4488
+vn 0.5324 -0.7496 -0.3933
+vn 0.7738 -0.3280 -0.5419
+vn 0.3945 -0.6617 -0.6376
+vn 0.5817 0.1070 -0.8064
+vn 0.7632 0.4515 -0.4623
+vn 0.1738 0.9808 -0.0884
+vn -0.1696 0.9756 0.1393
+vn -0.1601 0.9512 0.2636
+vn -0.3253 0.9170 0.2310
+vn -0.4178 0.8628 0.2847
+vn -0.4177 0.8628 0.2847
+vn -0.4779 0.8161 0.3250
+vn -0.2392 0.8916 0.3844
+vn -0.5013 0.7943 0.3432
+vn -0.7124 0.6566 0.2479
+vn -0.7474 0.6109 0.2612
+vn -0.6202 0.7535 0.2183
+vn -0.7124 0.6566 0.2478
+vn -0.4772 0.8605 0.1785
+vn -0.2479 0.9623 0.1121
+vn -0.4772 0.8605 0.1786
+vn 0.1456 0.9892 -0.0179
+vn 0.6600 0.7222 -0.2072
+vn 0.9244 0.1654 -0.3437
+vn 0.7631 0.4515 -0.4623
+vn 0.8916 -0.2892 -0.3485
+vn 0.8121 -0.4960 -0.3073
+vn 0.5562 -0.7211 -0.4131
+vn 0.8144 -0.4914 -0.3087
+vn 0.8237 -0.3975 -0.4043
+vn 0.5562 -0.7212 -0.4131
+vn 0.8176 -0.2303 -0.5277
+vn 0.8237 -0.3975 -0.4044
+vn 0.8176 -0.2304 -0.5277
+vn 0.7561 -0.1331 -0.6408
+vn 0.8177 -0.2303 -0.5276
+vn 0.6848 -0.0992 -0.7220
+vn 0.6848 -0.0992 -0.7219
+vn 0.6416 -0.1307 -0.7558
+vn 0.4475 -0.2066 -0.8701
+vn 0.6449 -0.2261 -0.7301
+vn 0.6855 -0.3662 -0.6293
+vn 0.6448 -0.2261 -0.7301
+vn 0.7060 -0.5136 -0.4876
+vn 0.4482 -0.5054 -0.7373
+vn 0.7919 -0.5655 -0.2306
+vn 0.4895 -0.6785 -0.5478
+vn 0.9142 -0.2527 -0.3169
+vn 0.9142 -0.2527 -0.3170
+vn 0.8792 -0.1591 -0.4490
+vn 0.8485 -0.0598 -0.5258
+vn 0.8464 0.0128 -0.5323
+vn 0.8464 0.0128 -0.5324
+vn 0.8773 0.0427 -0.4781
+vn 0.9217 0.0289 -0.3867
+vn 0.9218 0.0289 -0.3867
+vn 0.9218 0.0290 -0.3867
+vn 0.9568 -0.0272 -0.2896
+vn 0.9568 -0.0271 -0.2896
+vn 0.8237 -0.3976 -0.4043
+vn 0.9671 -0.1416 -0.2115
+vn 0.9671 -0.1417 -0.2115
+vn 0.9694 -0.1911 -0.1542
+vn 0.9757 -0.1461 -0.1631
+vn 0.8122 -0.4959 -0.3072
+vn 0.9806 0.1181 -0.1566
+vn 0.8727 0.4714 -0.1274
+vn 0.5611 0.8254 -0.0627
+vn 0.6600 0.7221 -0.2072
+vn 0.1013 0.9947 0.0175
+vn -0.3158 0.9457 0.0770
+vn -0.6055 0.7883 0.1096
+vn -0.6055 0.7882 0.1096
+vn -0.7851 0.6064 0.1262
+vn -0.8882 0.4385 0.1369
+vn -0.8882 0.4385 0.1370
+vn -0.9222 0.3600 0.1415
+vn -0.9807 0.1953 0.0031
+vn -0.9221 0.3601 0.1415
+vn -0.9961 0.0881 0.0002
+vn -0.9003 0.4351 0.0115
+vn -0.7093 0.7045 0.0231
+vn -0.9003 0.4350 0.0115
+vn -0.3746 0.9266 0.0325
+vn -0.7093 0.7045 0.0232
+vn 0.0569 0.9978 0.0329
+vn -0.3746 0.9266 0.0326
+vn 0.5611 0.8254 -0.0628
+vn 0.4618 0.8867 0.0237
+vn 0.8726 0.4715 -0.1274
+vn 0.7486 0.6629 0.0118
+vn 0.9101 0.4144 0.0034
+vn 0.9822 0.1880 -0.0001
+vn 0.9694 -0.1911 -0.1541
+vn 0.9947 0.1010 0.0185
+vn 0.9969 0.0744 -0.0239
+vn 0.9947 0.1011 0.0185
+vn 0.9889 0.1383 -0.0551
+vn 0.9796 0.1843 -0.0806
+vn 0.9889 0.1383 -0.0550
+vn 0.9217 0.0290 -0.3867
+vn 0.9705 0.1930 -0.1444
+vn 0.9629 0.1818 -0.1995
+vn 0.9705 0.1929 -0.1444
+vn 0.9638 0.1475 -0.2219
+vn 0.9751 0.0972 -0.1994
+vn 0.9638 0.1476 -0.2219
+vn 0.8792 -0.1592 -0.4490
+vn 0.9889 0.0469 -0.1412
+vn 0.9352 0.3511 0.0455
+vn 0.9352 0.3511 0.0454
+vn 0.9751 0.0973 -0.1994
+vn 0.9298 0.3579 0.0863
+vn 0.9380 0.3279 0.1123
+vn 0.9629 0.1819 -0.1995
+vn 0.9355 0.3150 0.1601
+vn 0.9355 0.3150 0.1600
+vn 0.9334 0.3016 0.1946
+vn 0.9334 0.3015 0.1946
+vn 0.9106 0.3120 0.2711
+vn 0.9165 0.3111 0.2513
+vn 0.9341 0.2959 0.1996
+vn 0.9165 0.3112 0.2513
+vn 0.9342 0.2958 0.1995
+vn 0.9225 0.3379 0.1867
+vn 0.8846 0.4418 0.1492
+vn 0.9225 0.3379 0.1868
+vn 0.7839 0.6068 0.1316
+vn 0.6238 0.7737 0.1104
+vn 0.3770 0.9227 0.0809
+vn 0.6238 0.7738 0.1104
+vn 0.4618 0.8867 0.0236
+vn 0.0160 0.9992 0.0356
+vn 0.3769 0.9227 0.0809
+vn 0.0161 0.9992 0.0356
+vn -0.4246 0.9050 -0.0276
+vn -0.4245 0.9050 -0.0276
+vn -0.7890 0.6078 -0.0901
+vn -0.9605 0.2464 -0.1293
+vn -0.9882 -0.0464 -0.1462
+vn -0.9744 -0.1675 -0.1499
+vn -0.9091 -0.2796 -0.3087
+vn -0.8403 -0.4284 -0.3322
+vn -0.9507 0.0371 -0.3080
+vn -0.9882 -0.0464 -0.1463
+vn -0.8335 0.4917 -0.2520
+vn -0.4581 0.8811 -0.1170
+vn -0.4582 0.8811 -0.1170
+vn -0.0182 0.9995 0.0275
+vn -0.0183 0.9995 0.0275
+vn 0.2960 0.9468 0.1260
+vn 0.2960 0.9468 0.1261
+vn 0.4979 0.8459 0.1911
+vn 0.7839 0.6068 0.1317
+vn 0.6338 0.7353 0.2400
+vn 0.8846 0.4419 0.1492
+vn 0.7186 0.6278 0.2992
+vn 0.7186 0.6278 0.2991
+vn 0.7777 0.5233 0.3484
+vn 0.8034 0.4419 0.3991
+vn 0.7777 0.5232 0.3484
+vn 0.7878 0.3928 0.4745
+vn 0.7877 0.3928 0.4745
+vn 0.7609 0.3654 0.5362
+vn 0.7877 0.3928 0.4746
+vn 0.7359 0.3704 0.5668
+vn 0.7010 0.4239 0.5735
+vn 0.7353 0.4841 0.4743
+vn 0.7267 0.5861 0.3582
+vn 0.7626 0.6110 0.2123
+vn 0.9298 0.3578 0.0863
+vn 0.7267 0.5862 0.3582
+vn 0.7626 0.6110 0.2122
+vn 0.5133 0.7908 0.3334
+vn 0.7627 0.6110 0.2123
+vn 0.4938 0.6858 0.5347
+vn 0.4957 0.5320 0.6865
+vn 0.4626 0.4305 0.7750
+vn 0.4812 0.3704 0.7945
+vn 0.5234 0.3776 0.7639
+vn 0.5715 0.4407 0.6922
+vn 0.5991 0.5438 0.5876
+vn 0.5866 0.6535 0.4783
+vn 0.5305 0.7472 0.4002
+vn 0.4532 0.8258 0.3358
+vn 0.3547 0.8974 0.2625
+vn 0.2080 0.9641 0.1650
+vn 0.3548 0.8973 0.2625
+vn 0.2959 0.9469 0.1261
+vn -0.0383 0.9992 0.0084
+vn -0.4436 0.8588 -0.2563
+vn -0.4436 0.8588 -0.2564
+vn -0.7931 0.3510 -0.4978
+vn -0.8221 -0.1952 -0.5349
+vn -0.8221 -0.1952 -0.5348
+vn -0.7342 -0.4927 -0.4671
+vn -0.6642 -0.5831 -0.4678
+vn -0.4009 -0.6562 -0.6393
+vn -0.3594 -0.7246 -0.5880
+vn -0.4952 -0.4107 -0.7656
+vn -0.7342 -0.4927 -0.4670
+vn -0.4009 -0.6561 -0.6393
+vn -0.5365 0.2067 -0.8182
+vn -0.3015 0.8462 -0.4393
+vn -0.5365 0.2066 -0.8182
+vn -0.3016 0.8462 -0.4393
+vn -0.0317 0.9994 -0.0160
+vn 0.1069 0.9750 0.1946
+vn 0.1840 0.9303 0.3174
+vn 0.4531 0.8258 0.3358
+vn 0.2348 0.8822 0.4081
+vn 0.5305 0.7473 0.4002
+vn 0.2347 0.8822 0.4081
+vn 0.2719 0.8305 0.4862
+vn 0.2838 0.7380 0.6122
+vn 0.3204 0.6028 0.7308
+vn 0.2838 0.7381 0.6121
+vn 0.3204 0.6028 0.7307
+vn 0.2972 0.4527 0.8407
+vn 0.2582 0.3613 0.8960
+vn 0.2583 0.3613 0.8960
+vn 0.2521 0.3464 0.9036
+vn 0.2364 0.4063 0.8826
+vn 0.2393 0.5448 0.8037
+vn 0.2332 0.7406 0.6301
+vn 0.2462 0.8817 0.4024
+vn 0.4937 0.6858 0.5347
+vn -0.0260 0.9854 0.1681
+vn 0.2461 0.8818 0.4024
+vn -0.0000 0.9066 0.4219
+vn -0.2220 0.9716 0.0817
+vn -0.2894 0.9432 0.1633
+vn -0.2758 0.8843 0.3767
+vn -0.2332 0.7407 0.6301
+vn -0.0000 0.7538 0.6571
+vn -0.2158 0.5612 0.7991
+vn -0.0000 0.7538 0.6572
+vn -0.0000 0.5625 0.8268
+vn -0.2315 0.4270 0.8741
+vn -0.0000 0.4087 0.9127
+vn -0.2180 0.3453 0.9128
+vn 0.0233 0.3315 0.9432
+vn 0.0067 0.3499 0.9368
+vn 0.0068 0.3499 0.9368
+vn -0.2462 0.3618 0.8992
+vn 0.0083 0.4522 0.8919
+vn -0.2462 0.3619 0.8991
+vn -0.2837 0.4570 0.8430
+vn 0.0063 0.6223 0.7828
+vn -0.2837 0.4569 0.8431
+vn 0.0084 0.4522 0.8919
+vn 0.0063 0.6223 0.7827
+vn -0.3102 0.6105 0.7288
+vn -0.2837 0.4569 0.8430
+vn -0.0282 0.7631 0.6457
+vn -0.3401 0.7317 0.5907
+vn -0.3103 0.6105 0.7288
+vn -0.0000 0.8553 0.5182
+vn -0.0000 0.9003 0.4353
+vn -0.2719 0.8305 0.4862
+vn -0.0000 0.9410 0.3385
+vn -0.2348 0.8822 0.4081
+vn -0.0000 0.9786 0.2060
+vn -0.1840 0.9303 0.3174
+vn -0.0000 0.9996 -0.0281
+vn -0.1069 0.9751 0.1945
+vn -0.0000 0.8429 -0.5381
+vn 0.0317 0.9994 -0.0160
+vn 0.5365 0.2066 -0.8182
+vn 0.0001 0.8428 -0.5382
+vn -0.0001 0.1364 -0.9907
+vn 0.4951 -0.4107 -0.7656
+vn -0.0000 -0.5015 -0.8652
+vn 0.4009 -0.6561 -0.6393
+vn -0.0000 -0.7160 -0.6981
+vn 0.3595 -0.7246 -0.5880
+vn -0.0000 -0.7717 -0.6360
+vn 0.7063 -0.5578 -0.4359
+vn 0.4009 -0.6562 -0.6393
+vn 0.3594 -0.7246 -0.5880
+vn 0.8880 -0.3230 -0.3272
+vn 0.7262 -0.4845 -0.4877
+vn 0.8588 -0.4097 -0.3075
+vn 0.8881 -0.3230 -0.3271
+vn 0.9744 -0.1675 -0.1499
+vn 0.8589 -0.4096 -0.3075
+vn 0.9961 0.0881 0.0002
+vn 0.9882 -0.0464 -0.1462
+vn 0.9222 0.3595 0.1428
+vn 0.9807 0.1953 0.0031
+vn 0.7474 0.6109 0.2612
+vn 0.8882 0.4385 0.1370
+vn 0.9222 0.3600 0.1415
+vn 0.5013 0.7943 0.3432
+vn 0.7124 0.6566 0.2479
+vn 0.2392 0.8916 0.3844
+vn 0.4779 0.8161 0.3250
+vn 0.4177 0.8628 0.2847
+vn 0.3253 0.9170 0.2310
+vn 0.4178 0.8628 0.2847
+vn 0.1696 0.9756 0.1393
+vn -0.1738 0.9808 -0.0884
+vn -0.5221 0.2351 -0.8199
+vn -0.7651 0.3211 -0.5581
+vn -0.7651 0.3211 -0.5582
+vn -0.7651 0.3210 -0.5582
+vn -0.8254 -0.1998 -0.5279
+vn -0.8254 -0.1999 -0.5279
+vn -0.6831 -0.5765 -0.4485
+vn -0.5324 -0.7496 -0.3933
+vn -0.4973 -0.7561 -0.4256
+vn -0.4972 -0.7561 -0.4256
+vn -0.4972 -0.7561 -0.4255
+vn -0.5731 -0.6047 -0.5530
+vn -0.5799 -0.4015 -0.7089
+vn -0.5798 -0.4015 -0.7089
+vn -0.5216 -0.2618 -0.8121
+vn -0.5216 -0.2618 -0.8120
+vn -0.5215 -0.2618 -0.8121
+vn -0.4555 -0.2033 -0.8667
+vn -0.4159 -0.2297 -0.8799
+vn -0.4160 -0.2296 -0.8799
+vn -0.4151 -0.3384 -0.8445
+vn -0.4672 -0.4779 -0.7439
+vn -0.5116 -0.6498 -0.5622
+vn -0.4982 -0.8108 -0.3072
+vn -0.4983 -0.8108 -0.3072
+vn -0.5825 -0.7868 -0.2040
+vn -0.7488 -0.6162 -0.2440
+vn -0.7489 -0.6161 -0.2440
+vn -0.7509 -0.4730 -0.4608
+vn -0.6854 -0.3721 -0.6259
+vn -0.5115 -0.6499 -0.5622
+vn -0.7509 -0.4731 -0.4608
+vn -0.6458 -0.2298 -0.7281
+vn -0.6442 -0.1312 -0.7535
+vn -0.6442 -0.1311 -0.7535
+vn -0.4159 -0.2296 -0.8799
+vn -0.4152 -0.3384 -0.8445
+vn -0.6874 -0.0967 -0.7198
+vn -0.7563 -0.1280 -0.6415
+vn -0.5216 -0.2617 -0.8121
+vn -0.8158 -0.2237 -0.5333
+vn -0.7564 -0.1280 -0.6415
+vn -0.8158 -0.2236 -0.5333
+vn -0.8219 -0.3916 -0.4136
+vn -0.8220 -0.3916 -0.4136
+vn -0.7650 -0.5494 -0.3359
+vn -0.8121 -0.4960 -0.3073
+vn -0.4973 -0.7561 -0.4255
+vn -0.9042 -0.2680 -0.3326
+vn -0.5324 -0.7496 -0.3932
+vn -0.9333 0.0494 -0.3557
+vn -0.6831 -0.5764 -0.4485
+vn -0.7383 0.6208 -0.2636
+vn -0.7383 0.6209 -0.2636
+vn -0.8255 -0.1998 -0.5279
+vn -0.1456 0.9892 -0.0179
+vn -0.7384 0.6208 -0.2635
+vn 0.2479 0.9623 0.1121
+vn 0.4772 0.8605 0.1786
+vn 0.4772 0.8605 0.1785
+vn 0.6202 0.7535 0.2183
+vn 0.7124 0.6566 0.2478
+vn 0.7851 0.6063 0.1262
+vn 0.8882 0.4385 0.1369
+vn 0.6202 0.7535 0.2182
+vn 0.6055 0.7882 0.1096
+vn 0.7851 0.6064 0.1262
+vn 0.4771 0.8605 0.1786
+vn 0.3158 0.9457 0.0770
+vn 0.6055 0.7883 0.1096
+vn -0.1013 0.9947 0.0175
+vn -0.5611 0.8254 -0.0627
+vn -0.8727 0.4714 -0.1274
+vn -0.9042 -0.2679 -0.3325
+vn -0.9806 0.1181 -0.1566
+vn -0.8122 -0.4959 -0.3072
+vn -0.9757 -0.1461 -0.1631
+vn -0.9469 -0.2578 -0.1923
+vn -0.9469 -0.2578 -0.1924
+vn -0.9651 -0.1353 -0.2242
+vn -0.8220 -0.3916 -0.4135
+vn -0.9527 -0.0241 -0.3030
+vn -0.9173 0.0301 -0.3970
+vn -0.9173 0.0302 -0.3970
+vn -0.9174 0.0301 -0.3969
+vn -0.6874 -0.0967 -0.7199
+vn -0.8747 0.0418 -0.4829
+vn -0.8464 0.0089 -0.5325
+vn -0.8464 0.0090 -0.5325
+vn -0.8502 -0.0667 -0.5223
+vn -0.8502 -0.0667 -0.5222
+vn -0.8817 -0.1672 -0.4413
+vn -0.9259 -0.2201 -0.3070
+vn -0.9380 -0.3077 -0.1595
+vn -0.9381 -0.3076 -0.1595
+vn -0.7489 -0.6162 -0.2440
+vn -0.8154 -0.5597 -0.1476
+vn -0.8154 -0.5597 -0.1477
+vn -0.9439 -0.3171 -0.0927
+vn -0.9981 0.0577 -0.0197
+vn -0.9972 0.0484 -0.0566
+vn -0.9912 0.0420 -0.1257
+vn -0.9786 0.0878 -0.1862
+vn -0.9786 0.0878 -0.1863
+vn -0.8817 -0.1672 -0.4412
+vn -0.9666 0.1371 -0.2163
+vn -0.9786 0.0877 -0.1862
+vn -0.9634 0.1737 -0.2043
+vn -0.9667 0.1371 -0.2163
+vn -0.9634 0.1737 -0.2042
+vn -0.9692 0.1883 -0.1585
+vn -0.9692 0.1884 -0.1585
+vn -0.9762 0.1687 -0.1366
+vn -0.9174 0.0302 -0.3969
+vn -0.9866 0.1425 -0.0799
+vn -0.9943 0.1051 -0.0182
+vn -0.9988 0.0443 -0.0219
+vn -0.9822 0.1880 -0.0001
+vn -0.9468 -0.2578 -0.1924
+vn -0.9988 0.0444 -0.0219
+vn -0.9101 0.4144 0.0034
+vn -0.7486 0.6629 0.0118
+vn -0.4618 0.8867 0.0237
+vn -0.8726 0.4715 -0.1274
+vn -0.0569 0.9978 0.0329
+vn -0.5611 0.8254 -0.0628
+vn 0.3746 0.9266 0.0326
+vn 0.3746 0.9266 0.0325
+vn 0.7093 0.7045 0.0232
+vn 0.7093 0.7045 0.0231
+vn 0.9003 0.4350 0.0115
+vn 0.9003 0.4351 0.0115
+vn 0.9605 0.2464 -0.1293
+vn 0.7890 0.6078 -0.0901
+vn 0.4245 0.9050 -0.0276
+vn -0.0161 0.9992 0.0356
+vn 0.4246 0.9050 -0.0276
+vn -0.0160 0.9992 0.0356
+vn -0.4618 0.8867 0.0236
+vn -0.3769 0.9227 0.0809
+vn -0.3770 0.9227 0.0809
+vn -0.6238 0.7738 0.1104
+vn -0.6238 0.7737 0.1104
+vn -0.7839 0.6068 0.1316
+vn -0.8846 0.4418 0.1492
+vn -0.9431 0.2977 0.1482
+vn -0.9431 0.2976 0.1482
+vn -0.9943 0.1051 -0.0183
+vn -0.9268 0.3147 0.2049
+vn -0.9214 0.3104 0.2339
+vn -0.9214 0.3104 0.2338
+vn -0.9275 0.3005 0.2223
+vn -0.9262 0.3077 0.2179
+vn -0.9388 0.3123 0.1453
+vn -0.9413 0.3219 0.1020
+vn -0.9786 0.0877 -0.1863
+vn -0.9440 0.3237 0.0642
+vn -0.9364 0.3454 0.0629
+vn -0.9363 0.3454 0.0628
+vn -0.9363 0.3454 0.0629
+vn -0.9413 0.3354 0.0386
+vn -0.9413 0.3355 0.0386
+vn -0.9413 0.3355 0.0385
+vn -0.9256 0.3773 0.0306
+vn -0.7765 0.6196 0.1149
+vn -0.7635 0.6043 0.2277
+vn -0.9413 0.3354 0.0385
+vn -0.7743 0.5450 0.3217
+vn -0.7636 0.6043 0.2277
+vn -0.7743 0.5450 0.3216
+vn -0.7096 0.4815 0.5144
+vn -0.7055 0.4069 0.5803
+vn -0.7185 0.3683 0.5900
+vn -0.7662 0.3671 0.5274
+vn -0.7921 0.3971 0.4636
+vn -0.8048 0.4488 0.3885
+vn -0.8048 0.4487 0.3885
+vn -0.7913 0.5199 0.3218
+vn -0.7288 0.6251 0.2795
+vn -0.6338 0.7353 0.2400
+vn -0.8846 0.4419 0.1492
+vn -0.7288 0.6251 0.2794
+vn -0.4979 0.8459 0.1911
+vn -0.7839 0.6068 0.1317
+vn -0.2960 0.9468 0.1261
+vn 0.0183 0.9995 0.0275
+vn -0.2960 0.9468 0.1260
+vn 0.4581 0.8812 -0.1170
+vn 0.0182 0.9995 0.0275
+vn 0.8335 0.4917 -0.2520
+vn 0.9507 0.0371 -0.3080
+vn 0.9882 -0.0464 -0.1463
+vn 0.8221 -0.1952 -0.5348
+vn 0.7931 0.3510 -0.4978
+vn 0.8221 -0.1952 -0.5349
+vn 0.4436 0.8588 -0.2564
+vn 0.4436 0.8588 -0.2563
+vn 0.0383 0.9992 0.0084
+vn -0.2959 0.9469 0.1261
+vn -0.2080 0.9641 0.1650
+vn -0.3548 0.8973 0.2625
+vn -0.3547 0.8974 0.2625
+vn -0.4532 0.8258 0.3358
+vn -0.5560 0.7320 0.3937
+vn -0.6132 0.6363 0.4680
+vn -0.6133 0.6363 0.4680
+vn -0.5962 0.5536 0.5814
+vn -0.5962 0.5536 0.5815
+vn -0.7920 0.3971 0.4636
+vn -0.5688 0.4470 0.6904
+vn -0.7920 0.3971 0.4637
+vn -0.5199 0.3796 0.7652
+vn -0.5200 0.3796 0.7652
+vn -0.4785 0.3688 0.7969
+vn -0.4622 0.4261 0.7777
+vn -0.4420 0.5331 0.7214
+vn -0.5242 0.6865 0.5039
+vn -0.5440 0.7786 0.3128
+vn -0.5337 0.8297 0.1636
+vn -0.5338 0.8296 0.1637
+vn -0.7508 0.6575 0.0630
+vn -0.5338 0.8296 0.1636
+vn -0.5001 0.8624 0.0780
+vn -0.2894 0.9432 0.1632
+vn -0.5002 0.8624 0.0780
+vn -0.2758 0.8844 0.3766
+vn -0.2757 0.8844 0.3766
+vn -0.2332 0.7406 0.6301
+vn -0.2314 0.4270 0.8741
+vn -0.2179 0.3453 0.9128
+vn -0.2462 0.3618 0.8991
+vn -0.5200 0.3795 0.7652
+vn -0.3102 0.6104 0.7288
+vn -0.2347 0.8822 0.4081
+vn -0.4531 0.8258 0.3358
+vn -0.1069 0.9750 0.1946
+vn -0.1070 0.9750 0.1946
+vn 0.3016 0.8462 -0.4393
+vn 0.3015 0.8462 -0.4393
+vn 0.5365 0.2067 -0.8182
+vn 0.4952 -0.4107 -0.7656
+vn -0.3595 -0.7246 -0.5880
+vn 0.0001 0.1364 -0.9907
+vn -0.4951 -0.4107 -0.7656
+vn -0.0001 0.8428 -0.5382
+vn 0.1069 0.9751 0.1945
+vn -0.0281 0.7631 0.6456
+vn 0.3205 0.6028 0.7307
+vn 0.2973 0.4527 0.8407
+vn 0.0234 0.3315 0.9432
+vn 0.2332 0.7407 0.6301
+vn -0.2884 -0.3697 0.8833
+vn -0.5469 -0.3704 0.7508
+vn -0.8833 -0.3697 0.2883
+vn -0.9469 -0.2842 -0.1504
+vn -0.8833 -0.3697 -0.2884
+vn -0.2883 -0.3697 -0.8833
+vn 0.2884 -0.3697 -0.8833
+vn 0.5469 -0.3704 -0.7508
+vn 0.8833 -0.3697 -0.2883
+vn 0.9469 -0.2842 0.1504
+vn 0.8833 -0.3697 0.2884
+vn 0.2883 -0.3697 0.8833
+vn -0.8072 -0.0528 0.5879
+vn -0.8534 -0.2848 0.4367
+vn -0.5879 -0.0528 -0.8072
+vn -0.4367 -0.2848 -0.8534
+vn -0.4549 -0.0528 -0.8890
+vn 0.4549 -0.0529 -0.8890
+vn 0.8072 -0.0528 -0.5879
+vn 0.8534 -0.2848 -0.4367
+vn 0.5879 -0.0528 0.8072
+vn 0.4367 -0.2848 0.8534
+vn 0.1477 0.3367 0.9300
+vn -0.1477 0.3367 0.9299
+vn -0.4549 -0.0529 0.8890
+vn -0.4288 0.3374 0.8380
+vn -0.8950 0.3371 0.2921
+vn -0.8890 -0.0529 -0.4549
+vn -0.8381 0.3373 -0.4288
+vn -0.4288 0.3373 -0.8380
+vn -0.2921 0.3371 -0.8950
+vn -0.1477 0.3367 -0.9300
+vn 0.1477 0.3367 -0.9299
+vn 0.4288 0.3374 -0.8380
+vn 0.8950 0.3371 -0.2921
+vn 0.8890 -0.0529 0.4549
+vn 0.8381 0.3373 0.4288
+vn 0.4288 0.3373 0.8380
+vn 0.2921 0.3371 0.8950
+vn 0.4289 0.3374 0.8380
+vn 0.2921 0.3370 0.8950
+vn -0.0931 0.8046 0.5864
+vn -0.1841 0.8049 0.5641
+vn -0.2702 0.8052 0.5280
+vn -0.5542 0.3375 0.7609
+vn -0.4191 0.8054 0.4192
+vn -0.5279 0.8051 0.2702
+vn -0.8380 0.3374 0.4289
+vn -0.8950 0.3370 0.2921
+vn -0.5642 0.8049 -0.1841
+vn -0.4192 0.8053 -0.4192
+vn -0.2702 0.8052 -0.5279
+vn -0.4289 0.3374 -0.8380
+vn -0.2921 0.3370 -0.8950
+vn 0.0931 0.8046 -0.5864
+vn 0.1841 0.8049 -0.5641
+vn 0.2702 0.8052 -0.5280
+vn 0.5542 0.3375 -0.7609
+vn 0.4191 0.8054 -0.4192
+vn 0.5279 0.8051 -0.2702
+vn 0.8380 0.3374 -0.4289
+vn 0.8950 0.3370 -0.2921
+vn 0.5642 0.8049 0.1841
+vn 0.4192 0.8053 0.4192
+vn 0.2702 0.8052 0.5279
+vn 0.3491 0.8053 0.4792
+vn 0.0145 0.9995 0.0284
+vn -0.4792 0.8053 0.3491
+vn -0.4191 0.8054 -0.4191
+vn -0.3491 0.8053 -0.4792
+vn -0.0145 0.9995 -0.0284
+vn 0.4792 0.8053 -0.3491
+vn 0.4191 0.8054 0.4191
+vn -0.1182 0.9247 -0.3620
+vn 0.0050 0.9995 0.0314
+vn 0.1182 0.9246 -0.3620
+vn 0.1733 0.9248 -0.3388
+vn -0.0302 0.9995 0.0099
+vn 0.3388 0.9247 0.1734
+vn 0.2688 0.9249 0.2689
+vn 0.1182 0.9247 0.3620
+vn -0.0050 0.9995 -0.0314
+vn -0.1182 0.9246 0.3620
+vn -0.1733 0.9248 0.3388
+vn 0.0302 0.9995 -0.0099
+vn -0.3388 0.9247 -0.1734
+vn -0.2688 0.9249 -0.2689
+vn -0.1893 0.7923 -0.5801
+vn -0.0957 0.7920 -0.6030
+vn -0.0000 0.7917 -0.6109
+vn 0.2777 0.7926 -0.5427
+vn 0.2777 0.7927 -0.5427
+vn 0.2689 0.9249 -0.2688
+vn 0.5801 0.7923 -0.1893
+vn 0.6030 0.7920 -0.0957
+vn 0.6109 0.7917 -0.0000
+vn 0.5428 0.7926 0.2778
+vn 0.1893 0.7923 0.5801
+vn 0.0957 0.7920 0.6030
+vn -0.0000 0.7917 0.6109
+vn -0.2777 0.7926 0.5427
+vn -0.2777 0.7927 0.5427
+vn -0.2689 0.9249 0.2688
+vn -0.5801 0.7923 0.1893
+vn -0.6030 0.7920 0.0957
+vn -0.6109 0.7917 -0.0000
+vn -0.5428 0.7926 -0.2778
+vn -0.4308 0.7929 -0.4309
+vn -0.5278 0.6654 -0.5278
+vn -0.2778 0.7926 -0.5427
+vn 0.4395 0.6653 -0.6034
+vn 0.4308 0.7929 -0.4308
+vn 0.5427 0.7926 -0.2778
+vn 0.7103 0.6646 0.2319
+vn 0.7103 0.6647 0.2319
+vn 0.4308 0.7929 0.4309
+vn 0.5278 0.6654 0.5278
+vn 0.2778 0.7926 0.5427
+vn -0.4395 0.6653 0.6034
+vn -0.4309 0.7929 0.4308
+vn -0.5427 0.7926 0.2778
+vn -0.7103 0.6646 -0.2319
+vn -0.7103 0.6647 -0.2319
+vn -0.6772 0.5460 -0.4933
+vn -0.2319 0.6647 -0.7103
+vn 0.4933 0.5460 -0.6772
+vn 0.7102 0.6647 -0.2319
+vn 0.8282 0.5447 -0.1315
+vn 0.8282 0.5448 0.1315
+vn 0.6772 0.5460 0.4933
+vn 0.2319 0.6647 0.7103
+vn -0.4933 0.5460 0.6772
+vn -0.7102 0.6647 0.2319
+vn -0.8282 0.5447 0.1315
+vn -0.8282 0.5448 -0.1315
+vn -0.7460 0.5456 -0.3817
+vn -0.4932 0.5460 -0.6772
+vn -0.4932 0.5459 -0.6772
+vn -0.3818 0.5456 -0.7460
+vn 0.3817 0.5456 -0.7461
+vn 0.4049 0.4585 -0.7911
+vn 0.6772 0.5460 -0.4932
+vn 0.6772 0.5459 -0.4932
+vn 0.7461 0.5456 -0.3817
+vn 0.7969 0.5452 -0.2602
+vn 0.7460 0.5456 0.3817
+vn 0.4932 0.5460 0.6772
+vn 0.4932 0.5459 0.6772
+vn 0.3818 0.5456 0.7460
+vn -0.3817 0.5456 0.7461
+vn -0.4049 0.4585 0.7911
+vn -0.5924 0.5461 0.5924
+vn -0.6772 0.5460 0.4932
+vn -0.6772 0.5459 0.4932
+vn -0.7461 0.5456 0.3817
+vn -0.7969 0.5452 0.2602
+vn -0.2758 0.4581 -0.8450
+vn 0.8450 0.4581 -0.2758
+vn 0.2758 0.4581 0.8450
+vn -0.8450 0.4581 0.2758
+vn -0.8935 0.4261 0.1418
+vn -0.8598 0.4266 -0.2806
+vn -0.1418 0.4261 -0.8935
+vn 0.2806 0.4266 -0.8598
+vn 0.8935 0.4261 -0.1418
+vn 0.8598 0.4266 0.2806
+vn 0.4120 0.4270 0.8050
+vn 0.1418 0.4261 0.8935
+vn -0.2806 0.4266 0.8598
+vn -0.8152 0.4018 -0.4171
+vn -0.6423 0.4181 -0.6423
+vn 0.5390 0.4021 -0.7401
+vn 0.6423 0.4181 -0.6423
+vn 0.6423 0.4181 0.6423
+vn -0.6423 0.4181 0.6423
+vn 0.1437 0.4010 -0.9047
+vn -0.1437 0.4010 0.9047
+vn -0.9054 0.3047 -0.2955
+vn -0.8478 0.3050 -0.4339
+vn 0.2955 0.3047 -0.9054
+vn 0.4339 0.3050 -0.8478
+vn 0.9054 0.3047 0.2955
+vn 0.8478 0.3050 0.4339
+vn -0.1493 0.3044 0.9408
+vn -0.2955 0.3047 0.9054
+vn -0.4339 0.3050 0.8478
+vn -0.6734 0.3053 0.6733
+vn -0.9408 0.3043 0.1494
+vn -0.1494 0.3043 -0.9408
+vn 0.6734 0.3053 -0.6733
+vn 0.9408 0.3043 -0.1494
+vn 0.1494 0.3043 0.9408
+vn -0.8751 0.1837 0.4478
+vn -0.4478 0.1837 -0.8751
+vn 0.8751 0.1837 -0.4478
+vn 0.4478 0.1837 0.8751
+vn -0.5858 0.0995 0.8043
+vn -0.9827 0.0992 -0.1561
+vn -0.8043 0.0995 -0.5858
+vn 0.1561 0.0992 -0.9827
+vn 0.5858 0.0995 -0.8043
+vn 0.9827 0.0992 0.1561
+vn 0.8043 0.0995 0.5858
+vn -0.1561 0.0992 0.9827
+vn -0.4554 -0.0280 0.8898
+vn -0.5885 -0.0281 0.8080
+vn -0.9828 0.0993 0.1560
+vn -0.9872 -0.0280 -0.1568
+vn -0.8898 -0.0281 -0.4554
+vn -0.8898 -0.0280 -0.4554
+vn -0.8080 -0.0281 -0.5885
+vn 0.1568 -0.0279 -0.9872
+vn 0.4554 -0.0280 -0.8898
+vn 0.5885 -0.0281 -0.8080
+vn 0.9828 0.0993 -0.1560
+vn 0.9872 -0.0280 0.1568
+vn 0.8898 -0.0281 0.4554
+vn 0.8898 -0.0280 0.4554
+vn 0.8080 -0.0281 0.5885
+vn -0.1568 -0.0279 0.9872
+vn -0.1567 -0.0279 0.9872
+vn -0.9873 -0.0279 0.1567
+vn -0.9872 -0.0279 -0.1567
+vn -0.9872 -0.0279 -0.1568
+vn -0.9289 -0.2123 -0.3033
+vn -0.1567 -0.0279 -0.9873
+vn 0.1567 -0.0279 -0.9872
+vn 0.3033 -0.2123 -0.9289
+vn 0.9873 -0.0279 -0.1567
+vn 0.9872 -0.0279 0.1567
+vn 0.9872 -0.0279 0.1568
+vn 0.9289 -0.2123 0.3033
+vn 0.1567 -0.0279 0.9873
+vn -0.1533 -0.2121 0.9652
+vn -0.4451 -0.2125 0.8699
+vn -0.8720 -0.3982 0.2846
+vn -0.9652 -0.2121 -0.1533
+vn -0.8699 -0.2125 -0.4451
+vn -0.2846 -0.3982 -0.8720
+vn 0.1533 -0.2121 -0.9652
+vn 0.4451 -0.2125 -0.8699
+vn 0.8720 -0.3982 -0.2846
+vn 0.9652 -0.2121 0.1533
+vn 0.8699 -0.2125 0.4451
+vn 0.2846 -0.3982 0.8720
+vn -0.1438 -0.3978 0.9061
+vn -0.7480 -0.5423 0.3827
+vn -0.9061 -0.3978 -0.1438
+vn -0.7479 -0.5423 -0.3827
+vn -0.3827 -0.5423 -0.7480
+vn 0.1438 -0.3978 -0.9061
+vn 0.7480 -0.5423 -0.3827
+vn 0.9061 -0.3978 0.1438
+vn 0.7479 -0.5423 0.3827
+vn 0.3827 -0.5423 0.7480
+vn 0.2373 -0.6442 0.7271
+vn -0.4945 -0.5426 0.6790
+vn -0.7271 -0.6442 0.2373
+vn -0.6805 -0.6446 0.3483
+vn -0.6790 -0.5426 -0.4945
+vn -0.2373 -0.6442 -0.7271
+vn -0.3483 -0.6446 -0.6805
+vn 0.4945 -0.5426 -0.6790
+vn 0.7271 -0.6442 -0.2373
+vn 0.6805 -0.6446 -0.3483
+vn 0.6790 -0.5426 0.4945
+vn -0.3482 -0.6446 0.6806
+vn -0.4500 -0.6449 0.6178
+vn -0.6806 -0.6446 -0.3482
+vn -0.6241 -0.7130 -0.3194
+vn -0.6178 -0.6449 -0.4500
+vn -0.4500 -0.6449 -0.6177
+vn 0.3482 -0.6446 -0.6806
+vn 0.4500 -0.6449 -0.6178
+vn 0.6177 -0.6449 -0.4500
+vn 0.6806 -0.6446 0.3482
+vn 0.6241 -0.7130 0.3194
+vn 0.6178 -0.6449 0.4500
+vn 0.4500 -0.6449 0.6177
+vn 0.3864 -0.7545 0.5306
+vn -0.1100 -0.7122 0.6933
+vn -0.5306 -0.7545 0.3864
+vn -0.6933 -0.7122 -0.1100
+vn -0.3864 -0.7545 -0.5306
+vn 0.1100 -0.7122 -0.6933
+vn 0.5306 -0.7545 -0.3864
+vn 0.6933 -0.7122 0.1100
+vn -0.2064 -0.7465 0.6325
+vn -0.6325 -0.7465 -0.2064
+vn 0.2064 -0.7465 -0.6325
+vn 0.6325 -0.7465 0.2064
+vn 0.5659 -0.5997 0.5658
+vn 0.3647 -0.5994 0.7126
+vn -0.5658 -0.5997 0.5659
+vn -0.5659 -0.5997 -0.5658
+vn -0.3647 -0.5994 -0.7126
+vn 0.5658 -0.5997 -0.5659
+vn 0.7913 -0.5985 0.1256
+vn 0.9032 -0.3118 0.2949
+vn 0.7679 -0.3123 0.5593
+vn 0.4711 -0.5996 0.6469
+vn 0.2948 -0.3119 0.9032
+vn 0.2948 -0.3118 0.9032
+vn -0.1256 -0.5985 0.7913
+vn -0.2949 -0.3118 0.9032
+vn -0.7679 -0.3124 0.5593
+vn -0.7126 -0.5994 0.3647
+vn -0.9032 -0.3119 0.2948
+vn -0.9032 -0.3118 0.2948
+vn -0.7913 -0.5985 -0.1256
+vn -0.9032 -0.3118 -0.2949
+vn -0.7679 -0.3123 -0.5593
+vn -0.4711 -0.5996 -0.6469
+vn -0.2948 -0.3119 -0.9032
+vn -0.2948 -0.3118 -0.9032
+vn 0.1256 -0.5985 -0.7913
+vn 0.2949 -0.3118 -0.9032
+vn 0.7679 -0.3124 -0.5593
+vn 0.7126 -0.5994 -0.3647
+vn 0.9032 -0.3119 -0.2948
+vn 0.9032 -0.3118 -0.2948
+vn 0.2369 -0.9708 0.0376
+vn 0.6429 -0.7367 0.2098
+vn 0.1171 -0.9895 0.0853
+vn 0.6016 -0.7371 0.3079
+vn 0.1933 -0.9710 0.1407
+vn 0.1171 -0.9895 0.0852
+vn 0.0102 -0.9999 0.0075
+vn 0.1408 -0.9710 0.1932
+vn 0.3079 -0.7370 0.6016
+vn 0.1853 -0.9136 0.3620
+vn 0.0853 -0.9895 0.1170
+vn 0.0852 -0.9895 0.1170
+vn 0.1090 -0.9709 0.2130
+vn 0.0140 -0.9960 0.0880
+vn -0.0376 -0.9708 0.2369
+vn -0.0853 -0.9895 0.1171
+vn -0.3079 -0.7371 0.6016
+vn -0.5593 -0.3123 0.7679
+vn -0.1407 -0.9710 0.1933
+vn -0.0852 -0.9895 0.1171
+vn -0.0075 -0.9999 0.0102
+vn -0.0628 -0.9961 0.0628
+vn -0.8457 -0.3121 0.4328
+vn -0.6016 -0.7370 0.3079
+vn -0.3620 -0.9136 0.1853
+vn -0.1170 -0.9895 0.0853
+vn -0.1932 -0.9710 0.1407
+vn -0.1170 -0.9895 0.0852
+vn -0.2130 -0.9709 0.1090
+vn -0.6428 -0.7367 0.2099
+vn -0.8238 -0.3791 0.4216
+vn -0.9032 -0.3119 0.2949
+vn -0.6429 -0.7367 0.2099
+vn -0.0880 -0.9960 0.0140
+vn -0.2369 -0.9708 -0.0376
+vn -0.6429 -0.7367 -0.2098
+vn -0.1171 -0.9895 -0.0853
+vn -0.6016 -0.7371 -0.3079
+vn -0.1933 -0.9710 -0.1407
+vn -0.1171 -0.9895 -0.0852
+vn -0.0102 -0.9999 -0.0075
+vn -0.1408 -0.9710 -0.1932
+vn -0.3079 -0.7370 -0.6016
+vn -0.1853 -0.9136 -0.3620
+vn -0.0853 -0.9895 -0.1170
+vn -0.0852 -0.9895 -0.1170
+vn -0.1090 -0.9709 -0.2130
+vn 0.0376 -0.9708 -0.2369
+vn 0.0853 -0.9895 -0.1171
+vn 0.3079 -0.7371 -0.6016
+vn 0.5593 -0.3123 -0.7679
+vn 0.1407 -0.9710 -0.1933
+vn 0.0852 -0.9895 -0.1171
+vn 0.0075 -0.9999 -0.0102
+vn 0.0628 -0.9961 -0.0628
+vn 0.1932 -0.9710 -0.1408
+vn 0.8457 -0.3121 -0.4328
+vn 0.6016 -0.7370 -0.3079
+vn 0.3620 -0.9136 -0.1853
+vn 0.1170 -0.9895 -0.0853
+vn 0.1170 -0.9895 -0.0852
+vn 0.2130 -0.9709 -0.1090
+vn 0.6428 -0.7367 -0.2099
+vn 0.8238 -0.3791 -0.4216
+vn 0.9032 -0.3119 -0.2949
+vn 0.6429 -0.7367 -0.2099
+vn 0.0880 -0.9960 -0.0140
+vn 0.3628 0.9133 -0.1850
+vn 0.1106 0.9923 -0.0563
+vn 0.4032 0.9129 0.0636
+vn 0.0729 0.9923 0.1002
+vn -0.3628 0.9133 0.1850
+vn -0.1106 0.9923 0.0563
+vn -0.4032 0.9129 -0.0636
+vn -0.0729 0.9923 -0.1002
+vn 0.1388 0.4533 -0.8805
+vn 0.1259 0.9131 -0.3878
+vn 0.1849 0.9134 -0.3628
+vn 0.4048 0.4538 -0.7939
+vn 0.2752 0.4535 -0.8477
+vn 0.5239 0.4541 -0.7206
+vn 0.3291 0.9135 -0.2393
+vn 0.7938 0.4539 -0.4047
+vn 0.3627 0.9134 -0.1849
+vn 0.3877 0.9131 -0.1258
+vn 0.3878 0.9131 0.1259
+vn 0.3628 0.9134 0.1849
+vn 0.7939 0.4538 0.4048
+vn 0.8477 0.4535 0.2752
+vn 0.7206 0.4541 0.5239
+vn 0.2393 0.9135 0.3291
+vn 0.4047 0.4539 0.7938
+vn 0.1849 0.9134 0.3627
+vn -0.1388 0.4533 0.8805
+vn -0.1259 0.9131 0.3878
+vn -0.1849 0.9134 0.3628
+vn -0.4048 0.4538 0.7939
+vn -0.2752 0.4535 0.8477
+vn -0.5239 0.4541 0.7206
+vn -0.3291 0.9135 0.2393
+vn -0.7938 0.4539 0.4047
+vn -0.3627 0.9134 0.1849
+vn -0.3877 0.9131 0.1258
+vn -0.3878 0.9131 -0.1259
+vn -0.3628 0.9134 -0.1849
+vn -0.7939 0.4538 -0.4048
+vn -0.8477 0.4535 -0.2752
+vn -0.7206 0.4541 -0.5239
+vn -0.2393 0.9135 -0.3291
+vn -0.4047 0.4539 -0.7938
+vn -0.1849 0.9134 -0.3627
+vn 0.4047 0.4538 -0.7939
+vn 0.5655 -0.2746 -0.7777
+vn 0.7207 0.4541 -0.5239
+vn 0.8567 -0.2743 -0.4369
+vn 0.9148 -0.2738 -0.2970
+vn 0.7939 0.4538 0.4047
+vn 0.7777 -0.2746 0.5655
+vn 0.5239 0.4541 0.7207
+vn 0.2970 -0.2738 0.9148
+vn -0.2970 -0.2739 0.9148
+vn -0.4047 0.4538 0.7939
+vn -0.5655 -0.2746 0.7777
+vn -0.7207 0.4541 0.5239
+vn -0.8567 -0.2743 0.4369
+vn -0.9148 -0.2738 0.2970
+vn -0.7939 0.4538 -0.4047
+vn -0.7777 -0.2746 -0.5655
+vn -0.5239 0.4541 -0.7207
+vn -0.4369 -0.2743 -0.8567
+vn 0.9147 -0.2739 0.2970
+vn 0.4369 -0.2743 0.8567
+vn -0.9147 -0.2739 -0.2970
+vn 0.1162 -0.6661 -0.7368
+vn 0.7088 -0.6667 -0.2304
+vn 0.6634 -0.6673 -0.3385
+vn 0.7368 -0.6661 0.1162
+vn -0.1162 -0.6661 0.7368
+vn -0.7088 -0.6667 0.2304
+vn -0.6634 -0.6673 0.3385
+vn -0.7368 -0.6661 -0.1162
+vn -0.3385 -0.6673 -0.6634
+vn -0.1162 -0.6660 -0.7368
+vn 0.7368 -0.6660 -0.1162
+vn 0.3385 -0.6673 0.6634
+vn 0.1162 -0.6660 0.7368
+vn -0.7368 -0.6660 0.1162
+vn -0.6336 -0.6213 -0.4609
+vn -0.6487 -0.3979 -0.6488
+vn -0.1435 -0.3959 -0.9070
+vn 0.4609 -0.6213 -0.6336
+vn 0.6488 -0.3979 -0.6487
+vn 0.9070 -0.3959 -0.1435
+vn 0.6336 -0.6213 0.4609
+vn 0.6487 -0.3979 0.6488
+vn 0.1435 -0.3959 0.9070
+vn -0.4609 -0.6213 0.6336
+vn -0.6488 -0.3979 0.6487
+vn -0.9070 -0.3959 0.1435
+vn -0.8172 -0.3972 -0.4175
+vn -0.5782 0.1871 -0.7942
+vn -0.3045 0.1873 -0.9339
+vn -0.4473 0.1871 -0.8746
+vn -0.3046 0.1872 -0.9339
+vn 0.3045 0.1872 -0.9339
+vn 0.4175 -0.3972 -0.8172
+vn 0.7942 0.1871 -0.5782
+vn 0.9339 0.1873 -0.3045
+vn 0.8746 0.1871 -0.4473
+vn 0.9339 0.1872 -0.3046
+vn 0.8172 -0.3972 0.4175
+vn 0.5782 0.1871 0.7942
+vn 0.3045 0.1873 0.9339
+vn 0.4473 0.1871 0.8746
+vn 0.3046 0.1872 0.9339
+vn -0.3045 0.1872 0.9339
+vn -0.4175 -0.3972 0.8172
+vn -0.7942 0.1871 0.5782
+vn -0.9339 0.1873 0.3045
+vn -0.8746 0.1871 0.4473
+vn -0.9339 0.1872 0.3046
+vn -0.5377 0.7466 -0.3916
+vn -0.4472 0.1872 -0.8746
+vn -0.2065 0.7462 -0.6329
+vn -0.1044 0.7459 -0.6578
+vn 0.1044 0.7459 -0.6579
+vn 0.3046 0.1873 -0.9339
+vn 0.4472 0.1871 -0.8746
+vn 0.3917 0.7466 -0.5377
+vn 0.6329 0.7462 -0.2065
+vn 0.6578 0.7459 -0.1044
+vn 0.6579 0.7459 0.1044
+vn 0.6947 0.1870 0.6946
+vn 0.5377 0.7466 0.3916
+vn 0.4472 0.1872 0.8746
+vn 0.2065 0.7462 0.6329
+vn 0.1044 0.7459 0.6578
+vn -0.1044 0.7459 0.6579
+vn -0.3046 0.1873 0.9339
+vn -0.4472 0.1871 0.8746
+vn -0.3917 0.7466 0.5377
+vn -0.6329 0.7462 0.2065
+vn -0.6578 0.7459 0.1044
+vn -0.6579 0.7459 -0.1044
+vn -0.1699 0.9279 -0.3320
+vn -0.1159 0.9277 -0.3549
+vn 0.1738 0.9693 -0.1737
+vn 0.3013 0.9279 -0.2194
+vn 0.3320 0.9279 -0.1699
+vn 0.3550 0.9277 -0.1158
+vn 0.1699 0.9279 0.3320
+vn 0.1159 0.9277 0.3549
+vn -0.1738 0.9693 0.1737
+vn -0.3013 0.9279 0.2194
+vn -0.3320 0.9279 0.1699
+vn -0.3550 0.9277 0.1158
+vn -0.2434 0.9692 -0.0386
+vn -0.1753 0.9829 -0.0572
+vn 0.2434 0.9692 0.0386
+vn 0.1753 0.9829 0.0572
+vn -0.1500 0.9884 -0.0238
+vn -0.1374 0.9855 -0.1000
+vn -0.0528 0.9854 -0.1618
+vn 0.0238 0.9884 -0.1500
+vn 0.1000 0.9855 -0.1374
+vn 0.1500 0.9884 0.0238
+vn 0.1374 0.9855 0.1000
+vn 0.0528 0.9854 0.1618
+vn -0.0238 0.9884 0.1500
+vn -0.1000 0.9855 0.1374
+vn -0.1799 0.9749 -0.1310
+vn 0.1310 0.9749 -0.1799
+vn 0.1799 0.9749 0.1310
+vn -0.1310 0.9749 0.1799
+vn -0.6592 0.7447 -0.1046
+vn -0.6342 0.7450 -0.2070
+vn 0.2070 0.7450 -0.6342
+vn 0.6592 0.7447 0.1046
+vn 0.6342 0.7450 0.2070
+vn -0.2070 0.7450 0.6342
+vn -0.4993 0.5299 0.6855
+vn -0.5387 0.7456 0.3924
+vn -0.6592 0.7446 0.1047
+vn -0.6592 0.7447 -0.1047
+vn -0.6855 0.5299 -0.4993
+vn -0.3924 0.7456 -0.5387
+vn -0.1047 0.7446 -0.6592
+vn 0.4993 0.5299 -0.6855
+vn 0.5387 0.7456 -0.3924
+vn 0.6592 0.7446 -0.1047
+vn 0.6592 0.7447 0.1047
+vn 0.6855 0.5299 0.4993
+vn 0.3924 0.7456 0.5387
+vn 0.1047 0.7446 0.6592
+vn 0.6278 -0.7784 -0.0010
+vn 0.8805 -0.4740 -0.0006
+vn 0.6278 0.7784 0.0012
+vn 0.1704 0.9854 0.0016
+vn -0.0000 1.0000 0.0016
+vn -0.0000 1.0000 0.0070
+vn -0.1704 0.9854 0.0016
+vn -0.1699 0.9851 0.0246
+vn -0.0000 0.9229 0.3849
+vn -0.2240 -0.1534 0.9625
+vn -0.0000 -0.8493 0.5278
+vn -0.0000 -0.8994 0.4371
+vn 0.2240 -0.1534 0.9625
+vn 0.1699 0.9851 0.0246
+vn 0.1927 0.9066 0.3754
+vn 0.2450 0.5832 0.7745
+vn 0.2263 0.2692 0.9361
+vn 0.5050 0.2687 0.8203
+vn 0.2205 -0.4369 0.8720
+vn 0.3824 -0.8302 0.4055
+vn 0.5049 0.2688 0.8202
+vn 0.3961 0.8955 0.2032
+vn 0.3783 0.9240 0.0554
+vn 0.6281 0.7768 0.0454
+vn 0.6968 -0.3252 0.6394
+vn 0.6543 -0.6630 0.3639
+vn 0.6324 -0.6953 0.3416
+vn 1.0000 0.0002 -0.0007
+vn 0.8796 -0.4173 0.2283
+vn 1.0000 -0.0016 -0.0016
+vn 1.0000 -0.0005 -0.0000
+vn 1.0000 -0.0000 0.0002
+vn 1.0000 -0.0001 0.0002
+vn 0.8878 -0.4043 -0.2198
+vn 1.0000 -0.0030 0.0012
+vn 1.0000 -0.0018 0.0004
+vn 1.0000 -0.0005 -0.0003
+vn 1.0000 -0.0016 -0.0017
+vn 0.6277 0.6928 -0.3548
+vn 0.8799 -0.4746 -0.0236
+vn 0.6273 -0.7787 -0.0042
+vn 0.6277 0.6929 -0.3548
+vn 0.3462 0.8018 -0.4872
+vn 0.3764 -0.9263 -0.0172
+vn 0.2323 -0.6709 -0.7043
+vn 0.2209 -0.3830 -0.8969
+vn 0.2229 0.2664 -0.9377
+vn -0.6277 0.6929 -0.3548
+vn -0.6277 0.6928 -0.3548
+vn -1.0000 0.0002 -0.0007
+vn -0.6324 -0.6953 0.3416
+vn -0.3824 -0.8302 0.4055
+vn -0.2205 -0.4369 0.8720
+vn -0.2263 0.2692 0.9361
+vn -0.2209 0.5579 0.7999
+vn -0.6278 0.7784 0.0012
+vn -0.3961 0.8955 0.2032
+vn -0.4759 0.0209 0.8793
+vn -0.7239 -0.3015 0.6206
+vn -0.4941 -0.3959 0.7740
+vn -0.4750 -0.2706 0.8374
+vn -0.7124 -0.3855 0.5864
+vn -0.7069 0.2293 0.6691
+vn -0.6467 0.7449 0.1640
+vn -0.6281 0.7768 0.0454
+vn -1.0000 -0.0000 0.0002
+vn -0.9053 0.2737 0.3248
+vn -1.0000 -0.0005 -0.0000
+vn -1.0000 -0.0016 -0.0016
+vn -0.8631 -0.4190 0.2818
+vn -1.0000 -0.0016 -0.0017
+vn -1.0000 -0.0005 -0.0003
+vn -1.0000 -0.0018 0.0004
+vn -1.0000 -0.0030 0.0012
+vn -0.9029 -0.2682 -0.3359
+vn -1.0000 -0.0001 0.0002
+vn -0.8805 -0.4740 -0.0006
+vn -0.6278 -0.7784 -0.0010
+vn -0.8799 -0.4746 -0.0236
+vn -0.6273 -0.7787 -0.0042
+vn -0.3764 -0.9263 -0.0172
+vn -0.1887 0.7742 -0.6041
+vn -0.1991 0.7013 -0.6845
+vn -0.2379 0.4505 -0.8605
+vn -0.2229 0.2664 -0.9377
+vn -0.2116 -0.8819 -0.4214
+vn 0.2643 -0.9328 -0.2452
+vn -0.0000 -0.9673 -0.2536
+vn -0.0000 -0.9673 -0.2535
+vn -0.0000 -0.7946 -0.6072
+vn -0.4671 -0.5905 -0.6581
+vn -0.0000 0.1250 -0.9922
+vn -0.1357 0.9710 -0.1969
+vn -0.0000 0.9657 -0.2596
+vn -0.0000 0.1249 -0.9922
+vn -0.0000 0.9866 0.1632
+vn -0.2023 0.9249 0.3218
+vn -0.0000 0.9657 -0.2595
+vn 0.0221 -0.8811 -0.4723
+vn -0.0841 0.9840 0.1571
+vn -0.1607 0.9510 0.2641
+vn -0.4779 0.8160 0.3250
+vn -0.6202 0.7535 0.2182
+vn 0.1737 0.9808 -0.0884
+vn 0.8122 -0.4960 -0.3073
+vn 0.7918 -0.5656 -0.2305
+vn 0.8122 -0.4959 -0.3073
+vn 0.8726 0.4714 -0.1275
+vn 0.1013 0.9947 0.0174
+vn -0.2479 0.9623 0.1120
+vn -0.4771 0.8605 0.1785
+vn -0.4771 0.8605 0.1786
+vn -0.7851 0.6063 0.1262
+vn -0.8882 0.4386 0.1369
+vn -0.9221 0.3602 0.1413
+vn -0.3745 0.9267 0.0325
+vn -0.3158 0.9457 0.0769
+vn 0.8726 0.4714 -0.1274
+vn 0.7486 0.6629 0.0117
+vn 0.9757 -0.1460 -0.1631
+vn -0.3745 0.9266 0.0325
+vn -0.4581 0.8811 -0.1171
+vn -0.4245 0.9050 -0.0277
+vn 0.7010 0.4238 0.5735
+vn 0.6338 0.7353 0.2401
+vn -0.4581 0.8812 -0.1170
+vn -0.8335 0.4917 -0.2521
+vn -0.4000 -0.6575 -0.6385
+vn -0.7343 -0.4927 -0.4670
+vn -0.4435 0.8588 -0.2564
+vn -0.7931 0.3510 -0.4979
+vn -0.4435 0.8588 -0.2563
+vn 0.2462 0.8817 0.4025
+vn -0.0000 0.7537 0.6572
+vn 0.0317 0.9994 -0.0159
+vn -0.0000 0.8428 -0.5382
+vn -0.0000 0.1364 -0.9907
+vn -0.0000 -0.5015 -0.8651
+vn 0.7263 -0.4845 -0.4877
+vn 0.9221 0.3601 0.1415
+vn 0.8882 0.4386 0.1369
+vn 0.4779 0.8160 0.3250
+vn 0.0841 0.9840 0.1570
+vn -0.1738 0.9808 -0.0883
+vn -0.8254 -0.1998 -0.5280
+vn -0.4670 -0.5905 -0.6581
+vn -0.6831 -0.5764 -0.4484
+vn -0.2218 -0.8576 -0.4640
+vn -0.5116 -0.6498 -0.5621
+vn -0.8122 -0.4960 -0.3073
+vn 0.1696 0.9756 0.1394
+vn -0.1737 0.9808 -0.0884
+vn 0.4771 0.8605 0.1785
+vn 0.2479 0.9623 0.1120
+vn -0.1013 0.9947 0.0174
+vn -0.8726 0.4714 -0.1275
+vn -0.8122 -0.4959 -0.3073
+vn -0.9757 -0.1460 -0.1631
+vn -0.8726 0.4714 -0.1274
+vn 0.3158 0.9457 0.0769
+vn 0.3745 0.9267 0.0325
+vn -0.9413 0.3218 0.1020
+vn -0.9388 0.3123 0.1452
+vn 0.4581 0.8811 -0.1170
+vn 0.4245 0.9050 -0.0277
+vn 0.4581 0.8811 -0.1171
+vn 0.8880 -0.3231 -0.3272
+vn 0.8335 0.4917 -0.2521
+vn -0.6338 0.7353 0.2401
+vn 0.4435 0.8588 -0.2563
+vn 0.7931 0.3510 -0.4979
+vn 0.4435 0.8588 -0.2564
+vn -0.0317 0.9994 -0.0159
+vn 0.1070 0.9750 0.1945
+vn -0.0000 0.9785 0.2060
+vn 0.2522 0.3464 0.9036
+vt 0.383629 0.365722
+vt 0.385969 0.351732
+vt 0.385957 0.365559
+vt 0.383629 0.351548
+vt 0.388012 0.338328
+vt 0.385694 0.337784
+vt 0.392061 0.325497
+vt 0.389822 0.324587
+vt 0.398084 0.313470
+vt 0.395995 0.312198
+vt 0.406019 0.302551
+vt 0.414182 0.291288
+vt 0.404166 0.300942
+vt 0.425600 0.283455
+vt 0.415735 0.293179
+vt 0.438104 0.277531
+vt 0.426813 0.285543
+vt 0.451456 0.273570
+vt 0.438965 0.279749
+vt 0.465503 0.271596
+vt 0.451964 0.275860
+vt 0.479504 0.273934
+vt 0.465666 0.273922
+vt 0.479688 0.271596
+vt 0.492919 0.275975
+vt 0.493463 0.273659
+vt 0.505760 0.280021
+vt 0.506670 0.277784
+vt 0.517795 0.286040
+vt 0.519069 0.283952
+vt 0.528723 0.293968
+vt 0.539994 0.302126
+vt 0.530333 0.292117
+vt 0.547834 0.313534
+vt 0.538103 0.303677
+vt 0.553762 0.326028
+vt 0.545744 0.314747
+vt 0.557726 0.339371
+vt 0.551543 0.326889
+vt 0.559702 0.353406
+vt 0.555435 0.339878
+vt 0.557363 0.367397
+vt 0.557374 0.353569
+vt 0.559702 0.367580
+vt 0.555320 0.380801
+vt 0.557637 0.381344
+vt 0.551271 0.393632
+vt 0.553509 0.394542
+vt 0.545247 0.405658
+vt 0.547337 0.406930
+vt 0.537313 0.416577
+vt 0.529149 0.427840
+vt 0.539165 0.418186
+vt 0.517731 0.435674
+vt 0.527596 0.425950
+vt 0.505228 0.441597
+vt 0.516518 0.433585
+vt 0.491875 0.445558
+vt 0.504366 0.439379
+vt 0.477828 0.447533
+vt 0.491367 0.443268
+vt 0.463827 0.445195
+vt 0.477665 0.445207
+vt 0.463643 0.447533
+vt 0.450412 0.443153
+vt 0.449868 0.445469
+vt 0.437572 0.439107
+vt 0.436661 0.441344
+vt 0.425536 0.433088
+vt 0.424263 0.435176
+vt 0.414608 0.425160
+vt 0.403337 0.417003
+vt 0.412998 0.427011
+vt 0.395497 0.405594
+vt 0.405229 0.415452
+vt 0.389569 0.393100
+vt 0.397587 0.404381
+vt 0.385605 0.379758
+vt 0.391788 0.392239
+vt 0.387896 0.379250
+vt 0.389555 0.378883
+vt 0.387662 0.351864
+vt 0.387645 0.365441
+vt 0.389687 0.338721
+vt 0.393677 0.326154
+vt 0.399591 0.314389
+vt 0.407353 0.303710
+vt 0.416855 0.294542
+vt 0.427690 0.287050
+vt 0.439588 0.281352
+vt 0.452332 0.277517
+vt 0.479371 0.275626
+vt 0.465784 0.275609
+vt 0.492525 0.277649
+vt 0.505102 0.281636
+vt 0.516876 0.287546
+vt 0.527563 0.295301
+vt 0.536738 0.304796
+vt 0.544236 0.315623
+vt 0.549938 0.327512
+vt 0.553777 0.340245
+vt 0.555669 0.367264
+vt 0.555686 0.353687
+vt 0.553644 0.380408
+vt 0.549654 0.392974
+vt 0.543740 0.404740
+vt 0.535978 0.415419
+vt 0.526476 0.424586
+vt 0.515641 0.432078
+vt 0.503743 0.437776
+vt 0.490999 0.441611
+vt 0.463960 0.443503
+vt 0.477547 0.443520
+vt 0.450806 0.441479
+vt 0.438230 0.437492
+vt 0.426455 0.431582
+vt 0.415768 0.423827
+vt 0.406593 0.414332
+vt 0.399096 0.403505
+vt 0.393393 0.391617
+vt 0.390700 0.378629
+vt 0.394500 0.391187
+vt 0.388809 0.365360
+vt 0.388832 0.351957
+vt 0.390844 0.338992
+vt 0.394791 0.326608
+vt 0.400630 0.315023
+vt 0.417628 0.295481
+vt 0.408276 0.304511
+vt 0.428296 0.288090
+vt 0.440019 0.282459
+vt 0.452586 0.278662
+vt 0.465866 0.276772
+vt 0.479279 0.276795
+vt 0.492253 0.278805
+vt 0.504647 0.282749
+vt 0.516241 0.288583
+vt 0.535799 0.305568
+vt 0.526761 0.296223
+vt 0.543195 0.316228
+vt 0.548831 0.327942
+vt 0.552631 0.340500
+vt 0.554522 0.353769
+vt 0.554499 0.367171
+vt 0.552487 0.380136
+vt 0.548540 0.392520
+vt 0.542702 0.404105
+vt 0.525703 0.423647
+vt 0.535055 0.414617
+vt 0.515035 0.431038
+vt 0.503313 0.436670
+vt 0.490745 0.440467
+vt 0.477466 0.442356
+vt 0.464052 0.442334
+vt 0.451078 0.440323
+vt 0.438684 0.436379
+vt 0.427090 0.430545
+vt 0.407533 0.413560
+vt 0.416570 0.422905
+vt 0.400136 0.402900
+vt 0.395286 0.390884
+vt 0.400873 0.402473
+vt 0.391512 0.378451
+vt 0.389632 0.365302
+vt 0.389661 0.352020
+vt 0.391664 0.339182
+vt 0.395581 0.326929
+vt 0.401365 0.315471
+vt 0.418174 0.296146
+vt 0.408931 0.305080
+vt 0.428723 0.288827
+vt 0.440321 0.283243
+vt 0.452764 0.279473
+vt 0.465923 0.277594
+vt 0.479216 0.277623
+vt 0.492063 0.279624
+vt 0.504327 0.283539
+vt 0.515793 0.289318
+vt 0.535133 0.306114
+vt 0.526192 0.296878
+vt 0.542458 0.316655
+vt 0.548046 0.328244
+vt 0.551819 0.340678
+vt 0.553699 0.353826
+vt 0.553671 0.367108
+vt 0.551668 0.379946
+vt 0.547750 0.392200
+vt 0.541966 0.403657
+vt 0.525157 0.422982
+vt 0.534400 0.414048
+vt 0.514608 0.430301
+vt 0.503010 0.435885
+vt 0.490567 0.439655
+vt 0.477408 0.441534
+vt 0.464116 0.441506
+vt 0.451268 0.439504
+vt 0.439005 0.435590
+vt 0.427538 0.429810
+vt 0.408198 0.413014
+vt 0.417139 0.422250
+vt 0.401565 0.402075
+vt 0.408822 0.412503
+vt 0.396021 0.390603
+vt 0.392273 0.378286
+vt 0.390399 0.365248
+vt 0.390437 0.352077
+vt 0.392432 0.339358
+vt 0.396321 0.327227
+vt 0.402054 0.315890
+vt 0.418686 0.296770
+vt 0.409547 0.305615
+vt 0.429122 0.289518
+vt 0.440603 0.283979
+vt 0.452929 0.280233
+vt 0.465977 0.278361
+vt 0.479158 0.278399
+vt 0.491887 0.280392
+vt 0.504028 0.284278
+vt 0.515373 0.290007
+vt 0.534509 0.306626
+vt 0.525656 0.297494
+vt 0.541766 0.317054
+vt 0.547310 0.328525
+vt 0.551058 0.340843
+vt 0.552932 0.353880
+vt 0.552894 0.367051
+vt 0.550900 0.379770
+vt 0.547010 0.391901
+vt 0.541277 0.403238
+vt 0.524645 0.422359
+vt 0.533784 0.413513
+vt 0.514209 0.429610
+vt 0.502729 0.435150
+vt 0.490402 0.438895
+vt 0.477354 0.440767
+vt 0.464173 0.440730
+vt 0.451444 0.438737
+vt 0.439303 0.434850
+vt 0.427958 0.429122
+vt 0.417675 0.421634
+vt 0.409601 0.411864
+vt 0.418341 0.420868
+vt 0.402428 0.401577
+vt 0.396941 0.390251
+vt 0.393224 0.378079
+vt 0.391361 0.365181
+vt 0.391408 0.352150
+vt 0.393391 0.339579
+vt 0.397246 0.327600
+vt 0.402914 0.316414
+vt 0.419325 0.297548
+vt 0.410314 0.306281
+vt 0.429620 0.290380
+vt 0.440955 0.284898
+vt 0.453136 0.281184
+vt 0.466044 0.279322
+vt 0.479086 0.279368
+vt 0.491666 0.281351
+vt 0.503655 0.285202
+vt 0.514849 0.290866
+vt 0.533730 0.307264
+vt 0.524990 0.298260
+vt 0.540903 0.317552
+vt 0.546390 0.328877
+vt 0.550107 0.341049
+vt 0.551970 0.353947
+vt 0.551924 0.366979
+vt 0.549940 0.379549
+vt 0.546086 0.391528
+vt 0.540417 0.402714
+vt 0.524006 0.421580
+vt 0.533017 0.412847
+vt 0.513711 0.428748
+vt 0.502376 0.434231
+vt 0.490195 0.437945
+vt 0.477287 0.439806
+vt 0.464245 0.439760
+vt 0.451665 0.437778
+vt 0.439677 0.433926
+vt 0.428482 0.428262
+vt 0.429156 0.427160
+vt 0.410599 0.411044
+vt 0.419195 0.419887
+vt 0.403535 0.400934
+vt 0.398122 0.389796
+vt 0.394447 0.377811
+vt 0.392601 0.365094
+vt 0.392656 0.352245
+vt 0.394624 0.339866
+vt 0.398431 0.328082
+vt 0.404017 0.317087
+vt 0.420146 0.298545
+vt 0.411296 0.307134
+vt 0.430263 0.291487
+vt 0.441411 0.286077
+vt 0.453404 0.282406
+vt 0.466131 0.280561
+vt 0.478991 0.280616
+vt 0.491379 0.282582
+vt 0.503173 0.286387
+vt 0.514175 0.291968
+vt 0.532732 0.308084
+vt 0.524137 0.299241
+vt 0.539796 0.318194
+vt 0.545210 0.329333
+vt 0.548884 0.341317
+vt 0.550730 0.354034
+vt 0.550675 0.366883
+vt 0.548707 0.379263
+vt 0.544900 0.391047
+vt 0.539314 0.402041
+vt 0.523186 0.420583
+vt 0.532036 0.411994
+vt 0.513068 0.427642
+vt 0.501921 0.433051
+vt 0.489927 0.436723
+vt 0.477200 0.438567
+vt 0.464341 0.438512
+vt 0.451952 0.436546
+vt 0.440159 0.432741
+vt 0.440751 0.431294
+vt 0.429981 0.425815
+vt 0.411817 0.410040
+vt 0.420236 0.418690
+vt 0.404887 0.400147
+vt 0.399565 0.389235
+vt 0.395944 0.377480
+vt 0.394123 0.364988
+vt 0.394185 0.352364
+vt 0.396131 0.340220
+vt 0.399880 0.328674
+vt 0.405363 0.317912
+vt 0.421150 0.299762
+vt 0.412494 0.308174
+vt 0.431051 0.292837
+vt 0.441971 0.287519
+vt 0.453736 0.283902
+vt 0.466238 0.282081
+vt 0.478871 0.282144
+vt 0.491025 0.284088
+vt 0.502580 0.287834
+vt 0.513350 0.293313
+vt 0.531514 0.309088
+vt 0.523095 0.300438
+vt 0.538444 0.318981
+vt 0.543766 0.329893
+vt 0.547387 0.341648
+vt 0.549209 0.354140
+vt 0.549146 0.366764
+vt 0.547200 0.378909
+vt 0.543452 0.390455
+vt 0.537968 0.401217
+vt 0.522181 0.419366
+vt 0.530838 0.410954
+vt 0.512280 0.426291
+vt 0.501360 0.431609
+vt 0.489595 0.435227
+vt 0.477094 0.437047
+vt 0.464460 0.436985
+vt 0.452306 0.435040
+vt 0.452725 0.433282
+vt 0.441446 0.429607
+vt 0.430945 0.424248
+vt 0.413236 0.408869
+vt 0.421449 0.417295
+vt 0.406462 0.399224
+vt 0.401249 0.388576
+vt 0.397695 0.377089
+vt 0.395905 0.364863
+vt 0.395973 0.352508
+vt 0.397891 0.340638
+vt 0.401569 0.329368
+vt 0.406931 0.318875
+vt 0.422323 0.301180
+vt 0.413889 0.309387
+vt 0.431975 0.294412
+vt 0.442631 0.289202
+vt 0.454127 0.285651
+vt 0.466362 0.283862
+vt 0.478727 0.283931
+vt 0.490607 0.285847
+vt 0.501885 0.289522
+vt 0.512387 0.294880
+vt 0.530095 0.310260
+vt 0.521882 0.301833
+vt 0.536869 0.319904
+vt 0.542082 0.330552
+vt 0.545636 0.342039
+vt 0.547426 0.354265
+vt 0.547358 0.366621
+vt 0.545440 0.378490
+vt 0.541762 0.389760
+vt 0.536400 0.400254
+vt 0.521008 0.417949
+vt 0.529442 0.409742
+vt 0.511356 0.424717
+vt 0.500700 0.429926
+vt 0.489204 0.433477
+vt 0.476969 0.435266
+vt 0.464604 0.435198
+vt 0.464770 0.433176
+vt 0.453203 0.431295
+vt 0.442235 0.427703
+vt 0.432034 0.422482
+vt 0.414835 0.407545
+vt 0.422815 0.415725
+vt 0.408239 0.398178
+vt 0.403151 0.387826
+vt 0.399676 0.376643
+vt 0.397925 0.364722
+vt 0.397997 0.352674
+vt 0.399879 0.341116
+vt 0.403474 0.330157
+vt 0.408699 0.319963
+vt 0.423648 0.302777
+vt 0.415461 0.310751
+vt 0.433022 0.296187
+vt 0.443382 0.291103
+vt 0.454573 0.287630
+vt 0.466504 0.285881
+vt 0.478561 0.285952
+vt 0.490128 0.287833
+vt 0.501096 0.291425
+vt 0.511297 0.296646
+vt 0.528497 0.311584
+vt 0.520516 0.303403
+vt 0.535092 0.320950
+vt 0.540180 0.331302
+vt 0.543655 0.342485
+vt 0.545406 0.354406
+vt 0.545335 0.366455
+vt 0.543452 0.378013
+vt 0.539857 0.388972
+vt 0.534632 0.399165
+vt 0.519683 0.416351
+vt 0.527870 0.408377
+vt 0.510309 0.422941
+vt 0.499950 0.428025
+vt 0.488758 0.431498
+vt 0.476827 0.433248
+vt 0.476277 0.425385
+vt 0.465477 0.425300
+vt 0.455140 0.423562
+vt 0.445378 0.420300
+vt 0.436326 0.415613
+vt 0.421044 0.402341
+vt 0.428144 0.409599
+vt 0.415138 0.394037
+vt 0.410545 0.384832
+vt 0.407383 0.374846
+vt 0.405794 0.364172
+vt 0.405879 0.353380
+vt 0.407618 0.343052
+vt 0.410883 0.333297
+vt 0.415573 0.324252
+vt 0.428855 0.308981
+vt 0.421592 0.316076
+vt 0.437166 0.303080
+vt 0.446378 0.298491
+vt 0.456372 0.295331
+vt 0.467055 0.293743
+vt 0.477854 0.293829
+vt 0.488191 0.295567
+vt 0.497953 0.298828
+vt 0.507006 0.303515
+vt 0.522287 0.316787
+vt 0.515188 0.309529
+vt 0.528194 0.325091
+vt 0.532786 0.334297
+vt 0.535948 0.344283
+vt 0.537537 0.354957
+vt 0.537452 0.365748
+vt 0.535713 0.376077
+vt 0.532448 0.385832
+vt 0.527758 0.394877
+vt 0.514476 0.410147
+vt 0.521739 0.403052
+vt 0.506165 0.416048
+vt 0.496953 0.420637
+vt 0.486959 0.423797
+vt 0.475807 0.418686
+vt 0.485414 0.417244
+vt 0.466093 0.418595
+vt 0.456807 0.417003
+vt 0.448057 0.414045
+vt 0.439959 0.409822
+vt 0.426277 0.397939
+vt 0.432641 0.404428
+vt 0.420964 0.390515
+vt 0.416813 0.382269
+vt 0.413941 0.373302
+vt 0.412498 0.363703
+vt 0.412589 0.353996
+vt 0.414182 0.344717
+vt 0.417143 0.335974
+vt 0.421369 0.327882
+vt 0.433261 0.314211
+vt 0.426766 0.320570
+vt 0.440691 0.308901
+vt 0.448943 0.304754
+vt 0.457917 0.301884
+vt 0.467524 0.300442
+vt 0.477238 0.300533
+vt 0.486524 0.302125
+vt 0.495274 0.305084
+vt 0.503372 0.309307
+vt 0.517054 0.321189
+vt 0.510690 0.314700
+vt 0.522368 0.328614
+vt 0.526518 0.336859
+vt 0.529390 0.345826
+vt 0.530834 0.355426
+vt 0.530742 0.365132
+vt 0.529149 0.374411
+vt 0.526188 0.383154
+vt 0.521962 0.391246
+vt 0.510070 0.404918
+vt 0.516565 0.398558
+vt 0.502640 0.410227
+vt 0.494388 0.414374
+vt 0.484091 0.411655
+vt 0.492195 0.409046
+vt 0.475407 0.412969
+vt 0.466624 0.412877
+vt 0.458235 0.411421
+vt 0.450342 0.408732
+vt 0.443047 0.404910
+vt 0.430714 0.394199
+vt 0.436456 0.400042
+vt 0.425910 0.387513
+vt 0.422145 0.380078
+vt 0.419534 0.371980
+vt 0.418219 0.363302
+vt 0.418311 0.354527
+vt 0.419768 0.346144
+vt 0.422459 0.338257
+vt 0.426285 0.330968
+vt 0.437004 0.318645
+vt 0.431156 0.324382
+vt 0.443695 0.313844
+vt 0.451136 0.310083
+vt 0.459240 0.307473
+vt 0.467924 0.306159
+vt 0.476707 0.306251
+vt 0.485096 0.307707
+vt 0.492989 0.310396
+vt 0.500284 0.314218
+vt 0.512617 0.324929
+vt 0.506875 0.319086
+vt 0.517421 0.331615
+vt 0.521186 0.339050
+vt 0.523797 0.347148
+vt 0.525112 0.355826
+vt 0.525020 0.364602
+vt 0.523563 0.372984
+vt 0.520872 0.380871
+vt 0.517047 0.388161
+vt 0.506328 0.400483
+vt 0.512175 0.394746
+vt 0.499636 0.405284
+vt 0.490318 0.404494
+vt 0.497069 0.401068
+vt 0.482957 0.406875
+vt 0.475064 0.408077
+vt 0.467081 0.407986
+vt 0.459460 0.406652
+vt 0.452297 0.404200
+vt 0.445682 0.400723
+vt 0.434497 0.391008
+vt 0.439709 0.396303
+vt 0.430129 0.384948
+vt 0.426701 0.378202
+vt 0.424318 0.370847
+vt 0.423115 0.362960
+vt 0.423206 0.354983
+vt 0.424541 0.347368
+vt 0.426995 0.340210
+vt 0.430475 0.333601
+vt 0.440197 0.322424
+vt 0.434898 0.327632
+vt 0.446262 0.318060
+vt 0.453013 0.314634
+vt 0.460374 0.312253
+vt 0.468267 0.311052
+vt 0.476250 0.311143
+vt 0.483871 0.312476
+vt 0.491034 0.314928
+vt 0.497649 0.318406
+vt 0.508834 0.328120
+vt 0.503622 0.322825
+vt 0.513202 0.334181
+vt 0.516630 0.340926
+vt 0.519013 0.348281
+vt 0.520216 0.356168
+vt 0.520125 0.364145
+vt 0.518790 0.371761
+vt 0.516336 0.378918
+vt 0.512856 0.385527
+vt 0.503134 0.396704
+vt 0.508433 0.391496
+vt 0.494866 0.397454
+vt 0.500396 0.393465
+vt 0.488705 0.400588
+vt 0.481982 0.402770
+vt 0.474770 0.403874
+vt 0.467474 0.403785
+vt 0.460512 0.402560
+vt 0.453975 0.400314
+vt 0.447942 0.397135
+vt 0.437738 0.388273
+vt 0.442496 0.393099
+vt 0.433747 0.382746
+vt 0.430609 0.376590
+vt 0.428426 0.369873
+vt 0.427321 0.362666
+vt 0.427410 0.355375
+vt 0.428636 0.348420
+vt 0.430884 0.341887
+vt 0.434066 0.335859
+vt 0.442935 0.325663
+vt 0.438104 0.330417
+vt 0.448466 0.321674
+vt 0.454626 0.318540
+vt 0.461349 0.316358
+vt 0.468561 0.315254
+vt 0.475858 0.315343
+vt 0.482819 0.316568
+vt 0.489356 0.318814
+vt 0.495389 0.321994
+vt 0.505593 0.330856
+vt 0.500836 0.326029
+vt 0.509585 0.336382
+vt 0.512722 0.342538
+vt 0.514905 0.349255
+vt 0.516010 0.356462
+vt 0.515921 0.363753
+vt 0.514695 0.370709
+vt 0.512447 0.377241
+vt 0.509265 0.383270
+vt 0.505227 0.388712
+vt 0.498038 0.390676
+vt 0.502465 0.386313
+vt 0.492966 0.394340
+vt 0.487313 0.397221
+vt 0.481141 0.399228
+vt 0.474516 0.400248
+vt 0.467813 0.400161
+vt 0.461421 0.399032
+vt 0.455421 0.396966
+vt 0.449888 0.394044
+vt 0.440529 0.385916
+vt 0.444896 0.390339
+vt 0.436863 0.380848
+vt 0.433980 0.375200
+vt 0.431971 0.369032
+vt 0.430950 0.362412
+vt 0.431038 0.355714
+vt 0.432167 0.349327
+vt 0.434235 0.343333
+vt 0.437159 0.337804
+vt 0.445293 0.328452
+vt 0.440867 0.332816
+vt 0.450365 0.324789
+vt 0.456018 0.321908
+vt 0.462190 0.319900
+vt 0.468816 0.318880
+vt 0.475519 0.318968
+vt 0.481910 0.320096
+vt 0.487910 0.322163
+vt 0.493443 0.325084
+vt 0.502802 0.333212
+vt 0.498435 0.328789
+vt 0.506468 0.338280
+vt 0.509351 0.343929
+vt 0.511361 0.350096
+vt 0.512381 0.356716
+vt 0.512294 0.363414
+vt 0.511164 0.369801
+vt 0.509096 0.375796
+vt 0.506172 0.381325
+vt 0.503493 0.379639
+vt 0.495995 0.388260
+vt 0.500071 0.384234
+vt 0.491320 0.391640
+vt 0.486107 0.394300
+vt 0.480412 0.396156
+vt 0.474295 0.397103
+vt 0.468106 0.397017
+vt 0.462208 0.395972
+vt 0.456675 0.394064
+vt 0.451575 0.391367
+vt 0.442948 0.383874
+vt 0.446976 0.387948
+vt 0.439564 0.379203
+vt 0.436902 0.373994
+vt 0.435045 0.368304
+vt 0.434098 0.362192
+vt 0.434184 0.356008
+vt 0.435229 0.350114
+vt 0.437139 0.344585
+vt 0.439838 0.339489
+vt 0.447337 0.330869
+vt 0.443260 0.334894
+vt 0.452011 0.327488
+vt 0.457224 0.324828
+vt 0.462919 0.322972
+vt 0.469036 0.322026
+vt 0.475225 0.322112
+vt 0.481123 0.323156
+vt 0.486656 0.325065
+vt 0.491756 0.327762
+vt 0.500383 0.335254
+vt 0.496355 0.331180
+vt 0.503767 0.339925
+vt 0.506429 0.345134
+vt 0.508286 0.350825
+vt 0.509233 0.356936
+vt 0.509147 0.363121
+vt 0.508102 0.369014
+vt 0.506192 0.374543
+vt 0.503658 0.373450
+vt 0.501155 0.378170
+vt 0.494213 0.386152
+vt 0.497983 0.382421
+vt 0.489884 0.389285
+vt 0.485054 0.391751
+vt 0.479775 0.393472
+vt 0.474103 0.394356
+vt 0.468363 0.394271
+vt 0.462895 0.393301
+vt 0.457768 0.391532
+vt 0.453046 0.389031
+vt 0.445058 0.382094
+vt 0.448791 0.385861
+vt 0.441921 0.377769
+vt 0.439454 0.372942
+vt 0.437731 0.367668
+vt 0.436846 0.362000
+vt 0.436932 0.356264
+vt 0.437903 0.350801
+vt 0.439673 0.345678
+vt 0.442176 0.340959
+vt 0.449119 0.332977
+vt 0.445348 0.336708
+vt 0.453447 0.329843
+vt 0.458277 0.327377
+vt 0.463556 0.325656
+vt 0.469228 0.324772
+vt 0.474969 0.324858
+vt 0.480436 0.325827
+vt 0.485563 0.327597
+vt 0.490286 0.330097
+vt 0.498274 0.337035
+vt 0.494540 0.333267
+vt 0.501410 0.341360
+vt 0.503877 0.346186
+vt 0.505600 0.351461
+vt 0.506485 0.357129
+vt 0.506399 0.362865
+vt 0.505428 0.368327
+vt 0.503075 0.367724
+vt 0.501428 0.372490
+vt 0.499099 0.376878
+vt 0.492646 0.384297
+vt 0.496145 0.380824
+vt 0.488621 0.387213
+vt 0.484128 0.389506
+vt 0.479215 0.391108
+vt 0.473934 0.391939
+vt 0.468588 0.391851
+vt 0.463500 0.390949
+vt 0.458730 0.389303
+vt 0.454339 0.386977
+vt 0.446913 0.380528
+vt 0.450389 0.384024
+vt 0.443996 0.376507
+vt 0.441701 0.372016
+vt 0.440097 0.367108
+vt 0.439265 0.361830
+vt 0.439353 0.356489
+vt 0.440257 0.351405
+vt 0.441903 0.346639
+vt 0.444232 0.342251
+vt 0.450685 0.334831
+vt 0.447186 0.338304
+vt 0.454710 0.331916
+vt 0.459204 0.329622
+vt 0.464116 0.328020
+vt 0.469398 0.327189
+vt 0.474743 0.327277
+vt 0.479831 0.328179
+vt 0.484601 0.329825
+vt 0.488993 0.332152
+vt 0.496418 0.338600
+vt 0.492942 0.335104
+vt 0.499335 0.342622
+vt 0.501631 0.347112
+vt 0.503234 0.352020
+vt 0.504066 0.357298
+vt 0.503978 0.362640
+vt 0.501819 0.362440
+vt 0.500977 0.367186
+vt 0.499443 0.371635
+vt 0.497270 0.375730
+vt 0.491253 0.382647
+vt 0.494505 0.379400
+vt 0.487498 0.385367
+vt 0.483303 0.387506
+vt 0.478716 0.389000
+vt 0.473783 0.389790
+vt 0.468788 0.389694
+vt 0.464037 0.388853
+vt 0.459585 0.387320
+vt 0.455487 0.385149
+vt 0.448565 0.379137
+vt 0.451814 0.382386
+vt 0.445842 0.375384
+vt 0.443702 0.371193
+vt 0.442207 0.366609
+vt 0.441416 0.361680
+vt 0.441512 0.356689
+vt 0.442354 0.351942
+vt 0.443889 0.347493
+vt 0.446061 0.343399
+vt 0.452078 0.336481
+vt 0.448826 0.339728
+vt 0.455833 0.333761
+vt 0.460028 0.331623
+vt 0.464615 0.330128
+vt 0.469548 0.329338
+vt 0.474543 0.329434
+vt 0.479294 0.330275
+vt 0.483746 0.331809
+vt 0.487844 0.333980
+vt 0.494766 0.339992
+vt 0.491517 0.336742
+vt 0.497489 0.343744
+vt 0.499629 0.347936
+vt 0.501124 0.352519
+vt 0.501915 0.357448
+vt 0.499912 0.362257
+vt 0.499975 0.357584
+vt 0.499131 0.366706
+vt 0.497697 0.370876
+vt 0.495666 0.374715
+vt 0.493091 0.378172
+vt 0.490024 0.381201
+vt 0.486503 0.383745
+vt 0.482570 0.385745
+vt 0.478269 0.387138
+vt 0.468971 0.387788
+vt 0.473647 0.387852
+vt 0.464518 0.387008
+vt 0.460345 0.385575
+vt 0.456502 0.383546
+vt 0.453043 0.380973
+vt 0.450012 0.377909
+vt 0.447466 0.374390
+vt 0.445464 0.370460
+vt 0.444070 0.366163
+vt 0.443419 0.356871
+vt 0.443356 0.361544
+vt 0.444201 0.352422
+vt 0.445634 0.348252
+vt 0.447665 0.344413
+vt 0.450240 0.340957
+vt 0.453307 0.337927
+vt 0.456828 0.335383
+vt 0.460761 0.333383
+vt 0.465062 0.331990
+vt 0.474360 0.331340
+vt 0.469684 0.331276
+vt 0.478813 0.332121
+vt 0.482987 0.333553
+vt 0.486829 0.335583
+vt 0.490288 0.338155
+vt 0.493319 0.341219
+vt 0.495866 0.344739
+vt 0.497867 0.348669
+vt 0.499261 0.352966
+vt 0.497597 0.353365
+vt 0.498208 0.362093
+vt 0.498257 0.357704
+vt 0.497480 0.366275
+vt 0.496137 0.370196
+vt 0.494232 0.373808
+vt 0.491816 0.377065
+vt 0.488925 0.379909
+vt 0.485612 0.382297
+vt 0.481912 0.384172
+vt 0.477869 0.385475
+vt 0.469135 0.386086
+vt 0.473527 0.386134
+vt 0.464950 0.385359
+vt 0.461025 0.384017
+vt 0.457411 0.382113
+vt 0.454151 0.379699
+vt 0.451305 0.376811
+vt 0.448915 0.373499
+vt 0.447038 0.369803
+vt 0.445734 0.365763
+vt 0.445123 0.357036
+vt 0.445075 0.361424
+vt 0.445851 0.352854
+vt 0.447194 0.348932
+vt 0.449099 0.345320
+vt 0.451515 0.342063
+vt 0.454406 0.339219
+vt 0.457720 0.336831
+vt 0.461419 0.334956
+vt 0.465462 0.333653
+vt 0.474196 0.333042
+vt 0.469804 0.332994
+vt 0.478381 0.333769
+vt 0.482306 0.335111
+vt 0.485921 0.337015
+vt 0.489180 0.339429
+vt 0.492026 0.342318
+vt 0.494416 0.345629
+vt 0.496293 0.349325
+vt 0.494834 0.349934
+vt 0.496055 0.353736
+vt 0.496630 0.361941
+vt 0.496670 0.357815
+vt 0.495951 0.365875
+vt 0.494691 0.369565
+vt 0.492901 0.372966
+vt 0.490629 0.376034
+vt 0.487906 0.378709
+vt 0.484785 0.380954
+vt 0.481303 0.382714
+vt 0.477499 0.383935
+vt 0.469287 0.384509
+vt 0.473416 0.384549
+vt 0.465350 0.383830
+vt 0.461657 0.382571
+vt 0.458253 0.380783
+vt 0.455183 0.378513
+vt 0.452506 0.375791
+vt 0.450259 0.372673
+vt 0.448497 0.369194
+vt 0.447276 0.365393
+vt 0.446701 0.357188
+vt 0.446661 0.361313
+vt 0.447381 0.353254
+vt 0.448640 0.349563
+vt 0.450430 0.346162
+vt 0.452702 0.343095
+vt 0.455426 0.340419
+vt 0.458546 0.338175
+vt 0.462028 0.336414
+vt 0.465833 0.335193
+vt 0.474044 0.334619
+vt 0.469915 0.334579
+vt 0.477981 0.335298
+vt 0.481674 0.336557
+vt 0.485078 0.338345
+vt 0.488148 0.340615
+vt 0.490826 0.343337
+vt 0.493072 0.346455
+vt 0.491807 0.347232
+vt 0.493462 0.350507
+vt 0.494607 0.354083
+vt 0.495147 0.361798
+vt 0.495183 0.357919
+vt 0.494512 0.365499
+vt 0.493330 0.368972
+vt 0.491648 0.372173
+vt 0.489510 0.375062
+vt 0.486946 0.377580
+vt 0.484007 0.379690
+vt 0.480730 0.381343
+vt 0.477151 0.382488
+vt 0.469430 0.383028
+vt 0.473312 0.383063
+vt 0.465726 0.382393
+vt 0.462250 0.381211
+vt 0.459046 0.379531
+vt 0.456156 0.377395
+vt 0.453636 0.374832
+vt 0.451524 0.371896
+vt 0.449870 0.368621
+vt 0.448724 0.365045
+vt 0.448184 0.357330
+vt 0.448149 0.361209
+vt 0.448819 0.353630
+vt 0.450001 0.350156
+vt 0.451683 0.346955
+vt 0.453821 0.344067
+vt 0.456386 0.341549
+vt 0.459324 0.339439
+vt 0.462601 0.337785
+vt 0.466180 0.336641
+vt 0.473901 0.336101
+vt 0.470019 0.336065
+vt 0.477605 0.336736
+vt 0.481081 0.337917
+vt 0.484285 0.339597
+vt 0.487175 0.341734
+vt 0.489695 0.344296
+vt 0.488638 0.345192
+vt 0.490625 0.347957
+vt 0.492180 0.351041
+vt 0.493256 0.354407
+vt 0.493764 0.361665
+vt 0.493796 0.358016
+vt 0.493169 0.365148
+vt 0.492058 0.368418
+vt 0.490476 0.371433
+vt 0.488463 0.374152
+vt 0.486049 0.376524
+vt 0.483281 0.378509
+vt 0.480195 0.380063
+vt 0.476827 0.381137
+vt 0.469563 0.381646
+vt 0.473215 0.381678
+vt 0.466077 0.381051
+vt 0.462805 0.379941
+vt 0.459787 0.378360
+vt 0.457066 0.376349
+vt 0.454693 0.373936
+vt 0.452706 0.371171
+vt 0.451151 0.368087
+vt 0.450076 0.364721
+vt 0.449567 0.357463
+vt 0.449535 0.361112
+vt 0.450163 0.353980
+vt 0.451273 0.350710
+vt 0.452855 0.347695
+vt 0.454868 0.344976
+vt 0.457282 0.342605
+vt 0.460050 0.340620
+vt 0.463136 0.339065
+vt 0.466505 0.337991
+vt 0.473768 0.337483
+vt 0.470116 0.337451
+vt 0.477254 0.338078
+vt 0.480527 0.339187
+vt 0.483544 0.340768
+vt 0.486265 0.342780
+vt 0.485436 0.343733
+vt 0.487675 0.346008
+vt 0.489548 0.348618
+vt 0.491014 0.351527
+vt 0.492026 0.354702
+vt 0.492505 0.361545
+vt 0.492536 0.358104
+vt 0.491946 0.364830
+vt 0.490900 0.367915
+vt 0.489409 0.370759
+vt 0.487509 0.373324
+vt 0.485232 0.375561
+vt 0.482621 0.377433
+vt 0.479709 0.378897
+vt 0.476532 0.379909
+vt 0.469683 0.380388
+vt 0.473127 0.380418
+vt 0.466396 0.379828
+vt 0.463309 0.378784
+vt 0.460462 0.377293
+vt 0.457895 0.375395
+vt 0.455656 0.373120
+vt 0.453783 0.370511
+vt 0.452317 0.367601
+vt 0.451305 0.364427
+vt 0.450826 0.357583
+vt 0.450796 0.361024
+vt 0.451385 0.354299
+vt 0.452431 0.351214
+vt 0.453922 0.348369
+vt 0.455822 0.345804
+vt 0.458099 0.343567
+vt 0.460710 0.341695
+vt 0.463622 0.340231
+vt 0.466799 0.339220
+vt 0.473648 0.338741
+vt 0.470205 0.338710
+vt 0.476935 0.339300
+vt 0.480022 0.340345
+vt 0.482869 0.341835
+vt 0.484713 0.344564
+vt 0.482281 0.342765
+vt 0.486835 0.346720
+vt 0.488609 0.349193
+vt 0.489997 0.351950
+vt 0.490955 0.354958
+vt 0.491409 0.361440
+vt 0.491438 0.358181
+vt 0.490880 0.364552
+vt 0.489890 0.367476
+vt 0.488478 0.370172
+vt 0.486677 0.372601
+vt 0.484520 0.374721
+vt 0.482045 0.376495
+vt 0.479286 0.377881
+vt 0.476276 0.378838
+vt 0.469788 0.379292
+vt 0.473050 0.379321
+vt 0.466673 0.378763
+vt 0.463748 0.377775
+vt 0.461050 0.376363
+vt 0.458618 0.374564
+vt 0.456496 0.372409
+vt 0.454722 0.369936
+vt 0.453334 0.367178
+vt 0.452376 0.364171
+vt 0.451922 0.357688
+vt 0.451893 0.360947
+vt 0.452451 0.354576
+vt 0.453441 0.351653
+vt 0.454854 0.348957
+vt 0.456654 0.346527
+vt 0.458811 0.344407
+vt 0.461286 0.342633
+vt 0.464045 0.341247
+vt 0.467056 0.340290
+vt 0.473543 0.339836
+vt 0.470281 0.339807
+vt 0.476658 0.340365
+vt 0.479583 0.341354
+vt 0.481805 0.343520
+vt 0.479228 0.342172
+vt 0.484126 0.345239
+vt 0.486153 0.347297
+vt 0.487847 0.349659
+vt 0.489172 0.352292
+vt 0.490086 0.355165
+vt 0.490520 0.361356
+vt 0.490548 0.358243
+vt 0.490015 0.364328
+vt 0.489071 0.367120
+vt 0.487722 0.369695
+vt 0.486002 0.372015
+vt 0.483943 0.374040
+vt 0.481579 0.375734
+vt 0.478943 0.377057
+vt 0.476068 0.377970
+vt 0.469872 0.378404
+vt 0.472987 0.378432
+vt 0.466898 0.377900
+vt 0.464104 0.376956
+vt 0.461527 0.375608
+vt 0.459205 0.373890
+vt 0.457178 0.371832
+vt 0.455484 0.369470
+vt 0.454159 0.366836
+vt 0.453245 0.363963
+vt 0.452811 0.357772
+vt 0.452784 0.360885
+vt 0.453316 0.354800
+vt 0.454260 0.352008
+vt 0.455609 0.349433
+vt 0.457329 0.347113
+vt 0.459388 0.345088
+vt 0.461752 0.343395
+vt 0.464388 0.342071
+vt 0.467263 0.341158
+vt 0.473459 0.340724
+vt 0.470344 0.340697
+vt 0.476433 0.341229
+vt 0.478974 0.342759
+vt 0.476273 0.341847
+vt 0.481464 0.344061
+vt 0.483706 0.345722
+vt 0.485665 0.347709
+vt 0.487302 0.349992
+vt 0.488582 0.352536
+vt 0.489464 0.355312
+vt 0.489884 0.361297
+vt 0.489912 0.358288
+vt 0.489396 0.364168
+vt 0.488484 0.366867
+vt 0.487181 0.369355
+vt 0.485519 0.371595
+vt 0.483530 0.373552
+vt 0.481246 0.375188
+vt 0.478699 0.376467
+vt 0.475921 0.377349
+vt 0.469932 0.377768
+vt 0.472943 0.377796
+vt 0.467058 0.377281
+vt 0.464357 0.376370
+vt 0.461867 0.375068
+vt 0.459625 0.373407
+vt 0.457667 0.371419
+vt 0.456029 0.369137
+vt 0.454750 0.366592
+vt 0.453867 0.363816
+vt 0.453447 0.357832
+vt 0.453420 0.360840
+vt 0.453935 0.354960
+vt 0.454847 0.352262
+vt 0.456150 0.349773
+vt 0.457812 0.347533
+vt 0.459801 0.345576
+vt 0.462086 0.343940
+vt 0.464633 0.342661
+vt 0.467411 0.341779
+vt 0.473399 0.341360
+vt 0.470388 0.341332
+vt 0.476183 0.342200
+vt 0.473366 0.341723
+vt 0.478830 0.343093
+vt 0.481270 0.344369
+vt 0.483467 0.345996
+vt 0.485386 0.347944
+vt 0.486990 0.350181
+vt 0.488244 0.352675
+vt 0.489109 0.355396
+vt 0.489520 0.361263
+vt 0.489547 0.358313
+vt 0.489043 0.364078
+vt 0.488149 0.366723
+vt 0.486872 0.369161
+vt 0.485244 0.371357
+vt 0.483295 0.373274
+vt 0.481056 0.374877
+vt 0.478560 0.376130
+vt 0.475837 0.376994
+vt 0.469965 0.377405
+vt 0.472917 0.377432
+vt 0.467148 0.376928
+vt 0.464502 0.376035
+vt 0.462061 0.374759
+vt 0.459864 0.373132
+vt 0.457945 0.371184
+vt 0.456341 0.368947
+vt 0.455087 0.366453
+vt 0.454222 0.363732
+vt 0.453811 0.357865
+vt 0.453784 0.360815
+vt 0.454288 0.355050
+vt 0.455182 0.352406
+vt 0.456459 0.349967
+vt 0.458087 0.347772
+vt 0.460036 0.345855
+vt 0.462275 0.344251
+vt 0.464771 0.342998
+vt 0.467494 0.342134
+vt 0.470414 0.341696
+vt 0.473342 0.341993
+vt 0.473312 0.342312
+vt 0.470433 0.341969
+vt 0.473265 0.342794
+vt 0.470455 0.342289
+vt 0.473192 0.343560
+vt 0.470489 0.342772
+vt 0.473081 0.344714
+vt 0.470543 0.343538
+vt 0.472925 0.346351
+vt 0.470624 0.344693
+vt 0.472713 0.348564
+vt 0.470739 0.346333
+vt 0.472439 0.351445
+vt 0.470894 0.348549
+vt 0.472092 0.355081
+vt 0.471096 0.351433
+vt 0.471666 0.359564
+vt 0.471351 0.355075
+vt 0.470618 0.355184
+vt 0.469933 0.355401
+vt 0.469306 0.355715
+vt 0.468743 0.356118
+vt 0.468253 0.356600
+vt 0.467843 0.357152
+vt 0.467522 0.357765
+vt 0.467298 0.358430
+vt 0.467179 0.359138
+vt 0.467173 0.359878
+vt 0.467282 0.360611
+vt 0.467499 0.361295
+vt 0.467813 0.361922
+vt 0.468217 0.362485
+vt 0.468699 0.362975
+vt 0.469252 0.363384
+vt 0.469866 0.363704
+vt 0.470531 0.363928
+vt 0.471239 0.364047
+vt 0.471980 0.364053
+vt 0.472713 0.363944
+vt 0.473398 0.363727
+vt 0.474025 0.363413
+vt 0.474588 0.363010
+vt 0.475079 0.362528
+vt 0.475488 0.361976
+vt 0.475809 0.361363
+vt 0.476033 0.360698
+vt 0.476152 0.359991
+vt 0.476158 0.359250
+vt 0.476049 0.358517
+vt 0.475832 0.357833
+vt 0.475518 0.357206
+vt 0.475115 0.356644
+vt 0.474632 0.356154
+vt 0.474079 0.355745
+vt 0.473466 0.355424
+vt 0.472800 0.355200
+vt 0.473721 0.351660
+vt 0.474451 0.348857
+vt 0.475012 0.346702
+vt 0.475427 0.345109
+vt 0.475720 0.343986
+vt 0.475914 0.343241
+vt 0.476036 0.342773
+vt 0.476116 0.342462
+vt 0.478723 0.343342
+vt 0.478595 0.343636
+vt 0.478402 0.344080
+vt 0.478094 0.344786
+vt 0.477631 0.345850
+vt 0.476973 0.347362
+vt 0.476083 0.349406
+vt 0.474926 0.352065
+vt 0.476038 0.352646
+vt 0.477589 0.350192
+vt 0.478781 0.348306
+vt 0.479663 0.346912
+vt 0.480284 0.345930
+vt 0.480696 0.345279
+vt 0.480955 0.344869
+vt 0.481126 0.344598
+vt 0.483292 0.346198
+vt 0.483081 0.346440
+vt 0.482763 0.346807
+vt 0.482256 0.347389
+vt 0.481493 0.348266
+vt 0.480410 0.349512
+vt 0.478945 0.351196
+vt 0.477039 0.353387
+vt 0.477913 0.354274
+vt 0.480128 0.352397
+vt 0.481831 0.350955
+vt 0.483090 0.349888
+vt 0.483977 0.349137
+vt 0.484565 0.348639
+vt 0.484935 0.348325
+vt 0.485179 0.348118
+vt 0.486759 0.350321
+vt 0.486486 0.350488
+vt 0.486072 0.350742
+vt 0.485415 0.351144
+vt 0.484425 0.351751
+vt 0.483019 0.352613
+vt 0.481117 0.353778
+vt 0.478643 0.355293
+vt 0.479212 0.356429
+vt 0.481889 0.355317
+vt 0.483946 0.354461
+vt 0.485467 0.353828
+vt 0.486539 0.353382
+vt 0.487250 0.353086
+vt 0.487697 0.352900
+vt 0.487994 0.352778
+vt 0.488845 0.355458
+vt 0.488533 0.355532
+vt 0.488062 0.355645
+vt 0.487313 0.355824
+vt 0.486185 0.356094
+vt 0.484585 0.356478
+vt 0.482420 0.356995
+vt 0.479604 0.357668
+vt 0.479803 0.358995
+vt 0.482689 0.358793
+vt 0.484907 0.358638
+vt 0.486548 0.358523
+vt 0.487704 0.358442
+vt 0.488471 0.358389
+vt 0.488954 0.358355
+vt 0.489274 0.358332
+vt 0.489250 0.361239
+vt 0.488931 0.361209
+vt 0.488449 0.361163
+vt 0.487682 0.361089
+vt 0.486528 0.360979
+vt 0.484889 0.360822
+vt 0.482674 0.360611
+vt 0.479791 0.360337
+vt 0.479576 0.361618
+vt 0.482381 0.362347
+vt 0.484538 0.362908
+vt 0.486132 0.363323
+vt 0.487256 0.363615
+vt 0.488001 0.363809
+vt 0.488470 0.363931
+vt 0.488781 0.364011
+vt 0.487900 0.366616
+vt 0.487607 0.366489
+vt 0.487162 0.366295
+vt 0.486455 0.365988
+vt 0.485390 0.365525
+vt 0.483877 0.364867
+vt 0.481832 0.363979
+vt 0.479170 0.362822
+vt 0.478589 0.363933
+vt 0.481045 0.365483
+vt 0.482932 0.366674
+vt 0.484328 0.367555
+vt 0.485310 0.368176
+vt 0.485962 0.368588
+vt 0.486372 0.368847
+vt 0.486643 0.369017
+vt 0.485042 0.371181
+vt 0.484800 0.370971
+vt 0.484433 0.370652
+vt 0.483851 0.370147
+vt 0.482973 0.369384
+vt 0.481726 0.368301
+vt 0.480041 0.366838
+vt 0.477848 0.364933
+vt 0.476960 0.365806
+vt 0.478838 0.368020
+vt 0.480281 0.369722
+vt 0.481349 0.370980
+vt 0.482101 0.371866
+vt 0.482600 0.372453
+vt 0.482914 0.372823
+vt 0.483121 0.373067
+vt 0.480916 0.374646
+vt 0.480749 0.374373
+vt 0.480495 0.373959
+vt 0.480092 0.373303
+vt 0.479485 0.372314
+vt 0.478622 0.370909
+vt 0.477457 0.369009
+vt 0.475940 0.366536
+vt 0.474803 0.367105
+vt 0.475916 0.369780
+vt 0.476772 0.371835
+vt 0.477406 0.373355
+vt 0.477852 0.374426
+vt 0.478148 0.375136
+vt 0.478335 0.375583
+vt 0.478457 0.375879
+vt 0.475775 0.376731
+vt 0.475701 0.376419
+vt 0.475588 0.375948
+vt 0.475408 0.375200
+vt 0.475138 0.374073
+vt 0.474755 0.372473
+vt 0.474237 0.370311
+vt 0.473563 0.367497
+vt 0.472235 0.367695
+vt 0.472437 0.370579
+vt 0.472592 0.372795
+vt 0.472707 0.374435
+vt 0.472788 0.375590
+vt 0.472842 0.376357
+vt 0.472876 0.376839
+vt 0.472898 0.377159
+vt 0.469989 0.377135
+vt 0.470019 0.376816
+vt 0.470066 0.376334
+vt 0.470139 0.375568
+vt 0.470250 0.374415
+vt 0.470406 0.372777
+vt 0.470618 0.370564
+vt 0.470892 0.367684
+vt 0.469610 0.367468
+vt 0.468880 0.370272
+vt 0.468320 0.372426
+vt 0.467904 0.374020
+vt 0.467611 0.375142
+vt 0.467417 0.375887
+vt 0.467295 0.376356
+vt 0.467215 0.376666
+vt 0.464608 0.375786
+vt 0.464736 0.375493
+vt 0.464929 0.375048
+vt 0.465237 0.374342
+vt 0.465700 0.373278
+vt 0.466358 0.371766
+vt 0.467248 0.369723
+vt 0.468405 0.367063
+vt 0.467293 0.366482
+vt 0.465742 0.368936
+vt 0.464550 0.370822
+vt 0.463668 0.372217
+vt 0.463047 0.373198
+vt 0.462635 0.373850
+vt 0.462376 0.374259
+vt 0.462205 0.374530
+vt 0.460039 0.372930
+vt 0.460250 0.372688
+vt 0.460569 0.372322
+vt 0.461075 0.371740
+vt 0.461838 0.370862
+vt 0.462921 0.369617
+vt 0.464386 0.367933
+vt 0.466292 0.365741
+vt 0.465419 0.364854
+vt 0.463203 0.366731
+vt 0.461500 0.368173
+vt 0.460241 0.369240
+vt 0.459354 0.369991
+vt 0.458767 0.370490
+vt 0.458397 0.370803
+vt 0.458152 0.371010
+vt 0.456572 0.368807
+vt 0.456846 0.368640
+vt 0.457259 0.368387
+vt 0.457916 0.367984
+vt 0.458906 0.367377
+vt 0.460312 0.366516
+vt 0.462214 0.365351
+vt 0.464689 0.363835
+vt 0.464119 0.362699
+vt 0.461442 0.363812
+vt 0.459385 0.364667
+vt 0.457864 0.365300
+vt 0.456792 0.365746
+vt 0.456081 0.366042
+vt 0.455634 0.366228
+vt 0.455338 0.366351
+vt 0.454486 0.363671
+vt 0.454798 0.363597
+vt 0.455269 0.363484
+vt 0.456018 0.363304
+vt 0.457146 0.363034
+vt 0.458747 0.362651
+vt 0.460911 0.362133
+vt 0.463727 0.361460
+vt 0.463528 0.360133
+vt 0.460642 0.360335
+vt 0.458424 0.360490
+vt 0.456783 0.360605
+vt 0.455627 0.360686
+vt 0.454860 0.360740
+vt 0.454377 0.360773
+vt 0.454057 0.360796
+vt 0.454081 0.357889
+vt 0.454400 0.357919
+vt 0.454883 0.357965
+vt 0.455649 0.358039
+vt 0.456804 0.358150
+vt 0.458442 0.358306
+vt 0.460657 0.358517
+vt 0.463540 0.358792
+vt 0.463755 0.357510
+vt 0.460950 0.356781
+vt 0.458794 0.356221
+vt 0.457199 0.355806
+vt 0.456076 0.355513
+vt 0.455330 0.355319
+vt 0.454861 0.355197
+vt 0.454551 0.355117
+vt 0.455431 0.352512
+vt 0.455725 0.352640
+vt 0.456169 0.352833
+vt 0.456876 0.353140
+vt 0.457941 0.353604
+vt 0.459454 0.354261
+vt 0.461499 0.355150
+vt 0.464161 0.356306
+vt 0.464742 0.355195
+vt 0.462286 0.353645
+vt 0.460399 0.352454
+vt 0.459003 0.351573
+vt 0.458021 0.350952
+vt 0.457369 0.350541
+vt 0.456959 0.350282
+vt 0.456688 0.350111
+vt 0.458289 0.347947
+vt 0.458531 0.348157
+vt 0.458898 0.348476
+vt 0.459481 0.348982
+vt 0.460359 0.349744
+vt 0.461605 0.350827
+vt 0.463291 0.352291
+vt 0.465484 0.354195
+vt 0.466372 0.353322
+vt 0.464493 0.351108
+vt 0.463050 0.349407
+vt 0.461982 0.348148
+vt 0.461230 0.347263
+vt 0.460732 0.346675
+vt 0.460418 0.346305
+vt 0.460211 0.346061
+vt 0.462415 0.344482
+vt 0.462582 0.344756
+vt 0.462836 0.345169
+vt 0.463239 0.345825
+vt 0.463846 0.346815
+vt 0.464709 0.348220
+vt 0.465874 0.350120
+vt 0.467391 0.352592
+vt 0.468528 0.352023
+vt 0.467415 0.349349
+vt 0.466559 0.347293
+vt 0.465925 0.345773
+vt 0.465479 0.344702
+vt 0.465183 0.343992
+vt 0.464996 0.343545
+vt 0.464874 0.343249
+vt 0.467556 0.342398
+vt 0.467630 0.342709
+vt 0.467743 0.343181
+vt 0.467923 0.343929
+vt 0.468193 0.345056
+vt 0.468576 0.346655
+vt 0.469094 0.348818
+vt 0.469768 0.351631
+vt 0.596705 0.308497
+vt 0.596349 0.308587
+vt 0.596340 0.308527
+vt 0.596367 0.308642
+vt 0.596393 0.308693
+vt 0.596427 0.308739
+vt 0.596466 0.308778
+vt 0.596512 0.308811
+vt 0.596562 0.308836
+vt 0.596616 0.308854
+vt 0.596674 0.308863
+vt 0.596735 0.308863
+vt 0.596794 0.308853
+vt 0.596850 0.308835
+vt 0.596901 0.308809
+vt 0.596947 0.308776
+vt 0.596986 0.308736
+vt 0.597019 0.308691
+vt 0.597044 0.308641
+vt 0.597062 0.308586
+vt 0.597071 0.308528
+vt 0.597071 0.308468
+vt 0.597061 0.308408
+vt 0.597043 0.308353
+vt 0.597017 0.308302
+vt 0.596984 0.308256
+vt 0.596944 0.308217
+vt 0.596899 0.308184
+vt 0.596848 0.308158
+vt 0.596794 0.308141
+vt 0.596736 0.308132
+vt 0.596676 0.308132
+vt 0.596616 0.308141
+vt 0.596560 0.308160
+vt 0.596509 0.308186
+vt 0.596464 0.308219
+vt 0.596424 0.308259
+vt 0.596392 0.308304
+vt 0.596366 0.308354
+vt 0.596348 0.308409
+vt 0.596339 0.308466
+vt 0.596120 0.308545
+vt 0.596119 0.308448
+vt 0.596135 0.308640
+vt 0.596164 0.308729
+vt 0.596207 0.308810
+vt 0.596260 0.308883
+vt 0.596396 0.308998
+vt 0.596323 0.308946
+vt 0.596476 0.309040
+vt 0.596563 0.309068
+vt 0.596656 0.309083
+vt 0.596752 0.309083
+vt 0.596848 0.309067
+vt 0.596937 0.309038
+vt 0.597018 0.308996
+vt 0.597091 0.308942
+vt 0.597207 0.308806
+vt 0.597154 0.308879
+vt 0.597248 0.308726
+vt 0.597276 0.308639
+vt 0.597291 0.308547
+vt 0.597291 0.308450
+vt 0.597276 0.308355
+vt 0.597246 0.308266
+vt 0.597204 0.308185
+vt 0.597151 0.308112
+vt 0.597014 0.307996
+vt 0.597087 0.308049
+vt 0.596934 0.307955
+vt 0.596847 0.307927
+vt 0.596755 0.307912
+vt 0.596658 0.307912
+vt 0.596562 0.307927
+vt 0.596473 0.307957
+vt 0.596392 0.307999
+vt 0.596320 0.308052
+vt 0.596204 0.308189
+vt 0.596256 0.308116
+vt 0.596163 0.308269
+vt 0.596134 0.308356
+vt 0.596001 0.308437
+vt 0.596019 0.308326
+vt 0.595999 0.308554
+vt 0.596019 0.308670
+vt 0.596056 0.308777
+vt 0.596107 0.308874
+vt 0.596171 0.308960
+vt 0.596334 0.309098
+vt 0.596248 0.309035
+vt 0.596429 0.309148
+vt 0.596533 0.309183
+vt 0.596645 0.309202
+vt 0.596762 0.309203
+vt 0.596878 0.309183
+vt 0.596985 0.309146
+vt 0.597082 0.309095
+vt 0.597169 0.309031
+vt 0.597307 0.308869
+vt 0.597243 0.308954
+vt 0.597356 0.308773
+vt 0.597391 0.308669
+vt 0.597410 0.308558
+vt 0.597411 0.308440
+vt 0.597391 0.308325
+vt 0.597355 0.308218
+vt 0.597304 0.308121
+vt 0.597239 0.308034
+vt 0.597077 0.307896
+vt 0.597163 0.307960
+vt 0.596981 0.307847
+vt 0.596877 0.307812
+vt 0.596766 0.307793
+vt 0.596648 0.307792
+vt 0.596532 0.307812
+vt 0.596426 0.307848
+vt 0.596328 0.307899
+vt 0.596242 0.307964
+vt 0.596104 0.308126
+vt 0.596167 0.308040
+vt 0.596054 0.308222
+vt 0.595963 0.308185
+vt 0.595924 0.308303
+vt 0.595902 0.308430
+vt 0.595924 0.308693
+vt 0.595901 0.308562
+vt 0.595965 0.308814
+vt 0.596023 0.308925
+vt 0.596097 0.309024
+vt 0.596184 0.309109
+vt 0.596283 0.309182
+vt 0.596392 0.309239
+vt 0.596511 0.309278
+vt 0.596637 0.309299
+vt 0.596901 0.309278
+vt 0.596770 0.309301
+vt 0.597022 0.309237
+vt 0.597133 0.309179
+vt 0.597232 0.309105
+vt 0.597318 0.309018
+vt 0.597390 0.308919
+vt 0.597447 0.308810
+vt 0.597487 0.308692
+vt 0.597508 0.308565
+vt 0.597487 0.308302
+vt 0.597509 0.308432
+vt 0.597445 0.308180
+vt 0.597387 0.308070
+vt 0.597314 0.307971
+vt 0.597226 0.307885
+vt 0.597128 0.307813
+vt 0.597018 0.307756
+vt 0.596900 0.307717
+vt 0.596773 0.307695
+vt 0.596509 0.307717
+vt 0.596640 0.307694
+vt 0.596388 0.307758
+vt 0.596277 0.307816
+vt 0.596178 0.307890
+vt 0.596093 0.307977
+vt 0.596020 0.308075
+vt 0.595877 0.307988
+vt 0.595809 0.308121
+vt 0.595762 0.308264
+vt 0.595737 0.308416
+vt 0.595762 0.308733
+vt 0.595737 0.308576
+vt 0.595811 0.308879
+vt 0.595882 0.309013
+vt 0.595970 0.309133
+vt 0.596075 0.309238
+vt 0.596195 0.309325
+vt 0.596328 0.309393
+vt 0.596472 0.309440
+vt 0.596624 0.309465
+vt 0.596941 0.309440
+vt 0.596784 0.309465
+vt 0.597087 0.309390
+vt 0.597221 0.309320
+vt 0.597341 0.309232
+vt 0.597446 0.309127
+vt 0.597533 0.309007
+vt 0.597601 0.308874
+vt 0.597649 0.308731
+vt 0.597674 0.308578
+vt 0.597648 0.308262
+vt 0.597673 0.308419
+vt 0.597599 0.308116
+vt 0.597529 0.307982
+vt 0.597440 0.307862
+vt 0.597335 0.307757
+vt 0.597215 0.307670
+vt 0.597082 0.307602
+vt 0.596939 0.307555
+vt 0.596786 0.307530
+vt 0.596470 0.307555
+vt 0.596627 0.307530
+vt 0.596323 0.307604
+vt 0.596189 0.307674
+vt 0.596069 0.307763
+vt 0.595964 0.307868
+vt 0.595730 0.307669
+vt 0.595617 0.307828
+vt 0.595529 0.308003
+vt 0.595467 0.308191
+vt 0.595436 0.308391
+vt 0.595469 0.308806
+vt 0.595438 0.308600
+vt 0.595533 0.308998
+vt 0.595624 0.309174
+vt 0.595739 0.309333
+vt 0.595876 0.309472
+vt 0.596035 0.309584
+vt 0.596210 0.309673
+vt 0.596399 0.309734
+vt 0.596599 0.309766
+vt 0.597014 0.309732
+vt 0.596808 0.309764
+vt 0.597206 0.309669
+vt 0.597383 0.309578
+vt 0.597541 0.309463
+vt 0.597680 0.309326
+vt 0.597793 0.309167
+vt 0.597881 0.308992
+vt 0.597943 0.308804
+vt 0.597975 0.308604
+vt 0.597941 0.308189
+vt 0.597973 0.308395
+vt 0.597878 0.307997
+vt 0.597787 0.307820
+vt 0.597671 0.307662
+vt 0.597534 0.307523
+vt 0.597375 0.307410
+vt 0.597200 0.307322
+vt 0.597012 0.307261
+vt 0.596811 0.307229
+vt 0.596397 0.307262
+vt 0.596603 0.307231
+vt 0.596204 0.307326
+vt 0.596028 0.307417
+vt 0.595869 0.307532
+vt 0.595316 0.307317
+vt 0.595513 0.307123
+vt 0.595156 0.307543
+vt 0.595031 0.307792
+vt 0.594945 0.308060
+vt 0.594901 0.308345
+vt 0.594949 0.308937
+vt 0.594905 0.308643
+vt 0.595038 0.309211
+vt 0.595166 0.309463
+vt 0.595329 0.309688
+vt 0.595523 0.309886
+vt 0.595750 0.310045
+vt 0.595999 0.310170
+vt 0.596268 0.310256
+vt 0.596553 0.310300
+vt 0.597145 0.310252
+vt 0.596851 0.310296
+vt 0.597420 0.310163
+vt 0.597671 0.310035
+vt 0.597897 0.309872
+vt 0.598095 0.309678
+vt 0.598254 0.309452
+vt 0.598379 0.309203
+vt 0.598465 0.308934
+vt 0.598509 0.308649
+vt 0.598461 0.308058
+vt 0.598505 0.308352
+vt 0.598372 0.307784
+vt 0.598244 0.307532
+vt 0.598081 0.307306
+vt 0.597887 0.307109
+vt 0.597661 0.306949
+vt 0.597412 0.306825
+vt 0.597143 0.306739
+vt 0.596857 0.306695
+vt 0.596265 0.306743
+vt 0.596560 0.306699
+vt 0.595991 0.306832
+vt 0.595739 0.306960
+vt 0.594944 0.306469
+vt 0.595278 0.306230
+vt 0.594653 0.306754
+vt 0.594419 0.307086
+vt 0.594237 0.307453
+vt 0.594111 0.307850
+vt 0.594048 0.308271
+vt 0.594119 0.309147
+vt 0.594055 0.308711
+vt 0.594249 0.309553
+vt 0.594436 0.309924
+vt 0.594676 0.310257
+vt 0.594960 0.310548
+vt 0.595293 0.310782
+vt 0.595660 0.310964
+vt 0.596057 0.311090
+vt 0.596479 0.311153
+vt 0.597355 0.311082
+vt 0.596919 0.311145
+vt 0.597761 0.310952
+vt 0.598133 0.310764
+vt 0.598466 0.310525
+vt 0.598757 0.310241
+vt 0.598991 0.309909
+vt 0.599174 0.309541
+vt 0.599300 0.309145
+vt 0.599363 0.308724
+vt 0.599292 0.307848
+vt 0.599355 0.308283
+vt 0.599162 0.307442
+vt 0.598974 0.307071
+vt 0.598735 0.306738
+vt 0.598450 0.306447
+vt 0.598117 0.306213
+vt 0.597750 0.306031
+vt 0.597353 0.305905
+vt 0.596932 0.305842
+vt 0.596055 0.305913
+vt 0.596491 0.305849
+vt 0.595649 0.306043
+vt 0.594706 0.305320
+vt 0.595228 0.305059
+vt 0.594237 0.305654
+vt 0.593825 0.306050
+vt 0.593500 0.306520
+vt 0.593246 0.307036
+vt 0.593072 0.307592
+vt 0.592986 0.308182
+vt 0.593085 0.309406
+vt 0.593000 0.308796
+vt 0.593264 0.309974
+vt 0.593525 0.310495
+vt 0.593859 0.310964
+vt 0.594256 0.311375
+vt 0.594726 0.311700
+vt 0.595243 0.311954
+vt 0.595799 0.312128
+vt 0.596389 0.312214
+vt 0.597614 0.312115
+vt 0.597005 0.312199
+vt 0.598183 0.311936
+vt 0.598704 0.311675
+vt 0.599173 0.311341
+vt 0.599585 0.310945
+vt 0.599911 0.310475
+vt 0.600165 0.309959
+vt 0.600339 0.309403
+vt 0.600424 0.308813
+vt 0.600325 0.307589
+vt 0.600410 0.308198
+vt 0.600146 0.307021
+vt 0.599885 0.306500
+vt 0.599551 0.306031
+vt 0.599154 0.305620
+vt 0.598685 0.305294
+vt 0.598168 0.305041
+vt 0.597611 0.304867
+vt 0.597021 0.304781
+vt 0.595796 0.304880
+vt 0.596406 0.304795
+vt 0.595453 0.303492
+vt 0.594672 0.303748
+vt 0.593957 0.304115
+vt 0.593316 0.304584
+vt 0.592295 0.305783
+vt 0.592761 0.305145
+vt 0.591935 0.306490
+vt 0.591688 0.307255
+vt 0.591560 0.308066
+vt 0.591560 0.308913
+vt 0.591696 0.309749
+vt 0.591953 0.310529
+vt 0.592320 0.311244
+vt 0.592789 0.311884
+vt 0.593989 0.312904
+vt 0.593351 0.312439
+vt 0.594696 0.313264
+vt 0.595462 0.313511
+vt 0.596273 0.313639
+vt 0.597121 0.313639
+vt 0.597958 0.313503
+vt 0.598738 0.313246
+vt 0.599454 0.312879
+vt 0.600094 0.312411
+vt 0.601115 0.311212
+vt 0.600650 0.311849
+vt 0.601475 0.310505
+vt 0.601723 0.309740
+vt 0.601851 0.308929
+vt 0.601851 0.308082
+vt 0.601714 0.307246
+vt 0.601458 0.306466
+vt 0.601091 0.305751
+vt 0.600622 0.305111
+vt 0.599422 0.304091
+vt 0.600060 0.304556
+vt 0.598714 0.303731
+vt 0.597949 0.303484
+vt 0.597137 0.303356
+vt 0.596290 0.303356
+vt 0.596100 0.301017
+vt 0.594881 0.301213
+vt 0.593745 0.301587
+vt 0.592704 0.302123
+vt 0.591773 0.302806
+vt 0.590291 0.304547
+vt 0.590965 0.303619
+vt 0.589766 0.305575
+vt 0.589404 0.306687
+vt 0.589217 0.307868
+vt 0.589219 0.309102
+vt 0.589415 0.310320
+vt 0.589789 0.311455
+vt 0.590326 0.312495
+vt 0.591009 0.313425
+vt 0.592752 0.314907
+vt 0.591823 0.314233
+vt 0.593781 0.315431
+vt 0.594894 0.315793
+vt 0.596075 0.315980
+vt 0.597310 0.315978
+vt 0.598529 0.315782
+vt 0.599666 0.315408
+vt 0.600706 0.314872
+vt 0.601637 0.314189
+vt 0.603119 0.312448
+vt 0.602445 0.313376
+vt 0.603644 0.311420
+vt 0.604007 0.310308
+vt 0.604193 0.309127
+vt 0.604192 0.307893
+vt 0.603995 0.306675
+vt 0.603621 0.305539
+vt 0.603085 0.304500
+vt 0.602401 0.303569
+vt 0.600658 0.302088
+vt 0.601587 0.302762
+vt 0.599630 0.301564
+vt 0.598517 0.301202
+vt 0.597335 0.301015
+vt 0.595846 0.297874
+vt 0.597601 0.297870
+vt 0.594113 0.298152
+vt 0.592498 0.298682
+vt 0.591020 0.299443
+vt 0.589698 0.300412
+vt 0.587593 0.302884
+vt 0.588549 0.301566
+vt 0.586849 0.304344
+vt 0.586335 0.305924
+vt 0.586070 0.307602
+vt 0.586073 0.309356
+vt 0.586352 0.311087
+vt 0.586883 0.312701
+vt 0.587644 0.314178
+vt 0.588613 0.315499
+vt 0.591088 0.317602
+vt 0.589769 0.316647
+vt 0.592549 0.318346
+vt 0.594130 0.318860
+vt 0.595809 0.319125
+vt 0.597564 0.319121
+vt 0.599297 0.318843
+vt 0.600912 0.318312
+vt 0.602390 0.317552
+vt 0.603713 0.316583
+vt 0.605817 0.314110
+vt 0.604861 0.315428
+vt 0.606562 0.312650
+vt 0.607076 0.311071
+vt 0.607341 0.309393
+vt 0.607337 0.307639
+vt 0.607059 0.305907
+vt 0.606528 0.304294
+vt 0.605766 0.302817
+vt 0.604797 0.301495
+vt 0.602323 0.299393
+vt 0.603642 0.300348
+vt 0.600861 0.298649
+vt 0.599280 0.298135
+vt 0.597918 0.294125
+vt 0.600189 0.294482
+vt 0.595544 0.294130
+vt 0.593199 0.294506
+vt 0.591015 0.295223
+vt 0.589016 0.296252
+vt 0.587227 0.297561
+vt 0.584381 0.300905
+vt 0.585673 0.299122
+vt 0.583374 0.302880
+vt 0.582679 0.305016
+vt 0.582322 0.307286
+vt 0.582327 0.309658
+vt 0.582703 0.312001
+vt 0.583421 0.314183
+vt 0.584450 0.316181
+vt 0.585761 0.317969
+vt 0.589107 0.320812
+vt 0.587323 0.319521
+vt 0.591083 0.321818
+vt 0.593222 0.322512
+vt 0.595493 0.322870
+vt 0.597867 0.322865
+vt 0.600211 0.322489
+vt 0.602396 0.321772
+vt 0.604395 0.320743
+vt 0.606184 0.319433
+vt 0.609030 0.316090
+vt 0.607737 0.317872
+vt 0.610036 0.314115
+vt 0.610731 0.311978
+vt 0.611089 0.309709
+vt 0.611084 0.307337
+vt 0.610707 0.304994
+vt 0.609990 0.302811
+vt 0.608960 0.300814
+vt 0.607650 0.299026
+vt 0.604304 0.296183
+vt 0.606088 0.297474
+vt 0.602327 0.295177
+vt 0.601184 0.290483
+vt 0.603933 0.291376
+vt 0.598264 0.290024
+vt 0.595212 0.290031
+vt 0.592198 0.290514
+vt 0.589390 0.291435
+vt 0.586820 0.292757
+vt 0.584521 0.294441
+vt 0.580864 0.298738
+vt 0.582525 0.296447
+vt 0.579570 0.301276
+vt 0.578677 0.304022
+vt 0.578217 0.306940
+vt 0.578224 0.309990
+vt 0.578707 0.313001
+vt 0.579630 0.315807
+vt 0.580953 0.318374
+vt 0.582638 0.320672
+vt 0.586938 0.324326
+vt 0.584645 0.322667
+vt 0.589478 0.325619
+vt 0.592227 0.326512
+vt 0.595146 0.326971
+vt 0.598199 0.326964
+vt 0.601212 0.326481
+vt 0.604020 0.325559
+vt 0.606590 0.324237
+vt 0.608889 0.322554
+vt 0.612547 0.318257
+vt 0.610886 0.320548
+vt 0.613840 0.315719
+vt 0.614733 0.312972
+vt 0.615193 0.310055
+vt 0.615186 0.307005
+vt 0.614703 0.303994
+vt 0.613780 0.301188
+vt 0.612458 0.298620
+vt 0.610773 0.296323
+vt 0.606472 0.292668
+vt 0.608765 0.294328
+vt 0.605578 0.287481
+vt 0.608695 0.289068
+vt 0.602204 0.286385
+vt 0.598620 0.285820
+vt 0.594872 0.285829
+vt 0.591172 0.286421
+vt 0.587724 0.287554
+vt 0.584570 0.289177
+vt 0.581749 0.291243
+vt 0.577261 0.296517
+vt 0.579299 0.293705
+vt 0.575673 0.299631
+vt 0.574575 0.303003
+vt 0.574010 0.306584
+vt 0.574019 0.310329
+vt 0.574612 0.314027
+vt 0.575745 0.317471
+vt 0.577370 0.320623
+vt 0.579438 0.323442
+vt 0.584715 0.327927
+vt 0.581902 0.325890
+vt 0.587832 0.329514
+vt 0.591206 0.330610
+vt 0.594791 0.331175
+vt 0.598538 0.331166
+vt 0.602239 0.330573
+vt 0.605686 0.329441
+vt 0.608840 0.327818
+vt 0.611661 0.325751
+vt 0.616150 0.320478
+vt 0.614111 0.323289
+vt 0.617738 0.317363
+vt 0.618835 0.313992
+vt 0.619400 0.310410
+vt 0.619392 0.306666
+vt 0.618798 0.302968
+vt 0.617665 0.299524
+vt 0.616040 0.296372
+vt 0.613973 0.293553
+vt 0.611509 0.291105
+vt 0.610836 0.285601
+vt 0.614151 0.288001
+vt 0.607165 0.283730
+vt 0.603189 0.282437
+vt 0.598963 0.281769
+vt 0.594545 0.281779
+vt 0.590182 0.282478
+vt 0.586118 0.283815
+vt 0.582402 0.285730
+vt 0.579078 0.288165
+vt 0.573791 0.294377
+vt 0.576193 0.291066
+vt 0.571919 0.298046
+vt 0.570624 0.302019
+vt 0.569957 0.306241
+vt 0.569966 0.310656
+vt 0.570666 0.315016
+vt 0.572003 0.319076
+vt 0.573920 0.322789
+vt 0.576357 0.326110
+vt 0.582574 0.331394
+vt 0.579260 0.328994
+vt 0.586246 0.333264
+vt 0.590222 0.334558
+vt 0.594447 0.335225
+vt 0.598866 0.335216
+vt 0.603229 0.334516
+vt 0.607292 0.333180
+vt 0.611008 0.331265
+vt 0.614332 0.328830
+vt 0.619619 0.322617
+vt 0.617218 0.325929
+vt 0.621492 0.318949
+vt 0.622786 0.314976
+vt 0.623454 0.310754
+vt 0.623444 0.306339
+vt 0.622744 0.301979
+vt 0.621407 0.297919
+vt 0.619491 0.294205
+vt 0.617053 0.290884
+vt 0.619822 0.288484
+vt 0.612763 0.282485
+vt 0.616526 0.285210
+vt 0.608593 0.280358
+vt 0.604077 0.278885
+vt 0.599275 0.278124
+vt 0.594250 0.278134
+vt 0.589289 0.278930
+vt 0.584671 0.280452
+vt 0.580450 0.282630
+vt 0.576676 0.285398
+vt 0.570672 0.292452
+vt 0.573400 0.288692
+vt 0.568544 0.296619
+vt 0.567070 0.301131
+vt 0.566308 0.305930
+vt 0.566319 0.310951
+vt 0.567115 0.315908
+vt 0.568638 0.320522
+vt 0.570818 0.324740
+vt 0.573588 0.328511
+vt 0.580648 0.334510
+vt 0.576884 0.331785
+vt 0.584817 0.336637
+vt 0.589333 0.338110
+vt 0.594136 0.338871
+vt 0.599161 0.338860
+vt 0.604122 0.338065
+vt 0.608739 0.336543
+vt 0.612960 0.334365
+vt 0.616734 0.331596
+vt 0.622738 0.324542
+vt 0.620011 0.328303
+vt 0.624866 0.320376
+vt 0.626341 0.315863
+vt 0.627102 0.311065
+vt 0.627092 0.306044
+vt 0.626296 0.301087
+vt 0.624773 0.296473
+vt 0.622592 0.292255
+vt 0.625142 0.290649
+vt 0.622096 0.286510
+vt 0.614348 0.279925
+vt 0.618479 0.282916
+vt 0.609771 0.277586
+vt 0.604810 0.275964
+vt 0.599533 0.275123
+vt 0.594007 0.275134
+vt 0.588552 0.276010
+vt 0.583478 0.277686
+vt 0.578842 0.280083
+vt 0.574700 0.283126
+vt 0.568110 0.290868
+vt 0.571104 0.286741
+vt 0.565770 0.295442
+vt 0.564146 0.300399
+vt 0.563305 0.305672
+vt 0.563316 0.311193
+vt 0.564192 0.316645
+vt 0.565870 0.321715
+vt 0.568269 0.326346
+vt 0.571314 0.330485
+vt 0.579063 0.337070
+vt 0.574932 0.334079
+vt 0.583640 0.339409
+vt 0.588600 0.341031
+vt 0.593877 0.341872
+vt 0.599403 0.341861
+vt 0.604859 0.340985
+vt 0.609933 0.339309
+vt 0.614568 0.336912
+vt 0.618710 0.333869
+vt 0.625300 0.326126
+vt 0.622307 0.330254
+vt 0.627641 0.321553
+vt 0.629264 0.316596
+vt 0.630105 0.311323
+vt 0.630095 0.305802
+vt 0.629218 0.300350
+vt 0.627540 0.295280
+vt 0.629534 0.294417
+vt 0.626976 0.289488
+vt 0.623731 0.285085
+vt 0.615493 0.278084
+vt 0.619887 0.281262
+vt 0.610623 0.275590
+vt 0.605343 0.273859
+vt 0.599723 0.272958
+vt 0.593832 0.272971
+vt 0.588016 0.273904
+vt 0.582614 0.275694
+vt 0.577681 0.278250
+vt 0.573275 0.281492
+vt 0.566268 0.289725
+vt 0.569448 0.285334
+vt 0.563773 0.294591
+vt 0.562040 0.299867
+vt 0.561139 0.305482
+vt 0.561151 0.311368
+vt 0.562085 0.317180
+vt 0.563876 0.322578
+vt 0.566434 0.327507
+vt 0.569679 0.331910
+vt 0.577918 0.338911
+vt 0.573524 0.335733
+vt 0.582788 0.341404
+vt 0.588068 0.343136
+vt 0.593687 0.344036
+vt 0.599578 0.344024
+vt 0.605394 0.343091
+vt 0.610796 0.341301
+vt 0.615730 0.338745
+vt 0.620136 0.335503
+vt 0.627143 0.327270
+vt 0.623962 0.331661
+vt 0.629638 0.322404
+vt 0.631371 0.317128
+vt 0.632272 0.311513
+vt 0.632259 0.305627
+vt 0.631326 0.299815
+vt 0.632653 0.299481
+vt 0.630789 0.293878
+vt 0.628130 0.288761
+vt 0.624758 0.284193
+vt 0.616209 0.276928
+vt 0.620761 0.280234
+vt 0.611155 0.274336
+vt 0.605674 0.272534
+vt 0.599840 0.271596
+vt 0.593721 0.271596
+vt 0.587682 0.272577
+vt 0.582075 0.274440
+vt 0.576954 0.277097
+vt 0.572381 0.280466
+vt 0.565111 0.289008
+vt 0.568420 0.284460
+vt 0.562517 0.294059
+vt 0.560714 0.299536
+vt 0.559775 0.305366
+vt 0.559775 0.311479
+vt 0.560757 0.317513
+vt 0.562621 0.323117
+vt 0.565280 0.328234
+vt 0.568652 0.332802
+vt 0.577201 0.340067
+vt 0.572649 0.336761
+vt 0.582255 0.342659
+vt 0.587737 0.344460
+vt 0.593571 0.345399
+vt 0.599689 0.345399
+vt 0.605728 0.344418
+vt 0.611336 0.342555
+vt 0.616457 0.339898
+vt 0.621029 0.336528
+vt 0.628299 0.327986
+vt 0.624991 0.332535
+vt 0.630893 0.322936
+vt 0.632696 0.317459
+vt 0.633636 0.311629
+vt 0.633636 0.305516
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570485 0.470581
+vt 0.570486 0.470581
+vt 0.570485 0.470580
+vt 0.570486 0.470580
+vt 0.570484 0.470579
+vt 0.570485 0.470580
+vt 0.570483 0.470578
+vt 0.570485 0.470579
+vt 0.570481 0.470577
+vt 0.570484 0.470578
+vt 0.570478 0.470574
+vt 0.570483 0.470576
+vt 0.570472 0.470568
+vt 0.570481 0.470572
+vt 0.570459 0.470558
+vt 0.570477 0.470564
+vt 0.570431 0.470535
+vt 0.570469 0.470549
+vt 0.570364 0.470480
+vt 0.570452 0.470518
+vt 0.570188 0.470335
+vt 0.570412 0.470440
+vt 0.569671 0.469896
+vt 0.570310 0.470234
+vt 0.567996 0.468387
+vt 0.570020 0.469613
+vt 0.562307 0.462943
+vt 0.569109 0.467508
+vt 0.544034 0.444330
+vt 0.565992 0.460130
+vt 0.569315 0.433640
+vt 0.570603 0.459271
+vt 0.570511 0.467239
+vt 0.570456 0.469519
+vt 0.570462 0.470196
+vt 0.570471 0.470424
+vt 0.570477 0.470510
+vt 0.570481 0.470546
+vt 0.570483 0.470562
+vt 0.570484 0.470571
+vt 0.570485 0.470575
+vt 0.570485 0.470577
+vt 0.570486 0.470579
+vt 0.570486 0.470579
+vt 0.570486 0.470580
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470580
+vt 0.570486 0.470579
+vt 0.570486 0.470579
+vt 0.570486 0.470577
+vt 0.570486 0.470575
+vt 0.570487 0.470571
+vt 0.570489 0.470562
+vt 0.570491 0.470546
+vt 0.570499 0.470511
+vt 0.570521 0.470429
+vt 0.570585 0.470215
+vt 0.570793 0.469595
+vt 0.571515 0.467593
+vt 0.574178 0.460309
+vt 0.579903 0.436948
+vt 0.586174 0.442931
+vt 0.576056 0.462127
+vt 0.572133 0.468082
+vt 0.571014 0.469740
+vt 0.570672 0.470262
+vt 0.570558 0.470446
+vt 0.570516 0.470518
+vt 0.570500 0.470549
+vt 0.570493 0.470564
+vt 0.570490 0.470571
+vt 0.570488 0.470575
+vt 0.570487 0.470578
+vt 0.570487 0.470579
+vt 0.570487 0.470580
+vt 0.570486 0.470580
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570487 0.470580
+vt 0.570487 0.470580
+vt 0.570487 0.470579
+vt 0.570488 0.470578
+vt 0.570489 0.470576
+vt 0.570491 0.470573
+vt 0.570496 0.470566
+vt 0.570506 0.470554
+vt 0.570529 0.470527
+vt 0.570584 0.470468
+vt 0.570729 0.470318
+vt 0.571145 0.469899
+vt 0.572454 0.468588
+vt 0.576890 0.463918
+vt 0.588892 0.449155
+vt 0.589532 0.454680
+vt 0.577260 0.465722
+vt 0.572610 0.469102
+vt 0.571221 0.470065
+vt 0.570766 0.470379
+vt 0.570603 0.470493
+vt 0.570539 0.470539
+vt 0.570511 0.470560
+vt 0.570499 0.470569
+vt 0.570493 0.470574
+vt 0.570490 0.470577
+vt 0.570488 0.470578
+vt 0.570488 0.470579
+vt 0.570487 0.470580
+vt 0.570487 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570487 0.470581
+vt 0.570487 0.470580
+vt 0.570488 0.470580
+vt 0.570489 0.470579
+vt 0.570491 0.470578
+vt 0.570494 0.470576
+vt 0.570501 0.470573
+vt 0.570515 0.470566
+vt 0.570545 0.470553
+vt 0.570615 0.470522
+vt 0.570790 0.470447
+vt 0.571262 0.470239
+vt 0.572657 0.469613
+vt 0.577290 0.467469
+vt 0.589401 0.459728
+vt 0.589141 0.464912
+vt 0.577034 0.469117
+vt 0.572636 0.470138
+vt 0.571274 0.470431
+vt 0.570800 0.470525
+vt 0.570622 0.470557
+vt 0.570549 0.470569
+vt 0.570517 0.470575
+vt 0.570502 0.470577
+vt 0.570495 0.470579
+vt 0.570491 0.470580
+vt 0.570489 0.470580
+vt 0.570488 0.470580
+vt 0.570487 0.470581
+vt 0.570487 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570487 0.470581
+vt 0.570487 0.470581
+vt 0.570488 0.470581
+vt 0.570489 0.470581
+vt 0.570491 0.470581
+vt 0.570495 0.470582
+vt 0.570502 0.470583
+vt 0.570517 0.470585
+vt 0.570548 0.470589
+vt 0.570619 0.470599
+vt 0.570792 0.470621
+vt 0.571244 0.470670
+vt 0.572523 0.470780
+vt 0.576522 0.471044
+vt 0.588668 0.470962
+vt 0.586754 0.479022
+vt 0.575384 0.473223
+vt 0.572186 0.471540
+vt 0.571124 0.470953
+vt 0.570745 0.470735
+vt 0.570599 0.470649
+vt 0.570539 0.470613
+vt 0.570512 0.470597
+vt 0.570500 0.470589
+vt 0.570494 0.470585
+vt 0.570490 0.470583
+vt 0.570489 0.470583
+vt 0.570488 0.470582
+vt 0.570487 0.470582
+vt 0.570487 0.470582
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570487 0.470582
+vt 0.570487 0.470583
+vt 0.570488 0.470584
+vt 0.570488 0.470585
+vt 0.570490 0.470589
+vt 0.570494 0.470595
+vt 0.570501 0.470607
+vt 0.570516 0.470635
+vt 0.570549 0.470696
+vt 0.570630 0.470845
+vt 0.570835 0.471230
+vt 0.571394 0.472296
+vt 0.573044 0.475544
+vt 0.579274 0.488832
+vt 0.569813 0.476271
+vt 0.566686 0.489095
+vt 0.567199 0.475685
+vt 0.558724 0.485066
+vt 0.565266 0.474672
+vt 0.553588 0.481075
+vt 0.563758 0.473594
+vt 0.549408 0.477625
+vt 0.562481 0.472457
+vt 0.545451 0.474152
+vt 0.561383 0.471024
+vt 0.541713 0.469703
+vt 0.560644 0.468984
+vt 0.538981 0.463253
+vt 0.560727 0.466200
+vt 0.538981 0.454405
+vt 0.567514 0.469385
+vt 0.569514 0.470213
+vt 0.570130 0.470447
+vt 0.570340 0.470524
+vt 0.570420 0.470554
+vt 0.570453 0.470567
+vt 0.570469 0.470573
+vt 0.570476 0.470576
+vt 0.570481 0.470578
+vt 0.570483 0.470579
+vt 0.570484 0.470580
+vt 0.570485 0.470580
+vt 0.570485 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570485 0.470581
+vt 0.570484 0.470581
+vt 0.570484 0.470580
+vt 0.570482 0.470580
+vt 0.570480 0.470580
+vt 0.570476 0.470579
+vt 0.570468 0.470578
+vt 0.570451 0.470576
+vt 0.570416 0.470572
+vt 0.570333 0.470565
+vt 0.570117 0.470547
+vt 0.569487 0.470489
+vt 0.567467 0.470237
+vt 0.567658 0.470872
+vt 0.569534 0.470703
+vt 0.570129 0.470629
+vt 0.570337 0.470600
+vt 0.570417 0.470588
+vt 0.570452 0.470584
+vt 0.570468 0.470582
+vt 0.570476 0.470581
+vt 0.570480 0.470581
+vt 0.570482 0.470581
+vt 0.570484 0.470581
+vt 0.570485 0.470581
+vt 0.570485 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570486 0.470581
+vt 0.570485 0.470582
+vt 0.570485 0.470582
+vt 0.570484 0.470582
+vt 0.570482 0.470582
+vt 0.570480 0.470582
+vt 0.570476 0.470583
+vt 0.570469 0.470586
+vt 0.570454 0.470591
+vt 0.570422 0.470603
+vt 0.570347 0.470629
+vt 0.570157 0.470696
+vt 0.569621 0.470868
+vt 0.567961 0.471331
+vt 0.568336 0.471706
+vt 0.569738 0.471010
+vt 0.570199 0.470755
+vt 0.570365 0.470656
+vt 0.570430 0.470616
+vt 0.570458 0.470598
+vt 0.570471 0.470590
+vt 0.570477 0.470585
+vt 0.570481 0.470583
+vt 0.570483 0.470582
+vt 0.570484 0.470582
+vt 0.570485 0.470582
+vt 0.570485 0.470582
+vt 0.570486 0.470582
+vt 0.570486 0.470581
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570485 0.470582
+vt 0.570484 0.470583
+vt 0.570483 0.470583
+vt 0.570482 0.470585
+vt 0.570479 0.470587
+vt 0.570474 0.470593
+vt 0.570464 0.470604
+vt 0.570442 0.470628
+vt 0.570391 0.470682
+vt 0.570260 0.470812
+vt 0.569898 0.471146
+vt 0.568810 0.472064
+vt 0.569452 0.472390
+vt 0.570127 0.471269
+vt 0.570349 0.470862
+vt 0.570429 0.470704
+vt 0.570460 0.470638
+vt 0.570473 0.470609
+vt 0.570479 0.470596
+vt 0.570482 0.470589
+vt 0.570483 0.470586
+vt 0.570484 0.470584
+vt 0.570485 0.470583
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570486 0.470582
+vt 0.570486 0.470583
+vt 0.570486 0.470584
+vt 0.570486 0.470586
+vt 0.570486 0.470590
+vt 0.570486 0.470597
+vt 0.570486 0.470611
+vt 0.570485 0.470643
+vt 0.570483 0.470714
+vt 0.570476 0.470885
+vt 0.570445 0.471328
+vt 0.570326 0.472556
+vt 0.581128 0.346117
+vt 0.594255 0.345472
+vt 0.590888 0.358371
+vt 0.596151 0.360749
+vt 0.604574 0.349832
+vt 0.599688 0.364223
+vt 0.611709 0.356483
+vt 0.601791 0.368097
+vt 0.616202 0.364176
+vt 0.602709 0.372090
+vt 0.618407 0.372392
+vt 0.602553 0.376062
+vt 0.618407 0.380795
+vt 0.601312 0.379905
+vt 0.616085 0.389055
+vt 0.598863 0.383468
+vt 0.611107 0.396729
+vt 0.594985 0.386437
+vt 0.602950 0.402977
+vt 0.589498 0.388164
+vt 0.558363 0.433110
+vt 0.583896 0.387712
+vt 0.588518 0.380698
+vt 0.585170 0.380569
+vt 0.587933 0.376597
+vt 0.585799 0.376555
+vt 0.587531 0.373917
+vt 0.586195 0.373903
+vt 0.587233 0.372103
+vt 0.586464 0.372087
+vt 0.586661 0.370962
+vt 0.587039 0.370945
+vt 0.586772 0.370360
+vt 0.586919 0.370344
+vt 0.586810 0.370105
+vt 0.586854 0.370095
+vt 0.586814 0.370022
+vt 0.586825 0.370018
+vt 0.586814 0.370003
+vt 0.586815 0.370002
+vt 0.586814 0.370002
+vt 0.586814 0.370001
+vt 0.586815 0.370002
+vt 0.586814 0.370001
+vt 0.586815 0.370001
+vt 0.586814 0.370001
+vt 0.586815 0.370001
+vt 0.586814 0.370001
+vt 0.586814 0.370001
+vt 0.586814 0.370001
+vt 0.586814 0.370001
+vt 0.586814 0.370000
+vt 0.586814 0.370001
+vt 0.586814 0.370000
+vt 0.586813 0.370000
+vt 0.586814 0.369999
+vt 0.586813 0.369999
+vt 0.586813 0.369998
+vt 0.586812 0.369998
+vt 0.586814 0.369999
+vt 0.586814 0.369997
+vt 0.586815 0.370000
+vt 0.586815 0.370000
+vt 0.586815 0.370001
+vt 0.586815 0.370001
+vt 0.586815 0.370001
+vt 0.586815 0.370001
+vt 0.586815 0.370001
+vt 0.586815 0.370001
+vt 0.586816 0.370002
+vt 0.586833 0.370012
+vt 0.586893 0.370074
+vt 0.587049 0.370281
+vt 0.587382 0.370794
+vt 0.587955 0.371808
+vt 0.588789 0.373449
+vt 0.589942 0.375887
+vt 0.591701 0.379624
+vt 0.593929 0.377890
+vt 0.591331 0.374773
+vt 0.589645 0.372726
+vt 0.588432 0.371373
+vt 0.587605 0.370579
+vt 0.587136 0.370200
+vt 0.586921 0.370050
+vt 0.586841 0.370006
+vt 0.586818 0.370000
+vt 0.586816 0.370000
+vt 0.586816 0.370000
+vt 0.586815 0.370000
+vt 0.586815 0.370000
+vt 0.586815 0.370000
+vt 0.586815 0.370000
+vt 0.586815 0.370000
+vt 0.586815 0.369999
+vt 0.586815 0.369998
+vt 0.586814 0.369996
+vt 0.586815 0.369997
+vt 0.586815 0.369995
+vt 0.586816 0.369998
+vt 0.586816 0.369999
+vt 0.586816 0.369999
+vt 0.586816 0.370000
+vt 0.586816 0.370000
+vt 0.586816 0.370000
+vt 0.586817 0.370000
+vt 0.586818 0.369999
+vt 0.586820 0.369999
+vt 0.586849 0.369999
+vt 0.586943 0.370024
+vt 0.587189 0.370110
+vt 0.587722 0.370343
+vt 0.588684 0.370886
+vt 0.590128 0.371896
+vt 0.592160 0.373475
+vt 0.595312 0.375845
+vt 0.595973 0.373670
+vt 0.592502 0.372122
+vt 0.590284 0.371055
+vt 0.588745 0.370409
+vt 0.587752 0.370112
+vt 0.587213 0.370017
+vt 0.586960 0.369993
+vt 0.586857 0.369989
+vt 0.586822 0.369996
+vt 0.586820 0.369997
+vt 0.586818 0.369998
+vt 0.586817 0.369998
+vt 0.586817 0.369998
+vt 0.586817 0.369998
+vt 0.586816 0.369998
+vt 0.586816 0.369997
+vt 0.586816 0.369996
+vt 0.586816 0.369995
+vt 0.586815 0.369992
+vt 0.586816 0.369992
+vt 0.586815 0.369990
+vt 0.586817 0.369994
+vt 0.586817 0.369995
+vt 0.586818 0.369996
+vt 0.586818 0.369996
+vt 0.586818 0.369996
+vt 0.586819 0.369995
+vt 0.586820 0.369995
+vt 0.586822 0.369994
+vt 0.586825 0.369992
+vt 0.586865 0.369975
+vt 0.586970 0.369957
+vt 0.587210 0.369920
+vt 0.587710 0.369899
+vt 0.588651 0.369977
+vt 0.590165 0.370264
+vt 0.592421 0.370790
+vt 0.595996 0.371456
+vt 0.595409 0.369257
+vt 0.591964 0.369518
+vt 0.589822 0.369549
+vt 0.588439 0.369602
+vt 0.587610 0.369712
+vt 0.587171 0.369826
+vt 0.586965 0.369906
+vt 0.586871 0.369957
+vt 0.586828 0.369984
+vt 0.586825 0.369987
+vt 0.586822 0.369989
+vt 0.586821 0.369991
+vt 0.586820 0.369992
+vt 0.586819 0.369992
+vt 0.586819 0.369992
+vt 0.586818 0.369991
+vt 0.586817 0.369990
+vt 0.586816 0.369988
+vt 0.586814 0.369987
+vt 0.586815 0.369985
+vt 0.586812 0.369984
+vt 0.586817 0.369986
+vt 0.586818 0.369986
+vt 0.586818 0.369987
+vt 0.586819 0.369987
+vt 0.586820 0.369986
+vt 0.586821 0.369984
+vt 0.586823 0.369982
+vt 0.586825 0.369979
+vt 0.586828 0.369974
+vt 0.586869 0.369934
+vt 0.586946 0.369862
+vt 0.587110 0.369742
+vt 0.587458 0.369551
+vt 0.588129 0.369278
+vt 0.589288 0.368906
+vt 0.591146 0.368323
+vt 0.594172 0.367125
+vt 0.592152 0.365192
+vt 0.589917 0.367248
+vt 0.588551 0.368343
+vt 0.587721 0.369002
+vt 0.587254 0.369419
+vt 0.587017 0.369674
+vt 0.586907 0.369823
+vt 0.586856 0.369910
+vt 0.586825 0.369962
+vt 0.586822 0.369968
+vt 0.586820 0.369973
+vt 0.586819 0.369976
+vt 0.586818 0.369979
+vt 0.586817 0.369980
+vt 0.586816 0.369981
+vt 0.586816 0.369982
+vt 0.586814 0.369982
+vt 0.586812 0.369982
+vt 0.586810 0.369982
+vt 0.586809 0.369980
+vt 0.586808 0.369982
+vt 0.586811 0.369979
+vt 0.586811 0.369978
+vt 0.586812 0.369977
+vt 0.586812 0.369976
+vt 0.586812 0.369973
+vt 0.586813 0.369970
+vt 0.586813 0.369965
+vt 0.586814 0.369959
+vt 0.586814 0.369951
+vt 0.586828 0.369893
+vt 0.586848 0.369799
+vt 0.586891 0.369632
+vt 0.586988 0.369335
+vt 0.587188 0.368818
+vt 0.587557 0.367954
+vt 0.588176 0.366496
+vt 0.589177 0.363842
+vt 0.584070 0.358434
+vt 0.585362 0.363876
+vt 0.570590 0.352440
+vt 0.578448 0.361833
+vt 0.582057 0.365590
+vt 0.584062 0.367508
+vt 0.585975 0.366534
+vt 0.585225 0.368592
+vt 0.586318 0.367996
+vt 0.585930 0.369146
+vt 0.586528 0.368847
+vt 0.586352 0.369487
+vt 0.586657 0.369357
+vt 0.586729 0.369638
+vt 0.586583 0.369707
+vt 0.586769 0.369800
+vt 0.586699 0.369835
+vt 0.586789 0.369893
+vt 0.586756 0.369912
+vt 0.586798 0.369950
+vt 0.586784 0.369960
+vt 0.586800 0.369958
+vt 0.586801 0.369964
+vt 0.586788 0.369967
+vt 0.586803 0.369969
+vt 0.586792 0.369972
+vt 0.586804 0.369972
+vt 0.586795 0.369975
+vt 0.586805 0.369975
+vt 0.586797 0.369978
+vt 0.586805 0.369976
+vt 0.586799 0.369980
+vt 0.586801 0.369981
+vt 0.586806 0.369977
+vt 0.586802 0.369982
+vt 0.586806 0.369979
+vt 0.586804 0.369982
+vt 0.586806 0.369980
+vt 0.586806 0.369983
+vt 0.586807 0.369982
+vt 0.586805 0.369984
+vt 0.586802 0.369989
+vt 0.586803 0.369985
+vt 0.586804 0.369987
+vt 0.586804 0.369991
+vt 0.586806 0.369993
+vt 0.586804 0.369993
+vt 0.586807 0.369996
+vt 0.586806 0.369996
+vt 0.586809 0.369997
+vt 0.586809 0.369998
+vt 0.586811 0.369998
+vt 0.586811 0.369999
+vt 0.586812 0.369999
+vt 0.586813 0.370000
+vt 0.586813 0.370001
+vt 0.586813 0.370001
+vt 0.586814 0.370001
+vt 0.586814 0.370001
+vt 0.586813 0.370001
+vt 0.586813 0.370002
+vt 0.586813 0.370002
+vt 0.586812 0.370003
+vt 0.586804 0.370024
+vt 0.586769 0.370104
+vt 0.586650 0.370332
+vt 0.586364 0.370864
+vt 0.585873 0.371881
+vt 0.585173 0.373538
+vt 0.584210 0.375911
+vt 0.582547 0.379564
+vt 0.579534 0.385989
+vt 0.570578 0.400526
+vt 0.576354 0.383362
+vt 0.580773 0.378047
+vt 0.583084 0.375011
+vt 0.584463 0.372963
+vt 0.585468 0.371547
+vt 0.586155 0.370702
+vt 0.586554 0.370276
+vt 0.586731 0.370094
+vt 0.586791 0.370025
+vt 0.586809 0.370004
+vt 0.586811 0.370003
+vt 0.586812 0.370002
+vt 0.586812 0.370002
+vt 0.586813 0.370001
+vt 0.586813 0.370001
+vt 0.586812 0.370001
+vt 0.586812 0.370001
+vt 0.586811 0.370000
+vt 0.586810 0.369999
+vt 0.586810 0.370000
+vt 0.586811 0.370001
+vt 0.586811 0.370001
+vt 0.586811 0.370001
+vt 0.586810 0.370002
+vt 0.586809 0.370002
+vt 0.586808 0.370003
+vt 0.586805 0.370004
+vt 0.586777 0.370022
+vt 0.586694 0.370072
+vt 0.586484 0.370200
+vt 0.586029 0.370508
+vt 0.585239 0.371155
+vt 0.584046 0.372287
+vt 0.582394 0.373945
+vt 0.579677 0.376308
+vt 0.574162 0.380188
+vt 0.565110 0.394315
+vt 0.561854 0.387711
+vt 0.559775 0.380685
+vt 0.573240 0.376830
+vt 0.579062 0.374462
+vt 0.582078 0.372811
+vt 0.583881 0.371577
+vt 0.585159 0.370751
+vt 0.585975 0.370302
+vt 0.586442 0.370110
+vt 0.586664 0.370039
+vt 0.586760 0.370013
+vt 0.586799 0.370002
+vt 0.586803 0.370002
+vt 0.586805 0.370001
+vt 0.586807 0.370001
+vt 0.586807 0.370001
+vt 0.586808 0.370000
+vt 0.586808 0.370000
+vt 0.586808 0.369999
+vt 0.586807 0.369998
+vt 0.586804 0.369995
+vt 0.586804 0.369997
+vt 0.586804 0.369998
+vt 0.586804 0.369998
+vt 0.586803 0.369998
+vt 0.586802 0.369999
+vt 0.586800 0.369999
+vt 0.586796 0.369998
+vt 0.586791 0.369998
+vt 0.586745 0.369997
+vt 0.586644 0.369993
+vt 0.586428 0.370011
+vt 0.585982 0.370095
+vt 0.585195 0.370350
+vt 0.583917 0.370859
+vt 0.582076 0.371664
+vt 0.578938 0.372518
+vt 0.572981 0.373352
+vt 0.559960 0.373693
+vt 0.573561 0.369668
+vt 0.579277 0.370397
+vt 0.582280 0.370304
+vt 0.584107 0.370020
+vt 0.585339 0.369927
+vt 0.586051 0.369900
+vt 0.586444 0.369909
+vt 0.586641 0.369940
+vt 0.586735 0.369971
+vt 0.586783 0.369989
+vt 0.586789 0.369991
+vt 0.586794 0.369993
+vt 0.586797 0.369993
+vt 0.586799 0.369994
+vt 0.586800 0.369994
+vt 0.586801 0.369993
+vt 0.586801 0.369993
+vt 0.586802 0.369991
+vt 0.586801 0.369986
+vt 0.586800 0.369987
+vt 0.586799 0.369988
+vt 0.586798 0.369987
+vt 0.586796 0.369987
+vt 0.586793 0.369985
+vt 0.586790 0.369983
+vt 0.586785 0.369980
+vt 0.586779 0.369975
+vt 0.586738 0.369941
+vt 0.586658 0.369885
+vt 0.586492 0.369805
+vt 0.586164 0.369693
+vt 0.585568 0.369541
+vt 0.584544 0.369302
+vt 0.582873 0.368834
+vt 0.580216 0.368116
+vt 0.575204 0.365778
+vt 0.561535 0.366783
+vt 0.564754 0.359728
+vt 0.586800 0.369981
+vt 0.555717 0.436564
+vt 0.570527 0.433567
+s 0
+usemtl FrontColor
+f 1/1/1 3/2/2 4/3/3
+f 2/4/4 6/5/5 3/2/2
+f 5/6/6 8/7/7 6/5/5
+f 7/8/8 10/9/9 8/7/7
+f 9/10/10 12/11/11 10/9/9
+f 13/12/12 12/11/11 11/13/13
+f 15/14/14 14/15/15 13/12/12
+f 17/16/16 16/17/17 15/14/14
+f 19/18/18 18/19/19 17/16/16
+f 21/20/20 20/21/21 19/18/18
+f 21/20/20 24/22/22 22/23/23
+f 23/24/24 26/25/25 24/22/22
+f 25/26/26 28/27/27 26/25/25
+f 27/28/28 30/29/29 28/27/27
+f 29/30/30 32/31/31 30/29/32
+f 33/32/33 32/31/31 31/33/34
+f 35/34/35 34/35/36 33/32/37
+f 37/36/38 36/37/39 35/34/35
+f 39/38/40 38/39/41 37/36/38
+f 41/40/42 40/41/43 39/38/40
+f 41/40/42 44/42/44 42/43/45
+f 43/44/46 46/45/47 44/42/44
+f 45/46/48 48/47/49 46/45/47
+f 47/48/50 50/49/51 48/47/49
+f 49/50/52 52/51/53 50/49/51
+f 53/52/54 52/51/53 51/53/55
+f 55/54/56 54/55/57 53/52/54
+f 57/56/58 56/57/59 55/54/56
+f 59/58/60 58/59/61 57/56/58
+f 61/60/62 60/61/63 59/58/60
+f 61/60/62 64/62/64 62/63/65
+f 63/64/66 66/65/67 64/62/64
+f 65/66/68 68/67/69 66/65/67
+f 67/68/70 70/69/71 68/67/69
+f 69/70/72 72/71/73 70/69/74
+f 73/72/75 72/71/73 71/73/76
+f 75/74/77 74/75/78 73/72/79
+f 77/76/80 76/77/81 75/74/77
+f 79/78/82 78/79/83 77/76/80
+f 1/1/1 80/80/84 79/78/82
+f 4/3/3 82/81/85 80/80/86
+f 4/3/3 83/82/87 81/83/88
+f 3/2/89 84/84/90 83/82/87
+f 6/5/91 85/85/92 84/84/90
+f 8/7/93 86/86/94 85/85/92
+f 10/9/95 87/87/96 86/86/94
+f 14/15/97 87/87/96 12/11/98
+f 16/17/17 88/88/99 14/15/97
+f 18/19/100 89/89/101 16/17/17
+f 20/21/21 90/90/102 18/19/100
+f 22/23/23 91/91/103 20/21/104
+f 22/23/23 93/92/105 92/93/106
+f 24/22/22 94/94/107 93/92/105
+f 26/25/108 95/95/109 94/94/107
+f 28/27/110 96/96/111 95/95/109
+f 30/29/29 97/97/112 96/96/111
+f 34/35/113 97/97/112 32/31/114
+f 36/37/39 98/98/115 34/35/113
+f 38/39/116 99/99/117 36/37/39
+f 40/41/43 100/100/118 38/39/116
+f 42/43/45 101/101/119 40/41/120
+f 42/43/45 103/102/121 102/103/122
+f 44/42/123 104/104/124 103/102/121
+f 46/45/125 105/105/126 104/104/124
+f 48/47/127 106/106/128 105/105/126
+f 50/49/129 107/107/130 106/106/128
+f 54/55/131 107/107/130 52/51/132
+f 56/57/59 108/108/133 54/55/131
+f 58/59/134 109/109/135 56/57/59
+f 60/61/63 110/110/136 58/59/134
+f 62/63/65 111/111/137 60/61/138
+f 62/63/65 113/112/139 112/113/140
+f 64/62/64 114/114/141 113/112/139
+f 66/65/142 115/115/143 114/114/141
+f 68/67/144 116/116/145 115/115/143
+f 70/69/71 117/117/146 116/116/145
+f 74/75/147 117/117/146 72/71/148
+f 76/77/81 118/118/149 74/75/147
+f 78/79/150 119/119/151 76/77/81
+f 80/80/84 120/120/152 78/79/150
+f 120/120/152 121/121/153 122/122/154
+f 82/81/85 123/123/155 121/121/156
+f 83/82/87 123/123/155 81/83/88
+f 84/84/90 124/124/157 83/82/87
+f 85/85/92 125/125/158 84/84/90
+f 86/86/159 126/126/160 85/85/92
+f 87/87/96 127/127/161 86/86/159
+f 87/87/96 129/128/162 128/129/163
+f 88/88/99 130/130/164 129/128/162
+f 89/89/165 131/131/166 130/130/167
+f 90/90/102 132/132/168 131/131/166
+f 91/91/103 133/133/169 132/132/170
+f 93/92/105 133/133/169 92/93/106
+f 94/94/107 134/134/171 93/92/105
+f 95/95/109 135/135/172 94/94/107
+f 96/96/173 136/136/174 95/95/109
+f 97/97/112 137/137/175 96/96/173
+f 97/97/112 139/138/176 138/139/177
+f 98/98/115 140/140/178 139/138/176
+f 99/99/117 141/141/179 140/140/180
+f 100/100/118 142/142/181 141/141/179
+f 101/101/119 143/143/182 142/142/183
+f 103/102/121 143/143/182 102/103/122
+f 104/104/124 144/144/184 103/102/121
+f 105/105/126 145/145/185 104/104/124
+f 106/106/186 146/146/187 105/105/126
+f 107/107/130 147/147/188 106/106/186
+f 107/107/130 149/148/189 148/149/190
+f 108/108/133 150/150/191 149/148/189
+f 109/109/192 151/151/193 150/150/194
+f 110/110/136 152/152/195 151/151/193
+f 111/111/137 153/153/196 152/152/197
+f 113/112/139 153/153/196 112/113/140
+f 114/114/141 154/154/198 113/112/139
+f 115/115/143 155/155/199 114/114/141
+f 116/116/200 156/156/201 115/115/143
+f 117/117/146 157/157/202 116/116/200
+f 117/117/146 159/158/203 158/159/204
+f 118/118/149 160/160/205 159/158/203
+f 119/119/206 122/122/154 160/160/207
+f 160/160/205 161/161/208 162/162/209
+f 122/122/154 163/163/210 161/161/211
+f 121/121/156 164/164/212 163/163/210
+f 124/124/213 164/164/212 123/123/214
+f 125/125/215 165/165/216 124/124/213
+f 126/126/217 166/166/218 125/125/219
+f 127/127/161 167/167/220 126/126/221
+f 128/129/163 168/168/222 127/127/161
+f 128/129/163 170/169/223 169/170/224
+f 129/128/162 171/171/225 170/169/223
+f 130/130/167 172/172/226 171/171/225
+f 131/131/166 173/173/227 172/172/228
+f 132/132/168 174/174/229 173/173/230
+f 134/134/171 174/174/229 133/133/169
+f 135/135/231 175/175/232 134/134/233
+f 136/136/234 176/176/235 135/135/236
+f 137/137/175 177/177/237 136/136/174
+f 138/139/177 178/178/238 137/137/175
+f 138/139/177 180/179/239 179/180/240
+f 139/138/241 181/181/242 180/179/239
+f 140/140/178 182/182/243 181/181/242
+f 141/141/179 183/183/244 182/182/245
+f 142/142/183 184/184/246 183/183/244
+f 144/144/247 184/184/246 143/143/248
+f 145/145/249 185/185/250 144/144/247
+f 146/146/251 186/186/252 145/145/253
+f 147/147/188 187/187/254 146/146/255
+f 148/149/190 188/188/256 147/147/188
+f 148/149/190 190/189/257 189/190/258
+f 149/148/189 191/191/259 190/189/257
+f 150/150/194 192/192/260 191/191/259
+f 151/151/193 193/193/261 192/192/262
+f 152/152/195 194/194/263 193/193/264
+f 154/154/198 194/194/263 153/153/196
+f 155/155/265 195/195/266 154/154/267
+f 156/156/268 196/196/269 155/155/270
+f 157/157/202 197/197/271 156/156/201
+f 158/159/204 198/198/272 157/157/202
+f 158/159/204 200/199/273 199/200/274
+f 159/158/275 162/162/209 200/199/273
+f 200/199/273 201/201/276 202/202/277
+f 162/162/209 203/203/278 201/201/276
+f 161/161/279 204/204/280 203/203/281
+f 163/163/282 205/205/283 204/204/280
+f 165/165/216 205/205/283 164/164/212
+f 166/166/218 206/206/284 165/165/216
+f 167/167/285 207/207/286 166/166/218
+f 168/168/287 208/208/288 167/167/285
+f 169/170/289 209/209/290 168/168/222
+f 169/170/291 211/210/292 210/211/293
+f 170/169/223 212/212/294 211/210/292
+f 171/171/225 213/213/295 212/212/294
+f 172/172/228 214/214/296 213/213/295
+f 173/173/227 215/215/297 214/214/296
+f 175/175/232 215/215/297 174/174/229
+f 176/176/235 216/216/298 175/175/299
+f 177/177/300 217/217/301 176/176/235
+f 178/178/302 218/218/303 177/177/300
+f 179/180/240 219/219/304 178/178/238
+f 179/180/305 221/220/306 220/221/307
+f 180/179/239 222/222/308 221/220/306
+f 181/181/242 223/223/309 222/222/308
+f 182/182/310 224/224/311 223/223/312
+f 183/183/313 225/225/314 224/224/311
+f 185/185/250 225/225/314 184/184/246
+f 186/186/252 226/226/315 185/185/250
+f 187/187/316 227/227/317 186/186/252
+f 188/188/318 228/228/319 187/187/316
+f 189/190/320 229/229/321 188/188/256
+f 189/190/322 231/230/323 230/231/324
+f 190/189/257 232/232/325 231/230/323
+f 191/191/259 233/233/326 232/232/325
+f 192/192/262 234/234/327 233/233/326
+f 193/193/261 235/235/328 234/234/327
+f 195/195/266 235/235/328 194/194/263
+f 196/196/269 236/236/329 195/195/330
+f 197/197/331 237/237/332 196/196/269
+f 198/198/333 238/238/334 197/197/331
+f 199/200/274 239/239/335 198/198/272
+f 199/200/336 202/202/277 240/240/337
+f 240/240/338 241/241/339 242/242/340
+f 202/202/277 243/243/341 241/241/339
+f 201/201/276 244/244/342 243/243/343
+f 203/203/278 245/245/344 244/244/345
+f 204/204/280 246/246/346 245/245/344
+f 206/206/347 246/246/348 205/205/283
+f 207/207/349 247/247/350 206/206/347
+f 208/208/351 248/248/352 207/207/349
+f 209/209/290 249/249/353 208/208/351
+f 210/211/354 250/250/355 209/209/290
+f 210/211/293 252/251/356 251/252/357
+f 211/210/292 253/253/358 252/251/356
+f 212/212/294 254/254/359 253/253/360
+f 213/213/295 255/255/361 254/254/359
+f 214/214/296 256/256/362 255/255/361
+f 216/216/298 256/256/362 215/215/297
+f 217/217/363 257/257/364 216/216/298
+f 218/218/303 258/258/365 217/217/363
+f 219/219/304 259/259/366 218/218/303
+f 220/221/307 260/260/367 219/219/304
+f 220/221/368 262/261/369 261/262/370
+f 221/220/306 263/263/371 262/261/369
+f 222/222/308 264/264/372 263/263/373
+f 223/223/309 265/265/374 264/264/375
+f 224/224/311 266/266/376 265/265/374
+f 226/226/377 266/266/378 225/225/314
+f 227/227/379 267/267/380 226/226/377
+f 228/228/381 268/268/382 227/227/379
+f 229/229/321 269/269/383 228/228/381
+f 230/231/384 270/270/385 229/229/321
+f 230/231/324 272/271/386 271/272/387
+f 231/230/323 273/273/388 272/271/386
+f 232/232/325 274/274/389 273/273/390
+f 233/233/326 275/275/391 274/274/389
+f 234/234/327 276/276/392 275/275/391
+f 236/236/329 276/276/392 235/235/328
+f 237/237/393 277/277/394 236/236/329
+f 238/238/334 278/278/395 237/237/393
+f 239/239/335 279/279/396 238/238/334
+f 240/240/337 280/280/397 239/239/335
+f 242/242/340 282/281/398 280/280/397
+f 242/242/399 283/282/400 281/283/401
+f 241/241/339 284/284/402 283/282/400
+f 243/243/341 285/285/403 284/284/404
+f 244/244/342 286/286/405 285/285/403
+f 245/245/406 287/287/407 286/286/405
+f 247/247/408 287/287/407 246/246/348
+f 248/248/352 288/288/409 247/247/408
+f 249/249/353 289/289/410 248/248/352
+f 250/250/355 290/290/411 249/249/353
+f 251/252/412 291/291/413 250/250/355
+f 251/252/357 293/292/414 292/293/415
+f 252/251/356 294/294/416 293/292/417
+f 253/253/358 295/295/418 294/294/419
+f 254/254/359 296/296/420 295/295/418
+f 255/255/421 297/297/422 296/296/420
+f 257/257/423 297/297/422 256/256/362
+f 258/258/424 298/298/425 257/257/423
+f 259/259/366 299/299/426 258/258/424
+f 260/260/367 300/300/427 259/259/366
+f 261/262/370 301/301/428 260/260/367
+f 261/262/429 303/302/430 302/303/431
+f 262/261/369 304/304/432 303/302/430
+f 263/263/371 305/305/433 304/304/434
+f 264/264/372 306/306/435 305/305/433
+f 265/265/436 307/307/437 306/306/435
+f 267/267/438 307/307/437 266/266/378
+f 268/268/382 308/308/439 267/267/438
+f 269/269/383 309/309/440 268/268/382
+f 270/270/385 310/310/441 269/269/383
+f 271/272/442 311/311/443 270/270/385
+f 271/272/387 313/312/444 312/313/445
+f 272/271/386 314/314/446 313/312/447
+f 273/273/388 315/315/448 314/314/449
+f 274/274/389 316/316/450 315/315/448
+f 275/275/451 317/317/452 316/316/450
+f 277/277/453 317/317/452 276/276/392
+f 278/278/454 318/318/455 277/277/453
+f 279/279/396 319/319/456 278/278/454
+f 280/280/397 320/320/457 279/279/396
+f 282/281/458 322/321/459 320/320/457
+f 281/283/460 321/322/461 282/281/458
+f 281/283/462 324/323/463 323/324/464
+f 283/282/465 325/325/466 324/323/467
+f 284/284/402 326/326/468 325/325/466
+f 285/285/403 327/327/469 326/326/468
+f 286/286/405 328/328/470 327/327/469
+f 288/288/471 328/328/470 287/287/407
+f 289/289/472 329/329/473 288/288/409
+f 290/290/474 330/330/475 289/289/472
+f 291/291/476 331/331/477 290/290/411
+f 292/293/478 332/332/479 291/291/476
+f 292/293/480 334/333/481 333/334/482
+f 293/292/417 335/335/483 334/333/484
+f 294/294/419 336/336/485 335/335/483
+f 295/295/418 337/337/486 336/336/487
+f 296/296/420 338/338/488 337/337/486
+f 298/298/489 338/338/488 297/297/422
+f 299/299/490 339/339/491 298/298/425
+f 300/300/492 340/340/493 299/299/490
+f 301/301/494 341/341/495 300/300/427
+f 302/303/496 342/342/497 301/301/494
+f 302/303/498 344/343/499 343/344/500
+f 303/302/501 345/345/502 344/343/503
+f 304/304/432 346/346/504 345/345/502
+f 305/305/433 347/347/505 346/346/504
+f 306/306/435 348/348/506 347/347/505
+f 308/308/507 348/348/506 307/307/437
+f 309/309/508 349/349/509 308/308/439
+f 310/310/510 350/350/511 309/309/508
+f 311/311/512 351/351/513 310/310/441
+f 312/313/514 352/352/515 311/311/512
+f 312/313/516 354/353/517 353/354/518
+f 313/312/447 355/355/519 354/353/520
+f 314/314/449 356/356/521 355/355/519
+f 315/315/448 357/357/522 356/356/523
+f 316/316/450 358/358/524 357/357/522
+f 318/318/525 358/358/524 317/317/452
+f 319/319/526 359/359/527 318/318/455
+f 320/320/528 360/360/529 319/319/526
+f 322/321/459 362/361/530 360/360/531
+f 321/322/532 361/362/533 322/321/459
+f 323/324/464 363/363/534 321/322/461
+f 323/324/464 365/364/535 364/365/536
+f 324/323/467 366/366/537 365/364/538
+f 325/325/466 367/367/539 366/366/540
+f 326/326/541 368/368/542 367/367/539
+f 327/327/469 369/369/543 368/368/544
+f 329/329/473 369/369/543 328/328/545
+f 330/330/546 370/370/547 329/329/473
+f 331/331/477 371/371/548 330/330/549
+f 332/332/479 372/372/550 331/331/477
+f 333/334/551 373/373/552 332/332/479
+f 333/334/551 375/374/553 374/375/554
+f 334/333/484 376/376/555 375/374/556
+f 335/335/483 377/377/557 376/376/558
+f 336/336/559 378/378/560 377/377/557
+f 337/337/486 379/379/561 378/378/560
+f 339/339/491 379/379/561 338/338/562
+f 340/340/493 380/380/563 339/339/491
+f 341/341/495 381/381/564 340/340/565
+f 342/342/566 382/382/567 341/341/495
+f 343/344/500 383/383/568 342/342/497
+f 343/344/500 385/384/569 384/385/570
+f 344/343/503 386/386/571 385/384/572
+f 345/345/502 387/387/573 386/386/574
+f 346/346/575 388/388/576 387/387/573
+f 347/347/505 389/389/577 388/388/578
+f 349/349/509 389/389/577 348/348/506
+f 350/350/579 390/390/580 349/349/509
+f 351/351/513 391/391/581 350/350/582
+f 352/352/515 392/392/583 351/351/513
+f 353/354/584 393/393/585 352/352/515
+f 353/354/584 395/394/586 394/395/587
+f 354/353/520 396/396/588 395/394/589
+f 355/355/519 397/397/590 396/396/591
+f 356/356/592 398/398/593 397/397/590
+f 357/357/522 399/399/594 398/398/593
+f 359/359/527 399/399/594 358/358/595
+f 360/360/529 400/400/596 359/359/527
+f 362/361/597 402/401/598 400/400/596
+f 361/362/599 401/402/600 362/361/530
+f 363/363/601 403/403/602 361/362/603
+f 364/365/536 404/404/604 363/363/605
+f 364/365/536 406/405/606 405/406/607
+f 365/364/535 407/407/608 406/405/606
+f 366/366/609 408/408/610 407/407/611
+f 367/367/539 409/409/612 408/408/610
+f 368/368/613 410/410/614 409/409/612
+f 370/370/615 410/410/614 369/369/543
+f 371/371/548 411/411/616 370/370/617
+f 372/372/618 412/412/619 371/371/548
+f 373/373/620 413/413/621 372/372/622
+f 374/375/554 414/414/623 373/373/624
+f 374/375/554 416/415/625 415/416/626
+f 375/374/553 417/417/627 416/415/625
+f 376/376/628 418/418/629 417/417/627
+f 377/377/557 419/419/630 418/418/629
+f 378/378/631 420/420/632 419/419/633
+f 380/380/634 420/420/632 379/379/561
+f 381/381/635 421/421/636 380/380/563
+f 382/382/637 422/422/638 381/381/564
+f 383/383/639 423/423/640 382/382/641
+f 384/385/570 424/424/642 383/383/643
+f 384/385/570 426/425/644 425/426/645
+f 385/384/569 427/427/646 426/425/644
+f 386/386/647 428/428/648 427/427/649
+f 387/387/573 429/429/650 428/428/648
+f 388/388/651 430/430/652 429/429/650
+f 390/390/653 430/430/652 389/389/577
+f 391/391/581 431/431/654 390/390/655
+f 392/392/656 432/432/657 391/391/581
+f 393/393/658 433/433/659 392/392/660
+f 394/395/587 434/434/661 393/393/662
+f 394/395/587 436/435/663 435/436/664
+f 395/394/586 437/437/665 436/435/663
+f 396/396/666 438/438/667 437/437/665
+f 397/397/590 439/439/668 438/438/667
+f 398/398/669 440/440/670 439/439/671
+f 400/400/672 440/440/670 399/399/594
+f 402/401/673 442/441/674 440/440/670
+f 401/402/600 441/442/675 402/401/598
+f 403/403/602 443/443/676 401/402/677
+f 404/404/604 444/444/678 403/403/602
+f 405/406/679 445/445/680 404/404/604
+f 405/406/681 447/446/682 446/447/683
+f 406/405/606 448/448/684 447/446/682
+f 407/407/611 449/449/685 448/448/684
+f 408/408/610 450/450/686 449/449/687
+f 409/409/688 451/451/689 450/450/686
+f 411/411/690 451/451/689 410/410/614
+f 412/412/619 452/452/691 411/411/616
+f 413/413/621 453/453/692 412/412/693
+f 414/414/623 454/454/694 413/413/621
+f 415/416/695 455/455/696 414/414/623
+f 415/416/697 457/456/698 456/457/699
+f 416/415/625 458/458/700 457/456/698
+f 417/417/701 459/459/702 458/458/700
+f 418/418/629 460/460/703 459/459/704
+f 419/419/630 461/461/705 460/460/703
+f 421/421/706 461/461/705 420/420/632
+f 422/422/638 462/462/707 421/421/636
+f 423/423/640 463/463/708 422/422/709
+f 424/424/642 464/464/710 423/423/640
+f 425/426/711 465/465/712 424/424/642
+f 425/426/713 467/466/714 466/467/715
+f 426/425/644 468/468/716 467/466/714
+f 427/427/649 469/469/717 468/468/716
+f 428/428/648 470/470/718 469/469/719
+f 429/429/720 471/471/721 470/470/718
+f 431/431/722 471/471/721 430/430/652
+f 432/432/657 472/472/723 431/431/654
+f 433/433/659 473/473/724 432/432/725
+f 434/434/661 474/474/726 433/433/659
+f 435/436/727 475/475/728 434/434/661
+f 435/436/729 477/476/730 476/477/731
+f 436/435/663 478/478/732 477/476/730
+f 437/437/733 479/479/734 478/478/732
+f 438/438/667 480/480/735 479/479/736
+f 439/439/668 442/441/674 480/480/735
+f 480/480/735 481/481/737 482/482/738
+f 441/442/739 481/481/737 442/441/674
+f 443/443/676 483/483/740 441/442/675
+f 444/444/741 484/484/742 443/443/676
+f 445/445/680 485/485/743 444/444/678
+f 446/447/683 486/486/744 445/445/680
+f 446/447/683 488/487/745 487/488/746
+f 447/446/682 489/489/747 488/487/748
+f 448/448/749 490/490/750 489/489/747
+f 449/449/751 491/491/752 490/490/750
+f 450/450/686 492/492/753 491/491/752
+f 452/452/754 492/492/753 451/451/689
+f 453/453/755 493/493/756 452/452/691
+f 454/454/757 494/494/758 453/453/755
+f 455/455/696 495/495/759 454/454/694
+f 456/457/699 496/496/760 455/455/696
+f 456/457/699 498/497/761 497/498/762
+f 457/456/698 499/499/763 498/497/764
+f 458/458/765 500/500/766 499/499/763
+f 459/459/767 501/501/768 500/500/766
+f 460/460/703 502/502/769 501/501/768
+f 462/462/770 502/502/769 461/461/705
+f 463/463/708 503/503/771 462/462/707
+f 464/464/772 504/504/773 463/463/708
+f 465/465/712 505/505/774 464/464/710
+f 466/467/715 506/506/775 465/465/712
+f 466/467/715 508/507/776 507/508/777
+f 467/466/714 509/509/778 508/507/779
+f 468/468/716 510/510/780 509/509/778
+f 469/469/781 511/511/782 510/510/780
+f 470/470/718 512/512/783 511/511/782
+f 472/472/784 512/512/783 471/471/721
+f 473/473/785 513/513/786 472/472/723
+f 474/474/787 514/514/788 473/473/785
+f 475/475/728 515/515/789 474/474/726
+f 476/477/731 516/516/790 475/475/728
+f 476/477/731 518/517/791 517/518/792
+f 477/476/730 519/519/793 518/517/794
+f 478/478/795 520/520/796 519/519/793
+f 479/479/797 482/482/738 520/520/796
+f 520/520/796 521/521/798 522/522/799
+f 482/482/738 523/523/800 521/521/798
+f 483/483/801 523/523/800 481/481/737
+f 484/484/742 524/524/802 483/483/740
+f 485/485/743 525/525/803 484/484/742
+f 486/486/804 526/526/805 485/485/743
+f 487/488/806 527/527/807 486/486/804
+f 487/488/746 529/528/808 528/529/809
+f 488/487/748 530/530/810 529/528/808
+f 489/489/747 531/531/811 530/530/812
+f 490/490/750 532/532/813 531/531/811
+f 491/491/752 533/533/814 532/532/813
+f 493/493/756 533/533/814 492/492/753
+f 494/494/758 534/534/815 493/493/816
+f 495/495/759 535/535/817 494/494/758
+f 496/496/818 536/536/819 495/495/759
+f 497/498/820 537/537/821 496/496/818
+f 497/498/762 539/538/822 538/539/823
+f 498/497/764 540/540/824 539/538/822
+f 499/499/763 541/541/825 540/540/826
+f 500/500/766 542/542/827 541/541/825
+f 501/501/768 543/543/828 542/542/827
+f 503/503/829 543/543/828 502/502/769
+f 504/504/773 544/544/830 503/503/771
+f 505/505/774 545/545/831 504/504/773
+f 506/506/832 546/546/833 505/505/774
+f 507/508/834 547/547/835 506/506/832
+f 507/508/777 549/548/836 548/549/837
+f 508/507/779 550/550/838 549/548/836
+f 509/509/778 551/551/839 550/550/840
+f 510/510/780 552/552/841 551/551/839
+f 511/511/782 553/553/842 552/552/841
+f 513/513/786 553/553/842 512/512/783
+f 514/514/788 554/554/843 513/513/844
+f 515/515/789 555/555/845 514/514/788
+f 516/516/846 556/556/847 515/515/789
+f 517/518/848 557/557/849 516/516/846
+f 517/518/792 559/558/850 558/559/851
+f 518/517/794 560/560/852 559/558/850
+f 519/519/793 522/522/799 560/560/853
+f 560/560/853 561/561/854 562/562/855
+f 522/522/799 563/563/856 561/561/854
+f 521/521/857 564/564/858 563/563/856
+f 524/524/859 564/564/858 523/523/800
+f 525/525/803 565/565/860 524/524/859
+f 526/526/805 566/566/861 525/525/803
+f 527/527/862 567/567/863 526/526/805
+f 528/529/809 568/568/864 527/527/862
+f 528/529/809 570/569/865 569/570/866
+f 529/528/867 571/571/868 570/569/865
+f 530/530/812 572/572/869 571/571/868
+f 531/531/811 573/573/870 572/572/869
+f 532/532/871 574/574/872 573/573/870
+f 534/534/815 574/574/872 533/533/814
+f 535/535/817 575/575/873 534/534/815
+f 536/536/819 576/576/874 535/535/817
+f 537/537/821 577/577/875 536/536/876
+f 538/539/823 578/578/877 537/537/821
+f 538/539/823 580/579/878 579/580/879
+f 539/538/880 581/581/881 580/579/882
+f 540/540/826 582/582/883 581/581/881
+f 541/541/825 583/583/884 582/582/883
+f 542/542/885 584/584/886 583/583/884
+f 544/544/887 584/584/886 543/543/828
+f 545/545/831 585/585/888 544/544/887
+f 546/546/833 586/586/889 545/545/831
+f 547/547/890 587/587/891 546/546/892
+f 548/549/837 588/588/893 547/547/890
+f 548/549/837 590/589/894 589/590/895
+f 549/548/896 591/591/897 590/589/894
+f 550/550/840 592/592/898 591/591/897
+f 551/551/839 593/593/899 592/592/898
+f 552/552/900 594/594/901 593/593/899
+f 554/554/843 594/594/901 553/553/842
+f 555/555/845 595/595/902 554/554/843
+f 556/556/847 596/596/903 555/555/845
+f 557/557/849 597/597/904 556/556/905
+f 558/559/851 598/598/906 557/557/849
+f 558/559/851 600/599/907 599/600/908
+f 559/558/909 562/562/855 600/599/910
+f 600/599/907 601/601/911 602/602/912
+f 562/562/913 603/603/914 601/601/911
+f 561/561/854 604/604/915 603/603/914
+f 563/563/856 605/605/916 604/604/915
+f 565/565/860 605/605/917 564/564/858
+f 566/566/861 606/606/918 565/565/860
+f 567/567/919 607/607/920 566/566/861
+f 568/568/864 608/608/921 567/567/919
+f 569/570/866 609/609/922 568/568/864
+f 569/570/866 611/610/923 610/611/924
+f 570/569/865 612/612/925 611/610/923
+f 571/571/926 613/613/927 612/612/925
+f 572/572/869 614/614/928 613/613/927
+f 573/573/870 615/615/929 614/614/928
+f 575/575/873 615/615/930 574/574/872
+f 576/576/874 616/616/931 575/575/873
+f 577/577/932 617/617/933 576/576/874
+f 578/578/877 618/618/934 577/577/932
+f 579/580/879 619/619/935 578/578/877
+f 579/580/879 621/620/936 620/621/937
+f 580/579/878 622/622/938 621/620/936
+f 581/581/939 623/623/940 622/622/938
+f 582/582/883 624/624/941 623/623/940
+f 583/583/884 625/625/942 624/624/941
+f 585/585/888 625/625/943 584/584/886
+f 586/586/889 626/626/944 585/585/888
+f 587/587/945 627/627/946 586/586/889
+f 588/588/893 628/628/947 587/587/945
+f 589/590/895 629/629/948 588/588/893
+f 589/590/895 631/630/949 630/631/950
+f 590/589/894 632/632/951 631/630/949
+f 591/591/952 633/633/953 632/632/951
+f 592/592/898 634/634/954 633/633/953
+f 593/593/899 635/635/955 634/634/954
+f 595/595/902 635/635/956 594/594/901
+f 596/596/903 636/636/957 595/595/902
+f 597/597/958 637/637/959 596/596/903
+f 598/598/906 638/638/960 597/597/958
+f 599/600/908 639/639/961 598/598/906
+f 599/600/908 602/602/912 640/640/962
+f 640/640/963 641/641/964 642/642/965
+f 602/602/912 643/643/966 641/641/967
+f 601/601/911 644/644/968 643/643/966
+f 603/603/914 645/645/969 644/644/968
+f 604/604/915 646/646/970 645/645/971
+f 606/606/918 646/646/970 605/605/917
+f 607/607/920 647/647/972 606/606/918
+f 608/608/921 648/648/973 607/607/920
+f 609/609/922 649/649/974 608/608/921
+f 610/611/924 650/650/975 609/609/922
+f 610/611/976 652/651/977 651/652/978
+f 611/610/923 653/653/979 652/651/980
+f 612/612/925 654/654/981 653/653/979
+f 613/613/927 655/655/982 654/654/981
+f 614/614/928 656/656/983 655/655/984
+f 616/616/931 656/656/983 615/615/930
+f 617/617/933 657/657/985 616/616/931
+f 618/618/934 658/658/986 617/617/933
+f 619/619/935 659/659/987 618/618/934
+f 620/621/937 660/660/988 619/619/935
+f 620/621/989 662/661/990 661/662/991
+f 621/620/936 663/663/992 662/661/993
+f 622/622/938 664/664/994 663/663/992
+f 623/623/940 665/665/995 664/664/994
+f 624/624/941 666/666/996 665/665/997
+f 626/626/944 666/666/996 625/625/943
+f 627/627/946 667/667/998 626/626/944
+f 628/628/947 668/668/999 627/627/946
+f 629/629/948 669/669/1000 628/628/947
+f 630/631/950 670/670/1001 629/629/948
+f 630/631/1002 672/671/1003 671/672/1004
+f 631/630/949 673/673/1005 672/671/1006
+f 632/632/951 674/674/1007 673/673/1005
+f 633/633/953 675/675/1008 674/674/1007
+f 634/634/954 676/676/1009 675/675/1010
+f 636/636/957 676/676/1009 635/635/956
+f 637/637/959 677/677/1011 636/636/957
+f 638/638/960 678/678/1012 637/637/959
+f 639/639/961 679/679/1013 638/638/960
+f 640/640/962 680/680/1014 639/639/961
+f 642/642/965 682/681/1015 680/680/1016
+f 642/642/1017 683/682/1018 681/683/1019
+f 641/641/964 684/684/1020 683/682/1018
+f 643/643/1021 685/685/1022 684/684/1020
+f 644/644/968 686/686/1023 685/685/1022
+f 645/645/969 687/687/1024 686/686/1023
+f 647/647/1025 687/687/1024 646/646/970
+f 648/648/973 688/688/1026 647/647/1027
+f 649/649/974 689/689/1028 648/648/973
+f 650/650/975 690/690/1029 649/649/974
+f 651/652/1030 691/691/1031 650/650/1032
+f 651/652/1033 693/692/1034 692/693/1035
+f 652/651/977 694/694/1036 693/692/1034
+f 653/653/1037 695/695/1038 694/694/1036
+f 654/654/981 696/696/1039 695/695/1038
+f 655/655/982 697/697/1040 696/696/1039
+f 657/657/1041 697/697/1040 656/656/983
+f 658/658/986 698/698/1042 657/657/1043
+f 659/659/987 699/699/1044 658/658/986
+f 660/660/988 700/700/1045 659/659/987
+f 661/662/991 701/701/1046 660/660/1047
+f 661/662/1048 703/702/1049 702/703/1050
+f 662/661/990 704/704/1051 703/702/1049
+f 663/663/1052 705/705/1053 704/704/1051
+f 664/664/994 706/706/1054 705/705/1053
+f 665/665/995 707/707/1055 706/706/1054
+f 667/667/1056 707/707/1055 666/666/996
+f 668/668/999 708/708/1057 667/667/1058
+f 669/669/1000 709/709/1059 668/668/999
+f 670/670/1001 710/710/1060 669/669/1000
+f 671/672/1061 711/711/1062 670/670/1063
+f 671/672/1064 713/712/1065 712/713/1066
+f 672/671/1003 714/714/1067 713/712/1065
+f 673/673/1068 715/715/1069 714/714/1067
+f 674/674/1007 716/716/1070 715/715/1069
+f 675/675/1008 717/717/1071 716/716/1070
+f 677/677/1072 717/717/1071 676/676/1009
+f 678/678/1012 718/718/1073 677/677/1074
+f 679/679/1013 719/719/1075 678/678/1012
+f 680/680/1014 720/720/1076 679/679/1013
+f 682/681/1015 722/721/1077 720/720/1076
+f 681/683/1019 721/722/1078 682/681/1015
+f 681/683/1079 724/723/1080 723/724/1081
+f 683/682/1018 725/725/1082 724/723/1080
+f 684/684/1020 726/726/1083 725/725/1082
+f 685/685/1022 727/727/1084 726/726/1083
+f 686/686/1023 728/728/1085 727/727/1084
+f 688/688/1026 728/728/1085 687/687/1024
+f 689/689/1028 729/729/1086 688/688/1026
+f 690/690/1029 730/730/1087 689/689/1028
+f 691/691/1031 731/731/1088 690/690/1029
+f 692/693/1035 732/732/1089 691/691/1031
+f 692/693/1090 734/733/1091 733/734/1092
+f 693/692/1034 735/735/1093 734/733/1091
+f 694/694/1036 736/736/1094 735/735/1093
+f 695/695/1038 737/737/1095 736/736/1094
+f 696/696/1039 738/738/1096 737/737/1095
+f 698/698/1042 738/738/1096 697/697/1040
+f 699/699/1044 739/739/1097 698/698/1042
+f 700/700/1045 740/740/1098 699/699/1044
+f 701/701/1046 741/741/1099 700/700/1045
+f 702/703/1050 742/742/1100 701/701/1046
+f 702/703/1101 744/743/1102 743/744/1103
+f 703/702/1049 745/745/1104 744/743/1102
+f 704/704/1051 746/746/1105 745/745/1104
+f 705/705/1053 747/747/1106 746/746/1105
+f 706/706/1054 748/748/1107 747/747/1106
+f 708/708/1057 748/748/1107 707/707/1055
+f 709/709/1059 749/749/1108 708/708/1057
+f 710/710/1060 750/750/1109 709/709/1059
+f 711/711/1062 751/751/1110 710/710/1060
+f 712/713/1066 752/752/1111 711/711/1062
+f 712/713/1112 754/753/1113 753/754/1114
+f 713/712/1065 755/755/1115 754/753/1113
+f 714/714/1067 756/756/1116 755/755/1115
+f 715/715/1069 757/757/1117 756/756/1116
+f 716/716/1070 758/758/1118 757/757/1117
+f 718/718/1073 758/758/1118 717/717/1071
+f 719/719/1075 759/759/1119 718/718/1073
+f 720/720/1076 760/760/1120 719/719/1075
+f 722/721/1121 762/761/1122 760/760/1120
+f 721/722/1123 761/762/1124 722/721/1121
+f 723/724/1125 763/763/1126 721/722/1078
+f 723/724/1127 765/764/1128 764/765/1129
+f 724/723/1130 766/766/1131 765/764/1128
+f 725/725/1082 767/767/1132 766/766/1131
+f 726/726/1083 768/768/1133 767/767/1132
+f 727/727/1084 769/769/1134 768/768/1135
+f 729/729/1086 769/769/1134 728/728/1085
+f 730/730/1087 770/770/1136 729/729/1086
+f 731/731/1137 771/771/1138 730/730/1087
+f 732/732/1139 772/772/1140 731/731/1137
+f 733/734/1141 773/773/1142 732/732/1089
+f 733/734/1143 775/774/1144 774/775/1145
+f 734/733/1146 776/776/1147 775/774/1144
+f 735/735/1093 777/777/1148 776/776/1147
+f 736/736/1094 778/778/1149 777/777/1148
+f 737/737/1095 779/779/1150 778/778/1151
+f 739/739/1097 779/779/1150 738/738/1096
+f 740/740/1098 780/780/1152 739/739/1097
+f 741/741/1153 781/781/1154 740/740/1098
+f 742/742/1155 782/782/1156 741/741/1153
+f 743/744/1157 783/783/1158 742/742/1100
+f 743/744/1159 785/784/1160 784/785/1161
+f 744/743/1162 786/786/1163 785/784/1160
+f 745/745/1104 787/787/1164 786/786/1163
+f 746/746/1105 788/788/1165 787/787/1164
+f 747/747/1106 789/789/1166 788/788/1167
+f 749/749/1108 789/789/1166 748/748/1107
+f 750/750/1109 790/790/1168 749/749/1108
+f 751/751/1169 791/791/1170 750/750/1109
+f 752/752/1171 792/792/1172 751/751/1169
+f 753/754/1173 793/793/1174 752/752/1111
+f 753/754/1175 795/794/1176 794/795/1177
+f 754/753/1178 796/796/1179 795/794/1176
+f 755/755/1115 797/797/1180 796/796/1179
+f 756/756/1116 798/798/1181 797/797/1180
+f 757/757/1117 799/799/1182 798/798/1183
+f 759/759/1119 799/799/1182 758/758/1118
+f 760/760/1120 800/800/1184 759/759/1119
+f 762/761/1122 802/801/1185 800/800/1186
+f 761/762/1124 801/802/1187 762/761/1122
+f 763/763/1126 803/803/1188 761/762/1124
+f 764/765/1129 804/804/1189 763/763/1126
+f 764/765/1129 806/805/1190 805/806/1191
+f 765/764/1128 807/807/1192 806/805/1193
+f 766/766/1131 808/808/1194 807/807/1195
+f 767/767/1132 809/809/1196 808/808/1194
+f 768/768/1133 810/810/1197 809/809/1198
+f 770/770/1136 810/810/1197 769/769/1134
+f 771/771/1138 811/811/1199 770/770/1200
+f 772/772/1140 812/812/1201 771/771/1138
+f 773/773/1142 813/813/1202 772/772/1140
+f 774/775/1145 814/814/1203 773/773/1142
+f 774/775/1145 816/815/1204 815/816/1205
+f 775/774/1144 817/817/1206 816/815/1207
+f 776/776/1147 818/818/1208 817/817/1209
+f 777/777/1148 819/819/1210 818/818/1208
+f 778/778/1211 820/820/1212 819/819/1213
+f 780/780/1214 820/820/1212 779/779/1150
+f 781/781/1154 821/821/1215 780/780/1214
+f 782/782/1156 822/822/1216 781/781/1154
+f 783/783/1158 823/823/1217 782/782/1156
+f 784/785/1161 824/824/1218 783/783/1158
+f 784/785/1161 826/825/1219 825/826/1220
+f 785/784/1160 827/827/1221 826/825/1222
+f 786/786/1163 828/828/1223 827/827/1224
+f 787/787/1164 829/829/1225 828/828/1223
+f 788/788/1165 830/830/1226 829/829/1227
+f 790/790/1168 830/830/1226 789/789/1166
+f 791/791/1170 831/831/1228 790/790/1229
+f 792/792/1172 832/832/1230 791/791/1170
+f 793/793/1174 833/833/1231 792/792/1172
+f 794/795/1177 834/834/1232 793/793/1174
+f 794/795/1177 836/835/1233 835/836/1234
+f 795/794/1176 837/837/1235 836/835/1236
+f 796/796/1179 838/838/1237 837/837/1238
+f 797/797/1180 839/839/1239 838/838/1237
+f 798/798/1240 840/840/1241 839/839/1242
+f 800/800/1186 840/840/1241 799/799/1182
+f 840/840/1241 841/841/1243 842/842/1244
+f 802/801/1245 843/843/1246 841/841/1243
+f 801/802/1187 844/844/1247 843/843/1248
+f 803/803/1188 845/845/1249 844/844/1250
+f 804/804/1189 846/846/1251 845/845/1249
+f 806/805/1193 846/846/1252 805/806/1253
+f 807/807/1254 847/847/1255 806/805/1193
+f 808/808/1194 848/848/1256 807/807/1254
+f 809/809/1257 849/849/1258 808/808/1194
+f 810/810/1197 850/850/1259 809/809/1260
+f 810/810/1197 852/851/1261 851/852/1262
+f 811/811/1263 853/853/1264 852/851/1261
+f 812/812/1201 854/854/1265 853/853/1264
+f 813/813/1202 855/855/1266 854/854/1267
+f 814/814/1203 856/856/1268 855/855/1266
+f 816/815/1207 856/856/1269 815/816/1270
+f 817/817/1271 857/857/1272 816/815/1207
+f 818/818/1208 858/858/1273 817/817/1271
+f 819/819/1213 859/859/1274 818/818/1208
+f 820/820/1212 860/860/1275 819/819/1276
+f 820/820/1212 862/861/1277 861/862/1278
+f 821/821/1279 863/863/1280 862/861/1277
+f 822/822/1216 864/864/1281 863/863/1280
+f 823/823/1217 865/865/1282 864/864/1283
+f 824/824/1218 866/866/1284 865/865/1282
+f 826/825/1222 866/866/1285 825/826/1286
+f 827/827/1287 867/867/1288 826/825/1222
+f 828/828/1223 868/868/1289 827/827/1287
+f 829/829/1290 869/869/1291 828/828/1223
+f 830/830/1226 870/870/1292 829/829/1293
+f 830/830/1226 872/871/1294 871/872/1295
+f 831/831/1296 873/873/1297 872/871/1294
+f 832/832/1230 874/874/1298 873/873/1297
+f 833/833/1231 875/875/1299 874/874/1300
+f 834/834/1232 876/876/1301 875/875/1299
+f 836/835/1236 876/876/1302 835/836/1303
+f 837/837/1304 877/877/1305 836/835/1236
+f 838/838/1237 878/878/1306 837/837/1304
+f 839/839/1242 879/879/1307 838/838/1237
+f 840/840/1241 880/880/1308 839/839/1309
+f 842/842/1244 882/881/1310 880/880/1311
+f 842/842/1244 883/882/1312 881/883/1313
+f 841/841/1314 884/884/1315 883/882/1312
+f 843/843/1316 885/885/1317 884/884/1318
+f 844/844/1250 886/886/1319 885/885/1317
+f 845/845/1249 887/887/1320 886/886/1321
+f 847/847/1255 887/887/1322 846/846/1252
+f 848/848/1323 888/888/1324 847/847/1255
+f 849/849/1325 889/889/1326 848/848/1327
+f 850/850/1259 890/890/1328 849/849/1329
+f 851/852/1262 891/891/1330 850/850/1331
+f 851/852/1262 893/892/1332 892/893/1333
+f 852/851/1261 894/894/1334 893/892/1332
+f 853/853/1335 895/895/1336 894/894/1337
+f 854/854/1267 896/896/1338 895/895/1336
+f 855/855/1266 897/897/1339 896/896/1340
+f 857/857/1272 897/897/1341 856/856/1269
+f 858/858/1342 898/898/1343 857/857/1272
+f 859/859/1344 899/899/1345 858/858/1346
+f 860/860/1347 900/900/1348 859/859/1349
+f 861/862/1278 901/901/1350 860/860/1351
+f 861/862/1278 903/902/1352 902/903/1353
+f 862/861/1354 904/904/1355 903/902/1352
+f 863/863/1356 905/905/1357 904/904/1358
+f 864/864/1283 906/906/1359 905/905/1357
+f 865/865/1282 907/907/1360 906/906/1361
+f 867/867/1288 907/907/1362 866/866/1285
+f 868/868/1363 908/908/1364 867/867/1288
+f 869/869/1365 909/909/1366 868/868/1367
+f 870/870/1292 910/910/1368 869/869/1369
+f 871/872/1295 911/911/1370 870/870/1371
+f 871/872/1295 913/912/1372 912/913/1373
+f 872/871/1294 914/914/1374 913/912/1372
+f 873/873/1375 915/915/1376 914/914/1377
+f 874/874/1300 916/916/1378 915/915/1376
+f 875/875/1299 917/917/1379 916/916/1380
+f 877/877/1305 917/917/1381 876/876/1302
+f 878/878/1382 918/918/1383 877/877/1305
+f 879/879/1384 919/919/1385 878/878/1386
+f 880/880/1387 920/920/1388 879/879/1389
+f 882/881/1390 922/921/1391 920/920/1388
+f 881/883/1313 921/922/1392 882/881/1390
+f 881/883/1313 924/923/1393 923/924/1394
+f 883/882/1312 925/925/1395 924/923/1393
+f 884/884/1315 926/926/1396 925/925/1397
+f 885/885/1317 927/927/1398 926/926/1399
+f 886/886/1400 928/928/1401 927/927/1398
+f 888/888/1402 928/928/1401 887/887/1322
+f 889/889/1326 929/929/1403 888/888/1404
+f 890/890/1328 930/930/1405 889/889/1326
+f 891/891/1406 931/931/1407 890/890/1328
+f 892/893/1333 932/932/1408 891/891/1406
+f 892/893/1333 934/933/1409 933/934/1410
+f 893/892/1332 935/935/1411 934/933/1409
+f 894/894/1334 936/936/1412 935/935/1413
+f 895/895/1336 937/937/1414 936/936/1415
+f 896/896/1416 938/938/1417 937/937/1414
+f 898/898/1418 938/938/1417 897/897/1339
+f 899/899/1345 939/939/1419 898/898/1420
+f 900/900/1348 940/940/1421 899/899/1345
+f 901/901/1422 941/941/1423 900/900/1348
+f 902/903/1353 942/942/1424 901/901/1422
+f 902/903/1353 944/943/1425 943/944/1426
+f 903/902/1352 945/945/1427 944/943/1425
+f 904/904/1355 946/946/1428 945/945/1429
+f 905/905/1357 947/947/1430 946/946/1431
+f 906/906/1432 948/948/1433 947/947/1430
+f 908/908/1434 948/948/1433 907/907/1362
+f 909/909/1366 949/949/1435 908/908/1436
+f 910/910/1368 950/950/1437 909/909/1366
+f 911/911/1438 951/951/1439 910/910/1368
+f 912/913/1373 952/952/1440 911/911/1438
+f 912/913/1373 954/953/1441 953/954/1442
+f 913/912/1372 955/955/1443 954/953/1441
+f 914/914/1374 956/956/1444 955/955/1445
+f 915/915/1376 957/957/1446 956/956/1447
+f 916/916/1448 958/958/1449 957/957/1446
+f 918/918/1450 958/958/1449 917/917/1379
+f 919/919/1385 959/959/1451 918/918/1452
+f 920/920/1388 960/960/1453 919/919/1385
+f 922/921/1454 962/961/1455 960/960/1456
+f 921/922/1392 961/962/1457 922/921/1391
+f 923/924/1458 963/963/1459 921/922/1392
+f 923/924/1458 965/964/1460 964/965/1461
+f 924/923/1393 966/966/1462 965/964/1460
+f 925/925/1395 967/967/1463 966/966/1462
+f 926/926/1464 968/968/1465 967/967/1463
+f 927/927/1398 969/969/1466 968/968/1465
+f 929/929/1403 969/969/1467 928/928/1401
+f 930/930/1405 970/970/1468 929/929/1469
+f 931/931/1470 971/971/1471 930/930/1472
+f 932/932/1408 972/972/1473 931/931/1407
+f 933/934/1474 973/973/1475 932/932/1408
+f 933/934/1474 975/974/1476 974/975/1477
+f 934/933/1409 976/976/1478 975/974/1476
+f 935/935/1411 977/977/1479 976/976/1478
+f 936/936/1412 978/978/1480 977/977/1481
+f 937/937/1414 979/979/1482 978/978/1480
+f 939/939/1419 979/979/1483 938/938/1417
+f 940/940/1421 980/980/1484 939/939/1485
+f 941/941/1486 981/981/1487 940/940/1488
+f 942/942/1424 982/982/1489 941/941/1423
+f 943/944/1490 983/983/1491 942/942/1424
+f 943/944/1490 985/984/1492 984/985/1493
+f 944/943/1425 986/986/1494 985/984/1492
+f 945/945/1427 987/987/1495 986/986/1494
+f 946/946/1496 988/988/1497 987/987/1498
+f 947/947/1430 989/989/1499 988/988/1497
+f 949/949/1435 989/989/1500 948/948/1433
+f 950/950/1437 990/990/1501 949/949/1502
+f 951/951/1503 991/991/1504 950/950/1505
+f 952/952/1440 992/992/1506 951/951/1439
+f 953/954/1507 993/993/1508 952/952/1440
+f 953/954/1507 995/994/1509 994/995/1510
+f 954/953/1441 996/996/1511 995/994/1509
+f 955/955/1443 997/997/1512 996/996/1511
+f 956/956/1444 998/998/1513 997/997/1514
+f 957/957/1446 999/999/1515 998/998/1513
+f 959/959/1451 999/999/1516 958/958/1449
+f 960/960/1453 1000/1000/1517 959/959/1518
+f 962/961/1455 1002/1001/1519 1000/1000/1517
+f 961/962/1457 1001/1002/1520 962/961/1521
+f 963/963/1459 1003/1003/1522 961/962/1457
+f 964/965/1523 1004/1004/1524 963/963/1459
+f 964/965/1523 1006/1005/1525 1005/1006/1526
+f 965/964/1460 1007/1007/1527 1006/1005/1525
+f 966/966/1528 1008/1008/1529 1007/1007/1527
+f 967/967/1463 1009/1009/1530 1008/1008/1529
+f 968/968/1531 1010/1010/1532 1009/1009/1530
+f 970/970/1533 1010/1010/1532 969/969/1534
+f 971/971/1471 1011/1011/1535 970/970/1468
+f 972/972/1473 1012/1012/1536 971/971/1537
+f 973/973/1475 1013/1013/1538 972/972/1473
+f 974/975/1477 1014/1014/1539 973/973/1475
+f 974/975/1477 1016/1015/1540 1015/1016/1541
+f 975/974/1476 1017/1017/1542 1016/1015/1540
+f 976/976/1543 1018/1018/1544 1017/1017/1542
+f 977/977/1481 1019/1019/1545 1018/1018/1544
+f 978/978/1546 1020/1020/1547 1019/1019/1545
+f 980/980/1548 1020/1020/1547 979/979/1549
+f 981/981/1487 1021/1021/1550 980/980/1484
+f 982/982/1489 1022/1022/1551 981/981/1552
+f 983/983/1491 1023/1023/1553 982/982/1489
+f 984/985/1554 1024/1024/1555 983/983/1491
+f 984/985/1554 1026/1025/1556 1025/1026/1557
+f 985/984/1492 1027/1027/1558 1026/1025/1556
+f 986/986/1559 1028/1028/1560 1027/1027/1558
+f 987/987/1498 1029/1029/1561 1028/1028/1560
+f 988/988/1562 1030/1030/1563 1029/1029/1561
+f 990/990/1564 1030/1030/1563 989/989/1565
+f 991/991/1504 1031/1031/1566 990/990/1501
+f 992/992/1506 1032/1032/1567 991/991/1568
+f 993/993/1508 1033/1033/1569 992/992/1506
+f 994/995/1510 1034/1034/1570 993/993/1508
+f 994/995/1510 1036/1035/1571 1035/1036/1572
+f 995/994/1509 1037/1037/1573 1036/1035/1571
+f 996/996/1574 1038/1038/1575 1037/1037/1573
+f 997/997/1514 1039/1039/1576 1038/1038/1575
+f 998/998/1577 1040/1040/1578 1039/1039/1576
+f 1000/1000/1579 1040/1040/1578 999/999/1580
+f 1002/1001/1519 1042/1041/1581 1040/1040/1578
+f 1001/1002/1520 1041/1042/1582 1002/1001/1519
+f 1003/1003/1522 1043/1043/1583 1001/1002/1584
+f 1004/1004/1585 1044/1044/1586 1003/1003/1522
+f 1005/1006/1526 1045/1045/1587 1004/1004/1524
+f 1005/1006/1526 1047/1046/1588 1046/1047/1589
+f 1006/1005/1525 1048/1048/1590 1047/1046/1588
+f 1007/1007/1527 1049/1049/1591 1048/1048/1590
+f 1008/1008/1592 1050/1050/1593 1049/1049/1591
+f 1009/1009/1530 1051/1051/1594 1050/1050/1595
+f 1011/1011/1535 1051/1051/1594 1010/1010/1532
+f 1012/1012/1536 1052/1052/1596 1011/1011/1535
+f 1013/1013/1538 1053/1053/1597 1012/1012/1598
+f 1014/1014/1599 1054/1054/1600 1013/1013/1538
+f 1015/1016/1541 1055/1055/1601 1014/1014/1539
+f 1015/1016/1541 1057/1056/1602 1056/1057/1603
+f 1016/1015/1540 1058/1058/1604 1057/1056/1602
+f 1017/1017/1542 1059/1059/1605 1058/1058/1604
+f 1018/1018/1544 1060/1060/1606 1059/1059/1605
+f 1019/1019/1545 1061/1061/1607 1060/1060/1608
+f 1021/1021/1550 1061/1061/1607 1020/1020/1547
+f 1022/1022/1551 1062/1062/1609 1021/1021/1550
+f 1023/1023/1553 1063/1063/1610 1022/1022/1611
+f 1024/1024/1612 1064/1064/1613 1023/1023/1553
+f 1025/1026/1557 1065/1065/1614 1024/1024/1555
+f 1025/1026/1557 1067/1066/1615 1066/1067/1616
+f 1026/1025/1556 1068/1068/1617 1067/1066/1615
+f 1027/1027/1558 1069/1069/1618 1068/1068/1617
+f 1028/1028/1619 1070/1070/1620 1069/1069/1618
+f 1029/1029/1561 1071/1071/1621 1070/1070/1622
+f 1031/1031/1566 1071/1071/1621 1030/1030/1563
+f 1032/1032/1567 1072/1072/1623 1031/1031/1566
+f 1033/1033/1569 1073/1073/1624 1032/1032/1625
+f 1034/1034/1626 1074/1074/1627 1033/1033/1569
+f 1035/1036/1572 1075/1075/1628 1034/1034/1570
+f 1035/1036/1572 1077/1076/1629 1076/1077/1630
+f 1036/1035/1571 1078/1078/1631 1077/1076/1629
+f 1037/1037/1573 1079/1079/1632 1078/1078/1631
+f 1038/1038/1575 1080/1080/1633 1079/1079/1632
+f 1039/1039/1576 1042/1041/1581 1080/1080/1634
+f 1080/1080/1633 1081/1081/1635 1082/1082/1636
+f 1041/1042/1582 1081/1081/1637 1042/1041/1638
+f 1043/1043/1639 1083/1083/1640 1041/1042/1582
+f 1044/1044/1586 1084/1084/1641 1043/1043/1583
+f 1045/1045/1587 1085/1085/1642 1044/1044/1586
+f 1046/1047/1589 1086/1086/1643 1045/1045/1587
+f 1046/1047/1589 1088/1087/1644 1087/1088/1645
+f 1047/1046/1588 1089/1089/1646 1088/1087/1644
+f 1048/1048/1590 1090/1090/1647 1089/1089/1648
+f 1049/1049/1649 1091/1091/1650 1090/1090/1647
+f 1050/1050/1593 1092/1092/1651 1091/1091/1650
+f 1052/1052/1596 1092/1092/1652 1051/1051/1653
+f 1053/1053/1654 1093/1093/1655 1052/1052/1596
+f 1054/1054/1600 1094/1094/1656 1053/1053/1597
+f 1055/1055/1601 1095/1095/1657 1054/1054/1600
+f 1056/1057/1603 1096/1096/1658 1055/1055/1601
+f 1056/1057/1603 1098/1097/1659 1097/1098/1660
+f 1057/1056/1602 1099/1099/1661 1098/1097/1659
+f 1058/1058/1604 1100/1100/1662 1099/1099/1663
+f 1059/1059/1664 1101/1101/1665 1100/1100/1662
+f 1060/1060/1606 1102/1102/1666 1101/1101/1665
+f 1062/1062/1609 1102/1102/1667 1061/1061/1668
+f 1063/1063/1669 1103/1103/1670 1062/1062/1609
+f 1064/1064/1613 1104/1104/1671 1063/1063/1610
+f 1065/1065/1614 1105/1105/1672 1064/1064/1613
+f 1066/1067/1616 1106/1106/1673 1065/1065/1614
+f 1066/1067/1616 1108/1107/1674 1107/1108/1675
+f 1067/1066/1615 1109/1109/1676 1108/1107/1674
+f 1068/1068/1617 1110/1110/1677 1109/1109/1678
+f 1069/1069/1679 1111/1111/1680 1110/1110/1677
+f 1070/1070/1620 1112/1112/1681 1111/1111/1680
+f 1072/1072/1623 1112/1112/1682 1071/1071/1683
+f 1073/1073/1684 1113/1113/1685 1072/1072/1623
+f 1074/1074/1627 1114/1114/1686 1073/1073/1624
+f 1075/1075/1628 1115/1115/1687 1074/1074/1627
+f 1076/1077/1630 1116/1116/1688 1075/1075/1628
+f 1076/1077/1630 1118/1117/1689 1117/1118/1690
+f 1077/1076/1629 1119/1119/1691 1118/1117/1689
+f 1078/1078/1631 1120/1120/1692 1119/1119/1693
+f 1079/1079/1694 1082/1082/1636 1120/1120/1692
+f 1120/1120/1692 1121/1121/1695 1122/1122/1696
+f 1082/1082/1636 1123/1123/1697 1121/1121/1695
+f 1083/1083/1640 1123/1123/1697 1081/1081/1698
+f 1084/1084/1641 1124/1124/1699 1083/1083/1640
+f 1085/1085/1700 1125/1125/1701 1084/1084/1641
+f 1086/1086/1643 1126/1126/1702 1085/1085/1700
+f 1087/1088/1645 1127/1127/1703 1086/1086/1704
+f 1087/1088/1645 1129/1128/1705 1128/1129/1706
+f 1088/1087/1707 1130/1130/1708 1129/1128/1705
+f 1089/1089/1646 1131/1131/1709 1130/1130/1708
+f 1090/1090/1647 1132/1132/1710 1131/1131/1709
+f 1091/1091/1650 1133/1133/1711 1132/1132/1710
+f 1093/1093/1655 1133/1133/1711 1092/1092/1712
+f 1094/1094/1656 1134/1134/1713 1093/1093/1655
+f 1095/1095/1714 1135/1135/1715 1094/1094/1656
+f 1096/1096/1658 1136/1136/1716 1095/1095/1714
+f 1097/1098/1660 1137/1137/1717 1096/1096/1718
+f 1097/1098/1660 1139/1138/1719 1138/1139/1720
+f 1098/1097/1721 1140/1140/1722 1139/1138/1719
+f 1099/1099/1661 1141/1141/1723 1140/1140/1722
+f 1100/1100/1662 1142/1142/1724 1141/1141/1723
+f 1101/1101/1665 1143/1143/1725 1142/1142/1724
+f 1103/1103/1670 1143/1143/1725 1102/1102/1726
+f 1104/1104/1671 1144/1144/1727 1103/1103/1670
+f 1105/1105/1728 1145/1145/1729 1104/1104/1671
+f 1106/1106/1673 1146/1146/1730 1105/1105/1728
+f 1107/1108/1675 1147/1147/1731 1106/1106/1732
+f 1107/1108/1675 1149/1148/1733 1148/1149/1734
+f 1108/1107/1735 1150/1150/1736 1149/1148/1733
+f 1109/1109/1676 1151/1151/1737 1150/1150/1736
+f 1110/1110/1677 1152/1152/1738 1151/1151/1737
+f 1111/1111/1680 1153/1153/1739 1152/1152/1738
+f 1113/1113/1685 1153/1153/1739 1112/1112/1740
+f 1114/1114/1686 1154/1154/1741 1113/1113/1685
+f 1115/1115/1742 1155/1155/1743 1114/1114/1686
+f 1116/1116/1688 1156/1156/1744 1115/1115/1742
+f 1117/1118/1690 1157/1157/1745 1116/1116/1746
+f 1117/1118/1690 1159/1158/1747 1158/1159/1748
+f 1118/1117/1749 1160/1160/1750 1159/1158/1747
+f 1119/1119/1691 1122/1122/1696 1160/1160/1750
+f 1160/1160/1750 1161/1161/1751 1162/1162/1752
+f 1122/1122/1696 1163/1163/1753 1161/1161/1751
+f 1121/1121/1695 1164/1164/1754 1163/1163/1753
+f 1124/1124/1699 1164/1164/1755 1123/1123/1697
+f 1125/1125/1701 1165/1165/1756 1124/1124/1699
+f 1126/1126/1757 1166/1166/1758 1125/1125/1701
+f 1127/1127/1703 1167/1167/1759 1126/1126/1757
+f 1128/1129/1706 1168/1168/1760 1127/1127/1703
+f 1128/1129/1706 1170/1169/1761 1169/1170/1762
+f 1129/1128/1705 1171/1171/1763 1170/1169/1764
+f 1130/1130/1708 1172/1172/1765 1171/1171/1763
+f 1131/1131/1709 1173/1173/1766 1172/1172/1765
+f 1132/1132/1710 1174/1174/1767 1173/1173/1766
+f 1134/1134/1713 1174/1174/1768 1133/1133/1711
+f 1135/1135/1715 1175/1175/1769 1134/1134/1713
+f 1136/1136/1770 1176/1176/1771 1135/1135/1715
+f 1137/1137/1717 1177/1177/1772 1136/1136/1770
+f 1138/1139/1720 1178/1178/1773 1137/1137/1717
+f 1138/1139/1720 1180/1179/1774 1179/1180/1775
+f 1139/1138/1719 1181/1181/1776 1180/1179/1777
+f 1140/1140/1722 1182/1182/1778 1181/1181/1776
+f 1141/1141/1723 1183/1183/1779 1182/1182/1778
+f 1142/1142/1724 1184/1184/1780 1183/1183/1779
+f 1144/1144/1727 1184/1184/1781 1143/1143/1725
+f 1145/1145/1729 1185/1185/1782 1144/1144/1727
+f 1146/1146/1783 1186/1186/1784 1145/1145/1729
+f 1147/1147/1731 1187/1187/1785 1146/1146/1783
+f 1148/1149/1734 1188/1188/1786 1147/1147/1731
+f 1148/1149/1734 1190/1189/1787 1189/1190/1788
+f 1149/1148/1733 1191/1191/1789 1190/1189/1790
+f 1150/1150/1736 1192/1192/1791 1191/1191/1789
+f 1151/1151/1737 1193/1193/1792 1192/1192/1791
+f 1152/1152/1738 1194/1194/1793 1193/1193/1792
+f 1154/1154/1741 1194/1194/1794 1153/1153/1739
+f 1155/1155/1743 1195/1195/1795 1154/1154/1741
+f 1156/1156/1796 1196/1196/1797 1155/1155/1743
+f 1157/1157/1745 1197/1197/1798 1156/1156/1796
+f 1158/1159/1748 1198/1198/1799 1157/1157/1745
+f 1158/1159/1748 1200/1199/1800 1199/1200/1801
+f 1159/1158/1747 1162/1162/1752 1200/1199/1802
+f 1200/1199/1802 1201/1201/1803 1202/1202/1804
+f 1162/1162/1752 1203/1203/1805 1201/1201/1806
+f 1161/1161/1807 1204/1204/1808 1203/1203/1805
+f 1163/1163/1753 1205/1205/1809 1204/1204/1808
+f 1165/1165/1756 1205/1205/1809 1164/1164/1755
+f 1166/1166/1758 1206/1206/1810 1165/1165/1756
+f 1167/1167/1759 1207/1207/1811 1166/1166/1758
+f 1168/1168/1812 1208/1208/1813 1167/1167/1759
+f 1169/1170/1762 1209/1209/1814 1168/1168/1815
+f 1169/1170/1762 1211/1210/1816 1210/1211/1817
+f 1170/1169/1764 1212/1212/1818 1211/1210/1816
+f 1171/1171/1763 1213/1213/1819 1212/1212/1820
+f 1172/1172/1765 1214/1214/1821 1213/1213/1819
+f 1173/1173/1822 1215/1215/1823 1214/1214/1821
+f 1175/1175/1769 1215/1215/1823 1174/1174/1768
+f 1176/1176/1771 1216/1216/1824 1175/1175/1769
+f 1177/1177/1772 1217/1217/1825 1176/1176/1771
+f 1178/1178/1826 1218/1218/1827 1177/1177/1772
+f 1179/1180/1775 1219/1219/1828 1178/1178/1829
+f 1179/1180/1775 1221/1220/1830 1220/1221/1831
+f 1180/1179/1777 1222/1222/1832 1221/1220/1830
+f 1181/1181/1776 1223/1223/1833 1222/1222/1834
+f 1182/1182/1835 1224/1224/1836 1223/1223/1833
+f 1183/1183/1779 1225/1225/1837 1224/1224/1836
+f 1185/1185/1782 1225/1225/1837 1184/1184/1781
+f 1186/1186/1784 1226/1226/1838 1185/1185/1782
+f 1187/1187/1785 1227/1227/1839 1186/1186/1784
+f 1188/1188/1840 1228/1228/1841 1187/1187/1785
+f 1189/1190/1788 1229/1229/1842 1188/1188/1843
+f 1189/1190/1788 1231/1230/1844 1230/1231/1845
+f 1190/1189/1790 1232/1232/1846 1231/1230/1844
+f 1191/1191/1789 1233/1233/1847 1232/1232/1848
+f 1192/1192/1791 1234/1234/1849 1233/1233/1847
+f 1193/1193/1850 1235/1235/1851 1234/1234/1849
+f 1195/1195/1795 1235/1235/1851 1194/1194/1794
+f 1196/1196/1797 1236/1236/1852 1195/1195/1795
+f 1197/1197/1798 1237/1237/1853 1196/1196/1797
+f 1198/1198/1854 1238/1238/1855 1197/1197/1798
+f 1199/1200/1801 1239/1239/1856 1198/1198/1857
+f 1199/1200/1801 1202/1202/1804 1240/1240/1858
+f 1241/1241/1859 1240/1240/1860 1202/1202/1804
+f 1243/1242/1861 1242/1243/1862 1241/1241/1859
+f 1245/1244/1863 1244/1245/1864 1243/1242/1861
+f 1247/1246/1865 1246/1247/1866 1245/1244/1863
+f 1249/1248/1867 1248/1249/1868 1247/1246/1865
+f 1251/1250/1869 1250/1251/1870 1249/1248/1867
+f 1253/1252/1871 1252/1253/1872 1251/1250/1873
+f 1255/1254/1874 1254/1255/1875 1253/1252/1871
+f 1257/1256/1876 1256/1257/1877 1255/1254/1874
+f 1259/1258/1878 1258/1259/1879 1257/1256/1876
+f 1259/1258/1878 1260/1260/1880 1258/1259/1879
+f 1259/1258/1878 1261/1261/1881 1260/1260/1880
+f 1259/1258/1878 1262/1262/1882 1261/1261/1881
+f 1259/1258/1878 1263/1263/1883 1262/1262/1884
+f 1259/1258/1878 1264/1264/1885 1263/1263/1883
+f 1259/1258/1878 1265/1265/1886 1264/1264/1885
+f 1259/1258/1878 1266/1266/1887 1265/1265/1886
+f 1259/1258/1878 1267/1267/1888 1266/1266/1889
+f 1259/1258/1878 1268/1268/1890 1267/1267/1888
+f 1259/1258/1878 1269/1269/1891 1268/1268/1890
+f 1259/1258/1878 1270/1270/1892 1269/1269/1891
+f 1259/1258/1878 1271/1271/1893 1270/1270/1892
+f 1259/1258/1878 1272/1272/1894 1271/1271/1893
+f 1259/1258/1878 1273/1273/1895 1272/1272/1896
+f 1259/1258/1878 1274/1274/1897 1273/1273/1895
+f 1259/1258/1878 1275/1275/1898 1274/1274/1897
+f 1259/1258/1878 1276/1276/1899 1275/1275/1898
+f 1259/1258/1878 1277/1277/1900 1276/1276/1901
+f 1259/1258/1878 1278/1278/1902 1277/1277/1900
+f 1259/1258/1878 1279/1279/1903 1278/1278/1902
+f 1259/1258/1878 1280/1280/1904 1279/1279/1903
+f 1259/1258/1878 1281/1281/1905 1280/1280/1904
+f 1259/1258/1878 1282/1282/1906 1281/1281/1905
+f 1259/1258/1878 1283/1283/1907 1282/1282/1908
+f 1259/1258/1878 1284/1284/1909 1283/1283/1907
+f 1259/1258/1878 1285/1285/1910 1284/1284/1909
+f 1259/1258/1878 1286/1286/1911 1285/1285/1910
+f 1259/1258/1878 1287/1287/1912 1286/1286/1913
+f 1259/1258/1878 1288/1288/1914 1287/1287/1912
+f 1259/1258/1878 1289/1289/1915 1288/1288/1914
+f 1259/1258/1878 1290/1290/1916 1289/1289/1915
+f 1259/1258/1878 1291/1291/1917 1290/1290/1916
+f 1259/1258/1878 1292/1292/1918 1291/1291/1917
+f 1259/1258/1878 1293/1293/1919 1292/1292/1920
+f 1259/1258/1878 1294/1294/1921 1293/1293/1919
+f 1259/1258/1878 1295/1295/1922 1294/1294/1921
+f 1259/1258/1878 1296/1296/1923 1295/1295/1922
+f 1259/1258/1878 1297/1297/1924 1296/1296/1925
+f 1259/1258/1878 1257/1256/1876 1297/1297/1924
+f 1297/1297/1924 1255/1254/1874 1298/1298/1926
+f 1298/1298/1926 1253/1252/1871 1299/1299/1927
+f 1299/1299/1927 1251/1250/1873 1300/1300/1928
+f 1300/1300/1928 1249/1248/1867 1301/1301/1929
+f 1301/1301/1929 1247/1246/1865 1302/1302/1930
+f 1302/1302/1930 1245/1244/1863 1303/1303/1931
+f 1303/1303/1932 1243/1242/1861 1304/1304/1933
+f 1304/1304/1934 1241/1241/1935 1305/1305/1936
+f 1305/1305/1936 1202/1202/1804 1201/1201/1937
+f 1306/1306/1938 1201/1201/1937 1203/1203/1805
+f 1307/1307/1939 1305/1305/1940 1306/1306/1941
+f 1308/1308/1942 1304/1304/1943 1307/1307/1944
+f 1309/1309/1945 1303/1303/1932 1308/1308/1946
+f 1310/1310/1947 1302/1302/1930 1309/1309/1948
+f 1311/1311/1949 1301/1301/1929 1310/1310/1947
+f 1312/1312/1950 1300/1300/1951 1311/1311/1949
+f 1313/1313/1952 1299/1299/1927 1312/1312/1950
+f 1296/1296/1923 1298/1298/1926 1313/1313/1953
+f 1295/1295/1922 1313/1313/1953 1314/1314/1954
+f 1314/1314/1955 1312/1312/1950 1315/1315/1956
+f 1315/1315/1956 1311/1311/1949 1316/1316/1957
+f 1316/1316/1958 1310/1310/1947 1317/1317/1959
+f 1317/1317/1960 1309/1309/1945 1318/1318/1961
+f 1318/1318/1962 1308/1308/1946 1319/1319/1963
+f 1319/1319/1963 1307/1307/1944 1320/1320/1964
+f 1320/1320/1965 1306/1306/1966 1321/1321/1967
+f 1321/1321/1967 1203/1203/1968 1204/1204/1808
+f 1322/1322/1969 1204/1204/1808 1205/1205/1809
+f 1323/1323/1970 1321/1321/1971 1322/1322/1969
+f 1324/1324/1972 1320/1320/1964 1323/1323/1970
+f 1325/1325/1973 1319/1319/1963 1324/1324/1974
+f 1326/1326/1975 1318/1318/1976 1325/1325/1973
+f 1327/1327/1977 1317/1317/1978 1326/1326/1975
+f 1328/1328/1979 1316/1316/1957 1327/1327/1980
+f 1329/1329/1981 1315/1315/1956 1328/1328/1979
+f 1294/1294/1982 1314/1314/1954 1329/1329/1981
+f 1294/1294/1983 1330/1330/1984 1293/1293/1985
+f 1329/1329/1981 1331/1331/1986 1330/1330/1987
+f 1328/1328/1979 1332/1332/1988 1331/1331/1986
+f 1327/1327/1977 1333/1333/1989 1332/1332/1988
+f 1326/1326/1975 1334/1334/1990 1333/1333/1991
+f 1325/1325/1973 1335/1335/1992 1334/1334/1993
+f 1324/1324/1974 1336/1336/1994 1335/1335/1992
+f 1323/1323/1995 1337/1337/1996 1336/1336/1997
+f 1322/1322/1969 1206/1206/1998 1337/1337/1999
+f 1337/1337/1996 1207/1207/1811 1338/1338/2000
+f 1336/1336/1997 1338/1338/2001 1339/1339/2002
+f 1335/1335/1992 1339/1339/2002 1340/1340/2003
+f 1334/1334/2004 1340/1340/2003 1341/1341/2005
+f 1333/1333/2006 1341/1341/2005 1342/1342/2007
+f 1332/1332/2008 1342/1342/2007 1343/1343/2009
+f 1331/1331/1986 1343/1343/2009 1344/1344/2010
+f 1330/1330/1987 1344/1344/2010 1345/1345/2011
+f 1293/1293/1919 1345/1345/2012 1292/1292/1918
+f 1292/1292/1920 1346/1346/2013 1291/1291/1917
+f 1345/1345/2011 1347/1347/2014 1346/1346/2013
+f 1344/1344/2010 1348/1348/2015 1347/1347/2014
+f 1343/1343/2009 1349/1349/2016 1348/1348/2017
+f 1342/1342/2007 1350/1350/2018 1349/1349/2016
+f 1341/1341/2005 1351/1351/2019 1350/1350/2018
+f 1340/1340/2003 1352/1352/2020 1351/1351/2021
+f 1339/1339/2022 1353/1353/2023 1352/1352/2020
+f 1338/1338/2000 1208/1208/2024 1353/1353/2025
+f 1353/1353/2023 1209/1209/1814 1354/1354/2026
+f 1352/1352/2027 1354/1354/2028 1355/1355/2029
+f 1351/1351/2019 1355/1355/2029 1356/1356/2030
+f 1350/1350/2018 1356/1356/2030 1357/1357/2031
+f 1349/1349/2016 1357/1357/2031 1358/1358/2032
+f 1348/1348/2017 1358/1358/2032 1359/1359/2033
+f 1347/1347/2014 1359/1359/2033 1360/1360/2034
+f 1346/1346/2013 1360/1360/2034 1361/1361/2035
+f 1291/1291/1917 1361/1361/2035 1290/1290/1916
+f 1290/1290/1916 1362/1362/2036 1289/1289/1915
+f 1361/1361/2035 1363/1363/2037 1362/1362/2036
+f 1360/1360/2034 1364/1364/2038 1363/1363/2037
+f 1359/1359/2033 1365/1365/2039 1364/1364/2038
+f 1358/1358/2032 1366/1366/2040 1365/1365/2039
+f 1357/1357/2041 1367/1367/2042 1366/1366/2040
+f 1356/1356/2030 1368/1368/2043 1367/1367/2044
+f 1355/1355/2029 1369/1369/2045 1368/1368/2043
+f 1354/1354/2026 1210/1211/2046 1369/1369/2047
+f 1370/1370/2048 1210/1211/2046 1211/1210/1816
+f 1371/1371/2049 1369/1369/2045 1370/1370/2048
+f 1372/1372/2050 1368/1368/2043 1371/1371/2049
+f 1373/1373/2051 1367/1367/2044 1372/1372/2050
+f 1374/1374/2052 1366/1366/2040 1373/1373/2051
+f 1375/1375/2053 1365/1365/2039 1374/1374/2052
+f 1376/1376/2054 1364/1364/2038 1375/1375/2055
+f 1377/1377/2056 1363/1363/2037 1376/1376/2054
+f 1288/1288/1914 1362/1362/2036 1377/1377/2056
+f 1287/1287/1912 1377/1377/2056 1378/1378/2057
+f 1378/1378/2057 1376/1376/2054 1379/1379/2058
+f 1379/1379/2058 1375/1375/2055 1380/1380/2059
+f 1380/1380/2059 1374/1374/2052 1381/1381/2060
+f 1381/1381/2060 1373/1373/2051 1382/1382/2061
+f 1382/1382/2061 1372/1372/2050 1383/1383/2062
+f 1383/1383/2063 1371/1371/2049 1384/1384/2064
+f 1384/1384/2065 1370/1370/2066 1385/1385/2067
+f 1385/1385/2067 1211/1210/1816 1212/1212/2068
+f 1386/1386/2069 1212/1212/2068 1213/1213/1819
+f 1387/1387/2070 1385/1385/2071 1386/1386/2069
+f 1388/1388/2072 1384/1384/2065 1387/1387/2070
+f 1389/1389/2073 1383/1383/2063 1388/1388/2074
+f 1390/1390/2075 1382/1382/2061 1389/1389/2076
+f 1391/1391/2077 1381/1381/2060 1390/1390/2075
+f 1392/1392/2078 1380/1380/2079 1391/1391/2077
+f 1393/1393/2080 1379/1379/2058 1392/1392/2078
+f 1286/1286/1911 1378/1378/2057 1393/1393/2081
+f 1285/1285/1910 1393/1393/2081 1394/1394/2082
+f 1394/1394/2083 1392/1392/2078 1395/1395/2084
+f 1395/1395/2084 1391/1391/2077 1396/1396/2085
+f 1396/1396/2086 1390/1390/2075 1397/1397/2087
+f 1397/1397/2088 1389/1389/2073 1398/1398/2089
+f 1398/1398/2090 1388/1388/2074 1399/1399/2091
+f 1399/1399/2091 1387/1387/2092 1400/1400/2093
+f 1400/1400/2094 1386/1386/2095 1401/1401/2096
+f 1401/1401/2096 1213/1213/1819 1214/1214/1821
+f 1402/1402/2097 1214/1214/1821 1215/1215/1823
+f 1403/1403/2098 1401/1401/2099 1402/1402/2097
+f 1404/1404/2100 1400/1400/2093 1403/1403/2098
+f 1405/1405/2101 1399/1399/2091 1404/1404/2102
+f 1406/1406/2103 1398/1398/2104 1405/1405/2101
+f 1407/1407/2105 1397/1397/2106 1406/1406/2103
+f 1408/1408/2107 1396/1396/2085 1407/1407/2105
+f 1409/1409/2108 1395/1395/2084 1408/1408/2107
+f 1284/1284/2109 1394/1394/2082 1409/1409/2108
+f 1284/1284/2110 1410/1410/2111 1283/1283/2112
+f 1409/1409/2108 1411/1411/2113 1410/1410/2114
+f 1408/1408/2107 1412/1412/2115 1411/1411/2113
+f 1407/1407/2105 1413/1413/2116 1412/1412/2115
+f 1406/1406/2103 1414/1414/2117 1413/1413/2118
+f 1405/1405/2101 1415/1415/2119 1414/1414/2120
+f 1404/1404/2102 1416/1416/2121 1415/1415/2119
+f 1403/1403/2122 1417/1417/2123 1416/1416/2124
+f 1402/1402/2097 1216/1216/1824 1417/1417/2125
+f 1417/1417/2123 1217/1217/1825 1418/1418/2126
+f 1416/1416/2124 1418/1418/2127 1419/1419/2128
+f 1415/1415/2119 1419/1419/2128 1420/1420/2129
+f 1414/1414/2130 1420/1420/2129 1421/1421/2131
+f 1413/1413/2132 1421/1421/2131 1422/1422/2133
+f 1412/1412/2134 1422/1422/2133 1423/1423/2135
+f 1411/1411/2113 1423/1423/2135 1424/1424/2136
+f 1410/1410/2114 1424/1424/2136 1425/1425/2137
+f 1283/1283/1907 1425/1425/2138 1282/1282/1906
+f 1282/1282/1908 1426/1426/2139 1281/1281/1905
+f 1425/1425/2138 1427/1427/2140 1426/1426/2139
+f 1424/1424/2136 1428/1428/2141 1427/1427/2140
+f 1423/1423/2135 1429/1429/2142 1428/1428/2143
+f 1422/1422/2133 1430/1430/2144 1429/1429/2142
+f 1421/1421/2131 1431/1431/2145 1430/1430/2144
+f 1420/1420/2129 1432/1432/2146 1431/1431/2147
+f 1419/1419/2148 1433/1433/2149 1432/1432/2150
+f 1418/1418/2126 1218/1218/1827 1433/1433/2151
+f 1433/1433/2149 1219/1219/1828 1434/1434/2152
+f 1432/1432/2150 1434/1434/2153 1435/1435/2154
+f 1431/1431/2147 1435/1435/2154 1436/1436/2155
+f 1430/1430/2144 1436/1436/2155 1437/1437/2156
+f 1429/1429/2142 1437/1437/2156 1438/1438/2157
+f 1428/1428/2143 1438/1438/2157 1439/1439/2158
+f 1427/1427/2140 1439/1439/2158 1440/1440/2159
+f 1426/1426/2139 1440/1440/2159 1441/1441/2160
+f 1281/1281/1905 1441/1441/2160 1280/1280/1904
+f 1280/1280/1904 1442/1442/2161 1279/1279/1903
+f 1441/1441/2160 1443/1443/2162 1442/1442/2161
+f 1440/1440/2159 1444/1444/2163 1443/1443/2162
+f 1439/1439/2158 1445/1445/2164 1444/1444/2163
+f 1438/1438/2157 1446/1446/2165 1445/1445/2164
+f 1437/1437/2166 1447/1447/2167 1446/1446/2165
+f 1436/1436/2155 1448/1448/2168 1447/1447/2169
+f 1435/1435/2154 1449/1449/2170 1448/1448/2168
+f 1434/1434/2152 1220/1221/2171 1449/1449/2172
+f 1450/1450/2173 1220/1221/2171 1221/1220/1830
+f 1451/1451/2174 1449/1449/2170 1450/1450/2173
+f 1452/1452/2175 1448/1448/2168 1451/1451/2174
+f 1453/1453/2176 1447/1447/2169 1452/1452/2175
+f 1454/1454/2177 1446/1446/2165 1453/1453/2176
+f 1455/1455/2178 1445/1445/2164 1454/1454/2177
+f 1456/1456/2179 1444/1444/2163 1455/1455/2180
+f 1457/1457/2181 1443/1443/2162 1456/1456/2179
+f 1278/1278/1902 1442/1442/2161 1457/1457/2181
+f 1277/1277/1900 1457/1457/2181 1458/1458/2182
+f 1458/1458/2182 1456/1456/2179 1459/1459/2183
+f 1459/1459/2183 1455/1455/2180 1460/1460/2184
+f 1460/1460/2184 1454/1454/2177 1461/1461/2185
+f 1461/1461/2185 1453/1453/2176 1462/1462/2186
+f 1462/1462/2186 1452/1452/2175 1463/1463/2187
+f 1463/1463/2188 1451/1451/2174 1464/1464/2189
+f 1464/1464/2190 1450/1450/2191 1465/1465/2192
+f 1465/1465/2192 1221/1220/1830 1222/1222/2193
+f 1466/1466/2194 1222/1222/2193 1223/1223/1833
+f 1467/1467/2195 1465/1465/2196 1466/1466/2197
+f 1468/1468/2198 1464/1464/2199 1467/1467/2200
+f 1469/1469/2201 1463/1463/2188 1468/1468/2202
+f 1470/1470/2203 1462/1462/2186 1469/1469/2204
+f 1471/1471/2205 1461/1461/2185 1470/1470/2203
+f 1472/1472/2206 1460/1460/2207 1471/1471/2205
+f 1473/1473/2208 1459/1459/2183 1472/1472/2206
+f 1276/1276/1899 1458/1458/2182 1473/1473/2209
+f 1275/1275/1898 1473/1473/2209 1474/1474/2210
+f 1474/1474/2211 1472/1472/2206 1475/1475/2212
+f 1475/1475/2212 1471/1471/2205 1476/1476/2213
+f 1476/1476/2214 1470/1470/2203 1477/1477/2215
+f 1477/1477/2216 1469/1469/2201 1478/1478/2217
+f 1478/1478/2218 1468/1468/2202 1479/1479/2219
+f 1479/1479/2219 1467/1467/2200 1480/1480/2220
+f 1480/1480/2221 1466/1466/2222 1481/1481/2223
+f 1481/1481/2223 1223/1223/2224 1224/1224/1836
+f 1482/1482/2225 1224/1224/1836 1225/1225/1837
+f 1483/1483/2226 1481/1481/2227 1482/1482/2225
+f 1484/1484/2228 1480/1480/2220 1483/1483/2226
+f 1485/1485/2229 1479/1479/2219 1484/1484/2230
+f 1486/1486/2231 1478/1478/2232 1485/1485/2229
+f 1487/1487/2233 1477/1477/2234 1486/1486/2231
+f 1488/1488/2235 1476/1476/2213 1487/1487/2236
+f 1489/1489/2237 1475/1475/2212 1488/1488/2235
+f 1274/1274/2238 1474/1474/2210 1489/1489/2237
+f 1274/1274/2239 1490/1490/2240 1273/1273/2241
+f 1489/1489/2237 1491/1491/2242 1490/1490/2243
+f 1488/1488/2235 1492/1492/2244 1491/1491/2242
+f 1487/1487/2233 1493/1493/2245 1492/1492/2244
+f 1486/1486/2231 1494/1494/2246 1493/1493/2247
+f 1485/1485/2229 1495/1495/2248 1494/1494/2249
+f 1484/1484/2230 1496/1496/2250 1495/1495/2248
+f 1483/1483/2251 1497/1497/2252 1496/1496/2253
+f 1482/1482/2225 1226/1226/2254 1497/1497/2255
+f 1497/1497/2252 1227/1227/1839 1498/1498/2256
+f 1496/1496/2253 1498/1498/2257 1499/1499/2258
+f 1495/1495/2248 1499/1499/2258 1500/1500/2259
+f 1494/1494/2260 1500/1500/2259 1501/1501/2261
+f 1493/1493/2262 1501/1501/2261 1502/1502/2263
+f 1492/1492/2264 1502/1502/2263 1503/1503/2265
+f 1491/1491/2242 1503/1503/2265 1504/1504/2266
+f 1490/1490/2243 1504/1504/2266 1505/1505/2267
+f 1273/1273/1895 1505/1505/2268 1272/1272/1894
+f 1272/1272/1896 1506/1506/2269 1271/1271/1893
+f 1505/1505/2267 1507/1507/2270 1506/1506/2269
+f 1504/1504/2266 1508/1508/2271 1507/1507/2270
+f 1503/1503/2265 1509/1509/2272 1508/1508/2273
+f 1502/1502/2263 1510/1510/2274 1509/1509/2272
+f 1501/1501/2261 1511/1511/2275 1510/1510/2274
+f 1500/1500/2259 1512/1512/2276 1511/1511/2277
+f 1499/1499/2278 1513/1513/2279 1512/1512/2276
+f 1498/1498/2256 1228/1228/2280 1513/1513/2281
+f 1513/1513/2279 1229/1229/1842 1514/1514/2282
+f 1512/1512/2283 1514/1514/2284 1515/1515/2285
+f 1511/1511/2275 1515/1515/2285 1516/1516/2286
+f 1510/1510/2274 1516/1516/2286 1517/1517/2287
+f 1509/1509/2272 1517/1517/2287 1518/1518/2288
+f 1508/1508/2273 1518/1518/2288 1519/1519/2289
+f 1507/1507/2270 1519/1519/2290 1520/1520/2291
+f 1506/1506/2269 1520/1520/2291 1521/1521/2292
+f 1271/1271/1893 1521/1521/2292 1270/1270/1892
+f 1270/1270/1892 1522/1522/2293 1269/1269/1891
+f 1521/1521/2292 1523/1523/2294 1522/1522/2293
+f 1520/1520/2291 1524/1524/2295 1523/1523/2294
+f 1519/1519/2289 1525/1525/2296 1524/1524/2295
+f 1518/1518/2288 1526/1526/2297 1525/1525/2296
+f 1517/1517/2298 1527/1527/2299 1526/1526/2297
+f 1516/1516/2286 1528/1528/2300 1527/1527/2301
+f 1515/1515/2285 1529/1529/2302 1528/1528/2300
+f 1514/1514/2282 1230/1231/2303 1529/1529/2304
+f 1530/1530/2305 1230/1231/2303 1231/1230/1844
+f 1531/1531/2306 1529/1529/2302 1530/1530/2305
+f 1532/1532/2307 1528/1528/2300 1531/1531/2306
+f 1533/1533/2308 1527/1527/2301 1532/1532/2307
+f 1534/1534/2309 1526/1526/2297 1533/1533/2308
+f 1535/1535/2310 1525/1525/2296 1534/1534/2309
+f 1536/1536/2311 1524/1524/2295 1535/1535/2312
+f 1537/1537/2313 1523/1523/2294 1536/1536/2311
+f 1268/1268/1890 1522/1522/2293 1537/1537/2313
+f 1267/1267/1888 1537/1537/2313 1538/1538/2314
+f 1538/1538/2314 1536/1536/2311 1539/1539/2315
+f 1539/1539/2315 1535/1535/2312 1540/1540/2316
+f 1540/1540/2316 1534/1534/2309 1541/1541/2317
+f 1541/1541/2317 1533/1533/2308 1542/1542/2318
+f 1542/1542/2318 1532/1532/2307 1543/1543/2319
+f 1543/1543/2320 1531/1531/2306 1544/1544/2321
+f 1544/1544/2322 1530/1530/2323 1545/1545/2324
+f 1545/1545/2324 1231/1230/1844 1232/1232/2325
+f 1546/1546/2326 1232/1232/2325 1233/1233/1847
+f 1547/1547/2327 1545/1545/2328 1546/1546/2326
+f 1548/1548/2329 1544/1544/2322 1547/1547/2327
+f 1549/1549/2330 1543/1543/2320 1548/1548/2331
+f 1550/1550/2332 1542/1542/2318 1549/1549/2333
+f 1551/1551/2334 1541/1541/2317 1550/1550/2332
+f 1552/1552/2335 1540/1540/2336 1551/1551/2334
+f 1553/1553/2337 1539/1539/2315 1552/1552/2335
+f 1266/1266/1887 1538/1538/2314 1553/1553/2338
+f 1265/1265/1886 1553/1553/2338 1554/1554/2339
+f 1554/1554/2340 1552/1552/2335 1555/1555/2341
+f 1555/1555/2341 1551/1551/2334 1556/1556/2342
+f 1556/1556/2343 1550/1550/2332 1557/1557/2344
+f 1557/1557/2345 1549/1549/2330 1558/1558/2346
+f 1558/1558/2347 1548/1548/2331 1559/1559/2348
+f 1559/1559/2348 1547/1547/2349 1560/1560/2350
+f 1560/1560/2351 1546/1546/2352 1561/1561/2353
+f 1561/1561/2353 1233/1233/1847 1234/1234/1849
+f 1562/1562/2354 1234/1234/1849 1235/1235/1851
+f 1563/1563/2355 1561/1561/2356 1562/1562/2354
+f 1564/1564/2357 1560/1560/2350 1563/1563/2355
+f 1565/1565/2358 1559/1559/2348 1564/1564/2359
+f 1566/1566/2360 1558/1558/2361 1565/1565/2358
+f 1567/1567/2362 1557/1557/2363 1566/1566/2360
+f 1568/1568/2364 1556/1556/2342 1567/1567/2362
+f 1569/1569/2365 1555/1555/2341 1568/1568/2364
+f 1264/1264/2366 1554/1554/2339 1569/1569/2365
+f 1264/1264/2367 1570/1570/2368 1263/1263/2369
+f 1569/1569/2365 1571/1571/2370 1570/1570/2371
+f 1568/1568/2364 1572/1572/2372 1571/1571/2370
+f 1567/1567/2362 1573/1573/2373 1572/1572/2372
+f 1566/1566/2360 1574/1574/2374 1573/1573/2375
+f 1565/1565/2358 1575/1575/2376 1574/1574/2377
+f 1564/1564/2359 1576/1576/2378 1575/1575/2376
+f 1563/1563/2379 1577/1577/2380 1576/1576/2381
+f 1562/1562/2354 1236/1236/1852 1577/1577/2382
+f 1577/1577/2380 1237/1237/1853 1578/1578/2383
+f 1576/1576/2381 1578/1578/2384 1579/1579/2385
+f 1575/1575/2376 1579/1579/2385 1580/1580/2386
+f 1574/1574/2387 1580/1580/2386 1581/1581/2388
+f 1573/1573/2389 1581/1581/2388 1582/1582/2390
+f 1572/1572/2391 1582/1582/2390 1583/1583/2392
+f 1571/1571/2370 1583/1583/2392 1584/1584/2393
+f 1570/1570/2371 1584/1584/2393 1585/1585/2394
+f 1263/1263/1883 1585/1585/2395 1262/1262/1882
+f 1262/1262/1884 1586/1586/2396 1261/1261/1881
+f 1585/1585/2395 1587/1587/2397 1586/1586/2396
+f 1584/1584/2393 1588/1588/2398 1587/1587/2397
+f 1583/1583/2392 1589/1589/2399 1588/1588/2400
+f 1582/1582/2390 1590/1590/2401 1589/1589/2399
+f 1581/1581/2388 1591/1591/2402 1590/1590/2401
+f 1580/1580/2386 1592/1592/2403 1591/1591/2404
+f 1579/1579/2405 1593/1593/2406 1592/1592/2407
+f 1578/1578/2383 1238/1238/1855 1593/1593/2408
+f 1593/1593/2406 1239/1239/1856 1594/1594/2409
+f 1592/1592/2407 1594/1594/2410 1595/1595/2411
+f 1591/1591/2404 1595/1595/2411 1596/1596/2412
+f 1590/1590/2401 1596/1596/2412 1597/1597/2413
+f 1589/1589/2399 1597/1597/2413 1598/1598/2414
+f 1588/1588/2400 1598/1598/2414 1599/1599/2415
+f 1587/1587/2397 1599/1599/2415 1600/1600/2416
+f 1586/1586/2396 1600/1600/2416 1601/1601/2417
+f 1261/1261/1881 1601/1601/2417 1260/1260/1880
+f 1260/1260/1880 1256/1257/1877 1258/1259/1879
+f 1601/1601/2417 1254/1255/1875 1256/1257/1877
+f 1600/1600/2416 1252/1253/1872 1254/1255/1875
+f 1599/1599/2415 1250/1251/1870 1252/1253/1872
+f 1598/1598/2414 1248/1249/1868 1250/1251/1870
+f 1597/1597/2418 1246/1247/2419 1248/1249/1868
+f 1596/1596/2412 1244/1245/1864 1246/1247/1866
+f 1595/1595/2411 1242/1243/1862 1244/1245/1864
+f 1594/1594/2409 1240/1240/1860 1242/1243/2420
+f 1602/1602/2421 1603/1603/2422 1604/1604/2423
+f 1602/1602/2421 1605/1605/2424 1603/1603/2425
+f 1602/1602/2421 1606/1606/2426 1605/1605/2424
+f 1602/1602/2421 1607/1607/2427 1606/1606/2426
+f 1602/1602/2421 1608/1608/2428 1607/1607/2427
+f 1602/1602/2421 1609/1609/2429 1608/1608/2428
+f 1602/1602/2421 1610/1610/2430 1609/1609/2429
+f 1602/1602/2421 1611/1611/2431 1610/1610/2430
+f 1602/1602/2421 1612/1612/2432 1611/1611/2431
+f 1602/1602/2421 1613/1613/2433 1612/1612/2434
+f 1602/1602/2421 1614/1614/2435 1613/1613/2433
+f 1602/1602/2421 1615/1615/2436 1614/1614/2437
+f 1602/1602/2421 1616/1616/2438 1615/1615/2436
+f 1602/1602/2421 1617/1617/2439 1616/1616/2438
+f 1602/1602/2421 1618/1618/2440 1617/1617/2439
+f 1602/1602/2421 1619/1619/2441 1618/1618/2440
+f 1602/1602/2421 1620/1620/2442 1619/1619/2441
+f 1602/1602/2421 1621/1621/2443 1620/1620/2442
+f 1602/1602/2421 1622/1622/2444 1621/1621/2443
+f 1602/1602/2421 1623/1623/2445 1622/1622/2446
+f 1602/1602/2421 1624/1624/2447 1623/1623/2445
+f 1602/1602/2421 1625/1625/2448 1624/1624/2449
+f 1602/1602/2421 1626/1626/2450 1625/1625/2448
+f 1602/1602/2421 1627/1627/2451 1626/1626/2450
+f 1602/1602/2421 1628/1628/2452 1627/1627/2451
+f 1602/1602/2421 1629/1629/2453 1628/1628/2452
+f 1602/1602/2421 1630/1630/2454 1629/1629/2453
+f 1602/1602/2421 1631/1631/2455 1630/1630/2454
+f 1602/1602/2421 1632/1632/2456 1631/1631/2455
+f 1602/1602/2421 1633/1633/2457 1632/1632/2458
+f 1602/1602/2421 1634/1634/2459 1633/1633/2457
+f 1602/1602/2421 1635/1635/2460 1634/1634/2461
+f 1602/1602/2421 1636/1636/2462 1635/1635/2460
+f 1602/1602/2421 1637/1637/2463 1636/1636/2462
+f 1602/1602/2421 1638/1638/2464 1637/1637/2463
+f 1602/1602/2421 1639/1639/2465 1638/1638/2464
+f 1602/1602/2421 1640/1640/2466 1639/1639/2465
+f 1602/1602/2421 1641/1641/2467 1640/1640/2466
+f 1602/1602/2421 1642/1642/2468 1641/1641/2467
+f 1602/1602/2421 1604/1604/2423 1642/1642/2469
+f 1642/1642/2469 1643/1643/2470 1644/1644/2471
+f 1603/1603/2422 1643/1643/2470 1604/1604/2423
+f 1605/1605/2424 1645/1645/2472 1603/1603/2422
+f 1606/1606/2426 1646/1646/2473 1605/1605/2424
+f 1607/1607/2427 1647/1647/2474 1606/1606/2426
+f 1608/1608/2428 1648/1648/2475 1607/1607/2476
+f 1608/1608/2428 1650/1649/2477 1649/1650/2478
+f 1609/1609/2429 1651/1651/2479 1650/1649/2477
+f 1610/1610/2430 1652/1652/2480 1651/1651/2479
+f 1611/1611/2431 1653/1653/2481 1652/1652/2480
+f 1612/1612/2434 1654/1654/2482 1653/1653/2483
+f 1614/1614/2435 1654/1654/2482 1613/1613/2433
+f 1615/1615/2436 1655/1655/2484 1614/1614/2435
+f 1616/1616/2485 1656/1656/2486 1615/1615/2436
+f 1617/1617/2439 1657/1657/2487 1616/1616/2438
+f 1618/1618/2440 1658/1658/2488 1617/1617/2439
+f 1618/1618/2440 1660/1659/2489 1659/1660/2490
+f 1619/1619/2441 1661/1661/2491 1660/1659/2489
+f 1620/1620/2442 1662/1662/2492 1661/1661/2493
+f 1621/1621/2443 1663/1663/2494 1662/1662/2492
+f 1622/1622/2446 1664/1664/2495 1663/1663/2494
+f 1624/1624/2447 1664/1664/2495 1623/1623/2445
+f 1625/1625/2448 1665/1665/2496 1624/1624/2447
+f 1626/1626/2450 1666/1666/2497 1625/1625/2448
+f 1627/1627/2451 1667/1667/2498 1626/1626/2450
+f 1628/1628/2452 1668/1668/2499 1627/1627/2500
+f 1628/1628/2452 1670/1669/2501 1669/1670/2502
+f 1629/1629/2453 1671/1671/2503 1670/1669/2501
+f 1630/1630/2454 1672/1672/2504 1671/1671/2503
+f 1631/1631/2455 1673/1673/2505 1672/1672/2504
+f 1632/1632/2458 1674/1674/2506 1673/1673/2507
+f 1634/1634/2459 1674/1674/2506 1633/1633/2457
+f 1635/1635/2460 1675/1675/2508 1634/1634/2459
+f 1636/1636/2509 1676/1676/2510 1635/1635/2460
+f 1637/1637/2463 1677/1677/2511 1636/1636/2462
+f 1638/1638/2464 1678/1678/2512 1637/1637/2463
+f 1638/1638/2464 1680/1679/2513 1679/1680/2514
+f 1639/1639/2465 1681/1681/2515 1680/1679/2513
+f 1640/1640/2466 1682/1682/2516 1681/1681/2517
+f 1641/1641/2467 1644/1644/2471 1682/1682/2516
+f 1682/1682/2518 1683/1683/2519 1684/1684/2520
+f 1644/1644/2471 1685/1685/2521 1683/1683/2522
+f 1645/1645/2472 1685/1685/2521 1643/1643/2470
+f 1646/1646/2473 1686/1686/2523 1645/1645/2472
+f 1647/1647/2524 1687/1687/2525 1646/1646/2473
+f 1648/1648/2526 1688/1688/2527 1647/1647/2528
+f 1649/1650/2529 1689/1689/2530 1648/1648/2526
+f 1649/1650/2529 1691/1690/2531 1690/1691/2532
+f 1650/1649/2477 1692/1692/2533 1691/1690/2534
+f 1651/1651/2535 1693/1693/2536 1692/1692/2537
+f 1652/1652/2538 1694/1694/2539 1693/1693/2540
+f 1653/1653/2483 1695/1695/2541 1694/1694/2539
+f 1655/1655/2484 1695/1695/2541 1654/1654/2482
+f 1656/1656/2486 1696/1696/2542 1655/1655/2484
+f 1657/1657/2543 1697/1697/2544 1656/1656/2545
+f 1658/1658/2546 1698/1698/2547 1657/1657/2548
+f 1659/1660/2549 1699/1699/2550 1658/1658/2546
+f 1659/1660/2490 1701/1700/2551 1700/1701/2552
+f 1660/1659/2489 1702/1702/2553 1701/1700/2554
+f 1661/1661/2555 1703/1703/2556 1702/1702/2557
+f 1662/1662/2558 1704/1704/2559 1703/1703/2560
+f 1663/1663/2494 1705/1705/2561 1704/1704/2562
+f 1665/1665/2496 1705/1705/2561 1664/1664/2495
+f 1666/1666/2497 1706/1706/2563 1665/1665/2496
+f 1667/1667/2564 1707/1707/2565 1666/1666/2497
+f 1668/1668/2566 1708/1708/2567 1667/1667/2568
+f 1669/1670/2569 1709/1709/2570 1668/1668/2566
+f 1669/1670/2569 1711/1710/2571 1710/1711/2572
+f 1670/1669/2501 1712/1712/2573 1711/1710/2574
+f 1671/1671/2575 1713/1713/2576 1712/1712/2577
+f 1672/1672/2578 1714/1714/2579 1713/1713/2580
+f 1673/1673/2507 1715/1715/2581 1714/1714/2579
+f 1675/1675/2508 1715/1715/2581 1674/1674/2506
+f 1676/1676/2510 1716/1716/2582 1675/1675/2508
+f 1677/1677/2583 1717/1717/2584 1676/1676/2585
+f 1678/1678/2586 1718/1718/2587 1677/1677/2588
+f 1679/1680/2589 1719/1719/2590 1678/1678/2586
+f 1679/1680/2514 1721/1720/2591 1720/1721/2592
+f 1680/1679/2513 1722/1722/2593 1721/1720/2594
+f 1681/1681/2595 1684/1684/2596 1722/1722/2597
+f 1684/1684/2596 1724/1723/2598 1722/1722/2599
+f 1683/1683/2519 1723/1724/2600 1684/1684/2520
+f 1685/1685/2521 1725/1725/2601 1683/1683/2519
+f 1685/1685/2521 1727/1726/2602 1726/1727/2603
+f 1686/1686/2523 1728/1728/2604 1727/1726/2602
+f 1687/1687/2525 1729/1729/2605 1728/1728/2606
+f 1688/1688/2607 1730/1730/2608 1729/1729/2609
+f 1689/1689/2610 1731/1731/2611 1730/1730/2608
+f 1691/1690/2531 1731/1731/2611 1690/1691/2532
+f 1692/1692/2612 1732/1732/2613 1691/1690/2534
+f 1693/1693/2536 1733/1733/2614 1692/1692/2612
+f 1694/1694/2539 1734/1734/2615 1693/1693/2540
+f 1695/1695/2541 1735/1735/2616 1694/1694/2539
+f 1695/1695/2541 1737/1736/2617 1736/1737/2618
+f 1696/1696/2542 1738/1738/2619 1737/1736/2617
+f 1697/1697/2544 1739/1739/2620 1738/1738/2621
+f 1698/1698/2622 1740/1740/2623 1739/1739/2624
+f 1699/1699/2625 1741/1741/2626 1740/1740/2623
+f 1701/1700/2551 1741/1741/2626 1700/1701/2552
+f 1702/1702/2627 1742/1742/2628 1701/1700/2554
+f 1703/1703/2556 1743/1743/2629 1702/1702/2627
+f 1704/1704/2559 1744/1744/2630 1703/1703/2560
+f 1705/1705/2561 1745/1745/2631 1704/1704/2559
+f 1705/1705/2561 1747/1746/2632 1746/1747/2633
+f 1706/1706/2563 1748/1748/2634 1747/1746/2632
+f 1707/1707/2565 1749/1749/2635 1748/1748/2636
+f 1708/1708/2637 1750/1750/2638 1749/1749/2639
+f 1709/1709/2640 1751/1751/2641 1750/1750/2638
+f 1711/1710/2571 1751/1751/2641 1710/1711/2572
+f 1712/1712/2642 1752/1752/2643 1711/1710/2574
+f 1713/1713/2576 1753/1753/2644 1712/1712/2642
+f 1714/1714/2579 1754/1754/2645 1713/1713/2580
+f 1715/1715/2581 1755/1755/2646 1714/1714/2579
+f 1715/1715/2581 1757/1756/2647 1756/1757/2648
+f 1716/1716/2582 1758/1758/2649 1757/1756/2647
+f 1717/1717/2584 1759/1759/2650 1758/1758/2651
+f 1718/1718/2652 1760/1760/2653 1759/1759/2654
+f 1719/1719/2655 1761/1761/2656 1760/1760/2653
+f 1721/1720/2591 1761/1761/2656 1720/1721/2592
+f 1722/1722/2599 1762/1762/2657 1721/1720/2594
+f 1724/1723/2598 1764/1763/2658 1762/1762/2659
+f 1723/1724/2660 1763/1764/2661 1724/1723/2662
+f 1725/1725/2601 1765/1765/2663 1723/1724/2600
+f 1726/1727/2603 1766/1766/2664 1725/1725/2601
+f 1726/1727/2603 1768/1767/2665 1767/1768/2666
+f 1727/1726/2602 1769/1769/2667 1768/1767/2668
+f 1728/1728/2669 1770/1770/2670 1769/1769/2671
+f 1729/1729/2672 1771/1771/2673 1770/1770/2670
+f 1730/1730/2608 1772/1772/2674 1771/1771/2673
+f 1732/1732/2675 1772/1772/2674 1731/1731/2676
+f 1733/1733/2614 1773/1773/2677 1732/1732/2675
+f 1734/1734/2678 1774/1774/2679 1733/1733/2680
+f 1735/1735/2616 1775/1775/2681 1734/1734/2615
+f 1736/1737/2618 1776/1776/2682 1735/1735/2616
+f 1736/1737/2618 1778/1777/2683 1777/1778/2684
+f 1737/1736/2617 1779/1779/2685 1778/1777/2686
+f 1738/1738/2619 1780/1780/2687 1779/1779/2688
+f 1739/1739/2689 1781/1781/2690 1780/1780/2687
+f 1740/1740/2623 1782/1782/2691 1781/1781/2690
+f 1742/1742/2692 1782/1782/2691 1741/1741/2693
+f 1743/1743/2629 1783/1783/2694 1742/1742/2692
+f 1744/1744/2695 1784/1784/2696 1743/1743/2697
+f 1745/1745/2631 1785/1785/2698 1744/1744/2630
+f 1746/1747/2633 1786/1786/2699 1745/1745/2631
+f 1746/1747/2633 1788/1787/2700 1787/1788/2701
+f 1747/1746/2702 1789/1789/2703 1788/1787/2704
+f 1748/1748/2634 1790/1790/2705 1789/1789/2706
+f 1749/1749/2707 1791/1791/2708 1790/1790/2705
+f 1750/1750/2638 1792/1792/2709 1791/1791/2708
+f 1752/1752/2710 1792/1792/2709 1751/1751/2711
+f 1753/1753/2644 1793/1793/2712 1752/1752/2710
+f 1754/1754/2713 1794/1794/2714 1753/1753/2715
+f 1755/1755/2646 1795/1795/2716 1754/1754/2645
+f 1756/1757/2648 1796/1796/2717 1755/1755/2646
+f 1756/1757/2648 1798/1797/2718 1797/1798/2719
+f 1757/1756/2647 1799/1799/2720 1798/1797/2721
+f 1758/1758/2649 1800/1800/2722 1799/1799/2723
+f 1759/1759/2724 1801/1801/2725 1800/1800/2722
+f 1760/1760/2653 1802/1802/2726 1801/1801/2725
+f 1762/1762/2659 1802/1802/2726 1761/1761/2727
+f 1764/1763/2658 1804/1803/2728 1802/1802/2726
+f 1763/1764/2661 1803/1804/2729 1764/1763/2658
+f 1765/1765/2663 1805/1805/2730 1763/1764/2661
+f 1766/1766/2664 1806/1806/2731 1765/1765/2732
+f 1767/1768/2666 1807/1807/2733 1766/1766/2734
+f 1767/1768/2666 1809/1808/2735 1808/1809/2736
+f 1768/1767/2665 1810/1810/2737 1809/1808/2738
+f 1769/1769/2671 1811/1811/2739 1810/1810/2740
+f 1770/1770/2670 1812/1812/2741 1811/1811/2739
+f 1771/1771/2673 1813/1813/2742 1812/1812/2741
+f 1773/1773/2677 1813/1813/2742 1772/1772/2674
+f 1774/1774/2679 1814/1814/2743 1773/1773/2677
+f 1775/1775/2681 1815/1815/2744 1774/1774/2679
+f 1776/1776/2682 1816/1816/2745 1775/1775/2746
+f 1777/1778/2684 1817/1817/2747 1776/1776/2748
+f 1777/1778/2684 1819/1818/2749 1818/1819/2750
+f 1778/1777/2683 1820/1820/2751 1819/1818/2752
+f 1779/1779/2688 1821/1821/2753 1820/1820/2754
+f 1780/1780/2687 1822/1822/2755 1821/1821/2756
+f 1781/1781/2690 1823/1823/2757 1822/1822/2755
+f 1783/1783/2694 1823/1823/2757 1782/1782/2691
+f 1784/1784/2696 1824/1824/2758 1783/1783/2694
+f 1785/1785/2698 1825/1825/2759 1784/1784/2696
+f 1786/1786/2699 1826/1826/2760 1785/1785/2761
+f 1787/1788/2701 1827/1827/2762 1786/1786/2763
+f 1787/1788/2701 1829/1828/2764 1828/1829/2765
+f 1788/1787/2700 1830/1830/2766 1829/1828/2767
+f 1789/1789/2706 1831/1831/2768 1830/1830/2769
+f 1790/1790/2705 1832/1832/2770 1831/1831/2768
+f 1791/1791/2708 1833/1833/2771 1832/1832/2770
+f 1793/1793/2712 1833/1833/2771 1792/1792/2709
+f 1794/1794/2714 1834/1834/2772 1793/1793/2712
+f 1795/1795/2716 1835/1835/2773 1794/1794/2714
+f 1796/1796/2717 1836/1836/2774 1795/1795/2775
+f 1797/1798/2719 1837/1837/2776 1796/1796/2777
+f 1797/1798/2719 1839/1838/2778 1838/1839/2779
+f 1798/1797/2718 1840/1840/2780 1839/1838/2781
+f 1799/1799/2723 1841/1841/2782 1840/1840/2783
+f 1800/1800/2722 1842/1842/2784 1841/1841/2785
+f 1801/1801/2725 1804/1803/2728 1842/1842/2784
+f 1842/1842/2784 1843/1843/2786 1844/1844/2787
+f 1803/1804/2729 1843/1843/2788 1804/1803/2789
+f 1805/1805/2730 1845/1845/2790 1803/1804/2729
+f 1806/1806/2731 1846/1846/2791 1805/1805/2730
+f 1807/1807/2792 1847/1847/2793 1806/1806/2731
+f 1808/1809/2736 1848/1848/2794 1807/1807/2795
+f 1808/1809/2736 1850/1849/2796 1849/1850/2797
+f 1809/1808/2798 1851/1851/2799 1850/1849/2796
+f 1810/1810/2737 1852/1852/2800 1851/1851/2799
+f 1811/1811/2801 1853/1853/2802 1852/1852/2800
+f 1812/1812/2741 1854/1854/2803 1853/1853/2802
+f 1814/1814/2743 1854/1854/2804 1813/1813/2805
+f 1815/1815/2744 1855/1855/2806 1814/1814/2743
+f 1816/1816/2745 1856/1856/2807 1815/1815/2744
+f 1817/1817/2808 1857/1857/2809 1816/1816/2745
+f 1818/1819/2750 1858/1858/2810 1817/1817/2811
+f 1818/1819/2750 1860/1859/2812 1859/1860/2813
+f 1819/1818/2814 1861/1861/2815 1860/1859/2812
+f 1820/1820/2751 1862/1862/2816 1861/1861/2815
+f 1821/1821/2753 1863/1863/2817 1862/1862/2816
+f 1822/1822/2755 1864/1864/2818 1863/1863/2817
+f 1824/1824/2758 1864/1864/2819 1823/1823/2820
+f 1825/1825/2759 1865/1865/2821 1824/1824/2758
+f 1826/1826/2760 1866/1866/2822 1825/1825/2759
+f 1827/1827/2823 1867/1867/2824 1826/1826/2760
+f 1828/1829/2765 1868/1868/2825 1827/1827/2826
+f 1828/1829/2765 1870/1869/2827 1869/1870/2828
+f 1829/1828/2829 1871/1871/2830 1870/1869/2827
+f 1830/1830/2766 1872/1872/2831 1871/1871/2830
+f 1831/1831/2832 1873/1873/2833 1872/1872/2831
+f 1832/1832/2770 1874/1874/2834 1873/1873/2833
+f 1834/1834/2772 1874/1874/2835 1833/1833/2836
+f 1835/1835/2773 1875/1875/2837 1834/1834/2772
+f 1836/1836/2774 1876/1876/2838 1835/1835/2773
+f 1837/1837/2839 1877/1877/2840 1836/1836/2774
+f 1838/1839/2779 1878/1878/2841 1837/1837/2842
+f 1838/1839/2779 1880/1879/2843 1879/1880/2844
+f 1839/1838/2845 1881/1881/2846 1880/1879/2843
+f 1840/1840/2780 1882/1882/2847 1881/1881/2846
+f 1841/1841/2782 1844/1844/2787 1882/1882/2847
+f 1882/1882/2847 1883/1883/2848 1884/1884/2849
+f 1844/1844/2787 1885/1885/2850 1883/1883/2848
+f 1845/1845/2790 1885/1885/2851 1843/1843/2852
+f 1846/1846/2791 1886/1886/2853 1845/1845/2854
+f 1847/1847/2793 1887/1887/2855 1846/1846/2791
+f 1848/1848/2794 1888/1888/2856 1847/1847/2793
+f 1849/1850/2797 1889/1889/2857 1848/1848/2794
+f 1849/1850/2797 1891/1890/2858 1890/1891/2859
+f 1850/1849/2796 1892/1892/2860 1891/1890/2861
+f 1851/1851/2799 1893/1893/2862 1892/1892/2863
+f 1852/1852/2800 1894/1894/2864 1893/1893/2862
+f 1853/1853/2802 1895/1895/2865 1894/1894/2864
+f 1855/1855/2806 1895/1895/2866 1854/1854/2867
+f 1856/1856/2807 1896/1896/2868 1855/1855/2869
+f 1857/1857/2809 1897/1897/2870 1856/1856/2807
+f 1858/1858/2810 1898/1898/2871 1857/1857/2809
+f 1859/1860/2813 1899/1899/2872 1858/1858/2810
+f 1859/1860/2813 1901/1900/2873 1900/1901/2874
+f 1860/1859/2812 1902/1902/2875 1901/1900/2876
+f 1861/1861/2815 1903/1903/2877 1902/1902/2878
+f 1862/1862/2816 1904/1904/2879 1903/1903/2877
+f 1863/1863/2817 1905/1905/2880 1904/1904/2879
+f 1865/1865/2821 1905/1905/2881 1864/1864/2882
+f 1866/1866/2822 1906/1906/2883 1865/1865/2884
+f 1867/1867/2824 1907/1907/2885 1866/1866/2822
+f 1868/1868/2825 1908/1908/2886 1867/1867/2824
+f 1869/1870/2828 1909/1909/2887 1868/1868/2825
+f 1869/1870/2828 1911/1910/2888 1910/1911/2889
+f 1870/1869/2827 1912/1912/2890 1911/1910/2891
+f 1871/1871/2830 1913/1913/2892 1912/1912/2893
+f 1872/1872/2831 1914/1914/2894 1913/1913/2892
+f 1873/1873/2833 1915/1915/2895 1914/1914/2894
+f 1875/1875/2837 1915/1915/2896 1874/1874/2897
+f 1876/1876/2838 1916/1916/2898 1875/1875/2899
+f 1877/1877/2840 1917/1917/2900 1876/1876/2838
+f 1878/1878/2841 1918/1918/2901 1877/1877/2840
+f 1879/1880/2844 1919/1919/2902 1878/1878/2841
+f 1879/1880/2844 1921/1920/2903 1920/1921/2904
+f 1880/1879/2843 1922/1922/2905 1921/1920/2906
+f 1881/1881/2846 1884/1884/2849 1922/1922/2907
+f 1922/1922/2907 1923/1923/2908 1924/1924/2909
+f 1884/1884/2910 1925/1925/2911 1923/1923/2912
+f 1883/1883/2913 1926/1926/2914 1925/1925/2911
+f 1886/1886/2915 1926/1926/2916 1885/1885/2850
+f 1887/1887/2917 1927/1927/2918 1886/1886/2853
+f 1888/1888/2919 1928/1928/2920 1887/1887/2921
+f 1889/1889/2857 1929/1929/2922 1888/1888/2919
+f 1890/1891/2859 1930/1930/2923 1889/1889/2857
+f 1890/1891/2859 1932/1931/2924 1931/1932/2925
+f 1891/1890/2858 1933/1933/2926 1932/1931/2924
+f 1892/1892/2863 1934/1934/2927 1933/1933/2928
+f 1893/1893/2929 1935/1935/2930 1934/1934/2931
+f 1894/1894/2932 1936/1936/2933 1935/1935/2930
+f 1896/1896/2934 1936/1936/2935 1895/1895/2865
+f 1897/1897/2936 1937/1937/2937 1896/1896/2868
+f 1898/1898/2871 1938/1938/2938 1897/1897/2939
+f 1899/1899/2872 1939/1939/2940 1898/1898/2871
+f 1900/1901/2874 1940/1940/2941 1899/1899/2872
+f 1900/1901/2874 1942/1941/2942 1941/1942/2943
+f 1901/1900/2873 1943/1943/2944 1942/1941/2942
+f 1902/1902/2878 1944/1944/2945 1943/1943/2946
+f 1903/1903/2947 1945/1945/2948 1944/1944/2949
+f 1904/1904/2950 1946/1946/2951 1945/1945/2948
+f 1906/1906/2952 1946/1946/2953 1905/1905/2880
+f 1907/1907/2954 1947/1947/2955 1906/1906/2883
+f 1908/1908/2956 1948/1948/2957 1907/1907/2958
+f 1909/1909/2887 1949/1949/2959 1908/1908/2956
+f 1910/1911/2889 1950/1950/2960 1909/1909/2887
+f 1910/1911/2889 1952/1951/2961 1951/1952/2962
+f 1911/1910/2888 1953/1953/2963 1952/1951/2961
+f 1912/1912/2893 1954/1954/2964 1953/1953/2965
+f 1913/1913/2966 1955/1955/2967 1954/1954/2968
+f 1914/1914/2969 1956/1956/2970 1955/1955/2967
+f 1916/1916/2971 1956/1956/2972 1915/1915/2895
+f 1917/1917/2973 1957/1957/2974 1916/1916/2898
+f 1918/1918/2901 1958/1958/2975 1917/1917/2976
+f 1919/1919/2902 1959/1959/2977 1918/1918/2901
+f 1920/1921/2904 1960/1960/2978 1919/1919/2902
+f 1920/1921/2904 1962/1961/2979 1961/1962/2980
+f 1921/1920/2903 1924/1924/2981 1962/1961/2979
+f 1924/1924/2982 1964/1963/2983 1962/1961/2979
+f 1923/1923/2908 1963/1964/2984 1924/1924/2981
+f 1925/1925/2985 1965/1965/2986 1923/1923/2987
+f 1926/1926/2988 1966/1966/2989 1925/1925/2985
+f 1926/1926/2990 1968/1967/2991 1967/1968/2992
+f 1927/1927/2918 1969/1969/2993 1968/1967/2994
+f 1928/1928/2995 1970/1970/2996 1969/1969/2993
+f 1929/1929/2997 1971/1971/2998 1970/1970/2996
+f 1930/1930/2923 1972/1972/2999 1971/1971/3000
+f 1932/1931/2924 1972/1972/2999 1931/1932/2925
+f 1933/1933/2926 1973/1973/3001 1932/1931/2924
+f 1934/1934/2927 1974/1974/3002 1933/1933/2926
+f 1935/1935/3003 1975/1975/3004 1934/1934/3005
+f 1936/1936/3006 1976/1976/3007 1935/1935/3003
+f 1936/1936/3008 1978/1977/3009 1977/1978/3010
+f 1937/1937/2937 1979/1979/3011 1978/1977/3012
+f 1938/1938/3013 1980/1980/3014 1979/1979/3011
+f 1939/1939/2940 1981/1981/3015 1980/1980/3014
+f 1940/1940/2941 1982/1982/3016 1981/1981/3017
+f 1942/1941/2942 1982/1982/3018 1941/1942/2943
+f 1943/1943/3019 1983/1983/3020 1942/1941/2942
+f 1944/1944/2945 1984/1984/3021 1943/1943/2944
+f 1945/1945/3022 1985/1985/3023 1944/1944/3024
+f 1946/1946/3025 1986/1986/3026 1945/1945/3022
+f 1946/1946/3025 1988/1987/3027 1987/1988/3028
+f 1947/1947/2955 1989/1989/3029 1988/1987/3030
+f 1948/1948/3031 1990/1990/3032 1989/1989/3029
+f 1949/1949/3033 1991/1991/3034 1990/1990/3032
+f 1950/1950/2960 1992/1992/3035 1991/1991/3036
+f 1952/1951/2961 1992/1992/3035 1951/1952/2962
+f 1953/1953/2963 1993/1993/3037 1952/1951/2961
+f 1954/1954/2964 1994/1994/3038 1953/1953/2963
+f 1955/1955/3039 1995/1995/3040 1954/1954/3041
+f 1956/1956/3042 1996/1996/3043 1955/1955/3039
+f 1956/1956/3044 1998/1997/3045 1997/1998/3046
+f 1957/1957/2974 1999/1999/3047 1998/1997/3048
+f 1958/1958/3049 2000/2000/3050 1999/1999/3047
+f 1959/1959/2977 2001/2001/3051 2000/2000/3050
+f 1960/1960/2978 2002/2002/3052 2001/2001/3053
+f 1962/1961/2979 2002/2002/3054 1961/1962/2980
+f 1964/1963/3055 2004/2003/3056 2002/2002/3054
+f 1963/1964/3057 2003/2004/3058 1964/1963/3055
+f 1965/1965/2986 2005/2005/3059 1963/1964/3057
+f 1966/1966/2989 2006/2006/3060 1965/1965/2986
+f 1967/1968/2992 2007/2007/3061 1966/1966/2989
+f 1967/1968/2992 2009/2008/3062 2008/2009/3063
+f 1968/1967/2994 2010/2010/3064 2009/2008/3062
+f 1969/1969/2993 2011/2011/3065 2010/2010/3064
+f 1970/1970/2996 2012/2012/3066 2011/2011/3065
+f 1971/1971/2998 2013/2013/3067 2012/2012/3066
+f 1973/1973/3068 2013/2013/3067 1972/1972/2999
+f 1974/1974/3069 2014/2014/3070 1973/1973/3068
+f 1975/1975/3004 2015/2015/3071 1974/1974/3002
+f 1976/1976/3007 2016/2016/3072 1975/1975/3004
+f 1977/1978/3010 2017/2017/3073 1976/1976/3007
+f 1977/1978/3010 2019/2018/3074 2018/2019/3075
+f 1978/1977/3012 2020/2020/3076 2019/2018/3074
+f 1979/1979/3011 2021/2021/3077 2020/2020/3076
+f 1980/1980/3014 2022/2022/3078 2021/2021/3077
+f 1981/1981/3015 2023/2023/3079 2022/2022/3078
+f 1983/1983/3080 2023/2023/3079 1982/1982/3018
+f 1984/1984/3081 2024/2024/3082 1983/1983/3080
+f 1985/1985/3023 2025/2025/3083 1984/1984/3081
+f 1986/1986/3026 2026/2026/3084 1985/1985/3023
+f 1987/1988/3028 2027/2027/3085 1986/1986/3026
+f 1987/1988/3028 2029/2028/3086 2028/2029/3087
+f 1988/1987/3030 2030/2030/3088 2029/2028/3086
+f 1989/1989/3029 2031/2031/3089 2030/2030/3088
+f 1990/1990/3032 2032/2032/3090 2031/2031/3089
+f 1991/1991/3034 2033/2033/3091 2032/2032/3090
+f 1993/1993/3092 2033/2033/3091 1992/1992/3035
+f 1994/1994/3093 2034/2034/3094 1993/1993/3092
+f 1995/1995/3040 2035/2035/3095 1994/1994/3038
+f 1996/1996/3043 2036/2036/3096 1995/1995/3040
+f 1997/1998/3046 2037/2037/3097 1996/1996/3043
+f 1997/1998/3046 2039/2038/3098 2038/2039/3099
+f 1998/1997/3048 2040/2040/3100 2039/2038/3098
+f 1999/1999/3047 2041/2041/3101 2040/2040/3100
+f 2000/2000/3050 2042/2042/3102 2041/2041/3101
+f 2001/2001/3051 2004/2003/3056 2042/2042/3102
+f 2042/2042/3103 2043/2043/3104 2044/2044/3105
+f 2003/2004/3106 2043/2043/3104 2004/2003/3056
+f 2005/2005/3107 2045/2045/3108 2003/2004/3058
+f 2006/2006/3060 2046/2046/3109 2005/2005/3110
+f 2007/2007/3111 2047/2047/3112 2006/2006/3113
+f 2008/2009/3063 2048/2048/3114 2007/2007/3061
+f 2008/2009/3063 2050/2049/3115 2049/2050/3116
+f 2009/2008/3062 2051/2051/3117 2050/2049/3115
+f 2010/2010/3064 2052/2052/3118 2051/2051/3117
+f 2011/2011/3065 2053/2053/3119 2052/2052/3118
+f 2012/2012/3120 2054/2054/3121 2053/2053/3119
+f 2014/2014/3122 2054/2054/3121 2013/2013/3067
+f 2015/2015/3123 2055/2055/3124 2014/2014/3070
+f 2016/2016/3072 2056/2056/3125 2015/2015/3071
+f 2017/2017/3073 2057/2057/3126 2016/2016/3127
+f 2018/2019/3075 2058/2058/3128 2017/2017/3073
+f 2018/2019/3075 2060/2059/3129 2059/2060/3130
+f 2019/2018/3074 2061/2061/3131 2060/2059/3129
+f 2020/2020/3076 2062/2062/3132 2061/2061/3131
+f 2021/2021/3133 2063/2063/3134 2062/2062/3132
+f 2022/2022/3135 2064/2064/3136 2063/2063/3134
+f 2024/2024/3137 2064/2064/3136 2023/2023/3079
+f 2025/2025/3138 2065/2065/3139 2024/2024/3082
+f 2026/2026/3084 2066/2066/3140 2025/2025/3141
+f 2027/2027/3142 2067/2067/3143 2026/2026/3144
+f 2028/2029/3087 2068/2068/3145 2027/2027/3085
+f 2028/2029/3087 2070/2069/3146 2069/2070/3147
+f 2029/2028/3086 2071/2071/3148 2070/2069/3146
+f 2030/2030/3088 2072/2072/3149 2071/2071/3148
+f 2031/2031/3089 2073/2073/3150 2072/2072/3149
+f 2032/2032/3151 2074/2074/3152 2073/2073/3150
+f 2034/2034/3153 2074/2074/3152 2033/2033/3091
+f 2035/2035/3154 2075/2075/3155 2034/2034/3094
+f 2036/2036/3096 2076/2076/3156 2035/2035/3095
+f 2037/2037/3097 2077/2077/3157 2036/2036/3158
+f 2038/2039/3099 2078/2078/3159 2037/2037/3097
+f 2038/2039/3099 2080/2079/3160 2079/2080/3161
+f 2039/2038/3098 2081/2081/3162 2080/2079/3160
+f 2040/2040/3100 2082/2082/3163 2081/2081/3162
+f 2041/2041/3164 2044/2044/3105 2082/2082/3163
+f 2082/2082/3163 2083/2083/3165 2084/2084/3166
+f 2044/2044/3167 2085/2085/3168 2083/2083/3165
+f 2045/2045/3108 2085/2085/3168 2043/2043/3104
+f 2046/2046/3109 2086/2086/3169 2045/2045/3108
+f 2047/2047/3170 2087/2087/3171 2046/2046/3109
+f 2048/2048/3114 2088/2088/3172 2047/2047/3112
+f 2049/2050/3173 2089/2089/3174 2048/2048/3114
+f 2049/2050/3175 2091/2090/3176 2090/2091/3177
+f 2050/2049/3115 2092/2092/3178 2091/2090/3176
+f 2051/2051/3179 2093/2093/3180 2092/2092/3178
+f 2052/2052/3118 2094/2094/3181 2093/2093/3180
+f 2053/2053/3182 2095/2095/3183 2094/2094/3181
+f 2055/2055/3184 2095/2095/3183 2054/2054/3121
+f 2056/2056/3125 2096/2096/3185 2055/2055/3124
+f 2057/2057/3186 2097/2097/3187 2056/2056/3125
+f 2058/2058/3128 2098/2098/3188 2057/2057/3126
+f 2059/2060/3189 2099/2099/3190 2058/2058/3128
+f 2059/2060/3189 2101/2100/3191 2100/2101/3192
+f 2060/2059/3129 2102/2102/3193 2101/2100/3191
+f 2061/2061/3194 2103/2103/3195 2102/2102/3193
+f 2062/2062/3132 2104/2104/3196 2103/2103/3197
+f 2063/2063/3198 2105/2105/3199 2104/2104/3196
+f 2065/2065/3139 2105/2105/3199 2064/2064/3136
+f 2066/2066/3140 2106/2106/3200 2065/2065/3139
+f 2067/2067/3201 2107/2107/3202 2066/2066/3140
+f 2068/2068/3145 2108/2108/3203 2067/2067/3143
+f 2069/2070/3204 2109/2109/3205 2068/2068/3145
+f 2069/2070/3206 2111/2110/3207 2110/2111/3208
+f 2070/2069/3146 2112/2112/3209 2111/2110/3207
+f 2071/2071/3210 2113/2113/3211 2112/2112/3209
+f 2072/2072/3149 2114/2114/3212 2113/2113/3211
+f 2073/2073/3213 2115/2115/3214 2114/2114/3212
+f 2075/2075/3215 2115/2115/3214 2074/2074/3152
+f 2076/2076/3156 2116/2116/3216 2075/2075/3155
+f 2077/2077/3217 2117/2117/3218 2076/2076/3156
+f 2078/2078/3159 2118/2118/3219 2077/2077/3157
+f 2079/2080/3220 2119/2119/3221 2078/2078/3159
+f 2079/2080/3220 2121/2120/3222 2120/2121/3223
+f 2080/2079/3160 2122/2122/3224 2121/2120/3222
+f 2081/2081/3225 2084/2084/3226 2122/2122/3224
+f 2122/2122/3224 2123/2123/3227 2124/2124/3228
+f 2084/2084/3226 2125/2125/3229 2123/2123/3227
+f 2083/2083/3165 2126/2126/3230 2125/2125/3229
+f 2086/2086/3169 2126/2126/3230 2085/2085/3168
+f 2087/2087/3171 2127/2127/3231 2086/2086/3169
+f 2088/2088/3172 2128/2128/3232 2087/2087/3171
+f 2089/2089/3174 2129/2129/3233 2088/2088/3172
+f 2090/2091/3234 2130/2130/3235 2089/2089/3174
+f 2090/2091/3236 2132/2131/3237 2131/2132/3238
+f 2091/2090/3176 2133/2133/3239 2132/2131/3237
+f 2092/2092/3178 2134/2134/3240 2133/2133/3239
+f 2093/2093/3180 2135/2135/3241 2134/2134/3240
+f 2094/2094/3181 2136/2136/3242 2135/2135/3241
+f 2096/2096/3185 2136/2136/3242 2095/2095/3183
+f 2097/2097/3187 2137/2137/3243 2096/2096/3185
+f 2098/2098/3188 2138/2138/3244 2097/2097/3187
+f 2099/2099/3190 2139/2139/3245 2098/2098/3188
+f 2100/2101/3192 2140/2140/3246 2099/2099/3190
+f 2100/2101/3247 2142/2141/3248 2141/2142/3249
+f 2101/2100/3191 2143/2143/3250 2142/2141/3248
+f 2102/2102/3193 2144/2144/3251 2143/2143/3250
+f 2103/2103/3195 2145/2145/3252 2144/2144/3251
+f 2104/2104/3196 2146/2146/3253 2145/2145/3252
+f 2106/2106/3200 2146/2146/3253 2105/2105/3199
+f 2107/2107/3202 2147/2147/3254 2106/2106/3200
+f 2108/2108/3203 2148/2148/3255 2107/2107/3202
+f 2109/2109/3205 2149/2149/3256 2108/2108/3203
+f 2110/2111/3257 2150/2150/3258 2109/2109/3205
+f 2110/2111/3259 2152/2151/3260 2151/2152/3261
+f 2111/2110/3207 2153/2153/3262 2152/2151/3260
+f 2112/2112/3209 2154/2154/3263 2153/2153/3262
+f 2113/2113/3211 2155/2155/3264 2154/2154/3263
+f 2114/2114/3212 2156/2156/3265 2155/2155/3264
+f 2116/2116/3216 2156/2156/3265 2115/2115/3214
+f 2117/2117/3218 2157/2157/3266 2116/2116/3216
+f 2118/2118/3219 2158/2158/3267 2117/2117/3218
+f 2119/2119/3221 2159/2159/3268 2118/2118/3219
+f 2120/2121/3223 2160/2160/3269 2119/2119/3221
+f 2120/2121/3270 2162/2161/3271 2161/2162/3272
+f 2121/2120/3222 2124/2124/3228 2162/2161/3271
+f 2162/2161/3271 2163/2163/3273 2164/2164/3274
+f 2124/2124/3228 2165/2165/3275 2163/2163/3273
+f 2123/2123/3227 2166/2166/3276 2165/2165/3277
+f 2125/2125/3229 2167/2167/3278 2166/2166/3276
+f 2127/2127/3231 2167/2167/3278 2126/2126/3279
+f 2128/2128/3232 2168/2168/3280 2127/2127/3231
+f 2129/2129/3233 2169/2169/3281 2128/2128/3232
+f 2130/2130/3235 2170/2170/3282 2129/2129/3233
+f 2131/2132/3238 2171/2171/3283 2130/2130/3235
+f 2131/2132/3238 2173/2172/3284 2172/2173/3285
+f 2132/2131/3237 2174/2174/3286 2173/2172/3284
+f 2133/2133/3239 2175/2175/3287 2174/2174/3286
+f 2134/2134/3288 2176/2176/3289 2175/2175/3290
+f 2135/2135/3241 2177/2177/3291 2176/2176/3289
+f 2137/2137/3243 2177/2177/3291 2136/2136/3292
+f 2138/2138/3244 2178/2178/3293 2137/2137/3243
+f 2139/2139/3245 2179/2179/3294 2138/2138/3295
+f 2140/2140/3246 2180/2180/3296 2139/2139/3245
+f 2141/2142/3249 2181/2181/3297 2140/2140/3246
+f 2141/2142/3249 2183/2182/3298 2182/2183/3299
+f 2142/2141/3248 2184/2184/3300 2183/2182/3298
+f 2143/2143/3250 2185/2185/3301 2184/2184/3300
+f 2144/2144/3251 2186/2186/3302 2185/2185/3303
+f 2145/2145/3252 2187/2187/3304 2186/2186/3302
+f 2147/2147/3254 2187/2187/3304 2146/2146/3305
+f 2148/2148/3255 2188/2188/3306 2147/2147/3254
+f 2149/2149/3256 2189/2189/3307 2148/2148/3255
+f 2150/2150/3258 2190/2190/3308 2149/2149/3256
+f 2151/2152/3261 2191/2191/3309 2150/2150/3258
+f 2151/2152/3261 2193/2192/3310 2192/2193/3311
+f 2152/2151/3260 2194/2194/3312 2193/2192/3310
+f 2153/2153/3262 2195/2195/3313 2194/2194/3312
+f 2154/2154/3314 2196/2196/3315 2195/2195/3316
+f 2155/2155/3264 2197/2197/3317 2196/2196/3315
+f 2157/2157/3266 2197/2197/3317 2156/2156/3318
+f 2158/2158/3267 2198/2198/3319 2157/2157/3266
+f 2159/2159/3268 2199/2199/3320 2158/2158/3321
+f 2160/2160/3269 2200/2200/3322 2159/2159/3268
+f 2161/2162/3272 2201/2201/3323 2160/2160/3269
+f 2161/2162/3272 2164/2164/3274 2202/2202/3324
+f 2202/2202/3324 2203/2203/3325 2204/2204/3326
+f 2164/2164/3274 2205/2205/3327 2203/2203/3328
+f 2163/2163/3329 2206/2206/3330 2205/2205/3327
+f 2165/2165/3275 2207/2207/3331 2206/2206/3330
+f 2166/2166/3332 2208/2208/3333 2207/2207/3331
+f 2168/2168/3280 2208/2208/3333 2167/2167/3278
+f 2169/2169/3281 2209/2209/3334 2168/2168/3280
+f 2170/2170/3282 2210/2210/3335 2169/2169/3336
+f 2171/2171/3283 2211/2211/3337 2170/2170/3282
+f 2172/2173/3285 2212/2212/3338 2171/2171/3283
+f 2172/2173/3285 2214/2213/3339 2213/2214/3340
+f 2173/2172/3284 2215/2215/3341 2214/2213/3342
+f 2174/2174/3286 2216/2216/3343 2215/2215/3341
+f 2175/2175/3287 2217/2217/3344 2216/2216/3343
+f 2176/2176/3345 2218/2218/3346 2217/2217/3344
+f 2178/2178/3293 2218/2218/3346 2177/2177/3291
+f 2179/2179/3294 2219/2219/3347 2178/2178/3293
+f 2180/2180/3348 2220/2220/3349 2179/2179/3350
+f 2181/2181/3297 2221/2221/3351 2180/2180/3296
+f 2182/2183/3299 2222/2222/3352 2181/2181/3297
+f 2182/2183/3299 2224/2223/3353 2223/2224/3354
+f 2183/2182/3298 2225/2225/3355 2224/2223/3356
+f 2184/2184/3357 2226/2226/3358 2225/2225/3355
+f 2185/2185/3301 2227/2227/3359 2226/2226/3358
+f 2186/2186/3360 2228/2228/3361 2227/2227/3359
+f 2188/2188/3306 2228/2228/3361 2187/2187/3304
+f 2189/2189/3307 2229/2229/3362 2188/2188/3306
+f 2190/2190/3308 2230/2230/3363 2189/2189/3364
+f 2191/2191/3309 2231/2231/3365 2190/2190/3308
+f 2192/2193/3311 2232/2232/3366 2191/2191/3309
+f 2192/2193/3311 2234/2233/3367 2233/2234/3368
+f 2193/2192/3310 2235/2235/3369 2234/2233/3370
+f 2194/2194/3312 2236/2236/3371 2235/2235/3369
+f 2195/2195/3313 2237/2237/3372 2236/2236/3371
+f 2196/2196/3373 2238/2238/3374 2237/2237/3372
+f 2198/2198/3319 2238/2238/3374 2197/2197/3317
+f 2199/2199/3320 2239/2239/3375 2198/2198/3319
+f 2200/2200/3376 2240/2240/3377 2199/2199/3378
+f 2201/2201/3323 2241/2241/3379 2200/2200/3322
+f 2202/2202/3324 2242/2242/3380 2201/2201/3323
+f 2204/2204/3326 2244/2243/3381 2242/2242/3380
+f 2204/2204/3382 2245/2244/3383 2243/2245/3384
+f 2203/2203/3325 2246/2246/3385 2245/2244/3386
+f 2205/2205/3327 2247/2247/3387 2246/2246/3385
+f 2206/2206/3330 2248/2248/3388 2247/2247/3387
+f 2207/2207/3331 2249/2249/3389 2248/2248/3388
+f 2209/2209/3334 2249/2249/3389 2208/2208/3333
+f 2210/2210/3335 2250/2250/3390 2209/2209/3334
+f 2211/2211/3337 2251/2251/3391 2210/2210/3335
+f 2212/2212/3338 2252/2252/3392 2211/2211/3337
+f 2213/2214/3340 2253/2253/3393 2212/2212/3338
+f 2213/2214/3394 2255/2254/3395 2254/2255/3396
+f 2214/2213/3339 2256/2256/3397 2255/2254/3398
+f 2215/2215/3341 2257/2257/3399 2256/2256/3397
+f 2216/2216/3343 2258/2258/3400 2257/2257/3399
+f 2217/2217/3344 2259/2259/3401 2258/2258/3400
+f 2219/2219/3347 2259/2259/3401 2218/2218/3346
+f 2220/2220/3402 2260/2260/3403 2219/2219/3347
+f 2221/2221/3351 2261/2261/3404 2220/2220/3402
+f 2222/2222/3352 2262/2262/3405 2221/2221/3351
+f 2223/2224/3354 2263/2263/3406 2222/2222/3352
+f 2223/2224/3407 2265/2264/3408 2264/2265/3409
+f 2224/2223/3353 2266/2266/3410 2265/2264/3411
+f 2225/2225/3355 2267/2267/3412 2266/2266/3410
+f 2226/2226/3358 2268/2268/3413 2267/2267/3412
+f 2227/2227/3359 2269/2269/3414 2268/2268/3413
+f 2229/2229/3362 2269/2269/3414 2228/2228/3361
+f 2230/2230/3363 2270/2270/3415 2229/2229/3362
+f 2231/2231/3365 2271/2271/3416 2230/2230/3363
+f 2232/2232/3366 2272/2272/3417 2231/2231/3365
+f 2233/2234/3368 2273/2273/3418 2232/2232/3366
+f 2233/2234/3419 2275/2274/3420 2274/2275/3421
+f 2234/2233/3367 2276/2276/3422 2275/2274/3423
+f 2235/2235/3369 2277/2277/3424 2276/2276/3422
+f 2236/2236/3371 2278/2278/3425 2277/2277/3424
+f 2237/2237/3372 2279/2279/3426 2278/2278/3425
+f 2239/2239/3375 2279/2279/3426 2238/2238/3374
+f 2240/2240/3427 2280/2280/3428 2239/2239/3375
+f 2241/2241/3379 2281/2281/3429 2240/2240/3427
+f 2242/2242/3380 2282/2282/3430 2241/2241/3379
+f 2244/2243/3381 2284/2283/3431 2282/2282/3430
+f 2243/2245/3384 2283/2284/3432 2244/2243/3381
+f 2243/2245/3384 2286/2285/3433 2285/2286/3434
+f 2245/2244/3383 2287/2287/3435 2286/2285/3433
+f 2246/2246/3385 2288/2288/3436 2287/2287/3437
+f 2247/2247/3387 2289/2289/3438 2288/2288/3436
+f 2248/2248/3388 2290/2290/3439 2289/2289/3438
+f 2250/2250/3390 2290/2290/3439 2249/2249/3389
+f 2251/2251/3391 2291/2291/3440 2250/2250/3390
+f 2252/2252/3392 2292/2292/3441 2251/2251/3391
+f 2253/2253/3393 2293/2293/3442 2252/2252/3392
+f 2254/2255/3396 2294/2294/3443 2253/2253/3393
+f 2254/2255/3396 2296/2295/3444 2295/2296/3445
+f 2255/2254/3395 2297/2297/3446 2296/2295/3444
+f 2256/2256/3397 2298/2298/3447 2297/2297/3448
+f 2257/2257/3399 2299/2299/3449 2298/2298/3447
+f 2258/2258/3400 2300/2300/3450 2299/2299/3449
+f 2260/2260/3403 2300/2300/3450 2259/2259/3401
+f 2261/2261/3404 2301/2301/3451 2260/2260/3403
+f 2262/2262/3405 2302/2302/3452 2261/2261/3404
+f 2263/2263/3406 2303/2303/3453 2262/2262/3405
+f 2264/2265/3409 2304/2304/3454 2263/2263/3406
+f 2264/2265/3409 2306/2305/3455 2305/2306/3456
+f 2265/2264/3408 2307/2307/3457 2306/2305/3455
+f 2266/2266/3410 2308/2308/3458 2307/2307/3459
+f 2267/2267/3412 2309/2309/3460 2308/2308/3458
+f 2268/2268/3413 2310/2310/3461 2309/2309/3460
+f 2270/2270/3415 2310/2310/3461 2269/2269/3414
+f 2271/2271/3416 2311/2311/3462 2270/2270/3415
+f 2272/2272/3417 2312/2312/3463 2271/2271/3416
+f 2273/2273/3418 2313/2313/3464 2272/2272/3417
+f 2274/2275/3421 2314/2314/3465 2273/2273/3418
+f 2274/2275/3421 2316/2315/3466 2315/2316/3467
+f 2275/2274/3420 2317/2317/3468 2316/2315/3466
+f 2276/2276/3422 2318/2318/3469 2317/2317/3470
+f 2277/2277/3424 2319/2319/3471 2318/2318/3469
+f 2278/2278/3425 2320/2320/3472 2319/2319/3471
+f 2280/2280/3428 2320/2320/3472 2279/2279/3426
+f 2281/2281/3429 2321/2321/3473 2280/2280/3428
+f 2282/2282/3430 2322/2322/3474 2281/2281/3429
+f 2284/2283/3431 2324/2323/3475 2322/2322/3474
+f 2283/2284/3432 2323/2324/3476 2284/2283/3477
+f 2285/2286/3434 2325/2325/3478 2283/2284/3432
+f 2285/2286/3434 2327/2326/3479 2326/2327/3480
+f 2286/2285/3433 2328/2328/3481 2327/2326/3479
+f 2287/2287/3437 2329/2329/3482 2328/2328/3481
+f 2288/2288/3436 2330/2330/3483 2329/2329/3484
+f 2289/2289/3438 2331/2331/3485 2330/2330/3486
+f 2291/2291/3440 2331/2331/3485 2290/2290/3439
+f 2292/2292/3441 2332/2332/3487 2291/2291/3440
+f 2293/2293/3442 2333/2333/3488 2292/2292/3441
+f 2294/2294/3443 2334/2334/3489 2293/2293/3490
+f 2295/2296/3445 2335/2335/3491 2294/2294/3443
+f 2295/2296/3445 2337/2336/3492 2336/2337/3493
+f 2296/2295/3444 2338/2338/3494 2337/2336/3492
+f 2297/2297/3448 2339/2339/3495 2338/2338/3494
+f 2298/2298/3447 2340/2340/3496 2339/2339/3497
+f 2299/2299/3449 2341/2341/3498 2340/2340/3499
+f 2301/2301/3451 2341/2341/3498 2300/2300/3450
+f 2302/2302/3452 2342/2342/3500 2301/2301/3451
+f 2303/2303/3453 2343/2343/3501 2302/2302/3452
+f 2304/2304/3454 2344/2344/3502 2303/2303/3503
+f 2305/2306/3456 2345/2345/3504 2304/2304/3454
+f 2305/2306/3456 2347/2346/3505 2346/2347/3506
+f 2306/2305/3455 2348/2348/3507 2347/2346/3505
+f 2307/2307/3459 2349/2349/3508 2348/2348/3507
+f 2308/2308/3458 2350/2350/3509 2349/2349/3510
+f 2309/2309/3460 2351/2351/3511 2350/2350/3512
+f 2311/2311/3462 2351/2351/3511 2310/2310/3461
+f 2312/2312/3463 2352/2352/3513 2311/2311/3462
+f 2313/2313/3464 2353/2353/3514 2312/2312/3463
+f 2314/2314/3465 2354/2354/3515 2313/2313/3516
+f 2315/2316/3467 2355/2355/3517 2314/2314/3465
+f 2315/2316/3467 2357/2356/3518 2356/2357/3519
+f 2316/2315/3466 2358/2358/3520 2357/2356/3518
+f 2317/2317/3470 2359/2359/3521 2358/2358/3520
+f 2318/2318/3469 2360/2360/3522 2359/2359/3523
+f 2319/2319/3471 2361/2361/3524 2360/2360/3525
+f 2321/2321/3473 2361/2361/3524 2320/2320/3472
+f 2322/2322/3474 2362/2362/3526 2321/2321/3473
+f 2324/2323/3475 2364/2363/3527 2362/2362/3528
+f 2323/2324/3476 2363/2364/3529 2324/2323/3475
+f 2325/2325/3530 2365/2365/3531 2323/2324/3476
+f 2326/2327/3480 2366/2366/3532 2325/2325/3478
+f 2326/2327/3480 2368/2367/3533 2367/2368/3534
+f 2327/2326/3479 2369/2369/3535 2368/2367/3536
+f 2328/2328/3481 2370/2370/3537 2369/2369/3535
+f 2329/2329/3482 2371/2371/3538 2370/2370/3537
+f 2330/2330/3539 2372/2372/3540 2371/2371/3541
+f 2332/2332/3487 2372/2372/3540 2331/2331/3542
+f 2333/2333/3488 2373/2373/3543 2332/2332/3544
+f 2334/2334/3489 2374/2374/3545 2333/2333/3488
+f 2335/2335/3546 2375/2375/3547 2334/2334/3489
+f 2336/2337/3493 2376/2376/3548 2335/2335/3491
+f 2336/2337/3493 2378/2377/3549 2377/2378/3550
+f 2337/2336/3492 2379/2379/3551 2378/2377/3552
+f 2338/2338/3494 2380/2380/3553 2379/2379/3551
+f 2339/2339/3497 2381/2381/3554 2380/2380/3553
+f 2340/2340/3499 2382/2382/3555 2381/2381/3554
+f 2342/2342/3556 2382/2382/3555 2341/2341/3557
+f 2343/2343/3501 2383/2383/3558 2342/2342/3559
+f 2344/2344/3502 2384/2384/3560 2343/2343/3501
+f 2345/2345/3561 2385/2385/3562 2344/2344/3502
+f 2346/2347/3506 2386/2386/3563 2345/2345/3504
+f 2346/2347/3506 2388/2387/3564 2387/2388/3565
+f 2347/2346/3505 2389/2389/3566 2388/2387/3567
+f 2348/2348/3507 2390/2390/3568 2389/2389/3566
+f 2349/2349/3508 2391/2391/3569 2390/2390/3568
+f 2350/2350/3570 2392/2392/3571 2391/2391/3572
+f 2352/2352/3513 2392/2392/3571 2351/2351/3573
+f 2353/2353/3514 2393/2393/3574 2352/2352/3575
+f 2354/2354/3515 2394/2394/3576 2353/2353/3514
+f 2355/2355/3577 2395/2395/3578 2354/2354/3515
+f 2356/2357/3519 2396/2396/3579 2355/2355/3517
+f 2356/2357/3519 2398/2397/3580 2397/2398/3581
+f 2357/2356/3518 2399/2399/3582 2398/2397/3583
+f 2358/2358/3520 2400/2400/3584 2399/2399/3582
+f 2359/2359/3523 2401/2401/3585 2400/2400/3584
+f 2360/2360/3525 2402/2402/3586 2401/2401/3585
+f 2362/2362/3587 2402/2402/3586 2361/2361/3588
+f 2403/2403/3589 2405/2403/3590 2406/2403/3591
+f 2404/2403/3592 2408/2403/3593 2405/2403/3590
+f 2407/2403/3594 2410/2403/3595 2408/2403/3593
+f 2409/2403/3596 2412/2403/3597 2410/2403/3598
+f 2411/2403/3599 2414/2403/3600 2412/2403/3597
+f 2413/2403/3600 2416/2403/3601 2414/2403/3600
+f 2415/2403/3602 2418/2403/3603 2416/2403/3601
+f 2417/2403/3604 2420/2403/3605 2418/2403/3603
+f 2419/2403/3606 2422/2403/3607 2420/2403/3605
+f 2421/2403/3608 2424/2403/3609 2422/2403/3607
+f 2424/2403/3609 2426/2403/3610 2425/2403/3611
+f 2427/2403/3612 2424/2403/3609 2425/2403/3613
+f 2429/2404/3614 2428/2403/3615 2427/2403/3612
+f 2431/2405/3616 2430/2406/3617 2429/2404/3618
+f 2433/2407/3619 2432/2408/3620 2431/2405/3616
+f 2435/2409/3621 2434/2410/3622 2433/2407/3619
+f 2437/2411/3623 2436/2412/3624 2435/2409/3625
+f 2439/2413/3626 2438/2414/3627 2437/2411/3628
+f 2441/2415/3629 2440/2416/3630 2439/2413/3631
+f 2443/2417/3632 2442/2418/3633 2441/2415/3634
+f 2445/2419/3635 2444/2420/3636 2443/2417/3637
+f 2447/2421/3638 2446/2422/3639 2445/2419/3635
+f 2449/2423/3640 2448/2424/3641 2447/2421/3638
+f 2451/2425/3642 2450/2426/3643 2449/2423/3640
+f 2453/2427/3644 2452/2428/3645 2451/2425/3642
+f 2455/2429/3646 2454/2430/3647 2453/2427/3648
+f 2457/2431/3649 2456/2432/3650 2455/2429/3646
+f 2459/2433/3651 2458/2434/3652 2457/2431/3653
+f 2461/2435/3654 2460/2436/3655 2459/2433/3651
+f 2463/2437/3656 2462/2438/3657 2461/2435/3654
+f 2462/2438/3657 2466/2439/3658 2465/2440/3659
+f 2460/2436/3655 2465/2440/3659 2467/2441/3660
+f 2458/2434/3652 2467/2441/3660 2468/2442/3661
+f 2456/2432/3650 2468/2442/3662 2469/2443/3663
+f 2454/2430/3647 2469/2443/3663 2470/2444/3664
+f 2452/2428/3645 2470/2444/3664 2471/2445/3665
+f 2450/2426/3643 2471/2445/3666 2472/2446/3667
+f 2448/2424/3641 2472/2446/3667 2473/2447/3668
+f 2446/2422/3639 2473/2447/3668 2474/2448/3669
+f 2444/2420/3636 2474/2448/3669 2475/2449/3670
+f 2442/2418/3633 2475/2449/3671 2476/2450/3672
+f 2440/2416/3630 2476/2450/3673 2477/2451/3674
+f 2438/2414/3627 2477/2451/3674 2478/2452/3675
+f 2436/2412/3624 2478/2452/3676 2479/2453/3677
+f 2434/2410/3622 2479/2453/3678 2480/2454/3679
+f 2432/2408/3620 2480/2454/3679 2481/2455/3680
+f 2430/2406/3617 2481/2455/3680 2482/2406/3681
+f 2428/2403/3615 2482/2406/3682 2483/2403/3683
+f 2424/2403/3609 2483/2403/3683 2422/2403/3684
+f 2422/2403/3607 2484/2403/3685 2420/2403/3686
+f 2483/2403/3683 2485/2406/3687 2484/2403/3685
+f 2482/2406/3681 2486/2455/3688 2485/2406/3687
+f 2481/2455/3680 2487/2456/3689 2486/2455/3688
+f 2480/2454/3690 2488/2457/3691 2487/2456/3689
+f 2479/2453/3677 2489/2458/3692 2488/2457/3693
+f 2489/2458/3692 2478/2452/3675 2477/2451/3674
+f 2489/2458/3692 2477/2451/3674 2490/2459/3694
+f 2477/2451/3674 2491/2460/3695 2490/2459/3696
+f 2476/2450/3697 2492/2461/3698 2491/2460/3695
+f 2475/2449/3670 2493/2462/3699 2492/2461/3700
+f 2474/2448/3701 2494/2463/3702 2493/2462/3699
+f 2473/2447/3668 2495/2464/3703 2494/2463/3704
+f 2472/2446/3667 2496/2465/3705 2495/2464/3703
+f 2496/2465/3705 2471/2445/3666 2470/2444/3664
+f 2496/2465/3705 2470/2444/3664 2497/2466/3706
+f 2497/2466/3706 2470/2444/3664 2469/2443/3663
+f 2497/2466/3707 2469/2443/3663 2498/2467/3708
+f 2498/2467/3709 2469/2443/3663 2468/2442/3662
+f 2498/2467/3710 2468/2442/3711 2499/2468/3712
+f 2499/2468/3713 2468/2442/3714 2467/2441/3660
+f 2499/2468/3713 2467/2441/3660 2500/2469/3715
+f 2467/2441/3660 2501/2470/3716 2500/2469/3715
+f 2465/2440/3659 2502/2471/3717 2501/2470/3718
+f 2501/2470/3718 2504/2472/3719 2503/2473/3720
+f 2505/2474/3721 2501/2470/3718 2503/2473/3720
+f 2505/2474/3721 2500/2469/3722 2501/2470/3723
+f 2506/2475/3724 2500/2469/3715 2505/2474/3721
+f 2506/2475/3724 2499/2468/3713 2500/2469/3715
+f 2507/2476/3725 2499/2468/3713 2506/2475/3724
+f 2507/2476/3725 2498/2467/3708 2499/2468/3713
+f 2508/2477/3726 2498/2467/3708 2507/2476/3725
+f 2508/2477/3726 2497/2466/3706 2498/2467/3708
+f 2509/2478/3727 2497/2466/3706 2508/2477/3726
+f 2509/2478/3727 2496/2465/3705 2497/2466/3707
+f 2510/2479/3728 2496/2465/3705 2509/2478/3729
+f 2510/2479/3730 2495/2464/3703 2496/2465/3705
+f 2494/2463/3731 2510/2479/3732 2511/2480/3733
+f 2493/2462/3699 2511/2480/3733 2512/2481/3734
+f 2492/2461/3698 2512/2481/3735 2513/2482/3736
+f 2491/2460/3695 2513/2482/3737 2514/2483/3738
+f 2515/2484/3739 2491/2460/3695 2514/2483/3738
+f 2515/2484/3740 2490/2459/3694 2491/2460/3695
+f 2516/2485/3741 2490/2459/3696 2515/2484/3740
+f 2516/2485/3742 2489/2458/3743 2490/2459/3694
+f 2517/2486/3744 2489/2458/3692 2516/2485/3741
+f 2517/2486/3744 2488/2457/3693 2489/2458/3692
+f 2487/2456/3689 2517/2486/3744 2518/2487/3745
+f 2486/2455/3688 2518/2487/3746 2519/2488/3747
+f 2485/2406/3687 2519/2488/3747 2520/2406/3748
+f 2484/2403/3685 2520/2406/3748 2521/2403/3749
+f 2420/2403/3605 2521/2403/3749 2418/2403/3750
+f 2418/2403/3750 2522/2403/3751 2416/2403/3752
+f 2521/2403/3749 2523/2406/3753 2522/2403/3754
+f 2520/2406/3755 2524/2489/3756 2523/2406/3753
+f 2519/2488/3757 2525/2490/3758 2524/2489/3756
+f 2518/2487/3759 2526/2491/3760 2525/2490/3761
+f 2526/2491/3762 2517/2486/3744 2516/2485/3741
+f 2526/2491/3762 2516/2485/3741 2527/2492/3763
+f 2527/2492/3763 2516/2485/3741 2515/2484/3740
+f 2527/2492/3763 2515/2484/3739 2528/2493/3764
+f 2515/2484/3765 2529/2494/3766 2528/2493/3764
+f 2514/2483/3738 2530/2495/3767 2529/2494/3768
+f 2513/2482/3736 2531/2496/3769 2530/2495/3770
+f 2512/2481/3734 2532/2497/3771 2531/2496/3772
+f 2511/2480/3733 2533/2498/3773 2532/2497/3774
+f 2510/2479/3732 2534/2499/3775 2533/2498/3773
+f 2534/2499/3776 2509/2478/3727 2508/2477/3726
+f 2534/2499/3776 2508/2477/3726 2535/2500/3777
+f 2535/2500/3777 2508/2477/3726 2507/2476/3778
+f 2535/2500/3777 2507/2476/3778 2536/2501/3779
+f 2536/2501/3779 2507/2476/3725 2506/2475/3724
+f 2536/2501/3779 2506/2475/3724 2537/2502/3780
+f 2537/2502/3780 2506/2475/3724 2505/2474/3721
+f 2537/2502/3780 2505/2474/3721 2538/2503/3781
+f 2538/2503/3781 2505/2474/3721 2503/2473/3720
+f 2538/2503/3781 2503/2473/3782 2539/2504/3783
+f 2503/2473/3720 2540/2505/3784 2539/2504/3783
+f 2539/2504/3783 2542/2506/3785 2541/2507/3786
+f 2538/2503/3781 2541/2507/3786 2543/2508/3787
+f 2537/2502/3780 2543/2508/3788 2544/2509/3789
+f 2536/2501/3779 2544/2509/3790 2545/2510/3791
+f 2535/2500/3792 2545/2510/3791 2546/2511/3793
+f 2534/2499/3794 2546/2511/3793 2547/2512/3795
+f 2533/2498/3773 2547/2512/3795 2548/2513/3796
+f 2532/2497/3774 2548/2513/3796 2549/2514/3797
+f 2531/2496/3798 2549/2514/3799 2550/2515/3800
+f 2530/2495/3770 2550/2515/3800 2551/2516/3801
+f 2529/2494/3768 2551/2516/3802 2552/2517/3803
+f 2528/2493/3764 2552/2517/3803 2553/2518/3804
+f 2527/2492/3763 2553/2518/3804 2554/2519/3805
+f 2526/2491/3760 2554/2519/3806 2555/2520/3807
+f 2525/2490/3761 2555/2520/3808 2556/2521/3809
+f 2524/2489/3756 2556/2521/3809 2557/2489/3810
+f 2523/2406/3753 2557/2489/3811 2558/2403/3812
+f 2522/2403/3754 2558/2403/3813 2559/2403/3814
+f 2416/2403/3601 2559/2403/3815 2414/2403/3815
+f 2414/2403/3600 2560/2403/3816 2412/2403/3597
+f 2559/2403/3814 2561/2403/3817 2560/2403/3818
+f 2558/2403/3812 2562/2522/3819 2561/2403/3820
+f 2557/2489/3810 2563/2523/3821 2562/2522/3822
+f 2556/2521/3809 2564/2524/3823 2563/2523/3824
+f 2555/2520/3808 2565/2525/3825 2564/2524/3823
+f 2554/2519/3805 2566/2526/3826 2565/2525/3825
+f 2553/2518/3804 2567/2527/3827 2566/2526/3826
+f 2552/2517/3803 2568/2528/3828 2567/2527/3827
+f 2551/2516/3802 2569/2529/3829 2568/2528/3830
+f 2550/2515/3831 2570/2530/3832 2569/2529/3829
+f 2549/2514/3799 2571/2531/3833 2570/2530/3832
+f 2548/2513/3796 2572/2532/3834 2571/2531/3833
+f 2547/2512/3795 2573/2533/3835 2572/2532/3834
+f 2546/2511/3793 2574/2534/3836 2573/2533/3835
+f 2545/2510/3791 2575/2535/3837 2574/2534/3836
+f 2544/2509/3790 2576/2536/3838 2575/2535/3837
+f 2543/2508/3788 2577/2537/3839 2576/2536/3840
+f 2541/2507/3786 2578/2538/3841 2577/2537/3842
+f 2577/2537/3843 2580/2539/3844 2579/2540/3845
+f 2581/2541/3846 2577/2537/3842 2579/2540/3845
+f 2581/2541/3846 2576/2536/3838 2577/2537/3842
+f 2582/2542/3847 2576/2536/3848 2581/2541/3846
+f 2582/2542/3849 2575/2535/3837 2576/2536/3848
+f 2583/2543/3850 2575/2535/3837 2582/2542/3847
+f 2583/2543/3851 2574/2534/3836 2575/2535/3837
+f 2584/2544/3852 2574/2534/3836 2583/2543/3851
+f 2584/2544/3852 2573/2533/3835 2574/2534/3836
+f 2585/2545/3853 2573/2533/3835 2584/2544/3852
+f 2585/2545/3853 2572/2532/3854 2573/2533/3835
+f 2586/2546/3855 2572/2532/3854 2585/2545/3853
+f 2586/2546/3855 2571/2531/3856 2572/2532/3834
+f 2570/2530/3857 2586/2546/3858 2587/2547/3859
+f 2569/2529/3829 2587/2547/3860 2588/2548/3861
+f 2568/2528/3830 2588/2548/3861 2589/2549/3862
+f 2567/2527/3827 2589/2549/3862 2590/2550/3863
+f 2591/2551/3864 2567/2527/3827 2590/2550/3863
+f 2591/2551/3864 2566/2526/3826 2567/2527/3827
+f 2592/2552/3865 2566/2526/3826 2591/2551/3864
+f 2592/2552/3865 2565/2525/3866 2566/2526/3826
+f 2593/2553/3867 2565/2525/3866 2592/2552/3865
+f 2593/2553/3867 2564/2524/3823 2565/2525/3868
+f 2594/2554/3869 2564/2524/3823 2593/2553/3867
+f 2594/2554/3869 2563/2523/3824 2564/2524/3823
+f 2562/2522/3819 2594/2554/3870 2595/2555/3871
+f 2561/2403/3817 2595/2555/3871 2596/2403/3872
+f 2560/2403/3873 2596/2403/3872 2597/2403/3874
+f 2412/2403/3597 2597/2403/3875 2410/2403/3595
+f 2410/2403/3876 2598/2403/3877 2408/2403/3593
+f 2597/2403/3874 2599/2403/3878 2598/2403/3877
+f 2596/2403/3872 2600/2556/3879 2599/2403/3880
+f 2595/2555/3871 2601/2557/3881 2600/2556/3879
+f 2601/2557/3881 2594/2554/3869 2593/2553/3867
+f 2601/2557/3881 2593/2553/3867 2602/2558/3882
+f 2602/2558/3883 2593/2553/3867 2592/2552/3865
+f 2602/2558/3882 2592/2552/3865 2603/2559/3884
+f 2603/2559/3884 2592/2552/3865 2591/2551/3864
+f 2603/2559/3884 2591/2551/3885 2604/2560/3886
+f 2604/2560/3887 2591/2551/3885 2590/2550/3863
+f 2604/2560/3886 2590/2550/3863 2605/2561/3888
+f 2590/2550/3889 2606/2562/3890 2605/2561/3888
+f 2589/2549/3891 2607/2563/3892 2606/2562/3890
+f 2588/2548/3861 2608/2564/3893 2607/2563/3892
+f 2587/2547/3860 2609/2565/3894 2608/2564/3893
+f 2609/2565/3895 2586/2546/3855 2585/2545/3853
+f 2609/2565/3895 2585/2545/3853 2610/2566/3896
+f 2610/2566/3897 2585/2545/3853 2584/2544/3852
+f 2610/2566/3897 2584/2544/3852 2611/2567/3898
+f 2611/2567/3898 2584/2544/3852 2583/2543/3851
+f 2611/2567/3898 2583/2543/3851 2612/2568/3899
+f 2612/2568/3899 2583/2543/3851 2582/2542/3847
+f 2612/2568/3899 2582/2542/3847 2613/2569/3900
+f 2613/2569/3900 2582/2542/3847 2581/2541/3846
+f 2613/2569/3900 2581/2541/3846 2614/2570/3901
+f 2614/2570/3902 2581/2541/3846 2579/2540/3845
+f 2614/2570/3901 2579/2540/3845 2615/2571/3903
+f 2579/2540/3845 2616/2572/3904 2615/2571/3903
+f 2615/2571/3905 2618/2573/3906 2617/2574/3907
+f 2619/2575/3908 2615/2571/3903 2617/2574/3907
+f 2619/2575/3909 2614/2570/3902 2615/2571/3903
+f 2620/2576/3910 2614/2570/3902 2619/2575/3908
+f 2620/2576/3910 2613/2569/3900 2614/2570/3902
+f 2621/2577/3911 2613/2569/3900 2620/2576/3910
+f 2621/2577/3911 2612/2568/3899 2613/2569/3900
+f 2622/2578/3912 2612/2568/3899 2621/2577/3911
+f 2622/2578/3913 2611/2567/3898 2612/2568/3899
+f 2623/2579/3914 2611/2567/3898 2622/2578/3913
+f 2623/2579/3914 2610/2566/3915 2611/2567/3898
+f 2624/2580/3916 2610/2566/3917 2623/2579/3914
+f 2624/2580/3916 2609/2565/3918 2610/2566/3917
+f 2608/2564/3893 2624/2580/3916 2625/2581/3919
+f 2607/2563/3892 2625/2581/3919 2626/2582/3920
+f 2606/2562/3890 2626/2582/3920 2627/2583/3921
+f 2605/2561/3888 2627/2583/3921 2628/2584/3922
+f 2629/2585/3923 2605/2561/3888 2628/2584/3922
+f 2629/2585/3924 2604/2560/3886 2605/2561/3925
+f 2630/2586/3926 2604/2560/3886 2629/2585/3927
+f 2630/2586/3926 2603/2559/3884 2604/2560/3886
+f 2631/2587/3928 2603/2559/3929 2630/2586/3930
+f 2631/2587/3928 2602/2558/3882 2603/2559/3931
+f 2601/2557/3881 2631/2587/3928 2632/2588/3932
+f 2600/2556/3933 2632/2588/3932 2633/2556/3934
+f 2599/2403/3880 2633/2556/3934 2634/2589/3935
+f 2598/2403/3877 2634/2589/3935 2635/2403/3936
+f 2408/2403/3593 2635/2403/3936 2405/2403/3937
+f 2405/2403/3590 2636/2589/3938 2406/2403/3591
+f 2635/2403/3936 2637/2589/3939 2636/2589/3938
+f 2634/2589/3935 2638/2590/3940 2637/2589/3939
+f 2633/2556/3934 2639/2591/3941 2638/2590/3940
+f 2632/2588/3932 2640/2592/3942 2639/2591/3941
+f 2631/2587/3928 2641/2593/3943 2640/2592/3944
+f 2630/2586/3926 2642/2594/3945 2641/2593/3943
+f 2629/2585/3923 2643/2595/3946 2642/2594/3947
+f 2628/2584/3922 2644/2596/3948 2643/2595/3946
+f 2627/2583/3921 2645/2597/3949 2644/2596/3948
+f 2626/2582/3950 2646/2598/3951 2645/2597/3949
+f 2625/2581/3919 2647/2599/3952 2646/2598/3953
+f 2624/2580/3916 2648/2600/3954 2647/2599/3952
+f 2623/2579/3914 2649/2601/3955 2648/2600/3954
+f 2622/2578/3913 2650/2602/3956 2649/2601/3955
+f 2621/2577/3911 2651/2603/3957 2650/2602/3956
+f 2620/2576/3910 2652/2604/3958 2651/2603/3957
+f 2619/2575/3909 2653/2605/3959 2652/2604/3958
+f 2617/2574/3960 840/2606/3961 2653/2605/3959
+f 840/2606/3961 2655/2607/3962 2653/2605/3959
+f 2654/2608/3963 2657/2609/3964 2655/2607/3962
+f 2656/2610/3965 2659/2611/3966 2657/2609/3964
+f 2658/2612/3967 2661/2613/3968 2659/2611/3969
+f 2660/2614/3970 2663/2615/3971 2661/2613/3972
+f 2662/2616/3973 2665/2617/3974 2663/2615/3971
+f 2664/2618/3975 2667/2619/3976 2665/2617/3974
+f 2666/2620/3977 2669/2621/3978 2667/2619/3976
+f 2668/2622/3979 2461/2435/3654 2669/2621/3978
+f 2669/2621/3978 2459/2433/3651 2670/2623/3980
+f 2670/2623/3980 2459/2433/3651 2457/2431/3653
+f 2670/2623/3980 2457/2431/3653 2671/2624/3981
+f 2671/2624/3981 2457/2431/3653 2455/2429/3646
+f 2671/2624/3981 2455/2429/3646 2672/2625/3982
+f 2672/2625/3983 2455/2429/3984 2453/2427/3644
+f 2672/2625/3985 2453/2427/3644 2673/2626/3986
+f 2673/2626/3986 2453/2427/3648 2451/2425/3642
+f 2673/2626/3986 2451/2425/3642 2674/2627/3987
+f 2674/2627/3988 2449/2423/3640 2675/2628/3989
+f 2675/2628/3989 2447/2421/3638 2676/2629/3990
+f 2676/2629/3991 2445/2419/3992 2677/2630/3993
+f 2677/2630/3993 2443/2417/3637 2678/2631/3994
+f 2678/2631/3994 2441/2415/3995 2679/2632/3996
+f 2679/2632/3996 2439/2413/3631 2680/2633/3997
+f 2680/2633/3997 2439/2413/3631 2437/2411/3623
+f 2680/2633/3997 2437/2411/3628 2681/2634/3998
+f 2681/2634/3999 2435/2409/3625 2682/2635/4000
+f 2682/2635/4001 2433/2407/4002 2683/2636/4003
+f 2683/2636/4003 2431/2405/3616 2684/2637/4004
+f 2684/2637/4004 2429/2404/3618 2685/2404/4005
+f 2685/2404/4005 2427/2403/3612 2686/2403/4006
+f 2686/2403/4006 2425/2403/3611 2687/2403/4007
+f 2425/2403/3611 2688/2403/4008 2687/2403/4009
+f 2687/2403/4009 2690/2403/4010 2689/2403/4011
+f 2691/2403/4012 2687/2403/4009 2689/2403/4011
+f 2692/2404/4013 2686/2403/4006 2691/2403/4012
+f 2693/2404/4014 2685/2404/4005 2692/2404/4013
+f 2694/2638/4015 2684/2637/4004 2693/2404/4014
+f 2695/2639/4016 2683/2636/4003 2694/2638/4015
+f 2696/2640/4017 2682/2635/4000 2695/2639/4018
+f 2696/2640/4017 2681/2634/3998 2682/2635/4001
+f 2697/2641/4019 2681/2634/3998 2696/2640/4017
+f 2697/2641/4020 2680/2633/3997 2681/2634/3999
+f 2698/2642/4021 2680/2633/3997 2697/2641/4019
+f 2698/2642/4022 2679/2632/3996 2680/2633/3997
+f 2699/2643/4023 2679/2632/3996 2698/2642/4024
+f 2700/2644/4025 2678/2631/3994 2699/2643/4026
+f 2701/2645/4027 2677/2630/3993 2700/2644/4028
+f 2702/2646/4029 2676/2629/4030 2701/2645/4027
+f 2703/2647/4031 2675/2628/3989 2702/2646/4032
+f 2703/2647/4031 2674/2627/3988 2675/2628/3989
+f 2704/2648/4033 2674/2627/3988 2703/2647/4034
+f 2704/2648/4033 2673/2626/3986 2674/2627/3988
+f 2705/2649/4035 2673/2626/3986 2704/2648/4036
+f 2705/2649/4037 2672/2625/3985 2673/2626/3986
+f 2706/2650/4038 2672/2625/3982 2705/2649/4039
+f 2706/2650/4038 2671/2624/3981 2672/2625/3985
+f 2707/2651/4040 2671/2624/3981 2706/2650/4038
+f 2707/2651/4041 2670/2623/3980 2671/2624/3981
+f 2667/2619/3976 2670/2623/3980 2707/2651/4040
+f 2667/2619/3976 2669/2621/3978 2670/2623/3980
+f 2665/2617/3974 2667/2619/3976 2707/2651/4040
+f 2665/2617/3974 2707/2651/4040 2708/2652/4042
+f 2708/2652/4042 2707/2651/4040 2706/2650/4038
+f 2708/2652/4042 2706/2650/4038 2709/2653/4043
+f 2709/2653/4043 2706/2650/4038 2705/2649/4037
+f 2709/2653/4043 2705/2649/4037 2710/2654/4044
+f 2710/2654/4045 2705/2649/4035 2704/2648/4036
+f 2710/2654/4044 2704/2648/4036 2711/2655/4046
+f 2711/2655/4046 2704/2648/4036 2703/2647/4034
+f 2711/2655/4046 2703/2647/4031 2712/2656/4047
+f 2712/2656/4047 2702/2646/4029 2713/2657/4048
+f 2713/2657/4048 2701/2645/4027 2714/2658/4049
+f 2714/2658/4050 2700/2644/4028 2715/2659/4051
+f 2715/2659/4052 2699/2643/4026 2716/2660/4053
+f 2716/2660/4054 2698/2642/4022 2717/2661/4055
+f 2717/2661/4056 2697/2641/4019 2718/2662/4057
+f 2718/2662/4057 2697/2641/4019 2696/2640/4017
+f 2718/2662/4057 2696/2640/4017 2719/2663/4058
+f 2719/2663/4059 2696/2640/4017 2695/2639/4018
+f 2719/2663/4058 2695/2639/4018 2720/2664/4060
+f 2720/2664/4061 2694/2638/4062 2721/2665/4063
+f 2721/2665/4064 2693/2404/4065 2722/2666/4066
+f 2722/2666/4066 2692/2404/4067 2723/2666/4068
+f 2723/2666/4068 2691/2403/4012 2724/2403/4069
+f 2724/2403/4070 2689/2403/4071 2725/2403/4072
+f 2689/2403/4011 2726/2403/4073 2725/2403/4074
+f 2725/2403/4074 2728/2403/4075 2727/2403/4075
+f 2729/2589/4076 2725/2403/4074 2727/2403/4076
+f 2730/2666/4077 2724/2403/4069 2729/2589/4078
+f 2731/2666/4079 2723/2666/4068 2730/2666/4080
+f 2732/2667/4081 2722/2666/4066 2731/2666/4082
+f 2733/2668/4083 2721/2665/4063 2732/2667/4081
+f 2734/2669/4084 2720/2664/4061 2733/2668/4085
+f 2735/2670/4086 2719/2663/4059 2734/2669/4087
+f 2736/2671/4088 2718/2662/4057 2735/2670/4086
+f 2737/2672/4089 2717/2661/4055 2736/2671/4088
+f 2738/2673/4090 2716/2660/4053 2737/2672/4091
+f 2739/2674/4092 2715/2659/4093 2738/2673/4090
+f 2740/2675/4094 2714/2658/4049 2739/2674/4095
+f 2741/2676/4096 2713/2657/4048 2740/2675/4094
+f 2742/2677/4097 2712/2656/4047 2741/2676/4096
+f 2743/2678/4098 2711/2655/4046 2742/2677/4097
+f 2744/2679/4099 2710/2654/4045 2743/2678/4098
+f 2745/2680/4100 2709/2653/4043 2744/2679/4101
+f 2663/2615/3971 2708/2652/4042 2745/2680/4102
+f 2661/2613/3972 2745/2680/4100 2746/2681/4103
+f 2746/2681/4103 2744/2679/4099 2747/2682/4104
+f 2747/2682/4105 2743/2678/4098 2748/2683/4106
+f 2748/2683/4106 2742/2677/4097 2749/2684/4107
+f 2749/2684/4107 2741/2676/4096 2750/2685/4108
+f 2750/2685/4108 2740/2675/4094 2751/2686/4109
+f 2751/2686/4110 2739/2674/4095 2752/2687/4111
+f 2752/2687/4111 2738/2673/4112 2753/2688/4113
+f 2753/2688/4113 2737/2672/4089 2754/2689/4114
+f 2754/2689/4115 2736/2671/4088 2755/2690/4116
+f 2755/2690/4117 2735/2670/4086 2756/2691/4118
+f 2756/2691/4118 2734/2669/4087 2757/2692/4119
+f 2757/2692/4119 2733/2668/4083 2758/2693/4120
+f 2758/2693/4121 2732/2667/4081 2759/2694/4122
+f 2759/2694/4123 2731/2666/4082 2760/2695/4124
+f 2760/2695/4125 2730/2666/4080 2761/2589/4126
+f 2761/2589/4127 2729/2589/4078 2762/2403/4128
+f 2762/2403/4129 2727/2403/4075 2763/2403/4130
+f 2727/2403/4075 2764/2403/4131 2763/2403/4130
+f 2763/2403/4130 2766/2403/4132 2765/2403/4133
+f 2767/2589/4134 2763/2403/4130 2765/2403/4135
+f 2768/2589/4136 2762/2403/4137 2767/2589/4138
+f 2769/2695/4139 2761/2589/4127 2768/2589/4136
+f 2770/2696/4140 2760/2695/4125 2769/2695/4139
+f 2771/2697/4141 2759/2694/4122 2770/2696/4142
+f 2771/2697/4143 2758/2693/4144 2759/2694/4145
+f 2772/2698/4146 2758/2693/4144 2771/2697/4141
+f 2772/2698/4147 2757/2692/4119 2758/2693/4120
+f 2773/2699/4148 2757/2692/4119 2772/2698/4147
+f 2773/2699/4148 2756/2691/4118 2757/2692/4119
+f 2774/2700/4149 2756/2691/4118 2773/2699/4150
+f 2774/2700/4149 2755/2690/4151 2756/2691/4118
+f 2775/2701/4152 2755/2690/4116 2774/2700/4153
+f 2776/2702/4154 2754/2689/4114 2775/2701/4152
+f 2777/2703/4155 2753/2688/4113 2776/2702/4154
+f 2778/2704/4156 2752/2687/4157 2777/2703/4158
+f 2779/2705/4159 2751/2686/4109 2778/2704/4160
+f 2779/2705/4159 2750/2685/4108 2751/2686/4109
+f 2780/2706/4161 2750/2685/4108 2779/2705/4159
+f 2780/2706/4161 2749/2684/4107 2750/2685/4108
+f 2781/2707/4162 2749/2684/4107 2780/2706/4161
+f 2781/2707/4162 2748/2683/4106 2749/2684/4107
+f 2782/2708/4163 2748/2683/4106 2781/2707/4162
+f 2782/2708/4163 2747/2682/4105 2748/2683/4106
+f 2783/2709/4164 2747/2682/4105 2782/2708/4163
+f 2783/2709/4164 2746/2681/4103 2747/2682/4105
+f 2659/2611/3969 2746/2681/4103 2783/2709/4164
+f 2659/2611/3969 2661/2613/3968 2746/2681/4103
+f 2657/2609/4165 2659/2611/4166 2783/2709/4164
+f 2657/2609/4165 2783/2709/4164 2784/2710/4167
+f 2784/2710/4167 2783/2709/4164 2782/2708/4163
+f 2784/2710/4167 2782/2708/4168 2785/2711/4169
+f 2785/2711/4169 2782/2708/4168 2781/2707/4162
+f 2785/2711/4169 2781/2707/4162 2786/2712/4170
+f 2786/2712/4171 2781/2707/4162 2780/2706/4161
+f 2786/2712/4170 2780/2706/4172 2787/2713/4173
+f 2787/2713/4173 2780/2706/4161 2779/2705/4159
+f 2787/2713/4174 2779/2705/4159 2788/2714/4175
+f 2788/2714/4175 2779/2705/4159 2778/2704/4156
+f 2788/2714/4175 2778/2704/4156 2789/2715/4176
+f 2789/2715/4177 2777/2703/4155 2790/2716/4178
+f 2790/2716/4178 2776/2702/4154 2791/2717/4179
+f 2791/2717/4179 2775/2701/4180 2792/2718/4181
+f 2792/2718/4181 2774/2700/4153 2793/2719/4182
+f 2793/2719/4182 2774/2700/4149 2773/2699/4150
+f 2793/2719/4182 2773/2699/4150 2794/2720/4183
+f 2794/2720/4183 2773/2699/4184 2772/2698/4147
+f 2794/2720/4183 2772/2698/4147 2795/2721/4185
+f 2795/2721/4186 2772/2698/4147 2771/2697/4187
+f 2795/2721/4186 2771/2697/4143 2796/2722/4188
+f 2796/2722/4189 2771/2697/4187 2770/2696/4142
+f 2796/2722/4189 2770/2696/4140 2797/2723/4190
+f 2797/2723/4190 2769/2695/4139 2798/2695/4191
+f 2798/2695/4191 2768/2589/4136 2799/2589/4192
+f 2799/2589/4193 2767/2589/4138 2800/2589/4194
+f 2800/2589/4194 2765/2403/4195 2801/2403/4196
+f 2765/2403/4135 2802/2403/4197 2801/2403/4196
+f 2801/2403/4196 2804/2403/4198 2803/2403/4199
+f 2805/2589/4200 2801/2403/4196 2803/2403/4201
+f 2806/2589/4202 2800/2589/4194 2805/2589/4200
+f 2807/2590/4203 2799/2589/4192 2806/2589/4202
+f 2808/2724/4204 2798/2695/4205 2807/2590/4203
+f 2809/2725/4206 2797/2723/4190 2808/2724/4204
+f 2810/2726/4207 2796/2722/4189 2809/2725/4206
+f 2810/2726/4207 2795/2721/4185 2796/2722/4189
+f 2811/2727/4208 2795/2721/4185 2810/2726/4209
+f 2811/2727/4208 2794/2720/4183 2795/2721/4186
+f 2812/2728/4210 2794/2720/4183 2811/2727/4208
+f 2812/2728/4210 2793/2719/4182 2794/2720/4183
+f 2813/2729/4211 2793/2719/4182 2812/2728/4210
+f 2814/2730/4212 2792/2718/4181 2813/2729/4211
+f 2815/2731/4213 2791/2717/4179 2814/2730/4212
+f 2816/2732/4214 2790/2716/4178 2815/2731/4213
+f 2817/2733/4215 2789/2715/4177 2816/2732/4216
+f 2817/2733/4215 2788/2714/4175 2789/2715/4176
+f 2818/2734/4217 2788/2714/4175 2817/2733/4215
+f 2818/2734/4217 2787/2713/4173 2788/2714/4175
+f 2819/2735/4218 2787/2713/4219 2818/2734/4217
+f 2819/2735/4218 2786/2712/4170 2787/2713/4174
+f 2820/2736/4220 2786/2712/4170 2819/2735/4218
+f 2820/2736/4221 2785/2711/4169 2786/2712/4170
+f 2821/2737/4222 2785/2711/4169 2820/2736/4221
+f 2821/2737/4222 2784/2710/4167 2785/2711/4169
+f 2655/2607/3962 2784/2710/4167 2821/2737/4222
+f 2655/2607/3962 2657/2609/3964 2784/2710/4167
+f 2653/2605/3959 2821/2737/4223 2652/2604/3958
+f 2652/2604/3958 2820/2736/4221 2651/2603/3957
+f 2651/2603/3957 2819/2735/4218 2650/2602/3956
+f 2650/2602/3956 2818/2734/4224 2649/2601/3955
+f 2649/2601/3955 2817/2733/4215 2648/2600/3954
+f 2648/2600/3954 2816/2732/4216 2647/2599/3952
+f 2647/2599/3952 2815/2731/4213 2646/2598/3953
+f 2646/2598/3953 2814/2730/4225 2645/2597/3949
+f 2645/2597/3949 2813/2729/4211 2644/2596/3948
+f 2644/2596/3948 2812/2728/4226 2643/2595/3946
+f 2643/2595/3946 2811/2727/4208 2642/2594/3947
+f 2642/2594/3945 2810/2726/4207 2641/2593/3943
+f 2641/2593/3943 2809/2725/4206 2640/2592/3944
+f 2640/2592/3942 2808/2724/4204 2639/2591/3941
+f 2639/2591/3941 2807/2590/4203 2638/2590/3940
+f 2638/2590/3940 2806/2589/4202 2637/2589/3939
+f 2637/2589/3939 2805/2589/4200 2636/2589/3938
+f 2636/2589/3938 2803/2403/4199 2406/2403/3591
+f 2803/2403/4199 2403/2403/3589 2406/2403/3591
+f 2822/2738/4227 2823/2739/4228 2824/2740/4229
+f 2823/2739/4230 2825/2741/4231 2824/2740/4229
+f 2823/2739/4228 2826/2742/4232 2825/2741/4231
+f 2826/2742/4233 2827/2743/4234 2825/2741/4231
+f 2826/2742/4232 2828/2744/4235 2827/2743/4234
+f 2828/2744/4235 2829/2745/4236 2827/2743/4234
+f 2828/2744/4235 2830/2746/4237 2829/2745/4236
+f 2830/2746/4237 2831/2747/4238 2829/2745/4236
+f 2830/2746/4237 2832/2748/4239 2831/2747/4238
+f 2832/2748/4239 2834/2749/4240 2831/2747/4238
+f 2833/2750/4241 2835/2751/4242 2834/2749/4240
+f 2833/2750/4241 2836/2752/4243 2835/2751/4242
+f 2836/2752/4243 2837/2753/4244 2835/2751/4242
+f 2836/2752/4243 2838/2754/4245 2837/2753/4244
+f 2838/2754/4245 2839/2755/4246 2837/2753/4244
+f 2838/2754/4245 2840/2756/4247 2839/2755/4246
+f 2840/2756/4247 2842/2757/4248 2839/2755/4246
+f 2842/2757/4248 2844/2758/4249 2843/2759/4250
+f 2846/2760/4251 2843/2759/4252 2845/2761/4253
+f 2848/2762/4254 2845/2761/4253 2847/2763/4255
+f 2850/2764/4256 2847/2763/4255 2849/2765/4257
+f 2852/2766/4258 2849/2765/4257 2851/2767/4259
+f 2853/2768/4260 2852/2766/4261 2851/2767/4259
+f 2853/2768/4260 2854/2769/4262 2852/2766/4263
+f 2855/2770/4264 2854/2769/4262 2853/2768/4265
+f 2855/2770/4264 2856/2771/4266 2854/2769/4262
+f 2857/2772/4267 2856/2771/4266 2855/2770/4264
+f 2857/2772/4267 2858/2773/4268 2856/2771/4266
+f 2859/2774/4269 2858/2773/4268 2857/2772/4267
+f 2859/2774/4269 2860/2775/4270 2858/2773/4268
+f 2861/2776/4271 2860/2775/4270 2859/2774/4269
+f 2861/2776/4272 2862/2777/4273 2860/2775/4274
+f 2863/2778/4275 2862/2777/4273 2861/2776/4272
+f 2865/2779/4276 2864/2780/4277 2863/2778/4275
+f 2867/2781/4278 2866/2782/4279 2865/2779/4276
+f 2869/2783/4280 2868/2784/4281 2867/2781/4278
+f 2871/2785/4282 2870/2786/4283 2869/2783/4284
+f 2874/2787/4285 2871/2785/4286 2873/2788/4287
+f 2876/2789/4288 2873/2788/4289 2875/2790/4290
+f 2878/2791/4291 2875/2790/4290 2877/2792/4292
+f 2880/2793/4293 2877/2792/4292 2879/2794/4294
+f 2882/2795/4295 2879/2794/4294 2881/2796/4296
+f 2883/2797/4297 2882/2795/4295 2884/2798/4298
+f 2885/2799/4299 2880/2793/4293 2883/2797/4297
+f 2886/2800/4300 2878/2791/4291 2885/2799/4301
+f 2887/2801/4302 2876/2789/4288 2886/2800/4300
+f 2888/2802/4303 2874/2787/4285 2887/2801/4304
+f 2870/2786/4305 2888/2802/4306 2889/2803/4307
+f 2868/2784/4281 2889/2803/4308 2890/2804/4309
+f 2866/2782/4279 2890/2804/4309 2891/2805/4310
+f 2864/2780/4277 2891/2805/4310 2892/2806/4311
+f 2862/2777/4273 2892/2806/4311 2893/2807/4312
+f 2894/2808/4313 2862/2777/4273 2893/2807/4312
+f 2894/2808/4313 2860/2775/4270 2862/2777/4273
+f 2895/2809/4314 2860/2775/4270 2894/2808/4313
+f 2895/2809/4314 2858/2773/4268 2860/2775/4270
+f 2896/2810/4315 2858/2773/4268 2895/2809/4314
+f 2896/2810/4315 2856/2771/4266 2858/2773/4268
+f 2897/2811/4316 2856/2771/4266 2896/2810/4315
+f 2897/2811/4317 2854/2769/4262 2856/2771/4266
+f 2898/2812/4318 2854/2769/4262 2897/2811/4316
+f 2898/2812/4318 2852/2766/4263 2854/2769/4262
+f 2899/2813/4319 2852/2766/4258 2898/2812/4318
+f 2900/2814/4320 2850/2764/4256 2899/2813/4321
+f 2901/2815/4322 2848/2762/4254 2900/2814/4320
+f 2839/2755/4246 2846/2760/4251 2901/2815/4322
+f 2837/2753/4244 2901/2815/4322 2902/2816/4323
+f 2902/2816/4323 2900/2814/4320 2903/2817/4324
+f 2903/2817/4324 2900/2814/4320 2899/2813/4319
+f 2903/2817/4324 2899/2813/4321 2904/2818/4325
+f 2904/2818/4326 2899/2813/4319 2898/2812/4318
+f 2904/2818/4327 2898/2812/4318 2905/2819/4328
+f 2905/2819/4328 2898/2812/4318 2897/2811/4316
+f 2905/2819/4328 2897/2811/4317 2906/2820/4329
+f 2906/2820/4329 2897/2811/4316 2896/2810/4315
+f 2906/2820/4329 2896/2810/4315 2907/2821/4330
+f 2907/2821/4330 2896/2810/4315 2895/2809/4314
+f 2907/2821/4330 2895/2809/4314 2908/2822/4331
+f 2908/2822/4331 2895/2809/4314 2894/2808/4313
+f 2908/2822/4331 2894/2808/4313 2909/2823/4332
+f 2909/2823/4332 2894/2808/4313 2893/2807/4312
+f 2909/2823/4332 2893/2807/4312 2910/2824/4333
+f 2893/2807/4334 2911/2825/4335 2910/2824/4333
+f 2892/2806/4311 2912/2826/4336 2911/2825/4337
+f 2891/2805/4310 2913/2827/4338 2912/2826/4336
+f 2913/2827/4338 2890/2804/4339 2889/2803/4308
+f 2913/2827/4338 2889/2803/4340 2914/2828/4341
+f 2889/2803/4308 2915/2829/4342 2914/2828/4341
+f 2915/2829/4342 2887/2801/4304 2916/2830/4343
+f 2916/2830/4343 2886/2800/4344 2917/2831/4345
+f 2917/2831/4345 2885/2799/4301 2918/2832/4346
+f 2918/2832/4347 2883/2797/4297 2919/2833/4348
+f 2919/2833/4348 2884/2798/4349 2920/2834/4350
+f 2921/2835/4351 2920/2834/4350 2922/2836/4352
+f 2923/2837/4353 2919/2833/4348 2921/2835/4354
+f 2924/2838/4355 2918/2832/4346 2923/2837/4353
+f 2925/2839/4356 2917/2831/4345 2924/2838/4357
+f 2926/2840/4358 2916/2830/4343 2925/2839/4356
+f 2914/2828/4341 2926/2840/4358 2927/2841/4359
+f 2928/2842/4360 2914/2828/4341 2927/2841/4359
+f 2928/2842/4360 2913/2827/4338 2914/2828/4361
+f 2929/2843/4362 2913/2827/4338 2928/2842/4360
+f 2929/2843/4362 2912/2826/4336 2913/2827/4338
+f 2911/2825/4335 2929/2843/4362 2930/2844/4363
+f 2910/2824/4364 2930/2844/4363 2931/2845/4365
+f 2932/2846/4366 2910/2824/4364 2931/2845/4365
+f 2932/2846/4366 2909/2823/4332 2910/2824/4367
+f 2933/2847/4368 2909/2823/4332 2932/2846/4369
+f 2933/2847/4370 2908/2822/4331 2909/2823/4332
+f 2934/2848/4371 2908/2822/4331 2933/2847/4372
+f 2934/2848/4371 2907/2821/4330 2908/2822/4331
+f 2935/2849/4373 2907/2821/4330 2934/2848/4371
+f 2935/2849/4374 2906/2820/4329 2907/2821/4330
+f 2936/2850/4375 2906/2820/4329 2935/2849/4373
+f 2936/2850/4375 2905/2819/4328 2906/2820/4376
+f 2937/2851/4377 2905/2819/4328 2936/2850/4375
+f 2937/2851/4377 2904/2818/4325 2905/2819/4328
+f 2938/2852/4378 2904/2818/4327 2937/2851/4379
+f 2938/2852/4378 2903/2817/4324 2904/2818/4327
+f 2939/2853/4380 2903/2817/4324 2938/2852/4378
+f 2939/2853/4380 2902/2816/4323 2903/2817/4381
+f 2835/2751/4382 2902/2816/4383 2939/2853/4380
+f 2834/2749/4240 2939/2853/4380 2940/2854/4384
+f 2940/2854/4384 2939/2853/4380 2938/2852/4378
+f 2940/2854/4385 2938/2852/4378 2941/2855/4386
+f 2941/2855/4386 2938/2852/4378 2937/2851/4377
+f 2941/2855/4386 2937/2851/4377 2942/2856/4387
+f 2942/2856/4387 2937/2851/4379 2936/2850/4375
+f 2942/2856/4387 2936/2850/4375 2943/2857/4388
+f 2943/2857/4389 2936/2850/4375 2935/2849/4374
+f 2943/2857/4388 2935/2849/4373 2944/2858/4390
+f 2944/2858/4390 2935/2849/4373 2934/2848/4371
+f 2944/2858/4390 2934/2848/4371 2945/2859/4391
+f 2945/2859/4392 2934/2848/4371 2933/2847/4372
+f 2945/2859/4393 2933/2847/4370 2946/2860/4394
+f 2946/2860/4395 2933/2847/4368 2932/2846/4366
+f 2946/2860/4395 2932/2846/4396 2947/2861/4397
+f 2947/2861/4398 2932/2846/4366 2931/2845/4365
+f 2947/2861/4397 2931/2845/4365 2948/2862/4399
+f 2931/2845/4365 2949/2863/4400 2948/2862/4399
+f 2930/2844/4401 2950/2864/4402 2949/2863/4400
+f 2929/2843/4362 2951/2865/4403 2950/2864/4402
+f 2928/2842/4360 2952/2866/4404 2951/2865/4403
+f 2927/2841/4405 2953/2867/4406 2952/2866/4404
+f 2953/2867/4406 2925/2839/4356 2954/2868/4407
+f 2954/2868/4407 2924/2838/4357 2955/2869/4408
+f 2955/2869/4409 2923/2837/4353 2956/2870/4410
+f 2956/2870/4410 2921/2835/4351 2957/2871/4411
+f 2957/2871/4412 2922/2836/4352 2958/2872/4413
+f 2959/2873/4414 2958/2872/4415 2960/2874/4416
+f 2961/2875/4417 2957/2871/4411 2959/2873/4414
+f 2962/2876/4418 2956/2870/4410 2961/2875/4419
+f 2963/2877/4420 2955/2869/4408 2962/2876/4421
+f 2964/2878/4422 2954/2868/4407 2963/2877/4423
+f 2952/2866/4424 2964/2878/4422 2965/2879/4425
+f 2951/2865/4426 2965/2879/4425 2966/2880/4427
+f 2950/2864/4402 2966/2880/4427 2967/2881/4428
+f 2949/2863/4400 2967/2881/4428 2968/2882/4429
+f 2948/2862/4430 2968/2882/4429 2969/2883/4431
+f 2970/2884/4432 2948/2862/4430 2969/2883/4433
+f 2970/2884/4432 2947/2861/4397 2948/2862/4430
+f 2971/2885/4434 2947/2861/4397 2970/2884/4432
+f 2971/2885/4434 2946/2860/4395 2947/2861/4397
+f 2972/2886/4435 2946/2860/4394 2971/2885/4436
+f 2972/2886/4435 2945/2859/4437 2946/2860/4395
+f 2973/2887/4438 2945/2859/4393 2972/2886/4435
+f 2973/2887/4438 2944/2858/4390 2945/2859/4393
+f 2974/2888/4439 2944/2858/4390 2973/2887/4440
+f 2974/2888/4439 2943/2857/4388 2944/2858/4390
+f 2975/2889/4441 2943/2857/4388 2974/2888/4439
+f 2975/2889/4441 2942/2856/4387 2943/2857/4388
+f 2976/2890/4442 2942/2856/4387 2975/2889/4443
+f 2976/2890/4442 2941/2855/4444 2942/2856/4387
+f 2977/2891/4445 2941/2855/4386 2976/2890/4442
+f 2977/2891/4445 2940/2854/4384 2941/2855/4386
+f 2831/2747/4238 2940/2854/4385 2977/2891/4445
+f 2831/2747/4238 2834/2749/4240 2940/2854/4384
+f 2829/2745/4236 2831/2747/4238 2977/2891/4445
+f 2829/2745/4236 2977/2891/4445 2978/2892/4446
+f 2978/2892/4447 2977/2891/4445 2976/2890/4442
+f 2978/2892/4446 2976/2890/4448 2979/2893/4449
+f 2979/2893/4449 2976/2890/4442 2975/2889/4443
+f 2979/2893/4449 2975/2889/4443 2980/2894/4450
+f 2980/2894/4450 2975/2889/4443 2974/2888/4451
+f 2980/2894/4450 2974/2888/4439 2981/2895/4452
+f 2981/2895/4453 2974/2888/4439 2973/2887/4438
+f 2981/2895/4452 2973/2887/4438 2982/2896/4454
+f 2982/2896/4454 2973/2887/4438 2972/2886/4435
+f 2982/2896/4455 2972/2886/4435 2983/2897/4456
+f 2972/2886/4435 2984/2898/4457 2983/2897/4456
+f 2971/2885/4436 2985/2899/4458 2984/2898/4459
+f 2985/2899/4458 2970/2884/4432 2969/2883/4433
+f 2985/2899/4460 2969/2883/4431 2986/2900/4461
+f 2969/2883/4431 2987/2901/4462 2986/2900/4463
+f 2968/2882/4429 2988/2902/4464 2987/2901/4462
+f 2967/2881/4428 2989/2903/4465 2988/2902/4464
+f 2966/2880/4427 2990/2904/4466 2989/2903/4467
+f 2965/2879/4468 2991/2905/4469 2990/2904/4470
+f 2991/2905/4471 2963/2877/4420 2992/2906/4472
+f 2992/2906/4473 2962/2876/4418 2993/2907/4474
+f 2993/2907/4474 2961/2875/4417 2994/2908/4475
+f 2994/2908/4475 2959/2873/4414 2995/2909/4476
+f 2995/2909/4476 2960/2874/4416 2996/2910/4477
+f 2997/2911/4478 2996/2910/4477 2998/2912/4479
+f 2999/2913/4480 2995/2909/4481 2997/2911/4478
+f 3000/2914/4482 2994/2908/4475 2999/2913/4480
+f 3001/2915/4483 2993/2907/4474 3000/2914/4482
+f 2991/2905/4471 3001/2915/4484 3002/2916/4485
+f 2990/2904/4470 3002/2916/4486 3003/2917/4487
+f 2989/2903/4465 3003/2917/4488 3004/2918/4489
+f 2988/2902/4490 3004/2918/4489 3005/2919/4491
+f 2987/2901/4492 3005/2919/4491 3006/2920/4493
+f 2986/2900/4463 3006/2920/4494 3007/2921/4495
+f 3008/2922/4496 2986/2900/4463 3007/2921/4497
+f 3008/2922/4496 2985/2899/4458 2986/2900/4463
+f 3009/2923/4498 2985/2899/4458 3008/2922/4496
+f 3009/2923/4499 2984/2898/4457 2985/2899/4458
+f 3010/2924/4500 2984/2898/4459 3009/2923/4501
+f 3010/2924/4500 2983/2897/4456 2984/2898/4457
+f 3011/2925/4502 2983/2897/4456 3010/2924/4500
+f 3011/2925/4502 2982/2896/4455 2983/2897/4456
+f 3012/2926/4503 2982/2896/4455 3011/2925/4502
+f 3013/2927/4504 2981/2895/4452 3012/2926/4503
+f 3014/2928/4505 2980/2894/4450 3013/2927/4504
+f 3015/2929/4506 2979/2893/4507 3014/2928/4508
+f 3015/2929/4506 2978/2892/4446 2979/2893/4507
+f 2827/2743/4234 2978/2892/4447 3015/2929/4506
+f 2827/2743/4234 2829/2745/4236 2978/2892/4447
+f 2825/2741/4231 2827/2743/4234 3015/2929/4506
+f 2825/2741/4231 3015/2929/4509 3016/2930/4510
+f 3016/2930/4510 3015/2929/4511 3014/2928/4508
+f 3016/2930/4510 3014/2928/4508 3017/2931/4512
+f 3017/2931/4512 3013/2927/4504 3018/2932/4513
+f 3018/2932/4513 3013/2927/4504 3012/2926/4503
+f 3018/2932/4513 3012/2926/4503 3019/2933/4514
+f 3019/2933/4514 3012/2926/4503 3011/2925/4502
+f 3019/2933/4514 3011/2925/4502 3020/2934/4515
+f 3020/2934/4515 3011/2925/4502 3010/2924/4500
+f 3020/2934/4515 3010/2924/4500 3021/2935/4516
+f 3021/2935/4516 3010/2924/4500 3009/2923/4498
+f 3021/2935/4516 3009/2923/4499 3022/2936/4517
+f 3022/2936/4517 3009/2923/4498 3008/2922/4496
+f 3022/2936/4517 3008/2922/4496 3023/2937/4518
+f 3023/2937/4518 3008/2922/4496 3007/2921/4497
+f 3023/2937/4518 3007/2921/4497 3024/2938/4519
+f 3024/2938/4519 3007/2921/4495 3006/2920/4494
+f 3024/2938/4519 3006/2920/4494 3025/2939/4520
+f 3006/2920/4493 3026/2940/4521 3025/2939/4520
+f 3005/2919/4491 3027/2941/4522 3026/2940/4521
+f 3004/2918/4489 3028/2942/4523 3027/2941/4524
+f 3003/2917/4525 3029/2943/4526 3028/2942/4523
+f 3002/2916/4486 3030/2944/4527 3029/2943/4526
+f 3030/2944/4528 3000/2914/4482 3031/2945/4529
+f 3031/2945/4529 2999/2913/4480 3032/2946/4530
+f 3032/2946/4531 2997/2911/4478 3033/2947/4532
+f 3033/2947/4532 2997/2911/4478 2998/2912/4479
+f 3033/2947/4532 2998/2912/4479 3034/2948/4533
+f 3035/2949/4534 3034/2948/4533 3036/2950/4535
+f 3037/2951/4536 3033/2947/4537 3035/2949/4538
+f 3038/2952/4539 3032/2946/4531 3037/2951/4536
+f 3039/2953/4540 3031/2945/4529 3038/2952/4541
+f 3029/2943/4526 3039/2953/4542 3040/2954/4543
+f 3028/2942/4523 3040/2954/4543 3041/2955/4544
+f 3027/2941/4522 3041/2955/4544 3042/2956/4545
+f 3026/2940/4546 3042/2956/4545 3043/2957/4547
+f 3025/2939/4548 3043/2957/4549 3044/2958/4550
+f 3024/2938/4519 3044/2958/4550 3045/2959/4551
+f 3046/2960/4552 3024/2938/4519 3045/2959/4553
+f 3046/2960/4554 3023/2937/4518 3024/2938/4519
+f 3047/2961/4555 3023/2937/4518 3046/2960/4554
+f 3047/2961/4555 3022/2936/4517 3023/2937/4518
+f 3048/2962/4556 3022/2936/4517 3047/2961/4555
+f 3048/2962/4557 3021/2935/4516 3022/2936/4517
+f 3049/2963/4558 3021/2935/4516 3048/2962/4557
+f 3049/2963/4558 3020/2934/4515 3021/2935/4516
+f 3050/2964/4559 3020/2934/4515 3049/2963/4558
+f 3050/2964/4559 3019/2933/4514 3020/2934/4515
+f 3051/2965/4560 3019/2933/4514 3050/2964/4559
+f 3051/2965/4560 3018/2932/4513 3019/2933/4514
+f 3052/2966/4561 3018/2932/4513 3051/2965/4560
+f 3053/2967/4562 3017/2931/4563 3052/2966/4561
+f 2824/2740/4229 3016/2930/4510 3053/2967/4562
+f 2824/2740/4229 2825/2741/4231 3016/2930/4510
+f 3054/2968/4564 3053/2967/4565 3055/2969/4566
+f 2822/2738/4227 2824/2740/4229 3054/2968/4564
+f 3054/2968/4564 3056/2970/4567 2822/2738/4227
+f 3054/2968/4564 3057/2971/4568 3056/2970/4567
+f 3058/2972/4569 3054/2968/4564 3055/2969/4566
+f 3060/2973/4570 3055/2969/4566 3059/2974/4571
+f 3062/2975/4572 3059/2974/4573 3061/2976/4574
+f 3064/2977/4575 3061/2976/4574 3063/2978/4576
+f 3066/2979/4577 3063/2978/4576 3065/2980/4578
+f 3067/2981/4579 3066/2979/4577 3065/2980/4578
+f 3067/2981/4580 3068/2982/4581 3066/2979/4577
+f 3069/2983/4582 3068/2982/4583 3067/2981/4580
+f 3069/2983/4582 3070/2984/4584 3068/2982/4583
+f 3071/2985/4585 3070/2984/4586 3069/2983/4587
+f 3071/2985/4588 3072/2986/4589 3070/2984/4590
+f 3073/2987/4591 3072/2986/4589 3071/2985/4585
+f 3073/2987/4591 3074/2988/4592 3072/2986/4593
+f 3075/2989/4594 3074/2988/4592 3073/2987/4591
+f 3077/2990/4595 3076/2991/4596 3075/2989/4594
+f 3079/2992/4597 3078/2993/4598 3077/2990/4595
+f 3081/2994/4599 3080/2995/4600 3079/2992/4597
+f 3083/2996/4601 3082/2997/4602 3081/2994/4599
+f 3085/2998/4603 3084/2999/4604 3083/2996/4601
+f 3088/3000/4605 3085/2998/4606 3087/3001/4607
+f 3090/3002/4608 3087/3001/4607 3089/3003/4609
+f 3092/3004/4610 3089/3003/4609 3091/3005/4611
+f 3094/3006/4612 3091/3005/4611 3093/3007/4613
+f 3095/3008/4614 3092/3004/4615 3094/3006/4616
+f 3095/3008/4614 3097/3009/4617 3096/3010/4618
+f 3095/3008/4614 3098/3011/4619 3097/3009/4620
+f 3099/3012/4621 3097/3009/4620 3098/3011/4622
+f 3101/3013/4623 3100/3014/4624 3099/3012/4621
+f 3103/3015/4625 3102/3016/4626 3101/3013/4623
+f 3105/3017/4627 3104/3018/4628 3103/3015/4629
+f 3107/3019/4630 3106/3020/4631 3105/3017/4627
+f 2881/2796/4632 3108/3021/4633 3107/3019/4630
+f 2879/2794/4294 3109/3022/4634 3108/3021/4633
+f 2877/2792/4292 3110/3023/4635 3109/3022/4636
+f 2875/2790/4290 3111/3024/4637 3110/3023/4635
+f 2873/2788/4287 3112/3025/4638 3111/3024/4637
+f 3112/3025/4638 2869/2783/4639 3113/3026/4640
+f 3113/3026/4641 2869/2783/4284 2867/2781/4278
+f 3113/3026/4642 2867/2781/4278 3114/3027/4643
+f 3114/3027/4644 2865/2779/4276 3115/3028/4645
+f 3115/3028/4645 2863/2778/4275 3116/3029/4646
+f 3116/3029/4646 2861/2776/4271 3117/3030/4647
+f 3117/3030/4648 2861/2776/4271 2859/2774/4269
+f 3117/3030/4649 2859/2774/4269 3118/3031/4650
+f 3118/3031/4650 2859/2774/4269 2857/2772/4267
+f 3118/3031/4650 2857/2772/4267 3119/3032/4651
+f 3119/3032/4652 2857/2772/4267 2855/2770/4264
+f 3119/3032/4651 2855/2770/4264 3120/3033/4653
+f 3120/3033/4654 2855/2770/4264 2853/2768/4260
+f 3120/3033/4655 2853/2768/4260 3121/3034/4656
+f 3121/3034/4656 2853/2768/4260 2851/2767/4259
+f 3121/3034/4656 2851/2767/4259 3122/3035/4657
+f 3122/3035/4658 2851/2767/4259 2849/2765/4257
+f 3122/3035/4658 2849/2765/4257 3123/3036/4659
+f 3123/3036/4659 2849/2765/4257 2847/2763/4255
+f 3123/3036/4659 2847/2763/4255 3124/3037/4660
+f 2847/2763/4255 3125/3038/4661 3124/3037/4660
+f 2845/2761/4253 3126/3039/4662 3125/3038/4661
+f 3126/3039/4663 2843/2759/4252 2844/2758/4249
+f 3126/3039/4663 2844/2758/4249 3127/3040/4664
+f 3128/3041/4665 3126/3039/4662 3127/3040/4664
+f 3125/3038/4661 3128/3041/4666 3129/3042/4667
+f 3130/3043/4668 3125/3038/4669 3129/3042/4670
+f 3130/3043/4668 3124/3037/4660 3125/3038/4661
+f 3131/3044/4671 3124/3037/4660 3130/3043/4668
+f 3131/3044/4671 3123/3036/4659 3124/3037/4660
+f 3132/3045/4672 3123/3036/4659 3131/3044/4671
+f 3132/3045/4673 3122/3035/4674 3123/3036/4675
+f 3133/3046/4676 3122/3035/4658 3132/3045/4673
+f 3133/3046/4676 3121/3034/4656 3122/3035/4658
+f 3134/3047/4677 3121/3034/4656 3133/3046/4676
+f 3134/3047/4677 3120/3033/4678 3121/3034/4656
+f 3135/3048/4679 3120/3033/4653 3134/3047/4680
+f 3135/3048/4681 3119/3032/4651 3120/3033/4678
+f 3136/3049/4682 3119/3032/4651 3135/3048/4681
+f 3136/3049/4683 3118/3031/4650 3119/3032/4651
+f 3137/3050/4684 3118/3031/4650 3136/3049/4683
+f 3137/3050/4684 3117/3030/4648 3118/3031/4650
+f 3138/3051/4685 3117/3030/4686 3137/3050/4684
+f 3139/3052/4687 3116/3029/4688 3138/3051/4685
+f 3140/3053/4689 3115/3028/4690 3139/3052/4687
+f 3140/3053/4689 3114/3027/4643 3115/3028/4645
+f 3141/3054/4691 3114/3027/4643 3140/3053/4689
+f 3141/3054/4692 3113/3026/4641 3114/3027/4693
+f 3142/3055/4694 3113/3026/4640 3141/3054/4695
+f 3111/3024/4637 3142/3055/4694 3143/3056/4696
+f 3110/3023/4635 3143/3056/4696 3144/3057/4697
+f 3109/3022/4636 3144/3057/4698 3145/3058/4699
+f 3108/3021/4633 3145/3058/4699 3106/3020/4700
+f 3106/3020/4631 3146/3059/4701 3104/3018/4702
+f 3145/3058/4703 3147/3060/4704 3146/3059/4705
+f 3144/3057/4706 3148/3061/4707 3147/3060/4708
+f 3143/3056/4696 3149/3062/4709 3148/3061/4707
+f 3149/3062/4709 3141/3054/4692 3150/3063/4710
+f 3150/3063/4710 3140/3053/4689 3151/3064/4711
+f 3151/3064/4711 3139/3052/4712 3152/3065/4713
+f 3152/3065/4713 3138/3051/4714 3153/3066/4715
+f 3153/3066/4715 3137/3050/4684 3154/3067/4716
+f 3154/3067/4717 3137/3050/4684 3136/3049/4683
+f 3154/3067/4717 3136/3049/4683 3155/3068/4718
+f 3155/3068/4718 3136/3049/4719 3135/3048/4681
+f 3155/3068/4718 3135/3048/4681 3156/3069/4720
+f 3156/3069/4720 3135/3048/4681 3134/3047/4677
+f 3156/3069/4720 3134/3047/4677 3157/3070/4721
+f 3157/3070/4722 3134/3047/4677 3133/3046/4676
+f 3157/3070/4723 3133/3046/4724 3158/3071/4725
+f 3158/3071/4725 3133/3046/4676 3132/3045/4672
+f 3158/3071/4725 3132/3045/4673 3159/3072/4726
+f 3159/3072/4726 3132/3045/4672 3131/3044/4671
+f 3159/3072/4727 3131/3044/4671 3160/3073/4728
+f 3160/3073/4729 3131/3044/4671 3130/3043/4668
+f 3160/3073/4728 3130/3043/4668 3161/3074/4730
+f 3161/3074/4730 3130/3043/4668 3129/3042/4667
+f 3161/3074/4730 3129/3042/4670 3162/3075/4731
+f 3129/3042/4670 3163/3076/4732 3162/3075/4731
+f 3163/3076/4733 3128/3041/4734 3164/3077/4735
+f 3128/3041/4734 3127/3040/4664 3164/3077/4735
+f 3163/3076/4732 3164/3077/4736 3165/3078/4737
+f 3163/3076/4732 3167/3079/4738 3166/3080/4739
+f 3168/3081/4740 3163/3076/4732 3166/3080/4739
+f 3168/3081/4740 3162/3075/4731 3163/3076/4732
+f 3169/3082/4741 3162/3075/4731 3168/3081/4740
+f 3169/3082/4742 3161/3074/4743 3162/3075/4731
+f 3170/3083/4744 3161/3074/4730 3169/3082/4745
+f 3170/3083/4744 3160/3073/4729 3161/3074/4743
+f 3171/3084/4746 3160/3073/4729 3170/3083/4747
+f 3171/3084/4748 3159/3072/4726 3160/3073/4729
+f 3172/3085/4749 3159/3072/4726 3171/3084/4748
+f 3172/3085/4750 3158/3071/4725 3159/3072/4727
+f 3173/3086/4751 3158/3071/4725 3172/3085/4750
+f 3173/3086/4751 3157/3070/4752 3158/3071/4725
+f 3174/3087/4753 3157/3070/4721 3173/3086/4751
+f 3174/3087/4753 3156/3069/4720 3157/3070/4721
+f 3175/3088/4754 3156/3069/4720 3174/3087/4753
+f 3175/3088/4754 3155/3068/4718 3156/3069/4720
+f 3176/3089/4755 3155/3068/4718 3175/3088/4754
+f 3176/3089/4755 3154/3067/4717 3155/3068/4718
+f 3177/3090/4756 3154/3067/4757 3176/3089/4758
+f 3178/3091/4759 3153/3066/4715 3177/3090/4756
+f 3179/3092/4760 3152/3065/4713 3178/3091/4759
+f 3180/3093/4761 3151/3064/4762 3179/3092/4760
+f 3181/3094/4763 3150/3063/4764 3180/3093/4761
+f 3148/3061/4707 3181/3094/4763 3182/3095/4765
+f 3147/3060/4708 3182/3095/4766 3183/3096/4767
+f 3146/3059/4705 3183/3096/4768 3184/3097/4769
+f 3104/3018/4702 3184/3097/4770 3102/3016/4626
+f 3102/3016/4626 3185/3098/4771 3100/3014/4624
+f 3184/3097/4770 3186/3099/4772 3185/3098/4771
+f 3183/3096/4768 3187/3100/4773 3186/3099/4772
+f 3182/3095/4766 3188/3101/4774 3187/3100/4775
+f 3188/3101/4776 3180/3093/4777 3189/3102/4778
+f 3189/3102/4779 3179/3092/4760 3190/3103/4780
+f 3190/3103/4781 3178/3091/4759 3191/3104/4782
+f 3191/3104/4782 3177/3090/4756 3192/3105/4783
+f 3192/3105/4783 3176/3089/4755 3193/3106/4784
+f 3193/3106/4785 3176/3089/4755 3175/3088/4786
+f 3193/3106/4784 3175/3088/4786 3194/3107/4787
+f 3194/3107/4787 3174/3087/4753 3195/3108/4788
+f 3195/3108/4789 3173/3086/4751 3196/3109/4790
+f 3196/3109/4790 3173/3086/4751 3172/3085/4750
+f 3196/3109/4790 3172/3085/4750 3197/3110/4791
+f 3197/3110/4791 3172/3085/4750 3171/3084/4748
+f 3197/3110/4791 3171/3084/4748 3198/3111/4792
+f 3198/3111/4792 3171/3084/4748 3170/3083/4747
+f 3198/3111/4792 3170/3083/4747 3199/3112/4793
+f 3199/3112/4793 3170/3083/4744 3169/3082/4794
+f 3199/3112/4793 3169/3082/4745 3200/3113/4795
+f 3200/3113/4795 3169/3082/4742 3168/3081/4740
+f 3200/3113/4795 3168/3081/4740 3201/3114/4796
+f 3201/3114/4797 3168/3081/4740 3166/3080/4739
+f 3201/3114/4798 3166/3080/4739 3202/3115/4799
+f 3202/3115/4800 3166/3080/4739 3167/3079/4738
+f 3202/3115/4801 3167/3079/4738 3203/3116/4802
+f 3204/3117/4803 3202/3115/4801 3203/3116/4802
+f 3205/3118/4804 3202/3115/4799 3204/3117/4803
+f 3205/3118/4804 3201/3114/4798 3202/3115/4805
+f 3206/3119/4806 3201/3114/4798 3205/3118/4807
+f 3206/3119/4808 3200/3113/4795 3201/3114/4798
+f 3199/3112/4793 3206/3119/4808 3207/3120/4809
+f 3198/3111/4792 3207/3120/4809 3208/3121/4810
+f 3197/3110/4791 3208/3121/4810 3209/3122/4811
+f 3210/3123/4812 3197/3110/4791 3209/3122/4811
+f 3210/3123/4812 3196/3109/4790 3197/3110/4791
+f 3211/3124/4813 3196/3109/4790 3210/3123/4812
+f 3211/3124/4813 3195/3108/4789 3196/3109/4790
+f 3212/3125/4814 3195/3108/4788 3211/3124/4813
+f 3212/3125/4815 3194/3107/4787 3195/3108/4788
+f 3213/3126/4816 3194/3107/4787 3212/3125/4815
+f 3213/3126/4816 3193/3106/4785 3194/3107/4787
+f 3214/3127/4817 3193/3106/4784 3213/3126/4816
+f 3215/3128/4818 3192/3105/4819 3214/3127/4820
+f 3216/3129/4821 3191/3104/4822 3215/3128/4818
+f 3217/3130/4823 3190/3103/4781 3216/3129/4821
+f 3218/3131/4824 3189/3102/4778 3217/3130/4825
+f 3219/3132/4826 3188/3101/4774 3218/3131/4827
+f 3186/3099/4772 3219/3132/4826 3220/3133/4828
+f 3185/3098/4771 3220/3133/4828 3221/3134/4829
+f 3100/3014/4830 3221/3134/4829 3097/3009/4620
+f 3097/3009/4617 3222/3135/4831 3096/3010/4618
+f 3221/3134/4829 3223/3136/4832 3222/3135/4833
+f 3220/3133/4828 3224/3137/4834 3223/3136/4832
+f 3224/3137/4835 3218/3131/4824 3225/3138/4836
+f 3225/3138/4836 3217/3130/4837 3226/3139/4838
+f 3226/3139/4838 3216/3129/4821 3227/3140/4839
+f 3227/3140/4840 3215/3128/4818 3228/3141/4841
+f 3228/3141/4841 3214/3127/4817 3229/3142/4842
+f 3229/3142/4842 3214/3127/4817 3213/3126/4816
+f 3229/3142/4842 3213/3126/4816 3230/3143/4843
+f 3230/3143/4844 3213/3126/4816 3212/3125/4814
+f 3230/3143/4843 3212/3125/4815 3231/3144/4845
+f 3231/3144/4846 3212/3125/4815 3211/3124/4847
+f 3231/3144/4846 3211/3124/4847 3232/3145/4848
+f 3232/3145/4848 3211/3124/4849 3210/3123/4812
+f 3232/3145/4848 3210/3123/4812 3233/3146/4850
+f 3233/3146/4851 3210/3123/4812 3209/3122/4811
+f 3233/3146/4851 3209/3122/4811 3234/3147/4852
+f 3234/3147/4852 3209/3122/4811 3208/3121/4810
+f 3234/3147/4852 3208/3121/4810 3235/3148/4853
+f 3235/3148/4853 3208/3121/4810 3207/3120/4809
+f 3235/3148/4853 3207/3120/4809 3236/3149/4854
+f 3207/3120/4809 3237/3150/4855 3236/3149/4854
+f 3237/3150/4855 3206/3119/4808 3205/3118/4804
+f 3237/3150/4855 3205/3118/4804 3238/3151/4856
+f 3238/3151/4856 3205/3118/4804 3204/3117/4803
+f 3238/3151/4856 3204/3117/4803 3239/3152/4857
+f 3239/3152/4858 3204/3117/4803 3240/3153/4859
+f 3204/3117/4803 3203/3116/4802 3240/3153/4859
+f 3239/3152/4860 3240/3153/4859 3241/3154/4861
+f 3057/2971/4862 3239/3152/4857 3241/3154/4863
+f 3058/2972/4864 3239/3152/4860 3057/2971/4862
+f 3058/2972/4864 3238/3151/4856 3239/3152/4860
+f 3237/3150/4855 3058/2972/4865 3060/2973/4866
+f 3236/3149/4854 3060/2973/4866 3062/2975/4572
+f 3064/2977/4867 3236/3149/4854 3062/2975/4572
+f 3064/2977/4575 3235/3148/4853 3236/3149/4854
+f 3066/2979/4577 3235/3148/4853 3064/2977/4575
+f 3066/2979/4577 3234/3147/4852 3235/3148/4853
+f 3068/2982/4581 3234/3147/4852 3066/2979/4868
+f 3068/2982/4869 3233/3146/4870 3234/3147/4852
+f 3070/2984/4590 3233/3146/4851 3068/2982/4581
+f 3070/2984/4586 3232/3145/4848 3233/3146/4851
+f 3072/2986/4871 3232/3145/4848 3070/2984/4590
+f 3072/2986/4589 3231/3144/4846 3232/3145/4848
+f 3074/2988/4592 3231/3144/4846 3072/2986/4593
+f 3074/2988/4592 3230/3143/4844 3231/3144/4846
+f 3076/2991/4596 3230/3143/4843 3074/2988/4592
+f 3078/2993/4872 3229/3142/4842 3076/2991/4596
+f 3080/2995/4600 3228/3141/4873 3078/2993/4598
+f 3082/2997/4874 3227/3140/4840 3080/2995/4600
+f 3084/2999/4604 3226/3139/4838 3082/2997/4875
+f 3086/3155/4876 3225/3138/4836 3084/2999/4604
+f 3223/3136/4832 3086/3155/4877 3088/3000/4605
+f 3222/3135/4831 3088/3000/4878 3090/3002/4879
+f 3096/3010/4618 3090/3002/4608 3092/3004/4610
+f 3057/2971/4862 3241/3154/4861 3056/2970/4567
+f 3091/3005/4611 3036/2950/4880 3093/3007/4613
+f 3089/3003/4609 3035/2949/4538 3091/3005/4611
+f 3087/3001/4881 3037/2951/4882 3089/3003/4609
+f 3085/2998/4883 3038/2952/4541 3087/3001/4881
+f 3040/2954/4543 3085/2998/4603 3083/2996/4601
+f 3041/2955/4884 3083/2996/4601 3081/2994/4599
+f 3042/2956/4545 3081/2994/4599 3079/2992/4597
+f 3043/2957/4547 3079/2992/4597 3077/2990/4595
+f 3044/2958/4550 3077/2990/4595 3075/2989/4594
+f 3045/2959/4553 3075/2989/4594 3073/2987/4885
+f 3071/2985/4588 3045/2959/4553 3073/2987/4591
+f 3071/2985/4585 3046/2960/4886 3045/2959/4551
+f 3069/2983/4587 3046/2960/4554 3071/2985/4585
+f 3069/2983/4582 3047/2961/4887 3046/2960/4552
+f 3067/2981/4579 3047/2961/4555 3069/2983/4582
+f 3067/2981/4579 3048/2962/4557 3047/2961/4555
+f 3065/2980/4578 3048/2962/4557 3067/2981/4579
+f 3065/2980/4578 3049/2963/4558 3048/2962/4557
+f 3063/2978/4576 3049/2963/4558 3065/2980/4888
+f 3061/2976/4574 3050/2964/4559 3063/2978/4576
+f 3059/2974/4573 3051/2965/4560 3061/2976/4574
+f 3055/2969/4566 3052/2966/4889 3059/2974/4571
+f 1/1/1 2/4/4 3/2/89
+f 2/4/4 5/6/4890 6/5/91
+f 5/6/4890 7/8/8 8/7/7
+f 7/8/8 9/10/4891 10/9/95
+f 9/10/10 11/13/13 12/11/11
+f 13/12/12 14/15/97 12/11/11
+f 15/14/14 16/17/17 14/15/15
+f 17/16/4892 18/19/100 16/17/17
+f 19/18/18 20/21/104 18/19/100
+f 21/20/20 22/23/23 20/21/104
+f 21/20/20 23/24/24 24/22/4893
+f 23/24/24 25/26/4894 26/25/108
+f 25/26/4894 27/28/28 28/27/27
+f 27/28/28 29/30/30 30/29/29
+f 29/30/30 31/33/34 32/31/31
+f 33/32/33 34/35/113 32/31/31
+f 35/34/35 36/37/39 34/35/113
+f 37/36/4895 38/39/116 36/37/39
+f 39/38/40 40/41/120 38/39/116
+f 41/40/42 42/43/45 40/41/120
+f 41/40/42 43/44/46 44/42/123
+f 43/44/46 45/46/4896 46/45/125
+f 45/46/4896 47/48/50 48/47/49
+f 47/48/50 49/50/4897 50/49/129
+f 49/50/52 51/53/55 52/51/53
+f 53/52/54 54/55/131 52/51/53
+f 55/54/56 56/57/59 54/55/57
+f 57/56/4898 58/59/134 56/57/59
+f 59/58/60 60/61/138 58/59/134
+f 61/60/62 62/63/65 60/61/138
+f 61/60/62 63/64/66 64/62/4899
+f 63/64/66 65/66/4900 66/65/142
+f 65/66/4900 67/68/70 68/67/69
+f 67/68/70 69/70/72 70/69/71
+f 69/70/72 71/73/76 72/71/73
+f 73/72/75 74/75/147 72/71/73
+f 75/74/77 76/77/81 74/75/147
+f 77/76/4901 78/79/150 76/77/81
+f 79/78/82 80/80/86 78/79/150
+f 1/1/1 4/3/3 80/80/86
+f 4/3/3 81/83/88 82/81/85
+f 4/3/3 3/2/2 83/82/87
+f 3/2/89 6/5/5 84/84/90
+f 6/5/5 8/7/7 85/85/92
+f 8/7/7 10/9/9 86/86/159
+f 10/9/9 12/11/98 87/87/96
+f 14/15/15 88/88/4902 87/87/96
+f 16/17/4903 89/89/101 88/88/4902
+f 18/19/19 90/90/102 89/89/101
+f 20/21/104 91/91/103 90/90/102
+f 22/23/23 92/93/106 91/91/103
+f 22/23/23 24/22/22 93/92/105
+f 24/22/4893 26/25/25 94/94/107
+f 26/25/25 28/27/27 95/95/109
+f 28/27/27 30/29/32 96/96/173
+f 30/29/32 32/31/114 97/97/112
+f 34/35/36 98/98/4904 97/97/112
+f 36/37/4905 99/99/4906 98/98/4904
+f 38/39/41 100/100/118 99/99/4906
+f 40/41/43 101/101/119 100/100/118
+f 42/43/45 102/103/122 101/101/119
+f 42/43/45 44/42/44 103/102/121
+f 44/42/123 46/45/47 104/104/124
+f 46/45/47 48/47/49 105/105/4907
+f 48/47/49 50/49/51 106/106/186
+f 50/49/51 52/51/132 107/107/130
+f 54/55/57 108/108/4908 107/107/130
+f 56/57/4909 109/109/135 108/108/4908
+f 58/59/61 110/110/136 109/109/135
+f 60/61/138 111/111/137 110/110/136
+f 62/63/65 112/113/140 111/111/137
+f 62/63/65 64/62/64 113/112/139
+f 64/62/4899 66/65/67 114/114/141
+f 66/65/67 68/67/69 115/115/143
+f 68/67/69 70/69/74 116/116/200
+f 70/69/74 72/71/148 117/117/146
+f 74/75/78 118/118/4910 117/117/146
+f 76/77/4911 119/119/151 118/118/4910
+f 78/79/83 120/120/152 119/119/151
+f 80/80/84 82/81/85 120/120/152
+f 120/120/152 82/81/85 121/121/4912
+f 82/81/85 81/83/88 123/123/155
+f 83/82/87 124/124/213 123/123/155
+f 84/84/90 125/125/219 124/124/4913
+f 85/85/4914 126/126/221 125/125/219
+f 86/86/159 127/127/161 126/126/4915
+f 87/87/96 128/129/163 127/127/161
+f 87/87/96 88/88/99 129/128/162
+f 88/88/99 89/89/101 130/130/167
+f 89/89/101 90/90/102 131/131/4916
+f 90/90/102 91/91/103 132/132/168
+f 91/91/103 92/93/106 133/133/169
+f 93/92/105 134/134/233 133/133/169
+f 94/94/107 135/135/236 134/134/171
+f 95/95/4917 136/136/174 135/135/236
+f 96/96/173 137/137/175 136/136/4918
+f 97/97/112 138/139/177 137/137/175
+f 97/97/112 98/98/115 139/138/176
+f 98/98/115 99/99/4906 140/140/4919
+f 99/99/4906 100/100/118 141/141/4920
+f 100/100/118 101/101/119 142/142/4921
+f 101/101/119 102/103/122 143/143/182
+f 103/102/121 144/144/247 143/143/182
+f 104/104/124 145/145/253 144/144/4922
+f 105/105/4907 146/146/255 145/145/253
+f 106/106/186 147/147/188 146/146/4923
+f 107/107/130 148/149/190 147/147/188
+f 107/107/130 108/108/133 149/148/189
+f 108/108/133 109/109/135 150/150/194
+f 109/109/135 110/110/136 151/151/4924
+f 110/110/136 111/111/137 152/152/195
+f 111/111/137 112/113/140 153/153/196
+f 113/112/139 154/154/267 153/153/196
+f 114/114/141 155/155/270 154/154/198
+f 115/115/4925 156/156/201 155/155/270
+f 116/116/200 157/157/202 156/156/4926
+f 117/117/146 158/159/204 157/157/202
+f 117/117/146 118/118/149 159/158/203
+f 118/118/149 119/119/151 160/160/4927
+f 119/119/151 120/120/152 122/122/4928
+f 160/160/4929 122/122/154 161/161/211
+f 122/122/4930 121/121/156 163/163/282
+f 121/121/156 123/123/214 164/164/212
+f 124/124/213 165/165/4931 164/164/212
+f 125/125/219 166/166/4932 165/165/4931
+f 126/126/4915 167/167/4933 166/166/218
+f 127/127/4934 168/168/222 167/167/285
+f 128/129/163 169/170/4935 168/168/222
+f 128/129/163 129/128/162 170/169/223
+f 129/128/162 130/130/167 171/171/4936
+f 130/130/4937 131/131/166 172/172/228
+f 131/131/4938 132/132/170 173/173/227
+f 132/132/168 133/133/169 174/174/229
+f 134/134/171 175/175/299 174/174/229
+f 135/135/236 176/176/235 175/175/232
+f 136/136/174 177/177/300 176/176/4939
+f 137/137/175 178/178/238 177/177/300
+f 138/139/177 179/180/4940 178/178/238
+f 138/139/177 139/138/176 180/179/239
+f 139/138/176 140/140/180 181/181/4941
+f 140/140/4942 141/141/179 182/182/245
+f 141/141/4943 142/142/183 183/183/313
+f 142/142/183 143/143/248 184/184/246
+f 144/144/247 185/185/4944 184/184/246
+f 145/145/253 186/186/4945 185/185/4944
+f 146/146/4923 187/187/4946 186/186/252
+f 147/147/4947 188/188/256 187/187/316
+f 148/149/190 189/190/4948 188/188/256
+f 148/149/190 149/148/189 190/189/257
+f 149/148/189 150/150/194 191/191/4949
+f 150/150/4950 151/151/193 192/192/262
+f 151/151/4951 152/152/197 193/193/261
+f 152/152/195 153/153/196 194/194/263
+f 154/154/198 195/195/330 194/194/263
+f 155/155/270 196/196/269 195/195/266
+f 156/156/201 197/197/331 196/196/4952
+f 157/157/202 198/198/272 197/197/331
+f 158/159/204 199/200/4953 198/198/272
+f 158/159/204 159/158/203 200/199/273
+f 159/158/203 160/160/207 162/162/4954
+f 200/199/4955 162/162/209 201/201/4956
+f 162/162/209 161/161/279 203/203/278
+f 161/161/279 163/163/282 204/204/280
+f 163/163/282 164/164/212 205/205/283
+f 165/165/216 206/206/284 205/205/283
+f 166/166/218 207/207/349 206/206/284
+f 167/167/285 208/208/351 207/207/286
+f 168/168/222 209/209/290 208/208/351
+f 169/170/289 210/211/293 209/209/290
+f 169/170/4935 170/169/223 211/210/292
+f 170/169/4957 171/171/225 212/212/294
+f 171/171/225 172/172/228 213/213/295
+f 172/172/228 173/173/230 214/214/296
+f 173/173/227 174/174/229 215/215/297
+f 175/175/232 216/216/298 215/215/297
+f 176/176/235 217/217/301 216/216/298
+f 177/177/300 218/218/303 217/217/301
+f 178/178/238 219/219/304 218/218/303
+f 179/180/4958 220/221/307 219/219/304
+f 179/180/305 180/179/239 221/220/306
+f 180/179/4959 181/181/242 222/222/4960
+f 181/181/242 182/182/310 223/223/309
+f 182/182/310 183/183/313 224/224/311
+f 183/183/313 184/184/246 225/225/314
+f 185/185/250 226/226/315 225/225/314
+f 186/186/252 227/227/379 226/226/315
+f 187/187/316 228/228/381 227/227/317
+f 188/188/256 229/229/321 228/228/381
+f 189/190/320 230/231/324 229/229/321
+f 189/190/4948 190/189/257 231/230/323
+f 190/189/4961 191/191/259 232/232/325
+f 191/191/259 192/192/262 233/233/326
+f 192/192/262 193/193/264 234/234/327
+f 193/193/261 194/194/263 235/235/328
+f 195/195/266 236/236/329 235/235/328
+f 196/196/269 237/237/332 236/236/329
+f 197/197/331 238/238/334 237/237/332
+f 198/198/272 239/239/335 238/238/334
+f 199/200/4962 240/240/337 239/239/335
+f 199/200/336 200/199/273 202/202/277
+f 240/240/338 202/202/277 241/241/339
+f 202/202/277 201/201/276 243/243/341
+f 201/201/4956 203/203/281 244/244/4963
+f 203/203/281 204/204/4964 245/245/406
+f 204/204/4964 205/205/283 246/246/348
+f 206/206/284 247/247/408 246/246/346
+f 207/207/286 248/248/4965 247/247/408
+f 208/208/351 249/249/4966 248/248/352
+f 209/209/290 250/250/355 249/249/353
+f 210/211/354 251/252/412 250/250/355
+f 210/211/293 211/210/292 252/251/356
+f 211/210/292 212/212/294 253/253/358
+f 212/212/294 213/213/4967 254/254/359
+f 213/213/4967 214/214/296 255/255/421
+f 214/214/296 215/215/297 256/256/362
+f 216/216/298 257/257/423 256/256/362
+f 217/217/301 258/258/424 257/257/423
+f 218/218/303 259/259/4968 258/258/424
+f 219/219/304 260/260/367 259/259/366
+f 220/221/307 261/262/4969 260/260/367
+f 220/221/368 221/220/306 262/261/369
+f 221/220/306 222/222/308 263/263/371
+f 222/222/4960 223/223/312 264/264/4970
+f 223/223/312 224/224/4971 265/265/436
+f 224/224/4971 225/225/314 266/266/378
+f 226/226/315 267/267/438 266/266/376
+f 227/227/317 268/268/4972 267/267/438
+f 228/228/381 269/269/4973 268/268/382
+f 229/229/321 270/270/385 269/269/383
+f 230/231/384 271/272/442 270/270/385
+f 230/231/324 231/230/323 272/271/386
+f 231/230/323 232/232/325 273/273/388
+f 232/232/325 233/233/4974 274/274/389
+f 233/233/4974 234/234/327 275/275/451
+f 234/234/327 235/235/328 276/276/392
+f 236/236/329 277/277/453 276/276/392
+f 237/237/332 278/278/454 277/277/453
+f 238/238/334 279/279/4975 278/278/454
+f 239/239/335 280/280/397 279/279/396
+f 240/240/337 242/242/4976 280/280/397
+f 242/242/4976 281/283/401 282/281/398
+f 242/242/340 241/241/339 283/282/400
+f 241/241/339 243/243/341 284/284/402
+f 243/243/341 244/244/342 285/285/4977
+f 244/244/342 245/245/344 286/286/4978
+f 245/245/344 246/246/348 287/287/407
+f 247/247/350 288/288/471 287/287/4979
+f 248/248/352 289/289/472 288/288/471
+f 249/249/353 290/290/4980 289/289/472
+f 250/250/355 291/291/413 290/290/4981
+f 251/252/4982 292/293/415 291/291/413
+f 251/252/412 252/251/356 293/292/414
+f 252/251/356 253/253/358 294/294/419
+f 253/253/358 254/254/359 295/295/4983
+f 254/254/359 255/255/361 296/296/4984
+f 255/255/361 256/256/362 297/297/422
+f 257/257/364 298/298/489 297/297/4985
+f 258/258/424 299/299/490 298/298/489
+f 259/259/366 300/300/427 299/299/490
+f 260/260/367 301/301/494 300/300/4986
+f 261/262/4969 302/303/431 301/301/428
+f 261/262/370 262/261/369 303/302/430
+f 262/261/369 263/263/371 304/304/432
+f 263/263/371 264/264/372 305/305/4987
+f 264/264/372 265/265/374 306/306/4988
+f 265/265/374 266/266/378 307/307/437
+f 267/267/380 308/308/507 307/307/4989
+f 268/268/382 309/309/508 308/308/507
+f 269/269/383 310/310/4990 309/309/508
+f 270/270/385 311/311/443 310/310/4991
+f 271/272/4992 312/313/445 311/311/443
+f 271/272/442 272/271/386 313/312/444
+f 272/271/386 273/273/388 314/314/449
+f 273/273/388 274/274/389 315/315/4993
+f 274/274/389 275/275/391 316/316/4994
+f 275/275/391 276/276/392 317/317/452
+f 277/277/394 318/318/525 317/317/4995
+f 278/278/454 319/319/526 318/318/525
+f 279/279/396 320/320/457 319/319/526
+f 280/280/397 282/281/458 320/320/4996
+f 282/281/458 321/322/532 322/321/459
+f 281/283/4997 323/324/4998 321/322/461
+f 281/283/460 283/282/465 324/323/463
+f 283/282/465 284/284/402 325/325/466
+f 284/284/4999 285/285/403 326/326/541
+f 285/285/403 286/286/4978 327/327/469
+f 286/286/405 287/287/407 328/328/470
+f 288/288/471 329/329/473 328/328/470
+f 289/289/472 330/330/546 329/329/473
+f 290/290/411 331/331/477 330/330/546
+f 291/291/476 332/332/5000 331/331/477
+f 292/293/5001 333/334/551 332/332/479
+f 292/293/478 293/292/417 334/333/481
+f 293/292/417 294/294/419 335/335/483
+f 294/294/5002 295/295/418 336/336/559
+f 295/295/418 296/296/4984 337/337/486
+f 296/296/420 297/297/422 338/338/488
+f 298/298/489 339/339/491 338/338/488
+f 299/299/490 340/340/5003 339/339/491
+f 300/300/427 341/341/495 340/340/5004
+f 301/301/494 342/342/566 341/341/495
+f 302/303/5005 343/344/5006 342/342/497
+f 302/303/496 303/302/501 344/343/499
+f 303/302/501 304/304/432 345/345/502
+f 304/304/5007 305/305/433 346/346/575
+f 305/305/433 306/306/4988 347/347/505
+f 306/306/435 307/307/437 348/348/506
+f 308/308/507 349/349/509 348/348/506
+f 309/309/508 350/350/579 349/349/509
+f 310/310/441 351/351/513 350/350/579
+f 311/311/512 352/352/5008 351/351/513
+f 312/313/5009 353/354/584 352/352/515
+f 312/313/514 313/312/447 354/353/517
+f 313/312/447 314/314/449 355/355/519
+f 314/314/5010 315/315/448 356/356/592
+f 315/315/448 316/316/4994 357/357/522
+f 316/316/450 317/317/452 358/358/524
+f 318/318/525 359/359/527 358/358/524
+f 319/319/526 360/360/5011 359/359/527
+f 320/320/457 322/321/459 360/360/5012
+f 322/321/459 361/362/603 362/361/530
+f 321/322/532 363/363/534 361/362/533
+f 323/324/464 364/365/536 363/363/5013
+f 323/324/464 324/323/463 365/364/538
+f 324/323/463 325/325/466 366/366/537
+f 325/325/466 326/326/5014 367/367/539
+f 326/326/468 327/327/469 368/368/544
+f 327/327/469 328/328/545 369/369/543
+f 329/329/473 370/370/547 369/369/543
+f 330/330/475 371/371/548 370/370/617
+f 331/331/477 372/372/622 371/371/548
+f 332/332/5000 373/373/552 372/372/550
+f 333/334/551 374/375/554 373/373/5015
+f 333/334/551 334/333/481 375/374/556
+f 334/333/484 335/335/483 376/376/555
+f 335/335/483 336/336/5016 377/377/557
+f 336/336/559 337/337/486 378/378/5017
+f 337/337/486 338/338/562 379/379/561
+f 339/339/491 380/380/5018 379/379/561
+f 340/340/493 381/381/564 380/380/5018
+f 341/341/495 382/382/641 381/381/564
+f 342/342/566 383/383/568 382/382/567
+f 343/344/500 384/385/570 383/383/5019
+f 343/344/500 344/343/499 385/384/572
+f 344/343/499 345/345/502 386/386/571
+f 345/345/502 346/346/5020 387/387/573
+f 346/346/504 347/347/505 388/388/578
+f 347/347/505 348/348/506 389/389/577
+f 349/349/509 390/390/580 389/389/577
+f 350/350/511 391/391/581 390/390/655
+f 351/351/513 392/392/660 391/391/581
+f 352/352/5008 393/393/585 392/392/583
+f 353/354/584 394/395/587 393/393/5021
+f 353/354/584 354/353/517 395/394/589
+f 354/353/520 355/355/519 396/396/588
+f 355/355/519 356/356/5022 397/397/590
+f 356/356/592 357/357/522 398/398/5023
+f 357/357/522 358/358/595 399/399/594
+f 359/359/527 400/400/5024 399/399/594
+f 360/360/529 362/361/530 400/400/5024
+f 362/361/530 401/402/600 402/401/673
+f 361/362/5025 403/403/602 401/402/600
+f 363/363/5013 404/404/604 403/403/602
+f 364/365/536 405/406/681 404/404/604
+f 364/365/536 365/364/5026 406/405/606
+f 365/364/5027 366/366/540 407/407/608
+f 366/366/5028 367/367/539 408/408/610
+f 367/367/539 368/368/542 409/409/612
+f 368/368/613 369/369/543 410/410/614
+f 370/370/615 411/411/616 410/410/614
+f 371/371/548 412/412/619 411/411/616
+f 372/372/5029 413/413/5030 412/412/619
+f 373/373/5015 414/414/623 413/413/621
+f 374/375/554 415/416/697 414/414/623
+f 374/375/554 375/374/5031 416/415/625
+f 375/374/5032 376/376/558 417/417/627
+f 376/376/5033 377/377/557 418/418/629
+f 377/377/5034 378/378/5017 419/419/633
+f 378/378/631 379/379/561 420/420/632
+f 380/380/634 421/421/636 420/420/632
+f 381/381/564 422/422/638 421/421/706
+f 382/382/5035 423/423/640 422/422/638
+f 383/383/5019 424/424/642 423/423/640
+f 384/385/570 425/426/713 424/424/642
+f 384/385/570 385/384/5036 426/425/644
+f 385/384/5037 386/386/574 427/427/646
+f 386/386/5038 387/387/573 428/428/648
+f 387/387/573 388/388/576 429/429/650
+f 388/388/651 389/389/577 430/430/652
+f 390/390/653 431/431/654 430/430/652
+f 391/391/581 432/432/657 431/431/654
+f 392/392/5039 433/433/5040 432/432/657
+f 393/393/5021 434/434/661 433/433/659
+f 394/395/5041 435/436/729 434/434/661
+f 394/395/587 395/394/5042 436/435/663
+f 395/394/5043 396/396/591 437/437/665
+f 396/396/5044 397/397/590 438/438/667
+f 397/397/5045 398/398/5023 439/439/671
+f 398/398/669 399/399/594 440/440/670
+f 400/400/672 402/401/598 440/440/670
+f 402/401/673 441/442/675 442/441/674
+f 401/402/600 443/443/676 441/442/675
+f 403/403/602 444/444/678 443/443/676
+f 404/404/604 445/445/680 444/444/678
+f 405/406/607 446/447/683 445/445/680
+f 405/406/679 406/405/606 447/446/682
+f 406/405/606 407/407/608 448/448/684
+f 407/407/611 408/408/5046 449/449/685
+f 408/408/610 409/409/612 450/450/686
+f 409/409/688 410/410/614 451/451/689
+f 411/411/690 452/452/691 451/451/689
+f 412/412/619 453/453/755 452/452/691
+f 413/413/621 454/454/694 453/453/755
+f 414/414/623 455/455/696 454/454/694
+f 415/416/626 456/457/699 455/455/696
+f 415/416/695 416/415/625 457/456/698
+f 416/415/625 417/417/627 458/458/700
+f 417/417/701 418/418/5047 459/459/704
+f 418/418/629 419/419/633 460/460/703
+f 419/419/630 420/420/632 461/461/705
+f 421/421/706 462/462/707 461/461/705
+f 422/422/638 463/463/708 462/462/707
+f 423/423/640 464/464/710 463/463/708
+f 424/424/642 465/465/712 464/464/710
+f 425/426/645 466/467/715 465/465/712
+f 425/426/711 426/425/644 467/466/714
+f 426/425/644 427/427/646 468/468/716
+f 427/427/649 428/428/5048 469/469/717
+f 428/428/648 429/429/650 470/470/718
+f 429/429/720 430/430/652 471/471/721
+f 431/431/722 472/472/723 471/471/721
+f 432/432/657 473/473/785 472/472/723
+f 433/433/659 474/474/726 473/473/785
+f 434/434/661 475/475/728 474/474/726
+f 435/436/664 476/477/731 475/475/728
+f 435/436/727 436/435/663 477/476/730
+f 436/435/663 437/437/665 478/478/732
+f 437/437/733 438/438/5049 479/479/736
+f 438/438/667 439/439/671 480/480/735
+f 439/439/668 440/440/670 442/441/674
+f 480/480/5050 442/441/674 481/481/737
+f 441/442/675 483/483/801 481/481/737
+f 443/443/5051 484/484/742 483/483/801
+f 444/444/741 485/485/743 484/484/742
+f 445/445/680 486/486/804 485/485/743
+f 446/447/683 487/488/806 486/486/744
+f 446/447/683 447/446/682 488/487/745
+f 447/446/682 448/448/749 489/489/747
+f 448/448/684 449/449/687 490/490/750
+f 449/449/687 450/450/686 491/491/752
+f 450/450/5052 451/451/689 492/492/753
+f 452/452/691 493/493/756 492/492/753
+f 453/453/5053 494/494/758 493/493/756
+f 454/454/757 495/495/759 494/494/758
+f 455/455/696 496/496/818 495/495/759
+f 456/457/699 497/498/820 496/496/760
+f 456/457/699 457/456/698 498/497/761
+f 457/456/698 458/458/765 499/499/763
+f 458/458/700 459/459/704 500/500/766
+f 459/459/702 460/460/703 501/501/768
+f 460/460/5054 461/461/705 502/502/769
+f 462/462/707 503/503/829 502/502/769
+f 463/463/5055 504/504/773 503/503/829
+f 464/464/772 505/505/774 504/504/773
+f 465/465/712 506/506/832 505/505/774
+f 466/467/715 507/508/834 506/506/775
+f 466/467/715 467/466/714 508/507/776
+f 467/466/714 468/468/5056 509/509/778
+f 468/468/716 469/469/719 510/510/780
+f 469/469/719 470/470/718 511/511/782
+f 470/470/5057 471/471/721 512/512/783
+f 472/472/723 513/513/786 512/512/783
+f 473/473/5058 514/514/788 513/513/786
+f 474/474/787 515/515/789 514/514/788
+f 475/475/728 516/516/846 515/515/789
+f 476/477/731 517/518/848 516/516/790
+f 476/477/731 477/476/730 518/517/791
+f 477/476/730 478/478/795 519/519/793
+f 478/478/732 479/479/736 520/520/796
+f 479/479/734 480/480/735 482/482/738
+f 520/520/796 482/482/738 521/521/798
+f 482/482/738 481/481/737 523/523/800
+f 483/483/801 524/524/859 523/523/800
+f 484/484/742 525/525/803 524/524/859
+f 485/485/743 526/526/5059 525/525/803
+f 486/486/804 527/527/862 526/526/5059
+f 487/488/5060 528/529/809 527/527/862
+f 487/488/746 488/487/748 529/528/808
+f 488/487/748 489/489/747 530/530/812
+f 489/489/747 490/490/750 531/531/811
+f 490/490/750 491/491/752 532/532/813
+f 491/491/752 492/492/753 533/533/814
+f 493/493/756 534/534/815 533/533/814
+f 494/494/758 535/535/817 534/534/815
+f 495/495/759 536/536/876 535/535/817
+f 496/496/760 537/537/5061 536/536/876
+f 497/498/5062 538/539/823 537/537/821
+f 497/498/762 498/497/764 539/538/822
+f 498/497/764 499/499/763 540/540/826
+f 499/499/763 500/500/766 541/541/825
+f 500/500/766 501/501/768 542/542/827
+f 501/501/768 502/502/769 543/543/828
+f 503/503/829 544/544/887 543/543/828
+f 504/504/773 545/545/831 544/544/887
+f 505/505/774 546/546/892 545/545/831
+f 506/506/832 547/547/890 546/546/892
+f 507/508/5063 548/549/837 547/547/890
+f 507/508/777 508/507/779 549/548/836
+f 508/507/779 509/509/778 550/550/840
+f 509/509/778 510/510/780 551/551/839
+f 510/510/780 511/511/782 552/552/841
+f 511/511/782 512/512/783 553/553/842
+f 513/513/786 554/554/843 553/553/842
+f 514/514/788 555/555/845 554/554/843
+f 515/515/789 556/556/905 555/555/845
+f 516/516/790 557/557/849 556/556/905
+f 517/518/5064 558/559/851 557/557/849
+f 517/518/792 518/517/794 559/558/850
+f 518/517/794 519/519/793 560/560/853
+f 519/519/793 520/520/796 522/522/799
+f 560/560/853 522/522/799 561/561/854
+f 522/522/799 521/521/798 563/563/856
+f 521/521/798 523/523/800 564/564/858
+f 524/524/802 565/565/860 564/564/858
+f 525/525/803 566/566/861 565/565/860
+f 526/526/805 567/567/863 566/566/861
+f 527/527/862 568/568/864 567/567/863
+f 528/529/809 569/570/866 568/568/864
+f 528/529/809 529/528/867 570/569/865
+f 529/528/808 530/530/812 571/571/926
+f 530/530/812 531/531/811 572/572/869
+f 531/531/811 532/532/813 573/573/870
+f 532/532/813 533/533/814 574/574/872
+f 534/534/5065 575/575/873 574/574/872
+f 535/535/817 576/576/874 575/575/873
+f 536/536/819 577/577/875 576/576/874
+f 537/537/821 578/578/877 577/577/875
+f 538/539/823 579/580/879 578/578/877
+f 538/539/823 539/538/880 580/579/878
+f 539/538/822 540/540/826 581/581/939
+f 540/540/826 541/541/825 582/582/883
+f 541/541/825 542/542/827 583/583/884
+f 542/542/827 543/543/828 584/584/886
+f 544/544/830 585/585/888 584/584/886
+f 545/545/831 586/586/889 585/585/888
+f 546/546/833 587/587/891 586/586/889
+f 547/547/890 588/588/893 587/587/891
+f 548/549/837 589/590/895 588/588/893
+f 548/549/837 549/548/896 590/589/894
+f 549/548/836 550/550/840 591/591/952
+f 550/550/840 551/551/839 592/592/898
+f 551/551/839 552/552/841 593/593/899
+f 552/552/841 553/553/842 594/594/901
+f 554/554/5066 595/595/902 594/594/901
+f 555/555/845 596/596/903 595/595/902
+f 556/556/847 597/597/904 596/596/903
+f 557/557/849 598/598/906 597/597/904
+f 558/559/851 599/600/908 598/598/906
+f 558/559/851 559/558/909 600/599/907
+f 559/558/850 560/560/853 562/562/913
+f 600/599/907 562/562/913 601/601/911
+f 562/562/913 561/561/854 603/603/914
+f 561/561/854 563/563/856 604/604/915
+f 563/563/856 564/564/858 605/605/917
+f 565/565/860 606/606/918 605/605/917
+f 566/566/861 607/607/920 606/606/918
+f 567/567/919 608/608/921 607/607/920
+f 568/568/864 609/609/922 608/608/921
+f 569/570/866 610/611/976 609/609/922
+f 569/570/866 570/569/865 611/610/923
+f 570/569/865 571/571/926 612/612/925
+f 571/571/926 572/572/869 613/613/927
+f 572/572/869 573/573/870 614/614/928
+f 573/573/870 574/574/872 615/615/930
+f 575/575/873 616/616/931 615/615/930
+f 576/576/874 617/617/933 616/616/931
+f 577/577/932 618/618/934 617/617/933
+f 578/578/877 619/619/935 618/618/934
+f 579/580/879 620/621/989 619/619/935
+f 579/580/879 580/579/878 621/620/936
+f 580/579/878 581/581/939 622/622/938
+f 581/581/939 582/582/883 623/623/940
+f 582/582/883 583/583/884 624/624/941
+f 583/583/884 584/584/886 625/625/943
+f 585/585/888 626/626/944 625/625/943
+f 586/586/889 627/627/946 626/626/944
+f 587/587/945 628/628/947 627/627/946
+f 588/588/893 629/629/948 628/628/947
+f 589/590/895 630/631/1002 629/629/948
+f 589/590/895 590/589/894 631/630/949
+f 590/589/894 591/591/952 632/632/951
+f 591/591/952 592/592/898 633/633/953
+f 592/592/898 593/593/899 634/634/954
+f 593/593/899 594/594/901 635/635/956
+f 595/595/902 636/636/957 635/635/956
+f 596/596/903 637/637/959 636/636/957
+f 597/597/958 638/638/960 637/637/959
+f 598/598/906 639/639/961 638/638/960
+f 599/600/908 640/640/963 639/639/961
+f 599/600/908 600/599/907 602/602/912
+f 640/640/963 602/602/912 641/641/964
+f 602/602/912 601/601/911 643/643/1021
+f 601/601/911 603/603/914 644/644/968
+f 603/603/914 604/604/915 645/645/971
+f 604/604/915 605/605/917 646/646/970
+f 606/606/918 647/647/972 646/646/970
+f 607/607/920 648/648/5067 647/647/1025
+f 608/608/921 649/649/5068 648/648/973
+f 609/609/922 650/650/1032 649/649/5068
+f 610/611/924 651/652/1030 650/650/975
+f 610/611/976 611/610/923 652/651/977
+f 611/610/923 612/612/925 653/653/1037
+f 612/612/925 613/613/927 654/654/981
+f 613/613/927 614/614/928 655/655/984
+f 614/614/928 615/615/930 656/656/983
+f 616/616/931 657/657/985 656/656/983
+f 617/617/933 658/658/5069 657/657/1041
+f 618/618/934 659/659/5070 658/658/986
+f 619/619/935 660/660/1047 659/659/5070
+f 620/621/937 661/662/1048 660/660/988
+f 620/621/989 621/620/936 662/661/990
+f 621/620/936 622/622/938 663/663/1052
+f 622/622/938 623/623/940 664/664/994
+f 623/623/940 624/624/941 665/665/997
+f 624/624/941 625/625/943 666/666/996
+f 626/626/944 667/667/998 666/666/996
+f 627/627/946 668/668/5071 667/667/1056
+f 628/628/947 669/669/5072 668/668/999
+f 629/629/948 670/670/1063 669/669/5072
+f 630/631/950 671/672/1061 670/670/1001
+f 630/631/1002 631/630/949 672/671/1003
+f 631/630/949 632/632/951 673/673/1068
+f 632/632/951 633/633/953 674/674/1007
+f 633/633/953 634/634/954 675/675/1010
+f 634/634/954 635/635/956 676/676/1009
+f 636/636/957 677/677/5073 676/676/1009
+f 637/637/959 678/678/5074 677/677/1072
+f 638/638/960 679/679/5075 678/678/1012
+f 639/639/961 680/680/1016 679/679/5075
+f 640/640/962 642/642/1017 680/680/1014
+f 642/642/5076 681/683/1079 682/681/1015
+f 642/642/965 641/641/967 683/682/1018
+f 641/641/964 643/643/1021 684/684/1020
+f 643/643/1021 644/644/968 685/685/1022
+f 644/644/968 645/645/5077 686/686/1023
+f 645/645/969 646/646/970 687/687/1024
+f 647/647/1025 688/688/1026 687/687/1024
+f 648/648/973 689/689/1028 688/688/1026
+f 649/649/974 690/690/1029 689/689/1028
+f 650/650/975 691/691/1031 690/690/1029
+f 651/652/978 692/693/1090 691/691/1031
+f 651/652/1030 652/651/980 693/692/1034
+f 652/651/977 653/653/1037 694/694/1036
+f 653/653/1037 654/654/981 695/695/1038
+f 654/654/981 655/655/5078 696/696/1039
+f 655/655/982 656/656/983 697/697/1040
+f 657/657/1041 698/698/1042 697/697/1040
+f 658/658/986 699/699/1044 698/698/1042
+f 659/659/987 700/700/1045 699/699/1044
+f 660/660/988 701/701/1046 700/700/1045
+f 661/662/5079 702/703/1101 701/701/1046
+f 661/662/991 662/661/993 703/702/1049
+f 662/661/990 663/663/1052 704/704/1051
+f 663/663/1052 664/664/994 705/705/1053
+f 664/664/994 665/665/5080 706/706/1054
+f 665/665/995 666/666/996 707/707/1055
+f 667/667/1056 708/708/1057 707/707/1055
+f 668/668/999 709/709/1059 708/708/1057
+f 669/669/1000 710/710/1060 709/709/1059
+f 670/670/1001 711/711/1062 710/710/1060
+f 671/672/1004 712/713/1112 711/711/1062
+f 671/672/1061 672/671/1006 713/712/1065
+f 672/671/1003 673/673/1068 714/714/1067
+f 673/673/1068 674/674/1007 715/715/1069
+f 674/674/1007 675/675/5081 716/716/1070
+f 675/675/1008 676/676/1009 717/717/1071
+f 677/677/1072 718/718/1073 717/717/1071
+f 678/678/1012 719/719/1075 718/718/1073
+f 679/679/1013 720/720/1076 719/719/1075
+f 680/680/1014 682/681/1015 720/720/1076
+f 682/681/1015 721/722/1078 722/721/1121
+f 681/683/1019 723/724/1125 721/722/1078
+f 681/683/1079 683/682/1018 724/723/1080
+f 683/682/1018 684/684/1020 725/725/5082
+f 684/684/1020 685/685/1022 726/726/1083
+f 685/685/1022 686/686/1023 727/727/1084
+f 686/686/1023 687/687/1024 728/728/1085
+f 688/688/1026 729/729/1086 728/728/1085
+f 689/689/1028 730/730/1087 729/729/1086
+f 690/690/1029 731/731/1137 730/730/1087
+f 691/691/1031 732/732/1089 731/731/1137
+f 692/693/1035 733/734/1141 732/732/1089
+f 692/693/1090 693/692/1034 734/733/1091
+f 693/692/1034 694/694/1036 735/735/5083
+f 694/694/1036 695/695/1038 736/736/1094
+f 695/695/1038 696/696/1039 737/737/1095
+f 696/696/1039 697/697/1040 738/738/1096
+f 698/698/1042 739/739/1097 738/738/1096
+f 699/699/1044 740/740/1098 739/739/1097
+f 700/700/1045 741/741/1153 740/740/1098
+f 701/701/1046 742/742/1100 741/741/1153
+f 702/703/1050 743/744/1157 742/742/1100
+f 702/703/1101 703/702/1049 744/743/1102
+f 703/702/1049 704/704/1051 745/745/5084
+f 704/704/1051 705/705/1053 746/746/1105
+f 705/705/1053 706/706/1054 747/747/1106
+f 706/706/1054 707/707/1055 748/748/1107
+f 708/708/1057 749/749/1108 748/748/1107
+f 709/709/1059 750/750/1109 749/749/1108
+f 710/710/1060 751/751/1169 750/750/1109
+f 711/711/1062 752/752/1111 751/751/1169
+f 712/713/1066 753/754/1173 752/752/1111
+f 712/713/1112 713/712/1065 754/753/1113
+f 713/712/1065 714/714/1067 755/755/5085
+f 714/714/1067 715/715/1069 756/756/1116
+f 715/715/1069 716/716/1070 757/757/1117
+f 716/716/1070 717/717/1071 758/758/1118
+f 718/718/1073 759/759/1119 758/758/1118
+f 719/719/1075 760/760/1120 759/759/1119
+f 720/720/1076 722/721/1121 760/760/1120
+f 722/721/1121 761/762/1124 762/761/1122
+f 721/722/1123 763/763/1126 761/762/1124
+f 723/724/1127 764/765/1129 763/763/5086
+f 723/724/1127 724/723/1080 765/764/1128
+f 724/723/1130 725/725/1082 766/766/1131
+f 725/725/1082 726/726/1083 767/767/1132
+f 726/726/1083 727/727/1084 768/768/1133
+f 727/727/1084 728/728/1085 769/769/1134
+f 729/729/1086 770/770/5087 769/769/1134
+f 730/730/1087 771/771/1138 770/770/1200
+f 731/731/1137 772/772/1140 771/771/1138
+f 732/732/1139 773/773/1142 772/772/1140
+f 733/734/1141 774/775/1145 773/773/5088
+f 733/734/1143 734/733/1091 775/774/1144
+f 734/733/1146 735/735/1093 776/776/1147
+f 735/735/1093 736/736/1094 777/777/1148
+f 736/736/1094 737/737/1095 778/778/1149
+f 737/737/1095 738/738/1096 779/779/1150
+f 739/739/1097 780/780/5089 779/779/1150
+f 740/740/1098 781/781/1154 780/780/1214
+f 741/741/1153 782/782/1156 781/781/1154
+f 742/742/1155 783/783/1158 782/782/1156
+f 743/744/1159 784/785/1161 783/783/5090
+f 743/744/1159 744/743/1102 785/784/1160
+f 744/743/1162 745/745/1104 786/786/1163
+f 745/745/1104 746/746/1105 787/787/1164
+f 746/746/1105 747/747/1106 788/788/1165
+f 747/747/1106 748/748/1107 789/789/1166
+f 749/749/1108 790/790/5091 789/789/1166
+f 750/750/1109 791/791/1170 790/790/1229
+f 751/751/1169 792/792/1172 791/791/1170
+f 752/752/1171 793/793/1174 792/792/1172
+f 753/754/1173 794/795/1177 793/793/5092
+f 753/754/1175 754/753/1113 795/794/1176
+f 754/753/1178 755/755/1115 796/796/1179
+f 755/755/1115 756/756/1116 797/797/1180
+f 756/756/1116 757/757/1117 798/798/1181
+f 757/757/1117 758/758/1118 799/799/1182
+f 759/759/1119 800/800/5093 799/799/1182
+f 760/760/1120 762/761/1122 800/800/1186
+f 762/761/1122 801/802/1187 802/801/1245
+f 761/762/1124 803/803/1188 801/802/1187
+f 763/763/1126 804/804/1189 803/803/5094
+f 764/765/1129 805/806/1191 804/804/5095
+f 764/765/1129 765/764/1128 806/805/1193
+f 765/764/1128 766/766/1131 807/807/1254
+f 766/766/1131 767/767/1132 808/808/1194
+f 767/767/1132 768/768/1133 809/809/1198
+f 768/768/5096 769/769/1134 810/810/1197
+f 770/770/1200 811/811/5097 810/810/1197
+f 771/771/1138 812/812/1201 811/811/1263
+f 772/772/1140 813/813/5098 812/812/1201
+f 773/773/1142 814/814/1203 813/813/5099
+f 774/775/1145 815/816/1205 814/814/5100
+f 774/775/1145 775/774/1144 816/815/1204
+f 775/774/1144 776/776/1147 817/817/1271
+f 776/776/1147 777/777/1148 818/818/1208
+f 777/777/1148 778/778/1149 819/819/1210
+f 778/778/1149 779/779/1150 820/820/1212
+f 780/780/1152 821/821/5101 820/820/1212
+f 781/781/1154 822/822/1216 821/821/1279
+f 782/782/1156 823/823/1217 822/822/1216
+f 783/783/1158 824/824/1218 823/823/5102
+f 784/785/1161 825/826/1220 824/824/5103
+f 784/785/1161 785/784/1160 826/825/1222
+f 785/784/1160 786/786/1163 827/827/1287
+f 786/786/1163 787/787/1164 828/828/1223
+f 787/787/1164 788/788/1165 829/829/1227
+f 788/788/5104 789/789/1166 830/830/1226
+f 790/790/1229 831/831/5105 830/830/1226
+f 791/791/1170 832/832/1230 831/831/1296
+f 792/792/1172 833/833/5106 832/832/1230
+f 793/793/1174 834/834/1232 833/833/5107
+f 794/795/1177 835/836/1234 834/834/5108
+f 794/795/1177 795/794/1176 836/835/1233
+f 795/794/1176 796/796/1179 837/837/1304
+f 796/796/1179 797/797/1180 838/838/1237
+f 797/797/1180 798/798/1181 839/839/1239
+f 798/798/1181 799/799/1182 840/840/1241
+f 800/800/1184 802/801/5109 840/840/1241
+f 840/840/1241 802/801/5110 841/841/1243
+f 802/801/5109 801/802/1187 843/843/1248
+f 801/802/1187 803/803/1188 844/844/1250
+f 803/803/1188 804/804/1189 845/845/1249
+f 804/804/1189 805/806/1253 846/846/1252
+f 806/805/1193 847/847/1255 846/846/1251
+f 807/807/1254 848/848/1256 847/847/1255
+f 808/808/1194 849/849/1329 848/848/1323
+f 809/809/5111 850/850/1259 849/849/1329
+f 810/810/1197 851/852/1262 850/850/1259
+f 810/810/1197 811/811/5112 852/851/1261
+f 811/811/5113 812/812/1201 853/853/5114
+f 812/812/1201 813/813/1202 854/854/1267
+f 813/813/1202 814/814/1203 855/855/1266
+f 814/814/1203 815/816/1270 856/856/1269
+f 816/815/1207 857/857/1272 856/856/1268
+f 817/817/1271 858/858/1273 857/857/1272
+f 818/818/1208 859/859/1349 858/858/1342
+f 819/819/5115 860/860/1275 859/859/1349
+f 820/820/1212 861/862/1278 860/860/1275
+f 820/820/1212 821/821/5116 862/861/1277
+f 821/821/5101 822/822/1216 863/863/5117
+f 822/822/1216 823/823/1217 864/864/1283
+f 823/823/1217 824/824/1218 865/865/1282
+f 824/824/1218 825/826/1286 866/866/1285
+f 826/825/1222 867/867/1288 866/866/1284
+f 827/827/1287 868/868/1289 867/867/1288
+f 828/828/1223 869/869/1369 868/868/1363
+f 829/829/5118 870/870/1292 869/869/1369
+f 830/830/1226 871/872/1295 870/870/1292
+f 830/830/1226 831/831/5119 872/871/1294
+f 831/831/5120 832/832/1230 873/873/5121
+f 832/832/1230 833/833/1231 874/874/1300
+f 833/833/1231 834/834/1232 875/875/1299
+f 834/834/1232 835/836/1303 876/876/1302
+f 836/835/1236 877/877/1305 876/876/1301
+f 837/837/1304 878/878/1306 877/877/1305
+f 838/838/1237 879/879/1307 878/878/1382
+f 839/839/5122 880/880/1308 879/879/1389
+f 840/840/1241 842/842/1244 880/880/1308
+f 842/842/1244 881/883/1313 882/881/1310
+f 842/842/1244 841/841/5123 883/882/1312
+f 841/841/1314 843/843/1246 884/884/1315
+f 843/843/1316 844/844/5124 885/885/1317
+f 844/844/1247 845/845/1249 886/886/1319
+f 845/845/1249 846/846/1252 887/887/1320
+f 847/847/1255 888/888/1404 887/887/1322
+f 848/848/1256 889/889/1326 888/888/1324
+f 849/849/1325 890/890/5125 889/889/1326
+f 850/850/1259 891/891/1330 890/890/1328
+f 851/852/1262 892/893/1333 891/891/1330
+f 851/852/1262 852/851/5126 893/892/1332
+f 852/851/1261 853/853/1264 894/894/1334
+f 853/853/1335 854/854/5127 895/895/1336
+f 854/854/1265 855/855/1266 896/896/1338
+f 855/855/1266 856/856/1269 897/897/1339
+f 857/857/1272 898/898/1420 897/897/1341
+f 858/858/1273 899/899/1345 898/898/1343
+f 859/859/1344 900/900/5128 899/899/1345
+f 860/860/1347 901/901/1350 900/900/1348
+f 861/862/1278 902/903/1353 901/901/1350
+f 861/862/1278 862/861/5129 903/902/1352
+f 862/861/1354 863/863/1280 904/904/1355
+f 863/863/1356 864/864/5130 905/905/1357
+f 864/864/1281 865/865/1282 906/906/1359
+f 865/865/1282 866/866/1285 907/907/1360
+f 867/867/1288 908/908/1436 907/907/1362
+f 868/868/1289 909/909/1366 908/908/1364
+f 869/869/1365 910/910/5131 909/909/1366
+f 870/870/1292 911/911/1370 910/910/1368
+f 871/872/1295 912/913/1373 911/911/1370
+f 871/872/1295 872/871/5132 913/912/1372
+f 872/871/1294 873/873/1297 914/914/1374
+f 873/873/1375 874/874/5133 915/915/1376
+f 874/874/1298 875/875/1299 916/916/1378
+f 875/875/1299 876/876/1302 917/917/1379
+f 877/877/1305 918/918/1452 917/917/1381
+f 878/878/1306 919/919/1385 918/918/1383
+f 879/879/1384 920/920/5134 919/919/1385
+f 880/880/1387 882/881/1310 920/920/1388
+f 882/881/1310 921/922/1392 922/921/1454
+f 881/883/1313 923/924/1394 921/922/1392
+f 881/883/1313 883/882/5135 924/923/1393
+f 883/882/5135 884/884/1315 925/925/1397
+f 884/884/1315 885/885/1317 926/926/1464
+f 885/885/1317 886/886/1321 927/927/1398
+f 886/886/1400 887/887/1322 928/928/1401
+f 888/888/1402 929/929/1403 928/928/1401
+f 889/889/1326 930/930/1472 929/929/1403
+f 890/890/1328 931/931/1407 930/930/5136
+f 891/891/1330 932/932/1408 931/931/1470
+f 892/893/1333 933/934/1410 932/932/1408
+f 892/893/1333 893/892/5137 934/933/1409
+f 893/892/5137 894/894/1334 935/935/1413
+f 894/894/1334 895/895/1336 936/936/5138
+f 895/895/1336 896/896/1340 937/937/1414
+f 896/896/1416 897/897/1339 938/938/1417
+f 898/898/1418 939/939/1419 938/938/1417
+f 899/899/1345 940/940/1488 939/939/1419
+f 900/900/1348 941/941/1423 940/940/5139
+f 901/901/1350 942/942/1424 941/941/1486
+f 902/903/1353 943/944/1426 942/942/1424
+f 902/903/1353 903/902/5140 944/943/1425
+f 903/902/5140 904/904/1355 945/945/1429
+f 904/904/1355 905/905/1357 946/946/1496
+f 905/905/1357 906/906/1361 947/947/1430
+f 906/906/1432 907/907/1362 948/948/1433
+f 908/908/1434 949/949/1435 948/948/1433
+f 909/909/1366 950/950/1505 949/949/1435
+f 910/910/1368 951/951/1439 950/950/5141
+f 911/911/1370 952/952/1440 951/951/1503
+f 912/913/1373 953/954/1442 952/952/1440
+f 912/913/1373 913/912/5142 954/953/1441
+f 913/912/5142 914/914/1374 955/955/1445
+f 914/914/1374 915/915/1376 956/956/5143
+f 915/915/1376 916/916/1380 957/957/1446
+f 916/916/1448 917/917/1379 958/958/1449
+f 918/918/1450 959/959/1451 958/958/1449
+f 919/919/1385 960/960/1456 959/959/1451
+f 920/920/1388 922/921/1391 960/960/5144
+f 922/921/1454 961/962/5145 962/961/1455
+f 921/922/1392 963/963/1459 961/962/5145
+f 923/924/1458 964/965/1461 963/963/1459
+f 923/924/1458 924/923/1393 965/964/1460
+f 924/923/1393 925/925/1397 966/966/1528
+f 925/925/1395 926/926/1399 967/967/1463
+f 926/926/1464 927/927/5146 968/968/1465
+f 927/927/1398 928/928/1401 969/969/1534
+f 929/929/1403 970/970/1468 969/969/1466
+f 930/930/5136 971/971/1471 970/970/1468
+f 931/931/1470 972/972/5147 971/971/5148
+f 932/932/1408 973/973/1475 972/972/5147
+f 933/934/1474 974/975/1477 973/973/1475
+f 933/934/1474 934/933/1409 975/974/1476
+f 934/933/1409 935/935/1413 976/976/1543
+f 935/935/1411 936/936/1415 977/977/1481
+f 936/936/5138 937/937/5149 978/978/1480
+f 937/937/1414 938/938/1417 979/979/1549
+f 939/939/1419 980/980/1484 979/979/1482
+f 940/940/1421 981/981/1487 980/980/1484
+f 941/941/1486 982/982/5150 981/981/5151
+f 942/942/1424 983/983/1491 982/982/5150
+f 943/944/1490 984/985/1493 983/983/1491
+f 943/944/1490 944/943/1425 985/984/1492
+f 944/943/1425 945/945/1429 986/986/1559
+f 945/945/1427 946/946/1431 987/987/1498
+f 946/946/1496 947/947/5152 988/988/1497
+f 947/947/1430 948/948/1433 989/989/1565
+f 949/949/1435 990/990/1501 989/989/1499
+f 950/950/5141 991/991/1504 990/990/1501
+f 951/951/1503 992/992/5153 991/991/5154
+f 952/952/1440 993/993/1508 992/992/5153
+f 953/954/1507 994/995/1510 993/993/1508
+f 953/954/1507 954/953/1441 995/994/1509
+f 954/953/1441 955/955/1445 996/996/1574
+f 955/955/1443 956/956/1447 997/997/1514
+f 956/956/5143 957/957/5155 998/998/1513
+f 957/957/1446 958/958/1449 999/999/1580
+f 959/959/1451 1000/1000/1517 999/999/1515
+f 960/960/1453 962/961/1455 1000/1000/1517
+f 962/961/1455 1001/1002/1584 1002/1001/1519
+f 961/962/1457 1003/1003/1522 1001/1002/1584
+f 963/963/1459 1004/1004/1524 1003/1003/1522
+f 964/965/1523 1005/1006/1526 1004/1004/1524
+f 964/965/1523 965/964/1460 1006/1005/1525
+f 965/964/1460 966/966/1528 1007/1007/1527
+f 966/966/1528 967/967/5156 1008/1008/1592
+f 967/967/1463 968/968/1465 1009/1009/1530
+f 968/968/5157 969/969/1467 1010/1010/1532
+f 970/970/1533 1011/1011/1535 1010/1010/1532
+f 971/971/1471 1012/1012/1598 1011/1011/1535
+f 972/972/1473 1013/1013/1538 1012/1012/1598
+f 973/973/1475 1014/1014/1539 1013/1013/1538
+f 974/975/1477 1015/1016/1541 1014/1014/1539
+f 974/975/1477 975/974/1476 1016/1015/1540
+f 975/974/1476 976/976/1543 1017/1017/1542
+f 976/976/1543 977/977/5158 1018/1018/5159
+f 977/977/1481 978/978/1480 1019/1019/1545
+f 978/978/5160 979/979/1483 1020/1020/1547
+f 980/980/5161 1021/1021/1550 1020/1020/1547
+f 981/981/1487 1022/1022/1611 1021/1021/1550
+f 982/982/1489 1023/1023/1553 1022/1022/1611
+f 983/983/1491 1024/1024/1555 1023/1023/1553
+f 984/985/1554 1025/1026/1557 1024/1024/1555
+f 984/985/1554 985/984/1492 1026/1025/1556
+f 985/984/1492 986/986/1559 1027/1027/1558
+f 986/986/1559 987/987/5162 1028/1028/1619
+f 987/987/1498 988/988/1497 1029/1029/1561
+f 988/988/5163 989/989/1500 1030/1030/1563
+f 990/990/5164 1031/1031/1566 1030/1030/1563
+f 991/991/1504 1032/1032/1625 1031/1031/1566
+f 992/992/1506 1033/1033/1569 1032/1032/1625
+f 993/993/1508 1034/1034/1570 1033/1033/1569
+f 994/995/1510 1035/1036/1572 1034/1034/1570
+f 994/995/1510 995/994/1509 1036/1035/1571
+f 995/994/1509 996/996/1574 1037/1037/1573
+f 996/996/1574 997/997/5165 1038/1038/5166
+f 997/997/1514 998/998/1513 1039/1039/1576
+f 998/998/5167 999/999/1516 1040/1040/1578
+f 1000/1000/5168 1002/1001/1519 1040/1040/1578
+f 1002/1001/1519 1041/1042/5169 1042/1041/1581
+f 1001/1002/1520 1043/1043/1583 1041/1042/1582
+f 1003/1003/1522 1044/1044/1586 1043/1043/1583
+f 1004/1004/1524 1045/1045/1587 1044/1044/1586
+f 1005/1006/1526 1046/1047/1589 1045/1045/1587
+f 1005/1006/1526 1006/1005/1525 1047/1046/1588
+f 1006/1005/5170 1007/1007/1527 1048/1048/1590
+f 1007/1007/1527 1008/1008/1529 1049/1049/1591
+f 1008/1008/1592 1009/1009/1530 1050/1050/1593
+f 1009/1009/1530 1010/1010/1532 1051/1051/1594
+f 1011/1011/1535 1052/1052/5171 1051/1051/1594
+f 1012/1012/1598 1053/1053/1597 1052/1052/1596
+f 1013/1013/1538 1054/1054/1600 1053/1053/1597
+f 1014/1014/1539 1055/1055/1601 1054/1054/1600
+f 1015/1016/1541 1056/1057/1603 1055/1055/1601
+f 1015/1016/1541 1016/1015/1540 1057/1056/1602
+f 1016/1015/5172 1017/1017/1542 1058/1058/1604
+f 1017/1017/1542 1018/1018/1544 1059/1059/1605
+f 1018/1018/5159 1019/1019/1545 1060/1060/1606
+f 1019/1019/1545 1020/1020/1547 1061/1061/1607
+f 1021/1021/1550 1062/1062/5173 1061/1061/1607
+f 1022/1022/1551 1063/1063/1610 1062/1062/1609
+f 1023/1023/1553 1064/1064/1613 1063/1063/1610
+f 1024/1024/1555 1065/1065/1614 1064/1064/1613
+f 1025/1026/1557 1066/1067/1616 1065/1065/1614
+f 1025/1026/1557 1026/1025/1556 1067/1066/1615
+f 1026/1025/5174 1027/1027/1558 1068/1068/1617
+f 1027/1027/1558 1028/1028/1560 1069/1069/1618
+f 1028/1028/1619 1029/1029/1561 1070/1070/1620
+f 1029/1029/1561 1030/1030/1563 1071/1071/1621
+f 1031/1031/1566 1072/1072/5175 1071/1071/1621
+f 1032/1032/1625 1073/1073/1624 1072/1072/1623
+f 1033/1033/1569 1074/1074/1627 1073/1073/1624
+f 1034/1034/1570 1075/1075/1628 1074/1074/1627
+f 1035/1036/1572 1076/1077/1630 1075/1075/1628
+f 1035/1036/1572 1036/1035/1571 1077/1076/1629
+f 1036/1035/5176 1037/1037/1573 1078/1078/1631
+f 1037/1037/1573 1038/1038/1575 1079/1079/1632
+f 1038/1038/5166 1039/1039/1576 1080/1080/1633
+f 1039/1039/1576 1040/1040/1578 1042/1041/1581
+f 1080/1080/1633 1042/1041/1638 1081/1081/1635
+f 1041/1042/1582 1083/1083/1640 1081/1081/1637
+f 1043/1043/1639 1084/1084/1641 1083/1083/1640
+f 1044/1044/1586 1085/1085/1642 1084/1084/1641
+f 1045/1045/1587 1086/1086/1643 1085/1085/1700
+f 1046/1047/1589 1087/1088/1645 1086/1086/1643
+f 1046/1047/1589 1047/1046/1588 1088/1087/1644
+f 1047/1046/1588 1048/1048/1590 1089/1089/1648
+f 1048/1048/1590 1049/1049/1591 1090/1090/1647
+f 1049/1049/1649 1050/1050/1593 1091/1091/1650
+f 1050/1050/1593 1051/1051/1653 1092/1092/1651
+f 1052/1052/1596 1093/1093/1655 1092/1092/1652
+f 1053/1053/1654 1094/1094/1656 1093/1093/1655
+f 1054/1054/1600 1095/1095/1657 1094/1094/1656
+f 1055/1055/1601 1096/1096/1658 1095/1095/1714
+f 1056/1057/1603 1097/1098/1660 1096/1096/1658
+f 1056/1057/1603 1057/1056/1602 1098/1097/1659
+f 1057/1056/1602 1058/1058/1604 1099/1099/1663
+f 1058/1058/1604 1059/1059/1605 1100/1100/1662
+f 1059/1059/1664 1060/1060/1606 1101/1101/1665
+f 1060/1060/1606 1061/1061/1668 1102/1102/1666
+f 1062/1062/1609 1103/1103/1670 1102/1102/1667
+f 1063/1063/1669 1104/1104/1671 1103/1103/1670
+f 1064/1064/1613 1105/1105/1672 1104/1104/1671
+f 1065/1065/1614 1106/1106/1673 1105/1105/1728
+f 1066/1067/1616 1107/1108/1675 1106/1106/1673
+f 1066/1067/1616 1067/1066/1615 1108/1107/1674
+f 1067/1066/1615 1068/1068/1617 1109/1109/1678
+f 1068/1068/1617 1069/1069/1618 1110/1110/1677
+f 1069/1069/1679 1070/1070/1620 1111/1111/1680
+f 1070/1070/1620 1071/1071/1683 1112/1112/1681
+f 1072/1072/1623 1113/1113/1685 1112/1112/1682
+f 1073/1073/1684 1114/1114/1686 1113/1113/1685
+f 1074/1074/1627 1115/1115/1687 1114/1114/1686
+f 1075/1075/1628 1116/1116/1688 1115/1115/1742
+f 1076/1077/1630 1117/1118/1690 1116/1116/1688
+f 1076/1077/1630 1077/1076/1629 1118/1117/1689
+f 1077/1076/1629 1078/1078/1631 1119/1119/1693
+f 1078/1078/1631 1079/1079/1632 1120/1120/1692
+f 1079/1079/1694 1080/1080/1633 1082/1082/1636
+f 1120/1120/1692 1082/1082/1636 1121/1121/1695
+f 1082/1082/1636 1081/1081/1698 1123/1123/1697
+f 1083/1083/1640 1124/1124/1699 1123/1123/1697
+f 1084/1084/1641 1125/1125/1701 1124/1124/1699
+f 1085/1085/1700 1126/1126/1757 1125/1125/1701
+f 1086/1086/1704 1127/1127/1703 1126/1126/1757
+f 1087/1088/1645 1128/1129/1706 1127/1127/1703
+f 1087/1088/1645 1088/1087/1707 1129/1128/1705
+f 1088/1087/1644 1089/1089/1646 1130/1130/5177
+f 1089/1089/1646 1090/1090/1647 1131/1131/1709
+f 1090/1090/1647 1091/1091/1650 1132/1132/1710
+f 1091/1091/1650 1092/1092/1712 1133/1133/1711
+f 1093/1093/1655 1134/1134/1713 1133/1133/1711
+f 1094/1094/1656 1135/1135/1715 1134/1134/1713
+f 1095/1095/1714 1136/1136/1770 1135/1135/1715
+f 1096/1096/1718 1137/1137/1717 1136/1136/1770
+f 1097/1098/1660 1138/1139/1720 1137/1137/1717
+f 1097/1098/1660 1098/1097/1721 1139/1138/1719
+f 1098/1097/1659 1099/1099/1661 1140/1140/5178
+f 1099/1099/1661 1100/1100/1662 1141/1141/1723
+f 1100/1100/1662 1101/1101/1665 1142/1142/1724
+f 1101/1101/1665 1102/1102/1726 1143/1143/1725
+f 1103/1103/1670 1144/1144/1727 1143/1143/1725
+f 1104/1104/1671 1145/1145/1729 1144/1144/1727
+f 1105/1105/1728 1146/1146/1783 1145/1145/1729
+f 1106/1106/1732 1147/1147/1731 1146/1146/1783
+f 1107/1108/1675 1148/1149/1734 1147/1147/1731
+f 1107/1108/1675 1108/1107/1735 1149/1148/1733
+f 1108/1107/1674 1109/1109/1676 1150/1150/5179
+f 1109/1109/1676 1110/1110/1677 1151/1151/1737
+f 1110/1110/1677 1111/1111/1680 1152/1152/1738
+f 1111/1111/1680 1112/1112/1740 1153/1153/1739
+f 1113/1113/1685 1154/1154/1741 1153/1153/1739
+f 1114/1114/1686 1155/1155/1743 1154/1154/1741
+f 1115/1115/1742 1156/1156/1796 1155/1155/1743
+f 1116/1116/1746 1157/1157/1745 1156/1156/1796
+f 1117/1118/1690 1158/1159/1748 1157/1157/1745
+f 1117/1118/1690 1118/1117/1749 1159/1158/1747
+f 1118/1117/1689 1119/1119/1691 1160/1160/5180
+f 1119/1119/1691 1120/1120/1692 1122/1122/1696
+f 1160/1160/1750 1122/1122/1696 1161/1161/1751
+f 1122/1122/1696 1121/1121/1695 1163/1163/1753
+f 1121/1121/1695 1123/1123/1697 1164/1164/1755
+f 1124/1124/1699 1165/1165/1756 1164/1164/5181
+f 1125/1125/1701 1166/1166/1758 1165/1165/1756
+f 1126/1126/1757 1167/1167/1759 1166/1166/5182
+f 1127/1127/1703 1168/1168/1815 1167/1167/1759
+f 1128/1129/1706 1169/1170/1762 1168/1168/1760
+f 1128/1129/1706 1129/1128/1705 1170/1169/1761
+f 1129/1128/1705 1130/1130/1708 1171/1171/1763
+f 1130/1130/1708 1131/1131/1709 1172/1172/1765
+f 1131/1131/1709 1132/1132/1710 1173/1173/1766
+f 1132/1132/1710 1133/1133/1711 1174/1174/1768
+f 1134/1134/1713 1175/1175/1769 1174/1174/5183
+f 1135/1135/1715 1176/1176/1771 1175/1175/1769
+f 1136/1136/1770 1177/1177/1772 1176/1176/1771
+f 1137/1137/1717 1178/1178/1829 1177/1177/1772
+f 1138/1139/1720 1179/1180/1775 1178/1178/1773
+f 1138/1139/1720 1139/1138/1719 1180/1179/1774
+f 1139/1138/1719 1140/1140/1722 1181/1181/1776
+f 1140/1140/1722 1141/1141/1723 1182/1182/1778
+f 1141/1141/1723 1142/1142/1724 1183/1183/1779
+f 1142/1142/1724 1143/1143/1725 1184/1184/1781
+f 1144/1144/1727 1185/1185/1782 1184/1184/5184
+f 1145/1145/1729 1186/1186/1784 1185/1185/1782
+f 1146/1146/1783 1187/1187/1785 1186/1186/5185
+f 1147/1147/1731 1188/1188/1843 1187/1187/1785
+f 1148/1149/1734 1189/1190/1788 1188/1188/1786
+f 1148/1149/1734 1149/1148/1733 1190/1189/1787
+f 1149/1148/1733 1150/1150/1736 1191/1191/1789
+f 1150/1150/1736 1151/1151/1737 1192/1192/1791
+f 1151/1151/1737 1152/1152/1738 1193/1193/1792
+f 1152/1152/1738 1153/1153/1739 1194/1194/1794
+f 1154/1154/1741 1195/1195/1795 1194/1194/5186
+f 1155/1155/1743 1196/1196/1797 1195/1195/1795
+f 1156/1156/1796 1197/1197/1798 1196/1196/1797
+f 1157/1157/1745 1198/1198/1857 1197/1197/1798
+f 1158/1159/1748 1199/1200/1801 1198/1198/1799
+f 1158/1159/1748 1159/1158/1747 1200/1199/1800
+f 1159/1158/1747 1160/1160/1750 1162/1162/1752
+f 1200/1199/5187 1162/1162/1752 1201/1201/5188
+f 1162/1162/1752 1161/1161/1751 1203/1203/1805
+f 1161/1161/1751 1163/1163/1753 1204/1204/5189
+f 1163/1163/1753 1164/1164/1755 1205/1205/1809
+f 1165/1165/5190 1206/1206/1810 1205/1205/1809
+f 1166/1166/1758 1207/1207/1811 1206/1206/1810
+f 1167/1167/1759 1208/1208/5191 1207/1207/1811
+f 1168/1168/1815 1209/1209/1814 1208/1208/5192
+f 1169/1170/1762 1210/1211/1817 1209/1209/1814
+f 1169/1170/1762 1170/1169/1764 1211/1210/1816
+f 1170/1169/5193 1171/1171/1763 1212/1212/5194
+f 1171/1171/1763 1172/1172/1765 1213/1213/1819
+f 1172/1172/1765 1173/1173/1766 1214/1214/1821
+f 1173/1173/1766 1174/1174/1768 1215/1215/1823
+f 1175/1175/1769 1216/1216/5195 1215/1215/1823
+f 1176/1176/5196 1217/1217/1825 1216/1216/5195
+f 1177/1177/1772 1218/1218/5197 1217/1217/1825
+f 1178/1178/1829 1219/1219/1828 1218/1218/5198
+f 1179/1180/1775 1220/1221/1831 1219/1219/1828
+f 1179/1180/1775 1180/1179/1777 1221/1220/1830
+f 1180/1179/5199 1181/1181/1776 1222/1222/5200
+f 1181/1181/1776 1182/1182/1778 1223/1223/1833
+f 1182/1182/1778 1183/1183/1779 1224/1224/5201
+f 1183/1183/1779 1184/1184/1781 1225/1225/1837
+f 1185/1185/5202 1226/1226/1838 1225/1225/1837
+f 1186/1186/1784 1227/1227/1839 1226/1226/1838
+f 1187/1187/1785 1228/1228/5203 1227/1227/1839
+f 1188/1188/1843 1229/1229/1842 1228/1228/5204
+f 1189/1190/1788 1230/1231/1845 1229/1229/1842
+f 1189/1190/1788 1190/1189/1790 1231/1230/1844
+f 1190/1189/5205 1191/1191/1789 1232/1232/5206
+f 1191/1191/1789 1192/1192/1791 1233/1233/1847
+f 1192/1192/1791 1193/1193/1792 1234/1234/1849
+f 1193/1193/1792 1194/1194/1794 1235/1235/1851
+f 1195/1195/1795 1236/1236/5207 1235/1235/1851
+f 1196/1196/5208 1237/1237/1853 1236/1236/5207
+f 1197/1197/1798 1238/1238/5209 1237/1237/1853
+f 1198/1198/1857 1239/1239/1856 1238/1238/5210
+f 1199/1200/1801 1240/1240/1858 1239/1239/1856
+f 1199/1200/1801 1200/1199/1802 1202/1202/1804
+f 1241/1241/1859 1242/1243/2420 1240/1240/1860
+f 1243/1242/1861 1244/1245/1864 1242/1243/1862
+f 1245/1244/1863 1246/1247/1866 1244/1245/1864
+f 1247/1246/5211 1248/1249/1868 1246/1247/2419
+f 1249/1248/1867 1250/1251/1870 1248/1249/1868
+f 1251/1250/1869 1252/1253/1872 1250/1251/1870
+f 1253/1252/1871 1254/1255/1875 1252/1253/1872
+f 1255/1254/1874 1256/1257/1877 1254/1255/1875
+f 1257/1256/1876 1258/1259/1879 1256/1257/1877
+f 1297/1297/1924 1257/1256/1876 1255/1254/1874
+f 1298/1298/1926 1255/1254/1874 1253/1252/1871
+f 1299/1299/1927 1253/1252/1871 1251/1250/1869
+f 1300/1300/1928 1251/1250/1869 1249/1248/1867
+f 1301/1301/1929 1249/1248/1867 1247/1246/1865
+f 1302/1302/1930 1247/1246/1865 1245/1244/1863
+f 1303/1303/1932 1245/1244/1863 1243/1242/1861
+f 1304/1304/5212 1243/1242/1861 1241/1241/1935
+f 1305/1305/1940 1241/1241/1859 1202/1202/1804
+f 1306/1306/1966 1305/1305/1936 1201/1201/5188
+f 1307/1307/1939 1304/1304/5212 1305/1305/1940
+f 1308/1308/1942 1303/1303/1932 1304/1304/1933
+f 1309/1309/1945 1302/1302/1930 1303/1303/1931
+f 1310/1310/1947 1301/1301/1929 1302/1302/1930
+f 1311/1311/1949 1300/1300/1928 1301/1301/1929
+f 1312/1312/1950 1299/1299/1927 1300/1300/1951
+f 1313/1313/1952 1298/1298/1926 1299/1299/1927
+f 1296/1296/1923 1297/1297/1924 1298/1298/1926
+f 1295/1295/1922 1296/1296/1925 1313/1313/1953
+f 1314/1314/1955 1313/1313/1952 1312/1312/1950
+f 1315/1315/1956 1312/1312/1950 1311/1311/1949
+f 1316/1316/1958 1311/1311/1949 1310/1310/1947
+f 1317/1317/5213 1310/1310/1947 1309/1309/1945
+f 1318/1318/1962 1309/1309/1945 1308/1308/1942
+f 1319/1319/1963 1308/1308/1942 1307/1307/5214
+f 1320/1320/1965 1307/1307/5214 1306/1306/1938
+f 1321/1321/1971 1306/1306/1966 1203/1203/1805
+f 1322/1322/1969 1321/1321/1967 1204/1204/5189
+f 1323/1323/1995 1320/1320/1965 1321/1321/1971
+f 1324/1324/1974 1319/1319/1963 1320/1320/1964
+f 1325/1325/1973 1318/1318/5215 1319/1319/1963
+f 1326/1326/1975 1317/1317/5216 1318/1318/1976
+f 1327/1327/1977 1316/1316/1957 1317/1317/1978
+f 1328/1328/1979 1315/1315/1956 1316/1316/1957
+f 1329/1329/1981 1314/1314/1955 1315/1315/1956
+f 1294/1294/1982 1295/1295/5217 1314/1314/1954
+f 1294/1294/1983 1329/1329/1981 1330/1330/1984
+f 1329/1329/1981 1328/1328/1979 1331/1331/1986
+f 1328/1328/1979 1327/1327/1977 1332/1332/1988
+f 1327/1327/1977 1326/1326/1975 1333/1333/1989
+f 1326/1326/1975 1325/1325/1973 1334/1334/5218
+f 1325/1325/1973 1324/1324/1974 1335/1335/1992
+f 1324/1324/1972 1323/1323/1970 1336/1336/1994
+f 1323/1323/1970 1322/1322/1969 1337/1337/1996
+f 1322/1322/1969 1205/1205/1809 1206/1206/1810
+f 1337/1337/1999 1206/1206/1810 1207/1207/1811
+f 1336/1336/1997 1337/1337/1999 1338/1338/2000
+f 1335/1335/1992 1336/1336/1994 1339/1339/5219
+f 1334/1334/2004 1335/1335/1992 1340/1340/5220
+f 1333/1333/5221 1334/1334/1990 1341/1341/2005
+f 1332/1332/2008 1333/1333/5222 1342/1342/2007
+f 1331/1331/1986 1332/1332/1988 1343/1343/2009
+f 1330/1330/1987 1331/1331/1986 1344/1344/2010
+f 1293/1293/1919 1330/1330/1984 1345/1345/2012
+f 1292/1292/1920 1345/1345/2011 1346/1346/2013
+f 1345/1345/2011 1344/1344/2010 1347/1347/2014
+f 1344/1344/2010 1343/1343/2009 1348/1348/2015
+f 1343/1343/2009 1342/1342/2007 1349/1349/2016
+f 1342/1342/2007 1341/1341/5223 1350/1350/2018
+f 1341/1341/2005 1340/1340/5220 1351/1351/2021
+f 1340/1340/2003 1339/1339/2022 1352/1352/2027
+f 1339/1339/2022 1338/1338/2001 1353/1353/2023
+f 1338/1338/2001 1207/1207/1811 1208/1208/2024
+f 1353/1353/2025 1208/1208/2024 1209/1209/1814
+f 1352/1352/2027 1353/1353/2025 1354/1354/2028
+f 1351/1351/2021 1352/1352/2020 1355/1355/2029
+f 1350/1350/2018 1351/1351/2019 1356/1356/2030
+f 1349/1349/2016 1350/1350/2018 1357/1357/2031
+f 1348/1348/2017 1349/1349/2016 1358/1358/2032
+f 1347/1347/2014 1348/1348/2017 1359/1359/5224
+f 1346/1346/2013 1347/1347/2014 1360/1360/2034
+f 1291/1291/1917 1346/1346/2013 1361/1361/2035
+f 1290/1290/1916 1361/1361/2035 1362/1362/2036
+f 1361/1361/2035 1360/1360/2034 1363/1363/2037
+f 1360/1360/2034 1359/1359/5224 1364/1364/2038
+f 1359/1359/2033 1358/1358/2032 1365/1365/2039
+f 1358/1358/2032 1357/1357/2031 1366/1366/2040
+f 1357/1357/2031 1356/1356/2030 1367/1367/2044
+f 1356/1356/2030 1355/1355/2029 1368/1368/2043
+f 1355/1355/2029 1354/1354/2026 1369/1369/2045
+f 1354/1354/2026 1209/1209/1814 1210/1211/2046
+f 1370/1370/2048 1369/1369/2047 1210/1211/2046
+f 1371/1371/2049 1368/1368/2043 1369/1369/2045
+f 1372/1372/2050 1367/1367/2044 1368/1368/2043
+f 1373/1373/5225 1366/1366/2040 1367/1367/2042
+f 1374/1374/2052 1365/1365/2039 1366/1366/2040
+f 1375/1375/2053 1364/1364/2038 1365/1365/2039
+f 1376/1376/2054 1363/1363/2037 1364/1364/2038
+f 1377/1377/2056 1362/1362/2036 1363/1363/2037
+f 1288/1288/1914 1289/1289/1915 1362/1362/2036
+f 1287/1287/1912 1288/1288/1914 1377/1377/2056
+f 1378/1378/2057 1377/1377/2056 1376/1376/2054
+f 1379/1379/2058 1376/1376/2054 1375/1375/2053
+f 1380/1380/2059 1375/1375/2053 1374/1374/2052
+f 1381/1381/2060 1374/1374/2052 1373/1373/2051
+f 1382/1382/2061 1373/1373/2051 1372/1372/2050
+f 1383/1383/2062 1372/1372/2050 1371/1371/2049
+f 1384/1384/2065 1371/1371/2049 1370/1370/2066
+f 1385/1385/2071 1370/1370/2048 1211/1210/1816
+f 1386/1386/2095 1385/1385/2067 1212/1212/2068
+f 1387/1387/2070 1384/1384/2064 1385/1385/2071
+f 1388/1388/2072 1383/1383/2063 1384/1384/2064
+f 1389/1389/2073 1382/1382/2061 1383/1383/2062
+f 1390/1390/2075 1381/1381/2060 1382/1382/2061
+f 1391/1391/2077 1380/1380/2059 1381/1381/2060
+f 1392/1392/2078 1379/1379/2058 1380/1380/2079
+f 1393/1393/2080 1378/1378/2057 1379/1379/2058
+f 1286/1286/1911 1287/1287/1912 1378/1378/2057
+f 1285/1285/1910 1286/1286/1913 1393/1393/2081
+f 1394/1394/2083 1393/1393/2080 1392/1392/2078
+f 1395/1395/2084 1392/1392/2078 1391/1391/2077
+f 1396/1396/2086 1391/1391/2077 1390/1390/2075
+f 1397/1397/5226 1390/1390/2075 1389/1389/2073
+f 1398/1398/2090 1389/1389/2073 1388/1388/2072
+f 1399/1399/2091 1388/1388/2072 1387/1387/5227
+f 1400/1400/2094 1387/1387/5227 1386/1386/2069
+f 1401/1401/2099 1386/1386/2095 1213/1213/1819
+f 1402/1402/2097 1401/1401/2096 1214/1214/5228
+f 1403/1403/2122 1400/1400/2094 1401/1401/2099
+f 1404/1404/2102 1399/1399/2091 1400/1400/2093
+f 1405/1405/2101 1398/1398/5229 1399/1399/2091
+f 1406/1406/2103 1397/1397/5230 1398/1398/2089
+f 1407/1407/2105 1396/1396/2085 1397/1397/2106
+f 1408/1408/2107 1395/1395/2084 1396/1396/2085
+f 1409/1409/2108 1394/1394/2083 1395/1395/2084
+f 1284/1284/2109 1285/1285/5231 1394/1394/2082
+f 1284/1284/2110 1409/1409/2108 1410/1410/2111
+f 1409/1409/2108 1408/1408/2107 1411/1411/2113
+f 1408/1408/2107 1407/1407/5232 1412/1412/2115
+f 1407/1407/2105 1406/1406/2103 1413/1413/2116
+f 1406/1406/2103 1405/1405/2101 1414/1414/2117
+f 1405/1405/2101 1404/1404/2102 1415/1415/2119
+f 1404/1404/2100 1403/1403/2098 1416/1416/2121
+f 1403/1403/2098 1402/1402/2097 1417/1417/2123
+f 1402/1402/2097 1215/1215/1823 1216/1216/5195
+f 1417/1417/2125 1216/1216/5195 1217/1217/5233
+f 1416/1416/2124 1417/1417/2125 1418/1418/2126
+f 1415/1415/2119 1416/1416/2121 1419/1419/5234
+f 1414/1414/2130 1415/1415/2119 1420/1420/5235
+f 1413/1413/5236 1414/1414/5237 1421/1421/2131
+f 1412/1412/2134 1413/1413/5238 1422/1422/2133
+f 1411/1411/2113 1412/1412/2115 1423/1423/2135
+f 1410/1410/2114 1411/1411/2113 1424/1424/2136
+f 1283/1283/1907 1410/1410/2111 1425/1425/2138
+f 1282/1282/1908 1425/1425/2138 1426/1426/2139
+f 1425/1425/2138 1424/1424/2136 1427/1427/2140
+f 1424/1424/2136 1423/1423/2135 1428/1428/2141
+f 1423/1423/2135 1422/1422/2133 1429/1429/2142
+f 1422/1422/2133 1421/1421/5239 1430/1430/2144
+f 1421/1421/2131 1420/1420/5235 1431/1431/2147
+f 1420/1420/2129 1419/1419/5234 1432/1432/5240
+f 1419/1419/2148 1418/1418/5241 1433/1433/2149
+f 1418/1418/2127 1217/1217/1825 1218/1218/5242
+f 1433/1433/2151 1218/1218/5242 1219/1219/1828
+f 1432/1432/5243 1433/1433/2151 1434/1434/2153
+f 1431/1431/2147 1432/1432/2146 1435/1435/2154
+f 1430/1430/2144 1431/1431/2145 1436/1436/2155
+f 1429/1429/2142 1430/1430/2144 1437/1437/2156
+f 1428/1428/2143 1429/1429/2142 1438/1438/2157
+f 1427/1427/2140 1428/1428/2143 1439/1439/5244
+f 1426/1426/2139 1427/1427/2140 1440/1440/2159
+f 1281/1281/1905 1426/1426/2139 1441/1441/2160
+f 1280/1280/1904 1441/1441/2160 1442/1442/2161
+f 1441/1441/2160 1440/1440/2159 1443/1443/2162
+f 1440/1440/2159 1439/1439/5244 1444/1444/2163
+f 1439/1439/2158 1438/1438/2157 1445/1445/2164
+f 1438/1438/2157 1437/1437/2156 1446/1446/2165
+f 1437/1437/2156 1436/1436/2155 1447/1447/2169
+f 1436/1436/2155 1435/1435/2154 1448/1448/2168
+f 1435/1435/2154 1434/1434/2152 1449/1449/2170
+f 1434/1434/2152 1219/1219/1828 1220/1221/2171
+f 1450/1450/2173 1449/1449/2172 1220/1221/2171
+f 1451/1451/2174 1448/1448/2168 1449/1449/2170
+f 1452/1452/2175 1447/1447/2169 1448/1448/2168
+f 1453/1453/5245 1446/1446/2165 1447/1447/2167
+f 1454/1454/2177 1445/1445/2164 1446/1446/2165
+f 1455/1455/2178 1444/1444/2163 1445/1445/2164
+f 1456/1456/2179 1443/1443/2162 1444/1444/2163
+f 1457/1457/2181 1442/1442/2161 1443/1443/2162
+f 1278/1278/1902 1279/1279/1903 1442/1442/2161
+f 1277/1277/1900 1278/1278/1902 1457/1457/2181
+f 1458/1458/2182 1457/1457/2181 1456/1456/2179
+f 1459/1459/2183 1456/1456/2179 1455/1455/2178
+f 1460/1460/2184 1455/1455/2178 1454/1454/2177
+f 1461/1461/2185 1454/1454/2177 1453/1453/2176
+f 1462/1462/2186 1453/1453/2176 1452/1452/2175
+f 1463/1463/2188 1452/1452/2175 1451/1451/2174
+f 1464/1464/5246 1451/1451/2174 1450/1450/2191
+f 1465/1465/2196 1450/1450/2173 1221/1220/1830
+f 1466/1466/2222 1465/1465/2192 1222/1222/5200
+f 1467/1467/2195 1464/1464/5246 1465/1465/2196
+f 1468/1468/2198 1463/1463/2188 1464/1464/2189
+f 1469/1469/2201 1462/1462/2186 1463/1463/2187
+f 1470/1470/2203 1461/1461/2185 1462/1462/2186
+f 1471/1471/2205 1460/1460/2184 1461/1461/2185
+f 1472/1472/2206 1459/1459/2183 1460/1460/2207
+f 1473/1473/2208 1458/1458/2182 1459/1459/2183
+f 1276/1276/1899 1277/1277/1900 1458/1458/2182
+f 1275/1275/1898 1276/1276/1901 1473/1473/2209
+f 1474/1474/2211 1473/1473/2208 1472/1472/2206
+f 1475/1475/2212 1472/1472/2206 1471/1471/2205
+f 1476/1476/2214 1471/1471/2205 1470/1470/2203
+f 1477/1477/5247 1470/1470/2203 1469/1469/2201
+f 1478/1478/2218 1469/1469/2201 1468/1468/2198
+f 1479/1479/2219 1468/1468/2198 1467/1467/5248
+f 1480/1480/2221 1467/1467/5248 1466/1466/2194
+f 1481/1481/2227 1466/1466/2222 1223/1223/1833
+f 1482/1482/2225 1481/1481/2223 1224/1224/5201
+f 1483/1483/2251 1480/1480/2221 1481/1481/2227
+f 1484/1484/2230 1479/1479/2219 1480/1480/2220
+f 1485/1485/2229 1478/1478/5249 1479/1479/2219
+f 1486/1486/2231 1477/1477/5250 1478/1478/2217
+f 1487/1487/2233 1476/1476/2213 1477/1477/2234
+f 1488/1488/2235 1475/1475/2212 1476/1476/2213
+f 1489/1489/2237 1474/1474/2211 1475/1475/2212
+f 1274/1274/2238 1275/1275/5251 1474/1474/2210
+f 1274/1274/2239 1489/1489/2237 1490/1490/2240
+f 1489/1489/2237 1488/1488/2235 1491/1491/2242
+f 1488/1488/2235 1487/1487/2233 1492/1492/2244
+f 1487/1487/2233 1486/1486/2231 1493/1493/2245
+f 1486/1486/2231 1485/1485/2229 1494/1494/5252
+f 1485/1485/2229 1484/1484/2230 1495/1495/2248
+f 1484/1484/2228 1483/1483/2226 1496/1496/2250
+f 1483/1483/2226 1482/1482/2225 1497/1497/2252
+f 1482/1482/2225 1225/1225/1837 1226/1226/1838
+f 1497/1497/2255 1226/1226/1838 1227/1227/1839
+f 1496/1496/2253 1497/1497/2255 1498/1498/2256
+f 1495/1495/2248 1496/1496/2250 1499/1499/5253
+f 1494/1494/2260 1495/1495/2248 1500/1500/5254
+f 1493/1493/5255 1494/1494/2246 1501/1501/2261
+f 1492/1492/2264 1493/1493/5256 1502/1502/2263
+f 1491/1491/2242 1492/1492/2244 1503/1503/2265
+f 1490/1490/2243 1491/1491/2242 1504/1504/2266
+f 1273/1273/1895 1490/1490/2240 1505/1505/2268
+f 1272/1272/1896 1505/1505/2267 1506/1506/2269
+f 1505/1505/2267 1504/1504/2266 1507/1507/2270
+f 1504/1504/2266 1503/1503/2265 1508/1508/2271
+f 1503/1503/2265 1502/1502/2263 1509/1509/2272
+f 1502/1502/2263 1501/1501/5257 1510/1510/2274
+f 1501/1501/2261 1500/1500/5254 1511/1511/2277
+f 1500/1500/2259 1499/1499/2278 1512/1512/2283
+f 1499/1499/2278 1498/1498/2257 1513/1513/2279
+f 1498/1498/2257 1227/1227/1839 1228/1228/2280
+f 1513/1513/2281 1228/1228/2280 1229/1229/1842
+f 1512/1512/2283 1513/1513/2281 1514/1514/2284
+f 1511/1511/2277 1512/1512/2276 1515/1515/2285
+f 1510/1510/2274 1511/1511/2275 1516/1516/2286
+f 1509/1509/2272 1510/1510/2274 1517/1517/2287
+f 1508/1508/2273 1509/1509/2272 1518/1518/2288
+f 1507/1507/2270 1508/1508/2273 1519/1519/2290
+f 1506/1506/2269 1507/1507/2270 1520/1520/2291
+f 1271/1271/1893 1506/1506/2269 1521/1521/2292
+f 1270/1270/1892 1521/1521/2292 1522/1522/2293
+f 1521/1521/2292 1520/1520/2291 1523/1523/2294
+f 1520/1520/2291 1519/1519/2290 1524/1524/2295
+f 1519/1519/2289 1518/1518/2288 1525/1525/2296
+f 1518/1518/2288 1517/1517/2287 1526/1526/2297
+f 1517/1517/2287 1516/1516/2286 1527/1527/2301
+f 1516/1516/2286 1515/1515/2285 1528/1528/2300
+f 1515/1515/2285 1514/1514/2282 1529/1529/2302
+f 1514/1514/2282 1229/1229/1842 1230/1231/2303
+f 1530/1530/2305 1529/1529/2304 1230/1231/2303
+f 1531/1531/2306 1528/1528/2300 1529/1529/2302
+f 1532/1532/2307 1527/1527/2301 1528/1528/2300
+f 1533/1533/5258 1526/1526/2297 1527/1527/2299
+f 1534/1534/2309 1525/1525/2296 1526/1526/2297
+f 1535/1535/2310 1524/1524/2295 1525/1525/2296
+f 1536/1536/2311 1523/1523/2294 1524/1524/2295
+f 1537/1537/2313 1522/1522/2293 1523/1523/2294
+f 1268/1268/1890 1269/1269/1891 1522/1522/2293
+f 1267/1267/1888 1268/1268/1890 1537/1537/2313
+f 1538/1538/2314 1537/1537/2313 1536/1536/2311
+f 1539/1539/2315 1536/1536/2311 1535/1535/2312
+f 1540/1540/2316 1535/1535/2310 1534/1534/2309
+f 1541/1541/2317 1534/1534/2309 1533/1533/2308
+f 1542/1542/2318 1533/1533/2308 1532/1532/2307
+f 1543/1543/2319 1532/1532/2307 1531/1531/2306
+f 1544/1544/2322 1531/1531/2306 1530/1530/2323
+f 1545/1545/2328 1530/1530/2305 1231/1230/1844
+f 1546/1546/2352 1545/1545/2324 1232/1232/2325
+f 1547/1547/2327 1544/1544/2321 1545/1545/2328
+f 1548/1548/2329 1543/1543/2320 1544/1544/2321
+f 1549/1549/2330 1542/1542/2318 1543/1543/2319
+f 1550/1550/2332 1541/1541/2317 1542/1542/2318
+f 1551/1551/2334 1540/1540/2316 1541/1541/2317
+f 1552/1552/2335 1539/1539/2315 1540/1540/2336
+f 1553/1553/2337 1538/1538/2314 1539/1539/2315
+f 1266/1266/1887 1267/1267/1888 1538/1538/2314
+f 1265/1265/1886 1266/1266/1889 1553/1553/2338
+f 1554/1554/2340 1553/1553/2337 1552/1552/2335
+f 1555/1555/2341 1552/1552/2335 1551/1551/2334
+f 1556/1556/2343 1551/1551/2334 1550/1550/2332
+f 1557/1557/5259 1550/1550/2332 1549/1549/2330
+f 1558/1558/2347 1549/1549/2330 1548/1548/2329
+f 1559/1559/2348 1548/1548/2329 1547/1547/5260
+f 1560/1560/2351 1547/1547/5260 1546/1546/2326
+f 1561/1561/2356 1546/1546/2352 1233/1233/1847
+f 1562/1562/2354 1561/1561/2353 1234/1234/5261
+f 1563/1563/2379 1560/1560/2351 1561/1561/2356
+f 1564/1564/2359 1559/1559/2348 1560/1560/2350
+f 1565/1565/2358 1558/1558/5262 1559/1559/2348
+f 1566/1566/2360 1557/1557/5263 1558/1558/2346
+f 1567/1567/2362 1556/1556/2342 1557/1557/2363
+f 1568/1568/2364 1555/1555/2341 1556/1556/2342
+f 1569/1569/2365 1554/1554/2340 1555/1555/2341
+f 1264/1264/2366 1265/1265/5264 1554/1554/2339
+f 1264/1264/2367 1569/1569/2365 1570/1570/2368
+f 1569/1569/2365 1568/1568/2364 1571/1571/2370
+f 1568/1568/2364 1567/1567/5265 1572/1572/2372
+f 1567/1567/2362 1566/1566/2360 1573/1573/2373
+f 1566/1566/2360 1565/1565/2358 1574/1574/5266
+f 1565/1565/2358 1564/1564/2359 1575/1575/2376
+f 1564/1564/2357 1563/1563/2355 1576/1576/2378
+f 1563/1563/2355 1562/1562/2354 1577/1577/2380
+f 1562/1562/2354 1235/1235/1851 1236/1236/5207
+f 1577/1577/2382 1236/1236/5207 1237/1237/5267
+f 1576/1576/2381 1577/1577/2382 1578/1578/2383
+f 1575/1575/2376 1576/1576/2378 1579/1579/5268
+f 1574/1574/2387 1575/1575/2376 1580/1580/5269
+f 1573/1573/5270 1574/1574/2374 1581/1581/2388
+f 1572/1572/2391 1573/1573/5271 1582/1582/2390
+f 1571/1571/2370 1572/1572/2372 1583/1583/2392
+f 1570/1570/2371 1571/1571/2370 1584/1584/2393
+f 1263/1263/1883 1570/1570/2368 1585/1585/2395
+f 1262/1262/1884 1585/1585/2395 1586/1586/2396
+f 1585/1585/2395 1584/1584/2393 1587/1587/2397
+f 1584/1584/2393 1583/1583/2392 1588/1588/2398
+f 1583/1583/2392 1582/1582/2390 1589/1589/2399
+f 1582/1582/2390 1581/1581/5272 1590/1590/2401
+f 1581/1581/2388 1580/1580/5269 1591/1591/2404
+f 1580/1580/2386 1579/1579/5268 1592/1592/5273
+f 1579/1579/2405 1578/1578/5274 1593/1593/2406
+f 1578/1578/2384 1237/1237/1853 1238/1238/5275
+f 1593/1593/2408 1238/1238/5275 1239/1239/1856
+f 1592/1592/5276 1593/1593/2408 1594/1594/2410
+f 1591/1591/2404 1592/1592/2403 1595/1595/2411
+f 1590/1590/2401 1591/1591/2402 1596/1596/2412
+f 1589/1589/2399 1590/1590/2401 1597/1597/2413
+f 1588/1588/2400 1589/1589/2399 1598/1598/2414
+f 1587/1587/2397 1588/1588/2400 1599/1599/5277
+f 1586/1586/2396 1587/1587/2397 1600/1600/2416
+f 1261/1261/1881 1586/1586/2396 1601/1601/2417
+f 1260/1260/1880 1601/1601/2417 1256/1257/1877
+f 1601/1601/2417 1600/1600/2416 1254/1255/1875
+f 1600/1600/2416 1599/1599/5277 1252/1253/1872
+f 1599/1599/2415 1598/1598/2414 1250/1251/1870
+f 1598/1598/2414 1597/1597/2413 1248/1249/1868
+f 1597/1597/2413 1596/1596/2412 1246/1247/1866
+f 1596/1596/2412 1595/1595/2411 1244/1245/1864
+f 1595/1595/2411 1594/1594/2409 1242/1243/1862
+f 1594/1594/2409 1239/1239/1856 1240/1240/1860
+f 1642/1642/2469 1604/1604/2423 1643/1643/2470
+f 1603/1603/2422 1645/1645/2472 1643/1643/2470
+f 1605/1605/2424 1646/1646/2473 1645/1645/2472
+f 1606/1606/2426 1647/1647/2528 1646/1646/2473
+f 1607/1607/2427 1648/1648/2475 1647/1647/2474
+f 1608/1608/2428 1649/1650/2478 1648/1648/2475
+f 1608/1608/2428 1609/1609/2429 1650/1649/2477
+f 1609/1609/2429 1610/1610/2430 1651/1651/5278
+f 1610/1610/5279 1611/1611/2431 1652/1652/2480
+f 1611/1611/2431 1612/1612/2434 1653/1653/2483
+f 1612/1612/2434 1613/1613/2433 1654/1654/2482
+f 1614/1614/2435 1655/1655/2484 1654/1654/2482
+f 1615/1615/2436 1656/1656/2486 1655/1655/5280
+f 1616/1616/2438 1657/1657/2548 1656/1656/2486
+f 1617/1617/2439 1658/1658/2488 1657/1657/2548
+f 1618/1618/2440 1659/1660/2490 1658/1658/2488
+f 1618/1618/2440 1619/1619/5281 1660/1659/2489
+f 1619/1619/2441 1620/1620/2442 1661/1661/2493
+f 1620/1620/2442 1621/1621/2443 1662/1662/2492
+f 1621/1621/2443 1622/1622/2446 1663/1663/2494
+f 1622/1622/2446 1623/1623/2445 1664/1664/2495
+f 1624/1624/2447 1665/1665/2496 1664/1664/2495
+f 1625/1625/2448 1666/1666/2497 1665/1665/2496
+f 1626/1626/2450 1667/1667/2568 1666/1666/2497
+f 1627/1627/2451 1668/1668/2499 1667/1667/2498
+f 1628/1628/2452 1669/1670/2502 1668/1668/2499
+f 1628/1628/2452 1629/1629/2453 1670/1669/2501
+f 1629/1629/2453 1630/1630/2454 1671/1671/5282
+f 1630/1630/5283 1631/1631/2455 1672/1672/2504
+f 1631/1631/2455 1632/1632/2458 1673/1673/2507
+f 1632/1632/2458 1633/1633/2457 1674/1674/2506
+f 1634/1634/2459 1675/1675/2508 1674/1674/2506
+f 1635/1635/2460 1676/1676/2510 1675/1675/5284
+f 1636/1636/2462 1677/1677/2588 1676/1676/2510
+f 1637/1637/2463 1678/1678/2512 1677/1677/2588
+f 1638/1638/2464 1679/1680/2514 1678/1678/2512
+f 1638/1638/2464 1639/1639/5285 1680/1679/2513
+f 1639/1639/2465 1640/1640/2466 1681/1681/2517
+f 1640/1640/2466 1641/1641/2467 1682/1682/2516
+f 1641/1641/2467 1642/1642/2469 1644/1644/2471
+f 1682/1682/2516 1644/1644/2471 1683/1683/2519
+f 1644/1644/2471 1643/1643/2470 1685/1685/2521
+f 1645/1645/2472 1686/1686/5286 1685/1685/2521
+f 1646/1646/5287 1687/1687/2525 1686/1686/2523
+f 1647/1647/5288 1688/1688/5289 1687/1687/5290
+f 1648/1648/2475 1689/1689/5291 1688/1688/2607
+f 1649/1650/2478 1690/1691/2532 1689/1689/2530
+f 1649/1650/2529 1650/1649/5292 1691/1690/2531
+f 1650/1649/5292 1651/1651/2479 1692/1692/5293
+f 1651/1651/5294 1652/1652/5295 1693/1693/2540
+f 1652/1652/2480 1653/1653/2483 1694/1694/2539
+f 1653/1653/2483 1654/1654/2482 1695/1695/2541
+f 1655/1655/2484 1696/1696/2542 1695/1695/2541
+f 1656/1656/5296 1697/1697/2544 1696/1696/2542
+f 1657/1657/5297 1698/1698/5298 1697/1697/5299
+f 1658/1658/2488 1699/1699/5300 1698/1698/2622
+f 1659/1660/2549 1700/1701/2552 1699/1699/2550
+f 1659/1660/2549 1660/1659/5301 1701/1700/2551
+f 1660/1659/5301 1661/1661/2493 1702/1702/5302
+f 1661/1661/5303 1662/1662/2492 1703/1703/2560
+f 1662/1662/2492 1663/1663/2494 1704/1704/2559
+f 1663/1663/2494 1664/1664/2495 1705/1705/2561
+f 1665/1665/2496 1706/1706/5304 1705/1705/2561
+f 1666/1666/5305 1707/1707/2565 1706/1706/2563
+f 1667/1667/5306 1708/1708/5307 1707/1707/5308
+f 1668/1668/2499 1709/1709/5309 1708/1708/2637
+f 1669/1670/2502 1710/1711/2572 1709/1709/2570
+f 1669/1670/2569 1670/1669/5310 1711/1710/2571
+f 1670/1669/5310 1671/1671/2503 1712/1712/5311
+f 1671/1671/5312 1672/1672/5313 1713/1713/2580
+f 1672/1672/2504 1673/1673/2507 1714/1714/2579
+f 1673/1673/2507 1674/1674/2506 1715/1715/2581
+f 1675/1675/2508 1716/1716/2582 1715/1715/2581
+f 1676/1676/5314 1717/1717/2584 1716/1716/2582
+f 1677/1677/5315 1718/1718/5316 1717/1717/5317
+f 1678/1678/2512 1719/1719/5318 1718/1718/2652
+f 1679/1680/2589 1720/1721/2592 1719/1719/2590
+f 1679/1680/2589 1680/1679/5319 1721/1720/2591
+f 1680/1679/5319 1681/1681/2517 1722/1722/5320
+f 1681/1681/5321 1682/1682/2516 1684/1684/2520
+f 1684/1684/2520 1723/1724/2660 1724/1723/2662
+f 1683/1683/2519 1725/1725/2601 1723/1724/2600
+f 1685/1685/2521 1726/1727/2603 1725/1725/2601
+f 1685/1685/2521 1686/1686/2523 1727/1726/2602
+f 1686/1686/2523 1687/1687/2525 1728/1728/2604
+f 1687/1687/5290 1688/1688/5322 1729/1729/2609
+f 1688/1688/5322 1689/1689/5291 1730/1730/5323
+f 1689/1689/2530 1690/1691/2532 1731/1731/2611
+f 1691/1690/5324 1732/1732/2675 1731/1731/2611
+f 1692/1692/2533 1733/1733/5325 1732/1732/2675
+f 1693/1693/2540 1734/1734/5326 1733/1733/2680
+f 1694/1694/2539 1735/1735/2616 1734/1734/2615
+f 1695/1695/2541 1736/1737/2618 1735/1735/2616
+f 1695/1695/2541 1696/1696/2542 1737/1736/2617
+f 1696/1696/2542 1697/1697/2544 1738/1738/2619
+f 1697/1697/5299 1698/1698/5327 1739/1739/2624
+f 1698/1698/5327 1699/1699/5300 1740/1740/5328
+f 1699/1699/2550 1700/1701/2552 1741/1741/2626
+f 1701/1700/5329 1742/1742/2692 1741/1741/2626
+f 1702/1702/2553 1743/1743/2629 1742/1742/2692
+f 1703/1703/2560 1744/1744/2695 1743/1743/2697
+f 1704/1704/2559 1745/1745/2631 1744/1744/5330
+f 1705/1705/2561 1746/1747/2633 1745/1745/2631
+f 1705/1705/2561 1706/1706/2563 1747/1746/2632
+f 1706/1706/2563 1707/1707/2565 1748/1748/5331
+f 1707/1707/5308 1708/1708/5332 1749/1749/2639
+f 1708/1708/5332 1709/1709/5309 1750/1750/5333
+f 1709/1709/2570 1710/1711/2572 1751/1751/2641
+f 1711/1710/5334 1752/1752/2710 1751/1751/2641
+f 1712/1712/2573 1753/1753/5335 1752/1752/2710
+f 1713/1713/2580 1754/1754/5336 1753/1753/2715
+f 1714/1714/2579 1755/1755/2646 1754/1754/2645
+f 1715/1715/2581 1756/1757/2648 1755/1755/2646
+f 1715/1715/2581 1716/1716/2582 1757/1756/2647
+f 1716/1716/2582 1717/1717/2584 1758/1758/2649
+f 1717/1717/5317 1718/1718/5337 1759/1759/2654
+f 1718/1718/5337 1719/1719/5318 1760/1760/5338
+f 1719/1719/2590 1720/1721/2592 1761/1761/2656
+f 1721/1720/5339 1762/1762/2659 1761/1761/2656
+f 1722/1722/2593 1724/1723/2598 1762/1762/2659
+f 1724/1723/5340 1763/1764/2661 1764/1763/2658
+f 1723/1724/2600 1765/1765/2732 1763/1764/2661
+f 1725/1725/2601 1766/1766/2734 1765/1765/2663
+f 1726/1727/2603 1767/1768/2666 1766/1766/2664
+f 1726/1727/2603 1727/1726/2602 1768/1767/2665
+f 1727/1726/2602 1728/1728/2604 1769/1769/2667
+f 1728/1728/2604 1729/1729/2605 1770/1770/2670
+f 1729/1729/2609 1730/1730/2608 1771/1771/2673
+f 1730/1730/2608 1731/1731/2676 1772/1772/2674
+f 1732/1732/2675 1773/1773/2677 1772/1772/2674
+f 1733/1733/5325 1774/1774/2679 1773/1773/2677
+f 1734/1734/2615 1775/1775/2746 1774/1774/2679
+f 1735/1735/2616 1776/1776/2748 1775/1775/2681
+f 1736/1737/2618 1777/1778/2684 1776/1776/2682
+f 1736/1737/2618 1737/1736/2617 1778/1777/2683
+f 1737/1736/2617 1738/1738/2619 1779/1779/2685
+f 1738/1738/5341 1739/1739/2620 1780/1780/2687
+f 1739/1739/2624 1740/1740/2623 1781/1781/2690
+f 1740/1740/2623 1741/1741/2693 1782/1782/2691
+f 1742/1742/2692 1783/1783/2694 1782/1782/2691
+f 1743/1743/5342 1784/1784/2696 1783/1783/2694
+f 1744/1744/2630 1785/1785/2761 1784/1784/2696
+f 1745/1745/2631 1786/1786/2763 1785/1785/2698
+f 1746/1747/2633 1787/1788/2701 1786/1786/2699
+f 1746/1747/2633 1747/1746/2632 1788/1787/2700
+f 1747/1746/2632 1748/1748/5331 1789/1789/2703
+f 1748/1748/5331 1749/1749/2635 1790/1790/2705
+f 1749/1749/2639 1750/1750/2638 1791/1791/2708
+f 1750/1750/2638 1751/1751/2711 1792/1792/2709
+f 1752/1752/2710 1793/1793/2712 1792/1792/2709
+f 1753/1753/5335 1794/1794/2714 1793/1793/2712
+f 1754/1754/2645 1795/1795/2775 1794/1794/2714
+f 1755/1755/2646 1796/1796/2777 1795/1795/2716
+f 1756/1757/2648 1797/1798/2719 1796/1796/2717
+f 1756/1757/2648 1757/1756/2647 1798/1797/2718
+f 1757/1756/2647 1758/1758/2649 1799/1799/2720
+f 1758/1758/5343 1759/1759/2650 1800/1800/2722
+f 1759/1759/2654 1760/1760/2653 1801/1801/2725
+f 1760/1760/2653 1761/1761/2727 1802/1802/2726
+f 1762/1762/2659 1764/1763/2658 1802/1802/2726
+f 1764/1763/2658 1803/1804/2729 1804/1803/2789
+f 1763/1764/2661 1805/1805/2730 1803/1804/2729
+f 1765/1765/2732 1806/1806/2731 1805/1805/2730
+f 1766/1766/2664 1807/1807/2792 1806/1806/2731
+f 1767/1768/2666 1808/1809/2736 1807/1807/2795
+f 1767/1768/2666 1768/1767/2668 1809/1808/5344
+f 1768/1767/2665 1769/1769/2667 1810/1810/2737
+f 1769/1769/2667 1770/1770/2670 1811/1811/2739
+f 1770/1770/2670 1771/1771/2673 1812/1812/2741
+f 1771/1771/2673 1772/1772/2674 1813/1813/2805
+f 1773/1773/2677 1814/1814/2743 1813/1813/2805
+f 1774/1774/2679 1815/1815/2744 1814/1814/2743
+f 1775/1775/2746 1816/1816/5345 1815/1815/5346
+f 1776/1776/2682 1817/1817/2808 1816/1816/2745
+f 1777/1778/2684 1818/1819/2750 1817/1817/2811
+f 1777/1778/2684 1778/1777/2686 1819/1818/5347
+f 1778/1777/2683 1779/1779/2688 1820/1820/2751
+f 1779/1779/2685 1780/1780/2687 1821/1821/2756
+f 1780/1780/2687 1781/1781/2690 1822/1822/2755
+f 1781/1781/2690 1782/1782/2691 1823/1823/2820
+f 1783/1783/2694 1824/1824/2758 1823/1823/2820
+f 1784/1784/2696 1825/1825/2759 1824/1824/2758
+f 1785/1785/2761 1826/1826/2760 1825/1825/2759
+f 1786/1786/2699 1827/1827/2823 1826/1826/2760
+f 1787/1788/2701 1828/1829/2765 1827/1827/2826
+f 1787/1788/2701 1788/1787/2704 1829/1828/5348
+f 1788/1787/2700 1789/1789/2703 1830/1830/2766
+f 1789/1789/2703 1790/1790/2705 1831/1831/2768
+f 1790/1790/2705 1791/1791/2708 1832/1832/2770
+f 1791/1791/2708 1792/1792/2709 1833/1833/2836
+f 1793/1793/2712 1834/1834/2772 1833/1833/2836
+f 1794/1794/2714 1835/1835/2773 1834/1834/2772
+f 1795/1795/2775 1836/1836/5349 1835/1835/5350
+f 1796/1796/2717 1837/1837/2839 1836/1836/2774
+f 1797/1798/2719 1838/1839/2779 1837/1837/2842
+f 1797/1798/2719 1798/1797/2721 1839/1838/5351
+f 1798/1797/2718 1799/1799/2723 1840/1840/2780
+f 1799/1799/2720 1800/1800/2722 1841/1841/2785
+f 1800/1800/2722 1801/1801/2725 1842/1842/2784
+f 1801/1801/2725 1802/1802/2726 1804/1803/2789
+f 1842/1842/2784 1804/1803/2728 1843/1843/2788
+f 1803/1804/2729 1845/1845/2790 1843/1843/2786
+f 1805/1805/5352 1846/1846/2791 1845/1845/2790
+f 1806/1806/2731 1847/1847/2793 1846/1846/2791
+f 1807/1807/5353 1848/1848/2794 1847/1847/2793
+f 1808/1809/2736 1849/1850/2797 1848/1848/2794
+f 1808/1809/2736 1809/1808/2735 1850/1849/2796
+f 1809/1808/2738 1810/1810/2737 1851/1851/2799
+f 1810/1810/2737 1811/1811/2739 1852/1852/2800
+f 1811/1811/2739 1812/1812/2741 1853/1853/2802
+f 1812/1812/2741 1813/1813/2742 1854/1854/2867
+f 1814/1814/2743 1855/1855/2806 1854/1854/2803
+f 1815/1815/5346 1856/1856/2807 1855/1855/2806
+f 1816/1816/2745 1857/1857/2809 1856/1856/2807
+f 1817/1817/5354 1858/1858/2810 1857/1857/2809
+f 1818/1819/2750 1859/1860/2813 1858/1858/2810
+f 1818/1819/2750 1819/1818/2749 1860/1859/2812
+f 1819/1818/2752 1820/1820/2751 1861/1861/2815
+f 1820/1820/2751 1821/1821/2756 1862/1862/2816
+f 1821/1821/2756 1822/1822/2755 1863/1863/2817
+f 1822/1822/2755 1823/1823/2757 1864/1864/2819
+f 1824/1824/2758 1865/1865/2821 1864/1864/2818
+f 1825/1825/5355 1866/1866/2822 1865/1865/2821
+f 1826/1826/2760 1867/1867/2824 1866/1866/2822
+f 1827/1827/5356 1868/1868/2825 1867/1867/2824
+f 1828/1829/2765 1869/1870/2828 1868/1868/2825
+f 1828/1829/2765 1829/1828/2764 1870/1869/2827
+f 1829/1828/2767 1830/1830/2766 1871/1871/2830
+f 1830/1830/2766 1831/1831/2768 1872/1872/2831
+f 1831/1831/2768 1832/1832/2770 1873/1873/2833
+f 1832/1832/2770 1833/1833/2771 1874/1874/2897
+f 1834/1834/2772 1875/1875/2837 1874/1874/2834
+f 1835/1835/5350 1876/1876/2838 1875/1875/2837
+f 1836/1836/2774 1877/1877/2840 1876/1876/2838
+f 1837/1837/5357 1878/1878/2841 1877/1877/2840
+f 1838/1839/2779 1879/1880/2844 1878/1878/2841
+f 1838/1839/2779 1839/1838/2778 1880/1879/2843
+f 1839/1838/2781 1840/1840/2780 1881/1881/2846
+f 1840/1840/2780 1841/1841/2785 1882/1882/2847
+f 1841/1841/2785 1842/1842/2784 1844/1844/2787
+f 1882/1882/2847 1844/1844/5358 1883/1883/2848
+f 1844/1844/2787 1843/1843/2788 1885/1885/5359
+f 1845/1845/2790 1886/1886/2853 1885/1885/2850
+f 1846/1846/2791 1887/1887/2855 1886/1886/2853
+f 1847/1847/2793 1888/1888/2919 1887/1887/2855
+f 1848/1848/2794 1889/1889/2857 1888/1888/2856
+f 1849/1850/2797 1890/1891/2859 1889/1889/5360
+f 1849/1850/2797 1850/1849/2796 1891/1890/2861
+f 1850/1849/2796 1851/1851/2799 1892/1892/2860
+f 1851/1851/2799 1852/1852/2800 1893/1893/2862
+f 1852/1852/2800 1853/1853/5361 1894/1894/2864
+f 1853/1853/2802 1854/1854/2804 1895/1895/5362
+f 1855/1855/2806 1896/1896/2868 1895/1895/2865
+f 1856/1856/2807 1897/1897/2870 1896/1896/2868
+f 1857/1857/2809 1898/1898/2871 1897/1897/2870
+f 1858/1858/2810 1899/1899/2872 1898/1898/2871
+f 1859/1860/2813 1900/1901/2874 1899/1899/5363
+f 1859/1860/2813 1860/1859/2812 1901/1900/2876
+f 1860/1859/2812 1861/1861/2815 1902/1902/2875
+f 1861/1861/2815 1862/1862/2816 1903/1903/2877
+f 1862/1862/2816 1863/1863/5364 1904/1904/2879
+f 1863/1863/2817 1864/1864/2819 1905/1905/5365
+f 1865/1865/2821 1906/1906/2883 1905/1905/2880
+f 1866/1866/2822 1907/1907/2885 1906/1906/2883
+f 1867/1867/2824 1908/1908/2956 1907/1907/2885
+f 1868/1868/2825 1909/1909/2887 1908/1908/2886
+f 1869/1870/2828 1910/1911/2889 1909/1909/5366
+f 1869/1870/2828 1870/1869/2827 1911/1910/2891
+f 1870/1869/2827 1871/1871/2830 1912/1912/2890
+f 1871/1871/2830 1872/1872/2831 1913/1913/2892
+f 1872/1872/2831 1873/1873/5367 1914/1914/2894
+f 1873/1873/2833 1874/1874/2835 1915/1915/5368
+f 1875/1875/2837 1916/1916/2898 1915/1915/2895
+f 1876/1876/2838 1917/1917/2900 1916/1916/2898
+f 1877/1877/2840 1918/1918/2901 1917/1917/2900
+f 1878/1878/2841 1919/1919/2902 1918/1918/2901
+f 1879/1880/2844 1920/1921/2904 1919/1919/5369
+f 1879/1880/2844 1880/1879/2843 1921/1920/2906
+f 1880/1879/2843 1881/1881/2846 1922/1922/2905
+f 1881/1881/2846 1882/1882/2847 1884/1884/2849
+f 1922/1922/2907 1884/1884/2910 1923/1923/2912
+f 1884/1884/5370 1883/1883/2848 1925/1925/2985
+f 1883/1883/2913 1885/1885/2850 1926/1926/2916
+f 1886/1886/2915 1927/1927/5371 1926/1926/2914
+f 1887/1887/2921 1928/1928/2920 1927/1927/5371
+f 1888/1888/2919 1929/1929/5372 1928/1928/5373
+f 1889/1889/5360 1930/1930/2923 1929/1929/5374
+f 1890/1891/2859 1931/1932/2925 1930/1930/2923
+f 1890/1891/2859 1891/1890/2861 1932/1931/2924
+f 1891/1890/2861 1892/1892/2863 1933/1933/5375
+f 1892/1892/2863 1893/1893/2929 1934/1934/2931
+f 1893/1893/5376 1894/1894/2864 1935/1935/3003
+f 1894/1894/2932 1895/1895/2865 1936/1936/2935
+f 1896/1896/2934 1937/1937/5377 1936/1936/2933
+f 1897/1897/2939 1938/1938/2938 1937/1937/5377
+f 1898/1898/2871 1939/1939/5378 1938/1938/5379
+f 1899/1899/2872 1940/1940/2941 1939/1939/5380
+f 1900/1901/2874 1941/1942/2943 1940/1940/2941
+f 1900/1901/2874 1901/1900/2876 1942/1941/2942
+f 1901/1900/2876 1902/1902/2878 1943/1943/3019
+f 1902/1902/2878 1903/1903/2947 1944/1944/2949
+f 1903/1903/5381 1904/1904/2879 1945/1945/3022
+f 1904/1904/2950 1905/1905/2880 1946/1946/2953
+f 1906/1906/2952 1947/1947/5382 1946/1946/2951
+f 1907/1907/2958 1948/1948/2957 1947/1947/5382
+f 1908/1908/2956 1949/1949/5383 1948/1948/5384
+f 1909/1909/5366 1950/1950/2960 1949/1949/5385
+f 1910/1911/2889 1951/1952/2962 1950/1950/2960
+f 1910/1911/2889 1911/1910/2891 1952/1951/2961
+f 1911/1910/2891 1912/1912/2893 1953/1953/5386
+f 1912/1912/2893 1913/1913/2966 1954/1954/2968
+f 1913/1913/5387 1914/1914/2894 1955/1955/3039
+f 1914/1914/2969 1915/1915/2895 1956/1956/2972
+f 1916/1916/2971 1957/1957/5388 1956/1956/2970
+f 1917/1917/2976 1958/1958/2975 1957/1957/5388
+f 1918/1918/2901 1959/1959/5389 1958/1958/5390
+f 1919/1919/2902 1960/1960/2978 1959/1959/5391
+f 1920/1921/2904 1961/1962/2980 1960/1960/2978
+f 1920/1921/2904 1921/1920/2906 1962/1961/2979
+f 1921/1920/2906 1922/1922/2907 1924/1924/2982
+f 1924/1924/2982 1963/1964/3057 1964/1963/3055
+f 1923/1923/2987 1965/1965/2986 1963/1964/3057
+f 1925/1925/2985 1966/1966/2989 1965/1965/2986
+f 1926/1926/2988 1967/1968/2992 1966/1966/5392
+f 1926/1926/2990 1927/1927/2918 1968/1967/2994
+f 1927/1927/2918 1928/1928/5393 1969/1969/2993
+f 1928/1928/5373 1929/1929/5374 1970/1970/5394
+f 1929/1929/5374 1930/1930/2923 1971/1971/5395
+f 1930/1930/2923 1931/1932/2925 1972/1972/2999
+f 1932/1931/2924 1973/1973/5396 1972/1972/2999
+f 1933/1933/5397 1974/1974/3069 1973/1973/3068
+f 1934/1934/5398 1975/1975/3004 1974/1974/3069
+f 1935/1935/3003 1976/1976/3007 1975/1975/3004
+f 1936/1936/3006 1977/1978/3010 1976/1976/5399
+f 1936/1936/3008 1937/1937/2937 1978/1977/3012
+f 1937/1937/2937 1938/1938/3013 1979/1979/3011
+f 1938/1938/5379 1939/1939/5380 1980/1980/5400
+f 1939/1939/2940 1940/1940/2941 1981/1981/5401
+f 1940/1940/2941 1941/1942/2943 1982/1982/3018
+f 1942/1941/2942 1983/1983/5402 1982/1982/3016
+f 1943/1943/3019 1984/1984/3081 1983/1983/3080
+f 1944/1944/3024 1985/1985/3023 1984/1984/3081
+f 1945/1945/3022 1986/1986/3026 1985/1985/3023
+f 1946/1946/5403 1987/1988/3028 1986/1986/5404
+f 1946/1946/3025 1947/1947/2955 1988/1987/3030
+f 1947/1947/2955 1948/1948/5405 1989/1989/3029
+f 1948/1948/5384 1949/1949/5385 1990/1990/5406
+f 1949/1949/5385 1950/1950/2960 1991/1991/5407
+f 1950/1950/2960 1951/1952/2962 1992/1992/3035
+f 1952/1951/2961 1993/1993/5408 1992/1992/3035
+f 1953/1953/5409 1994/1994/3093 1993/1993/3092
+f 1954/1954/5410 1995/1995/3040 1994/1994/3093
+f 1955/1955/3039 1996/1996/3043 1995/1995/3040
+f 1956/1956/3042 1997/1998/3046 1996/1996/5411
+f 1956/1956/3044 1957/1957/2974 1998/1997/3048
+f 1957/1957/2974 1958/1958/3049 1999/1999/3047
+f 1958/1958/5390 1959/1959/5391 2000/2000/5412
+f 1959/1959/2977 1960/1960/2978 2001/2001/5413
+f 1960/1960/2978 1961/1962/2980 2002/2002/3054
+f 1962/1961/2979 1964/1963/5414 2002/2002/3052
+f 1964/1963/3055 2003/2004/3058 2004/2003/3056
+f 1963/1964/3057 2005/2005/3059 2003/2004/3058
+f 1965/1965/2986 2006/2006/3060 2005/2005/3059
+f 1966/1966/2989 2007/2007/3061 2006/2006/3060
+f 1967/1968/2992 2008/2009/3063 2007/2007/3061
+f 1967/1968/2992 1968/1967/2994 2009/2008/3062
+f 1968/1967/2994 1969/1969/2993 2010/2010/3064
+f 1969/1969/2993 1970/1970/5394 2011/2011/3065
+f 1970/1970/2996 1971/1971/2998 2012/2012/3066
+f 1971/1971/2998 1972/1972/2999 2013/2013/3067
+f 1973/1973/3068 2014/2014/3070 2013/2013/3067
+f 1974/1974/3069 2015/2015/3071 2014/2014/3070
+f 1975/1975/3004 2016/2016/3072 2015/2015/3071
+f 1976/1976/3007 2017/2017/3073 2016/2016/3072
+f 1977/1978/3010 2018/2019/3075 2017/2017/3073
+f 1977/1978/3010 1978/1977/3012 2019/2018/3074
+f 1978/1977/3012 1979/1979/3011 2020/2020/3076
+f 1979/1979/3011 1980/1980/3014 2021/2021/3077
+f 1980/1980/3014 1981/1981/3015 2022/2022/3078
+f 1981/1981/3015 1982/1982/3018 2023/2023/3079
+f 1983/1983/3080 2024/2024/3082 2023/2023/3079
+f 1984/1984/3081 2025/2025/3083 2024/2024/3082
+f 1985/1985/3023 2026/2026/3084 2025/2025/3083
+f 1986/1986/3026 2027/2027/3085 2026/2026/3084
+f 1987/1988/3028 2028/2029/3087 2027/2027/3085
+f 1987/1988/3028 1988/1987/3030 2029/2028/3086
+f 1988/1987/3030 1989/1989/3029 2030/2030/3088
+f 1989/1989/3029 1990/1990/5406 2031/2031/3089
+f 1990/1990/3032 1991/1991/3034 2032/2032/3090
+f 1991/1991/3034 1992/1992/3035 2033/2033/3091
+f 1993/1993/3092 2034/2034/3094 2033/2033/3091
+f 1994/1994/3093 2035/2035/3095 2034/2034/3094
+f 1995/1995/3040 2036/2036/3096 2035/2035/3095
+f 1996/1996/3043 2037/2037/3097 2036/2036/3096
+f 1997/1998/3046 2038/2039/3099 2037/2037/3097
+f 1997/1998/3046 1998/1997/3048 2039/2038/3098
+f 1998/1997/3048 1999/1999/3047 2040/2040/3100
+f 1999/1999/3047 2000/2000/3050 2041/2041/3101
+f 2000/2000/3050 2001/2001/3051 2042/2042/3102
+f 2001/2001/3051 2002/2002/3054 2004/2003/3056
+f 2042/2042/3103 2004/2003/3056 2043/2043/3104
+f 2003/2004/3106 2045/2045/3108 2043/2043/3104
+f 2005/2005/3107 2046/2046/3109 2045/2045/3108
+f 2006/2006/3060 2047/2047/3112 2046/2046/3109
+f 2007/2007/3061 2048/2048/3114 2047/2047/3112
+f 2008/2009/3063 2049/2050/3173 2048/2048/3114
+f 2008/2009/3063 2009/2008/3062 2050/2049/3115
+f 2009/2008/3062 2010/2010/5415 2051/2051/3117
+f 2010/2010/3064 2011/2011/3065 2052/2052/3118
+f 2011/2011/5416 2012/2012/3066 2053/2053/3119
+f 2012/2012/3120 2013/2013/3067 2054/2054/3121
+f 2014/2014/3122 2055/2055/3124 2054/2054/3121
+f 2015/2015/3071 2056/2056/3125 2055/2055/3124
+f 2016/2016/3072 2057/2057/3126 2056/2056/3125
+f 2017/2017/3073 2058/2058/3128 2057/2057/3126
+f 2018/2019/3075 2059/2060/5417 2058/2058/3128
+f 2018/2019/3075 2019/2018/3074 2060/2059/3129
+f 2019/2018/5418 2020/2020/5419 2061/2061/3131
+f 2020/2020/3076 2021/2021/5420 2062/2062/3132
+f 2021/2021/3133 2022/2022/3078 2063/2063/3134
+f 2022/2022/3135 2023/2023/3079 2064/2064/3136
+f 2024/2024/3137 2065/2065/3139 2064/2064/3136
+f 2025/2025/3138 2066/2066/3140 2065/2065/3139
+f 2026/2026/3084 2067/2067/3143 2066/2066/3140
+f 2027/2027/3085 2068/2068/3145 2067/2067/3143
+f 2028/2029/3087 2069/2070/3204 2068/2068/3145
+f 2028/2029/3087 2029/2028/3086 2070/2069/3146
+f 2029/2028/3086 2030/2030/5421 2071/2071/3148
+f 2030/2030/3088 2031/2031/3089 2072/2072/3149
+f 2031/2031/5422 2032/2032/3090 2073/2073/3150
+f 2032/2032/3151 2033/2033/3091 2074/2074/3152
+f 2034/2034/3153 2075/2075/3155 2074/2074/3152
+f 2035/2035/3095 2076/2076/3156 2075/2075/3155
+f 2036/2036/3096 2077/2077/3157 2076/2076/3156
+f 2037/2037/3097 2078/2078/3159 2077/2077/3157
+f 2038/2039/3099 2079/2080/5423 2078/2078/3159
+f 2038/2039/3099 2039/2038/3098 2080/2079/3160
+f 2039/2038/5424 2040/2040/5425 2081/2081/3162
+f 2040/2040/3100 2041/2041/5426 2082/2082/3163
+f 2041/2041/3164 2042/2042/3102 2044/2044/3105
+f 2082/2082/3163 2044/2044/3105 2083/2083/3165
+f 2044/2044/3105 2043/2043/3104 2085/2085/3168
+f 2045/2045/5427 2086/2086/3169 2085/2085/3168
+f 2046/2046/3109 2087/2087/5428 2086/2086/3169
+f 2047/2047/3170 2088/2088/3172 2087/2087/3171
+f 2048/2048/3114 2089/2089/3174 2088/2088/3172
+f 2049/2050/3175 2090/2091/3177 2089/2089/3174
+f 2049/2050/3175 2050/2049/3115 2091/2090/3176
+f 2050/2049/3115 2051/2051/3117 2092/2092/3178
+f 2051/2051/3179 2052/2052/3118 2093/2093/3180
+f 2052/2052/3118 2053/2053/3119 2094/2094/3181
+f 2053/2053/3182 2054/2054/3121 2095/2095/3183
+f 2055/2055/3184 2096/2096/3185 2095/2095/3183
+f 2056/2056/3125 2097/2097/3187 2096/2096/3185
+f 2057/2057/3186 2098/2098/3188 2097/2097/3187
+f 2058/2058/3128 2099/2099/3190 2098/2098/3188
+f 2059/2060/3189 2100/2101/3192 2099/2099/3190
+f 2059/2060/3130 2060/2059/3129 2101/2100/3191
+f 2060/2059/3129 2061/2061/3131 2102/2102/3193
+f 2061/2061/3194 2062/2062/3132 2103/2103/3195
+f 2062/2062/3132 2063/2063/3134 2104/2104/3196
+f 2063/2063/3134 2064/2064/3136 2105/2105/3199
+f 2065/2065/5429 2106/2106/3200 2105/2105/3199
+f 2066/2066/3140 2107/2107/5430 2106/2106/3200
+f 2067/2067/3201 2108/2108/3203 2107/2107/3202
+f 2068/2068/3145 2109/2109/3205 2108/2108/3203
+f 2069/2070/3206 2110/2111/3208 2109/2109/3205
+f 2069/2070/3206 2070/2069/3146 2111/2110/3207
+f 2070/2069/3146 2071/2071/3148 2112/2112/3209
+f 2071/2071/3210 2072/2072/3149 2113/2113/3211
+f 2072/2072/3149 2073/2073/3150 2114/2114/3212
+f 2073/2073/3213 2074/2074/3152 2115/2115/3214
+f 2075/2075/3215 2116/2116/3216 2115/2115/3214
+f 2076/2076/3156 2117/2117/3218 2116/2116/3216
+f 2077/2077/3217 2118/2118/3219 2117/2117/3218
+f 2078/2078/3159 2119/2119/3221 2118/2118/3219
+f 2079/2080/3220 2120/2121/3223 2119/2119/3221
+f 2079/2080/3161 2080/2079/3160 2121/2120/3222
+f 2080/2079/3160 2081/2081/3162 2122/2122/3224
+f 2081/2081/3225 2082/2082/3163 2084/2084/3226
+f 2122/2122/3224 2084/2084/3226 2123/2123/3227
+f 2084/2084/3226 2083/2083/3165 2125/2125/3229
+f 2083/2083/3165 2085/2085/3168 2126/2126/3230
+f 2086/2086/3169 2127/2127/3231 2126/2126/3230
+f 2087/2087/3171 2128/2128/3232 2127/2127/3231
+f 2088/2088/3172 2129/2129/3233 2128/2128/3232
+f 2089/2089/3174 2130/2130/3235 2129/2129/3233
+f 2090/2091/3234 2131/2132/3238 2130/2130/3235
+f 2090/2091/3236 2091/2090/3176 2132/2131/3237
+f 2091/2090/3176 2092/2092/3178 2133/2133/3239
+f 2092/2092/3178 2093/2093/3180 2134/2134/3240
+f 2093/2093/3180 2094/2094/3181 2135/2135/3241
+f 2094/2094/3181 2095/2095/3183 2136/2136/3242
+f 2096/2096/3185 2137/2137/3243 2136/2136/3242
+f 2097/2097/3187 2138/2138/3244 2137/2137/3243
+f 2098/2098/3188 2139/2139/3245 2138/2138/3244
+f 2099/2099/3190 2140/2140/3246 2139/2139/3245
+f 2100/2101/3192 2141/2142/3249 2140/2140/3246
+f 2100/2101/3247 2101/2100/3191 2142/2141/3248
+f 2101/2100/3191 2102/2102/3193 2143/2143/3250
+f 2102/2102/3193 2103/2103/3195 2144/2144/3251
+f 2103/2103/3195 2104/2104/3196 2145/2145/3252
+f 2104/2104/3196 2105/2105/3199 2146/2146/3253
+f 2106/2106/3200 2147/2147/3254 2146/2146/3253
+f 2107/2107/3202 2148/2148/3255 2147/2147/3254
+f 2108/2108/3203 2149/2149/3256 2148/2148/3255
+f 2109/2109/3205 2150/2150/3258 2149/2149/3256
+f 2110/2111/3257 2151/2152/3261 2150/2150/3258
+f 2110/2111/3259 2111/2110/3207 2152/2151/3260
+f 2111/2110/3207 2112/2112/3209 2153/2153/3262
+f 2112/2112/3209 2113/2113/3211 2154/2154/3263
+f 2113/2113/3211 2114/2114/3212 2155/2155/3264
+f 2114/2114/3212 2115/2115/3214 2156/2156/3265
+f 2116/2116/3216 2157/2157/3266 2156/2156/3265
+f 2117/2117/3218 2158/2158/3267 2157/2157/3266
+f 2118/2118/3219 2159/2159/3268 2158/2158/3267
+f 2119/2119/3221 2160/2160/3269 2159/2159/3268
+f 2120/2121/3223 2161/2162/3272 2160/2160/3269
+f 2120/2121/3270 2121/2120/3222 2162/2161/3271
+f 2121/2120/3222 2122/2122/3224 2124/2124/3228
+f 2162/2161/3271 2124/2124/3228 2163/2163/3273
+f 2124/2124/3228 2123/2123/3227 2165/2165/3275
+f 2123/2123/3227 2125/2125/3229 2166/2166/3276
+f 2125/2125/3229 2126/2126/3279 2167/2167/3278
+f 2127/2127/3231 2168/2168/3280 2167/2167/3278
+f 2128/2128/3232 2169/2169/3336 2168/2168/3280
+f 2129/2129/3233 2170/2170/3282 2169/2169/3281
+f 2130/2130/3235 2171/2171/3283 2170/2170/3282
+f 2131/2132/3238 2172/2173/3285 2171/2171/3283
+f 2131/2132/3238 2132/2131/3237 2173/2172/3284
+f 2132/2131/3237 2133/2133/3239 2174/2174/3286
+f 2133/2133/3239 2134/2134/3288 2175/2175/3287
+f 2134/2134/3240 2135/2135/3241 2176/2176/3289
+f 2135/2135/3241 2136/2136/3292 2177/2177/3291
+f 2137/2137/3243 2178/2178/3293 2177/2177/3291
+f 2138/2138/3295 2179/2179/3350 2178/2178/3293
+f 2139/2139/3245 2180/2180/3296 2179/2179/3294
+f 2140/2140/3246 2181/2181/3297 2180/2180/3296
+f 2141/2142/3249 2182/2183/3299 2181/2181/3297
+f 2141/2142/3249 2142/2141/3248 2183/2182/3298
+f 2142/2141/3248 2143/2143/3250 2184/2184/3300
+f 2143/2143/3250 2144/2144/3251 2185/2185/3301
+f 2144/2144/3251 2145/2145/3252 2186/2186/3302
+f 2145/2145/3252 2146/2146/3305 2187/2187/3304
+f 2147/2147/3254 2188/2188/3306 2187/2187/3304
+f 2148/2148/3255 2189/2189/3364 2188/2188/3306
+f 2149/2149/3256 2190/2190/3308 2189/2189/3307
+f 2150/2150/3258 2191/2191/3309 2190/2190/3308
+f 2151/2152/3261 2192/2193/3311 2191/2191/3309
+f 2151/2152/3261 2152/2151/3260 2193/2192/3310
+f 2152/2151/3260 2153/2153/3262 2194/2194/3312
+f 2153/2153/3262 2154/2154/3314 2195/2195/3313
+f 2154/2154/3263 2155/2155/3264 2196/2196/3315
+f 2155/2155/3264 2156/2156/3318 2197/2197/3317
+f 2157/2157/3266 2198/2198/3319 2197/2197/3317
+f 2158/2158/3321 2199/2199/3378 2198/2198/3319
+f 2159/2159/3268 2200/2200/3322 2199/2199/3320
+f 2160/2160/3269 2201/2201/3323 2200/2200/3322
+f 2161/2162/3272 2202/2202/3324 2201/2201/3323
+f 2161/2162/3272 2162/2161/3271 2164/2164/3274
+f 2202/2202/3324 2164/2164/3274 2203/2203/3325
+f 2164/2164/3274 2163/2163/3273 2205/2205/3327
+f 2163/2163/3329 2165/2165/3277 2206/2206/3330
+f 2165/2165/3275 2166/2166/3276 2207/2207/3331
+f 2166/2166/3276 2167/2167/3278 2208/2208/3333
+f 2168/2168/5431 2209/2209/3334 2208/2208/3333
+f 2169/2169/3281 2210/2210/3335 2209/2209/3334
+f 2170/2170/3282 2211/2211/3337 2210/2210/3335
+f 2171/2171/3283 2212/2212/5432 2211/2211/3337
+f 2172/2173/3285 2213/2214/3394 2212/2212/3338
+f 2172/2173/3285 2173/2172/3284 2214/2213/3339
+f 2173/2172/3284 2174/2174/3286 2215/2215/3341
+f 2174/2174/3286 2175/2175/3290 2216/2216/5433
+f 2175/2175/3287 2176/2176/3289 2217/2217/3344
+f 2176/2176/3289 2177/2177/3291 2218/2218/3346
+f 2178/2178/5434 2219/2219/3347 2218/2218/3346
+f 2179/2179/3294 2220/2220/3402 2219/2219/3347
+f 2180/2180/3348 2221/2221/3351 2220/2220/3402
+f 2181/2181/3297 2222/2222/5435 2221/2221/3351
+f 2182/2183/3299 2223/2224/3407 2222/2222/3352
+f 2182/2183/3299 2183/2182/3298 2224/2223/3353
+f 2183/2182/3298 2184/2184/3300 2225/2225/3355
+f 2184/2184/3357 2185/2185/3303 2226/2226/3358
+f 2185/2185/3301 2186/2186/3302 2227/2227/3359
+f 2186/2186/3302 2187/2187/3304 2228/2228/3361
+f 2188/2188/5436 2229/2229/3362 2228/2228/3361
+f 2189/2189/3307 2230/2230/3363 2229/2229/3362
+f 2190/2190/3308 2231/2231/3365 2230/2230/3363
+f 2191/2191/3309 2232/2232/5437 2231/2231/3365
+f 2192/2193/3311 2233/2234/3419 2232/2232/3366
+f 2192/2193/3311 2193/2192/3310 2234/2233/3367
+f 2193/2192/3310 2194/2194/3312 2235/2235/3369
+f 2194/2194/3312 2195/2195/3316 2236/2236/5438
+f 2195/2195/3313 2196/2196/3315 2237/2237/3372
+f 2196/2196/3315 2197/2197/3317 2238/2238/3374
+f 2198/2198/5439 2239/2239/3375 2238/2238/3374
+f 2199/2199/3320 2240/2240/3427 2239/2239/3375
+f 2200/2200/3376 2241/2241/3379 2240/2240/3427
+f 2201/2201/3323 2242/2242/5440 2241/2241/3379
+f 2202/2202/3324 2204/2204/3382 2242/2242/3380
+f 2204/2204/3326 2243/2245/3384 2244/2243/3381
+f 2204/2204/3382 2203/2203/3325 2245/2244/3383
+f 2203/2203/3325 2205/2205/3327 2246/2246/3385
+f 2205/2205/3327 2206/2206/3330 2247/2247/3387
+f 2206/2206/3330 2207/2207/3331 2248/2248/3388
+f 2207/2207/3331 2208/2208/3333 2249/2249/3389
+f 2209/2209/3334 2250/2250/3390 2249/2249/3389
+f 2210/2210/3335 2251/2251/3391 2250/2250/3390
+f 2211/2211/3337 2252/2252/3392 2251/2251/3391
+f 2212/2212/3338 2253/2253/5441 2252/2252/3392
+f 2213/2214/3340 2254/2255/3396 2253/2253/3393
+f 2213/2214/3394 2214/2213/3339 2255/2254/3395
+f 2214/2213/3339 2215/2215/3341 2256/2256/3397
+f 2215/2215/3341 2216/2216/3343 2257/2257/3399
+f 2216/2216/3343 2217/2217/3344 2258/2258/3400
+f 2217/2217/3344 2218/2218/3346 2259/2259/3401
+f 2219/2219/3347 2260/2260/3403 2259/2259/3401
+f 2220/2220/3402 2261/2261/3404 2260/2260/3403
+f 2221/2221/3351 2262/2262/3405 2261/2261/3404
+f 2222/2222/3352 2263/2263/5442 2262/2262/3405
+f 2223/2224/3354 2264/2265/3409 2263/2263/3406
+f 2223/2224/3407 2224/2223/3353 2265/2264/3408
+f 2224/2223/3353 2225/2225/3355 2266/2266/3410
+f 2225/2225/3355 2226/2226/3358 2267/2267/3412
+f 2226/2226/3358 2227/2227/3359 2268/2268/3413
+f 2227/2227/3359 2228/2228/3361 2269/2269/3414
+f 2229/2229/3362 2270/2270/3415 2269/2269/3414
+f 2230/2230/3363 2271/2271/3416 2270/2270/3415
+f 2231/2231/3365 2272/2272/3417 2271/2271/3416
+f 2232/2232/3366 2273/2273/5443 2272/2272/3417
+f 2233/2234/3368 2274/2275/3421 2273/2273/3418
+f 2233/2234/3419 2234/2233/3367 2275/2274/3420
+f 2234/2233/3367 2235/2235/3369 2276/2276/3422
+f 2235/2235/3369 2236/2236/3371 2277/2277/3424
+f 2236/2236/3371 2237/2237/3372 2278/2278/3425
+f 2237/2237/3372 2238/2238/3374 2279/2279/3426
+f 2239/2239/3375 2280/2280/3428 2279/2279/3426
+f 2240/2240/3427 2281/2281/3429 2280/2280/3428
+f 2241/2241/3379 2282/2282/3430 2281/2281/3429
+f 2242/2242/3380 2244/2243/5444 2282/2282/3430
+f 2244/2243/3381 2283/2284/3432 2284/2283/3431
+f 2243/2245/3384 2285/2286/3434 2283/2284/3432
+f 2243/2245/3384 2245/2244/3383 2286/2285/3433
+f 2245/2244/3383 2246/2246/3385 2287/2287/3435
+f 2246/2246/3385 2247/2247/3387 2288/2288/3436
+f 2247/2247/3387 2248/2248/3388 2289/2289/3438
+f 2248/2248/3388 2249/2249/3389 2290/2290/3439
+f 2250/2250/3390 2291/2291/3440 2290/2290/3439
+f 2251/2251/3391 2292/2292/3441 2291/2291/3440
+f 2252/2252/3392 2293/2293/3490 2292/2292/3441
+f 2253/2253/3393 2294/2294/3443 2293/2293/3442
+f 2254/2255/3396 2295/2296/3445 2294/2294/3443
+f 2254/2255/3396 2255/2254/3395 2296/2295/3444
+f 2255/2254/3395 2256/2256/3397 2297/2297/3446
+f 2256/2256/3397 2257/2257/3399 2298/2298/3447
+f 2257/2257/3399 2258/2258/3400 2299/2299/3449
+f 2258/2258/3400 2259/2259/3401 2300/2300/3450
+f 2260/2260/3403 2301/2301/3451 2300/2300/3450
+f 2261/2261/3404 2302/2302/3452 2301/2301/3451
+f 2262/2262/3405 2303/2303/3503 2302/2302/3452
+f 2263/2263/3406 2304/2304/3454 2303/2303/3453
+f 2264/2265/3409 2305/2306/3456 2304/2304/3454
+f 2264/2265/3409 2265/2264/3408 2306/2305/3455
+f 2265/2264/3408 2266/2266/3410 2307/2307/3457
+f 2266/2266/3410 2267/2267/3412 2308/2308/3458
+f 2267/2267/3412 2268/2268/3413 2309/2309/3460
+f 2268/2268/3413 2269/2269/3414 2310/2310/3461
+f 2270/2270/3415 2311/2311/3462 2310/2310/3461
+f 2271/2271/3416 2312/2312/3463 2311/2311/3462
+f 2272/2272/3417 2313/2313/3516 2312/2312/3463
+f 2273/2273/3418 2314/2314/3465 2313/2313/3464
+f 2274/2275/3421 2315/2316/3467 2314/2314/3465
+f 2274/2275/3421 2275/2274/3420 2316/2315/3466
+f 2275/2274/3420 2276/2276/3422 2317/2317/3468
+f 2276/2276/3422 2277/2277/3424 2318/2318/3469
+f 2277/2277/3424 2278/2278/3425 2319/2319/3471
+f 2278/2278/3425 2279/2279/3426 2320/2320/3472
+f 2280/2280/3428 2321/2321/3473 2320/2320/3472
+f 2281/2281/3429 2322/2322/3474 2321/2321/3473
+f 2282/2282/3430 2284/2283/3477 2322/2322/3474
+f 2284/2283/3477 2323/2324/3476 2324/2323/3475
+f 2283/2284/3432 2325/2325/3478 2323/2324/3476
+f 2285/2286/3434 2326/2327/3480 2325/2325/3478
+f 2285/2286/3434 2286/2285/3433 2327/2326/3479
+f 2286/2285/3433 2287/2287/3437 2328/2328/3481
+f 2287/2287/3435 2288/2288/3436 2329/2329/3482
+f 2288/2288/3436 2289/2289/3438 2330/2330/3483
+f 2289/2289/3438 2290/2290/3439 2331/2331/3485
+f 2291/2291/3440 2332/2332/5445 2331/2331/3485
+f 2292/2292/3441 2333/2333/5446 2332/2332/3487
+f 2293/2293/3490 2334/2334/3489 2333/2333/3488
+f 2294/2294/3443 2335/2335/3491 2334/2334/3489
+f 2295/2296/3445 2336/2337/3493 2335/2335/3491
+f 2295/2296/3445 2296/2295/3444 2337/2336/3492
+f 2296/2295/3444 2297/2297/3448 2338/2338/3494
+f 2297/2297/3446 2298/2298/3447 2339/2339/3495
+f 2298/2298/3447 2299/2299/3449 2340/2340/3496
+f 2299/2299/3449 2300/2300/3450 2341/2341/3498
+f 2301/2301/3451 2342/2342/3556 2341/2341/3498
+f 2302/2302/3452 2343/2343/5447 2342/2342/3500
+f 2303/2303/3503 2344/2344/3502 2343/2343/3501
+f 2304/2304/3454 2345/2345/3504 2344/2344/3502
+f 2305/2306/3456 2346/2347/3506 2345/2345/3504
+f 2305/2306/3456 2306/2305/3455 2347/2346/3505
+f 2306/2305/3455 2307/2307/3459 2348/2348/3507
+f 2307/2307/3457 2308/2308/3458 2349/2349/3508
+f 2308/2308/3458 2309/2309/3460 2350/2350/3509
+f 2309/2309/3460 2310/2310/3461 2351/2351/3511
+f 2311/2311/3462 2352/2352/5448 2351/2351/3511
+f 2312/2312/3463 2353/2353/5449 2352/2352/3513
+f 2313/2313/3516 2354/2354/3515 2353/2353/3514
+f 2314/2314/3465 2355/2355/3517 2354/2354/3515
+f 2315/2316/3467 2356/2357/3519 2355/2355/3517
+f 2315/2316/3467 2316/2315/3466 2357/2356/3518
+f 2316/2315/3466 2317/2317/3470 2358/2358/3520
+f 2317/2317/3468 2318/2318/3469 2359/2359/3521
+f 2318/2318/3469 2319/2319/3471 2360/2360/3522
+f 2319/2319/3471 2320/2320/3472 2361/2361/3524
+f 2321/2321/3473 2362/2362/3587 2361/2361/3524
+f 2322/2322/3474 2324/2323/5450 2362/2362/3526
+f 2324/2323/5450 2363/2364/3529 2364/2363/3527
+f 2323/2324/3476 2365/2365/3531 2363/2364/3529
+f 2325/2325/3478 2366/2366/3532 2365/2365/3531
+f 2326/2327/3480 2367/2368/3534 2366/2366/5451
+f 2326/2327/3480 2327/2326/3479 2368/2367/3536
+f 2327/2326/5452 2328/2328/3481 2369/2369/3535
+f 2328/2328/3481 2329/2329/3482 2370/2370/3537
+f 2329/2329/3482 2330/2330/5453 2371/2371/3538
+f 2330/2330/3483 2331/2331/3542 2372/2372/3540
+f 2332/2332/5454 2373/2373/3543 2372/2372/3540
+f 2333/2333/3488 2374/2374/3545 2373/2373/3543
+f 2334/2334/3489 2375/2375/3547 2374/2374/3545
+f 2335/2335/3491 2376/2376/3548 2375/2375/3547
+f 2336/2337/3493 2377/2378/3550 2376/2376/5455
+f 2336/2337/3493 2337/2336/3492 2378/2377/3552
+f 2337/2336/5456 2338/2338/3494 2379/2379/3551
+f 2338/2338/3494 2339/2339/3495 2380/2380/3553
+f 2339/2339/3495 2340/2340/5457 2381/2381/3554
+f 2340/2340/3499 2341/2341/3557 2382/2382/3555
+f 2342/2342/3556 2383/2383/3558 2382/2382/3555
+f 2343/2343/5447 2384/2384/3560 2383/2383/3558
+f 2344/2344/3502 2385/2385/3562 2384/2384/3560
+f 2345/2345/3504 2386/2386/3563 2385/2385/3562
+f 2346/2347/3506 2387/2388/3565 2386/2386/5458
+f 2346/2347/3506 2347/2346/3505 2388/2387/3567
+f 2347/2346/5459 2348/2348/3507 2389/2389/3566
+f 2348/2348/3507 2349/2349/3508 2390/2390/3568
+f 2349/2349/3508 2350/2350/5460 2391/2391/3569
+f 2350/2350/3509 2351/2351/3573 2392/2392/3571
+f 2352/2352/5461 2393/2393/3574 2392/2392/3571
+f 2353/2353/3514 2394/2394/3576 2393/2393/3574
+f 2354/2354/3515 2395/2395/3578 2394/2394/3576
+f 2355/2355/3517 2396/2396/3579 2395/2395/3578
+f 2356/2357/3519 2397/2398/3581 2396/2396/5462
+f 2356/2357/3519 2357/2356/3518 2398/2397/3583
+f 2357/2356/5463 2358/2358/3520 2399/2399/3582
+f 2358/2358/3520 2359/2359/3521 2400/2400/3584
+f 2359/2359/3521 2360/2360/5464 2401/2401/3585
+f 2360/2360/3525 2361/2361/3588 2402/2402/3586
+f 2362/2362/3587 2364/2363/3527 2402/2402/3586
+f 2403/2403/3589 2404/2403/3592 2405/2403/3590
+f 2404/2403/3592 2407/2403/3594 2408/2403/3593
+f 2407/2403/3594 2409/2403/5465 2410/2403/3595
+f 2409/2403/5465 2411/2403/5466 2412/2403/3597
+f 2411/2403/5466 2413/2403/3600 2414/2403/3600
+f 2413/2403/3600 2415/2403/3602 2416/2403/3752
+f 2415/2403/3602 2417/2403/5467 2418/2403/3603
+f 2417/2403/5467 2419/2403/3606 2420/2403/3605
+f 2419/2403/3606 2421/2403/3608 2422/2403/3607
+f 2421/2403/5468 2423/2403/5469 2424/2403/5470
+f 2424/2403/5470 2423/2403/5469 2426/2403/5471
+f 2427/2403/5472 2428/2403/3615 2424/2403/3609
+f 2429/2404/3618 2430/2406/3617 2428/2403/3615
+f 2431/2405/3616 2432/2408/3620 2430/2406/3617
+f 2433/2407/3619 2434/2410/3622 2432/2408/3620
+f 2435/2409/3621 2436/2412/5473 2434/2410/3622
+f 2437/2411/3623 2438/2414/3627 2436/2412/5473
+f 2439/2413/3631 2440/2416/3630 2438/2414/3627
+f 2441/2415/3995 2442/2418/3633 2440/2416/3630
+f 2443/2417/3637 2444/2420/3636 2442/2418/3633
+f 2445/2419/5474 2446/2422/3639 2444/2420/3636
+f 2447/2421/3638 2448/2424/3641 2446/2422/3639
+f 2449/2423/3640 2450/2426/3643 2448/2424/3641
+f 2451/2425/3642 2452/2428/3645 2450/2426/3643
+f 2453/2427/3644 2454/2430/3647 2452/2428/3645
+f 2455/2429/3646 2456/2432/3650 2454/2430/3647
+f 2457/2431/3649 2458/2434/3652 2456/2432/3650
+f 2459/2433/3651 2460/2436/3655 2458/2434/3652
+f 2461/2435/3654 2462/2438/3657 2460/2436/5475
+f 2463/2437/3656 2464/3156/5476 2462/2438/3657
+f 2462/2438/3657 2464/3156/5476 2466/2439/3658
+f 2460/2436/5475 2462/2438/3657 2465/2440/3659
+f 2458/2434/3652 2460/2436/3655 2467/2441/3660
+f 2456/2432/3650 2458/2434/3652 2468/2442/3662
+f 2454/2430/3647 2456/2432/3650 2469/2443/3663
+f 2452/2428/3645 2454/2430/3647 2470/2444/3664
+f 2450/2426/3643 2452/2428/3645 2471/2445/3666
+f 2448/2424/3641 2450/2426/3643 2472/2446/3667
+f 2446/2422/3639 2448/2424/3641 2473/2447/3668
+f 2444/2420/3636 2446/2422/3639 2474/2448/5477
+f 2442/2418/3633 2444/2420/3636 2475/2449/3670
+f 2440/2416/3630 2442/2418/3633 2476/2450/3697
+f 2438/2414/3627 2440/2416/3630 2477/2451/3674
+f 2436/2412/5473 2438/2414/3627 2478/2452/3676
+f 2434/2410/3622 2436/2412/5473 2479/2453/3678
+f 2432/2408/3620 2434/2410/3622 2480/2454/3679
+f 2430/2406/3617 2432/2408/3620 2481/2455/3680
+f 2428/2403/3615 2430/2406/3617 2482/2406/3681
+f 2424/2403/3609 2428/2403/3615 2483/2403/5478
+f 2422/2403/3684 2483/2403/3683 2484/2403/3685
+f 2483/2403/3683 2482/2406/3681 2485/2406/3687
+f 2482/2406/3682 2481/2455/3680 2486/2455/3688
+f 2481/2455/3680 2480/2454/3679 2487/2456/3689
+f 2480/2454/3679 2479/2453/5479 2488/2457/3691
+f 2479/2453/3677 2478/2452/3675 2489/2458/3692
+f 2477/2451/5480 2476/2450/5481 2491/2460/5482
+f 2476/2450/3697 2475/2449/3671 2492/2461/3698
+f 2475/2449/3670 2474/2448/3701 2493/2462/3699
+f 2474/2448/3669 2473/2447/3668 2494/2463/3731
+f 2473/2447/3668 2472/2446/5483 2495/2464/3703
+f 2472/2446/5483 2471/2445/3666 2496/2465/3705
+f 2467/2441/3660 2465/2440/3659 2501/2470/3718
+f 2465/2440/3659 2466/2439/3658 2502/2471/5484
+f 2501/2470/3718 2502/2471/5484 2504/2472/3719
+f 2494/2463/3731 2495/2464/3703 2510/2479/3732
+f 2493/2462/3699 2494/2463/3731 2511/2480/3733
+f 2492/2461/3700 2493/2462/3699 2512/2481/3735
+f 2491/2460/5485 2492/2461/3700 2513/2482/3736
+f 2487/2456/5486 2488/2457/3691 2517/2486/3744
+f 2486/2455/3688 2487/2456/3689 2518/2487/3746
+f 2485/2406/5487 2486/2455/3688 2519/2488/3757
+f 2484/2403/3685 2485/2406/3687 2520/2406/3748
+f 2420/2403/3605 2484/2403/3685 2521/2403/3749
+f 2418/2403/3750 2521/2403/3749 2522/2403/3751
+f 2521/2403/3749 2520/2406/5488 2523/2406/3753
+f 2520/2406/3748 2519/2488/3747 2524/2489/3756
+f 2519/2488/3747 2518/2487/3745 2525/2490/3758
+f 2518/2487/3759 2517/2486/3744 2526/2491/3762
+f 2515/2484/3765 2514/2483/3738 2529/2494/3768
+f 2514/2483/3738 2513/2482/3736 2530/2495/3770
+f 2513/2482/3737 2512/2481/3735 2531/2496/3798
+f 2512/2481/3734 2511/2480/3733 2532/2497/3774
+f 2511/2480/3733 2510/2479/3732 2533/2498/3773
+f 2510/2479/5489 2509/2478/3727 2534/2499/3775
+f 2503/2473/5490 2504/2472/5491 2540/2505/3784
+f 2539/2504/3783 2540/2505/3784 2542/2506/5492
+f 2538/2503/3781 2539/2504/5493 2541/2507/3786
+f 2537/2502/3780 2538/2503/3781 2543/2508/3788
+f 2536/2501/3779 2537/2502/3780 2544/2509/3790
+f 2535/2500/3777 2536/2501/3779 2545/2510/3791
+f 2534/2499/3776 2535/2500/3777 2546/2511/5494
+f 2533/2498/3773 2534/2499/3775 2547/2512/3795
+f 2532/2497/3771 2533/2498/3773 2548/2513/3796
+f 2531/2496/3769 2532/2497/3774 2549/2514/3799
+f 2530/2495/3770 2531/2496/3769 2550/2515/3800
+f 2529/2494/3768 2530/2495/3770 2551/2516/5495
+f 2528/2493/3764 2529/2494/3766 2552/2517/3803
+f 2527/2492/3763 2528/2493/3764 2553/2518/3804
+f 2526/2491/3762 2527/2492/3763 2554/2519/3806
+f 2525/2490/3758 2526/2491/3760 2555/2520/3808
+f 2524/2489/3756 2525/2490/3761 2556/2521/3809
+f 2523/2406/3753 2524/2489/3756 2557/2489/3811
+f 2522/2403/3751 2523/2406/3753 2558/2403/3812
+f 2416/2403/3752 2522/2403/3751 2559/2403/5496
+f 2414/2403/3600 2559/2403/5497 2560/2403/3818
+f 2559/2403/5497 2558/2403/3812 2561/2403/3817
+f 2558/2403/3812 2557/2489/3810 2562/2522/3822
+f 2557/2489/3810 2556/2521/3809 2563/2523/3824
+f 2556/2521/3809 2555/2520/3808 2564/2524/3823
+f 2555/2520/3808 2554/2519/3806 2565/2525/5498
+f 2554/2519/3805 2553/2518/3804 2566/2526/3826
+f 2553/2518/5499 2552/2517/5500 2567/2527/3827
+f 2552/2517/3803 2551/2516/3802 2568/2528/3828
+f 2551/2516/3802 2550/2515/3800 2569/2529/3829
+f 2550/2515/3831 2549/2514/3799 2570/2530/3832
+f 2549/2514/5501 2548/2513/3796 2571/2531/3833
+f 2548/2513/3796 2547/2512/3795 2572/2532/3834
+f 2547/2512/3795 2546/2511/5502 2573/2533/3835
+f 2546/2511/5494 2545/2510/3791 2574/2534/3836
+f 2545/2510/3791 2544/2509/3790 2575/2535/3837
+f 2544/2509/3790 2543/2508/3788 2576/2536/3838
+f 2543/2508/3788 2541/2507/3786 2577/2537/3839
+f 2541/2507/3786 2542/2506/3785 2578/2538/3841
+f 2577/2537/3842 2578/2538/3841 2580/2539/5503
+f 2570/2530/3832 2571/2531/3833 2586/2546/3858
+f 2569/2529/3829 2570/2530/3832 2587/2547/3859
+f 2568/2528/3830 2569/2529/3829 2588/2548/3861
+f 2567/2527/3827 2568/2528/3830 2589/2549/3862
+f 2562/2522/3819 2563/2523/3824 2594/2554/3870
+f 2561/2403/3817 2562/2522/3819 2595/2555/3871
+f 2560/2403/3818 2561/2403/5504 2596/2403/3872
+f 2412/2403/3597 2560/2403/3818 2597/2403/3875
+f 2410/2403/5505 2597/2403/3875 2598/2403/3877
+f 2597/2403/3874 2596/2403/3872 2599/2403/3880
+f 2596/2403/3872 2595/2555/3871 2600/2556/3879
+f 2595/2555/3871 2594/2554/3869 2601/2557/3881
+f 2590/2550/3863 2589/2549/3862 2606/2562/3890
+f 2589/2549/3862 2588/2548/3861 2607/2563/3892
+f 2588/2548/3861 2587/2547/3859 2608/2564/3893
+f 2587/2547/3859 2586/2546/3855 2609/2565/3894
+f 2579/2540/3845 2580/2539/5506 2616/2572/3904
+f 2615/2571/5507 2616/2572/3904 2618/2573/3906
+f 2608/2564/3893 2609/2565/3894 2624/2580/3916
+f 2607/2563/3892 2608/2564/3893 2625/2581/3919
+f 2606/2562/3890 2607/2563/3892 2626/2582/3920
+f 2605/2561/3888 2606/2562/3890 2627/2583/3921
+f 2601/2557/3881 2602/2558/3883 2631/2587/3928
+f 2600/2556/3879 2601/2557/3881 2632/2588/3932
+f 2599/2403/3880 2600/2556/3933 2633/2556/3934
+f 2598/2403/5508 2599/2403/3880 2634/2589/3935
+f 2408/2403/3593 2598/2403/3877 2635/2403/3936
+f 2405/2403/3590 2635/2403/3936 2636/2589/3938
+f 2635/2403/3936 2634/2589/3935 2637/2589/3939
+f 2634/2589/3935 2633/2556/3934 2638/2590/3940
+f 2633/2556/3934 2632/2588/3932 2639/2591/3941
+f 2632/2588/3932 2631/2587/3928 2640/2592/3942
+f 2631/2587/3928 2630/2586/3926 2641/2593/3943
+f 2630/2586/3926 2629/2585/3923 2642/2594/3945
+f 2629/2585/5509 2628/2584/5510 2643/2595/3946
+f 2628/2584/5510 2627/2583/3921 2644/2596/3948
+f 2627/2583/3921 2626/2582/3950 2645/2597/3949
+f 2626/2582/3950 2625/2581/3919 2646/2598/3953
+f 2625/2581/5511 2624/2580/3916 2647/2599/3952
+f 2624/2580/3916 2623/2579/3914 2648/2600/3954
+f 2623/2579/3914 2622/2578/3913 2649/2601/3955
+f 2622/2578/3913 2621/2577/3911 2650/2602/3956
+f 2621/2577/3911 2620/2576/3910 2651/2603/3957
+f 2620/2576/3910 2619/2575/3909 2652/2604/3958
+f 2619/2575/3908 2617/2574/3907 2653/2605/3959
+f 2617/2574/3960 2618/2573/3906 840/2606/3961
+f 840/2606/3961 2654/2608/3963 2655/2607/3962
+f 2654/2608/3963 2656/2610/3965 2657/2609/3964
+f 2656/2610/3965 2658/2612/5512 2659/2611/3969
+f 2658/2612/5513 2660/2614/3970 2661/2613/3968
+f 2660/2614/3970 2662/2616/3973 2663/2615/3971
+f 2662/2616/5514 2664/2618/3975 2665/2617/3974
+f 2664/2618/3975 2666/2620/5515 2667/2619/3976
+f 2666/2620/3977 2668/2622/3979 2669/2621/3978
+f 2668/2622/5516 2463/2437/3656 2461/2435/3654
+f 2669/2621/3978 2461/2435/3654 2459/2433/3651
+f 2674/2627/3988 2451/2425/3642 2449/2423/5517
+f 2675/2628/3989 2449/2423/5517 2447/2421/3638
+f 2676/2629/4030 2447/2421/3638 2445/2419/3635
+f 2677/2630/3993 2445/2419/3992 2443/2417/3637
+f 2678/2631/3994 2443/2417/3632 2441/2415/3995
+f 2679/2632/3996 2441/2415/5518 2439/2413/5519
+f 2681/2634/3999 2437/2411/3628 2435/2409/3625
+f 2682/2635/4001 2435/2409/3621 2433/2407/3619
+f 2683/2636/4003 2433/2407/3619 2431/2405/3616
+f 2684/2637/4004 2431/2405/3616 2429/2404/3614
+f 2685/2404/4005 2429/2404/3618 2427/2403/3612
+f 2686/2403/4006 2427/2403/3612 2425/2403/3613
+f 2425/2403/3611 2426/2403/3610 2688/2403/4008
+f 2687/2403/4009 2688/2403/4008 2690/2403/5520
+f 2691/2403/4012 2686/2403/4006 2687/2403/4009
+f 2692/2404/4013 2685/2404/4005 2686/2403/4006
+f 2693/2404/4065 2684/2637/4004 2685/2404/4005
+f 2694/2638/4015 2683/2636/4003 2684/2637/4004
+f 2695/2639/4018 2682/2635/4001 2683/2636/5521
+f 2699/2643/4026 2678/2631/5522 2679/2632/3996
+f 2700/2644/4025 2677/2630/3993 2678/2631/5522
+f 2701/2645/4027 2676/2629/4030 2677/2630/3993
+f 2702/2646/5523 2675/2628/5524 2676/2629/5525
+f 2712/2656/4047 2703/2647/5526 2702/2646/5523
+f 2713/2657/4048 2702/2646/4029 2701/2645/4027
+f 2714/2658/4049 2701/2645/4027 2700/2644/4028
+f 2715/2659/4093 2700/2644/4025 2699/2643/4023
+f 2716/2660/4053 2699/2643/4026 2698/2642/5527
+f 2717/2661/4055 2698/2642/4024 2697/2641/4019
+f 2720/2664/4061 2695/2639/4016 2694/2638/4062
+f 2721/2665/4064 2694/2638/5528 2693/2404/4014
+f 2722/2666/4066 2693/2404/4014 2692/2404/4013
+f 2723/2666/4068 2692/2404/5529 2691/2403/4012
+f 2724/2403/4070 2691/2403/4012 2689/2403/4071
+f 2689/2403/4011 2690/2403/5520 2726/2403/4073
+f 2725/2403/4072 2726/2403/4073 2728/2403/4075
+f 2729/2589/5530 2724/2403/4070 2725/2403/4072
+f 2730/2666/4080 2723/2666/4068 2724/2403/4070
+f 2731/2666/4079 2722/2666/4066 2723/2666/4068
+f 2732/2667/4081 2721/2665/4063 2722/2666/4066
+f 2733/2668/4083 2720/2664/4061 2721/2665/4064
+f 2734/2669/4084 2719/2663/4058 2720/2664/4060
+f 2735/2670/4086 2718/2662/4057 2719/2663/4059
+f 2736/2671/4088 2717/2661/4056 2718/2662/5531
+f 2737/2672/5532 2716/2660/4053 2717/2661/4055
+f 2738/2673/4090 2715/2659/4052 2716/2660/4053
+f 2739/2674/4092 2714/2658/4049 2715/2659/4052
+f 2740/2675/4094 2713/2657/4048 2714/2658/4050
+f 2741/2676/4096 2712/2656/4047 2713/2657/4048
+f 2742/2677/5533 2711/2655/4046 2712/2656/4047
+f 2743/2678/4098 2710/2654/4044 2711/2655/4046
+f 2744/2679/4099 2709/2653/4043 2710/2654/4045
+f 2745/2680/4100 2708/2652/4042 2709/2653/4043
+f 2663/2615/3971 2665/2617/3974 2708/2652/5534
+f 2661/2613/3972 2663/2615/3971 2745/2680/4100
+f 2746/2681/4103 2745/2680/4100 2744/2679/4099
+f 2747/2682/4104 2744/2679/4099 2743/2678/4098
+f 2748/2683/4106 2743/2678/4098 2742/2677/5533
+f 2749/2684/4107 2742/2677/5535 2741/2676/4096
+f 2750/2685/4108 2741/2676/4096 2740/2675/4094
+f 2751/2686/4109 2740/2675/4094 2739/2674/5536
+f 2752/2687/4111 2739/2674/4092 2738/2673/4112
+f 2753/2688/4113 2738/2673/4090 2737/2672/4089
+f 2754/2689/4115 2737/2672/4089 2736/2671/4088
+f 2755/2690/4117 2736/2671/5537 2735/2670/5538
+f 2756/2691/5539 2735/2670/4086 2734/2669/4087
+f 2757/2692/4119 2734/2669/4084 2733/2668/4083
+f 2758/2693/4121 2733/2668/4083 2732/2667/4081
+f 2759/2694/4123 2732/2667/4081 2731/2666/4082
+f 2760/2695/4124 2731/2666/4082 2730/2666/4080
+f 2761/2589/4127 2730/2666/4080 2729/2589/5540
+f 2762/2403/4128 2729/2589/5540 2727/2403/4075
+f 2727/2403/4075 2728/2403/4075 2764/2403/5541
+f 2763/2403/4130 2764/2403/5541 2766/2403/5542
+f 2767/2589/4134 2762/2403/4128 2763/2403/4130
+f 2768/2589/4136 2761/2589/5543 2762/2403/4128
+f 2769/2695/4139 2760/2695/4125 2761/2589/4127
+f 2770/2696/4140 2759/2694/4122 2760/2695/4125
+f 2775/2701/4152 2754/2689/4114 2755/2690/4116
+f 2776/2702/4154 2753/2688/4113 2754/2689/4114
+f 2777/2703/4158 2752/2687/4111 2753/2688/4113
+f 2778/2704/4156 2751/2686/4109 2752/2687/4111
+f 2789/2715/4176 2778/2704/4156 2777/2703/4158
+f 2790/2716/4178 2777/2703/4158 2776/2702/4154
+f 2791/2717/4179 2776/2702/4154 2775/2701/4152
+f 2792/2718/4181 2775/2701/4152 2774/2700/4153
+f 2797/2723/4190 2770/2696/4140 2769/2695/4139
+f 2798/2695/4191 2769/2695/4139 2768/2589/4136
+f 2799/2589/4192 2768/2589/4136 2767/2589/4138
+f 2800/2589/4194 2767/2589/4138 2765/2403/5544
+f 2765/2403/4135 2766/2403/5542 2802/2403/4197
+f 2801/2403/4196 2802/2403/4197 2804/2403/4198
+f 2805/2589/4200 2800/2589/4194 2801/2403/4196
+f 2806/2589/4202 2799/2589/4192 2800/2589/5545
+f 2807/2590/4203 2798/2695/4205 2799/2589/4192
+f 2808/2724/4204 2797/2723/4190 2798/2695/4191
+f 2809/2725/4206 2796/2722/4188 2797/2723/4190
+f 2813/2729/4211 2792/2718/4181 2793/2719/4182
+f 2814/2730/4212 2791/2717/4179 2792/2718/4181
+f 2815/2731/4213 2790/2716/4178 2791/2717/4179
+f 2816/2732/4216 2789/2715/4176 2790/2716/4178
+f 2653/2605/3959 2655/2607/3962 2821/2737/5546
+f 2652/2604/3958 2821/2737/4222 2820/2736/5547
+f 2651/2603/3957 2820/2736/4221 2819/2735/4218
+f 2650/2602/3956 2819/2735/4218 2818/2734/4217
+f 2649/2601/3955 2818/2734/4217 2817/2733/4215
+f 2648/2600/3954 2817/2733/5548 2816/2732/4216
+f 2647/2599/3952 2816/2732/4214 2815/2731/5549
+f 2646/2598/3953 2815/2731/4213 2814/2730/4225
+f 2645/2597/3949 2814/2730/4212 2813/2729/4211
+f 2644/2596/3948 2813/2729/4211 2812/2728/4226
+f 2643/2595/3946 2812/2728/4210 2811/2727/4208
+f 2642/2594/3945 2811/2727/4208 2810/2726/4207
+f 2641/2593/3943 2810/2726/5550 2809/2725/4206
+f 2640/2592/3942 2809/2725/4206 2808/2724/4204
+f 2639/2591/3941 2808/2724/4204 2807/2590/4203
+f 2638/2590/3940 2807/2590/4203 2806/2589/4202
+f 2637/2589/3939 2806/2589/4202 2805/2589/4200
+f 2636/2589/3938 2805/2589/4200 2803/2403/4199
+f 2803/2403/4199 2804/2403/4198 2403/2403/3589
+f 2832/2748/4239 2833/2750/4241 2834/2749/4240
+f 2840/2756/5551 2841/3157/5552 2842/2757/4248
+f 2842/2757/4248 2841/3157/5553 2844/2758/4249
+f 2846/2760/4251 2842/2757/4248 2843/2759/4252
+f 2848/2762/4254 2846/2760/5554 2845/2761/4253
+f 2850/2764/4256 2848/2762/4254 2847/2763/4255
+f 2852/2766/4261 2850/2764/4256 2849/2765/4257
+f 2863/2778/4275 2864/2780/4277 2862/2777/4273
+f 2865/2779/4276 2866/2782/4279 2864/2780/4277
+f 2867/2781/5555 2868/2784/4281 2866/2782/4279
+f 2869/2783/4280 2870/2786/5556 2868/2784/4281
+f 2871/2785/5557 2872/2786/5558 2870/2786/5559
+f 2874/2787/5560 2872/2786/5558 2871/2785/4286
+f 2876/2789/4288 2874/2787/4285 2873/2788/4287
+f 2878/2791/4291 2876/2789/4288 2875/2790/4290
+f 2880/2793/4293 2878/2791/4291 2877/2792/4292
+f 2882/2795/4295 2880/2793/4293 2879/2794/4294
+f 2883/2797/4297 2880/2793/4293 2882/2795/4295
+f 2885/2799/5561 2878/2791/4291 2880/2793/4293
+f 2886/2800/4300 2876/2789/4288 2878/2791/4291
+f 2887/2801/4304 2874/2787/4285 2876/2789/4288
+f 2888/2802/4303 2872/2786/5562 2874/2787/5560
+f 2870/2786/5559 2872/2786/5558 2888/2802/4306
+f 2868/2784/4281 2870/2786/5556 2889/2803/4307
+f 2866/2782/4279 2868/2784/4281 2890/2804/4339
+f 2864/2780/4277 2866/2782/4279 2891/2805/4310
+f 2862/2777/5563 2864/2780/4277 2892/2806/4311
+f 2899/2813/4321 2850/2764/4256 2852/2766/4261
+f 2900/2814/4320 2848/2762/4254 2850/2764/4256
+f 2901/2815/4322 2846/2760/5554 2848/2762/4254
+f 2839/2755/4246 2842/2757/4248 2846/2760/4251
+f 2837/2753/4244 2839/2755/4246 2901/2815/4322
+f 2902/2816/4323 2901/2815/4322 2900/2814/4320
+f 2893/2807/4312 2892/2806/4311 2911/2825/4335
+f 2892/2806/4311 2891/2805/4310 2912/2826/4336
+f 2891/2805/4310 2890/2804/4339 2913/2827/4338
+f 2889/2803/4308 2888/2802/4303 2915/2829/4342
+f 2915/2829/4342 2888/2802/4303 2887/2801/4302
+f 2916/2830/4343 2887/2801/5564 2886/2800/5565
+f 2917/2831/4345 2886/2800/4300 2885/2799/4301
+f 2918/2832/4347 2885/2799/4301 2883/2797/4297
+f 2919/2833/4348 2883/2797/4297 2884/2798/4349
+f 2921/2835/4351 2919/2833/5566 2920/2834/4350
+f 2923/2837/5567 2918/2832/4347 2919/2833/4348
+f 2924/2838/4357 2917/2831/4345 2918/2832/4346
+f 2925/2839/4356 2916/2830/4343 2917/2831/4345
+f 2926/2840/4358 2915/2829/5568 2916/2830/4343
+f 2914/2828/4341 2915/2829/4342 2926/2840/4358
+f 2911/2825/4337 2912/2826/4336 2929/2843/4362
+f 2910/2824/4333 2911/2825/4337 2930/2844/5569
+f 2835/2751/4382 2837/2753/4244 2902/2816/4323
+f 2834/2749/4240 2835/2751/5570 2939/2853/4380
+f 2931/2845/4365 2930/2844/5569 2949/2863/4400
+f 2930/2844/5571 2929/2843/4362 2950/2864/4402
+f 2929/2843/4362 2928/2842/4360 2951/2865/5572
+f 2928/2842/4360 2927/2841/4405 2952/2866/4404
+f 2927/2841/4405 2926/2840/4358 2953/2867/5573
+f 2953/2867/4406 2926/2840/4358 2925/2839/4356
+f 2954/2868/4407 2925/2839/5574 2924/2838/5575
+f 2955/2869/4409 2924/2838/5576 2923/2837/4353
+f 2956/2870/5577 2923/2837/4353 2921/2835/4351
+f 2957/2871/4412 2921/2835/4351 2922/2836/4352
+f 2959/2873/4414 2957/2871/5578 2958/2872/5579
+f 2961/2875/4417 2956/2870/5577 2957/2871/4411
+f 2962/2876/4421 2955/2869/4408 2956/2870/4410
+f 2963/2877/5580 2954/2868/5581 2955/2869/4408
+f 2964/2878/4422 2953/2867/4406 2954/2868/4407
+f 2952/2866/4424 2953/2867/4406 2964/2878/4422
+f 2951/2865/4426 2952/2866/4424 2965/2879/4468
+f 2950/2864/4402 2951/2865/5582 2966/2880/5583
+f 2949/2863/5584 2950/2864/4402 2967/2881/4428
+f 2948/2862/4430 2949/2863/4400 2968/2882/4429
+f 2972/2886/4435 2971/2885/4434 2984/2898/4457
+f 2971/2885/4436 2970/2884/4432 2985/2899/4458
+f 2969/2883/4431 2968/2882/4429 2987/2901/4462
+f 2968/2882/4429 2967/2881/4428 2988/2902/4464
+f 2967/2881/4428 2966/2880/4427 2989/2903/4467
+f 2966/2880/4427 2965/2879/4468 2990/2904/4470
+f 2965/2879/4468 2964/2878/4422 2991/2905/4469
+f 2991/2905/4469 2964/2878/4422 2963/2877/4420
+f 2992/2906/4473 2963/2877/5585 2962/2876/4418
+f 2993/2907/4474 2962/2876/4418 2961/2875/4417
+f 2994/2908/4475 2961/2875/4417 2959/2873/4414
+f 2995/2909/4481 2959/2873/4414 2960/2874/4416
+f 2997/2911/4478 2995/2909/4476 2996/2910/4477
+f 2999/2913/4480 2994/2908/4475 2995/2909/4476
+f 3000/2914/4482 2993/2907/4474 2994/2908/4475
+f 3001/2915/5586 2992/2906/5587 2993/2907/4474
+f 2991/2905/4471 2992/2906/5587 3001/2915/4483
+f 2990/2904/4466 2991/2905/4469 3002/2916/4486
+f 2989/2903/4467 2990/2904/4470 3003/2917/4488
+f 2988/2902/4464 2989/2903/4467 3004/2918/4489
+f 2987/2901/4462 2988/2902/4464 3005/2919/4491
+f 2986/2900/4461 2987/2901/4462 3006/2920/4494
+f 3012/2926/5588 2981/2895/4452 2982/2896/4454
+f 3013/2927/4504 2980/2894/4450 2981/2895/4452
+f 3014/2928/4505 2979/2893/4507 2980/2894/4450
+f 3017/2931/4512 3014/2928/4505 3013/2927/4504
+f 3006/2920/4494 3005/2919/4491 3026/2940/4521
+f 3005/2919/5589 3004/2918/4489 3027/2941/4524
+f 3004/2918/4489 3003/2917/4525 3028/2942/4523
+f 3003/2917/4525 3002/2916/4485 3029/2943/4526
+f 3002/2916/4486 3001/2915/4483 3030/2944/4528
+f 3030/2944/4528 3001/2915/5590 3000/2914/5591
+f 3031/2945/4529 3000/2914/4482 2999/2913/4480
+f 3032/2946/4530 2999/2913/4480 2997/2911/4478
+f 3035/2949/5592 3033/2947/4532 3034/2948/4533
+f 3037/2951/4882 3032/2946/4531 3033/2947/5593
+f 3038/2952/4541 3031/2945/4529 3032/2946/4530
+f 3039/2953/4540 3030/2944/5594 3031/2945/5595
+f 3029/2943/4526 3030/2944/5596 3039/2953/4542
+f 3028/2942/4523 3029/2943/4526 3040/2954/4543
+f 3027/2941/4524 3028/2942/4523 3041/2955/4884
+f 3026/2940/4546 3027/2941/4524 3042/2956/4545
+f 3025/2939/4548 3026/2940/4521 3043/2957/4549
+f 3024/2938/4519 3025/2939/4520 3044/2958/4550
+f 3052/2966/4889 3017/2931/4512 3018/2932/4513
+f 3053/2967/5597 3016/2930/4510 3017/2931/4512
+f 3054/2968/4564 2824/2740/4229 3053/2967/4562
+f 3058/2972/4864 3057/2971/4862 3054/2968/4564
+f 3060/2973/4866 3058/2972/4865 3055/2969/4566
+f 3062/2975/4572 3060/2973/4866 3059/2974/5598
+f 3064/2977/4575 3062/2975/4572 3061/2976/4574
+f 3066/2979/4868 3064/2977/4575 3063/2978/4576
+f 3075/2989/4594 3076/2991/4596 3074/2988/4592
+f 3077/2990/4595 3078/2993/4598 3076/2991/4596
+f 3079/2992/4597 3080/2995/4600 3078/2993/4598
+f 3081/2994/4599 3082/2997/4874 3080/2995/4600
+f 3083/2996/4601 3084/2999/5599 3082/2997/4602
+f 3085/2998/4603 3086/3155/4877 3084/2999/5599
+f 3088/3000/4605 3086/3155/4876 3085/2998/5600
+f 3090/3002/4608 3088/3000/4605 3087/3001/5601
+f 3092/3004/4615 3090/3002/4879 3089/3003/5602
+f 3094/3006/4612 3092/3004/4615 3091/3005/4611
+f 3095/3008/4614 3096/3010/5603 3092/3004/4615
+f 3099/3012/4621 3100/3014/4624 3097/3009/4620
+f 3101/3013/4623 3102/3016/4626 3100/3014/4830
+f 3103/3015/5604 3104/3018/5605 3102/3016/4626
+f 3105/3017/4627 3106/3020/4631 3104/3018/4628
+f 3107/3019/4630 3108/3021/5606 3106/3020/4631
+f 2881/2796/4632 2879/2794/4294 3108/3021/4633
+f 2879/2794/4294 2877/2792/4292 3109/3022/4634
+f 2877/2792/4292 2875/2790/4290 3110/3023/4635
+f 2875/2790/4290 2873/2788/5607 3111/3024/4637
+f 2873/2788/4289 2871/2785/4286 3112/3025/5608
+f 3112/3025/5608 2871/2785/5557 2869/2783/4284
+f 3114/3027/5609 2867/2781/5610 2865/2779/4276
+f 3115/3028/5611 2865/2779/4276 2863/2778/4275
+f 3116/3029/4646 2863/2778/4275 2861/2776/5612
+f 2847/2763/4255 2845/2761/4253 3125/3038/5613
+f 2845/2761/4253 2843/2759/4252 3126/3039/4663
+f 3125/3038/4661 3126/3039/4662 3128/3041/4734
+f 3138/3051/5614 3116/3029/4646 3117/3030/4686
+f 3139/3052/4687 3115/3028/4690 3116/3029/4646
+f 3142/3055/4694 3112/3025/4638 3113/3026/4641
+f 3111/3024/5615 3112/3025/5616 3142/3055/4694
+f 3110/3023/4635 3111/3024/4637 3143/3056/4696
+f 3109/3022/4636 3110/3023/4635 3144/3057/4697
+f 3108/3021/4633 3109/3022/4634 3145/3058/4703
+f 3106/3020/4631 3145/3058/4699 3146/3059/4701
+f 3145/3058/4699 3144/3057/4706 3147/3060/4704
+f 3144/3057/5617 3143/3056/4696 3148/3061/4707
+f 3143/3056/5618 3142/3055/4694 3149/3062/4709
+f 3149/3062/5619 3142/3055/4694 3141/3054/4692
+f 3150/3063/4710 3141/3054/4691 3140/3053/4689
+f 3151/3064/5620 3140/3053/4689 3139/3052/4687
+f 3152/3065/4713 3139/3052/4687 3138/3051/5621
+f 3153/3066/4715 3138/3051/5614 3137/3050/4684
+f 3129/3042/4670 3128/3041/4666 3163/3076/4732
+f 3163/3076/4732 3165/3078/4737 3167/3079/4738
+f 3177/3090/4756 3153/3066/4715 3154/3067/4717
+f 3178/3091/4759 3152/3065/4713 3153/3066/5622
+f 3179/3092/4760 3151/3064/5623 3152/3065/4713
+f 3180/3093/4777 3150/3063/4764 3151/3064/4762
+f 3181/3094/4763 3149/3062/4709 3150/3063/4710
+f 3148/3061/4707 3149/3062/4709 3181/3094/4763
+f 3147/3060/4708 3148/3061/5624 3182/3095/5625
+f 3146/3059/4705 3147/3060/4708 3183/3096/4767
+f 3104/3018/4702 3146/3059/4701 3184/3097/4770
+f 3102/3016/4626 3184/3097/4770 3185/3098/4771
+f 3184/3097/4770 3183/3096/4768 3186/3099/4772
+f 3183/3096/4768 3182/3095/4766 3187/3100/4773
+f 3182/3095/4766 3181/3094/4763 3188/3101/4776
+f 3188/3101/4776 3181/3094/4763 3180/3093/4777
+f 3189/3102/4778 3180/3093/4777 3179/3092/4760
+f 3190/3103/4780 3179/3092/4760 3178/3091/4759
+f 3191/3104/4782 3178/3091/4759 3177/3090/4756
+f 3192/3105/4783 3177/3090/4756 3176/3089/4755
+f 3194/3107/4787 3175/3088/4754 3174/3087/4753
+f 3195/3108/4789 3174/3087/4753 3173/3086/4751
+f 3199/3112/5626 3200/3113/4795 3206/3119/4808
+f 3198/3111/4792 3199/3112/4793 3207/3120/4809
+f 3197/3110/4791 3198/3111/5627 3208/3121/4810
+f 3214/3127/4817 3192/3105/4783 3193/3106/4784
+f 3215/3128/4818 3191/3104/4782 3192/3105/4783
+f 3216/3129/4821 3190/3103/4780 3191/3104/4782
+f 3217/3130/4823 3189/3102/4778 3190/3103/4780
+f 3218/3131/4824 3188/3101/4776 3189/3102/4779
+f 3219/3132/5628 3187/3100/5629 3188/3101/4774
+f 3186/3099/4772 3187/3100/4773 3219/3132/5630
+f 3185/3098/4771 3186/3099/4772 3220/3133/4828
+f 3100/3014/4624 3185/3098/4771 3221/3134/4829
+f 3097/3009/5631 3221/3134/4829 3222/3135/4833
+f 3221/3134/4829 3220/3133/4828 3223/3136/4832
+f 3220/3133/5632 3219/3132/4826 3224/3137/4834
+f 3224/3137/4834 3219/3132/5628 3218/3131/4824
+f 3225/3138/4836 3218/3131/4827 3217/3130/4837
+f 3226/3139/4838 3217/3130/4837 3216/3129/4821
+f 3227/3140/4839 3216/3129/4821 3215/3128/5633
+f 3228/3141/4841 3215/3128/4818 3214/3127/4817
+f 3207/3120/4809 3206/3119/4808 3237/3150/4855
+f 3237/3150/4855 3238/3151/4856 3058/2972/4865
+f 3236/3149/4854 3237/3150/4855 3060/2973/4570
+f 3076/2991/4596 3229/3142/4842 3230/3143/4844
+f 3078/2993/4872 3228/3141/4841 3229/3142/4842
+f 3080/2995/4600 3227/3140/4839 3228/3141/4873
+f 3082/2997/4602 3226/3139/4838 3227/3140/4839
+f 3084/2999/4604 3225/3138/4836 3226/3139/4838
+f 3086/3155/4876 3224/3137/5634 3225/3138/4836
+f 3223/3136/5635 3224/3137/5636 3086/3155/4877
+f 3222/3135/4833 3223/3136/4832 3088/3000/4605
+f 3096/3010/4618 3222/3135/4831 3090/3002/4608
+f 3091/3005/4611 3035/2949/4534 3036/2950/4880
+f 3089/3003/5602 3037/2951/4536 3035/2949/4534
+f 3087/3001/5601 3038/2952/4541 3037/2951/4882
+f 3085/2998/5600 3039/2953/4542 3038/2952/4541
+f 3040/2954/4543 3039/2953/4540 3085/2998/4603
+f 3041/2955/4544 3040/2954/5637 3083/2996/4601
+f 3042/2956/4545 3041/2955/5638 3081/2994/5639
+f 3043/2957/4547 3042/2956/4545 3079/2992/4597
+f 3044/2958/4550 3043/2957/4547 3077/2990/4595
+f 3045/2959/4553 3044/2958/4550 3075/2989/4594
+f 3063/2978/4576 3050/2964/4559 3049/2963/5640
+f 3061/2976/4574 3051/2965/4560 3050/2964/4559
+f 3059/2974/5598 3052/2966/4561 3051/2965/4560
+f 3055/2969/4566 3053/2967/4562 3052/2966/4561
diff --git a/engine/build.rs b/engine/build.rs
new file mode 100644
index 0000000..e9a8322
--- /dev/null
+++ b/engine/build.rs
@@ -0,0 +1,60 @@
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+use serde::Deserialize;
+
+fn main()
+{
+ let slang_artifacts = std::env::var("DEP_CRATE_SHADER_SLANG_ARTIFACTS").unwrap();
+
+ let cargo_metadata_output = Command::new(build_rs::input::cargo())
+ .args([
+ "metadata",
+ "--format-version=1",
+ "-q",
+ "--no-deps",
+ "--color=never",
+ "--manifest-path",
+ build_rs::input::cargo_manifest_path().to_str().unwrap(),
+ ])
+ .output()
+ .unwrap();
+
+ if !cargo_metadata_output.status.success() {
+ panic!(
+ "'cargo metadata' command failed:\n\nstdout:\n{}\n\nstderr:\n{}",
+ String::from_utf8_lossy(&cargo_metadata_output.stdout),
+ String::from_utf8_lossy(&cargo_metadata_output.stderr),
+ );
+ }
+
+ let cargo_metadata = serde_json::from_str::<CargoMetadata>(
+ str::from_utf8(&cargo_metadata_output.stdout).unwrap(),
+ )
+ .unwrap();
+
+ let final_artifact_dir = (if build_rs::input::target() == build_rs::input::host() {
+ cargo_metadata.target_directory
+ } else {
+ cargo_metadata
+ .target_directory
+ .join(build_rs::input::target())
+ })
+ .join(build_rs::input::profile());
+
+ for artifact_path in slang_artifacts.split(";") {
+ let artifact_path = Path::new(artifact_path);
+
+ std::fs::copy(
+ artifact_path,
+ final_artifact_dir.join(artifact_path.file_name().unwrap()),
+ )
+ .unwrap();
+ }
+}
+
+#[derive(Debug, Deserialize)]
+struct CargoMetadata
+{
+ target_directory: PathBuf,
+}
diff --git a/engine/res/FiraMono-Regular.ttf b/engine/res/FiraMono-Regular.ttf
new file mode 100644
index 0000000..67bbd42
--- /dev/null
+++ b/engine/res/FiraMono-Regular.ttf
Binary files differ
diff --git a/engine/res/imgui_shader.slang b/engine/res/imgui_shader.slang
new file mode 100644
index 0000000..29ad9bf
--- /dev/null
+++ b/engine/res/imgui_shader.slang
@@ -0,0 +1,54 @@
+struct Vertex
+{
+ float3 pos : STD_POSITION;
+ float4 color : STD_COLOR;
+ float2 texture_coords : STD_UV;
+};
+
+struct VertexData
+{
+ float4 color;
+ float2 texture_coords;
+};
+
+struct VertexStageOutput
+{
+ VertexData vertex_data : VertexData;
+ float4 sv_position : SV_Position;
+};
+
+struct Fragment
+{
+ float4 color;
+};
+
+cbuffer Uniforms
+{
+ float4x4 projection;
+};
+
+Sampler2D main_texture;
+
+[shader("vertex")]
+VertexStageOutput vertex_main(Vertex vertex)
+{
+ VertexStageOutput stage_output;
+
+ stage_output.sv_position = mul(projection, float4(vertex.pos.xy, 0.f, 1.f));
+
+ stage_output.vertex_data.color = vertex.color;
+ stage_output.vertex_data.texture_coords = vertex.texture_coords;
+
+ return stage_output;
+}
+
+[shader("fragment")]
+Fragment fragment_main(VertexData vertex_data: VertexData) : SV_Target
+{
+ Fragment fragment;
+
+ fragment.color = vertex_data.color * main_texture.Sample(vertex_data.texture_coords);
+
+ return fragment;
+}
+
diff --git a/engine/res/main_3d_shader.slang b/engine/res/main_3d_shader.slang
new file mode 100644
index 0000000..7da164c
--- /dev/null
+++ b/engine/res/main_3d_shader.slang
@@ -0,0 +1,270 @@
+#define MAX_LIGHT_CNT 64
+
+struct Material
+{
+ float3 ambient;
+ float3 diffuse;
+ float3 specular;
+ float shininess;
+};
+
+struct LightPhong
+{
+ float3 diffuse;
+ float3 specular;
+};
+
+struct AttenuationProperties
+{
+ float constant;
+ float linear;
+ float quadratic;
+};
+
+struct PointLight
+{
+ LightPhong phong;
+ float3 position;
+ AttenuationProperties attenuation_props;
+};
+
+struct DirectionalLight
+{
+ LightPhong phong;
+ float3 direction;
+};
+
+struct CalculatedLight
+{
+ float3 diffuse;
+ float3 specular;
+};
+
+struct BlinnPhongLighting
+{
+ float3 view_pos;
+ Material material;
+
+ DirectionalLight directional_lights[MAX_LIGHT_CNT];
+ uint directional_light_cnt;
+
+ PointLight point_lights[MAX_LIGHT_CNT];
+ uint point_light_cnt;
+
+ float4 evaluate(in VertexData vertex_data)
+ {
+ float3 ambient_light = this.calc_ambient_light(vertex_data.texture_coords);
+
+ float3 directional_light_sum = this.calc_dir_light_sum(vertex_data);
+ float3 point_light_sum = this.calc_point_light_sum(vertex_data);
+
+ return float4((ambient_light + directional_light_sum + point_light_sum), 1.0);
+ }
+
+ float3 calc_dir_light_sum(in VertexData vertex_data)
+ {
+ float3 directional_light_sum = float3(0.0, 0.0, 0.0);
+
+ for (uint index = 0; index < this.directional_light_cnt; index++) {
+ CalculatedLight calculated_dir_light;
+
+ this.calc_light(
+ // Negated since we want the light to point from the light
+ // direction
+ normalize(-this.directional_lights[index].direction),
+ this.directional_lights[index].phong,
+ vertex_data,
+ calculated_dir_light
+ );
+
+ directional_light_sum +=
+ calculated_dir_light.diffuse + calculated_dir_light.specular;
+ }
+
+ return directional_light_sum;
+ }
+
+ float3 calc_point_light_sum(in VertexData vertex_data)
+ {
+ float3 point_light_sum = float3(0.0, 0.0, 0.0);
+
+ for (uint index = 0; index < this.point_light_cnt; index++) {
+ float3 light_direction = normalize(
+ this.point_lights[index].position - vertex_data.world_space_pos
+ );
+
+ CalculatedLight calculated_point_light;
+
+ this.calc_light(
+ light_direction,
+ this.point_lights[index].phong,
+ vertex_data,
+ calculated_point_light
+ );
+
+ float attenuation = this.calc_attenuation(
+ this.point_lights[index], vertex_data.world_space_pos
+ );
+
+ calculated_point_light.diffuse *= attenuation;
+ calculated_point_light.specular *= attenuation;
+
+ point_light_sum +=
+ calculated_point_light.diffuse + calculated_point_light.specular;
+ }
+
+ return point_light_sum;
+ }
+
+ void calc_light(
+ in float3 light_direction,
+ in LightPhong light_phong,
+ in VertexData vertex_data,
+ out CalculatedLight calculated_light
+ )
+ {
+ float3 norm = normalize(vertex_data.world_space_normal);
+
+ calculated_light.diffuse = this.calc_diffuse_light(
+ light_phong, light_direction, norm, vertex_data.texture_coords
+ );
+
+ calculated_light.specular = this.calc_specular_light(
+ light_phong,
+ light_direction,
+ norm,
+ vertex_data.world_space_pos,
+ vertex_data.texture_coords
+ );
+ }
+
+ float3 calc_ambient_light(in float2 texture_coords)
+ {
+ return ambient_map.Sample(texture_coords).xyz * this.material.ambient;
+ }
+
+ float3 calc_diffuse_light(
+ in LightPhong light_phong,
+ in float3 light_dir,
+ in float3 norm,
+ in float2 texture_coords
+ )
+ {
+ float diff = max(dot(norm, light_dir), 0.0);
+
+ return light_phong.diffuse *
+ (diff * (diffuse_map.Sample(texture_coords).xyz * this.material.diffuse));
+ }
+
+ float3 calc_specular_light(
+ in LightPhong light_phong,
+ in float3 light_dir,
+ in float3 norm,
+ in float3 frag_pos,
+ in float2 texture_coords
+ )
+ {
+ // The result of the below pow is undefined if both arguments is 0. This stops
+ // that problem. This prevents buggy specular light when shininess is 0.
+ if (this.material.shininess == 0.0) {
+ return light_phong.specular *
+ (specular_map.Sample(texture_coords).xyz * this.material.specular);
+ }
+
+ float3 view_direction = normalize(this.view_pos - frag_pos);
+
+ float3 halfway_direction = normalize(light_dir + view_direction);
+
+ float spec = pow(max(dot(norm, halfway_direction), 0.0), this.material.shininess);
+
+ return light_phong.specular * (spec * (specular_map.Sample(texture_coords).xyz *
+ this.material.specular));
+ }
+
+ float calc_attenuation(in PointLight point_light, in float3 position)
+ {
+ float light_distance = length(point_light.position - position);
+
+ return 1.0 / (point_light.attenuation_props.constant +
+ point_light.attenuation_props.linear * light_distance +
+ point_light.attenuation_props.quadratic * pow(light_distance, 2));
+ }
+};
+
+struct Model3D
+{
+ float4x4 model;
+ float4x4 model_inverted;
+ float4x4 view;
+ float4x4 projection;
+}
+
+cbuffer Uniforms
+{
+ Model3D model_3d;
+ BlinnPhongLighting lighting;
+}
+
+Sampler2D ambient_map;
+Sampler2D diffuse_map;
+Sampler2D specular_map;
+
+struct VertexData
+{
+ float2 texture_coords;
+ float3 world_space_pos;
+ float3 world_space_normal;
+};
+
+struct VertexStageOutput
+{
+ VertexData vertex_data : VertexData;
+ float4 sv_position : SV_Position;
+};
+
+struct Vertex
+{
+ float3 pos : STD_POSITION;
+ float2 texture_coords : STD_UV;
+ float3 normal : STD_NORMAL;
+};
+
+struct Fragment
+{
+ float4 color;
+};
+
+[shader("vertex")]
+VertexStageOutput vertex_main(Vertex vertex)
+{
+ VertexStageOutput stage_output;
+
+ float4x4 proj_view = mul(model_3d.projection, model_3d.view);
+
+ float4x4 proj_view_model = mul(proj_view, model_3d.model);
+
+ stage_output.sv_position = mul(proj_view_model, float4(vertex.pos, 1.0));
+
+ float4 vertex_pos = float4(vertex.pos, 1.0);
+
+ stage_output.vertex_data.world_space_pos =
+ float3(mul(model_3d.model, vertex_pos).xyz);
+
+ stage_output.vertex_data.texture_coords = vertex.texture_coords;
+
+ stage_output.vertex_data.world_space_normal =
+ mul(float3x3(transpose(model_3d.model_inverted)), vertex.normal);
+
+ return stage_output;
+}
+
+[shader("fragment")]
+Fragment fragment_main(VertexData vertex_data: VertexData) : SV_Target
+{
+ Fragment fragment;
+
+ fragment.color = lighting.evaluate(vertex_data);
+
+ return fragment;
+}
+
diff --git a/engine/res/sky_box_shader.slang b/engine/res/sky_box_shader.slang
new file mode 100644
index 0000000..1a41220
--- /dev/null
+++ b/engine/res/sky_box_shader.slang
@@ -0,0 +1,34 @@
+SamplerCube cube_texture;
+
+cbuffer Uniforms
+{
+ float4x4 projection;
+ float4x4 view;
+}
+
+struct VertexStageOutput
+{
+ float4 sv_position : SV_Position;
+ float3 vertex_pos : VertexPosition;
+};
+
+[shader("vertex")]
+VertexStageOutput vertex_main(float3 pos: STD_POSITION)
+{
+ VertexStageOutput output;
+
+ output.vertex_pos = pos;
+
+ float4x4 proj_view = mul(projection, view);
+
+ output.sv_position = mul(proj_view, float4(pos, 1.0)).xyww;
+
+ return output;
+}
+
+[shader("fragment")]
+float4 fragment_main(float3 vertex_pos: VertexPosition) : SV_Target
+{
+ return cube_texture.Sample(vertex_pos);
+}
+
diff --git a/engine/res/ui/delete.png b/engine/res/ui/delete.png
new file mode 100644
index 0000000..f85ba1c
--- /dev/null
+++ b/engine/res/ui/delete.png
Binary files differ
diff --git a/engine/res/ui/edit.png b/engine/res/ui/edit.png
new file mode 100644
index 0000000..2431e79
--- /dev/null
+++ b/engine/res/ui/edit.png
Binary files differ
diff --git a/engine/res/ui/warning.png b/engine/res/ui/warning.png
new file mode 100644
index 0000000..4b8e6ad
--- /dev/null
+++ b/engine/res/ui/warning.png
Binary files differ
diff --git a/engine/src/asset.rs b/engine/src/asset.rs
new file mode 100644
index 0000000..eefda8d
--- /dev/null
+++ b/engine/src/asset.rs
@@ -0,0 +1,947 @@
+use std::any::{type_name, Any};
+use std::borrow::Cow;
+use std::cell::RefCell;
+use std::collections::HashMap;
+use std::convert::Infallible;
+use std::ffi::{OsStr, OsString};
+use std::fmt::{Debug, Display};
+use std::hash::{DefaultHasher, Hash, Hasher};
+use std::hint::cold_path;
+use std::marker::PhantomData;
+use std::path::{Path, PathBuf};
+use std::sync::mpsc::{
+ channel as mpsc_channel,
+ Receiver as MpscReceiver,
+ Sender as MpscSender,
+};
+use std::sync::Arc;
+
+use ecs::actions::Actions;
+
+use crate::ecs::pair::ChildOf;
+use crate::ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE};
+use crate::ecs::sole::Single;
+use crate::ecs::{declare_entity, pair, Sole};
+use crate::work_queue::{Work, WorkQueue};
+
+declare_entity! {
+pub HANDLE_ASSETS_PHASE: (Phase, pair!(ChildOf, { *PRE_UPDATE_PHASE }));
+}
+
+/// Asset label.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Label<'a>
+{
+ pub path: Cow<'a, Path>,
+ pub name: Option<Cow<'a, str>>,
+}
+
+impl Label<'_>
+{
+ pub fn to_owned(&self) -> LabelOwned
+ {
+ LabelOwned {
+ path: self.path.to_path_buf(),
+ name: self.name.as_ref().map(|name| name.to_string()),
+ }
+ }
+}
+
+impl<'a> From<&'a Path> for Label<'a>
+{
+ fn from(path: &'a Path) -> Self
+ {
+ Self { path: path.into(), name: None }
+ }
+}
+
+impl From<PathBuf> for Label<'_>
+{
+ fn from(path: PathBuf) -> Self
+ {
+ Self { path: path.into(), name: None }
+ }
+}
+
+impl<'a> From<&'a LabelOwned> for Label<'a>
+{
+ fn from(label: &'a LabelOwned) -> Self
+ {
+ Self {
+ path: (&label.path).into(),
+ name: label.name.as_ref().map(|name| Cow::Borrowed(name.as_str())),
+ }
+ }
+}
+
+impl Display for Label<'_>
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ write!(formatter, "{}", self.path.display())?;
+
+ if let Some(name) = &self.name {
+ formatter.write_str("::")?;
+ formatter.write_str(&name)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct LabelOwned
+{
+ pub path: PathBuf,
+ pub name: Option<String>,
+}
+
+impl LabelOwned
+{
+ pub fn to_label(&self) -> Label<'_>
+ {
+ Label {
+ path: (&self.path).into(),
+ name: self.name.as_ref().map(|name| Cow::Borrowed(name.as_str())),
+ }
+ }
+}
+
+impl Display for LabelOwned
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ write!(formatter, "{}", self.path.display())?;
+
+ if let Some(name) = &self.name {
+ formatter.write_str("::")?;
+ formatter.write_str(&name)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Debug, Sole)]
+pub struct Assets
+{
+ assets: Vec<StoredAsset>,
+ asset_lookup: RefCell<HashMap<LabelHash, LookupEntry>>,
+ importers: Vec<WrappedImporterFn>,
+ importer_lookup: HashMap<OsString, usize>,
+ import_work_queue: WorkQueue<ImportWorkUserData>,
+ import_work_msg_receiver: MpscReceiver<ImportWorkMessage>,
+ import_work_msg_sender: MpscSender<ImportWorkMessage>,
+ events: Events,
+}
+
+impl Assets
+{
+ pub fn with_capacity(capacity: usize) -> Self
+ {
+ let (import_work_msg_sender, import_work_msg_receiver) =
+ mpsc_channel::<ImportWorkMessage>();
+
+ Self {
+ assets: Vec::with_capacity(capacity),
+ asset_lookup: RefCell::new(HashMap::with_capacity(capacity)),
+ importers: Vec::new(),
+ importer_lookup: HashMap::new(),
+ import_work_queue: WorkQueue::new("asset_importing_work_queue"),
+ import_work_msg_receiver,
+ import_work_msg_sender,
+ events: Events::default(),
+ }
+ }
+
+ pub fn set_importer<'file_ext, AssetSettings, Err>(
+ &mut self,
+ file_extensions: impl IntoIterator<Item: Into<Cow<'file_ext, str>>>,
+ func: impl Fn(&mut Submitter<'_>, &Path, Option<&AssetSettings>) -> Result<(), Err>,
+ ) where
+ AssetSettings: 'static,
+ Err: std::error::Error + Send + Sync + 'static,
+ {
+ self.importers.push(WrappedImporterFn::new(func));
+
+ let importer_index = self.importers.len() - 1;
+
+ self.importer_lookup
+ .extend(file_extensions.into_iter().map(|file_ext| {
+ let file_ext: Cow<str> = file_ext.into();
+
+ (file_ext.into_owned().into(), importer_index)
+ }));
+ }
+
+ #[tracing::instrument(skip_all, fields(asset_type=type_name::<Asset>()))]
+ pub fn get<Asset: 'static + Send + Sync>(
+ &self,
+ handle: &Handle<Asset>,
+ ) -> Option<&Asset>
+ {
+ let asset_lookup = self.asset_lookup.borrow();
+
+ let LookupEntry::Occupied(asset_index, _) =
+ asset_lookup.get(&handle.id.label_hash)?
+ else {
+ return None;
+ };
+
+ let stored_asset = self.assets.get(*asset_index).expect("Not possible");
+
+ let Some(asset) = stored_asset.strong.downcast_ref::<Asset>() else {
+ tracing::error!("Wrong asset type");
+ return None;
+ };
+
+ Some(asset)
+ }
+
+ #[tracing::instrument(skip_all, fields(asset_type=type_name::<Asset>()))]
+ pub fn get_handle_to_loaded<'label, Asset: 'static + Send + Sync>(
+ &self,
+ label: impl Into<Label<'label>>,
+ ) -> Option<Handle<Asset>>
+ {
+ let label = label.into();
+
+ let label_hash = LabelHash::new(&label);
+
+ let asset_lookup = self.asset_lookup.borrow();
+
+ let LookupEntry::Occupied(asset_index, _) = asset_lookup.get(&label_hash)? else {
+ return None;
+ };
+
+ let stored_asset = self.assets.get(*asset_index).expect("Not possible");
+
+ if stored_asset.strong.downcast_ref::<Asset>().is_none() {
+ tracing::error!("Wrong asset type");
+ return None;
+ };
+
+ Some(Handle::new(label_hash))
+ }
+
+ pub fn is_loaded_and_has_type<Asset: 'static + Send + Sync>(
+ &self,
+ handle: &Handle<Asset>,
+ ) -> bool
+ {
+ let asset_lookup = self.asset_lookup.borrow();
+
+ let Some(LookupEntry::Occupied(asset_index, _)) =
+ asset_lookup.get(&handle.id.label_hash)
+ else {
+ return false;
+ };
+
+ let stored_asset = self.assets.get(*asset_index).expect("Not possible");
+
+ stored_asset.strong.downcast_ref::<Asset>().is_some()
+ }
+
+ pub fn get_label<Asset: 'static + Send + Sync>(
+ &self,
+ handle: &Handle<Asset>,
+ ) -> Option<LabelOwned>
+ {
+ let lookup_entry = self
+ .asset_lookup
+ .borrow()
+ .get(&handle.id.label_hash)?
+ .clone();
+
+ let LookupEntry::Occupied(_, label) = lookup_entry else {
+ return None;
+ };
+
+ Some(label)
+ }
+
+ pub fn get_label_by_id(&self, id: Id) -> Option<LabelOwned>
+ {
+ let lookup_entry = self.asset_lookup.borrow().get(&id.label_hash)?.clone();
+
+ let LookupEntry::Occupied(_, label) = lookup_entry else {
+ return None;
+ };
+
+ Some(label)
+ }
+
+ #[tracing::instrument(skip(self))]
+ pub fn load<'i, Asset: 'static + Send + Sync>(
+ &self,
+ label: impl Into<Label<'i>> + Debug,
+ ) -> Handle<Asset>
+ {
+ let label = label.into();
+
+ let label_hash = LabelHash::new(&label);
+
+ let mut asset_lookup = self.asset_lookup.borrow_mut();
+
+ if Self::is_pending(&asset_lookup, &label) {
+ return Handle::new(label_hash);
+ }
+
+ let Some(lookup_entry) = asset_lookup.get(&label_hash) else {
+ self.add_import_work::<Infallible>(
+ &label,
+ label_hash,
+ None,
+ &mut asset_lookup,
+ );
+
+ return Handle::new(label_hash);
+ };
+
+ match lookup_entry {
+ LookupEntry::Occupied(asset_index, _) => {
+ let stored_asset = self.assets.get(*asset_index).expect("Not possible");
+
+ if stored_asset.strong.downcast_ref::<Asset>().is_none() {
+ tracing::error!("Wrong asset type {}", type_name::<Asset>());
+ }
+ }
+ LookupEntry::Pending => {}
+ }
+
+ Handle::new(label_hash)
+ }
+
+ #[tracing::instrument(skip(self))]
+ pub fn load_with_settings<'i, Asset, AssetSettings>(
+ &self,
+ label: impl Into<Label<'i>> + Debug,
+ asset_settings: AssetSettings,
+ ) -> Handle<Asset>
+ where
+ Asset: Send + Sync + 'static,
+ AssetSettings: Send + Sync + Debug + 'static,
+ {
+ let label = label.into();
+
+ let label_hash = LabelHash::new(&label);
+
+ let mut asset_lookup = self.asset_lookup.borrow_mut();
+
+ if Self::is_pending(&asset_lookup, &label) {
+ return Handle::new(label_hash);
+ }
+
+ let Some(lookup_entry) = asset_lookup.get(&label_hash) else {
+ self.add_import_work::<AssetSettings>(
+ &label,
+ label_hash,
+ Some(asset_settings),
+ &mut asset_lookup,
+ );
+
+ return Handle::new(label_hash);
+ };
+
+ match lookup_entry {
+ LookupEntry::Occupied(asset_index, _) => {
+ let stored_asset = self.assets.get(*asset_index).expect("Not possible");
+
+ if stored_asset.strong.downcast_ref::<Asset>().is_none() {
+ tracing::error!(
+ "Wrong asset type {} for asset",
+ type_name::<Asset>()
+ );
+ }
+ }
+ LookupEntry::Pending => {}
+ }
+
+ Handle::new(label_hash)
+ }
+
+ pub fn store_with_name<'name, Asset: 'static + Send + Sync>(
+ &mut self,
+ name: impl Into<Cow<'name, str>>,
+ asset: Asset,
+ ) -> Handle<Asset>
+ {
+ self.store_with_label(
+ Label {
+ path: Path::new("").into(),
+ name: Some(name.into()),
+ },
+ asset,
+ )
+ }
+
+ pub fn store_with_name_with<'name, Asset: 'static + Send + Sync>(
+ &mut self,
+ name: impl Into<Cow<'name, str>>,
+ func: impl FnOnce(&mut Self) -> Asset,
+ ) -> Handle<Asset>
+ {
+ let asset = func(self);
+
+ self.store_with_label(
+ Label {
+ path: Path::new("").into(),
+ name: Some(name.into()),
+ },
+ asset,
+ )
+ }
+
+ #[tracing::instrument(skip(self, asset), fields(asset_type=type_name::<Asset>()))]
+ pub fn store_with_label<'i, Asset: 'static + Send + Sync>(
+ &mut self,
+ label: impl Into<Label<'i>> + Debug,
+ asset: Asset,
+ ) -> Handle<Asset>
+ {
+ let label = label.into();
+
+ let label_hash = LabelHash::new(&label);
+
+ if matches!(
+ self.asset_lookup.get_mut().get(&label_hash),
+ Some(LookupEntry::Occupied(_, _))
+ ) {
+ tracing::error!("Asset already exists");
+
+ return Handle::new(label_hash);
+ }
+
+ tracing::debug!("Storing asset");
+
+ self.assets.push(StoredAsset::new(asset));
+
+ let index = self.assets.len() - 1;
+
+ self.asset_lookup
+ .get_mut()
+ .insert(label_hash, LookupEntry::Occupied(index, label.to_owned()));
+
+ if label.name.is_some() {
+ let parent_asset_label = Label {
+ path: label.path.as_ref().into(),
+ name: None,
+ };
+
+ let parent_asset_label_hash = LabelHash::new(&parent_asset_label);
+
+ if matches!(
+ self.asset_lookup.get_mut().get(&parent_asset_label_hash),
+ Some(LookupEntry::Pending)
+ ) {
+ self.asset_lookup.get_mut().remove(&parent_asset_label_hash);
+ } else if self
+ .asset_lookup
+ .get_mut()
+ .get(&parent_asset_label_hash)
+ .is_none()
+ {
+ self.assets
+ .push(StoredAsset::new::<Option<Infallible>>(None));
+
+ self.asset_lookup.get_mut().insert(
+ parent_asset_label_hash,
+ LookupEntry::Occupied(
+ self.assets.len() - 1,
+ parent_asset_label.to_owned(),
+ ),
+ );
+ }
+ }
+
+ self.events
+ .curr_tick_events
+ .push(Event::Stored(Id { label_hash }, label.to_owned()));
+
+ Handle::new(label_hash)
+ }
+
+ pub fn events(&self) -> &Events
+ {
+ &self.events
+ }
+
+ fn is_pending(asset_lookup: &HashMap<LabelHash, LookupEntry>, label: &Label) -> bool
+ {
+ if label.name.is_some() {
+ if let Some(LookupEntry::Pending) =
+ asset_lookup.get(&LabelHash::new(&Label {
+ path: label.path.as_ref().into(),
+ name: None,
+ }))
+ {
+ return true;
+ }
+ }
+
+ if let Some(LookupEntry::Pending) = asset_lookup.get(&LabelHash::new(label)) {
+ return true;
+ };
+
+ false
+ }
+
+ fn add_import_work<AssetSettings>(
+ &self,
+ label: &Label<'_>,
+ label_hash: LabelHash,
+ asset_settings: Option<AssetSettings>,
+ asset_lookup: &mut HashMap<LabelHash, LookupEntry>,
+ ) where
+ AssetSettings: Any + Send + Sync,
+ {
+ let Some(file_ext) = label.path.extension() else {
+ tracing::error!("Asset file is missing a file extension");
+ return;
+ };
+
+ let Some(importer) = self.get_importer(file_ext) else {
+ tracing::error!(
+ "No importer exists for asset file extension {}",
+ file_ext.to_string_lossy()
+ );
+ return;
+ };
+
+ self.import_work_queue.add_work(Work {
+ func: |ImportWorkUserData {
+ import_work_msg_sender,
+ asset_path,
+ asset_settings,
+ importer,
+ }| {
+ if let Err(err) = importer.call(
+ import_work_msg_sender,
+ asset_path.as_path(),
+ asset_settings.as_deref(),
+ ) {
+ tracing::error!(
+ "Failed to import asset {}: {:#}",
+ asset_path.display(),
+ crate::Error::new(err)
+ );
+ }
+ },
+ user_data: ImportWorkUserData {
+ import_work_msg_sender: self.import_work_msg_sender.clone(),
+ asset_path: label.path.to_path_buf(),
+ asset_settings: asset_settings.map(|asset_settings| {
+ Box::new(asset_settings) as Box<dyn Any + Send + Sync>
+ }),
+ importer: importer.clone(),
+ },
+ });
+
+ asset_lookup.insert(label_hash, LookupEntry::Pending);
+
+ if label.name.is_some() {
+ asset_lookup.insert(
+ LabelHash::new(&Label {
+ path: label.path.as_ref().into(),
+ name: None,
+ }),
+ LookupEntry::Pending,
+ );
+ }
+ }
+
+ fn get_importer(&self, file_ext: &OsStr) -> Option<&WrappedImporterFn>
+ {
+ let index = *self.importer_lookup.get(file_ext)?;
+
+ Some(self.importers.get(index).expect("Not possible"))
+ }
+}
+
+impl Default for Assets
+{
+ fn default() -> Self
+ {
+ Self::with_capacity(0)
+ }
+}
+
+pub struct Submitter<'path>
+{
+ import_work_msg_sender: MpscSender<ImportWorkMessage>,
+ asset_path: &'path Path,
+}
+
+impl Submitter<'_>
+{
+ pub fn submit_load_other<'label, Asset: Send + Sync + 'static>(
+ &self,
+ label: impl Into<Label<'label>>,
+ ) -> Handle<Asset>
+ {
+ let label = label.into();
+
+ let _ = self.import_work_msg_sender.send(ImportWorkMessage::Load {
+ do_load: |assets, label, _asset_settings| {
+ let _ = assets.load::<Asset>(label);
+ },
+ label: label.to_owned(),
+ asset_settings: None,
+ });
+
+ Handle::new(LabelHash::new(&label))
+ }
+
+ pub fn submit_load_other_with_settings<'label, Asset, AssetSettings>(
+ &self,
+ label: impl Into<Label<'label>>,
+ asset_settings: AssetSettings,
+ ) -> Handle<Asset>
+ where
+ Asset: Send + Sync + 'static,
+ AssetSettings: Send + Sync + Debug + 'static,
+ {
+ let label = label.into();
+
+ let _ = self.import_work_msg_sender.send(ImportWorkMessage::Load {
+ do_load: |assets, label, asset_settings| {
+ let asset_settings = *asset_settings
+ .expect("Not possible")
+ .downcast::<AssetSettings>()
+ .expect("Not possible");
+
+ let _ = assets
+ .load_with_settings::<Asset, AssetSettings>(label, asset_settings);
+ },
+ label: label.to_owned(),
+ asset_settings: Some(Box::new(asset_settings)),
+ });
+
+ Handle::new(LabelHash::new(&label))
+ }
+
+ pub fn submit_store<Asset: Send + Sync + 'static>(
+ &self,
+ asset: Asset,
+ ) -> Handle<Asset>
+ {
+ let label = LabelOwned {
+ path: self.asset_path.into(),
+ name: None,
+ };
+
+ let label_hash = LabelHash::new(&label.to_label());
+
+ let _ = self.import_work_msg_sender.send(ImportWorkMessage::Store {
+ do_store: |assets, label, boxed_asset| {
+ let Ok(asset) = boxed_asset.downcast::<Asset>() else {
+ unreachable!();
+ };
+
+ assets.store_with_label::<Asset>(&label, *asset);
+ },
+ label,
+ asset: Box::new(asset),
+ });
+
+ Handle::new(label_hash)
+ }
+
+ pub fn submit_store_named<Asset: Send + Sync + 'static>(
+ &self,
+ name: impl AsRef<str>,
+ asset: Asset,
+ ) -> Handle<Asset>
+ {
+ let label = LabelOwned {
+ path: self.asset_path.into(),
+ name: Some(name.as_ref().into()),
+ };
+
+ let label_hash = LabelHash::new(&label.to_label());
+
+ let _ = self.import_work_msg_sender.send(ImportWorkMessage::Store {
+ do_store: |assets, label, boxed_asset| {
+ let Ok(asset) = boxed_asset.downcast::<Asset>() else {
+ unreachable!();
+ };
+
+ assets.store_with_label::<Asset>(&label, *asset);
+ },
+ label,
+ asset: Box::new(asset),
+ });
+
+ Handle::new(label_hash)
+ }
+}
+
+/// Asset handle.
+#[derive(Debug)]
+pub struct Handle<Asset: 'static>
+{
+ id: Id,
+ _pd: PhantomData<Asset>,
+}
+
+impl<Asset: 'static> Handle<Asset>
+{
+ pub fn from_id(id: Id) -> Self
+ {
+ Self { id, _pd: PhantomData }
+ }
+
+ pub fn id(&self) -> Id
+ {
+ self.id
+ }
+
+ fn new(label_hash: LabelHash) -> Self
+ {
+ Self {
+ id: Id { label_hash },
+ _pd: PhantomData,
+ }
+ }
+}
+
+impl<Asset: 'static> Clone for Handle<Asset>
+{
+ fn clone(&self) -> Self
+ {
+ Self { id: self.id, _pd: PhantomData }
+ }
+}
+
+/// Asset ID.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Id
+{
+ label_hash: LabelHash,
+}
+
+#[derive(Debug, Default)]
+pub struct Events
+{
+ curr_tick_events: Vec<Event>,
+ last_tick_events: Vec<Event>,
+}
+
+impl Events
+{
+ pub fn last_tick_events(&self) -> impl Iterator<Item = &Event>
+ {
+ self.last_tick_events.iter()
+ }
+}
+
+/// Asset event.
+#[derive(Debug)]
+pub enum Event
+{
+ /// Asset stored.
+ Stored(Id, LabelOwned),
+}
+
+#[derive(Debug, thiserror::Error)]
+enum ImporterError
+{
+ #[error("Settings has a incorrect type")]
+ IncorrectAssetSettingsType(PathBuf),
+
+ #[error(transparent)]
+ Other(Box<dyn std::error::Error + Send + Sync>),
+}
+
+#[derive(Debug, Clone)]
+struct WrappedImporterFn
+{
+ wrapper_func: fn(
+ MpscSender<ImportWorkMessage>,
+ &Path,
+ Option<&(dyn Any + Send + Sync)>,
+ ) -> Result<(), ImporterError>,
+}
+
+impl WrappedImporterFn
+{
+ fn new<InnerFunc, AssetSettings, Err>(inner_func_param: InnerFunc) -> Self
+ where
+ InnerFunc:
+ Fn(&mut Submitter<'_>, &Path, Option<&AssetSettings>) -> Result<(), Err>,
+ AssetSettings: 'static,
+ Err: std::error::Error + Send + Sync + 'static,
+ {
+ assert_eq!(size_of::<InnerFunc>(), 0);
+
+ let wrapper_func =
+ |import_work_msg_sender: MpscSender<ImportWorkMessage>,
+ asset_path: &Path,
+ asset_settings: Option<&(dyn Any + Send + Sync)>| {
+ let inner_func = unsafe { std::mem::zeroed::<InnerFunc>() };
+
+ let asset_settings = asset_settings
+ .map(|asset_settings| {
+ asset_settings
+ .downcast_ref::<AssetSettings>()
+ .ok_or_else(|| {
+ ImporterError::IncorrectAssetSettingsType(
+ asset_path.to_path_buf(),
+ )
+ })
+ })
+ .transpose()?;
+
+ inner_func(
+ &mut Submitter { import_work_msg_sender, asset_path },
+ asset_path,
+ asset_settings,
+ )
+ .map_err(|err| ImporterError::Other(Box::new(err)))?;
+
+ Ok(())
+ };
+
+ std::mem::forget(inner_func_param);
+
+ Self { wrapper_func }
+ }
+
+ fn call(
+ &self,
+ import_work_msg_sender: MpscSender<ImportWorkMessage>,
+ asset_path: &Path,
+ asset_settings: Option<&(dyn Any + Send + Sync)>,
+ ) -> Result<(), ImporterError>
+ {
+ (self.wrapper_func)(import_work_msg_sender, asset_path, asset_settings)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+struct LabelHash(u64);
+
+impl LabelHash
+{
+ fn new(label: &Label<'_>) -> Self
+ {
+ let mut hasher = DefaultHasher::new();
+
+ label.hash(&mut hasher);
+
+ Self(hasher.finish())
+ }
+}
+
+#[derive(Debug, Default)]
+pub(crate) struct Extension
+{
+ pub assets: Assets,
+}
+
+impl crate::ecs::extension::Extension for Extension
+{
+ fn collect(self, mut collector: crate::ecs::extension::Collector<'_>)
+ {
+ let _ = collector.add_sole(self.assets);
+
+ collector.spawn_declared_entity(&HANDLE_ASSETS_PHASE);
+
+ collector.add_system(*HANDLE_ASSETS_PHASE, add_received_assets);
+ collector.add_system(*HANDLE_ASSETS_PHASE, check_import_wq_thread_not_panicked);
+ }
+}
+
+fn add_received_assets(mut assets: Single<Assets>)
+{
+ let Ok(assets) = assets.get_mut() else {
+ unreachable!();
+ };
+
+ let Events { curr_tick_events, last_tick_events } = &mut assets.events;
+
+ std::mem::swap(last_tick_events, curr_tick_events);
+
+ curr_tick_events.clear();
+
+ while let Some(import_work_msg) = assets.import_work_msg_receiver.try_recv().ok() {
+ match import_work_msg {
+ ImportWorkMessage::Store { do_store, label, asset } => {
+ do_store(assets, label, asset);
+ }
+ ImportWorkMessage::Load { do_load, label, asset_settings } => {
+ do_load(
+ &assets,
+ Label {
+ path: label.path.as_path().into(),
+ name: label.name.as_deref().map(|name| name.into()),
+ },
+ asset_settings,
+ );
+ }
+ }
+ }
+}
+
+fn check_import_wq_thread_not_panicked(assets: Single<Assets>, mut actions: Actions<'_>)
+{
+ let Ok(assets) = assets.get() else {
+ unreachable!();
+ };
+
+ if assets.import_work_queue.get_thread_panic().is_some() {
+ cold_path();
+
+ actions.stop();
+ }
+}
+
+#[derive(Debug)]
+struct ImportWorkUserData
+{
+ import_work_msg_sender: MpscSender<ImportWorkMessage>,
+ asset_path: PathBuf,
+ asset_settings: Option<Box<dyn Any + Send + Sync>>,
+ importer: WrappedImporterFn,
+}
+
+#[derive(Debug)]
+enum ImportWorkMessage
+{
+ Store
+ {
+ do_store: fn(&mut Assets, LabelOwned, Box<dyn Any + Send + Sync>),
+ label: LabelOwned,
+ asset: Box<dyn Any + Send + Sync>,
+ },
+
+ Load
+ {
+ do_load: fn(&Assets, Label<'_>, Option<Box<dyn Any + Send + Sync>>),
+ label: LabelOwned,
+ asset_settings: Option<Box<dyn Any + Send + Sync>>,
+ },
+}
+
+#[derive(Debug, Clone)]
+enum LookupEntry
+{
+ Occupied(usize, LabelOwned),
+ Pending,
+}
+
+#[derive(Debug)]
+struct StoredAsset
+{
+ strong: Arc<dyn Any + Send + Sync>,
+}
+
+impl StoredAsset
+{
+ fn new<Asset: Any + Send + Sync>(asset: Asset) -> Self
+ {
+ let strong = Arc::new(asset);
+
+ Self { strong }
+ }
+}
diff --git a/engine/src/camera.rs b/engine/src/camera.rs
index 66150af..51cd54c 100644
--- a/engine/src/camera.rs
+++ b/engine/src/camera.rs
@@ -1,11 +1,12 @@
-use ecs::Component;
-
+use crate::ecs::Component;
+use crate::matrix::Matrix;
use crate::projection::{Perspective, Projection};
+use crate::reflection::Reflection;
use crate::vector::Vec3;
pub mod fly;
-#[derive(Debug, Component)]
+#[derive(Debug, Clone, Component, Reflection)]
pub struct Camera
{
pub target: Vec3<f32>,
@@ -13,12 +14,24 @@ pub struct Camera
pub projection: Projection,
}
+impl Camera
+{
+ pub fn to_view_matrix(&self, camera_world_pos: Vec3<f32>) -> Matrix<f32, 4, 4>
+ {
+ let mut view = Matrix::new();
+
+ view.look_at(camera_world_pos, self.target, self.global_up);
+
+ view
+ }
+}
+
impl Default for Camera
{
fn default() -> Self
{
Self {
- target: Vec3::default(),
+ target: Vec3 { x: 0.0, y: 0.0, z: 0.0 },
global_up: Vec3::UP,
projection: Projection::Perspective(Perspective::default()),
}
@@ -26,5 +39,20 @@ impl Default for Camera
}
/// Marker component for cameras that are active.
-#[derive(Debug, Default, Clone, Copy, Component)]
+#[derive(Debug, Default, Clone, Copy, Component, Reflection)]
pub struct Active;
+
+/// Cameras that can be controlled have this component.
+#[derive(Debug, Clone, Copy, Component, Reflection)]
+pub struct Controllable
+{
+ pub control_enabled: bool,
+}
+
+impl Default for Controllable
+{
+ fn default() -> Self
+ {
+ Self { control_enabled: true }
+ }
+}
diff --git a/engine/src/camera/fly.rs b/engine/src/camera/fly.rs
index 087f727..5f5ab4b 100644
--- a/engine/src/camera/fly.rs
+++ b/engine/src/camera/fly.rs
@@ -1,24 +1,28 @@
-use ecs::component::local::Local;
-use ecs::phase::UPDATE as UPDATE_PHASE;
-use ecs::sole::Single;
-use ecs::system::{Into, System};
-use ecs::{Component, Query};
-
-use crate::camera::{Active as ActiveCamera, Camera};
-use crate::delta_time::DeltaTime;
-use crate::input::{Cursor, CursorFlags, Key, KeyState, Keys};
-use crate::transform::Position;
-use crate::util::builder;
-use crate::vector::{Vec2, Vec3};
+use ecs::time::Time;
+
+use crate::builder;
+use crate::camera::{Active as ActiveCamera, Camera, Controllable as ControllableCamera};
+use crate::ecs::actions::Actions;
+use crate::ecs::component::local::Local;
+use crate::ecs::phase::UPDATE as UPDATE_PHASE;
+use crate::ecs::query::term::With;
+use crate::ecs::sole::Single;
+use crate::ecs::system::initializable::Initializable;
+use crate::ecs::system::Into;
+use crate::ecs::{Component, Query};
+use crate::input::keyboard::{Key, Keyboard};
+use crate::input::mouse::Mouse;
+use crate::reflection::Reflection;
+use crate::transform::Transform;
+use crate::vector::{Angles, Vec2, Vec3};
builder! {
/// A fly camera.
#[builder(name = Builder, derives = (Debug))]
-#[derive(Debug, Component)]
+#[derive(Debug, Component, Reflection)]
#[non_exhaustive]
pub struct Fly {
- pub current_pitch: f64,
- pub current_yaw: f64,
+ pub angles: Angles<f32>,
pub speed: f32,
}
}
@@ -45,8 +49,7 @@ impl Default for Builder
fn default() -> Self
{
Self {
- current_yaw: -90.0,
- current_pitch: 0.0,
+ angles: Vec3::BACK.into_deg_angles(),
speed: 3.0,
}
}
@@ -55,16 +58,11 @@ impl Default for Builder
/// Fly camera extension.
pub struct Extension(pub Options);
-impl ecs::extension::Extension for Extension
+impl crate::ecs::extension::Extension for Extension
{
- fn collect(self, mut collector: ecs::extension::Collector<'_>)
+ fn collect(self, mut collector: crate::ecs::extension::Collector<'_>)
{
- collector.add_system(
- *UPDATE_PHASE,
- update
- .into_system()
- .initialize((CursorState::default(), self.0)),
- );
+ collector.add_system(*UPDATE_PHASE, update.into_system().initialize((self.0,)));
}
}
@@ -75,81 +73,81 @@ pub struct Options
}
fn update(
- camera_query: Query<(&mut Camera, &mut Position, &mut Fly, &ActiveCamera)>,
- keys: Single<Keys>,
- cursor: Single<Cursor>,
- cursor_flags: Single<CursorFlags>,
- delta_time: Single<DeltaTime>,
- mut cursor_state: Local<CursorState>,
+ camera_query: Query<(
+ &mut Camera,
+ &mut Transform,
+ &mut Fly,
+ Option<&ControllableCamera>,
+ With<ActiveCamera>,
+ )>,
+ keyboard: Single<Keyboard>,
+ mouse: Single<Mouse>,
+ time: Single<Time>,
options: Local<Options>,
-)
+ mut actions: Actions,
+) -> Result<(), crate::Error>
{
- for (mut camera, mut camera_pos, mut fly_camera, _) in &camera_query {
- if cursor.has_moved && cursor_flags.is_first_move.flag {
- tracing::debug!("First cursor move");
+ let keyboard = keyboard.get()?;
+ let mouse = mouse.get()?;
- cursor_state.last_pos = cursor.position;
+ for (
+ camera_ent_id,
+ (mut camera, mut camera_transform, mut fly_camera, controllable_camera),
+ ) in camera_query.iter_with_euids()
+ {
+ if let Some(controllable_camera) = controllable_camera {
+ if !controllable_camera.control_enabled {
+ continue;
+ }
+ } else {
+ actions.add_components(camera_ent_id, (ControllableCamera::default(),));
}
- let delta_time = delta_time.duration;
-
- let mut x_offset = cursor.position.x - cursor_state.last_pos.x;
- let mut y_offset = cursor_state.last_pos.y - cursor.position.y;
-
- cursor_state.last_pos = cursor.position;
+ if mouse.curr_tick_position_delta != (Vec2 { x: 0.0, y: 0.0 }) {
+ let x_offset =
+ mouse.curr_tick_position_delta.x * f64::from(options.mouse_sensitivity);
- x_offset *= f64::from(options.mouse_sensitivity);
- y_offset *= f64::from(options.mouse_sensitivity);
+ let y_offset = (-mouse.curr_tick_position_delta.y)
+ * f64::from(options.mouse_sensitivity);
- fly_camera.current_yaw += x_offset;
- fly_camera.current_pitch += y_offset;
+ fly_camera.angles.yaw += x_offset as f32;
+ fly_camera.angles.pitch += y_offset as f32;
- fly_camera.current_pitch = fly_camera.current_pitch.clamp(-89.0, 89.0);
-
- // TODO: This casting to a f32 from a f64 is horrible. fix it
- #[allow(clippy::cast_possible_truncation)]
- let direction = Vec3 {
- x: (fly_camera.current_yaw.to_radians().cos()
- * fly_camera.current_pitch.to_radians().cos()) as f32,
- y: fly_camera.current_pitch.to_radians().sin() as f32,
- z: (fly_camera.current_yaw.to_radians().sin()
- * fly_camera.current_pitch.to_radians().cos()) as f32,
+ fly_camera.angles.pitch = fly_camera.angles.pitch.clamp(-89.0, 89.0);
}
- .normalize();
+
+ let direction = Vec3::direction_from_deg_angles(fly_camera.angles);
let cam_right = direction.cross(&Vec3::UP).normalize();
camera.global_up = cam_right.cross(&direction).normalize();
- if keys.get_key_state(Key::W) == KeyState::Pressed {
- camera_pos.position +=
- direction * fly_camera.speed * delta_time.as_secs_f32();
+ if keyboard.pressed(Key::W) {
+ camera_transform.position +=
+ direction * fly_camera.speed * time.delta_time.as_secs_f32();
}
- if keys.get_key_state(Key::S) == KeyState::Pressed {
- camera_pos.position -=
- direction * fly_camera.speed * delta_time.as_secs_f32();
+ if keyboard.pressed(Key::S) {
+ camera_transform.position -=
+ direction * fly_camera.speed * time.delta_time.as_secs_f32();
}
- if keys.get_key_state(Key::A) == KeyState::Pressed {
+ if keyboard.pressed(Key::A) {
let cam_left = -direction.cross(&Vec3::UP).normalize();
- camera_pos.position += cam_left * fly_camera.speed * delta_time.as_secs_f32();
+ camera_transform.position +=
+ cam_left * fly_camera.speed * time.delta_time.as_secs_f32();
}
- if keys.get_key_state(Key::D) == KeyState::Pressed {
+ if keyboard.pressed(Key::D) {
let cam_right = direction.cross(&Vec3::UP).normalize();
- camera_pos.position +=
- cam_right * fly_camera.speed * delta_time.as_secs_f32();
+ camera_transform.position +=
+ cam_right * fly_camera.speed * time.delta_time.as_secs_f32();
}
- camera.target = camera_pos.position + direction;
+ camera.target = camera_transform.position + direction;
}
-}
-#[derive(Debug, Default, Component)]
-struct CursorState
-{
- last_pos: Vec2<f64>,
+ Ok(())
}
diff --git a/engine/src/collision.rs b/engine/src/collision.rs
index aefd9b6..015ab58 100644
--- a/engine/src/collision.rs
+++ b/engine/src/collision.rs
@@ -1,6 +1,6 @@
-use ecs::Component;
-
+use crate::ecs::Component;
use crate::mesh::Mesh;
+use crate::reflection::Reflection;
use crate::vector::Vec3;
pub trait Collider<Other>
@@ -8,7 +8,7 @@ pub trait Collider<Other>
fn intersects(&self, other: &Other) -> bool;
}
-#[derive(Debug, Default, Clone, Component)]
+#[derive(Debug, Default, Clone, Component, Reflection)]
#[non_exhaustive]
pub struct BoxCollider
{
@@ -79,7 +79,7 @@ impl Collider<Vec3<f32>> for BoxCollider
}
}
-#[derive(Debug, Default, Clone, Component)]
+#[derive(Debug, Default, Clone, Component, Reflection)]
pub struct SphereCollider
{
pub center: Vec3<f32>,
@@ -101,7 +101,7 @@ impl Collider<SphereCollider> for SphereCollider
{
fn intersects(&self, other: &SphereCollider) -> bool
{
- (&self.center - &other.center).length() <= self.radius + other.radius
+ (self.center - other.center).length() <= self.radius + other.radius
}
}
@@ -137,6 +137,6 @@ impl Collider<Vec3<f32>> for SphereCollider
{
fn intersects(&self, other: &Vec3<f32>) -> bool
{
- (&self.center - other).length() <= self.radius
+ (self.center - *other).length() <= self.radius
}
}
diff --git a/engine/src/data_types/color.rs b/engine/src/data_types/color.rs
index cef3b92..d2fd217 100644
--- a/engine/src/data_types/color.rs
+++ b/engine/src/data_types/color.rs
@@ -1,59 +1,145 @@
-use std::ops::{Add, Div, Mul, Neg, Sub};
+use std::ops::{Add, Div, Mul, Sub};
-#[derive(Debug, Clone, Default)]
-#[repr(C)]
-pub struct Color<Value>
+use crate::reflection::Reflection;
+
+pub trait Pixel: sealed::Sealed
{
- pub red: Value,
- pub green: Value,
- pub blue: Value,
+ type Component: PixelComponent;
}
-impl Color<f32>
+pub trait PixelComponent: Clone + Copy + sealed::Sealed
{
- pub const WHITE_F32: Self = Self { red: 1.0, green: 1.0, blue: 1.0 };
+ const COLOR_MAX: Self;
+}
+
+macro_rules! gen_color {
+ ($ident: ident, components=($($component: ident),+)) => {
+ #[derive(Debug, Clone, Default, Reflection)]
+ #[reflection(impl_with_generics(<u8>, <u16>, <u32>, <f32>))]
+ pub struct $ident<Component>
+ {
+ $(pub $component: Component),+
+ }
+
+ impl<Component: PixelComponent> $ident<Component>
+ {
+ pub fn white() -> Self
+ {
+ Self {
+ $($component: Component::COLOR_MAX),*
+ }
+ }
+ }
+
+ impl<Component: PixelComponent> Pixel for $ident<Component>
+ {
+ type Component = Component;
+ }
+
+ impl<Component> sealed::Sealed for $ident<Component> {}
+ };
}
-impl Color<u8>
+gen_color!(Rgb, components = (r, g, b));
+gen_color!(Rgba, components = (r, g, b, a));
+
+#[derive(Debug, Clone, Reflection)]
+#[reflection(impl_with_generics(<u8>, <u16>, <u32>, <f32>))]
+pub enum Color<Component>
{
- pub const WHITE_U8: Self = Self { red: 255, green: 255, blue: 255 };
+ Rgb(Rgb<Component>),
+ Rgba(Rgba<Component>),
}
-impl<Value: Clone> From<Value> for Color<Value>
+impl<Component: PixelComponent> Color<Component>
{
- fn from(value: Value) -> Self
+ pub fn to_rgb_lossy(&self) -> Rgb<Component>
+ {
+ match self {
+ Self::Rgb(rgb) => rgb.clone(),
+ Self::Rgba(rgba) => Rgb { r: rgba.r, g: rgba.g, b: rgba.b },
+ }
+ }
+
+ pub fn to_rgba_lossy(&self) -> Rgba<Component>
{
- Self {
- red: value.clone(),
- green: value.clone(),
- blue: value,
+ match self {
+ Self::Rgb(rgb) => Rgba {
+ r: rgb.r,
+ g: rgb.g,
+ b: rgb.b,
+ a: Component::COLOR_MAX,
+ },
+ Self::Rgba(rgba) => rgba.clone(),
}
}
}
-macro_rules! impl_math_op {
- ($math_op_trait: ident, $function: ident) => {
- impl<Value> $math_op_trait for Color<Value>
+impl<Component> From<Rgb<Component>> for Color<Component>
+{
+ fn from(rgb: Rgb<Component>) -> Self
+ {
+ Self::Rgb(rgb)
+ }
+}
+
+impl<Component> From<Rgba<Component>> for Color<Component>
+{
+ fn from(rgba: Rgba<Component>) -> Self
+ {
+ Self::Rgba(rgba)
+ }
+}
+
+impl<Component: Default> Default for Color<Component>
+{
+ fn default() -> Self
+ {
+ Self::Rgb(Rgb::default())
+ }
+}
+
+macro_rules! gen_scalar_math_op_impl {
+ (
+ $ident: ident,
+ $math_op_trait: ident,
+ $function: ident,
+ components=($($component: ident),+)
+ ) => {
+ impl<Value> $math_op_trait<Value> for $ident<Value>
where
- Value: $math_op_trait<Output = Value>,
+ Value: $math_op_trait<Output = Value> + Clone,
{
type Output = Self;
- fn $function(self, rhs: Self) -> Self::Output
+ fn $function(self, rhs: Value) -> Self::Output
{
Self {
- red: self.red.$function(rhs.red),
- green: self.green.$function(rhs.green),
- blue: self.blue.$function(rhs.blue),
+ $($component: self.$component.$function(rhs.clone())),+
}
}
}
};
}
-macro_rules! impl_scalar_math_op {
- ($math_op_trait: ident, $function: ident) => {
- impl<Value> $math_op_trait<Value> for Color<Value>
+gen_scalar_math_op_impl!(Rgb, Add, add, components = (r, g, b));
+gen_scalar_math_op_impl!(Rgb, Sub, sub, components = (r, g, b));
+gen_scalar_math_op_impl!(Rgb, Mul, mul, components = (r, g, b));
+gen_scalar_math_op_impl!(Rgb, Div, div, components = (r, g, b));
+
+gen_scalar_math_op_impl!(Rgba, Add, add, components = (r, g, b, a));
+gen_scalar_math_op_impl!(Rgba, Sub, sub, components = (r, g, b, a));
+gen_scalar_math_op_impl!(Rgba, Mul, mul, components = (r, g, b, a));
+gen_scalar_math_op_impl!(Rgba, Div, div, components = (r, g, b, a));
+
+macro_rules! gen_enum_scalar_math_op_impl {
+ (
+ $ident: ident,
+ $math_op_trait: ident,
+ $function: ident,
+ variants=($($variant: ident),+)
+ ) => {
+ impl<Value> $math_op_trait<Value> for $ident<Value>
where
Value: $math_op_trait<Output = Value> + Clone,
{
@@ -61,38 +147,44 @@ macro_rules! impl_scalar_math_op {
fn $function(self, rhs: Value) -> Self::Output
{
- Self {
- red: self.red.$function(rhs.clone()),
- green: self.green.$function(rhs.clone()),
- blue: self.blue.$function(rhs),
+ match self {
+ $(
+ Self::$variant(inner) =>
+ Self::$variant(inner.$function(rhs.clone()))
+ ),+
}
}
}
};
}
-impl_math_op!(Add, add);
-impl_math_op!(Sub, sub);
-impl_math_op!(Mul, mul);
-impl_math_op!(Div, div);
+gen_enum_scalar_math_op_impl!(Color, Add, add, variants = (Rgb, Rgba));
+gen_enum_scalar_math_op_impl!(Color, Sub, sub, variants = (Rgb, Rgba));
+gen_enum_scalar_math_op_impl!(Color, Mul, mul, variants = (Rgb, Rgba));
+gen_enum_scalar_math_op_impl!(Color, Div, div, variants = (Rgb, Rgba));
-impl_scalar_math_op!(Add, add);
-impl_scalar_math_op!(Sub, sub);
-impl_scalar_math_op!(Mul, mul);
-impl_scalar_math_op!(Div, div);
+impl PixelComponent for u8
+{
+ const COLOR_MAX: Self = u8::MAX;
+}
-impl<Value> Neg for Color<Value>
-where
- Value: Neg<Output = Value>,
+impl sealed::Sealed for u8 {}
+
+impl PixelComponent for u16
{
- type Output = Self;
+ const COLOR_MAX: Self = u16::MAX;
+}
- fn neg(self) -> Self::Output
- {
- Self {
- red: -self.red,
- green: -self.green,
- blue: -self.blue,
- }
- }
+impl sealed::Sealed for u16 {}
+
+impl PixelComponent for f32
+{
+ const COLOR_MAX: Self = 1.0;
+}
+
+impl sealed::Sealed for f32 {}
+
+mod sealed
+{
+ pub trait Sealed {}
}
diff --git a/engine/src/data_types/dimens.rs b/engine/src/data_types/dimens.rs
index 5002436..64a30d3 100644
--- a/engine/src/data_types/dimens.rs
+++ b/engine/src/data_types/dimens.rs
@@ -1,16 +1,88 @@
+use std::num::NonZeroU32;
+use std::ops::Div;
+
+use crate::reflection::Reflection;
+
/// 2D dimensions.
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Reflection)]
+#[reflection(impl_with_generics(<f32>, <f64>, <u32>, <u16>))]
pub struct Dimens<Value>
{
pub width: Value,
pub height: Value,
}
+impl<Value: Clone> From<Value> for Dimens<Value>
+{
+ fn from(value: Value) -> Self
+ {
+ Self { width: value.clone(), height: value }
+ }
+}
+
+impl<Value> From<(Value, Value)> for Dimens<Value>
+{
+ fn from(value: (Value, Value)) -> Self
+ {
+ Self { width: value.0, height: value.1 }
+ }
+}
+
+impl<Value: Div<Output = Value> + Clone> Div<Value> for Dimens<Value>
+{
+ type Output = Self;
+
+ fn div(self, rhs: Value) -> Self::Output
+ {
+ Self {
+ width: self.width / rhs.clone(),
+ height: self.height / rhs,
+ }
+ }
+}
+
+impl Dimens<u32>
+{
+ #[must_use]
+ pub fn try_into_nonzero(self) -> Option<Dimens<NonZeroU32>>
+ {
+ Some(Dimens {
+ width: NonZeroU32::new(self.width)?,
+ height: NonZeroU32::new(self.height)?,
+ })
+ }
+}
+
/// 3D dimensions.
-#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
+#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Reflection)]
+#[reflection(impl_with_generics(<f32>, <f64>, <u32>, <u16>))]
pub struct Dimens3<Value>
{
pub width: Value,
pub height: Value,
pub depth: Value,
}
+
+impl<Value: Clone> From<Value> for Dimens3<Value>
+{
+ fn from(value: Value) -> Self
+ {
+ Self {
+ width: value.clone(),
+ height: value.clone(),
+ depth: value,
+ }
+ }
+}
+
+impl<Value: Clone> From<(Value, Value, Value)> for Dimens3<Value>
+{
+ fn from(value: (Value, Value, Value)) -> Self
+ {
+ Self {
+ width: value.0,
+ height: value.1,
+ depth: value.2,
+ }
+ }
+}
diff --git a/engine/src/data_types/matrix.rs b/engine/src/data_types/matrix.rs
index 3a29ae2..3c54bb1 100644
--- a/engine/src/data_types/matrix.rs
+++ b/engine/src/data_types/matrix.rs
@@ -1,10 +1,12 @@
-use crate::vector::Vec3;
+use std::ops::{Index, IndexMut, Mul};
+
+use crate::data_types::dimens::Dimens3;
+use crate::vector::{Vec3, Vec4};
#[derive(Debug, Clone)]
pub struct Matrix<Value, const ROWS: usize, const COLUMNS: usize>
{
- /// Items must be layed out this way for it to work with OpenGL shaders.
- items: [[Value; ROWS]; COLUMNS],
+ items: [[Value; COLUMNS]; ROWS],
}
impl<Value, const ROWS: usize, const COLUMNS: usize> Matrix<Value, ROWS, COLUMNS>
@@ -20,17 +22,35 @@ impl<Value, const ROWS: usize, const COLUMNS: usize> Matrix<Value, ROWS, COLUMNS
}
}
- /// Sets the value at the specified cell.
- pub fn set_cell(&mut self, row: usize, column: usize, value: Value)
+ pub fn from_columns<Column>(columns: [Column; COLUMNS]) -> Self
+ where
+ Column: Into<[Value; ROWS]>,
+ Value: Default,
{
- self.items[column][row] = value;
+ let mut items =
+ std::array::from_fn(|_| std::array::from_fn(|_| Value::default()));
+
+ for (column_index, column) in columns.into_iter().enumerate() {
+ for (row_index, item) in column.into().into_iter().enumerate() {
+ items[row_index][column_index] = item;
+ }
+ }
+
+ Self { items }
}
- /// Returns the internal 2D array as a pointer.
- #[must_use]
- pub fn as_ptr(&self) -> *const Value
+ pub fn get_column_copied(&self, column_index: usize) -> [Value; ROWS]
+ where
+ Value: Default + Copy,
{
- self.items[0].as_ptr()
+ std::array::from_fn::<Value, ROWS, _>(|row_index| {
+ self[CellPos { row: row_index, col: column_index }]
+ })
+ }
+
+ pub fn items(&self) -> &[Value]
+ {
+ &self.items.as_flattened()
}
}
@@ -51,19 +71,13 @@ impl<const ROWS_COLS: usize> Matrix<f32, ROWS_COLS, ROWS_COLS>
#[must_use]
pub fn new_identity() -> Self
{
- let mut index = 0;
-
- let items = [(); ROWS_COLS].map(|()| {
- let mut columns = [0.0; ROWS_COLS];
-
- columns[index] = 1.0;
+ let mut identity = Self::new();
- index += 1;
-
- columns
- });
+ for index in 0..ROWS_COLS {
+ identity[CellPos { row: index, col: index }] = 1.0;
+ }
- Self { items }
+ identity
}
}
@@ -71,21 +85,21 @@ impl Matrix<f32, 4, 4>
{
pub fn translate(&mut self, translation: &Vec3<f32>)
{
- self.set_cell(0, 3, translation.x);
- self.set_cell(1, 3, translation.y);
- self.set_cell(2, 3, translation.z);
- self.set_cell(3, 3, 1.0);
+ self[CellPos { row: 0, col: 3 }] = translation.x;
+ self[CellPos { row: 1, col: 3 }] = translation.y;
+ self[CellPos { row: 2, col: 3 }] = translation.z;
+ self[CellPos { row: 3, col: 3 }] = 1.0;
}
- pub fn scale(&mut self, scaling: &Vec3<f32>)
+ pub fn scale(&mut self, scaling: &Dimens3<f32>)
{
- self.set_cell(0, 0, scaling.x);
- self.set_cell(1, 1, scaling.y);
- self.set_cell(2, 2, scaling.z);
- self.set_cell(3, 3, 1.0);
+ self[CellPos { row: 0, col: 0 }] = scaling.width;
+ self[CellPos { row: 1, col: 1 }] = scaling.height;
+ self[CellPos { row: 2, col: 2 }] = scaling.depth;
+ self[CellPos { row: 3, col: 3 }] = 1.0;
}
- pub fn look_at(&mut self, eye: &Vec3<f32>, target: &Vec3<f32>, up: &Vec3<f32>)
+ pub fn look_at(&mut self, eye: Vec3<f32>, target: Vec3<f32>, up: Vec3<f32>)
{
let rev_target_direction = (eye - target).normalize();
@@ -93,30 +107,206 @@ impl Matrix<f32, 4, 4>
let camera_up = rev_target_direction.cross(&camera_right);
- self.set_cell(0, 0, camera_right.x);
- self.set_cell(0, 1, camera_right.y);
- self.set_cell(0, 2, camera_right.z);
+ self[CellPos { row: 0, col: 0 }] = camera_right.x;
+ self[CellPos { row: 0, col: 1 }] = camera_right.y;
+ self[CellPos { row: 0, col: 2 }] = camera_right.z;
- self.set_cell(1, 0, camera_up.x);
- self.set_cell(1, 1, camera_up.y);
- self.set_cell(1, 2, camera_up.z);
+ self[CellPos { row: 1, col: 0 }] = camera_up.x;
+ self[CellPos { row: 1, col: 1 }] = camera_up.y;
+ self[CellPos { row: 1, col: 2 }] = camera_up.z;
- self.set_cell(2, 0, rev_target_direction.x);
- self.set_cell(2, 1, rev_target_direction.y);
- self.set_cell(2, 2, rev_target_direction.z);
+ self[CellPos { row: 2, col: 0 }] = rev_target_direction.x;
+ self[CellPos { row: 2, col: 1 }] = rev_target_direction.y;
+ self[CellPos { row: 2, col: 2 }] = rev_target_direction.z;
// The vector is negated since we want the world to be translated in the opposite
// direction of where we want the camera to move.
let camera_pos = -Vec3 {
- x: camera_right.dot(eye),
- y: camera_up.dot(eye),
- z: rev_target_direction.dot(eye),
+ x: camera_right.dot(&eye),
+ y: camera_up.dot(&eye),
+ z: rev_target_direction.dot(&eye),
+ };
+
+ self[CellPos { row: 0, col: 3 }] = camera_pos.x;
+ self[CellPos { row: 1, col: 3 }] = camera_pos.y;
+ self[CellPos { row: 2, col: 3 }] = camera_pos.z;
+
+ self[CellPos { row: 3, col: 3 }] = 1.0;
+ }
+
+ pub fn inverse(&self) -> Self
+ {
+ let coef_00 = self[CellPos { row: 2, col: 2 }] * self[CellPos { row: 3, col: 3 }]
+ - self[CellPos { row: 2, col: 3 }] * self[CellPos { row: 3, col: 2 }];
+ let coef_02 = self[CellPos { row: 2, col: 1 }] * self[CellPos { row: 3, col: 3 }]
+ - self[CellPos { row: 2, col: 3 }] * self[CellPos { row: 3, col: 1 }];
+ let coef_03 = self[CellPos { row: 2, col: 1 }] * self[CellPos { row: 3, col: 2 }]
+ - self[CellPos { row: 2, col: 2 }] * self[CellPos { row: 3, col: 1 }];
+
+ let coef_04 = self[CellPos { row: 1, col: 2 }] * self[CellPos { row: 3, col: 3 }]
+ - self[CellPos { row: 1, col: 3 }] * self[CellPos { row: 3, col: 2 }];
+ let coef_06 = self[CellPos { row: 1, col: 1 }] * self[CellPos { row: 3, col: 3 }]
+ - self[CellPos { row: 1, col: 3 }] * self[CellPos { row: 3, col: 1 }];
+ let coef_07 = self[CellPos { row: 1, col: 1 }] * self[CellPos { row: 3, col: 2 }]
+ - self[CellPos { row: 1, col: 2 }] * self[CellPos { row: 3, col: 1 }];
+
+ let coef_08 = self[CellPos { row: 1, col: 2 }] * self[CellPos { row: 2, col: 3 }]
+ - self[CellPos { row: 1, col: 3 }] * self[CellPos { row: 2, col: 2 }];
+ let coef_10 = self[CellPos { row: 1, col: 1 }] * self[CellPos { row: 2, col: 3 }]
+ - self[CellPos { row: 1, col: 3 }] * self[CellPos { row: 2, col: 1 }];
+ let coef_11 = self[CellPos { row: 1, col: 1 }] * self[CellPos { row: 2, col: 2 }]
+ - self[CellPos { row: 1, col: 2 }] * self[CellPos { row: 2, col: 1 }];
+
+ let coef_12 = self[CellPos { row: 0, col: 2 }] * self[CellPos { row: 3, col: 3 }]
+ - self[CellPos { row: 0, col: 3 }] * self[CellPos { row: 3, col: 2 }];
+ let coef_14 = self[CellPos { row: 0, col: 1 }] * self[CellPos { row: 3, col: 3 }]
+ - self[CellPos { row: 0, col: 3 }] * self[CellPos { row: 3, col: 1 }];
+ let coef_15 = self[CellPos { row: 0, col: 1 }] * self[CellPos { row: 3, col: 2 }]
+ - self[CellPos { row: 0, col: 2 }] * self[CellPos { row: 3, col: 1 }];
+
+ let coef_16 = self[CellPos { row: 0, col: 2 }] * self[CellPos { row: 2, col: 3 }]
+ - self[CellPos { row: 0, col: 3 }] * self[CellPos { row: 2, col: 2 }];
+ let coef_18 = self[CellPos { row: 0, col: 1 }] * self[CellPos { row: 2, col: 3 }]
+ - self[CellPos { row: 0, col: 3 }] * self[CellPos { row: 2, col: 1 }];
+ let coef_19 = self[CellPos { row: 0, col: 1 }] * self[CellPos { row: 2, col: 2 }]
+ - self[CellPos { row: 0, col: 2 }] * self[CellPos { row: 2, col: 1 }];
+
+ let coef_20 = self[CellPos { row: 0, col: 2 }] * self[CellPos { row: 1, col: 3 }]
+ - self[CellPos { row: 0, col: 3 }] * self[CellPos { row: 1, col: 2 }];
+ let coef_22 = self[CellPos { row: 0, col: 1 }] * self[CellPos { row: 1, col: 3 }]
+ - self[CellPos { row: 0, col: 3 }] * self[CellPos { row: 1, col: 1 }];
+ let coef_23 = self[CellPos { row: 0, col: 1 }] * self[CellPos { row: 1, col: 2 }]
+ - self[CellPos { row: 0, col: 2 }] * self[CellPos { row: 1, col: 1 }];
+
+ let fac_0 = Vec4 {
+ x: coef_00,
+ y: coef_00,
+ z: coef_02,
+ w: coef_03,
+ };
+ let fac_1 = Vec4 {
+ x: coef_04,
+ y: coef_04,
+ z: coef_06,
+ w: coef_07,
+ };
+ let fac_2 = Vec4 {
+ x: coef_08,
+ y: coef_08,
+ z: coef_10,
+ w: coef_11,
+ };
+ let fac_3 = Vec4 {
+ x: coef_12,
+ y: coef_12,
+ z: coef_14,
+ w: coef_15,
+ };
+ let fac_4 = Vec4 {
+ x: coef_16,
+ y: coef_16,
+ z: coef_18,
+ w: coef_19,
+ };
+ let fac_5 = Vec4 {
+ x: coef_20,
+ y: coef_20,
+ z: coef_22,
+ w: coef_23,
+ };
+
+ let vec_0 = Vec4 {
+ x: self[CellPos { row: 0, col: 1 }],
+ y: self[CellPos { row: 0, col: 0 }],
+ z: self[CellPos { row: 0, col: 0 }],
+ w: self[CellPos { row: 0, col: 0 }],
+ };
+ let vec_1 = Vec4 {
+ x: self[CellPos { row: 1, col: 1 }],
+ y: self[CellPos { row: 1, col: 0 }],
+ z: self[CellPos { row: 1, col: 0 }],
+ w: self[CellPos { row: 1, col: 0 }],
+ };
+ let vec_2 = Vec4 {
+ x: self[CellPos { row: 2, col: 1 }],
+ y: self[CellPos { row: 2, col: 0 }],
+ z: self[CellPos { row: 2, col: 0 }],
+ w: self[CellPos { row: 2, col: 0 }],
+ };
+ let vec_3 = Vec4 {
+ x: self[CellPos { row: 3, col: 1 }],
+ y: self[CellPos { row: 3, col: 0 }],
+ z: self[CellPos { row: 3, col: 0 }],
+ w: self[CellPos { row: 3, col: 0 }],
+ };
+
+ let inv_0 = vec_1 * fac_0 - vec_2 * fac_1 + vec_3 * fac_2;
+ let inv_1 = vec_0 * fac_0 - vec_2 * fac_3 + vec_3 * fac_4;
+ let inv_2 = vec_0 * fac_1 - vec_1 * fac_3 + vec_3 * fac_5;
+ let inv_3 = vec_0 * fac_2 - vec_1 * fac_4 + vec_2 * fac_5;
+
+ let sign_a = Vec4 { x: 1.0, y: -1.0, z: 1.0, w: -1.0 };
+ let sign_b = Vec4 { x: -1.0, y: 1.0, z: -1.0, w: 1.0 };
+
+ let inverse = Self::from_columns([
+ inv_0 * sign_a,
+ inv_1 * sign_b,
+ inv_2 * sign_a,
+ inv_3 * sign_b,
+ ]);
+
+ let row_0 = Vec4 {
+ x: inverse[CellPos { row: 0, col: 0 }],
+ y: inverse[CellPos { row: 0, col: 1 }],
+ z: inverse[CellPos { row: 0, col: 2 }],
+ w: inverse[CellPos { row: 0, col: 3 }],
};
- self.set_cell(0, 3, camera_pos.x);
- self.set_cell(1, 3, camera_pos.y);
- self.set_cell(2, 3, camera_pos.z);
+ let dot_0 = Vec4::<f32>::from(self.get_column_copied(0)) * row_0;
- self.set_cell(3, 3, 1.0);
+ let dot_1 = (dot_0.x + dot_0.y) + (dot_0.z + dot_0.w);
+
+ let one_over_determinant = 1.0 / dot_1;
+
+ inverse * one_over_determinant
}
}
+
+impl Mul<f32> for Matrix<f32, 4, 4>
+{
+ type Output = Self;
+
+ fn mul(self, scalar: f32) -> Self::Output
+ {
+ Self {
+ items: self.items.map(|row| (Vec4::from(row) * scalar).into()),
+ }
+ }
+}
+
+impl<Value, const ROWS: usize, const COLUMNS: usize> Index<CellPos>
+ for Matrix<Value, ROWS, COLUMNS>
+{
+ type Output = Value;
+
+ fn index(&self, cell_pos: CellPos) -> &Self::Output
+ {
+ &self.items[cell_pos.row][cell_pos.col]
+ }
+}
+
+impl<Value, const ROWS: usize, const COLUMNS: usize> IndexMut<CellPos>
+ for Matrix<Value, ROWS, COLUMNS>
+{
+ fn index_mut(&mut self, cell_pos: CellPos) -> &mut Self::Output
+ {
+ &mut self.items[cell_pos.row][cell_pos.col]
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct CellPos
+{
+ pub row: usize,
+ pub col: usize,
+}
diff --git a/engine/src/data_types/vector.rs b/engine/src/data_types/vector.rs
index 100c709..dfe9a99 100644
--- a/engine/src/data_types/vector.rs
+++ b/engine/src/data_types/vector.rs
@@ -1,83 +1,125 @@
-use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
-
-use crate::color::Color;
+use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
+
+use crate::reflection::Reflection;
+
+macro_rules! impl_math_op_traits {
+ (impl $op_trait: ident for $vector: ident, ($($vec_field: ident),+)) => {
+ paste::paste! {
+ impl<Value> $op_trait for $vector<Value>
+ where
+ Value: $op_trait<Output = Value>,
+ {
+ type Output = Self;
+
+ fn [<$op_trait:lower>](self, rhs: Self) -> Self::Output
+ {
+ Self::Output {$(
+ $vec_field: self.$vec_field.[<$op_trait:lower>](rhs.$vec_field)
+ ),+}
+ }
+ }
+
+ impl<Value> [<$op_trait Assign>] for $vector<Value>
+ where
+ Value: [<$op_trait Assign>],
+ {
+ fn [<$op_trait:lower _assign>](&mut self, rhs: Self)
+ {
+ $(
+ self.$vec_field.[<$op_trait:lower _assign>](rhs.$vec_field);
+ )+
+ }
+ }
+ }
+ };
+
+ (impl $op_trait: ident <Value> for $vector: ident, ($($vec_field: ident),+)) => {
+ paste::paste! {
+ impl<Value> $op_trait<Value> for $vector<Value>
+ where
+ Value: $op_trait<Output = Value> + Clone,
+ {
+ type Output = Self;
+
+ fn [<$op_trait:lower>](self, rhs: Value) -> Self::Output
+ {
+ Self {$(
+ $vec_field: self.$vec_field.[<$op_trait:lower>](rhs.clone())
+ ),+}
+ }
+ }
+
+ impl<Value> [<$op_trait Assign>]<Value> for $vector<Value>
+ where
+ Value: [<$op_trait Assign>] + Clone,
+ {
+ fn [<$op_trait:lower _assign>](&mut self, rhs: Value)
+ {
+ $(
+ self.$vec_field.[<$op_trait:lower _assign>](rhs.clone());
+ )+
+ }
+ }
+ }
+ };
+}
-#[derive(Debug, Default, Clone, Copy, PartialEq)]
-#[repr(C)]
-pub struct Vec2<Value>
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Reflection)]
+#[reflection(impl_with_generics(<f32>, <f64>, <u32>, <u16>))]
+pub struct Vec2<Value: 'static>
{
pub x: Value,
pub y: Value,
}
+impl<Value> Vec2<Value>
+{
+ pub fn into_array(self) -> [Value; 2]
+ {
+ self.into()
+ }
+}
+
impl Vec2<u32>
{
pub const ZERO: Self = Self { x: 0, y: 0 };
}
-impl<Value> Add<Value> for Vec2<Value>
-where
- Value: Add<Output = Value> + Clone,
-{
- type Output = Self;
+impl_math_op_traits!(impl Add for Vec2, (x, y));
+impl_math_op_traits!(impl Sub for Vec2, (x, y));
- fn add(self, rhs: Value) -> Self::Output
- {
- Self {
- x: self.x + rhs.clone(),
- y: self.y + rhs,
- }
- }
-}
+impl_math_op_traits!(impl Add<Value> for Vec2, (x, y));
+impl_math_op_traits!(impl Sub<Value> for Vec2, (x, y));
+impl_math_op_traits!(impl Mul<Value> for Vec2, (x, y));
+impl_math_op_traits!(impl Div<Value> for Vec2, (x, y));
-impl<Value> Sub<Value> for Vec2<Value>
-where
- Value: Sub<Output = Value> + Clone,
+impl<Value> From<[Value; 2]> for Vec2<Value>
{
- type Output = Self;
-
- fn sub(self, rhs: Value) -> Self::Output
+ fn from([x, y]: [Value; 2]) -> Self
{
- Self {
- x: self.x - rhs.clone(),
- y: self.y - rhs,
- }
+ Self { x, y }
}
}
-impl<Value> Mul<Value> for Vec2<Value>
-where
- Value: Mul<Output = Value> + Clone,
+impl<Value> From<(Value, Value)> for Vec2<Value>
{
- type Output = Self;
-
- fn mul(self, rhs: Value) -> Self::Output
+ fn from((x, y): (Value, Value)) -> Self
{
- Self {
- x: self.x * rhs.clone(),
- y: self.y * rhs,
- }
+ Self { x, y }
}
}
-impl<Value> Div<Value> for Vec2<Value>
-where
- Value: Div<Output = Value> + Clone,
+impl<Value> From<Vec2<Value>> for [Value; 2]
{
- type Output = Self;
-
- fn div(self, rhs: Value) -> Self::Output
+ fn from(vec: Vec2<Value>) -> Self
{
- Self {
- x: self.x / rhs.clone(),
- y: self.y / rhs,
- }
+ [vec.x, vec.y]
}
}
-#[derive(Debug, Default, Clone, Copy, PartialEq)]
-#[repr(C)]
-pub struct Vec3<Value>
+#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)]
+#[reflection(impl_with_generics(<f32>, <f64>, <u32>, <u16>))]
+pub struct Vec3<Value: 'static>
{
pub x: Value,
pub y: Value,
@@ -131,105 +173,43 @@ impl Vec3<f32>
(self.x * rhs.x) + (self.y * rhs.y) + (self.z * rhs.z)
}
- /// Returns a direction vector from the specified angle (in degrees).
+ /// Returns a normalized direction vector from the specified angle (in degrees).
#[must_use]
- pub fn direction_from_angle(pitch_degs: f32, yaw_degs: f32) -> Self
+ pub fn direction_from_deg_angles(angles: Angles<f32>) -> Self
{
Self {
- x: yaw_degs.to_radians().cos() * pitch_degs.to_radians().cos(),
- y: pitch_degs.to_radians().sin(),
- z: yaw_degs.to_radians().sin() * pitch_degs.to_radians().cos(),
- }
- }
-}
-
-impl<Value> Vec3<Value>
-{
- pub fn as_ptr(&self) -> *const Value
- {
- &self.x
- }
-}
-
-impl<Value> Sub for Vec3<Value>
-where
- Value: Sub<Value, Output = Value>,
-{
- type Output = Self;
-
- fn sub(self, rhs: Self) -> Self::Output
- {
- Self::Output {
- x: self.x - rhs.x,
- y: self.y - rhs.y,
- z: self.z - rhs.z,
- }
- }
-}
-
-impl<Value> Sub for &Vec3<Value>
-where
- for<'a, 'b> &'a Value: Sub<&'b Value, Output = Value>,
-{
- type Output = Vec3<Value>;
-
- fn sub(self, rhs: Self) -> Self::Output
- {
- Self::Output {
- x: &self.x - &rhs.x,
- y: &self.y - &rhs.y,
- z: &self.z - &rhs.z,
+ x: angles.yaw.to_radians().cos() * angles.pitch.to_radians().cos(),
+ y: angles.pitch.to_radians().sin(),
+ z: angles.yaw.to_radians().sin() * angles.pitch.to_radians().cos(),
}
+ .normalize()
}
-}
-impl<Value> Add for Vec3<Value>
-where
- Value: Add<Value, Output = Value>,
-{
- type Output = Self;
-
- fn add(self, rhs: Self) -> Self::Output
+ pub fn into_deg_angles(self) -> Angles<f32>
{
- Self::Output {
- x: self.x + rhs.x,
- y: self.y + rhs.y,
- z: self.z + rhs.z,
+ Angles {
+ yaw: self.z.atan2(self.x).to_degrees(),
+ pitch: self.y.asin().to_degrees(),
+ roll: 0.0,
}
}
}
-impl<Value> Add for &Vec3<Value>
-where
- for<'a, 'b> &'a Value: Add<&'b Value, Output = Value>,
+impl<Value> Vec3<Value>
{
- type Output = Vec3<Value>;
-
- fn add(self, rhs: Self) -> Self::Output
+ pub fn into_array(self) -> [Value; 3]
{
- Self::Output {
- x: &self.x + &rhs.x,
- y: &self.y + &rhs.y,
- z: &self.z + &rhs.z,
- }
+ self.into()
}
}
-impl<Value> Mul for Vec3<Value>
-where
- Value: Mul<Value, Output = Value>,
-{
- type Output = Self;
+impl_math_op_traits!(impl Add for Vec3, (x, y, z));
+impl_math_op_traits!(impl Sub for Vec3, (x, y, z));
- fn mul(self, rhs: Self) -> Self::Output
- {
- Self::Output {
- x: self.x * rhs.x,
- y: self.y * rhs.y,
- z: self.z * rhs.z,
- }
- }
-}
+impl_math_op_traits!(impl Add<Value> for Vec3, (x, y, z));
+impl_math_op_traits!(impl Sub<Value> for Vec3, (x, y, z));
+impl_math_op_traits!(impl Mul<Value> for Vec3, (x, y, z));
+impl_math_op_traits!(impl Div<Value> for Vec3, (x, y, z));
impl<Value> Neg for Vec3<Value>
where
@@ -247,100 +227,85 @@ where
}
}
-impl<Value> Add<Value> for Vec3<Value>
-where
- Value: Add<Value, Output = Value> + Clone,
+impl From<f32> for Vec3<f32>
{
- type Output = Self;
-
- fn add(mut self, rhs: Value) -> Self::Output
+ fn from(value: f32) -> Self
{
- self.x = self.x + rhs.clone();
- self.y = self.y + rhs.clone();
- self.z = self.z + rhs.clone();
-
- self
+ Self { x: value, y: value, z: value }
}
}
-impl<Value> Sub<Value> for Vec3<Value>
-where
- Value: Sub<Value, Output = Value> + Clone,
+impl<Value> From<[Value; 3]> for Vec3<Value>
{
- type Output = Self;
-
- fn sub(mut self, rhs: Value) -> Self::Output
+ fn from([x, y, z]: [Value; 3]) -> Self
{
- self.x = self.x - rhs.clone();
- self.y = self.y - rhs.clone();
- self.z = self.z - rhs.clone();
-
- self
+ Self { x, y, z }
}
}
-impl<Value> Mul<Value> for Vec3<Value>
-where
- Value: Mul<Value, Output = Value> + Clone,
+impl<Value> From<Vec3<Value>> for [Value; 3]
{
- type Output = Self;
-
- fn mul(mut self, rhs: Value) -> Self::Output
+ fn from(vec: Vec3<Value>) -> Self
{
- self.x = self.x * rhs.clone();
- self.y = self.y * rhs.clone();
- self.z = self.z * rhs.clone();
-
- self
+ [vec.x, vec.y, vec.z]
}
}
-impl<Value> AddAssign for Vec3<Value>
-where
- Value: AddAssign<Value>,
+#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)]
+#[reflection(impl_with_generics(<f32>, <f64>, <u32>, <u16>))]
+pub struct Vec4<Value: 'static>
{
- fn add_assign(&mut self, rhs: Self)
- {
- self.x += rhs.x;
- self.y += rhs.y;
- self.z += rhs.z;
- }
+ pub x: Value,
+ pub y: Value,
+ pub z: Value,
+ pub w: Value,
}
-impl<Value> SubAssign for Vec3<Value>
-where
- Value: SubAssign<Value>,
-{
- fn sub_assign(&mut self, rhs: Self)
- {
- self.x -= rhs.x;
- self.y -= rhs.y;
- self.z -= rhs.z;
- }
-}
+impl_math_op_traits!(impl Add for Vec4, (x, y, z, w));
+impl_math_op_traits!(impl Sub for Vec4, (x, y, z, w));
+impl_math_op_traits!(impl Mul for Vec4, (x, y, z, w));
-impl<Value> From<Value> for Vec3<Value>
-where
- Value: Clone,
+impl_math_op_traits!(impl Add<Value> for Vec4, (x, y, z, w));
+impl_math_op_traits!(impl Sub<Value> for Vec4, (x, y, z, w));
+impl_math_op_traits!(impl Mul<Value> for Vec4, (x, y, z, w));
+impl_math_op_traits!(impl Div<Value> for Vec4, (x, y, z, w));
+
+impl<Value: Clone> From<Value> for Vec4<Value>
{
fn from(value: Value) -> Self
{
Self {
x: value.clone(),
y: value.clone(),
- z: value,
+ z: value.clone(),
+ w: value,
}
}
}
-impl<Value> From<Color<Value>> for Vec3<Value>
+impl<Value> From<[Value; 4]> for Vec4<Value>
{
- fn from(color: Color<Value>) -> Self
+ fn from(values: [Value; 4]) -> Self
{
- Self {
- x: color.red,
- y: color.green,
- z: color.blue,
- }
+ let [x, y, z, w] = values;
+
+ Self { x, y, z, w }
}
}
+
+impl<Value> From<Vec4<Value>> for [Value; 4]
+{
+ fn from(vec: Vec4<Value>) -> Self
+ {
+ [vec.x, vec.y, vec.z, vec.w]
+ }
+}
+
+#[derive(Debug, Clone, Copy, Reflection)]
+#[reflection(impl_with_generics(<f32>, <f64>))]
+pub struct Angles<Value>
+{
+ pub yaw: Value,
+ pub pitch: Value,
+ pub roll: Value,
+}
diff --git a/engine/src/delta_time.rs b/engine/src/delta_time.rs
deleted file mode 100644
index 33a2fc8..0000000
--- a/engine/src/delta_time.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use std::time::{Duration, Instant};
-
-use ecs::component::local::Local;
-use ecs::sole::Single;
-use ecs::{Component, Sole};
-
-#[derive(Debug, Clone, Default, Sole)]
-pub struct DeltaTime
-{
- pub duration: Duration,
-}
-
-#[derive(Debug, Clone, Default, Component)]
-pub struct LastUpdate
-{
- pub time: Option<Instant>,
-}
-
-/// Updates the current delta time.
-///
-/// # Panics
-/// Will panic if no delta time component exists.
-pub fn update(mut delta_time: Single<DeltaTime>, mut last_update: Local<LastUpdate>)
-{
- let current_time = Instant::now();
-
- if let Some(last_update_time) = last_update.time {
- delta_time.duration = current_time.duration_since(last_update_time);
- }
-
- last_update.time = Some(current_time);
-}
diff --git a/engine/src/draw_flags.rs b/engine/src/draw_flags.rs
index df5eed1..b32bb14 100644
--- a/engine/src/draw_flags.rs
+++ b/engine/src/draw_flags.rs
@@ -1,11 +1,11 @@
-use ecs::Component;
-
-use crate::util::builder;
+use crate::builder;
+use crate::ecs::Component;
+use crate::reflection::Reflection;
builder! {
/// Flags for how a object should be drawn.
#[builder(name = Builder, derives = (Debug, Default, Clone))]
-#[derive(Debug, Default, Clone, Component)]
+#[derive(Debug, Default, Clone, Component, Reflection)]
#[non_exhaustive]
pub struct DrawFlags
{
@@ -22,14 +22,16 @@ impl DrawFlags
}
}
-#[derive(Debug, Default, Clone)]
+#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Reflection)]
pub struct PolygonModeConfig
{
pub face: PolygonModeFace,
pub mode: PolygonMode,
}
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(
+ Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflection,
+)]
pub enum PolygonMode
{
Point,
@@ -39,7 +41,9 @@ pub enum PolygonMode
Fill,
}
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(
+ Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflection,
+)]
pub enum PolygonModeFace
{
Front,
@@ -50,5 +54,5 @@ pub enum PolygonModeFace
}
/// Component that makes a object not be drawn.
-#[derive(Debug, Clone, Copy, Component)]
+#[derive(Debug, Default, Clone, Copy, Component, Reflection)]
pub struct NoDraw;
diff --git a/engine/src/file_format/wavefront/common.rs b/engine/src/file_format/wavefront/common.rs
index 8cdfb34..83fd8af 100644
--- a/engine/src/file_format/wavefront/common.rs
+++ b/engine/src/file_format/wavefront/common.rs
@@ -1,5 +1,7 @@
use std::num::{ParseFloatError, ParseIntError};
+use engine_ecs::util::array_vec::ArrayVec;
+
macro_rules! keyword {
(
$(#[$attr: meta])*
@@ -68,9 +70,11 @@ where
let mut parts = line.split(' ');
let keyword = KeywordT::from_str(parts.next().unwrap(), line_no)?;
- let arguments = parts
- .map(|part| Value::parse::<KeywordT>(part, line_no))
- .collect::<Result<Vec<_>, _>>()?;
+ let mut arguments = ArrayVec::<Value, 16>::default();
+
+ for part in parts {
+ arguments.push(Value::parse::<KeywordT>(part, line_no)?);
+ }
Ok(Some(Statement { keyword, arguments }))
}
@@ -265,7 +269,7 @@ impl Value
pub struct Statement<KeywordT: Keyword>
{
pub keyword: KeywordT,
- pub arguments: Vec<Value>,
+ pub arguments: ArrayVec<Value, 16>,
}
impl<KeywordT: Keyword> Statement<KeywordT>
diff --git a/engine/src/file_format/wavefront/mtl.rs b/engine/src/file_format/wavefront/mtl.rs
index d90dbcf..24387b9 100644
--- a/engine/src/file_format/wavefront/mtl.rs
+++ b/engine/src/file_format/wavefront/mtl.rs
@@ -2,17 +2,15 @@
//!
//! File format documentation: <https://paulbourke.net/dataformats/mtl>
-use std::path::Path;
+use std::path::{Path, PathBuf};
-use crate::color::Color;
+use crate::color::{Color, Rgb};
use crate::file_format::wavefront::common::{
keyword,
parse_statement_line,
ParsingError,
Statement,
};
-use crate::material::{Builder as MaterialBuilder, Material};
-use crate::texture::{Error as TextureError, Texture};
/// Parses the content of a Wavefront `.mtl`.
///
@@ -50,18 +48,41 @@ pub fn parse(obj_content: &str) -> Result<Vec<NamedMaterial>, Error>
}
#[derive(Debug, Clone)]
+#[non_exhaustive]
pub struct NamedMaterial
{
pub name: String,
- pub material: Material,
+ pub ambient: Color<f32>,
+ pub diffuse: Color<f32>,
+ pub specular: Color<f32>,
+ pub ambient_map: Option<TextureMap>,
+ pub diffuse_map: Option<TextureMap>,
+ pub specular_map: Option<TextureMap>,
+ pub shininess: f32,
+}
+
+impl Default for NamedMaterial
+{
+ fn default() -> Self
+ {
+ Self {
+ name: String::new(),
+ ambient: Color::Rgb(Rgb::<f32>::white()),
+ diffuse: Color::Rgb(Rgb::<f32>::white()),
+ specular: Color::Rgb(Rgb::<f32>::white()),
+ ambient_map: None,
+ diffuse_map: None,
+ specular_map: None,
+ shininess: 0.0,
+ }
+ }
}
#[derive(Debug, Clone)]
-pub struct UnfinishedNamedMaterial
+#[non_exhaustive]
+pub struct TextureMap
{
- name: String,
- material_builder: MaterialBuilder,
- ready: bool,
+ pub path: PathBuf,
}
#[derive(Debug, thiserror::Error)]
@@ -70,8 +91,14 @@ pub enum Error
#[error(transparent)]
ParsingError(#[from] ParsingError),
- #[error("Failed to open texture")]
- TextureError(#[from] TextureError),
+ #[error(
+ "A material start statement (newmtl) is expected before statement at line {}",
+ line_no
+ )]
+ ExpectedMaterialStartStmtBeforeStmt
+ {
+ line_no: usize
+ },
#[error(
"Unsupported number of arguments ({arg_count}) to {keyword} at line {line_no}"
@@ -100,59 +127,52 @@ fn statements_to_materials(
{
let mut materials = Vec::<NamedMaterial>::with_capacity(material_cnt);
- let mut curr_material = UnfinishedNamedMaterial {
- name: String::new(),
- material_builder: MaterialBuilder::new(),
- ready: false,
- };
-
for (line_no, statement) in statements {
if statement.keyword == Keyword::Newmtl {
- if curr_material.ready {
- tracing::debug!("Building material");
-
- let material = curr_material.material_builder.clone().build();
-
- materials.push(NamedMaterial { name: curr_material.name, material });
- }
-
let name = statement.get_text_arg(0, line_no)?;
- curr_material.name = name.to_string();
- curr_material.ready = true;
+ materials.push(NamedMaterial {
+ name: name.to_string(),
+ ..Default::default()
+ });
continue;
}
- if !curr_material.ready {
- // Discard statements not belonging to a material
- continue;
+ let Some(curr_material) = materials.last_mut() else {
+ return Err(Error::ExpectedMaterialStartStmtBeforeStmt { line_no });
};
match statement.keyword {
Keyword::Ka => {
let color = get_color_from_statement(&statement, line_no)?;
- tracing::debug!("Adding ambient color");
+ tracing::debug!(
+ "Adding ambient color {color:?} to material {}",
+ curr_material.name
+ );
- curr_material.material_builder =
- curr_material.material_builder.ambient(color);
+ curr_material.ambient = color;
}
Keyword::Kd => {
let color = get_color_from_statement(&statement, line_no)?;
- tracing::debug!("Adding diffuse color");
+ tracing::debug!(
+ "Adding diffuse color {color:?} to material {}",
+ curr_material.name
+ );
- curr_material.material_builder =
- curr_material.material_builder.diffuse(color);
+ curr_material.diffuse = color;
}
Keyword::Ks => {
let color = get_color_from_statement(&statement, line_no)?;
- tracing::debug!("Adding specular color");
+ tracing::debug!(
+ "Adding specular color {color:?} to material {}",
+ curr_material.name
+ );
- curr_material.material_builder =
- curr_material.material_builder.specular(color);
+ curr_material.specular = color;
}
Keyword::MapKa => {
if statement.arguments.len() > 1 {
@@ -165,51 +185,75 @@ fn statements_to_materials(
let texture_file_path = statement.get_text_arg(0, line_no)?;
- let texture = Texture::open(Path::new(texture_file_path))?;
-
- tracing::debug!("Adding ambient map");
+ tracing::debug!(
+ "Adding ambient map {texture_file_path} to material {}",
+ curr_material.name
+ );
- let texture_id = texture.id();
-
- curr_material.material_builder = curr_material
- .material_builder
- .texture(texture)
- .ambient_map(texture_id);
+ curr_material.ambient_map = Some(TextureMap {
+ path: Path::new(texture_file_path).to_path_buf(),
+ });
}
Keyword::MapKd => {
- let texture = get_map_from_texture(&statement, line_no)?;
+ if statement.arguments.len() > 1 {
+ return Err(Error::UnsupportedArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no,
+ });
+ }
- tracing::debug!("Adding diffuse map");
+ let texture_file_path = statement.get_text_arg(0, line_no)?;
- let texture_id = texture.id();
+ tracing::debug!(
+ "Adding diffuse map {texture_file_path} to material {}",
+ curr_material.name
+ );
- curr_material.material_builder = curr_material
- .material_builder
- .texture(texture)
- .diffuse_map(texture_id);
+ curr_material.diffuse_map = Some(TextureMap {
+ path: Path::new(texture_file_path).to_path_buf(),
+ });
}
Keyword::MapKs => {
- let texture = get_map_from_texture(&statement, line_no)?;
+ if statement.arguments.len() > 1 {
+ return Err(Error::UnsupportedArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no,
+ });
+ }
- tracing::debug!("Adding specular map");
+ let texture_file_path = statement.get_text_arg(0, line_no)?;
- let texture_id = texture.id();
+ tracing::debug!(
+ "Adding specular map {texture_file_path} to material {}",
+ curr_material.name
+ );
- curr_material.material_builder = curr_material
- .material_builder
- .texture(texture)
- .specular_map(texture_id);
+ curr_material.specular_map = Some(TextureMap {
+ path: Path::new(texture_file_path).to_path_buf(),
+ });
}
- Keyword::Newmtl => {}
- }
- }
+ Keyword::Ns => {
+ if statement.arguments.len() != 1 {
+ return Err(Error::UnsupportedArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no,
+ });
+ }
- if curr_material.ready {
- tracing::debug!("Building last material");
+ let shininess = statement.get_float_arg(0, line_no)?;
- let material = curr_material.material_builder.build();
+ tracing::debug!(
+ "Adding shininess {shininess} to material {}",
+ curr_material.name
+ );
- materials.push(NamedMaterial { name: curr_material.name, material });
+ curr_material.shininess = shininess;
+ }
+ Keyword::Newmtl => {}
+ }
}
Ok(materials)
@@ -232,25 +276,7 @@ fn get_color_from_statement(
let green = statement.get_float_arg(1, line_no)?;
let blue = statement.get_float_arg(2, line_no)?;
- Ok(Color { red, green, blue })
-}
-
-fn get_map_from_texture(
- statement: &Statement<Keyword>,
- line_no: usize,
-) -> Result<Texture, Error>
-{
- if statement.arguments.len() > 1 {
- return Err(Error::UnsupportedArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no,
- });
- }
-
- let texture_file_path = statement.get_text_arg(0, line_no)?;
-
- Ok(Texture::open(Path::new(texture_file_path))?)
+ Ok(Color::Rgb(Rgb { r: red, g: green, b: blue }))
}
keyword! {
@@ -271,5 +297,7 @@ keyword! {
#[keyword(rename = "map_Ks")]
MapKs,
+
+ Ns,
}
}
diff --git a/engine/src/file_format/wavefront/obj.rs b/engine/src/file_format/wavefront/obj.rs
index 88d566c..bebe291 100644
--- a/engine/src/file_format/wavefront/obj.rs
+++ b/engine/src/file_format/wavefront/obj.rs
@@ -6,15 +6,23 @@ use std::collections::HashMap;
use std::fs::read_to_string;
use std::path::PathBuf;
+use engine_ecs::util::Either;
+
use crate::file_format::wavefront::common::{
keyword,
parse_statement_line,
+ Keyword as _,
ParsingError,
Statement,
Triplet,
};
-use crate::mesh::{Mesh, Vertex};
-use crate::util::try_option;
+use crate::mesh::vertex_buffer::{
+ NamedVertexAttr,
+ VertexAttrInfo,
+ VertexBuffer as MeshVertexBuffer,
+ VertexLabel,
+};
+use crate::mesh::{Mesh, VertexAttrType};
use crate::vector::{Vec2, Vec3};
/// Parses the content of a Wavefront `.obj`.
@@ -28,34 +36,37 @@ pub fn parse(obj_content: &str) -> Result<Obj, Error>
.enumerate()
.map(|(line_index, line)| (line_index + 1, line));
- let statements = lines
- .map(|(line_no, line)| (line_no, parse_statement_line::<Keyword>(line, line_no)))
- .filter_map(|(line_no, result)| {
- let opt_statement = match result {
- Ok(opt_statement) => opt_statement,
- Err(err) => {
- return Some(Err(err));
- }
- };
-
- Some(Ok((line_no, opt_statement?)))
- })
- .collect::<Result<Vec<_>, _>>()?;
-
- let vertex_positions = get_vertex_positions_from_statements(&statements)?;
- let texture_positions = get_texture_positions_from_statements(&statements)?;
- let vertex_normals = get_vertex_normals_from_statements(&statements)?;
- let material_specs = get_material_specs_from_statements(&statements)?;
- let faces = get_faces_from_statements(&statements, &material_specs)?;
- let mtl_libs = get_mtl_libs_from_statements(&statements)?;
-
- Ok(Obj {
- vertex_positions,
- vertex_normals,
- texture_positions,
- faces,
- mtl_libs,
- })
+ let mut item_counts = ItemCounts::default();
+
+ for (line_index, line) in obj_content.lines().enumerate() {
+ if line.is_empty() || line.starts_with('#') {
+ continue;
+ }
+
+ let Some((keyword, _)) = line.split_once(" ") else {
+ continue;
+ };
+
+ let Ok(keyword) = Keyword::from_str(keyword, line_index + 1) else {
+ continue;
+ };
+
+ item_counts.increment_item_cnt_by_keyword(keyword);
+ }
+
+ let mut obj = Obj::with_capacities_from_item_cnts(item_counts);
+
+ let mut parsing_state = ParsingState::default();
+
+ for (line_no, line) in lines {
+ let Some(statement) = parse_statement_line::<Keyword>(line, line_no)? else {
+ continue;
+ };
+
+ obj.handle_statement(line_no, statement, &mut parsing_state)?;
+ }
+
+ Ok(obj)
}
/// The data of a Wavefront object file.
@@ -67,7 +78,8 @@ pub struct Obj
pub vertex_normals: Vec<Vec3<f32>>,
pub texture_positions: Vec<Vec2<f32>>,
pub faces: Vec<Face>,
- pub mtl_libs: Vec<Vec<PathBuf>>,
+ pub mtl_libs: Vec<PathBuf>,
+ pub unique_used_material_names: Vec<Box<str>>,
}
impl Obj
@@ -82,7 +94,24 @@ impl Obj
/// - A face index does not fit in a [`u32`]
pub fn to_mesh(&self) -> Result<Mesh, Error>
{
- let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3);
+ let mut vertex_buf = MeshVertexBuffer::with_capacity(
+ &[
+ VertexAttrInfo {
+ label: VertexLabel::Position,
+ ty: VertexAttrType::Float32Array { length: 3 },
+ },
+ VertexAttrInfo {
+ label: VertexLabel::Uv,
+ ty: VertexAttrType::Float32Array { length: 2 },
+ },
+ VertexAttrInfo {
+ label: VertexLabel::Normal,
+ ty: VertexAttrType::Float32Array { length: 3 },
+ },
+ ],
+ self.faces.len() * 3,
+ );
+
let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3);
let mut added_face_vertices =
@@ -96,10 +125,75 @@ impl Obj
continue;
}
- vertices.push(face_vertex.to_vertex(self)?);
+ let pos = self
+ .vertex_positions
+ .get(face_vertex.position as usize - 1)
+ .ok_or(Error::FaceVertexPositionNotFound {
+ vertex_pos_index: face_vertex.position,
+ })?
+ .clone();
+
+ let texture_pos = face_vertex.texture.map_or_else(
+ || {
+ if !self.texture_positions.is_empty() {
+ tracing::warn!(concat!(
+ "Wavefront OBJ has texture coordinates ",
+ "but face vertex does not specify one"
+ ));
+ }
+
+ Ok(Vec2::default())
+ },
+ |face_vertex_texture| {
+ self.texture_positions
+ .get(face_vertex_texture as usize - 1)
+ .ok_or(Error::FaceTexturePositionNotFound {
+ texture_pos_index: face_vertex_texture,
+ })
+ .cloned()
+ },
+ )?;
+
+ let normal = face_vertex.normal.map_or_else(
+ || {
+ if !self.vertex_normals.is_empty() {
+ tracing::warn!(concat!(
+ "Wavefront OBJ has normals ",
+ "but face vertex does not specify one"
+ ));
+ }
+
+ Ok(Vec3::default())
+ },
+ |face_vertex_normal| {
+ self.vertex_normals
+ .get(face_vertex_normal as usize - 1)
+ .ok_or(Error::FaceVertexNormalNotFound {
+ vertex_normal_index: face_vertex_normal,
+ })
+ .cloned()
+ },
+ )?;
- let vertex_index = u32::try_from(vertices.len() - 1)
- .map_err(|_| Error::FaceIndexTooBig(vertices.len() - 1))?;
+ vertex_buf.push((
+ NamedVertexAttr {
+ label: VertexLabel::Position,
+ value: pos.into_array(),
+ },
+ NamedVertexAttr {
+ label: VertexLabel::Uv,
+ value: texture_pos.into_array(),
+ },
+ NamedVertexAttr {
+ label: VertexLabel::Normal,
+ value: normal.into_array(),
+ },
+ ));
+
+ let vertex_index = vertex_buf.len() - 1;
+
+ let vertex_index = u32::try_from(vertex_index)
+ .map_err(|_| Error::FaceIndexTooBig(vertex_index))?;
indices.push(vertex_index);
@@ -107,7 +201,10 @@ impl Obj
}
}
- Ok(Mesh::new(vertices, Some(indices)))
+ Ok(Mesh::builder()
+ .vertices(vertex_buf)
+ .indices(indices)
+ .build())
}
/// Reads and parses the material libraries of this `Obj`.
@@ -123,7 +220,6 @@ impl Obj
{
self.mtl_libs
.iter()
- .flatten()
.map(|mtl_lib| {
Ok(parse_material_lib(&read_to_string(mtl_lib).map_err(
|err| MaterialLibsError::ReadingMaterialLibFailed {
@@ -138,6 +234,207 @@ impl Obj
})
.collect::<Result<Vec<_>, _>>()
}
+
+ fn with_capacities_from_item_cnts(item_counts: ItemCounts) -> Self
+ {
+ Self {
+ vertex_positions: Vec::with_capacity(item_counts.pos_cnt),
+ vertex_normals: Vec::with_capacity(item_counts.normal_cnt),
+ texture_positions: Vec::with_capacity(item_counts.uv_cnt),
+ faces: Vec::with_capacity(item_counts.face_cnt),
+ mtl_libs: Vec::with_capacity(item_counts.mtl_lib_cnt),
+ unique_used_material_names: Vec::with_capacity(
+ item_counts.material_usage_cnt,
+ ),
+ }
+ }
+
+ fn handle_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ parsing_state: &mut ParsingState,
+ ) -> Result<(), Error>
+ {
+ match statement.keyword {
+ Keyword::V => self.handle_position_statement(line_no, statement),
+ Keyword::Vn => self.handle_normal_statement(line_no, statement),
+ Keyword::Vt => self.handle_uv_statement(line_no, statement),
+ Keyword::F => self.handle_face_statement(line_no, statement, parsing_state),
+ Keyword::Mtllib => self.handle_material_lib_statement(line_no, statement),
+ Keyword::Usemtl => {
+ self.handle_material_usage_statement(line_no, statement, parsing_state)
+ }
+ Keyword::O | Keyword::S => Ok(()),
+ }
+ }
+
+ fn handle_position_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ ) -> Result<(), Error>
+ {
+ debug_assert_eq!(statement.keyword, Keyword::V);
+
+ if statement.arguments.len() == 4 {
+ return Err(Error::UnsupportedArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ if statement.arguments.len() > 4 {
+ return Err(Error::InvalidArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ let x = statement.get_float_arg(0, line_no)?;
+ let y = statement.get_float_arg(1, line_no)?;
+ let z = statement.get_float_arg(2, line_no)?;
+
+ self.vertex_positions.push(Vec3 { x, y, z });
+
+ Ok(())
+ }
+
+ fn handle_uv_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ ) -> Result<(), Error>
+ {
+ debug_assert_eq!(statement.keyword, Keyword::Vt);
+
+ if statement.arguments.len() == 3 {
+ return Err(Error::UnsupportedArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ if statement.arguments.len() > 3 {
+ return Err(Error::InvalidArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ let u = statement.get_float_arg(0, line_no)?;
+ let v = statement.get_float_arg(1, line_no)?;
+
+ self.texture_positions.push(Vec2 { x: u, y: v });
+
+ Ok(())
+ }
+
+ fn handle_normal_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ ) -> Result<(), Error>
+ {
+ debug_assert_eq!(statement.keyword, Keyword::Vn);
+
+ if statement.arguments.len() > 3 {
+ return Err(Error::InvalidArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ let i = statement.get_float_arg(0, line_no)?;
+ let j = statement.get_float_arg(1, line_no)?;
+ let k = statement.get_float_arg(2, line_no)?;
+
+ self.vertex_normals.push(Vec3 { x: i, y: j, z: k });
+
+ Ok(())
+ }
+
+ fn handle_material_usage_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ parsing_state: &mut ParsingState,
+ ) -> Result<(), Error>
+ {
+ debug_assert_eq!(statement.keyword, Keyword::Usemtl);
+
+ if statement.arguments.len() > 1 {
+ return Err(Error::InvalidArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ let material_name = statement
+ .get_text_arg(0, line_no)?
+ .to_owned()
+ .into_boxed_str();
+
+ if !self.unique_used_material_names.contains(&material_name) {
+ self.unique_used_material_names.push(material_name.clone());
+ }
+
+ parsing_state.current_material_name = Some(material_name);
+
+ Ok(())
+ }
+
+ fn handle_face_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ parsing_state: &mut ParsingState,
+ ) -> Result<(), Error>
+ {
+ debug_assert_eq!(statement.keyword, Keyword::F);
+
+ if statement.arguments.len() > 3 {
+ return Err(Error::UnsupportedArgumentCount {
+ keyword: statement.keyword.to_string(),
+ arg_count: statement.arguments.len(),
+ line_no: line_no,
+ });
+ }
+
+ let vertex_a = statement.get_triplet_arg(0, line_no)?;
+ let vertex_b = statement.get_triplet_arg(1, line_no)?;
+ let vertex_c = statement.get_triplet_arg(2, line_no)?;
+
+ self.faces.push(Face {
+ vertices: [vertex_a.into(), vertex_b.into(), vertex_c.into()],
+ material_name: parsing_state.current_material_name.clone(),
+ });
+
+ Ok(())
+ }
+
+ fn handle_material_lib_statement(
+ &mut self,
+ line_no: usize,
+ statement: Statement<Keyword>,
+ ) -> Result<(), Error>
+ {
+ debug_assert_eq!(statement.keyword, Keyword::Mtllib);
+
+ for (index, value) in statement.arguments.iter().enumerate() {
+ let mtl_lib = PathBuf::from(value.to_text(index, line_no)?);
+
+ self.mtl_libs.push(mtl_lib);
+ }
+
+ Ok(())
+ }
}
#[derive(Debug)]
@@ -145,7 +442,7 @@ impl Obj
pub struct Face
{
pub vertices: [FaceVertex; 3],
- pub material_name: Option<String>,
+ pub material_name: Option<Box<str>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -156,51 +453,6 @@ pub struct FaceVertex
pub normal: Option<u32>,
}
-impl FaceVertex
-{
- /// Tries to convert this face vertex into a [`Vertex`].
- ///
- /// # Errors
- /// Returns `Err` if:
- /// - The face's vertex position cannot be found in the given [`Obj`]
- /// - The face's texture position cannot be found in the given [`Obj`]
- /// - The face's vertex normal cannot be found in the given [`Obj`]
- pub fn to_vertex(&self, obj: &Obj) -> Result<Vertex, Error>
- {
- let mut vertex_builder = Vertex::builder();
-
- let vertex_pos = *obj.vertex_positions.get(self.position as usize - 1).ok_or(
- Error::FaceVertexPositionNotFound { vertex_pos_index: self.position },
- )?;
-
- vertex_builder = vertex_builder.pos(vertex_pos);
-
- if let Some(face_vertex_texture) = self.texture {
- let texture_pos = obj
- .texture_positions
- .get(face_vertex_texture as usize - 1)
- .ok_or(Error::FaceTexturePositionNotFound {
- texture_pos_index: face_vertex_texture,
- })?;
-
- vertex_builder = vertex_builder.texture_coords(*texture_pos);
- }
-
- if let Some(face_vertex_normal) = self.normal {
- let vertex_normal = *obj
- .vertex_normals
- .get(face_vertex_normal as usize - 1)
- .ok_or(Error::FaceVertexNormalNotFound {
- vertex_normal_index: face_vertex_normal,
- })?;
-
- vertex_builder = vertex_builder.normal(vertex_normal);
- }
-
- Ok(vertex_builder.build())
- }
-}
-
impl From<Triplet> for FaceVertex
{
fn from(triplet: Triplet) -> Self
@@ -280,6 +532,39 @@ pub enum MaterialLibsError<MaterialLibParseError>
},
}
+#[derive(Debug, Default)]
+struct ParsingState
+{
+ current_material_name: Option<Box<str>>,
+}
+
+#[derive(Debug, Default)]
+struct ItemCounts
+{
+ pos_cnt: usize,
+ uv_cnt: usize,
+ normal_cnt: usize,
+ face_cnt: usize,
+ mtl_lib_cnt: usize,
+ material_usage_cnt: usize,
+}
+
+impl ItemCounts
+{
+ fn increment_item_cnt_by_keyword(&mut self, keyword: Keyword)
+ {
+ match keyword {
+ Keyword::V => self.pos_cnt += 1,
+ Keyword::Vn => self.normal_cnt += 1,
+ Keyword::Vt => self.uv_cnt += 1,
+ Keyword::F => self.face_cnt += 1,
+ Keyword::Mtllib => self.mtl_lib_cnt += 1,
+ Keyword::Usemtl => self.material_usage_cnt += 1,
+ Keyword::O | Keyword::S => {}
+ }
+ }
+}
+
keyword! {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum Keyword {
@@ -309,233 +594,6 @@ keyword! {
}
}
-#[derive(Debug)]
-struct MaterialSpecifier
-{
- material_name: String,
- line_no: usize,
-}
-
-fn find_material_specifier_for_line_no(
- material_specifiers: &[MaterialSpecifier],
- line_no: usize,
-) -> Option<&MaterialSpecifier>
-{
- material_specifiers
- .iter()
- .rev()
- .find(|material_specifier| material_specifier.line_no < line_no)
-}
-
-enum Either<ValA, ValB>
-{
- A(ValA),
- B(ValB),
-}
-
-impl<ValA, ValB, Item> Iterator for Either<ValA, ValB>
-where
- ValA: Iterator<Item = Item>,
- ValB: Iterator<Item = Item>,
-{
- type Item = Item;
-
- fn next(&mut self) -> Option<Self::Item>
- {
- match self {
- Self::A(iter_a) => iter_a.next(),
- Self::B(iter_b) => iter_b.next(),
- }
- }
-}
-
-fn get_vertex_positions_from_statements(
- statements: &[(usize, Statement<Keyword>)],
-) -> Result<Vec<Vec3<f32>>, Error>
-{
- statements
- .iter()
- .filter_map(|(line_no, statement)| {
- if statement.keyword != Keyword::V {
- return None;
- }
-
- if statement.arguments.len() == 4 {
- return Some(Err(Error::UnsupportedArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- if statement.arguments.len() > 4 {
- return Some(Err(Error::InvalidArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- let x = try_option!(statement.get_float_arg(0, *line_no));
- let y = try_option!(statement.get_float_arg(1, *line_no));
- let z = try_option!(statement.get_float_arg(2, *line_no));
-
- Some(Ok(Vec3 { x, y, z }))
- })
- .collect::<Result<Vec<_>, Error>>()
-}
-
-fn get_texture_positions_from_statements(
- statements: &[(usize, Statement<Keyword>)],
-) -> Result<Vec<Vec2<f32>>, Error>
-{
- statements
- .iter()
- .filter_map(|(line_no, statement)| {
- if statement.keyword != Keyword::Vt {
- return None;
- }
-
- if statement.arguments.len() == 3 {
- return Some(Err(Error::UnsupportedArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- if statement.arguments.len() > 3 {
- return Some(Err(Error::InvalidArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- let u = try_option!(statement.get_float_arg(0, *line_no));
- let v = try_option!(statement.get_float_arg(1, *line_no));
-
- Some(Ok(Vec2 { x: u, y: v }))
- })
- .collect::<Result<Vec<_>, Error>>()
-}
-
-fn get_vertex_normals_from_statements(
- statements: &[(usize, Statement<Keyword>)],
-) -> Result<Vec<Vec3<f32>>, Error>
-{
- statements
- .iter()
- .filter_map(|(line_no, statement)| {
- if statement.keyword != Keyword::Vn {
- return None;
- }
-
- if statement.arguments.len() > 3 {
- return Some(Err(Error::InvalidArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- let i = try_option!(statement.get_float_arg(0, *line_no));
- let j = try_option!(statement.get_float_arg(1, *line_no));
- let k = try_option!(statement.get_float_arg(2, *line_no));
-
- Some(Ok(Vec3 { x: i, y: j, z: k }))
- })
- .collect::<Result<Vec<_>, Error>>()
-}
-
-fn get_material_specs_from_statements(
- statements: &[(usize, Statement<Keyword>)],
-) -> Result<Vec<MaterialSpecifier>, Error>
-{
- statements
- .iter()
- .filter_map(|(line_no, statement)| {
- if statement.keyword != Keyword::Usemtl {
- return None;
- }
-
- if statement.arguments.len() > 1 {
- return Some(Err(Error::InvalidArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- let material_name = try_option!(statement.get_text_arg(0, *line_no));
-
- Some(Ok(MaterialSpecifier {
- material_name: material_name.to_string(),
- line_no: *line_no,
- }))
- })
- .collect::<Result<Vec<_>, Error>>()
-}
-
-fn get_faces_from_statements(
- statements: &[(usize, Statement<Keyword>)],
- material_specifiers: &[MaterialSpecifier],
-) -> Result<Vec<Face>, Error>
-{
- statements
- .iter()
- .filter_map(|(line_no, statement)| {
- if statement.keyword != Keyword::F {
- return None;
- }
-
- if statement.arguments.len() > 3 {
- return Some(Err(Error::UnsupportedArgumentCount {
- keyword: statement.keyword.to_string(),
- arg_count: statement.arguments.len(),
- line_no: *line_no,
- }));
- }
-
- let vertex_a = try_option!(statement.get_triplet_arg(0, *line_no)).into();
- let vertex_b = try_option!(statement.get_triplet_arg(1, *line_no)).into();
- let vertex_c = try_option!(statement.get_triplet_arg(2, *line_no)).into();
-
- let material_name =
- find_material_specifier_for_line_no(material_specifiers, *line_no)
- .map(|material_specifier| material_specifier.material_name.clone());
-
- Some(Ok(Face {
- vertices: [vertex_a, vertex_b, vertex_c],
- material_name,
- }))
- })
- .collect::<Result<Vec<Face>, Error>>()
-}
-
-fn get_mtl_libs_from_statements(
- statements: &[(usize, Statement<Keyword>)],
-) -> Result<Vec<Vec<PathBuf>>, Error>
-{
- statements
- .iter()
- .filter_map(|(line_no, statement)| {
- if statement.keyword != Keyword::Mtllib {
- return None;
- }
-
- let mtl_lib_paths = try_option!(statement
- .arguments
- .iter()
- .enumerate()
- .map(|(index, value)| Ok(PathBuf::from(value.to_text(index, *line_no)?)))
- .collect::<Result<Vec<_>, ParsingError>>());
-
- Some(Ok(mtl_lib_paths))
- })
- .collect::<Result<Vec<_>, Error>>()
-}
-
#[cfg(test)]
mod tests
{
@@ -556,17 +614,26 @@ mod tests
assert_eq!(obj.faces.len(), 3);
assert_eq!(
- obj.faces[0].material_name.as_ref().expect("Expected Some"),
+ obj.faces[0]
+ .material_name
+ .as_deref()
+ .expect("Expected Some"),
"dark-green"
);
assert_eq!(
- obj.faces[1].material_name.as_ref().expect("Expected Some"),
+ obj.faces[1]
+ .material_name
+ .as_deref()
+ .expect("Expected Some"),
"dark-green"
);
assert_eq!(
- obj.faces[2].material_name.as_ref().expect("Expected Some"),
+ obj.faces[2]
+ .material_name
+ .as_deref()
+ .expect("Expected Some"),
"light-pink"
);
}
diff --git a/engine/src/image.rs b/engine/src/image.rs
new file mode 100644
index 0000000..b01ea53
--- /dev/null
+++ b/engine/src/image.rs
@@ -0,0 +1,363 @@
+use std::fs::File;
+use std::io::{BufRead, BufReader, Seek};
+use std::marker::PhantomData;
+use std::ops::Deref;
+use std::path::Path;
+
+use image_rs::GenericImageView as _;
+
+use crate::color::{Pixel as ColorPixel, Rgb, Rgba};
+use crate::data_types::dimens::Dimens;
+use crate::vector::Vec2;
+
+#[derive(Debug, Clone)]
+pub struct Image
+{
+ inner: image_rs::DynamicImage,
+}
+
+impl Image
+{
+ pub fn open(path: impl AsRef<Path>) -> Result<Self, Error>
+ {
+ let buffered_reader = BufReader::new(File::open(&path).map_err(Error::Io)?);
+
+ Self::from_reader(
+ buffered_reader,
+ Format::from_image_rs(
+ image_rs::ImageFormat::from_path(path)
+ .map_err(|_| Error::UnsupportedFormat)?,
+ )
+ .ok_or(Error::UnsupportedFormat)?,
+ )
+ }
+
+ pub fn from_reader(reader: impl BufRead + Seek, format: Format)
+ -> Result<Self, Error>
+ {
+ let image_reader =
+ image_rs::ImageReader::with_format(reader, format.into_image_rs());
+
+ let image = match image_reader
+ .decode()
+ .map_err(|err| Error::DecodeFailed(DecodeError(err)))?
+ {
+ image @ (image_rs::DynamicImage::ImageLuma8(_)
+ | image_rs::DynamicImage::ImageLumaA8(_)
+ | image_rs::DynamicImage::ImageLuma16(_)
+ | image_rs::DynamicImage::ImageLumaA16(_)) => image.into_rgba32f().into(),
+ image => image,
+ };
+
+ Ok(Self { inner: image })
+ }
+
+ pub fn from_pixels<PixelT, Buf>(
+ pixels: PixelBuffer<PixelT, Buf>,
+ ) -> Result<Self, PixelBufferLenIncorrectForSize>
+ where
+ PixelT: ColorPixel,
+ Buf: Deref<Target = [PixelT::Component]>,
+ Self: TryFrom<PixelBuffer<PixelT, Buf>, Error = PixelBufferLenIncorrectForSize>,
+ {
+ Self::try_from(pixels)
+ }
+
+ pub fn from_color<PixelT: ColorPixel>(color: PixelT, size: Dimens<u32>) -> Self
+ where
+ Self: From<(PixelT, Dimens<u32>)>,
+ {
+ Self::from((color, size))
+ }
+
+ pub fn dimensions(&self) -> Dimens<u32>
+ {
+ self.inner.dimensions().into()
+ }
+
+ pub fn color_type(&self) -> ColorType
+ {
+ self.inner.color().into()
+ }
+
+ pub fn color_space_is_srgb(&self) -> bool
+ {
+ match self.inner.color_space().primaries {
+ image_rs::metadata::CicpColorPrimaries::SRgb => true,
+ _ => false,
+ }
+ }
+
+ /// Returns a immutable view of a subsection of this image.
+ /// The `offset` argument sets the position of the top left corner of the view.
+ ///
+ /// # Panics
+ /// Panics if the view would be out of bounds
+ pub fn sub_view(&self, offset: Vec2<u32>, size: Dimens<u32>) -> SubView<'_>
+ {
+ assert!(offset.x + size.width <= self.dimensions().width);
+ assert!(offset.y + size.height <= self.dimensions().height);
+
+ SubView { image: &self, offset, size }
+ }
+
+ pub fn to_rgba8(&self) -> Self
+ {
+ Self { inner: self.inner.to_rgba8().into() }
+ }
+
+ /// Consumes the image and returns a RGBA8 image. If it already is RGBA8, the image is
+ /// returned as is. If not, the image is converted to RGBA8.
+ pub fn into_rgba8(self) -> Self
+ {
+ Self {
+ inner: self.inner.into_rgba8().into(),
+ }
+ }
+
+ pub fn as_bytes(&self) -> &[u8]
+ {
+ self.inner.as_bytes()
+ }
+
+ pub fn into_bytes(self) -> Vec<u8>
+ {
+ self.inner.into_bytes()
+ }
+}
+
+impl From<(Rgb<u8>, Dimens<u32>)> for Image
+{
+ fn from((color, size): (Rgb<u8>, Dimens<u32>)) -> Self
+ {
+ Self {
+ inner: image_rs::RgbImage::from_pixel(
+ size.width,
+ size.height,
+ image_rs::Rgb([color.r, color.g, color.b]),
+ )
+ .into(),
+ }
+ }
+}
+
+impl From<(Rgba<u8>, Dimens<u32>)> for Image
+{
+ fn from((color, size): (Rgba<u8>, Dimens<u32>)) -> Self
+ {
+ Self {
+ inner: image_rs::RgbaImage::from_pixel(
+ size.width,
+ size.height,
+ image_rs::Rgba([color.r, color.g, color.b, color.a]),
+ )
+ .into(),
+ }
+ }
+}
+
+macro_rules! gen_try_from_pixel_buffer_impl {
+ ($pixel: ident<$component: ty>) => {
+ impl<Buf> TryFrom<PixelBuffer<$pixel<$component>, Buf>> for Image
+ where
+ Buf: Deref<Target = [$component]>,
+ {
+ type Error = PixelBufferLenIncorrectForSize;
+
+ fn try_from(
+ pixels: PixelBuffer<$pixel<$component>, Buf>,
+ ) -> Result<Self, Self::Error>
+ {
+ Ok(
+ Image {
+ inner: image_rs::ImageBuffer::<
+ image_rs::$pixel<$component>,
+ Vec<_>,
+ >::from_raw(
+ pixels.size.width,
+ pixels.size.height,
+ pixels.buf.deref().to_vec(),
+ )
+ .ok_or(PixelBufferLenIncorrectForSize)?
+ .into(),
+ },
+ )
+ }
+ }
+ };
+}
+
+gen_try_from_pixel_buffer_impl!(Rgb<u8>);
+gen_try_from_pixel_buffer_impl!(Rgb<u16>);
+gen_try_from_pixel_buffer_impl!(Rgb<f32>);
+
+gen_try_from_pixel_buffer_impl!(Rgba<u8>);
+gen_try_from_pixel_buffer_impl!(Rgba<u16>);
+gen_try_from_pixel_buffer_impl!(Rgba<f32>);
+
+#[derive(Debug)]
+pub struct PixelBuffer<PixelT, Buf>
+where
+ PixelT: ColorPixel,
+ Buf: Deref<Target = [PixelT::Component]>,
+{
+ buf: Buf,
+ size: Dimens<u32>,
+ _pd: PhantomData<PixelT>,
+}
+
+impl<PixelT, Buf> PixelBuffer<PixelT, Buf>
+where
+ PixelT: ColorPixel,
+ Buf: Deref<Target = [PixelT::Component]>,
+{
+ pub fn new(buf: Buf, size: Dimens<u32>) -> Self
+ {
+ Self { buf, size, _pd: PhantomData }
+ }
+}
+
+/// An enumeration over supported color types and bit depths
+#[derive(Copy, PartialEq, Eq, Debug, Clone, Hash)]
+#[non_exhaustive]
+pub enum ColorType
+{
+ /// Pixel contains 8-bit R, G and B channels
+ Rgb8,
+
+ /// Pixel is 8-bit RGB with an alpha channel
+ Rgba8,
+
+ /// Pixel is 16-bit RGB
+ Rgb16,
+
+ /// Pixel is 16-bit RGBA
+ Rgba16,
+
+ /// Pixel is 32-bit float RGB
+ Rgb32F,
+
+ /// Pixel is 32-bit float RGBA
+ Rgba32F,
+}
+
+impl From<image_rs::ColorType> for ColorType
+{
+ fn from(color_type: image_rs::ColorType) -> Self
+ {
+ match color_type {
+ image_rs::ColorType::Rgb8 => Self::Rgb8,
+ image_rs::ColorType::Rgba8 => Self::Rgba8,
+ image_rs::ColorType::Rgb16 => Self::Rgb16,
+ image_rs::ColorType::Rgba16 => Self::Rgba16,
+ image_rs::ColorType::Rgb32F => Self::Rgb32F,
+ image_rs::ColorType::Rgba32F => Self::Rgba32F,
+ image_rs::ColorType::L8
+ | image_rs::ColorType::La8
+ | image_rs::ColorType::L16
+ | image_rs::ColorType::La16 => unimplemented!(),
+ _ => {
+ panic!("Unrecognized image_rs::ColorType variant");
+ }
+ }
+ }
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
+#[non_exhaustive]
+pub enum Format
+{
+ Png,
+ Jpeg,
+ Ico,
+}
+
+impl Format
+{
+ fn into_image_rs(self) -> image_rs::ImageFormat
+ {
+ match self {
+ Self::Png => image_rs::ImageFormat::Png,
+ Self::Jpeg => image_rs::ImageFormat::Jpeg,
+ Self::Ico => image_rs::ImageFormat::Ico,
+ }
+ }
+
+ fn from_image_rs(format: image_rs::ImageFormat) -> Option<Self>
+ {
+ match format {
+ image_rs::ImageFormat::Png => Some(Self::Png),
+ image_rs::ImageFormat::Jpeg => Some(Self::Jpeg),
+ image_rs::ImageFormat::Ico => Some(Self::Ico),
+ _ => None,
+ }
+ }
+}
+
+pub struct SubView<'image>
+{
+ image: &'image Image,
+ offset: Vec2<u32>,
+ size: Dimens<u32>,
+}
+
+impl SubView<'_>
+{
+ pub fn to_image(&self) -> Image
+ {
+ fn create_sub_image<Image>(
+ image: Image,
+ sub_view: &SubView<'_>,
+ ) -> image_rs::SubImage<Image>
+ {
+ let SubView { image: _, offset, size } = sub_view;
+
+ image_rs::SubImage::new(image, offset.x, offset.y, size.width, size.height)
+ }
+
+ Image {
+ inner: match &self.image.inner {
+ image_rs::DynamicImage::ImageRgb8(image) => {
+ create_sub_image(image, self).to_image().into()
+ }
+ image_rs::DynamicImage::ImageRgba8(image) => {
+ create_sub_image(image, self).to_image().into()
+ }
+ image_rs::DynamicImage::ImageRgb16(image) => {
+ create_sub_image(image, self).to_image().into()
+ }
+ image_rs::DynamicImage::ImageRgba16(image) => {
+ create_sub_image(image, self).to_image().into()
+ }
+ image_rs::DynamicImage::ImageRgb32F(image) => {
+ create_sub_image(image, self).to_image().into()
+ }
+ image_rs::DynamicImage::ImageRgba32F(image) => {
+ create_sub_image(image, self).to_image().into()
+ }
+ _ => unimplemented!(),
+ },
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("I/O error")]
+ Io(#[source] std::io::Error),
+
+ #[error("Failed to decode image")]
+ DecodeFailed(DecodeError),
+
+ #[error("Unsupported image format")]
+ UnsupportedFormat,
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct DecodeError(image_rs::ImageError);
+
+#[derive(Debug, thiserror::Error)]
+#[error("Length of pixel buffer is incorrect for the given image size")]
+pub struct PixelBufferLenIncorrectForSize;
diff --git a/engine/src/input.rs b/engine/src/input.rs
index d6a82f6..c5e6d09 100644
--- a/engine/src/input.rs
+++ b/engine/src/input.rs
@@ -1,249 +1,26 @@
-use std::collections::HashMap;
+use crate::ecs::extension::Collector as ExtensionCollector;
+use crate::ecs::pair::ChildOf;
+use crate::ecs::phase::Phase;
+use crate::ecs::{declare_entity, pair};
+use crate::windowing::PHASE as WINDOWING_PHASE;
-use ecs::extension::Collector as ExtensionCollector;
-use ecs::pair::{ChildOf, Pair};
-use ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE, START as START_PHASE};
-use ecs::sole::Single;
-use ecs::{static_entity, Sole};
+pub mod keyboard;
+pub mod mouse;
-use crate::vector::Vec2;
-use crate::window::{Window, UPDATE_PHASE as WINDOW_UPDATE_PHASE};
-
-mod reexports
-{
- pub use crate::window::{Key, KeyState};
-}
-
-pub use reexports::*;
-
-static_entity!(
- SET_PREV_KEY_STATE_PHASE,
- (Phase, Pair::new::<ChildOf>(*WINDOW_UPDATE_PHASE))
-);
-
-#[derive(Debug, Sole)]
-pub struct Keys
-{
- map: HashMap<Key, KeyData>,
- pending: Vec<(Key, KeyState)>,
-}
-
-impl Keys
-{
- #[must_use]
- pub fn new() -> Self
- {
- Self {
- map: Key::KEYS
- .iter()
- .map(|key| {
- (
- *key,
- KeyData {
- state: KeyState::Released,
- prev_tick_state: KeyState::Released,
- },
- )
- })
- .collect(),
- pending: Vec::with_capacity(Key::KEYS.len()),
- }
- }
-
- #[must_use]
- pub fn get_key_state(&self, key: Key) -> KeyState
- {
- let Some(key_data) = self.map.get(&key) else {
- unreachable!();
- };
-
- key_data.state
- }
-
- #[must_use]
- pub fn get_prev_key_state(&self, key: Key) -> KeyState
- {
- let Some(key_data) = self.map.get(&key) else {
- unreachable!();
- };
-
- key_data.prev_tick_state
- }
-
- pub fn set_key_state(&mut self, key: Key, new_key_state: KeyState)
- {
- let Some(key_data) = self.map.get_mut(&key) else {
- unreachable!();
- };
-
- key_data.state = new_key_state;
- }
-
- #[must_use]
- pub fn is_anything_pressed(&self) -> bool
- {
- self.map
- .values()
- .any(|key_data| matches!(key_data.state, KeyState::Pressed))
- }
-}
-
-impl Default for Keys
-{
- fn default() -> Self
- {
- Self::new()
- }
-}
-
-#[derive(Debug, Default, Clone, Sole)]
-pub struct Cursor
-{
- pub position: Vec2<f64>,
- pub has_moved: bool,
-}
-
-#[derive(Debug, Clone, Sole)]
-pub struct CursorFlags
-{
- /// This flag is set in two situations:
- /// A: The window has just started
- /// B: The window has gained focus again after losing focus.
- ///
- /// This flag only lasts a single tick then it is cleared (at the beginning of the
- /// next tick).
- pub is_first_move: CursorFlag,
-}
-
-impl Default for CursorFlags
-{
- fn default() -> Self
- {
- Self {
- is_first_move: CursorFlag { flag: true, ..Default::default() },
- }
- }
-}
-
-#[derive(Debug, Default, Clone)]
-pub struct CursorFlag
-{
- pub flag: bool,
- pub pending_clear: bool,
-}
-
-impl CursorFlag
-{
- pub fn clear(&mut self)
- {
- self.flag = false;
- self.pending_clear = false;
- }
+declare_entity! {
+pub PHASE: (Phase, pair!(ChildOf, { *WINDOWING_PHASE }));
}
/// Input extension.
#[derive(Debug, Default)]
pub struct Extension {}
-impl ecs::extension::Extension for Extension
+impl crate::ecs::extension::Extension for Extension
{
fn collect(self, mut collector: ExtensionCollector<'_>)
{
- collector.add_system(*START_PHASE, initialize);
- collector.add_system(*PRE_UPDATE_PHASE, maybe_clear_cursor_is_first_move);
- collector.add_system(*SET_PREV_KEY_STATE_PHASE, set_pending_key_states);
+ collector.spawn_declared_entity(&PHASE);
- collector.add_sole(Keys::default()).ok();
- collector.add_sole(Cursor::default()).ok();
- collector.add_sole(CursorFlags::default()).ok();
+ // TODO: Add input mapping
}
}
-
-fn initialize(
- keys: Single<Keys>,
- cursor: Single<Cursor>,
- cursor_flags: Single<CursorFlags>,
- window: Single<Window>,
-)
-{
- let keys_weak_ref = keys.to_weak_ref();
-
- window.set_key_callback(move |key, _scancode, key_state, _modifiers| {
- let keys_ref = keys_weak_ref.access().expect("No world");
-
- let mut keys = keys_ref.to_single();
-
- keys.pending.push((key, key_state));
- });
-
- let cursor_weak_ref = cursor.to_weak_ref();
-
- window.set_cursor_pos_callback(move |cursor_position| {
- let cursor_ref = cursor_weak_ref.access().expect("No world");
-
- let mut cursor = cursor_ref.to_single();
-
- cursor.position = Vec2 {
- x: cursor_position.x,
- y: cursor_position.y,
- };
-
- cursor.has_moved = true;
- });
-
- let cursor_flags_weak_ref = cursor_flags.to_weak_ref();
-
- window.set_focus_callback(move |is_focused| {
- tracing::trace!("Window is focused: {is_focused}");
-
- let cursor_flags_ref = cursor_flags_weak_ref.access().expect("No world");
-
- cursor_flags_ref.to_single().is_first_move.flag = is_focused;
- });
-}
-
-fn maybe_clear_cursor_is_first_move(
- cursor: Single<Cursor>,
- mut cursor_flags: Single<CursorFlags>,
-)
-{
- if cursor_flags.is_first_move.pending_clear {
- tracing::trace!("Clearing is_first_move");
-
- // This flag was set for the whole previous tick so it can be cleared now
- cursor_flags.is_first_move.clear();
-
- return;
- }
-
- if cursor.has_moved && cursor_flags.is_first_move.flag {
- tracing::trace!("Setting flag to clear is_first_move next tick");
-
- // Make this system clear is_first_move the next time it runs
- cursor_flags.is_first_move.pending_clear = true;
- }
-}
-
-fn set_pending_key_states(mut keys: Single<Keys>)
-{
- let Keys { map, pending } = &mut *keys;
-
- for key_data in map.values_mut() {
- key_data.prev_tick_state = key_data.state;
- }
-
- for (key, key_state) in pending {
- let Some(key_data) = map.get_mut(key) else {
- unreachable!();
- };
-
- key_data.state = *key_state;
- }
-}
-
-#[derive(Debug)]
-struct KeyData
-{
- state: KeyState,
- prev_tick_state: KeyState,
-}
diff --git a/engine/src/input/keyboard.rs b/engine/src/input/keyboard.rs
new file mode 100644
index 0000000..d226df0
--- /dev/null
+++ b/engine/src/input/keyboard.rs
@@ -0,0 +1,6 @@
+mod reexports
+{
+ pub use crate::windowing::keyboard::{Key, KeyState, Keyboard};
+}
+
+pub use reexports::*;
diff --git a/engine/src/input/mouse.rs b/engine/src/input/mouse.rs
new file mode 100644
index 0000000..c10aaf2
--- /dev/null
+++ b/engine/src/input/mouse.rs
@@ -0,0 +1,6 @@
+mod reexports
+{
+ pub use crate::windowing::mouse::{Button, ButtonState, Buttons, Mouse};
+}
+
+pub use reexports::*;
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index c537e06..36affff 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -1,42 +1,46 @@
#![deny(clippy::all, clippy::pedantic)]
#![allow(clippy::needless_pass_by_value)]
-use ecs::component::Sequence as ComponentSequence;
-use ecs::extension::Extension;
-use ecs::pair::Pair;
-use ecs::phase::PRE_UPDATE as PRE_UPDATE_PHASE;
-use ecs::sole::Sole;
-use ecs::system::{Into, System};
-use ecs::uid::Uid;
-use ecs::{SoleAlreadyExistsError, World};
-
-use crate::delta_time::{update as update_delta_time, DeltaTime, LastUpdate};
-
-mod opengl;
+use crate::asset::{Assets, Extension as AssetExtension};
+use crate::ecs::error::HandlerFn as ErrorHandlerFn;
+use crate::ecs::extension::Extension;
+use crate::ecs::World;
+
mod util;
+mod work_queue;
+pub mod asset;
pub mod camera;
pub mod collision;
pub mod data_types;
-pub mod delta_time;
pub mod draw_flags;
pub mod file_format;
+pub mod image;
pub mod input;
pub mod lighting;
pub mod material;
pub mod math;
pub mod mesh;
+pub mod model;
pub mod projection;
-pub mod renderer;
+pub mod reflection;
+pub mod rendering;
+pub mod scene;
+pub mod sky_box;
pub mod texture;
pub mod transform;
-pub mod window;
+pub mod ui;
+pub mod windowing;
+
+pub extern crate engine_ecs as ecs;
-pub extern crate ecs;
+pub use ecs::error::Error;
pub(crate) use crate::data_types::matrix;
pub use crate::data_types::{color, vector};
+const INITIAL_ASSET_CAPACITY: usize = 128;
+
#[derive(Debug)]
pub struct Engine
{
@@ -51,55 +55,28 @@ impl Engine
{
let mut world = World::new();
- world.add_sole(DeltaTime::default()).ok();
+ let mut assets = Assets::with_capacity(INITIAL_ASSET_CAPACITY);
- world.register_system(
- *PRE_UPDATE_PHASE,
- update_delta_time
- .into_system()
- .initialize((LastUpdate::default(),)),
- );
+ crate::model::asset::add_importers(&mut assets);
+ crate::material::asset::add_importers(&mut assets);
- Self { world }
- }
-
- pub fn spawn<Comps>(&mut self, components: Comps) -> Uid
- where
- Comps: ComponentSequence,
- {
- self.world.create_entity(components)
- }
+ crate::texture::initialize(&mut assets);
- pub fn register_system<'this, SystemImpl>(
- &'this mut self,
- phase_euid: Uid,
- system: impl System<'this, SystemImpl>,
- )
- {
- self.world.register_system(phase_euid, system);
- }
+ world.add_extension(AssetExtension { assets });
- pub fn register_observer_system<'this, SystemImpl>(
- &'this mut self,
- system: impl System<'this, SystemImpl>,
- event: Pair<Uid, Uid>,
- )
- {
- self.world.register_observer_system(system, event);
+ Self { world }
}
- /// Adds a globally shared singleton value.
- ///
- /// # Errors
- /// Returns `Err` if this [`Sole`] has already been added.
- pub fn add_sole(&mut self, sole: impl Sole) -> Result<(), SoleAlreadyExistsError>
+ pub fn with_error_handler(mut self, error_handler: ErrorHandlerFn) -> Self
{
- self.world.add_sole(sole)
+ self.world.set_err_handler(error_handler);
+ self
}
- pub fn add_extension(&mut self, extension: impl Extension)
+ pub fn with_extension(mut self, extension: impl Extension) -> Self
{
self.world.add_extension(extension);
+ self
}
/// Runs the event loop.
@@ -116,3 +93,10 @@ impl Default for Engine
Self::new()
}
}
+
+#[macro_export]
+macro_rules! error {
+ ($($tt: tt)*) => {
+ $crate::ecs::error!($($tt)*)
+ };
+}
diff --git a/engine/src/lighting.rs b/engine/src/lighting.rs
index 48adb0e..5314e58 100644
--- a/engine/src/lighting.rs
+++ b/engine/src/lighting.rs
@@ -1,16 +1,17 @@
-use ecs::{Component, Sole};
-
-use crate::color::Color;
+use crate::builder;
+use crate::color::{Color, Rgb};
use crate::data_types::vector::Vec3;
-use crate::util::builder;
+use crate::ecs::Component;
+use crate::reflection::Reflection;
builder! {
#[builder(name = PointLightBuilder, derives = (Debug, Clone))]
-#[derive(Debug, Clone, Component)]
+#[derive(Debug, Clone, Component, Reflection)]
#[non_exhaustive]
pub struct PointLight
{
- pub position: Vec3<f32>,
+ /// Position in local space.
+ pub local_position: Vec3<f32>,
pub diffuse: Color<f32>,
pub specular: Color<f32>,
pub attenuation_params: AttenuationParams,
@@ -31,9 +32,9 @@ impl Default for PointLight
fn default() -> Self
{
Self {
- position: Vec3::default(),
- diffuse: Color { red: 0.5, green: 0.5, blue: 0.5 },
- specular: Color { red: 1.0, green: 1.0, blue: 1.0 },
+ local_position: Vec3::default(),
+ diffuse: Color::Rgb(Rgb { r: 0.5, g: 0.5, b: 0.5 }),
+ specular: Color::Rgb(Rgb::<f32>::white()),
attenuation_params: AttenuationParams::default(),
}
}
@@ -48,7 +49,7 @@ impl Default for PointLightBuilder
}
/// Parameters for light [attenuation](https://en.wikipedia.org/wiki/Attenuation).
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Reflection)]
pub struct AttenuationParams
{
pub constant: f32,
@@ -58,7 +59,6 @@ pub struct AttenuationParams
impl Default for AttenuationParams
{
- #[must_use]
fn default() -> Self
{
Self {
@@ -70,8 +70,8 @@ impl Default for AttenuationParams
}
builder! {
-#[builder(name = DirectionalLightBuilder, derives = (Debug, Default, Clone))]
-#[derive(Debug, Default, Clone, Component)]
+#[builder(name = DirectionalLightBuilder, derives = (Debug, Clone))]
+#[derive(Debug, Clone, Component, Reflection)]
#[non_exhaustive]
pub struct DirectionalLight
{
@@ -90,22 +90,58 @@ impl DirectionalLight
}
}
+impl Default for DirectionalLight
+{
+ fn default() -> Self
+ {
+ Self::builder().build()
+ }
+}
+
+impl Default for DirectionalLightBuilder
+{
+ fn default() -> Self
+ {
+ Self {
+ diffuse: Color::Rgb(Rgb { r: 0.5, g: 0.5, b: 0.5 }),
+ specular: Color::Rgb(Rgb::<f32>::white()),
+ direction: Vec3::LEFT,
+ }
+ }
+}
+
builder! {
-/// Global light properties.
-#[builder(name = GlobalLightBuilder, derives = (Debug, Clone, Default))]
-#[derive(Debug, Clone, Default, Sole)]
+#[builder(name = EnvironmentalBuilder, derives = (Debug, Clone))]
+#[derive(Debug, Clone, Component, Reflection)]
#[non_exhaustive]
-pub struct GlobalLight
+pub struct Environmental
{
- pub ambient: Color<f32>,
+ pub ambient_color: Color<f32>,
}
}
-impl GlobalLight
+impl Environmental
{
- #[must_use]
- pub fn builder() -> GlobalLightBuilder
+ pub fn builder() -> EnvironmentalBuilder
+ {
+ EnvironmentalBuilder::default()
+ }
+}
+
+impl Default for Environmental
+{
+ fn default() -> Self
{
- GlobalLightBuilder::default()
+ Self::builder().build()
+ }
+}
+
+impl Default for EnvironmentalBuilder
+{
+ fn default() -> Self
+ {
+ Self {
+ ambient_color: Color::Rgb(Rgb { r: 0.2, g: 0.2, b: 0.2 }),
+ }
}
}
diff --git a/engine/src/material.rs b/engine/src/material.rs
index e368519..af982f4 100644
--- a/engine/src/material.rs
+++ b/engine/src/material.rs
@@ -1,21 +1,22 @@
-use ecs::Component;
+use crate::asset::Handle as AssetHandle;
+use crate::builder;
+use crate::color::{Color, Rgb};
+use crate::ecs::Component;
+use crate::reflection::Reflection;
+use crate::texture::Texture;
-use crate::color::Color;
-use crate::data_types::dimens::Dimens;
-use crate::texture::{Id as TextureId, Texture};
-use crate::util::builder;
+pub mod asset;
-#[derive(Debug, Clone, Component)]
+#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct Material
{
pub ambient: Color<f32>,
pub diffuse: Color<f32>,
pub specular: Color<f32>,
- pub ambient_map: TextureId,
- pub diffuse_map: TextureId,
- pub specular_map: TextureId,
- pub textures: Vec<Texture>,
+ pub ambient_map: Option<AssetHandle<Texture>>,
+ pub diffuse_map: Option<AssetHandle<Texture>>,
+ pub specular_map: Option<AssetHandle<Texture>>,
pub shininess: f32,
}
@@ -23,7 +24,22 @@ impl Material
{
pub fn builder() -> Builder
{
- Builder::default()
+ Builder::new()
+ }
+
+ pub fn textures(&self) -> impl Iterator<Item = &AssetHandle<Texture>>
+ {
+ [&self.ambient_map, &self.diffuse_map, &self.specular_map]
+ .into_iter()
+ .flatten()
+ }
+}
+
+impl Default for Material
+{
+ fn default() -> Self
+ {
+ Self::builder().build()
}
}
@@ -31,13 +47,12 @@ impl Material
#[derive(Debug, Clone)]
pub struct Builder
{
- ambient: Option<Color<f32>>,
- diffuse: Option<Color<f32>>,
- specular: Option<Color<f32>>,
- ambient_map: Option<TextureId>,
- diffuse_map: Option<TextureId>,
- specular_map: Option<TextureId>,
- textures: Vec<Texture>,
+ ambient: Color<f32>,
+ diffuse: Color<f32>,
+ specular: Color<f32>,
+ ambient_map: Option<AssetHandle<Texture>>,
+ diffuse_map: Option<AssetHandle<Texture>>,
+ specular_map: Option<AssetHandle<Texture>>,
shininess: f32,
}
@@ -47,13 +62,12 @@ impl Builder
pub fn new() -> Self
{
Self {
- ambient: None,
- diffuse: None,
- specular: None,
+ ambient: Color::Rgb(Rgb::<f32>::white()),
+ diffuse: Color::Rgb(Rgb::<f32>::white()),
+ specular: Color::Rgb(Rgb::<f32>::white()),
ambient_map: None,
diffuse_map: None,
specular_map: None,
- textures: Vec::new(),
shininess: 32.0,
}
}
@@ -61,7 +75,7 @@ impl Builder
#[must_use]
pub fn ambient(mut self, ambient: Color<f32>) -> Self
{
- self.ambient = Some(ambient);
+ self.ambient = ambient;
self
}
@@ -69,7 +83,7 @@ impl Builder
#[must_use]
pub fn diffuse(mut self, diffuse: Color<f32>) -> Self
{
- self.diffuse = Some(diffuse);
+ self.diffuse = diffuse;
self
}
@@ -77,13 +91,13 @@ impl Builder
#[must_use]
pub fn specular(mut self, specular: Color<f32>) -> Self
{
- self.specular = Some(specular);
+ self.specular = specular;
self
}
#[must_use]
- pub fn ambient_map(mut self, ambient_map: TextureId) -> Self
+ pub fn ambient_map(mut self, ambient_map: AssetHandle<Texture>) -> Self
{
self.ambient_map = Some(ambient_map);
@@ -91,7 +105,7 @@ impl Builder
}
#[must_use]
- pub fn diffuse_map(mut self, diffuse_map: TextureId) -> Self
+ pub fn diffuse_map(mut self, diffuse_map: AssetHandle<Texture>) -> Self
{
self.diffuse_map = Some(diffuse_map);
@@ -99,7 +113,7 @@ impl Builder
}
#[must_use]
- pub fn specular_map(mut self, specular_map: TextureId) -> Self
+ pub fn specular_map(mut self, specular_map: AssetHandle<Texture>) -> Self
{
self.specular_map = Some(specular_map);
@@ -107,22 +121,6 @@ impl Builder
}
#[must_use]
- pub fn textures(mut self, textures: impl IntoIterator<Item = Texture>) -> Self
- {
- self.textures = textures.into_iter().collect();
-
- self
- }
-
- #[must_use]
- pub fn texture(mut self, texture: Texture) -> Self
- {
- self.textures.push(texture);
-
- self
- }
-
- #[must_use]
pub fn shininess(mut self, shininess: f32) -> Self
{
self.shininess = shininess;
@@ -135,43 +133,15 @@ impl Builder
/// # Panics
/// Will panic if no ambient map, diffuse map or specular map is set.
#[must_use]
- pub fn build(mut self) -> Material
+ pub const fn build(self) -> Material
{
- let ambient_map = self.ambient_map.unwrap_or_else(|| {
- let texture = create_1x1_white_texture();
- let texture_id = texture.id();
-
- self.textures.push(texture);
-
- texture_id
- });
-
- let diffuse_map = self.diffuse_map.unwrap_or_else(|| {
- let texture = create_1x1_white_texture();
- let texture_id = texture.id();
-
- self.textures.push(texture);
-
- texture_id
- });
-
- let specular_map = self.specular_map.unwrap_or_else(|| {
- let texture = create_1x1_white_texture();
- let texture_id = texture.id();
-
- self.textures.push(texture);
-
- texture_id
- });
-
Material {
- ambient: self.ambient.unwrap_or(Color::WHITE_F32),
- diffuse: self.diffuse.unwrap_or(Color::WHITE_F32),
- specular: self.specular.unwrap_or(Color::WHITE_F32),
- ambient_map,
- diffuse_map,
- specular_map,
- textures: self.textures,
+ ambient: self.ambient,
+ diffuse: self.diffuse,
+ specular: self.specular,
+ ambient_map: self.ambient_map,
+ diffuse_map: self.diffuse_map,
+ specular_map: self.specular_map,
shininess: self.shininess,
}
}
@@ -187,8 +157,8 @@ impl Default for Builder
builder! {
/// Material flags.
-#[builder(name = FlagsBuilder, derives = (Debug, Default, Clone))]
-#[derive(Debug, Default, Clone, Component)]
+#[builder(name = FlagsBuilder, derives = (Debug, Clone))]
+#[derive(Debug, Clone, Component, Reflection)]
#[non_exhaustive]
pub struct Flags
{
@@ -201,13 +171,32 @@ pub struct Flags
impl Flags
{
#[must_use]
- pub fn builder() -> FlagsBuilder
+ pub const fn builder() -> FlagsBuilder
{
- FlagsBuilder::default()
+ FlagsBuilder::new()
}
}
-fn create_1x1_white_texture() -> Texture
+impl Default for Flags
{
- Texture::new_from_color(&Dimens { width: 1, height: 1 }, &Color::WHITE_U8)
+ fn default() -> Self
+ {
+ Self::builder().build()
+ }
+}
+
+impl FlagsBuilder
+{
+ pub const fn new() -> Self
+ {
+ Self { use_ambient_color: false }
+ }
+}
+
+impl Default for FlagsBuilder
+{
+ fn default() -> Self
+ {
+ Self::new()
+ }
}
diff --git a/engine/src/material/asset.rs b/engine/src/material/asset.rs
new file mode 100644
index 0000000..b210154
--- /dev/null
+++ b/engine/src/material/asset.rs
@@ -0,0 +1,89 @@
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::fs::read_to_string;
+use std::path::{Path, PathBuf};
+
+use crate::asset::{Assets, Handle as AssetHandle, Submitter as AssetSubmitter};
+use crate::material::Material;
+
+#[derive(Debug, Clone)]
+pub struct Map
+{
+ pub assets: HashMap<Cow<'static, str>, AssetHandle<Material>>,
+}
+
+/// Material asset import settings.
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Settings {}
+
+pub fn add_importers(assets: &mut Assets)
+{
+ assets.set_importer(["mtl"], import_wavefront_mtl_asset);
+}
+
+fn import_wavefront_mtl_asset(
+ asset_submitter: &mut AssetSubmitter<'_>,
+ path: &Path,
+ _settings: Option<&'_ Settings>,
+) -> Result<(), Error>
+{
+ let named_materials = crate::file_format::wavefront::mtl::parse(
+ &read_to_string(path)
+ .map_err(|err| Error::ReadFailed(err, path.to_path_buf()))?,
+ )?;
+
+ let mut mat_asset_map = Map {
+ assets: HashMap::with_capacity(named_materials.len()),
+ };
+
+ for material in named_materials {
+ let mut material_builder = Material::builder()
+ .ambient(material.ambient)
+ .diffuse(material.diffuse)
+ .specular(material.specular)
+ .shininess(material.shininess);
+
+ if let Some(ambient_map) = material.ambient_map {
+ material_builder = material_builder.ambient_map(
+ asset_submitter.submit_load_other(ambient_map.path.as_path()),
+ );
+ }
+
+ if let Some(diffuse_map) = material.diffuse_map {
+ material_builder = material_builder.diffuse_map(
+ asset_submitter.submit_load_other(diffuse_map.path.as_path()),
+ );
+ }
+
+ if let Some(specular_map) = material.specular_map {
+ material_builder = material_builder.specular_map(
+ asset_submitter.submit_load_other(specular_map.path.as_path()),
+ );
+ }
+
+ let material_name = material.name;
+ let material = material_builder.build();
+
+ let material_asset =
+ asset_submitter.submit_store_named(material_name.clone(), material);
+
+ mat_asset_map
+ .assets
+ .insert(material_name.into(), material_asset);
+ }
+
+ asset_submitter.submit_store(mat_asset_map);
+
+ Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+enum Error
+{
+ #[error("Failed to read file {}", .1.display())]
+ ReadFailed(#[source] std::io::Error, PathBuf),
+
+ #[error(transparent)]
+ Other(#[from] crate::file_format::wavefront::mtl::Error),
+}
diff --git a/engine/src/math.rs b/engine/src/math.rs
index 0340de8..eeed61e 100644
--- a/engine/src/math.rs
+++ b/engine/src/math.rs
@@ -5,9 +5,9 @@ use crate::vector::Vec3;
/// Calculates the surface normal of a triangle.
#[must_use]
pub fn calc_triangle_surface_normal(
- egde_a: &Vec3<f32>,
- edge_b: &Vec3<f32>,
- edge_c: &Vec3<f32>,
+ egde_a: Vec3<f32>,
+ edge_b: Vec3<f32>,
+ edge_c: Vec3<f32>,
) -> Vec3<f32>
{
let v1 = edge_b - egde_a;
diff --git a/engine/src/mesh.rs b/engine/src/mesh.rs
index 91d199e..c0120a2 100644
--- a/engine/src/mesh.rs
+++ b/engine/src/mesh.rs
@@ -1,35 +1,105 @@
-use ecs::Component;
+use std::alloc::Layout;
-use crate::util::builder;
-use crate::vector::{Vec2, Vec3};
+use zerocopy::IntoBytes;
-pub mod cube;
+use crate::data_types::dimens::Dimens3;
+use crate::mesh::vertex_buffer::{VertexAttrProperties, VertexLabel};
+use crate::vector::Vec3;
-#[derive(Debug, Clone, Component)]
+pub mod vertex_buffer;
+
+#[derive(Debug, Clone)]
pub struct Mesh
{
- vertices: Vec<Vertex>,
+ vertex_buf: vertex_buffer::VertexBuffer,
indices: Option<Vec<u32>>,
}
impl Mesh
{
- #[must_use]
- pub fn new(vertices: Vec<Vertex>, indices: Option<Vec<u32>>) -> Self
+ pub fn cube(size: Dimens3<f32>) -> Self
{
- Self { vertices, indices }
+ let half_w = size.width / 2.0;
+ let half_h = size.height / 2.0;
+ let half_d = size.depth / 2.0;
+
+ let vertices = [
+ // pos.x, pos.y, pos.z, texture_coords.x, texture_coords.y, normal.x,
+ // normal.y, normal.z
+ [half_w, half_h, half_d, 1.00, 1.00, 0.0, 1.00, 0.0],
+ [-half_w, half_h, half_d, 0.0, 1.00, 0.0, 1.00, 0.0],
+ [half_w, half_h, -half_d, 1.00, 0.0, 0.0, 1.00, 0.0],
+ [-half_w, half_h, -half_d, 0.0, 0.0, 0.0, 1.00, 0.0],
+ [half_w, -half_h, half_d, 1.00, 1.00, 0.0, -1.00, 0.0],
+ [-half_w, -half_h, half_d, 0.0, 1.00, 0.0, -1.00, 0.0],
+ [half_w, -half_h, -half_d, 1.00, 0.0, 0.0, -1.00, 0.0],
+ [-half_w, -half_h, -half_d, 0.0, 0.0, 0.0, -1.00, 0.0],
+ [-half_w, half_h, half_d, 1.00, 1.00, -1.00, 0.0, 0.0],
+ [-half_w, half_h, -half_d, 0.0, 1.00, -1.00, 0.0, 0.0],
+ [-half_w, -half_h, half_d, 1.00, 0.0, -1.00, 0.0, 0.0],
+ [-half_w, -half_h, -half_d, 0.0, 0.0, -1.00, 0.0, 0.0],
+ [half_w, half_h, half_d, 1.00, 1.00, 1.00, 0.0, 0.0],
+ [half_w, half_h, -half_d, 0.0, 1.00, 1.00, 0.0, 0.0],
+ [half_w, -half_h, half_d, 1.00, 0.0, 1.00, 0.0, 0.0],
+ [half_w, -half_h, -half_d, 0.0, 0.0, 1.00, 0.0, 0.0],
+ [half_w, half_h, -half_d, 1.00, 1.00, 0.0, 0.0, -1.00],
+ [-half_w, half_h, -half_d, 0.0, 1.00, 0.0, 0.0, -1.00],
+ [half_w, -half_h, -half_d, 1.00, 0.0, 0.0, 0.0, -1.00],
+ [-half_w, -half_h, -half_d, 0.0, 0.0, 0.0, 0.0, -1.00],
+ [half_w, half_h, half_d, 1.00, 1.00, 0.0, 0.0, 1.00],
+ [-half_w, half_h, half_d, 0.0, 1.00, 0.0, 0.0, 1.00],
+ [half_w, -half_h, half_d, 1.00, 0.0, 0.0, 0.0, 1.00],
+ [-half_w, -half_h, half_d, 0.0, 0.0, 0.0, 0.0, 1.00],
+ ];
+
+ let elements = [
+ 0, 1, 2, 1, 3, 2, 4, 5, 6, 5, 7, 6, 8, 9, 10, 9, 11, 10, 12, 13, 14, 13, 15,
+ 14, 16, 17, 18, 17, 19, 18, 20, 21, 22, 21, 23, 22,
+ ];
+
+ Mesh::builder()
+ .vertices(unsafe {
+ vertex_buffer::VertexBuffer::from_raw(
+ &[
+ VertexAttrProperties {
+ label: VertexLabel::Position,
+ ty: VertexAttrType::Float32Array { length: 3 },
+ layout: Layout::new::<[f32; 3]>(),
+ byte_offset: 0,
+ },
+ VertexAttrProperties {
+ label: VertexLabel::Uv,
+ ty: VertexAttrType::Float32Array { length: 2 },
+ layout: Layout::new::<[f32; 2]>(),
+ byte_offset: size_of::<[f32; 3]>(),
+ },
+ VertexAttrProperties {
+ label: VertexLabel::Normal,
+ ty: VertexAttrType::Float32Array { length: 3 },
+ layout: Layout::new::<[f32; 3]>(),
+ byte_offset: size_of::<[f32; 3]>() + size_of::<[f32; 2]>(),
+ },
+ ],
+ vertices.as_bytes().to_vec(),
+ )
+ })
+ .indices(elements)
+ .build()
}
- #[must_use]
- pub fn vertices(&self) -> &[Vertex]
+ pub fn builder() -> Builder
{
- &self.vertices
+ Builder::default()
}
- #[must_use]
- pub fn vertices_mut(&mut self) -> &mut [Vertex]
+ pub fn vertex_buf(&self) -> &vertex_buffer::VertexBuffer
{
- &mut self.vertices
+ &self.vertex_buf
+ }
+
+ pub fn vertex_buf_mut(&mut self) -> &mut vertex_buffer::VertexBuffer
+ {
+ &mut self.vertex_buf
}
#[must_use]
@@ -44,23 +114,36 @@ impl Mesh
self.indices.as_deref_mut()
}
+ pub fn set_indices(&mut self, indices: impl IntoIterator<Item = u32>)
+ {
+ let curr_indices = self.indices.get_or_insert_with(|| Vec::new());
+
+ curr_indices.clear();
+
+ curr_indices.extend(indices.into_iter());
+ }
+
/// Finds the vertex positions that are furthest in every 3D direction. Keep in mind
/// that this can be quite time-expensive if the mesh has many vertices.
- pub fn find_furthest_vertex_positions(&self) -> DirectionPositions<'_>
+ pub fn find_furthest_vertex_positions(&self) -> DirectionPositions
{
- let mut point_iter = self.vertices().iter().map(|vertex| &vertex.pos).into_iter();
+ let mut pos_iter = self
+ .vertex_buf()
+ .iter::<[f32; 3]>(VertexLabel::Position)
+ .map(|vertex_pos| Vec3::from(*vertex_pos))
+ .into_iter();
- let first_point = point_iter.next().unwrap();
+ let first_pos = pos_iter.next().unwrap();
- point_iter
+ pos_iter
.fold(
FurthestPosAcc {
- up: FurthestPos::new(&first_point, &Vec3::UP),
- down: FurthestPos::new(&first_point, &Vec3::DOWN),
- left: FurthestPos::new(&first_point, &Vec3::LEFT),
- right: FurthestPos::new(&first_point, &Vec3::RIGHT),
- back: FurthestPos::new(&first_point, &Vec3::BACK),
- front: FurthestPos::new(&first_point, &Vec3::FRONT),
+ up: FurthestPos::new(first_pos, &Vec3::UP),
+ down: FurthestPos::new(first_pos, &Vec3::DOWN),
+ left: FurthestPos::new(first_pos, &Vec3::LEFT),
+ right: FurthestPos::new(first_pos, &Vec3::RIGHT),
+ back: FurthestPos::new(first_pos, &Vec3::BACK),
+ front: FurthestPos::new(first_pos, &Vec3::FRONT),
},
|mut furthest_pos_acc, pos| {
furthest_pos_acc.up.update_if_further(pos);
@@ -77,39 +160,75 @@ impl Mesh
}
}
-builder! {
-#[builder(name = VertexBuilder, derives = (Debug, Default, Clone))]
-#[derive(Debug, Clone, Default, PartialEq)]
-#[non_exhaustive]
-pub struct Vertex
+/// Mesh builder
+#[derive(Debug, Clone, Default)]
+pub struct Builder
{
- pub pos: Vec3<f32>,
- pub texture_coords: Vec2<f32>,
- pub normal: Vec3<f32>,
+ vertex_buf: vertex_buffer::VertexBuffer,
+ indices: Option<Vec<u32>>,
}
+
+impl Builder
+{
+ pub fn new() -> Self
+ {
+ Self::default()
+ }
+
+ pub fn vertices(mut self, vertices_bytes: vertex_buffer::VertexBuffer) -> Self
+ {
+ self.vertex_buf = vertices_bytes;
+ self
+ }
+
+ pub fn indices(mut self, indices: impl IntoIterator<Item = u32>) -> Self
+ {
+ self.indices = Some(indices.into_iter().collect());
+ self
+ }
+
+ pub fn build(self) -> Mesh
+ {
+ Mesh {
+ vertex_buf: self.vertex_buf,
+ indices: self.indices,
+ }
+ }
}
-impl Vertex
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum VertexAttrType
{
- #[must_use]
- pub fn builder() -> VertexBuilder
+ Float32,
+ Float32Array
{
- VertexBuilder::default()
+ length: usize,
+ },
+}
+
+impl VertexAttrType
+{
+ pub fn layout(&self) -> Layout
+ {
+ match self {
+ Self::Float32 => Layout::new::<f32>(),
+ Self::Float32Array { length } => Layout::array::<f32>(*length).unwrap(),
+ }
}
}
#[derive(Debug, Clone)]
-pub struct DirectionPositions<'mesh>
+pub struct DirectionPositions
{
- pub up: &'mesh Vec3<f32>,
- pub down: &'mesh Vec3<f32>,
- pub left: &'mesh Vec3<f32>,
- pub right: &'mesh Vec3<f32>,
- pub back: &'mesh Vec3<f32>,
- pub front: &'mesh Vec3<f32>,
+ pub up: Vec3<f32>,
+ pub down: Vec3<f32>,
+ pub left: Vec3<f32>,
+ pub right: Vec3<f32>,
+ pub back: Vec3<f32>,
+ pub front: Vec3<f32>,
}
-impl<'mesh> From<FurthestPosAcc<'mesh>> for DirectionPositions<'mesh>
+impl<'mesh> From<FurthestPosAcc<'mesh>> for DirectionPositions
{
fn from(acc: FurthestPosAcc<'mesh>) -> Self
{
@@ -138,14 +257,14 @@ struct FurthestPosAcc<'mesh>
#[derive(Debug)]
struct FurthestPos<'mesh>
{
- pos: &'mesh Vec3<f32>,
+ pos: Vec3<f32>,
dot_prod: f32,
direction: &'mesh Vec3<f32>,
}
impl<'mesh> FurthestPos<'mesh>
{
- fn new(pos: &'mesh Vec3<f32>, direction: &'mesh Vec3<f32>) -> Self
+ fn new(pos: Vec3<f32>, direction: &'mesh Vec3<f32>) -> Self
{
Self {
pos,
@@ -154,9 +273,9 @@ impl<'mesh> FurthestPos<'mesh>
}
}
- fn update_if_further(&mut self, point: &'mesh Vec3<f32>)
+ fn update_if_further(&mut self, point: Vec3<f32>)
{
- let point_dot_prod = self.direction.dot(point);
+ let point_dot_prod = self.direction.dot(&point);
if point_dot_prod > self.dot_prod {
self.pos = point;
diff --git a/engine/src/mesh/cube.rs b/engine/src/mesh/cube.rs
deleted file mode 100644
index 6c9d381..0000000
--- a/engine/src/mesh/cube.rs
+++ /dev/null
@@ -1,480 +0,0 @@
-use crate::math::calc_triangle_surface_normal;
-use crate::mesh::{Mesh, Vertex};
-use crate::util::builder;
-use crate::vector::{Vec2, Vec3};
-
-builder! {
-/// Cube mesh creation specification.
-#[builder(name = CreationSpecBuilder, derives = (Debug, Default))]
-#[derive(Debug, Default)]
-#[non_exhaustive]
-pub struct CreationSpec
-{
- pub width: f32,
- pub height: f32,
- pub depth: f32,
-}
-}
-
-impl CreationSpec
-{
- /// Returns a new `CreationSpec` builder.
- #[must_use]
- pub fn builder() -> CreationSpecBuilder
- {
- CreationSpecBuilder::default()
- }
-}
-
-/// Describes a single side of a cube (obviously).
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum Side
-{
- /// +Z
- Front,
-
- /// -Z
- Back,
-
- /// -X
- Left,
-
- /// +X
- Right,
-
- /// +Y
- Top,
-
- /// -Y
- Bottom,
-}
-
-/// Describes what location on a side of a cube a face is.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum FaceLocation
-{
- /// 🮝
- RightUp,
-
- /// 🮟
- LeftDown,
-}
-
-/// Creates a cube mesh.
-///
-/// By default, the texture coordinates are arranged so that the full texture is visible
-/// on every side. This can be changed inside of the `face_cb` function.
-pub fn create(
- creation_spec: CreationSpec,
- face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices,
-) -> Mesh
-{
- let mut data = Data::default();
-
- create_side(&SidePositions::new_top(&creation_spec), &mut data);
- create_side(&SidePositions::new_bottom(&creation_spec), &mut data);
- create_side(&SidePositions::new_left(&creation_spec), &mut data);
- create_side(&SidePositions::new_right(&creation_spec), &mut data);
- create_side(&SidePositions::new_back(&creation_spec), &mut data);
- create_side(&SidePositions::new_front(&creation_spec), &mut data);
-
- data.into_mesh(face_cb)
-}
-
-#[derive(Debug, Default)]
-struct Data
-{
- faces: Vec<Face>,
- vertex_data: VertexData,
-}
-
-#[derive(Debug, Default)]
-struct VertexData
-{
- vertex_positions: Vec<Vec3<f32>>,
- vertex_normals: Vec<Vec3<f32>>,
-}
-
-impl Data
-{
- fn into_mesh(
- self,
- mut face_cb: impl FnMut(FaceVertices, Side, FaceLocation) -> FaceVertices,
- ) -> Mesh
- {
- let mut vertices = Vec::<Vertex>::with_capacity(self.faces.len() * 3);
- let mut indices = Vec::<u32>::with_capacity(self.faces.len() * 3);
-
- let mut face_location = FaceLocation::RightUp;
-
- let Self { faces, vertex_data } = self;
-
- for face in faces {
- let side = face.side;
-
- let face_vertices = face_cb(
- FaceVertices::new(face, &vertex_data)
- .with_full_per_side_tex_coords(face_location),
- side,
- face_location,
- );
-
- for vertex in face_vertices.vertices {
- if let Some((prev_vertex_index, _)) = vertices
- .iter()
- .enumerate()
- .find(|(_, prev_vertex)| *prev_vertex == &vertex)
- {
- indices
- .push(u32::try_from(prev_vertex_index).expect(
- "Vertex index does not fit into 32-bit unsigned int",
- ));
-
- continue;
- }
-
- vertices.push(vertex);
-
- let vertex_index = u32::try_from(vertices.len() - 1)
- .expect("Vertex index does not fit into 32-bit unsigned int");
-
- indices.push(vertex_index);
- }
-
- match face_location {
- FaceLocation::RightUp => face_location = FaceLocation::LeftDown,
- FaceLocation::LeftDown => face_location = FaceLocation::RightUp,
- }
- }
-
- Mesh::new(vertices, Some(indices))
- }
-}
-
-/// The vertices of a single face of a cube.
-#[derive(Debug, Default, Clone)]
-pub struct FaceVertices
-{
- /// The three vertices of a face in counter-clockwise order.
- ///
- /// Order when [`FaceLocation::RightUp`]:
- /// ```text
- /// ₂ ₁
- /// 🮝
- /// ³
- /// ```
- ///
- /// Order when [`FaceLocation::LeftDown`]:
- /// ```text
- /// ₁
- /// 🮟
- /// ² ³
- /// ```
- pub vertices: [Vertex; 3],
-}
-
-impl FaceVertices
-{
- fn new(face: Face, vertex_data: &VertexData) -> Self
- {
- Self {
- vertices: face.vertices.map(|face_vertex| {
- let vertex_pos = vertex_data
- .vertex_positions
- .get(face_vertex.pos_index as usize)
- .expect("Vertex position index is out of bounds")
- .clone();
-
- let vertex_normal = vertex_data
- .vertex_normals
- .get(face_vertex.normal_index as usize)
- .expect("Vertex normal index is out of bounds")
- .clone();
-
- Vertex::builder()
- .pos(vertex_pos)
- .normal(vertex_normal)
- .build()
- }),
- }
- }
-
- fn with_full_per_side_tex_coords(mut self, face_location: FaceLocation) -> Self
- {
- match face_location {
- FaceLocation::RightUp => {
- self.vertices[0].texture_coords = Vec2 { x: 1.0, y: 1.0 };
- self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 1.0 };
- self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 };
- }
- FaceLocation::LeftDown => {
- self.vertices[0].texture_coords = Vec2 { x: 0.0, y: 1.0 };
- self.vertices[1].texture_coords = Vec2 { x: 0.0, y: 0.0 };
- self.vertices[2].texture_coords = Vec2 { x: 1.0, y: 0.0 };
- }
- };
-
- self
- }
-}
-
-#[derive(Debug)]
-struct Face
-{
- vertices: [FaceVertex; 3],
- side: Side,
-}
-
-#[derive(Debug, PartialEq, Eq, Hash, Clone)]
-struct FaceVertex
-{
- pos_index: u32,
- normal_index: u32,
-}
-
-#[derive(Debug)]
-struct SidePositions
-{
- up_left: Vec3<f32>,
- up_right: Vec3<f32>,
- down_left: Vec3<f32>,
- down_right: Vec3<f32>,
- normal_calc_order: NormalCalcOrder,
- side: Side,
-}
-
-impl SidePositions
-{
- fn new_top(creation_spec: &CreationSpec) -> Self
- {
- let up_left = Vec3 {
- x: -(creation_spec.width / 2.0),
- y: creation_spec.height / 2.0,
- z: creation_spec.depth / 2.0,
- };
-
- let down_right = Vec3 {
- x: creation_spec.width / 2.0,
- y: creation_spec.height / 2.0,
- z: -(creation_spec.depth / 2.0),
- };
-
- Self {
- up_left,
- up_right: Vec3 { x: down_right.x, ..up_left.clone() },
- down_left: Vec3 { x: up_left.x, ..down_right.clone() },
- down_right,
- normal_calc_order: NormalCalcOrder::Clockwise,
- side: Side::Top,
- }
- }
-
- fn new_bottom(creation_spec: &CreationSpec) -> Self
- {
- let up_left = Vec3 {
- x: -(creation_spec.width / 2.0),
- y: -creation_spec.height / 2.0,
- z: creation_spec.depth / 2.0,
- };
-
- let down_right = Vec3 {
- x: creation_spec.width / 2.0,
- y: -creation_spec.height / 2.0,
- z: -(creation_spec.depth / 2.0),
- };
-
- Self {
- up_left,
- up_right: Vec3 { x: down_right.x, ..up_left.clone() },
- down_left: Vec3 { x: up_left.x, ..down_right.clone() },
- down_right,
- normal_calc_order: NormalCalcOrder::CounterClockwise,
- side: Side::Bottom,
- }
- }
-
- fn new_left(creation_spec: &CreationSpec) -> Self
- {
- let up_left = Vec3 {
- x: -(creation_spec.width / 2.0),
- y: creation_spec.height / 2.0,
- z: -(creation_spec.depth / 2.0),
- };
-
- let down_right = Vec3 {
- x: -(creation_spec.width / 2.0),
- y: -(creation_spec.height / 2.0),
- z: creation_spec.depth / 2.0,
- };
-
- Self {
- up_left,
- up_right: Vec3 { z: down_right.z, ..up_left.clone() },
- down_left: Vec3 { z: up_left.z, ..down_right.clone() },
- down_right,
- normal_calc_order: NormalCalcOrder::CounterClockwise,
- side: Side::Left,
- }
- }
-
- fn new_right(creation_spec: &CreationSpec) -> Self
- {
- let up_left = Vec3 {
- x: (creation_spec.width / 2.0),
- y: creation_spec.height / 2.0,
- z: -(creation_spec.depth / 2.0),
- };
-
- let down_right = Vec3 {
- x: (creation_spec.width / 2.0),
- y: -(creation_spec.height / 2.0),
- z: creation_spec.depth / 2.0,
- };
-
- Self {
- up_left,
- up_right: Vec3 { z: down_right.z, ..up_left.clone() },
- down_left: Vec3 { z: up_left.z, ..down_right.clone() },
- down_right,
- normal_calc_order: NormalCalcOrder::Clockwise,
- side: Side::Right,
- }
- }
-
- fn new_back(creation_spec: &CreationSpec) -> Self
- {
- let up_left = Vec3 {
- x: -(creation_spec.width / 2.0),
- y: creation_spec.height / 2.0,
- z: -creation_spec.depth / 2.0,
- };
-
- let down_right = Vec3 {
- x: creation_spec.width / 2.0,
- y: -(creation_spec.height / 2.0),
- z: -creation_spec.depth / 2.0,
- };
-
- Self {
- up_left,
- up_right: Vec3 { x: down_right.x, ..up_left.clone() },
- down_left: Vec3 { x: up_left.x, ..down_right.clone() },
- down_right,
- normal_calc_order: NormalCalcOrder::Clockwise,
- side: Side::Back,
- }
- }
-
- fn new_front(creation_spec: &CreationSpec) -> Self
- {
- let up_left = Vec3 {
- x: -(creation_spec.width / 2.0),
- y: creation_spec.height / 2.0,
- z: creation_spec.depth / 2.0,
- };
-
- let down_right = Vec3 {
- x: creation_spec.width / 2.0,
- y: -(creation_spec.height / 2.0),
- z: creation_spec.depth / 2.0,
- };
-
- Self {
- up_left,
- up_right: Vec3 { x: down_right.x, ..up_left.clone() },
- down_left: Vec3 { x: up_left.x, ..down_right.clone() },
- down_right,
- normal_calc_order: NormalCalcOrder::CounterClockwise,
- side: Side::Front,
- }
- }
-}
-
-#[derive(Debug)]
-enum NormalCalcOrder
-{
- Clockwise,
- CounterClockwise,
-}
-
-fn create_side(side_positions: &SidePositions, data: &mut Data)
-{
- let normal = match side_positions.normal_calc_order {
- NormalCalcOrder::Clockwise => calc_triangle_surface_normal(
- &side_positions.up_left,
- &side_positions.up_right,
- &side_positions.down_left,
- ),
- NormalCalcOrder::CounterClockwise => calc_triangle_surface_normal(
- &side_positions.up_left,
- &side_positions.down_left,
- &side_positions.up_right,
- ),
- };
-
- data.vertex_data.vertex_normals.push(normal);
-
- let top_normal_index = data.vertex_data.vertex_normals.len() - 1;
-
- data.vertex_data
- .vertex_positions
- .push(side_positions.up_right);
-
- let up_right_pos_index = data.vertex_data.vertex_positions.len() - 1;
-
- data.vertex_data
- .vertex_positions
- .push(side_positions.up_left);
-
- let up_left_pos_index = data.vertex_data.vertex_positions.len() - 1;
-
- data.vertex_data
- .vertex_positions
- .push(side_positions.down_left);
-
- let down_left_pos_index = data.vertex_data.vertex_positions.len() - 1;
-
- data.vertex_data
- .vertex_positions
- .push(side_positions.down_right);
-
- let down_right_pos_index = data.vertex_data.vertex_positions.len() - 1;
-
- // 🮝
- data.faces.push(Face {
- vertices: [
- FaceVertex {
- pos_index: up_right_pos_index as u32,
- normal_index: top_normal_index as u32,
- },
- FaceVertex {
- pos_index: up_left_pos_index as u32,
- normal_index: top_normal_index as u32,
- },
- FaceVertex {
- pos_index: down_right_pos_index as u32,
- normal_index: top_normal_index as u32,
- },
- ],
- side: side_positions.side,
- });
-
- // 🮟
- data.faces.push(Face {
- vertices: [
- FaceVertex {
- pos_index: up_left_pos_index as u32,
- normal_index: top_normal_index as u32,
- },
- FaceVertex {
- pos_index: down_left_pos_index as u32,
- normal_index: top_normal_index as u32,
- },
- FaceVertex {
- pos_index: down_right_pos_index as u32,
- normal_index: top_normal_index as u32,
- },
- ],
- side: side_positions.side,
- });
-}
diff --git a/engine/src/mesh/vertex_buffer.rs b/engine/src/mesh/vertex_buffer.rs
new file mode 100644
index 0000000..90e5752
--- /dev/null
+++ b/engine/src/mesh/vertex_buffer.rs
@@ -0,0 +1,279 @@
+use std::alloc::Layout;
+use std::borrow::Cow;
+use std::marker::PhantomData;
+
+use seq_macro::seq;
+use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
+
+use crate::mesh::VertexAttrType;
+
+pub trait VertexAttrValue:
+ IntoBytes + FromBytes + KnownLayout + Immutable + 'static
+{
+ fn ty() -> VertexAttrType;
+}
+
+impl VertexAttrValue for f32
+{
+ fn ty() -> VertexAttrType
+ {
+ VertexAttrType::Float32
+ }
+}
+
+impl<const LEN: usize> VertexAttrValue for [f32; LEN]
+{
+ fn ty() -> VertexAttrType
+ {
+ VertexAttrType::Float32Array { length: LEN }
+ }
+}
+
+#[derive(Debug)]
+pub struct NamedVertexAttr<Value: VertexAttrValue>
+{
+ pub label: VertexLabel,
+ pub value: Value,
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct VertexAttrProperties
+{
+ pub label: VertexLabel,
+ pub ty: VertexAttrType,
+ pub layout: Layout,
+ pub byte_offset: usize,
+}
+
+#[derive(Debug)]
+pub struct VertexAttrInfo
+{
+ pub label: VertexLabel,
+ pub ty: VertexAttrType,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum VertexLabel
+{
+ Position,
+ Normal,
+ Uv,
+ Color,
+ Other(Cow<'static, str>),
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct VertexBuffer
+{
+ buf: Vec<u8>,
+ vertex_size: usize,
+ vertex_attr_props: Vec<VertexAttrProperties>,
+}
+
+impl VertexBuffer
+{
+ pub unsafe fn from_raw(
+ vertex_attr_props: &[VertexAttrProperties],
+ buffer: Vec<u8>,
+ ) -> Self
+ {
+ let last_vertex_attr_props = vertex_attr_props
+ .iter()
+ .max_by_key(|props| props.byte_offset)
+ .expect("No vertex attribute properties are given");
+
+ let vertex_size =
+ last_vertex_attr_props.byte_offset + last_vertex_attr_props.layout.size();
+
+ Self {
+ buf: buffer,
+ vertex_size,
+ vertex_attr_props: vertex_attr_props.to_vec(),
+ }
+ }
+
+ pub fn with_capacity(vertex_attrs: &[VertexAttrInfo], capacity: usize) -> Self
+ {
+ let mut vertex_attr_props = vertex_attrs
+ .iter()
+ .map(|VertexAttrInfo { label, ty }| VertexAttrProperties {
+ label: label.clone(),
+ ty: ty.clone(),
+ layout: ty.layout(),
+ byte_offset: 0,
+ })
+ .collect::<Vec<_>>();
+
+ let mut vertex_layout = Layout::new::<()>();
+
+ for VertexAttrProperties {
+ layout: vertex_attr_layout,
+ byte_offset: vertex_attr_byte_offset,
+ ..
+ } in &mut vertex_attr_props
+ {
+ let (new_struct_layout, byte_offset) =
+ vertex_layout.extend(*vertex_attr_layout).unwrap();
+
+ *vertex_attr_byte_offset = byte_offset;
+ vertex_layout = new_struct_layout;
+ }
+
+ let vertex_layout = vertex_layout.pad_to_align();
+
+ Self {
+ buf: Vec::with_capacity(vertex_layout.size() * capacity),
+ vertex_size: vertex_layout.size(),
+ vertex_attr_props,
+ }
+ }
+
+ pub fn push<VertexAttrs: NamedVertexAttrs>(&mut self, vertex: VertexAttrs)
+ {
+ assert_eq!(
+ self.vertex_attr_props.len(),
+ vertex.vertex_attr_cnt(),
+ "Vertex has incorrect amount of attributes"
+ );
+
+ if self.buf.spare_capacity_mut().len() < self.vertex_size {
+ self.buf.reserve_exact(self.vertex_size * (self.len() / 2));
+ }
+
+ let spare_capacity = self.buf.spare_capacity_mut();
+
+ let vertex_attrs = vertex.vertex_attrs();
+
+ for (vertex_label, vertex_attr_bytes, vertex_attr_ty) in vertex_attrs {
+ let vertex_attr_props = self
+ .vertex_attr_props
+ .iter()
+ .find(|vertex_attr_props| &vertex_attr_props.label == vertex_label)
+ .unwrap();
+
+ assert_eq!(vertex_attr_ty, vertex_attr_props.ty);
+
+ let start_offset = vertex_attr_props.byte_offset;
+
+ let end_offset = start_offset + vertex_attr_props.layout.size();
+
+ spare_capacity[start_offset..end_offset]
+ .write_copy_of_slice(vertex_attr_bytes);
+ }
+
+ unsafe {
+ self.buf.set_len(self.buf.len() + self.vertex_size);
+ }
+ }
+
+ pub fn vertex_attr_props(&self) -> &[VertexAttrProperties]
+ {
+ &self.vertex_attr_props
+ }
+
+ pub fn len(&self) -> usize
+ {
+ assert_eq!(self.buf.len() % self.vertex_size, 0, "Invalid length");
+
+ self.buf.len() / self.vertex_size
+ }
+
+ pub fn vertex_size(&self) -> usize
+ {
+ self.vertex_size
+ }
+
+ pub fn as_bytes(&self) -> &[u8]
+ {
+ &self.buf
+ }
+
+ pub fn clear(&mut self)
+ {
+ self.buf.clear();
+ }
+
+ pub fn iter<VertexAttr: VertexAttrValue>(
+ &self,
+ vertex_label: VertexLabel,
+ ) -> Iter<'_, VertexAttr>
+ {
+ let vertex_attr_props = self
+ .vertex_attr_props
+ .iter()
+ .find(|vertex_attr_props| vertex_attr_props.label == vertex_label)
+ .unwrap();
+
+ assert_eq!(VertexAttr::ty(), vertex_attr_props.ty);
+
+ Iter {
+ buf: self,
+ vertex_attr_props,
+ curr_index: 0,
+ _pd: PhantomData,
+ }
+ }
+}
+
+pub struct Iter<'a, VertexAttr: VertexAttrValue>
+{
+ buf: &'a VertexBuffer,
+ vertex_attr_props: &'a VertexAttrProperties,
+ curr_index: usize,
+ _pd: PhantomData<VertexAttr>,
+}
+
+impl<'a, VertexAttr: VertexAttrValue> Iterator for Iter<'a, VertexAttr>
+{
+ type Item = &'a VertexAttr;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let start_offset =
+ (self.buf.vertex_size * self.curr_index) + self.vertex_attr_props.byte_offset;
+
+ let end_offset = start_offset + self.vertex_attr_props.layout.size();
+
+ let bytes = self.buf.buf.get(start_offset..end_offset)?;
+
+ self.curr_index += 1;
+
+ Some(VertexAttr::ref_from_bytes(bytes).unwrap())
+ }
+}
+
+pub trait NamedVertexAttrs
+{
+ fn vertex_attr_cnt(&self) -> usize;
+
+ fn vertex_attrs(&self)
+ -> impl Iterator<Item = (&VertexLabel, &[u8], VertexAttrType)>;
+}
+
+macro_rules! impl_named_vertex_attrs {
+ ($cnt: tt) => {
+ seq!(I in 0..$cnt {
+ impl<#(VertexAttr~I: VertexAttrValue,)*>
+ NamedVertexAttrs for (#(NamedVertexAttr<VertexAttr~I>,)*)
+ {
+ fn vertex_attr_cnt(&self) -> usize
+ {
+ $cnt
+ }
+
+ fn vertex_attrs(&self)
+ -> impl Iterator<Item = (&VertexLabel, &[u8], VertexAttrType)>
+ {
+ [#(
+ (&self.I.label, self.I.value.as_bytes(), VertexAttr~I::ty()),
+ )*].into_iter()
+ }
+ }
+ });
+ };
+}
+
+seq!(I in 0..16 {
+ impl_named_vertex_attrs!(I);
+});
diff --git a/engine/src/model.rs b/engine/src/model.rs
new file mode 100644
index 0000000..60a5615
--- /dev/null
+++ b/engine/src/model.rs
@@ -0,0 +1,192 @@
+use std::borrow::Cow;
+use std::collections::HashMap;
+
+use crate::asset::{Assets, Handle as AssetHandle};
+use crate::ecs::Component;
+use crate::material::asset::Map as MaterialAssetMap;
+use crate::material::Material;
+use crate::mesh::Mesh;
+
+pub mod asset;
+
+#[derive(Debug, Clone, Component)]
+#[non_exhaustive]
+pub struct Model
+{
+ pub spec_asset: AssetHandle<Spec>,
+}
+
+impl Model
+{
+ pub fn new(asset_handle: AssetHandle<Spec>) -> Self
+ {
+ Self { spec_asset: asset_handle }
+ }
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Spec
+{
+ pub mesh_asset: Option<AssetHandle<Mesh>>,
+ pub materials: Materials,
+ pub material_names: Vec<Cow<'static, str>>,
+}
+
+impl Spec
+{
+ pub fn builder() -> SpecBuilder
+ {
+ SpecBuilder::default()
+ }
+
+ pub fn find_first_material<'assets>(
+ &'assets self,
+ assets: &'assets Assets,
+ ) -> MaterialSearchResult<'assets>
+ {
+ let Some(material_name) = self.material_names.first() else {
+ return MaterialSearchResult::NoMaterials;
+ };
+
+ let material_asset = match &self.materials {
+ Materials::Maps(material_asset_map_assets) => material_asset_map_assets
+ .iter()
+ .find_map(|mat_asset_map_asset| {
+ let mat_asset_map = assets.get(mat_asset_map_asset)?;
+
+ mat_asset_map.assets.get(material_name)
+ }),
+ Materials::Direct(material_assets) => material_assets.get(material_name),
+ };
+
+ let Some(material_asset) = material_asset else {
+ return MaterialSearchResult::NotFound;
+ };
+
+ if assets.get(material_asset).is_none() {
+ tracing::trace!("Missing material asset");
+ return MaterialSearchResult::NotFound;
+ }
+
+ MaterialSearchResult::Found(material_asset)
+ }
+}
+
+#[derive(Debug, Default, Clone)]
+pub struct SpecBuilder
+{
+ mesh_asset: Option<AssetHandle<Mesh>>,
+ materials: Materials,
+ material_names: Vec<Cow<'static, str>>,
+}
+
+impl SpecBuilder
+{
+ pub fn mesh(mut self, asset: AssetHandle<Mesh>) -> Self
+ {
+ self.mesh_asset = Some(asset);
+
+ self
+ }
+
+ pub fn materials(mut self, materials: Materials) -> Self
+ {
+ self.materials = materials;
+
+ self
+ }
+
+ pub fn material_name(mut self, material_name: impl Into<Cow<'static, str>>) -> Self
+ {
+ self.material_names.push(material_name.into());
+
+ self
+ }
+
+ pub fn material_names<MaterialName>(
+ mut self,
+ material_names: impl IntoIterator<Item = MaterialName>,
+ ) -> Self
+ where
+ MaterialName: Into<Cow<'static, str>>,
+ {
+ self.material_names
+ .extend(material_names.into_iter().map(|mat_name| mat_name.into()));
+
+ self
+ }
+
+ #[tracing::instrument(skip_all)]
+ pub fn build(self) -> Spec
+ {
+ if !self.materials.is_empty() && self.material_names.is_empty() {
+ tracing::warn!("Model spec will have materials but no material names");
+ }
+
+ if self.materials.is_empty() && !self.material_names.is_empty() {
+ tracing::warn!("Model spec will have material names but no materials");
+ }
+
+ Spec {
+ mesh_asset: self.mesh_asset,
+ materials: self.materials,
+ material_names: self.material_names,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub enum Materials
+{
+ Direct(HashMap<Cow<'static, str>, AssetHandle<Material>>),
+ Maps(Vec<AssetHandle<MaterialAssetMap>>),
+}
+
+impl Materials
+{
+ pub fn direct<MaterialName>(
+ material_assets: impl IntoIterator<Item = (MaterialName, AssetHandle<Material>)>,
+ ) -> Self
+ where
+ MaterialName: Into<Cow<'static, str>>,
+ {
+ Self::Direct(
+ material_assets
+ .into_iter()
+ .map(|(material_name, mat_asset)| (material_name.into(), mat_asset))
+ .collect(),
+ )
+ }
+
+ pub fn is_empty(&self) -> bool
+ {
+ match self {
+ Self::Direct(material_assets) => material_assets.is_empty(),
+ Self::Maps(material_asset_map_assets) => material_asset_map_assets.is_empty(),
+ }
+ }
+
+ pub fn len(&self) -> usize
+ {
+ match self {
+ Self::Direct(material_assets) => material_assets.len(),
+ Self::Maps(material_asset_map_assets) => material_asset_map_assets.len(),
+ }
+ }
+}
+
+impl Default for Materials
+{
+ fn default() -> Self
+ {
+ Self::Maps(Vec::new())
+ }
+}
+
+pub enum MaterialSearchResult<'a>
+{
+ Found(&'a AssetHandle<Material>),
+ NotFound,
+ NoMaterials,
+}
diff --git a/engine/src/model/asset.rs b/engine/src/model/asset.rs
new file mode 100644
index 0000000..dd9e60b
--- /dev/null
+++ b/engine/src/model/asset.rs
@@ -0,0 +1,64 @@
+use std::fs::read_to_string;
+use std::path::{Path, PathBuf};
+
+use crate::asset::{Assets, Submitter as AssetSubmitter};
+use crate::material::asset::Map as MaterialAssetMap;
+use crate::model::{Materials, Spec};
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Settings {}
+
+pub fn add_importers(assets: &mut Assets)
+{
+ assets.set_importer(["obj"], import_wavefront_obj_asset);
+}
+
+fn import_wavefront_obj_asset(
+ asset_submitter: &mut AssetSubmitter<'_>,
+ path: &Path,
+ _settings: Option<&'_ Settings>,
+) -> Result<(), Error>
+{
+ let obj = crate::file_format::wavefront::obj::parse(
+ &read_to_string(path)
+ .map_err(|err| Error::ReadFailed(err, path.to_path_buf()))?,
+ )?;
+
+ let mesh = obj.to_mesh()?;
+
+ let mesh_asset = asset_submitter.submit_store_named("mesh", mesh);
+
+ let mut material_asset_map_assets = Vec::with_capacity(obj.mtl_libs.iter().count());
+
+ for mtl_lib_path in &obj.mtl_libs {
+ let mtl_lib_asset =
+ asset_submitter.submit_load_other::<MaterialAssetMap>(mtl_lib_path.as_path());
+
+ material_asset_map_assets.push(mtl_lib_asset);
+ }
+
+ asset_submitter.submit_store(
+ Spec::builder()
+ .mesh(mesh_asset)
+ .materials(Materials::Maps(material_asset_map_assets))
+ .material_names(
+ obj.unique_used_material_names
+ .into_iter()
+ .map(|material_name| material_name.into_string()),
+ )
+ .build(),
+ );
+
+ Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+enum Error
+{
+ #[error("Failed to read file {}", .1.display())]
+ ReadFailed(#[source] std::io::Error, PathBuf),
+
+ #[error(transparent)]
+ Other(#[from] crate::file_format::wavefront::obj::Error),
+}
diff --git a/engine/src/opengl/buffer.rs b/engine/src/opengl/buffer.rs
deleted file mode 100644
index eded553..0000000
--- a/engine/src/opengl/buffer.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-use std::marker::PhantomData;
-use std::mem::size_of_val;
-use std::ptr::null;
-
-#[derive(Debug)]
-pub struct Buffer<Item>
-{
- buf: gl::types::GLuint,
- _pd: PhantomData<Item>,
-}
-
-impl<Item> Buffer<Item>
-{
- pub fn new() -> Self
- {
- let mut buffer = gl::types::GLuint::default();
-
- unsafe {
- gl::CreateBuffers(1, &mut buffer);
- };
-
- Self { buf: buffer, _pd: PhantomData }
- }
-
- /// Stores items in the currently bound buffer.
- pub fn store(&mut self, items: &[Item], usage: Usage)
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::NamedBufferData(
- self.buf,
- size_of_val(items) as gl::types::GLsizeiptr,
- items.as_ptr().cast(),
- usage.into_gl(),
- );
- }
- }
-
- pub fn store_mapped<Value>(
- &mut self,
- values: &[Value],
- usage: Usage,
- mut map_func: impl FnMut(&Value) -> Item,
- )
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::NamedBufferData(
- self.buf,
- (size_of::<Item>() * values.len()) as gl::types::GLsizeiptr,
- null(),
- usage.into_gl(),
- );
- }
-
- for (index, value) in values.iter().enumerate() {
- let item = map_func(value);
-
- unsafe {
- gl::NamedBufferSubData(
- self.buf,
- (index * size_of::<Item>()) as gl::types::GLintptr,
- size_of::<Item>() as gl::types::GLsizeiptr,
- (&raw const item).cast(),
- );
- }
- }
- }
-
- pub fn object(&self) -> gl::types::GLuint
- {
- self.buf
- }
-}
-
-/// Buffer usage.
-#[derive(Debug)]
-#[allow(dead_code)]
-pub enum Usage
-{
- /// The buffer data is set only once and used by the GPU at most a few times.
- Stream,
-
- /// The buffer data is set only once and used many times.
- Static,
-
- /// The buffer data is changed a lot and used many times.
- Dynamic,
-}
-
-impl Usage
-{
- fn into_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Stream => gl::STREAM_DRAW,
- Self::Static => gl::STATIC_DRAW,
- Self::Dynamic => gl::DYNAMIC_DRAW,
- }
- }
-}
diff --git a/engine/src/opengl/debug.rs b/engine/src/opengl/debug.rs
deleted file mode 100644
index 203590a..0000000
--- a/engine/src/opengl/debug.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use std::ffi::c_void;
-use std::io::{stderr, Write};
-use std::panic::catch_unwind;
-use std::ptr::null_mut;
-use std::sync::Mutex;
-
-use crate::opengl::util::gl_enum;
-
-pub type MessageCallback = fn(
- source: MessageSource,
- ty: MessageType,
- id: u32,
- severity: MessageSeverity,
- message: &str,
-);
-
-pub fn enable_debug_output()
-{
- unsafe {
- gl::Enable(gl::DEBUG_OUTPUT);
- gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
- }
-}
-
-pub fn set_debug_message_callback(cb: MessageCallback)
-{
- *DEBUG_MESSAGE_CB.lock().unwrap() = Some(cb);
-
- unsafe {
- gl::DebugMessageCallback(Some(debug_message_cb), null_mut());
- }
-}
-
-pub fn set_debug_message_control(
- source: Option<MessageSource>,
- ty: Option<MessageType>,
- severity: Option<MessageSeverity>,
- ids: &[u32],
- ids_action: MessageIdsAction,
-)
-{
- // Ids shouldn't realistically be large enough to cause a panic here
- let ids_len: i32 = ids.len().try_into().unwrap();
-
- unsafe {
- gl::DebugMessageControl(
- source.map_or(gl::DONT_CARE, |source| source as u32),
- ty.map_or(gl::DONT_CARE, |ty| ty as u32),
- severity.map_or(gl::DONT_CARE, |severity| severity as u32),
- ids_len,
- ids.as_ptr(),
- ids_action as u8,
- );
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-#[allow(dead_code)]
-pub enum MessageIdsAction
-{
- Enable = 1,
- Disable = 0,
-}
-
-gl_enum! {
-pub enum MessageSource
-{
- Api = gl::DEBUG_SOURCE_API,
- WindowSystem = gl::DEBUG_SOURCE_WINDOW_SYSTEM,
- ShaderCompiler = gl::DEBUG_SOURCE_SHADER_COMPILER,
- ThirdParty = gl::DEBUG_SOURCE_THIRD_PARTY,
- Application = gl::DEBUG_SOURCE_APPLICATION,
- Other = gl::DEBUG_SOURCE_OTHER,
-}
-}
-
-gl_enum! {
-pub enum MessageType
-{
- DeprecatedBehavior = gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR,
- Error = gl::DEBUG_TYPE_ERROR,
- Marker = gl::DEBUG_TYPE_MARKER,
- Other = gl::DEBUG_TYPE_OTHER,
- Performance = gl::DEBUG_TYPE_PERFORMANCE,
- PopGroup = gl::DEBUG_TYPE_POP_GROUP,
- PushGroup = gl::DEBUG_TYPE_PUSH_GROUP,
- Portability = gl::DEBUG_TYPE_PORTABILITY,
- UndefinedBehavior = gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR,
-}
-}
-
-gl_enum! {
-pub enum MessageSeverity
-{
- High = gl::DEBUG_SEVERITY_HIGH,
- Medium = gl::DEBUG_SEVERITY_MEDIUM,
- Low = gl::DEBUG_SEVERITY_LOW,
- Notification = gl::DEBUG_SEVERITY_NOTIFICATION,
-}
-}
-
-static DEBUG_MESSAGE_CB: Mutex<Option<MessageCallback>> = Mutex::new(None);
-
-extern "system" fn debug_message_cb(
- source: gl::types::GLenum,
- ty: gl::types::GLenum,
- id: gl::types::GLuint,
- severity: gl::types::GLenum,
- message_length: gl::types::GLsizei,
- message: *const gl::types::GLchar,
- _user_param: *mut c_void,
-)
-{
- // Unwinds are catched because unwinding from Rust code into foreign code is UB.
- let res = catch_unwind(|| {
- let cb_lock = DEBUG_MESSAGE_CB.lock().unwrap();
-
- if let Some(cb) = *cb_lock {
- let msg_source = MessageSource::from_gl(source).unwrap();
- let msg_type = MessageType::from_gl(ty).unwrap();
- let msg_severity = MessageSeverity::from_gl(severity).unwrap();
-
- let msg_length = usize::try_from(message_length).unwrap();
-
- // SAFETY: The received message should be a valid ASCII string
- let message = unsafe {
- std::str::from_utf8_unchecked(std::slice::from_raw_parts(
- message.cast(),
- msg_length,
- ))
- };
-
- cb(msg_source, msg_type, id, msg_severity, message);
- }
- });
-
- if res.is_err() {
- // eprintln is not used since it can panic and unwinds are unwanted because
- // unwinding from Rust code into foreign code is UB.
- stderr()
- .write_all(b"ERROR: Panic in debug message callback")
- .ok();
- println!();
- }
-}
diff --git a/engine/src/opengl/glsl.rs b/engine/src/opengl/glsl.rs
deleted file mode 100644
index 6fd5638..0000000
--- a/engine/src/opengl/glsl.rs
+++ /dev/null
@@ -1,616 +0,0 @@
-use std::borrow::Cow;
-use std::fmt::{Display, Formatter};
-use std::path::{Path, PathBuf};
-use std::string::FromUtf8Error;
-
-const PREINCLUDE_DIRECTIVE: &str = "#preinclude";
-
-pub fn preprocess<'content>(
- shader_content: impl Into<Cow<'content, str>>,
- read_file: &impl Fn(&Path) -> Result<Vec<u8>, std::io::Error>,
-) -> Result<Cow<'content, str>, PreprocessingError>
-{
- do_preprocess(shader_content, SpanPath::Original, read_file)
-}
-
-fn do_preprocess<'content>(
- shader_content: impl Into<Cow<'content, str>>,
- shader_path: SpanPath<'_>,
- read_file: &impl Fn(&Path) -> Result<Vec<u8>, std::io::Error>,
-) -> Result<Cow<'content, str>, PreprocessingError>
-{
- let shader_content = shader_content.into();
-
- let mut preincludes = shader_content
- .match_indices(PREINCLUDE_DIRECTIVE)
- .peekable();
-
- if preincludes.peek().is_none() {
- // Shader content contains no preincludes
- return Ok(shader_content.into());
- };
-
- let mut preprocessed = shader_content.to_string();
-
- let mut curr = shader_content.find(PREINCLUDE_DIRECTIVE);
-
- let mut last_start = 0;
- let mut span_line_offset = 0;
-
- while let Some(preinclude_start) = curr {
- let replacement_job = handle_preinclude(
- &preprocessed,
- &shader_path,
- preinclude_start,
- span_line_offset,
- )?;
-
- let path = replacement_job.path.clone();
-
- let mut included =
- String::from_utf8(read_file(&replacement_job.path).map_err(|err| {
- PreprocessingError::ReadIncludedShaderFailed {
- source: err,
- path: replacement_job.path.clone(),
- }
- })?)
- .map_err(|err| {
- PreprocessingError::IncludedShaderInvalidUtf8 {
- source: err,
- path: path.clone(),
- }
- })?;
-
- if let Some(first_line) = included.lines().next() {
- if first_line.starts_with("#version") {
- included = included
- .chars()
- .skip_while(|character| *character != '\n')
- .collect();
- }
- }
-
- let included_preprocessed = do_preprocess(
- &included,
- SpanPath::Path(replacement_job.path.as_path().into()),
- read_file,
- )?;
-
- let start = replacement_job.start_index;
- let end = replacement_job.end_index;
-
- preprocessed.replace_range(start..end, &included_preprocessed);
-
- curr = preprocessed[last_start + 1..]
- .find(PREINCLUDE_DIRECTIVE)
- .map(|index| index + 1);
-
- last_start = preinclude_start + included_preprocessed.len();
-
- span_line_offset += included_preprocessed.lines().count();
- }
-
- Ok(preprocessed.into())
-}
-
-fn handle_preinclude(
- shader_content: &str,
- shader_path: &SpanPath<'_>,
- preinclude_start_index: usize,
- span_line_offset: usize,
-) -> Result<ReplacementJob, PreprocessingError>
-{
- let expect_token = |token: char, index: usize| {
- let token_found = shader_content.chars().nth(index).ok_or_else(|| {
- PreprocessingError::ExpectedToken {
- expected: token,
- span: Span::new(
- shader_content,
- shader_path.to_owned(),
- index,
- span_line_offset,
- preinclude_start_index,
- ),
- }
- })?;
-
- if token_found != token {
- return Err(PreprocessingError::InvalidToken {
- expected: token,
- found: token_found,
- span: Span::new(
- shader_content,
- shader_path.to_owned(),
- index,
- span_line_offset,
- preinclude_start_index,
- ),
- });
- }
-
- Ok(())
- };
-
- let space_index = preinclude_start_index + PREINCLUDE_DIRECTIVE.len();
- let quote_open_index = space_index + 1;
-
- expect_token(' ', space_index)?;
- expect_token('"', quote_open_index)?;
-
- let buf = shader_content[quote_open_index + 1..]
- .chars()
- .take_while(|character| *character != '"')
- .map(|character| character as u8)
- .collect::<Vec<_>>();
-
- if buf.is_empty() {
- return Err(PreprocessingError::ExpectedToken {
- expected: '"',
- span: Span::new(
- shader_content,
- shader_path.to_owned(),
- shader_content.len() - 1,
- span_line_offset,
- preinclude_start_index,
- ),
- });
- }
-
- let path_len = buf.len();
-
- let path = PathBuf::from(String::from_utf8(buf).map_err(|err| {
- PreprocessingError::PreincludePathInvalidUtf8 {
- source: err,
- span: Span::new(
- shader_content,
- shader_path.to_owned(),
- quote_open_index + 1,
- span_line_offset,
- preinclude_start_index,
- ),
- }
- })?);
-
- Ok(ReplacementJob {
- start_index: preinclude_start_index,
- end_index: quote_open_index + 1 + path_len + 1,
- path,
- })
-}
-
-struct ReplacementJob
-{
- start_index: usize,
- end_index: usize,
- path: PathBuf,
-}
-
-#[derive(Debug, thiserror::Error)]
-pub enum PreprocessingError
-{
- #[error(
- "Invalid token at line {}, column {} of {}. Expected '{}', found '{}'",
- span.line,
- span.column,
- span.path,
- expected,
- found
- )]
- InvalidToken
- {
- expected: char,
- found: char,
- span: Span,
- },
-
- #[error(
- "Expected token '{}' at line {}, column {} of {}. Found eof",
- expected,
- span.line,
- span.column,
- span.path
- )]
- ExpectedToken
- {
- expected: char, span: Span
- },
-
- #[error(
- "Preinclude path at line {}, column {} of {} is invalid UTF-8",
- span.line,
- span.column,
- span.path
- )]
- PreincludePathInvalidUtf8
- {
- #[source]
- source: FromUtf8Error,
- span: Span,
- },
-
- #[error("Failed to read included shader")]
- ReadIncludedShaderFailed
- {
- #[source]
- source: std::io::Error,
- path: PathBuf,
- },
-
- #[error("Included shader is not valid UTF-8")]
- IncludedShaderInvalidUtf8
- {
- #[source]
- source: FromUtf8Error,
- path: PathBuf,
- },
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-#[non_exhaustive]
-pub struct Span
-{
- pub line: usize,
- pub column: usize,
- pub path: SpanPath<'static>,
-}
-
-impl Span
-{
- fn new(
- file_content: &str,
- path: SpanPath<'static>,
- char_index: usize,
- line_offset: usize,
- line_start_index: usize,
- ) -> Self
- {
- let line = find_line_of_index(file_content, char_index) + 1
- - line_offset.saturating_sub(1);
-
- Self {
- line,
- column: char_index - line_start_index + 1,
- path,
- }
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum SpanPath<'a>
-{
- Original,
- Path(Cow<'a, Path>),
-}
-
-impl<'a> SpanPath<'a>
-{
- fn to_owned(&self) -> SpanPath<'static>
- {
- match self {
- Self::Original => SpanPath::Original,
- Self::Path(path) => SpanPath::Path(Cow::Owned(path.to_path_buf().into())),
- }
- }
-}
-
-impl<'a> Display for SpanPath<'a>
-{
- fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result
- {
- match self {
- Self::Original => write!(formatter, "original file"),
- Self::Path(path) => write!(formatter, "file {}", path.display()),
- }
- }
-}
-
-impl<'a, PathLike> PartialEq<PathLike> for SpanPath<'a>
-where
- PathLike: AsRef<Path>,
-{
- fn eq(&self, other: &PathLike) -> bool
- {
- match self {
- Self::Original => false,
- Self::Path(path) => path == other.as_ref(),
- }
- }
-}
-
-fn find_line_of_index(text: &str, index: usize) -> usize
-{
- text.chars()
- .take(index + 1)
- .enumerate()
- .filter(|(_, character)| *character == '\n')
- .count()
-}
-
-#[cfg(test)]
-mod tests
-{
- use std::ffi::OsStr;
- use std::path::Path;
-
- use super::{preprocess, PreprocessingError};
- use crate::opengl::glsl::SpanPath;
-
- #[test]
- fn preprocess_no_directives_is_same()
- {
- assert_eq!(
- preprocess("#version 330 core\n", &|_| { unreachable!() }).unwrap(),
- "#version 330 core\n"
- );
- }
-
- #[test]
- fn preprocess_with_directives_works()
- {
- assert_eq!(
- preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"foo.glsl\"\n",
- "\n",
- "void main() {}",
- ),
- &|_| { Ok(b"out vec4 FragColor;".to_vec()) }
- )
- .unwrap(),
- concat!(
- "#version 330 core\n",
- "\n",
- "out vec4 FragColor;\n",
- "\n",
- "void main() {}",
- )
- );
-
- assert_eq!(
- preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- ),
- &|_| { Ok(b"out vec4 FragColor;".to_vec()) }
- )
- .unwrap(),
- concat!(
- "#version 330 core\n",
- "\n",
- "out vec4 FragColor;\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- )
- );
-
- assert_eq!(
- preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "#preinclude \"foo.glsl\"\n",
- "\n",
- "void main() {}",
- ),
- &|path| {
- if path == OsStr::new("bar.glsl") {
- Ok(b"out vec4 FragColor;".to_vec())
- } else {
- Ok(concat!(
- "uniform sampler2D input_texture;\n",
- "in vec2 in_texture_coords;"
- )
- .as_bytes()
- .to_vec())
- }
- },
- )
- .unwrap(),
- concat!(
- "#version 330 core\n",
- "\n",
- "out vec4 FragColor;\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "uniform sampler2D input_texture;\n",
- "in vec2 in_texture_coords;\n",
- "\n",
- "void main() {}",
- )
- );
- }
-
- #[test]
- fn preprocess_invalid_directive_does_not_work()
- {
- let res = preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- // Missing "
- "#preinclude foo.glsl\"\n",
- "\n",
- "void main() {}",
- ),
- &|_| Ok(b"out vec4 FragColor;".to_vec()),
- );
-
- let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
- panic!(
- "Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
- );
- };
-
- assert_eq!(expected, '"');
- assert_eq!(found, 'f');
- assert_eq!(span.line, 3);
- assert_eq!(span.column, 13);
- assert_eq!(span.path, SpanPath::Original);
- }
-
- #[test]
- fn preprocess_error_has_correct_span()
- {
- let res = preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "#preinclude \"foo.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- ),
- &|path| {
- if path == OsStr::new("bar.glsl") {
- Ok(concat!(
- "out vec4 FragColor;\n",
- "in vec2 in_texture_coords;\n",
- "in float foo;"
- )
- .as_bytes()
- .to_vec())
- } else if path == OsStr::new("foo.glsl") {
- Ok(concat!(
- "uniform sampler2D input_texture;\n",
- "\n",
- // Missing space before first "
- "#preinclude\"shared_types.glsl\"\n",
- )
- .as_bytes()
- .to_vec())
- } else {
- panic!(concat!(
- "Expected read function to be called with ",
- "either path bar.glsl or foo.glsl"
- ));
- }
- },
- );
-
- let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
- panic!(
- "Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
- );
- };
-
- assert_eq!(expected, ' ');
- assert_eq!(found, '"');
- assert_eq!(span.line, 3);
- assert_eq!(span.column, 12);
- assert_eq!(span.path, SpanPath::Path(Path::new("foo.glsl").into()));
- }
-
- #[test]
- fn preprocess_included_shader_with_include_works()
- {
- assert_eq!(
- preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- ),
- &|path| {
- if path == OsStr::new("bar.glsl") {
- Ok(concat!(
- "#preinclude \"foo.glsl\"\n",
- "\n",
- "out vec4 FragColor;"
- )
- .as_bytes()
- .to_vec())
- } else {
- Ok(concat!(
- "uniform sampler2D input_texture;\n",
- "in vec2 in_texture_coords;"
- )
- .as_bytes()
- .to_vec())
- }
- }
- )
- .unwrap(),
- concat!(
- "#version 330 core\n",
- "\n",
- "uniform sampler2D input_texture;\n",
- "in vec2 in_texture_coords;\n",
- "\n",
- "out vec4 FragColor;\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- )
- );
- }
-
- #[test]
- fn preprocess_included_shader_with_include_error_span_is_correct()
- {
- let res = preprocess(
- concat!(
- "#version 330 core\n",
- "\n",
- "#preinclude \"bar.glsl\"\n",
- "\n",
- "in vec3 in_frag_color;\n",
- "\n",
- "void main() {}",
- ),
- &|path| {
- if path == OsStr::new("bar.glsl") {
- Ok(concat!(
- // ' instead of "
- "#preinclude 'foo.glsl\"\n",
- "\n",
- "out vec4 FragColor;"
- )
- .as_bytes()
- .to_vec())
- } else {
- Ok(concat!(
- "uniform sampler2D input_texture;\n",
- "in vec2 in_texture_coords;"
- )
- .as_bytes()
- .to_vec())
- }
- },
- );
-
- let Err(PreprocessingError::InvalidToken { expected, found, span }) = res else {
- panic!(
- "Expected result to be Err(Error::InvalidToken {{ ... }}), is {res:?}"
- );
- };
-
- assert_eq!(expected, '"');
- assert_eq!(found, '\'');
- assert_eq!(span.line, 1);
- assert_eq!(span.column, 13);
- assert_eq!(span.path, Path::new("bar.glsl"));
- }
-}
diff --git a/engine/src/opengl/mod.rs b/engine/src/opengl/mod.rs
deleted file mode 100644
index 53e0120..0000000
--- a/engine/src/opengl/mod.rs
+++ /dev/null
@@ -1,128 +0,0 @@
-use bitflags::bitflags;
-use gl::types::GLint;
-
-use crate::data_types::dimens::Dimens;
-use crate::vector::Vec2;
-
-pub mod buffer;
-pub mod glsl;
-pub mod shader;
-pub mod texture;
-pub mod vertex_array;
-
-mod util;
-
-pub mod debug;
-
-pub fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
-{
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::Viewport(
- position.x as i32,
- position.y as i32,
- size.width as i32,
- size.height as i32,
- );
- }
-}
-
-pub fn clear_buffers(mask: BufferClearMask)
-{
- unsafe {
- gl::Clear(mask.bits());
- }
-}
-
-pub fn set_polygon_mode(face: impl Into<PolygonModeFace>, mode: impl Into<PolygonMode>)
-{
- unsafe {
- gl::PolygonMode(face.into() as u32, mode.into() as u32);
- }
-}
-
-pub fn enable(capacity: Capability)
-{
- unsafe {
- gl::Enable(capacity as u32);
- }
-}
-
-pub fn get_context_flags() -> ContextFlags
-{
- let mut context_flags: GLint = 0;
-
- unsafe {
- gl::GetIntegerv(gl::CONTEXT_FLAGS as u32, &mut context_flags);
- }
-
- ContextFlags::from_bits_truncate(context_flags as u32)
-}
-
-bitflags! {
- #[derive(Debug, Clone, Copy)]
- pub struct BufferClearMask: u32 {
- const COLOR = gl::COLOR_BUFFER_BIT;
- const DEPTH = gl::DEPTH_BUFFER_BIT;
- const STENCIL = gl::STENCIL_BUFFER_BIT;
- }
-}
-
-#[derive(Debug)]
-#[repr(u32)]
-pub enum Capability
-{
- DepthTest = gl::DEPTH_TEST,
- MultiSample = gl::MULTISAMPLE,
-}
-
-#[derive(Debug)]
-#[repr(u32)]
-pub enum PolygonMode
-{
- Point = gl::POINT,
- Line = gl::LINE,
- Fill = gl::FILL,
-}
-
-impl From<crate::draw_flags::PolygonMode> for PolygonMode
-{
- fn from(mode: crate::draw_flags::PolygonMode) -> Self
- {
- match mode {
- crate::draw_flags::PolygonMode::Point => Self::Point,
- crate::draw_flags::PolygonMode::Fill => Self::Fill,
- crate::draw_flags::PolygonMode::Line => Self::Line,
- }
- }
-}
-
-#[derive(Debug)]
-#[repr(u32)]
-pub enum PolygonModeFace
-{
- Front = gl::FRONT,
- Back = gl::BACK,
- FrontAndBack = gl::FRONT_AND_BACK,
-}
-
-impl From<crate::draw_flags::PolygonModeFace> for PolygonModeFace
-{
- fn from(face: crate::draw_flags::PolygonModeFace) -> Self
- {
- match face {
- crate::draw_flags::PolygonModeFace::Front => Self::Front,
- crate::draw_flags::PolygonModeFace::Back => Self::Back,
- crate::draw_flags::PolygonModeFace::FrontAndBack => Self::FrontAndBack,
- }
- }
-}
-
-bitflags! {
-#[derive(Debug, Clone, Copy)]
-pub struct ContextFlags: u32 {
- const FORWARD_COMPATIBLE = gl::CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
- const DEBUG = gl::CONTEXT_FLAG_DEBUG_BIT;
- const ROBUST_ACCESS = gl::CONTEXT_FLAG_ROBUST_ACCESS_BIT;
-}
-}
diff --git a/engine/src/opengl/shader.rs b/engine/src/opengl/shader.rs
deleted file mode 100644
index 36dc1a4..0000000
--- a/engine/src/opengl/shader.rs
+++ /dev/null
@@ -1,247 +0,0 @@
-use std::ffi::CStr;
-use std::ptr::null_mut;
-
-use crate::matrix::Matrix;
-use crate::vector::Vec3;
-
-#[derive(Debug)]
-pub struct Shader
-{
- shader: gl::types::GLuint,
-}
-
-impl Shader
-{
- pub fn new(kind: Kind) -> Self
- {
- let shader = unsafe { gl::CreateShader(kind.into_gl()) };
-
- Self { shader }
- }
-
- pub fn set_source(&mut self, source: &str) -> Result<(), Error>
- {
- if !source.is_ascii() {
- return Err(Error::SourceNotAscii);
- }
-
- unsafe {
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl::ShaderSource(
- self.shader,
- 1,
- &source.as_ptr().cast(),
- &(source.len() as gl::types::GLint),
- );
- }
-
- Ok(())
- }
-
- pub fn compile(&mut self) -> Result<(), Error>
- {
- unsafe {
- gl::CompileShader(self.shader);
- }
-
- let mut compile_success = gl::types::GLint::default();
-
- unsafe {
- gl::GetShaderiv(self.shader, gl::COMPILE_STATUS, &mut compile_success);
- }
-
- if compile_success == 0 {
- let info_log = self.get_info_log();
-
- return Err(Error::CompileFailed(info_log));
- }
-
- Ok(())
- }
-
- fn get_info_log(&self) -> String
- {
- let mut buf = vec![gl::types::GLchar::default(); 512];
-
- unsafe {
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl::GetShaderInfoLog(
- self.shader,
- buf.len() as gl::types::GLsizei,
- null_mut(),
- buf.as_mut_ptr(),
- );
- }
-
- let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) };
-
- unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) }
- }
-}
-
-impl Drop for Shader
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteShader(self.shader);
- }
- }
-}
-
-/// Shader kind.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Kind
-{
- Vertex,
- Fragment,
-}
-
-impl Kind
-{
- fn into_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Vertex => gl::VERTEX_SHADER,
- Self::Fragment => gl::FRAGMENT_SHADER,
- }
- }
-}
-
-/// Shader program
-#[derive(Debug, PartialEq, Eq, Hash)]
-pub struct Program
-{
- program: gl::types::GLuint,
-}
-
-impl Program
-{
- pub fn new() -> Self
- {
- let program = unsafe { gl::CreateProgram() };
-
- Self { program }
- }
-
- pub fn attach(&mut self, shader: &Shader)
- {
- unsafe {
- gl::AttachShader(self.program, shader.shader);
- }
- }
-
- pub fn link(&mut self) -> Result<(), Error>
- {
- unsafe {
- gl::LinkProgram(self.program);
- }
-
- let mut link_success = gl::types::GLint::default();
-
- unsafe {
- gl::GetProgramiv(self.program, gl::LINK_STATUS, &mut link_success);
- }
-
- if link_success == 0 {
- let info_log = self.get_info_log();
-
- return Err(Error::CompileFailed(info_log));
- }
-
- Ok(())
- }
-
- pub fn activate(&self)
- {
- unsafe {
- gl::UseProgram(self.program);
- }
- }
-
- pub fn set_uniform_matrix_4fv(&mut self, name: &CStr, matrix: &Matrix<f32, 4, 4>)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniformMatrix4fv(
- self.program,
- uniform_location,
- 1,
- gl::FALSE,
- matrix.as_ptr(),
- );
- }
- }
-
- pub fn set_uniform_vec_3fv(&mut self, name: &CStr, vec: &Vec3<f32>)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniform3fv(self.program, uniform_location, 1, vec.as_ptr());
- }
- }
-
- pub fn set_uniform_1fv(&mut self, name: &CStr, num: f32)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniform1fv(self.program, uniform_location, 1, &num);
- }
- }
-
- pub fn set_uniform_1i(&mut self, name: &CStr, num: i32)
- {
- let uniform_location =
- unsafe { gl::GetUniformLocation(self.program, name.as_ptr().cast()) };
-
- unsafe {
- gl::ProgramUniform1i(self.program, uniform_location, num);
- }
- }
-
- fn get_info_log(&self) -> String
- {
- let mut buf = vec![gl::types::GLchar::default(); 512];
-
- unsafe {
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl::GetProgramInfoLog(
- self.program,
- buf.len() as gl::types::GLsizei,
- null_mut(),
- buf.as_mut_ptr(),
- );
- }
-
- let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) };
-
- unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) }
- }
-}
-
-impl Drop for Program
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteProgram(self.program);
- }
- }
-}
-
-/// Shader error.
-#[derive(Debug, thiserror::Error)]
-pub enum Error
-{
- #[error("All characters in source are not within the ASCII range")]
- SourceNotAscii,
-
- #[error("Failed to compile: {0}")]
- CompileFailed(String),
-}
diff --git a/engine/src/opengl/texture.rs b/engine/src/opengl/texture.rs
deleted file mode 100644
index 52c8554..0000000
--- a/engine/src/opengl/texture.rs
+++ /dev/null
@@ -1,240 +0,0 @@
-use crate::data_types::dimens::Dimens;
-use crate::texture::Properties;
-
-#[derive(Debug)]
-pub struct Texture
-{
- texture: gl::types::GLuint,
-}
-
-impl Texture
-{
- pub fn new() -> Self
- {
- let mut texture = gl::types::GLuint::default();
-
- unsafe {
- gl::CreateTextures(gl::TEXTURE_2D, 1, &mut texture);
- };
-
- Self { texture }
- }
-
- pub fn bind(&self)
- {
- unsafe {
- gl::BindTexture(gl::TEXTURE_2D, self.texture);
- }
- }
-
- pub fn generate(
- &mut self,
- dimens: Dimens<u32>,
- data: &[u8],
- pixel_data_format: PixelDataFormat,
- )
- {
- self.alloc_image(pixel_data_format, dimens, data);
-
- unsafe {
- gl::GenerateTextureMipmap(self.texture);
- }
- }
-
- pub fn apply_properties(&mut self, properties: &Properties)
- {
- self.set_wrap(properties.wrap);
- self.set_magnifying_filter(properties.magnifying_filter);
- self.set_minifying_filter(properties.minifying_filter);
- }
-
- pub fn set_wrap(&mut self, wrapping: Wrapping)
- {
- let wrapping_gl = wrapping.to_gl();
-
- #[allow(clippy::cast_possible_wrap)]
- unsafe {
- gl::TextureParameteri(self.texture, gl::TEXTURE_WRAP_S, wrapping_gl as i32);
- gl::TextureParameteri(self.texture, gl::TEXTURE_WRAP_T, wrapping_gl as i32);
- }
- }
-
- pub fn set_magnifying_filter(&mut self, filtering: Filtering)
- {
- let filtering_gl = filtering.to_gl();
-
- #[allow(clippy::cast_possible_wrap)]
- unsafe {
- gl::TextureParameteri(
- self.texture,
- gl::TEXTURE_MAG_FILTER,
- filtering_gl as i32,
- );
- }
- }
-
- pub fn set_minifying_filter(&mut self, filtering: Filtering)
- {
- let filtering_gl = filtering.to_gl();
-
- #[allow(clippy::cast_possible_wrap)]
- unsafe {
- gl::TextureParameteri(
- self.texture,
- gl::TEXTURE_MIN_FILTER,
- filtering_gl as i32,
- );
- }
- }
-
- fn alloc_image(
- &mut self,
- pixel_data_format: PixelDataFormat,
- dimens: Dimens<u32>,
- data: &[u8],
- )
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::TextureStorage2D(
- self.texture,
- 1,
- pixel_data_format.to_sized_internal_format(),
- dimens.width as i32,
- dimens.height as i32,
- );
-
- #[allow(clippy::cast_possible_wrap)]
- gl::TextureSubImage2D(
- self.texture,
- 0,
- 0,
- 0,
- dimens.width as i32,
- dimens.height as i32,
- pixel_data_format.to_format(),
- gl::UNSIGNED_BYTE,
- data.as_ptr().cast(),
- );
- }
- }
-}
-
-impl Drop for Texture
-{
- fn drop(&mut self)
- {
- unsafe {
- gl::DeleteTextures(1, &self.texture);
- }
- }
-}
-
-/// Texture wrapping.
-#[derive(Debug, Clone, Copy)]
-pub enum Wrapping
-{
- Repeat,
- MirroredRepeat,
- ClampToEdge,
- ClampToBorder,
-}
-
-impl Wrapping
-{
- fn to_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Repeat => gl::REPEAT,
- Self::MirroredRepeat => gl::MIRRORED_REPEAT,
- Self::ClampToEdge => gl::CLAMP_TO_EDGE,
- Self::ClampToBorder => gl::CLAMP_TO_BORDER,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-pub enum Filtering
-{
- Nearest,
- Linear,
-}
-
-impl Filtering
-{
- fn to_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Linear => gl::LINEAR,
- Self::Nearest => gl::NEAREST,
- }
- }
-}
-
-/// Texture pixel data format.
-#[derive(Debug, Clone, Copy)]
-pub enum PixelDataFormat
-{
- Rgb8,
- Rgba8,
-}
-
-impl PixelDataFormat
-{
- fn to_sized_internal_format(self) -> gl::types::GLenum
- {
- match self {
- Self::Rgb8 => gl::RGB8,
- Self::Rgba8 => gl::RGBA8,
- }
- }
-
- fn to_format(self) -> gl::types::GLenum
- {
- match self {
- Self::Rgb8 => gl::RGB,
- Self::Rgba8 => gl::RGBA,
- }
- }
-}
-
-pub fn set_active_texture_unit(texture_unit: TextureUnit)
-{
- unsafe {
- gl::ActiveTexture(texture_unit.into_gl());
- }
-}
-
-macro_rules! texture_unit_enum {
- (cnt=$cnt: literal) => {
- seq_macro::seq!(N in 0..$cnt {
- #[derive(Debug, Clone, Copy)]
- pub enum TextureUnit {
- #(
- No~N,
- )*
- }
-
- impl TextureUnit {
- fn into_gl(self) -> gl::types::GLenum {
- match self {
- #(
- Self::No~N => gl::TEXTURE~N,
- )*
- }
- }
-
- pub fn from_num(num: usize) -> Option<Self> {
- match num {
- #(
- N => Some(Self::No~N),
- )*
- _ => None
- }
- }
- }
- });
- };
-}
-
-texture_unit_enum!(cnt = 31);
diff --git a/engine/src/opengl/util.rs b/engine/src/opengl/util.rs
deleted file mode 100644
index e60778f..0000000
--- a/engine/src/opengl/util.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-// May only be used when certain crate features are enabled
-#![allow(unused_macros, unused_imports)]
-
-macro_rules! gl_enum {
- (
- $visibility: vis enum $name: ident
- {$(
- $variant: ident = gl::$gl_enum: ident,
- )+}
- ) => {
- #[derive(Debug, Clone, Copy)]
- #[repr(u32)]
- $visibility enum $name
- {$(
- $variant = gl::$gl_enum,
- )+}
-
- impl $name {
- fn from_gl(num: gl::types::GLenum) -> Option<Self>
- {
- match num {
- $(gl::$gl_enum => Some(Self::$variant),)+
- _ => None
- }
- }
- }
- };
-}
-
-pub(crate) use gl_enum;
diff --git a/engine/src/opengl/vertex_array.rs b/engine/src/opengl/vertex_array.rs
deleted file mode 100644
index 1f8a870..0000000
--- a/engine/src/opengl/vertex_array.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use std::mem::size_of;
-
-use crate::opengl::buffer::Buffer;
-
-#[derive(Debug)]
-pub struct VertexArray
-{
- array: gl::types::GLuint,
-}
-
-impl VertexArray
-{
- pub fn new() -> Self
- {
- let mut array = 0;
-
- unsafe {
- gl::CreateVertexArrays(1, &mut array);
- }
-
- Self { array }
- }
-
- /// Draws the currently bound vertex array.
- pub fn draw_arrays(primitive_kind: PrimitiveKind, start_index: u32, cnt: u32)
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::DrawArrays(
- primitive_kind.into_gl(),
- start_index as gl::types::GLint,
- cnt as gl::types::GLsizei,
- );
- }
- }
-
- /// Draws the currently bound vertex array.
- pub fn draw_elements(primitive_kind: PrimitiveKind, offset: u32, cnt: u32)
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::DrawElements(
- primitive_kind.into_gl(),
- cnt as gl::types::GLsizei,
- gl::UNSIGNED_INT,
- (offset as gl::types::GLint) as *const _,
- );
- }
- }
-
- pub fn bind_element_buffer(&mut self, element_buffer: &Buffer<u32>)
- {
- unsafe {
- gl::VertexArrayElementBuffer(self.array, element_buffer.object());
- }
- }
-
- pub fn bind_vertex_buffer<VertexT>(
- &mut self,
- binding_index: u32,
- vertex_buffer: &Buffer<VertexT>,
- offset: isize,
- )
- {
- unsafe {
- gl::VertexArrayVertexBuffer(
- self.array,
- binding_index,
- vertex_buffer.object(),
- offset,
- size_of::<VertexT>() as i32,
- );
- }
- }
-
- pub fn enable_attrib(&mut self, attrib_index: u32)
- {
- unsafe {
- gl::EnableVertexArrayAttrib(self.array, attrib_index as gl::types::GLuint);
- }
- }
-
- pub fn set_attrib_format(
- &mut self,
- attrib_index: u32,
- data_type: DataType,
- normalized: bool,
- offset: u32,
- )
- {
- unsafe {
- #[allow(clippy::cast_possible_wrap)]
- gl::VertexArrayAttribFormat(
- self.array,
- attrib_index,
- data_type.size() as gl::types::GLint,
- data_type as u32,
- if normalized { gl::TRUE } else { gl::FALSE },
- offset,
- );
- }
- }
-
- /// Associate a vertex attribute and a vertex buffer binding.
- pub fn set_attrib_vertex_buf_binding(
- &mut self,
- attrib_index: u32,
- vertex_buf_binding_index: u32,
- )
- {
- unsafe {
- gl::VertexArrayAttribBinding(
- self.array,
- attrib_index,
- vertex_buf_binding_index,
- );
- }
- }
-
- pub fn bind(&self)
- {
- unsafe { gl::BindVertexArray(self.array) }
- }
-}
-
-#[derive(Debug)]
-pub enum PrimitiveKind
-{
- Triangles,
-}
-
-impl PrimitiveKind
-{
- fn into_gl(self) -> gl::types::GLenum
- {
- match self {
- Self::Triangles => gl::TRIANGLES,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(u32)]
-pub enum DataType
-{
- Float = gl::FLOAT,
-}
-
-impl DataType
-{
- pub fn size(self) -> u32
- {
- #[allow(clippy::cast_possible_truncation)]
- match self {
- Self::Float => size_of::<gl::types::GLfloat>() as u32,
- }
- }
-}
diff --git a/engine/src/projection.rs b/engine/src/projection.rs
index faa741f..2191a93 100644
--- a/engine/src/projection.rs
+++ b/engine/src/projection.rs
@@ -1,9 +1,11 @@
-use crate::data_types::dimens::Dimens3;
-use crate::matrix::Matrix;
-use crate::util::builder;
-use crate::vector::Vec3;
-
-#[derive(Debug)]
+use crate::builder;
+use crate::data_types::dimens::Dimens;
+use crate::matrix::{CellPos as MatrixCellPos, Matrix};
+use crate::reflection::Reflection;
+use crate::vector::Vec2;
+use crate::windowing::dpi::PhysicalSize;
+
+#[derive(Debug, Clone, Reflection)]
#[non_exhaustive]
pub enum Projection
{
@@ -11,11 +13,32 @@ pub enum Projection
Orthographic(Orthographic),
}
+impl Projection
+{
+ pub fn to_matrix_rh(
+ &self,
+ window_size: PhysicalSize<u32>,
+ clip_volume: ClipVolume,
+ ) -> Matrix<f32, 4, 4>
+ {
+ match self {
+ Projection::Perspective(perspective_proj) => perspective_proj.to_matrix_rh(
+ window_size.width as f32 / window_size.height as f32,
+ clip_volume,
+ ),
+ Projection::Orthographic(orthographic_proj) => {
+ orthographic_proj.to_matrix_rh(window_size, clip_volume)
+ }
+ }
+ }
+}
+
/// Perspective projection parameters.
-#[derive(Debug)]
+#[derive(Debug, Clone, Reflection)]
pub struct Perspective
{
- pub fov_radians: f32,
+ /// Field-of-view in degrees
+ pub fov_degs: f32,
pub far: f32,
pub near: f32,
}
@@ -29,13 +52,18 @@ impl Perspective
{
let mut out = Matrix::new();
+ let fov_rads = self.fov_degs.to_radians();
+
match clip_volume {
ClipVolume::NegOneToOne => {
- out.set_cell(0, 0, (1.0 / (self.fov_radians / 2.0).tan()) / aspect);
- out.set_cell(1, 1, 1.0 / (self.fov_radians / 2.0).tan());
- out.set_cell(2, 2, (self.near + self.far) / (self.near - self.far));
- out.set_cell(2, 3, (2.0 * self.near * self.far) / (self.near - self.far));
- out.set_cell(3, 2, -1.0);
+ out[MatrixCellPos { row: 0, col: 0 }] =
+ (1.0 / (fov_rads / 2.0).tan()) / aspect;
+ out[MatrixCellPos { row: 1, col: 1 }] = 1.0 / (fov_rads / 2.0).tan();
+ out[MatrixCellPos { row: 2, col: 2 }] =
+ (self.near + self.far) / (self.near - self.far);
+ out[MatrixCellPos { row: 2, col: 3 }] =
+ (2.0 * self.near * self.far) / (self.near - self.far);
+ out[MatrixCellPos { row: 3, col: 2 }] = -1.0;
}
}
@@ -48,20 +76,32 @@ impl Default for Perspective
fn default() -> Self
{
Self {
- fov_radians: 80.0f32.to_radians(),
+ fov_degs: 80.0,
far: 100.0,
near: 0.1,
}
}
}
+#[derive(Debug, Default, Clone, Reflection)]
+pub enum OrthographicSize
+{
+ FixedSize(Dimens<f32>),
+
+ #[default]
+ WindowSize,
+}
+
builder! {
#[builder(name = OrthographicBuilder, derives=(Debug, Clone))]
-#[derive(Debug, Clone, PartialEq, PartialOrd)]
+#[derive(Debug, Clone, Reflection)]
#[non_exhaustive]
pub struct Orthographic
{
- pub size: Dimens3<f32>,
+ pub near: f32,
+ pub far: f32,
+ pub viewport_origin: Vec2<f32>,
+ pub size: OrthographicSize,
}
}
@@ -75,28 +115,43 @@ impl Orthographic
/// Creates a orthographic projection matrix using right-handed coordinates.
pub fn to_matrix_rh(
&self,
- center_pos: &Vec3<f32>,
+ window_size: PhysicalSize<u32>,
clip_volume: ClipVolume,
) -> Matrix<f32, 4, 4>
{
let mut result = Matrix::<f32, 4, 4>::new();
- let left = center_pos.x - (self.size.width / 2.0);
- let right = center_pos.x + (self.size.width / 2.0);
- let bottom = center_pos.y - (self.size.height / 2.0);
- let top = center_pos.y + (self.size.height / 2.0);
- let near = center_pos.z - (self.size.depth / 2.0);
- let far = center_pos.z + (self.size.depth / 2.0);
+ let size = match self.size {
+ OrthographicSize::FixedSize(fixed_size) => fixed_size,
+ OrthographicSize::WindowSize => Dimens {
+ width: window_size.width as f32,
+ height: window_size.height as f32,
+ },
+ };
+
+ let origin_x = size.width * self.viewport_origin.x;
+ let origin_y = size.height * self.viewport_origin.y;
+
+ let left = -origin_x;
+ let right = size.width - origin_x;
+
+ let bottom = -origin_y;
+ let top = size.height - origin_y;
+
+ let near = self.near;
+ let far = self.far;
match clip_volume {
ClipVolume::NegOneToOne => {
- result.set_cell(0, 0, 2.0 / (right - left));
- result.set_cell(1, 1, 2.0 / (top - bottom));
- result.set_cell(2, 2, -2.0 / (far - near));
- result.set_cell(0, 3, -(right + left) / (right - left));
- result.set_cell(1, 3, -(top + bottom) / (top - bottom));
- result.set_cell(2, 3, -(far + near) / (far - near));
- result.set_cell(3, 3, 1.0);
+ result[MatrixCellPos { row: 0, col: 0 }] = 2.0 / (right - left);
+ result[MatrixCellPos { row: 1, col: 1 }] = 2.0 / (top - bottom);
+ result[MatrixCellPos { row: 2, col: 2 }] = -2.0 / (far - near);
+ result[MatrixCellPos { row: 0, col: 3 }] =
+ -(right + left) / (right - left);
+ result[MatrixCellPos { row: 1, col: 3 }] =
+ -(top + bottom) / (top - bottom);
+ result[MatrixCellPos { row: 2, col: 3 }] = -(far + near) / (far - near);
+ result[MatrixCellPos { row: 3, col: 3 }] = 1.0;
}
}
@@ -108,13 +163,7 @@ impl Default for Orthographic
{
fn default() -> Self
{
- Self {
- size: Dimens3 {
- width: 10.0,
- height: 7.0,
- depth: 10.0,
- },
- }
+ Self::builder().build()
}
}
@@ -122,9 +171,12 @@ impl Default for OrthographicBuilder
{
fn default() -> Self
{
- let orthographic = Orthographic::default();
-
- OrthographicBuilder { size: orthographic.size }
+ Self {
+ near: 0.0,
+ far: 1000.0,
+ viewport_origin: Vec2 { x: 0.5, y: 0.5 },
+ size: OrthographicSize::WindowSize,
+ }
}
}
diff --git a/engine/src/reflection.rs b/engine/src/reflection.rs
new file mode 100644
index 0000000..fd7e379
--- /dev/null
+++ b/engine/src/reflection.rs
@@ -0,0 +1,2 @@
+pub use engine_macros::Reflection;
+pub use engine_reflection::*;
diff --git a/engine/src/renderer.rs b/engine/src/renderer.rs
deleted file mode 100644
index 17bc925..0000000
--- a/engine/src/renderer.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-use ecs::pair::{ChildOf, Pair};
-use ecs::phase::{Phase, UPDATE as UPDATE_PHASE};
-use ecs::static_entity;
-
-pub mod opengl;
-
-static_entity!(pub RENDER_PHASE, (Phase, Pair::new::<ChildOf>(*UPDATE_PHASE)));
diff --git a/engine/src/renderer/opengl.rs b/engine/src/renderer/opengl.rs
deleted file mode 100644
index 43ec16c..0000000
--- a/engine/src/renderer/opengl.rs
+++ /dev/null
@@ -1,751 +0,0 @@
-//! OpenGL renderer.
-
-use std::collections::HashMap;
-use std::ffi::{c_void, CString};
-use std::io::{Error as IoError, ErrorKind as IoErrorKind};
-use std::ops::Deref;
-use std::path::Path;
-use std::process::abort;
-
-use ecs::actions::Actions;
-use ecs::component::local::Local;
-use ecs::phase::START as START_PHASE;
-use ecs::query::term::Without;
-use ecs::sole::Single;
-use ecs::system::{Into as _, System};
-use ecs::{Component, Query};
-
-use crate::camera::{Active as ActiveCamera, Camera};
-use crate::color::Color;
-use crate::data_types::dimens::Dimens;
-use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
-use crate::lighting::{DirectionalLight, GlobalLight, PointLight};
-use crate::material::{Flags as MaterialFlags, Material};
-use crate::matrix::Matrix;
-use crate::mesh::Mesh;
-use crate::opengl::buffer::{Buffer, Usage as BufferUsage};
-use crate::opengl::debug::{
- enable_debug_output,
- set_debug_message_callback,
- set_debug_message_control,
- MessageIdsAction,
- MessageSeverity,
- MessageSource,
- MessageType,
-};
-use crate::opengl::glsl::{
- preprocess as glsl_preprocess,
- PreprocessingError as GlslPreprocessingError,
-};
-use crate::opengl::shader::{
- Error as GlShaderError,
- Kind as ShaderKind,
- Program as GlShaderProgram,
- Shader as GlShader,
-};
-use crate::opengl::texture::{
- set_active_texture_unit,
- Texture as GlTexture,
- TextureUnit,
-};
-use crate::opengl::vertex_array::{
- DataType as VertexArrayDataType,
- PrimitiveKind,
- VertexArray,
-};
-use crate::opengl::{
- clear_buffers,
- enable,
- get_context_flags as get_opengl_context_flags,
- BufferClearMask,
- Capability,
- ContextFlags,
-};
-use crate::projection::{ClipVolume, Projection};
-use crate::renderer::opengl::vertex::{AttributeComponentType, Vertex};
-use crate::renderer::RENDER_PHASE;
-use crate::texture::{Id as TextureId, Texture};
-use crate::transform::{Position, Scale};
-use crate::util::{defer, Defer, RefOrValue};
-use crate::vector::{Vec2, Vec3};
-use crate::window::Window;
-
-mod vertex;
-
-type RenderableEntity<'a> = (
- &'a Mesh,
- &'a Material,
- Option<&'a MaterialFlags>,
- Option<&'a Position>,
- Option<&'a Scale>,
- Option<&'a DrawFlags>,
- Option<&'a GlObjects>,
-);
-
-#[derive(Debug, Default)]
-#[non_exhaustive]
-pub struct Extension {}
-
-impl ecs::extension::Extension for Extension
-{
- fn collect(self, mut collector: ecs::extension::Collector<'_>)
- {
- collector.add_system(*START_PHASE, initialize);
-
- collector.add_system(
- *RENDER_PHASE,
- render
- .into_system()
- .initialize((GlobalGlObjects::default(),)),
- );
- }
-}
-
-fn initialize(window: Single<Window>)
-{
- window
- .make_context_current()
- .expect("Failed to make window context current");
-
- gl::load_with(|symbol| match window.get_proc_address(symbol) {
- Ok(addr) => addr as *const c_void,
- Err(err) => {
- println!(
- "FATAL ERROR: Failed to get adress of OpenGL function {symbol}: {err}",
- );
-
- abort();
- }
- });
-
- if get_opengl_context_flags().contains(ContextFlags::DEBUG) {
- initialize_debug();
- }
-
- let window_size = window.size().expect("Failed to get window size");
-
- set_viewport(Vec2 { x: 0, y: 0 }, window_size);
-
- window.set_framebuffer_size_callback(|new_window_size| {
- set_viewport(Vec2::ZERO, new_window_size);
- });
-
- enable(Capability::DepthTest);
- enable(Capability::MultiSample);
-}
-
-#[allow(clippy::too_many_arguments)]
-fn render(
- query: Query<RenderableEntity<'_>, (Without<NoDraw>,)>,
- point_light_query: Query<(&PointLight,)>,
- directional_lights: Query<(&DirectionalLight,)>,
- camera_query: Query<(&Camera, &Position, &ActiveCamera)>,
- window: Single<Window>,
- global_light: Single<GlobalLight>,
- mut gl_objects: Local<GlobalGlObjects>,
- mut actions: Actions,
-)
-{
- let Some((camera, camera_pos, _)) = camera_query.iter().next() else {
- tracing::warn!("No current camera. Nothing will be rendered");
- return;
- };
-
- let point_lights = point_light_query
- .iter()
- .map(|(point_light,)| point_light)
- .collect::<Vec<_>>();
-
- let directional_lights = directional_lights.iter().collect::<Vec<_>>();
-
- let GlobalGlObjects {
- shader_program,
- textures: gl_textures,
- } = &mut *gl_objects;
-
- let shader_program =
- shader_program.get_or_insert_with(|| create_default_shader_program().unwrap());
-
- clear_buffers(BufferClearMask::COLOR | BufferClearMask::DEPTH);
-
- for (
- euid,
- (mesh, material, material_flags, position, scale, draw_flags, gl_objects),
- ) in query.iter_with_euids()
- {
- let material_flags = material_flags
- .map(|material_flags| material_flags.clone())
- .unwrap_or_default();
-
- let gl_objs = match gl_objects.as_deref() {
- Some(gl_objs) => RefOrValue::Ref(gl_objs),
- None => RefOrValue::Value(Some(GlObjects::new(&mesh))),
- };
-
- defer!(|gl_objs| {
- if let RefOrValue::Value(opt_gl_objs) = gl_objs {
- actions.add_components(euid, (opt_gl_objs.take().unwrap(),));
- };
- });
-
- apply_transformation_matrices(
- Transformation {
- position: position.map(|pos| *pos).unwrap_or_default().position,
- scale: scale.map(|scale| *scale).unwrap_or_default().scale,
- },
- shader_program,
- &camera,
- &camera_pos,
- window.size().expect("Failed to get window size"),
- );
-
- apply_light(
- &material,
- &material_flags,
- &global_light,
- shader_program,
- point_lights.as_slice(),
- directional_lights
- .iter()
- .map(|(dir_light,)| &**dir_light)
- .collect::<Vec<_>>()
- .as_slice(),
- &camera_pos,
- );
-
- for (index, texture) in material.textures.iter().enumerate() {
- let gl_texture = gl_textures
- .entry(texture.id())
- .or_insert_with(|| create_gl_texture(texture));
-
- let texture_unit = TextureUnit::from_num(index).expect("Too many textures");
-
- set_active_texture_unit(texture_unit);
-
- gl_texture.bind();
- }
-
- shader_program.activate();
-
- if let Some(draw_flags) = &draw_flags {
- crate::opengl::set_polygon_mode(
- draw_flags.polygon_mode_config.face,
- draw_flags.polygon_mode_config.mode,
- );
- }
-
- draw_mesh(gl_objs.get().unwrap());
-
- if draw_flags.is_some() {
- let default_polygon_mode_config = PolygonModeConfig::default();
-
- crate::opengl::set_polygon_mode(
- default_polygon_mode_config.face,
- default_polygon_mode_config.mode,
- );
- }
- }
-}
-
-#[derive(Debug, Default, Component)]
-struct GlobalGlObjects
-{
- shader_program: Option<GlShaderProgram>,
- textures: HashMap<TextureId, GlTexture>,
-}
-
-fn set_viewport(position: Vec2<u32>, size: Dimens<u32>)
-{
- crate::opengl::set_viewport(position, size);
-}
-
-fn initialize_debug()
-{
- enable_debug_output();
-
- set_debug_message_callback(opengl_debug_message_cb);
- set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable);
-}
-
-fn draw_mesh(gl_objects: &GlObjects)
-{
- gl_objects.vertex_arr.bind();
-
- if gl_objects.index_buffer.is_some() {
- VertexArray::draw_elements(PrimitiveKind::Triangles, 0, gl_objects.element_cnt);
- } else {
- VertexArray::draw_arrays(PrimitiveKind::Triangles, 0, gl_objects.element_cnt);
- }
-}
-
-fn create_gl_texture(texture: &Texture) -> GlTexture
-{
- let mut gl_texture = GlTexture::new();
-
- gl_texture.generate(
- *texture.dimensions(),
- texture.image().as_bytes(),
- texture.pixel_data_format(),
- );
-
- gl_texture.apply_properties(texture.properties());
-
- gl_texture
-}
-
-const VERTEX_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex.glsl");
-const FRAGMENT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/fragment.glsl");
-
-const VERTEX_DATA_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/vertex_data.glsl");
-const LIGHT_GLSL_SHADER_SRC: &str = include_str!("opengl/glsl/light.glsl");
-
-fn create_default_shader_program() -> Result<GlShaderProgram, CreateShaderError>
-{
- let mut vertex_shader = GlShader::new(ShaderKind::Vertex);
-
- vertex_shader.set_source(&*glsl_preprocess(
- VERTEX_GLSL_SHADER_SRC,
- &get_glsl_shader_content,
- )?)?;
-
- vertex_shader.compile()?;
-
- let mut fragment_shader = GlShader::new(ShaderKind::Fragment);
-
- fragment_shader.set_source(&*glsl_preprocess(
- FRAGMENT_GLSL_SHADER_SRC,
- &get_glsl_shader_content,
- )?)?;
-
- fragment_shader.compile()?;
-
- let mut gl_shader_program = GlShaderProgram::new();
-
- gl_shader_program.attach(&vertex_shader);
- gl_shader_program.attach(&fragment_shader);
-
- gl_shader_program.link()?;
-
- Ok(gl_shader_program)
-}
-
-#[derive(Debug, thiserror::Error)]
-enum CreateShaderError
-{
- #[error(transparent)]
- ShaderError(#[from] GlShaderError),
-
- #[error(transparent)]
- PreprocessingError(#[from] GlslPreprocessingError),
-}
-
-fn get_glsl_shader_content(path: &Path) -> Result<Vec<u8>, std::io::Error>
-{
- if path == Path::new("vertex_data.glsl") {
- return Ok(VERTEX_DATA_GLSL_SHADER_SRC.as_bytes().to_vec());
- }
-
- if path == Path::new("light.glsl") {
- return Ok(LIGHT_GLSL_SHADER_SRC.as_bytes().to_vec());
- }
-
- Err(IoError::new(
- IoErrorKind::NotFound,
- format!("Content for shader file {} not found", path.display()),
- ))
-}
-
-#[derive(Debug, Component)]
-struct GlObjects
-{
- /// Vertex and index buffer has to live as long as the vertex array
- _vertex_buffer: Buffer<Vertex>,
- index_buffer: Option<Buffer<u32>>,
- element_cnt: u32,
-
- vertex_arr: VertexArray,
-}
-
-impl GlObjects
-{
- #[tracing::instrument(skip_all)]
- fn new(mesh: &Mesh) -> Self
- {
- tracing::trace!(
- "Creating vertex array, vertex buffer{}",
- if mesh.indices().is_some() {
- " and index buffer"
- } else {
- ""
- }
- );
-
- let mut vertex_arr = VertexArray::new();
- let mut vertex_buffer = Buffer::new();
-
- vertex_buffer.store_mapped(mesh.vertices(), BufferUsage::Static, |vertex| {
- Vertex {
- pos: vertex.pos,
- texture_coords: vertex.texture_coords,
- normal: vertex.normal,
- }
- });
-
- vertex_arr.bind_vertex_buffer(0, &vertex_buffer, 0);
-
- let mut offset = 0u32;
-
- for attrib in Vertex::attrs() {
- vertex_arr.enable_attrib(attrib.index);
-
- vertex_arr.set_attrib_format(
- attrib.index,
- match attrib.component_type {
- AttributeComponentType::Float => VertexArrayDataType::Float,
- },
- false,
- offset,
- );
-
- vertex_arr.set_attrib_vertex_buf_binding(attrib.index, 0);
-
- offset += attrib.component_size * attrib.component_cnt as u32;
- }
-
- if let Some(indices) = mesh.indices() {
- let mut index_buffer = Buffer::new();
-
- index_buffer.store(indices, BufferUsage::Static);
-
- vertex_arr.bind_element_buffer(&index_buffer);
-
- return Self {
- _vertex_buffer: vertex_buffer,
- index_buffer: Some(index_buffer),
- element_cnt: indices
- .len()
- .try_into()
- .expect("Mesh index count does not fit into a 32-bit unsigned int"),
- vertex_arr,
- };
- }
-
- Self {
- _vertex_buffer: vertex_buffer,
- index_buffer: None,
- element_cnt: mesh
- .vertices()
- .len()
- .try_into()
- .expect("Mesh vertex count does not fit into a 32-bit unsigned int"),
- vertex_arr,
- }
- }
-}
-
-fn apply_transformation_matrices(
- transformation: Transformation,
- gl_shader_program: &mut GlShaderProgram,
- camera: &Camera,
- camera_pos: &Position,
- window_size: Dimens<u32>,
-)
-{
- gl_shader_program
- .set_uniform_matrix_4fv(c"model", &create_transformation_matrix(transformation));
-
- let view_matrix = create_view_matrix(camera, &camera_pos.position);
-
- gl_shader_program.set_uniform_matrix_4fv(c"view", &view_matrix);
-
- #[allow(clippy::cast_precision_loss)]
- let proj_matrix = match &camera.projection {
- Projection::Perspective(perspective_proj) => perspective_proj.to_matrix_rh(
- window_size.width as f32 / window_size.height as f32,
- ClipVolume::NegOneToOne,
- ),
- Projection::Orthographic(orthographic_proj) => {
- orthographic_proj.to_matrix_rh(&camera_pos.position, ClipVolume::NegOneToOne)
- }
- };
-
- gl_shader_program.set_uniform_matrix_4fv(c"projection", &proj_matrix);
-}
-
-fn apply_light<PointLightHolder>(
- material: &Material,
- material_flags: &MaterialFlags,
- global_light: &GlobalLight,
- gl_shader_program: &mut GlShaderProgram,
- point_lights: &[PointLightHolder],
- directional_lights: &[&DirectionalLight],
- camera_pos: &Position,
-) where
- PointLightHolder: Deref<Target = PointLight>,
-{
- debug_assert!(
- point_lights.len() < 64,
- "Shader cannot handle more than 64 point lights"
- );
-
- debug_assert!(
- directional_lights.len() < 64,
- "Shader cannot handle more than 64 directional lights"
- );
-
- for (dir_light_index, dir_light) in directional_lights.iter().enumerate() {
- gl_shader_program.set_uniform_vec_3fv(
- &create_light_uniform_name(
- "directional_lights",
- dir_light_index,
- "direction",
- ),
- &dir_light.direction,
- );
-
- set_light_phong_uniforms(
- gl_shader_program,
- "directional_lights",
- dir_light_index,
- *dir_light,
- );
- }
-
- // There probably won't be more than 2147483648 directional lights
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl_shader_program
- .set_uniform_1i(c"directional_light_cnt", directional_lights.len() as i32);
-
- for (point_light_index, point_light) in point_lights.iter().enumerate() {
- gl_shader_program.set_uniform_vec_3fv(
- &create_light_uniform_name("point_lights", point_light_index, "position"),
- &point_light.position,
- );
-
- set_light_phong_uniforms(
- gl_shader_program,
- "point_lights",
- point_light_index,
- &**point_light,
- );
-
- set_light_attenuation_uniforms(
- gl_shader_program,
- "point_lights",
- point_light_index,
- point_light,
- );
- }
-
- // There probably won't be more than 2147483648 point lights
- #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
- gl_shader_program.set_uniform_1i(c"point_light_cnt", point_lights.len() as i32);
-
- gl_shader_program.set_uniform_vec_3fv(
- c"material.ambient",
- &if material_flags.use_ambient_color {
- material.ambient.clone()
- } else {
- global_light.ambient.clone()
- }
- .into(),
- );
-
- gl_shader_program
- .set_uniform_vec_3fv(c"material.diffuse", &material.diffuse.clone().into());
-
- #[allow(clippy::cast_possible_wrap)]
- gl_shader_program
- .set_uniform_vec_3fv(c"material.specular", &material.specular.clone().into());
-
- let texture_map = material
- .textures
- .iter()
- .enumerate()
- .map(|(index, texture)| (texture.id(), index))
- .collect::<HashMap<_, _>>();
-
- #[allow(clippy::cast_possible_wrap)]
- gl_shader_program.set_uniform_1i(
- c"material.ambient_map",
- *texture_map.get(&material.ambient_map).unwrap() as i32,
- );
-
- #[allow(clippy::cast_possible_wrap)]
- gl_shader_program.set_uniform_1i(
- c"material.diffuse_map",
- *texture_map.get(&material.diffuse_map).unwrap() as i32,
- );
-
- #[allow(clippy::cast_possible_wrap)]
- gl_shader_program.set_uniform_1i(
- c"material.specular_map",
- *texture_map.get(&material.specular_map).unwrap() as i32,
- );
-
- gl_shader_program.set_uniform_1fv(c"material.shininess", material.shininess);
-
- gl_shader_program.set_uniform_vec_3fv(c"view_pos", &camera_pos.position);
-}
-
-fn set_light_attenuation_uniforms(
- gl_shader_program: &mut GlShaderProgram,
- light_array: &str,
- light_index: usize,
- light: &PointLight,
-)
-{
- gl_shader_program.set_uniform_1fv(
- &create_light_uniform_name(
- light_array,
- light_index,
- "attenuation_props.constant",
- ),
- light.attenuation_params.constant,
- );
-
- gl_shader_program.set_uniform_1fv(
- &create_light_uniform_name(light_array, light_index, "attenuation_props.linear"),
- light.attenuation_params.linear,
- );
-
- gl_shader_program.set_uniform_1fv(
- &create_light_uniform_name(
- light_array,
- light_index,
- "attenuation_props.quadratic",
- ),
- light.attenuation_params.quadratic,
- );
-}
-
-fn set_light_phong_uniforms(
- gl_shader_program: &mut GlShaderProgram,
- light_array: &str,
- light_index: usize,
- light: &impl Light,
-)
-{
- gl_shader_program.set_uniform_vec_3fv(
- &create_light_uniform_name(light_array, light_index, "phong.diffuse"),
- &light.diffuse().clone().into(),
- );
-
- gl_shader_program.set_uniform_vec_3fv(
- &create_light_uniform_name(light_array, light_index, "phong.specular"),
- &light.specular().clone().into(),
- );
-}
-
-trait Light
-{
- fn diffuse(&self) -> &Color<f32>;
- fn specular(&self) -> &Color<f32>;
-}
-
-impl Light for PointLight
-{
- fn diffuse(&self) -> &Color<f32>
- {
- &self.diffuse
- }
-
- fn specular(&self) -> &Color<f32>
- {
- &self.specular
- }
-}
-
-impl Light for DirectionalLight
-{
- fn diffuse(&self) -> &Color<f32>
- {
- &self.diffuse
- }
-
- fn specular(&self) -> &Color<f32>
- {
- &self.specular
- }
-}
-
-fn create_light_uniform_name(
- light_array: &str,
- light_index: usize,
- light_field: &str,
-) -> CString
-{
- unsafe {
- CString::from_vec_with_nul_unchecked(
- format!("{light_array}[{light_index}].{light_field}\0").into(),
- )
- }
-}
-
-fn create_view_matrix(camera: &Camera, camera_pos: &Vec3<f32>) -> Matrix<f32, 4, 4>
-{
- let mut view = Matrix::new();
-
- view.look_at(&camera_pos, &camera.target, &camera.global_up);
-
- view
-}
-
-#[tracing::instrument(skip_all)]
-fn opengl_debug_message_cb(
- source: MessageSource,
- ty: MessageType,
- id: u32,
- severity: MessageSeverity,
- message: &str,
-)
-{
- use std::backtrace::{Backtrace, BacktraceStatus};
-
- use tracing::{event, Level};
-
- macro_rules! create_event {
- ($level: expr) => {
- event!($level, ?source, ?ty, id, ?severity, message);
- };
- }
-
- if matches!(severity, MessageSeverity::Notification) {
- return;
- }
-
- match ty {
- MessageType::Error => {
- create_event!(Level::ERROR);
-
- let backtrace = Backtrace::capture();
-
- if matches!(backtrace.status(), BacktraceStatus::Captured) {
- event!(Level::TRACE, "{backtrace}");
- }
- }
- MessageType::Other => {
- create_event!(Level::INFO);
- }
- _ => {
- create_event!(Level::WARN);
- }
- };
-}
-
-#[derive(Debug)]
-struct Transformation
-{
- position: Vec3<f32>,
- scale: Vec3<f32>,
-}
-
-fn create_transformation_matrix(transformation: Transformation) -> Matrix<f32, 4, 4>
-{
- let mut matrix = Matrix::new_identity();
-
- matrix.translate(&transformation.position);
- matrix.scale(&transformation.scale);
-
- matrix
-}
diff --git a/engine/src/renderer/opengl/glsl/fragment.glsl b/engine/src/renderer/opengl/glsl/fragment.glsl
deleted file mode 100644
index 5bf5ff2..0000000
--- a/engine/src/renderer/opengl/glsl/fragment.glsl
+++ /dev/null
@@ -1,73 +0,0 @@
-#version 330 core
-
-#preinclude "light.glsl"
-#preinclude "vertex_data.glsl"
-
-#define MAX_LIGHT_CNT 64
-
-out vec4 FragColor;
-
-in VertexData vertex_data;
-
-uniform vec3 view_pos;
-uniform sampler2D input_texture;
-uniform Material material;
-
-uniform PointLight point_lights[MAX_LIGHT_CNT];
-uniform int point_light_cnt;
-
-uniform DirectionalLight directional_lights[MAX_LIGHT_CNT];
-uniform int directional_light_cnt;
-
-void main()
-{
- vec3 ambient_light = calc_ambient_light(material, vertex_data.texture_coords);
-
- vec3 directional_light_sum = vec3(0.0, 0.0, 0.0);
-
- for (int dl_index = 0; dl_index < directional_light_cnt; dl_index++) {
- CalculatedLight calculated_dir_light;
-
- calc_light(
- // Negated since we want the light to point from the light direction
- normalize(-directional_lights[dl_index].direction),
- directional_lights[dl_index].phong,
- vertex_data,
- view_pos,
- material,
- calculated_dir_light
- );
-
- directional_light_sum +=
- calculated_dir_light.diffuse + calculated_dir_light.specular;
- }
-
- vec3 point_light_sum = vec3(0.0, 0.0, 0.0);
-
- for (int pl_index = 0; pl_index < point_light_cnt; pl_index++) {
- vec3 light_direction =
- normalize(point_lights[pl_index].position - vertex_data.world_space_pos);
-
- CalculatedLight calculated_point_light;
-
- calc_light(
- light_direction,
- point_lights[pl_index].phong,
- vertex_data,
- view_pos,
- material,
- calculated_point_light
- );
-
- float attenuation =
- calc_attenuation(point_lights[pl_index], vertex_data.world_space_pos);
-
- calculated_point_light.diffuse *= attenuation;
- calculated_point_light.specular *= attenuation;
-
- point_light_sum +=
- calculated_point_light.diffuse + calculated_point_light.specular;
- }
-
- FragColor = vec4((ambient_light + directional_light_sum + point_light_sum), 1.0);
-}
diff --git a/engine/src/renderer/opengl/glsl/light.glsl b/engine/src/renderer/opengl/glsl/light.glsl
deleted file mode 100644
index f12b5fe..0000000
--- a/engine/src/renderer/opengl/glsl/light.glsl
+++ /dev/null
@@ -1,133 +0,0 @@
-#version 330 core
-
-#ifndef LIGHT_GLSL
-#define LIGHT_GLSL
-
-#preinclude "vertex_data.glsl"
-
-struct Material
-{
- vec3 ambient;
- vec3 diffuse;
- vec3 specular;
- sampler2D ambient_map;
- sampler2D diffuse_map;
- sampler2D specular_map;
- float shininess;
-};
-
-struct LightPhong
-{
- vec3 diffuse;
- vec3 specular;
-};
-
-struct AttenuationProperties
-{
- float constant;
- float linear;
- float quadratic;
-};
-
-struct PointLight
-{
- LightPhong phong;
- vec3 position;
- AttenuationProperties attenuation_props;
-};
-
-struct DirectionalLight
-{
- LightPhong phong;
- vec3 direction;
-};
-
-struct CalculatedLight
-{
- vec3 diffuse;
- vec3 specular;
-};
-
-vec3 calc_ambient_light(in Material material, in vec2 texture_coords)
-{
- return vec3(texture(material.ambient_map, texture_coords)) * material.ambient;
-}
-
-vec3 calc_diffuse_light(
- in Material material,
- in LightPhong light_phong,
- in vec3 light_dir,
- in vec3 norm,
- in vec2 texture_coords
-)
-{
- float diff = max(dot(norm, light_dir), 0.0);
-
- return light_phong.diffuse * (
- diff * (vec3(texture(material.diffuse_map, texture_coords)) * material.diffuse)
- );
-}
-
-vec3 calc_specular_light(
- in Material material,
- in LightPhong light_phong,
- in vec3 light_dir,
- in vec3 norm,
- in vec3 view_pos,
- in vec3 frag_pos,
- in vec2 texture_coords
-)
-{
- vec3 view_direction = normalize(view_pos - frag_pos);
-
- vec3 halfway_direction = normalize(light_dir + view_direction);
-
- float spec =
- pow(max(dot(norm, halfway_direction), 0.0), material.shininess);
-
- return light_phong.specular * (
- spec * (vec3(texture(material.specular_map, texture_coords)) * material.specular)
- );
-}
-
-float calc_attenuation(in PointLight point_light, in vec3 position)
-{
- float light_distance = length(point_light.position - position);
-
- return 1.0 / (point_light.attenuation_props.constant
- + point_light.attenuation_props.linear
- * light_distance + point_light.attenuation_props.quadratic
- * pow(light_distance, 2));
-}
-
-void calc_light(
- in vec3 light_direction,
- in LightPhong light_phong,
- in VertexData vertex_data,
- in vec3 view_pos,
- in Material material,
- out CalculatedLight calculated_light
-)
-{
- vec3 norm = normalize(vertex_data.world_space_normal);
-
- calculated_light.diffuse = calc_diffuse_light(
- material,
- light_phong,
- light_direction,
- norm,
- vertex_data.texture_coords
- );
-
- calculated_light.specular = calc_specular_light(
- material,
- light_phong,
- light_direction,
- norm,
- view_pos,
- vertex_data.world_space_pos,
- vertex_data.texture_coords
- );
-}
-
-#endif
diff --git a/engine/src/renderer/opengl/glsl/vertex.glsl b/engine/src/renderer/opengl/glsl/vertex.glsl
deleted file mode 100644
index b57caa6..0000000
--- a/engine/src/renderer/opengl/glsl/vertex.glsl
+++ /dev/null
@@ -1,24 +0,0 @@
-#version 330 core
-
-#preinclude "vertex_data.glsl"
-
-layout (location = 0) in vec3 pos;
-layout (location = 1) in vec2 texture_coords;
-layout (location = 2) in vec3 normal;
-
-out VertexData vertex_data;
-
-uniform mat4 model;
-uniform mat4 view;
-uniform mat4 projection;
-
-void main()
-{
- gl_Position = projection * view * model * vec4(pos, 1.0);
-
- vertex_data.world_space_pos = vec3(model * vec4(pos, 1.0));
- vertex_data.texture_coords = texture_coords;
-
- // TODO: Do this using CPU for performance increase
- vertex_data.world_space_normal = mat3(transpose(inverse(model))) * normal;
-}
diff --git a/engine/src/renderer/opengl/glsl/vertex_data.glsl b/engine/src/renderer/opengl/glsl/vertex_data.glsl
deleted file mode 100644
index 486d445..0000000
--- a/engine/src/renderer/opengl/glsl/vertex_data.glsl
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef VERTEX_DATA_GLSL
-#define VERTEX_DATA_GLSL
-
-struct VertexData
-{
- vec2 texture_coords;
- vec3 world_space_pos;
- vec3 world_space_normal;
-};
-
-#endif
diff --git a/engine/src/renderer/opengl/vertex.rs b/engine/src/renderer/opengl/vertex.rs
deleted file mode 100644
index 499b94b..0000000
--- a/engine/src/renderer/opengl/vertex.rs
+++ /dev/null
@@ -1,64 +0,0 @@
-use crate::vector::{Vec2, Vec3};
-
-#[derive(Debug, Clone)]
-#[repr(C)]
-pub struct Vertex
-{
- pub pos: Vec3<f32>,
- pub texture_coords: Vec2<f32>,
- pub normal: Vec3<f32>,
-}
-
-impl Vertex
-{
- pub fn attrs() -> &'static [Attribute]
- {
- #[allow(clippy::cast_possible_truncation)]
- &[
- Attribute {
- index: 0,
- component_type: AttributeComponentType::Float,
- component_cnt: AttributeComponentCnt::Three,
- component_size: size_of::<f32>() as u32,
- },
- Attribute {
- index: 1,
- component_type: AttributeComponentType::Float,
- component_cnt: AttributeComponentCnt::Two,
- component_size: size_of::<f32>() as u32,
- },
- Attribute {
- index: 2,
- component_type: AttributeComponentType::Float,
- component_cnt: AttributeComponentCnt::Three,
- component_size: size_of::<f32>() as u32,
- },
- ]
- }
-}
-
-#[derive(Debug)]
-pub struct Attribute
-{
- pub index: u32,
- pub component_type: AttributeComponentType,
- pub component_cnt: AttributeComponentCnt,
- pub component_size: u32,
-}
-
-#[derive(Debug)]
-pub enum AttributeComponentType
-{
- Float,
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(u32)]
-#[allow(dead_code)]
-pub enum AttributeComponentCnt
-{
- One = 1,
- Two = 2,
- Three = 3,
- Four = 4,
-}
diff --git a/engine/src/rendering.rs b/engine/src/rendering.rs
new file mode 100644
index 0000000..1bb11cc
--- /dev/null
+++ b/engine/src/rendering.rs
@@ -0,0 +1,701 @@
+use std::collections::VecDeque;
+use std::sync::atomic::{AtomicU64, Ordering};
+
+use bitflags::bitflags;
+use engine_macros::Reflection;
+
+use crate::asset::Handle as AssetHandle;
+use crate::builder;
+use crate::data_types::dimens::Dimens;
+use crate::draw_flags::PolygonModeConfig;
+use crate::ecs::actions::Actions;
+use crate::ecs::component::local::Local;
+use crate::ecs::event::component::{EventMatchExt, Removed};
+use crate::ecs::pair::{ChildOf, Pair};
+use crate::ecs::phase::{Phase, POST_UPDATE as POST_UPDATE_PHASE};
+use crate::ecs::query::term::With;
+use crate::ecs::sole::Single;
+use crate::ecs::system::initializable::Initializable;
+use crate::ecs::system::observer::Observe;
+use crate::ecs::system::Into;
+use crate::ecs::{declare_entity, pair, Component, Query, Sole};
+use crate::image::{ColorType as ImageColorType, Image};
+use crate::mesh::Mesh;
+use crate::rendering::blending::Config as BlendingConfig;
+use crate::rendering::object::{Id as ObjectId, Store as ObjectStore};
+use crate::rendering::shader::cursor::Binding as ShaderBinding;
+use crate::rendering::shader::Program as ShaderProgram;
+use crate::texture::{CubeMapFace, Properties as TextureProperties};
+use crate::vector::Vec2;
+use crate::windowing::dpi::PhysicalSize;
+use crate::windowing::window::Window;
+
+pub mod backend;
+pub mod blending;
+pub mod main_render_pass;
+pub mod object;
+pub mod shader;
+
+static NEXT_SURFACE_ID: AtomicU64 = AtomicU64::new(0);
+
+declare_entity! {
+pub PRE_RENDER_PHASE: (Phase, pair!(ChildOf, { *POST_UPDATE_PHASE }));
+
+pub RENDER_PHASE: (Phase, pair!(ChildOf, { *PRE_RENDER_PHASE }));
+
+pub POST_RENDER_PHASE: (Phase, pair!(ChildOf, { *RENDER_PHASE }));
+}
+
+builder! {
+#[builder(name=ExtensionBuilder, derives=(Debug, Clone, Default))]
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Extension {
+ pub graphics_props: GraphicsProperties,
+}
+}
+
+impl Extension
+{
+ pub fn builder() -> ExtensionBuilder
+ {
+ ExtensionBuilder::default()
+ }
+}
+
+impl crate::ecs::extension::Extension for Extension
+{
+ fn collect(self, mut collector: crate::ecs::extension::Collector<'_>)
+ {
+ collector.spawn_declared_entity(&PRE_RENDER_PHASE);
+ collector.spawn_declared_entity(&RENDER_PHASE);
+ collector.spawn_declared_entity(&POST_RENDER_PHASE);
+
+ let _ = collector.add_sole(RenderPasses::default());
+ let _ = collector.add_sole(CommandQueue::default());
+ let _ = collector.add_sole(ObjectStore::default());
+
+ let _ = collector.add_sole(self.graphics_props);
+
+ collector.add_system(
+ *PRE_RENDER_PHASE,
+ main_render_pass::add_main_render_pass
+ .into_system()
+ .initialize(Default::default()),
+ );
+
+ collector.add_system(
+ *RENDER_PHASE,
+ enqueue_commands_from_render_passes
+ .into_system()
+ .initialize((ActiveDrawProperties::default(),)),
+ );
+
+ collector.add_observer(handle_window_removed);
+
+ crate::rendering::shader::prepare(&mut collector);
+
+ crate::rendering::backend::get_default().collect(collector);
+ }
+}
+
+impl Default for Extension
+{
+ fn default() -> Self
+ {
+ Self::builder().build()
+ }
+}
+
+/// Marker component for windows that should be renderer to.
+#[derive(Debug, Default, Clone, Copy, Component, Reflection)]
+pub struct TargetWindow;
+
+builder! {
+#[builder(name=GraphicsPropertiesBuilder, derives=(Debug, Clone))]
+#[derive(Debug, Clone, Sole)]
+#[non_exhaustive]
+pub struct GraphicsProperties
+{
+ /// Number of samples for multisampling. `None` means no multisampling.
+ #[builder(skip_generate_fn)]
+ pub multisampling_sample_cnt: Option<u8>,
+
+ /// Whether graphics API debugging is enabled.
+ pub debug: bool,
+
+ /// Whether depth testing is enabled
+ pub depth_test: bool,
+}
+}
+
+impl GraphicsProperties
+{
+ pub fn builder() -> GraphicsPropertiesBuilder
+ {
+ GraphicsPropertiesBuilder::default()
+ }
+}
+
+impl Default for GraphicsProperties
+{
+ fn default() -> Self
+ {
+ Self::builder().build()
+ }
+}
+
+impl GraphicsPropertiesBuilder
+{
+ pub fn multisampling_sample_cnt(mut self, multisampling_sample_cnt: u8) -> Self
+ {
+ self.multisampling_sample_cnt = Some(multisampling_sample_cnt);
+ self
+ }
+
+ pub fn no_multisampling(mut self) -> Self
+ {
+ self.multisampling_sample_cnt = None;
+ self
+ }
+}
+
+impl Default for GraphicsPropertiesBuilder
+{
+ fn default() -> Self
+ {
+ Self {
+ multisampling_sample_cnt: Some(4),
+ debug: false,
+ depth_test: true,
+ }
+ }
+}
+
+#[derive(Debug, Component)]
+pub struct Surface
+{
+ pub id: SurfaceId,
+ pub size: PhysicalSize<u32>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SurfaceId
+{
+ inner: u64,
+}
+
+impl SurfaceId
+{
+ pub fn new_unique() -> Self
+ {
+ Self {
+ inner: NEXT_SURFACE_ID.fetch_add(1, Ordering::Relaxed),
+ }
+ }
+}
+
+#[derive(Debug, Default, Sole)]
+pub struct RenderPasses
+{
+ pub passes: VecDeque<RenderPass>,
+}
+
+#[derive(Debug)]
+pub struct RenderPass
+{
+ pub commands: Vec<Command>,
+ pub draw_properties: DrawProperties,
+}
+
+#[derive(Debug, Reflection)]
+#[non_exhaustive]
+pub enum Command
+{
+ RemoveSurface(SurfaceId),
+ MakeCurrent(SurfaceId),
+ SetSurfaceSize(SurfaceId, PhysicalSize<u32>),
+ ClearBuffers(BufferClearMask),
+ SwapBuffers(SurfaceId),
+ CreateShaderProgram(ObjectId, ShaderProgram),
+ ActivateShader(ObjectId),
+ SetShaderBinding(ObjectId, ShaderBinding),
+ CreateTexture
+ {
+ obj_id: ObjectId,
+ pixel_data_format: TexturePixelDataFormat,
+ creation: TextureCreation,
+ properties: TextureProperties,
+ },
+ UpdateTexture
+ {
+ obj_id: ObjectId,
+ pixels: Box<[u8]>,
+ pixel_data_format: TexturePixelDataFormat,
+ update: TextureUpdate,
+ },
+ RemoveTexture(ObjectId),
+ CreateFramebuffer(ObjectId),
+ RemoveFramebuffer(ObjectId),
+ UpdateFramebufferProperties
+ {
+ object_id: ObjectId,
+ properties: FramebufferProperties,
+ update_flags: FramebufferPropertiesUpdateFlags,
+ },
+ CreateMesh
+ {
+ obj_id: ObjectId,
+ mesh: AssetOrValue<Mesh>,
+ usage: MeshUsage,
+ },
+ UpdateMesh
+ {
+ obj_id: ObjectId,
+ mesh: Mesh,
+ usage: MeshUsage,
+ },
+ RemoveMesh(ObjectId),
+ DrawMesh(ObjectId, DrawMeshOptions),
+ UpdateDrawProperties(DrawProperties, DrawPropertiesUpdateFlags),
+}
+
+#[derive(Debug)]
+pub enum AssetOrValue<T: 'static>
+{
+ Asset(AssetHandle<T>),
+ Value(T),
+}
+
+builder! {
+ #[builder(name = DrawMeshOptionsBuilder, derives = (Debug, Default, Clone))]
+ #[derive(Debug, Default, Clone)]
+ #[non_exhaustive]
+ pub struct DrawMeshOptions {
+ pub element_offset: u32,
+ pub vertex_offset: u32,
+
+ #[builder(skip_generate_fn)]
+ pub element_cnt: Option<u32>,
+ }
+}
+
+impl DrawMeshOptions
+{
+ pub fn builder() -> DrawMeshOptionsBuilder
+ {
+ DrawMeshOptionsBuilder::default()
+ }
+}
+
+impl DrawMeshOptionsBuilder
+{
+ pub fn element_cnt(mut self, element_cnt: u32) -> Self
+ {
+ self.element_cnt = Some(element_cnt);
+ self
+ }
+}
+
+bitflags! {
+ #[derive(Debug)]
+ pub struct BufferClearMask: u8 {
+ const COLOR = 1;
+ const DEPTH = 2;
+ const STENCIL = 3;
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum MeshUsage
+{
+ /// The mesh data is set only once and used by the GPU at most a few times.
+ Stream,
+
+ /// The mesh data is set only once and used many times.
+ Static,
+
+ /// The mesh data is changed a lot and used many times.
+ Dynamic,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ScissorBox
+{
+ /// Size of the scissor box in window coordinates. When `None`, the dimensions of the
+ /// window is used
+ pub size: Option<Dimens<u16>>,
+
+ /// Position (in window coordinates) of the lower left corner of the scissor box.
+ pub lower_left_corner_pos: Vec2<u16>,
+}
+
+impl Default for ScissorBox
+{
+ fn default() -> Self
+ {
+ Self {
+ size: None,
+ lower_left_corner_pos: Vec2 { x: 0, y: 0 },
+ }
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+pub enum DepthFunction
+{
+ /// Never passes.
+ Never,
+
+ /// Passes if the incoming depth value is less than the stored depth value.
+ #[default]
+ Less,
+
+ /// Passes if the incoming depth value is equal to the stored depth value.
+ Equal,
+
+ /// Passes if the incoming depth value is less than or equal to the stored depth
+ /// value.
+ LessOrEqual,
+
+ /// Passes if the incoming depth value is greater than the stored depth value.
+ Greater,
+
+ /// Passes if the incoming depth value is not equal to the stored depth value.
+ NotEqual,
+
+ /// Passes if the incoming depth value is greater than or equal to the stored depth
+ /// value.
+ GreaterOrEqual,
+
+ /// Always passes.
+ Always,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[non_exhaustive]
+pub struct DrawProperties
+{
+ pub polygon_mode_config: PolygonModeConfig,
+ pub blending_enabled: bool,
+ pub blending_config: BlendingConfig,
+ pub depth_test_enabled: bool,
+ pub depth_function: DepthFunction,
+ pub scissor_test_enabled: bool,
+ pub scissor_box: ScissorBox,
+ pub face_culling_enabled: bool,
+
+ /// `None` for default framebuffer
+ pub draw_framebuffer: Option<ObjectId>,
+
+ /// `None` for default framebuffer
+ pub read_framebuffer: Option<ObjectId>,
+}
+
+impl Default for DrawProperties
+{
+ fn default() -> Self
+ {
+ Self {
+ polygon_mode_config: PolygonModeConfig::default(),
+ blending_enabled: false,
+ blending_config: BlendingConfig::default(),
+ depth_test_enabled: true,
+ depth_function: DepthFunction::default(),
+ scissor_test_enabled: false,
+ scissor_box: ScissorBox::default(),
+ face_culling_enabled: false,
+ draw_framebuffer: None,
+ read_framebuffer: None,
+ }
+ }
+}
+
+bitflags! {
+ #[derive(Debug, Clone, Copy)]
+ pub struct DrawPropertiesUpdateFlags: u16
+ {
+ const POLYGON_MODE_CONFIG = 1 << 0;
+ const BLENDING_CONFIG = 1 << 1;
+ const BLENDING_ENABLED = 1 << 2;
+ const DEPTH_TEST_ENABLED = 1 << 3;
+ const DEPTH_FUNCTION = 1 << 4;
+ const SCISSOR_TEST_ENABLED = 1 << 5;
+ const SCISSOR_BOX = 1 << 6;
+ const FACE_CULLING_ENABLED = 1 << 7;
+ const DRAW_FRAMEBUFFER = 1 << 8;
+ const READ_FRAMEBUFFER = 1 << 9;
+ }
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum TextureCreation
+{
+ Texture2D
+ {
+ size: Dimens<u32>,
+ image: Option<Box<[u8]>>,
+ },
+ CubeMap
+ {
+ size: Dimens<u32>,
+ images: Option<[(CubeMapFace, Box<[u8]>); 6]>,
+ },
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub enum TextureUpdate
+{
+ Texture2D
+ {
+ size: Dimens<u32>,
+
+ /// Specifies texel index offsets in the x and y directions within the texture
+ /// pixels.
+ offset: Vec2<u32>,
+ },
+ CubeMap
+ {
+ size: Dimens<u32>,
+
+ /// Specifies texel index offsets in the x and y directions within the texture
+ /// pixels.
+ offset: Vec2<u32>,
+
+ face: CubeMapFace,
+ },
+}
+
+/// Rendering command FIFO queue.
+#[derive(Debug, Sole)]
+pub struct CommandQueue
+{
+ queue: VecDeque<Command>,
+}
+
+impl CommandQueue
+{
+ pub fn push(&mut self, command: Command)
+ {
+ self.queue.push_back(command);
+ }
+
+ pub fn drain(&mut self) -> impl Iterator<Item = Command> + use<'_>
+ {
+ self.queue.drain(..)
+ }
+}
+
+impl Default for CommandQueue
+{
+ fn default() -> Self
+ {
+ CommandQueue { queue: VecDeque::with_capacity(100) }
+ }
+}
+
+#[tracing::instrument(skip_all)]
+fn enqueue_commands_from_render_passes(
+ window_surface_query: Query<(&mut Surface, &Window, With<TargetWindow>)>,
+ mut command_queue: Single<CommandQueue>,
+ mut render_passes: Single<RenderPasses>,
+ mut active_draw_props: Local<ActiveDrawProperties>,
+)
+{
+ let Ok(command_queue) = command_queue.get_mut() else {
+ unreachable!();
+ };
+
+ let Ok(render_passes) = render_passes.get_mut() else {
+ unreachable!();
+ };
+
+ let mut last_render_pass_draw_props = active_draw_props.draw_properties.clone();
+
+ for (mut window_surface, window) in &window_surface_query {
+ if window_surface.size != window.inner_size {
+ command_queue.push(Command::SetSurfaceSize(
+ window_surface.id,
+ window.inner_size,
+ ));
+
+ window_surface.size = window.inner_size.clone();
+ }
+ }
+
+ for render_pass in render_passes.passes.drain(..) {
+ if render_pass.draw_properties != last_render_pass_draw_props {
+ command_queue.push(Command::UpdateDrawProperties(
+ render_pass.draw_properties.clone(),
+ DrawPropertiesUpdateFlags::all(),
+ ));
+
+ last_render_pass_draw_props = render_pass.draw_properties;
+ }
+
+ let last_updated_draw_props = render_pass
+ .commands
+ .iter()
+ .filter_map(|command| match command {
+ Command::UpdateDrawProperties(draw_props, _) => Some(draw_props.clone()),
+ _ => None,
+ })
+ .last();
+
+ command_queue.queue.extend(render_pass.commands);
+
+ if let Some(last_updated_draw_props) = last_updated_draw_props {
+ last_render_pass_draw_props = last_updated_draw_props;
+ }
+ }
+
+ active_draw_props.draw_properties = last_render_pass_draw_props;
+
+ for (window_surface, _) in &window_surface_query {
+ command_queue.push(Command::SwapBuffers(window_surface.id));
+ }
+}
+
+#[tracing::instrument(skip_all)]
+fn handle_window_removed(
+ observe: Observe<Pair<Removed, Window>>,
+ mut command_queue: Single<CommandQueue>,
+ mut actions: Actions,
+)
+{
+ let Ok(command_queue) = command_queue.get_mut() else {
+ unreachable!();
+ };
+
+ for evt_match in &observe {
+ let window_ent_id = evt_match.entity_id();
+
+ let window_ent = evt_match.get_entity();
+
+ if window_ent.get::<TargetWindow>().is_none() {
+ continue;
+ }
+
+ tracing::debug!(
+ entity_id = %window_ent_id,
+ title = %evt_match.get_ent_target_comp().title,
+ "Handling removal of window"
+ );
+
+ let Some(window_surface) = window_ent.get::<Surface>() else {
+ continue;
+ };
+
+ actions.remove_comps::<(Surface,)>(window_ent_id);
+
+ command_queue
+ .queue
+ .push_front(Command::RemoveSurface(window_surface.id));
+ }
+}
+
+#[derive(Debug, Default, Clone, Component)]
+struct ActiveDrawProperties
+{
+ pub draw_properties: DrawProperties,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum RgbTextureDataType
+{
+ UnsignedByte,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum RgbaTextureDataType
+{
+ UnsignedByte,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum SrgbTextureDataType
+{
+ UnsignedByte,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum SrgbaTextureDataType
+{
+ UnsignedByte,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum DepthTextureDataType
+{
+ Float32,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum TexturePixelDataFormat
+{
+ Rgb(RgbTextureDataType),
+ Rgba(RgbaTextureDataType),
+ Srgb(SrgbTextureDataType),
+ Srgba(SrgbaTextureDataType),
+ Depth(DepthTextureDataType),
+}
+
+impl TexturePixelDataFormat
+{
+ pub fn for_image(image: &Image) -> Option<Self>
+ {
+ let is_srgb = image.color_space_is_srgb();
+
+ match image.color_type() {
+ ImageColorType::Rgb8 if is_srgb => Some(TexturePixelDataFormat::Srgb(
+ SrgbTextureDataType::UnsignedByte,
+ )),
+ ImageColorType::Rgb8 => Some(TexturePixelDataFormat::Rgb(
+ RgbTextureDataType::UnsignedByte,
+ )),
+ ImageColorType::Rgba8 if is_srgb => Some(TexturePixelDataFormat::Srgba(
+ SrgbaTextureDataType::UnsignedByte,
+ )),
+ ImageColorType::Rgba8 => Some(TexturePixelDataFormat::Rgba(
+ RgbaTextureDataType::UnsignedByte,
+ )),
+ _ => None,
+ }
+ }
+}
+
+/// Index of a color texture attached to a framebuffer.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub struct FramebufferColorTextureIndex(pub u8);
+
+#[derive(Debug, Default, Clone)]
+#[non_exhaustive]
+pub struct FramebufferProperties
+{
+ pub depth_texture: Option<FramebufferTexture>,
+ pub color_draw_buffer: Option<FramebufferColorTextureIndex>,
+}
+
+bitflags! {
+ #[derive(Debug, Default, Clone)]
+ pub struct FramebufferPropertiesUpdateFlags: u8
+ {
+ const DEPTH_TEXTURE = 1 << 0;
+ const COLOR_DRAW_BUFFER = 1 << 1;
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct FramebufferTexture
+{
+ pub object_id: ObjectId,
+ pub mipmap_level: u16,
+}
diff --git a/engine/src/rendering/backend.rs b/engine/src/rendering/backend.rs
new file mode 100644
index 0000000..d69b338
--- /dev/null
+++ b/engine/src/rendering/backend.rs
@@ -0,0 +1,9 @@
+use ecs::extension::Extension;
+
+pub mod opengl;
+
+/// Returns the default rendering backend.
+pub fn get_default() -> impl Extension
+{
+ self::opengl::Extension::default()
+}
diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs
new file mode 100644
index 0000000..32afd8f
--- /dev/null
+++ b/engine/src/rendering/backend/opengl.rs
@@ -0,0 +1,2042 @@
+//! OpenGL rendering backend.
+
+use std::borrow::Cow;
+use std::collections::HashMap;
+use std::convert::Infallible;
+use std::hint::cold_path;
+use std::num::NonZero;
+
+use ecs::query::term::With;
+use glutin::config::Config as GlutinConfig;
+use glutin::display::GetGlDisplay;
+use glutin::error::Error as GlutinError;
+use glutin::prelude::{GlDisplay, PossiblyCurrentGlContext};
+use glutin::surface::{
+ GlSurface as _,
+ Surface as GlutinSurface,
+ WindowSurface as GlutinWindowSurface,
+};
+use intmap::IntMap;
+use opengl_bindings::blending::{
+ configure as gl_blending_configure,
+ Configuration as GlBlendingConfig,
+ Equation as GlBlendingEquation,
+ Factor as GlBlendingFactor,
+};
+use opengl_bindings::buffer::{
+ BindingTarget as GlBufferBindingTarget,
+ Buffer as GlBuffer,
+};
+use opengl_bindings::debug::{
+ set_debug_message_callback,
+ set_debug_message_control,
+ MessageIdsAction,
+ MessageSeverity,
+ MessageSource,
+ MessageType,
+ SetDebugMessageControlError as GlSetDebugMessageControlError,
+};
+use opengl_bindings::framebuffer::{
+ bind as gl_bind_framebuffer,
+ Attachment as GlFramebufferAttachment,
+ ColorAttachment as GlFramebufferColorAttachment,
+ Framebuffer as GlFramebuffer,
+ Target as GlFramebufferTarget,
+};
+use opengl_bindings::misc::{
+ clear_buffers,
+ define_scissor_box as gl_define_scissor_box,
+ enable,
+ get_viewport as gl_get_viewport,
+ set_depth_function as gl_set_depth_function,
+ set_enabled,
+ set_viewport as gl_set_viewport,
+ BufferClearMask as GlBufferClearMask,
+ Capability,
+ DepthFunction as GlDepthFunction,
+};
+use opengl_bindings::shader::{
+ Error as GlShaderError,
+ Kind as ShaderKind,
+ Program as GlShaderProgram,
+ Shader as GlShader,
+};
+use opengl_bindings::texture::{
+ CubeMapFace as GlCubeMapTextureFace,
+ Error as GlTextureError,
+ Filtering as GlTextureFiltering,
+ PixelDataFormat as GlTexturePixelDataFormat,
+ Texture as GlTexture,
+ Wrapping as GlTextureWrapping,
+};
+use opengl_bindings::vertex_array::{
+ DrawError as GlDrawError,
+ PrimitiveKind,
+ VertexArray,
+};
+use opengl_bindings::{
+ MakeContextCurrentError as GlMakeContextCurrentError,
+ MaybeCurrentContextWithFns,
+};
+use raw_window_handle::WindowHandle;
+use safer_ffi::layout::ReprC;
+use zerocopy::IntoBytes;
+
+use crate::asset::Assets;
+use crate::data_types::color::Color;
+use crate::data_types::dimens::Dimens;
+use crate::ecs::actions::Actions;
+use crate::ecs::query::term::Without;
+use crate::ecs::sole::Single;
+use crate::ecs::{Component, Query, Sole};
+use crate::reflection::EnumReflectionExt;
+use crate::rendering::backend::opengl::glutin_compat::{
+ DisplayBuilder,
+ Error as GlutinCompatError,
+};
+use crate::rendering::backend::opengl::graphics_mesh::GraphicsMesh;
+use crate::rendering::blending::{
+ Equation as BlendingEquation,
+ Factor as BlendingFactor,
+};
+use crate::rendering::object::{
+ Id as ObjectId,
+ Kind as ObjectKind,
+ Object,
+ RawValue as ObjectRawValue,
+ Store as ObjectStore,
+};
+use crate::rendering::shader::cursor::{
+ Binding as ShaderBinding,
+ BindingLocation as ShaderBindingLocation,
+ BindingValue as ShaderBindingValue,
+};
+use crate::rendering::shader::{
+ Error as ShaderError,
+ Program as ShaderProgram,
+ ProgramMetadata as ShaderProgramMetadata,
+ Stage as ShaderStage,
+};
+use crate::rendering::{
+ AssetOrValue,
+ BufferClearMask,
+ Command,
+ CommandQueue,
+ DepthFunction,
+ DrawMeshOptions,
+ DrawProperties,
+ DrawPropertiesUpdateFlags,
+ FramebufferProperties,
+ FramebufferPropertiesUpdateFlags,
+ GraphicsProperties,
+ Surface,
+ SurfaceId,
+ TargetWindow,
+ TextureCreation,
+ TexturePixelDataFormat,
+ TextureUpdate,
+ POST_RENDER_PHASE,
+ RENDER_PHASE,
+};
+use crate::texture::{
+ CubeMapFace as CubeMapTextureFace,
+ Filtering as TextureFiltering,
+ Properties as TextureProperties,
+ Wrapping as TextureWrapping,
+};
+use crate::util::OptionExt;
+use crate::vector::{Vec2, Vec3};
+use crate::windowing::dpi::PhysicalSize;
+use crate::windowing::window::{
+ Closed as WindowClosed,
+ CreationAttributes as WindowCreationAttributes,
+ CreationReady,
+ Window,
+};
+use crate::windowing::Context as WindowingContext;
+
+mod glutin_compat;
+mod graphics_mesh;
+
+#[derive(Debug, Component)]
+struct WindowGlConfig
+{
+ gl_config: GlutinConfig,
+}
+
+#[derive(Sole, Default)]
+struct GraphicsContext
+{
+ gl_context: Option<MaybeCurrentContextWithFns>,
+ surfaces: HashMap<SurfaceId, GraphicsContextSurface>,
+ backend_resources: BackendResourceStore,
+}
+
+#[derive(Debug, Default)]
+struct BackendResourceStore
+{
+ inner: IntMap<BackendResourceId, BackendResource>,
+ next_id: BackendResourceId,
+}
+
+impl BackendResourceStore
+{
+ fn try_create_resource<Err>(
+ &mut self,
+ object_store: &mut ObjectStore,
+ object_id: ObjectId,
+ resource_fn: impl FnOnce() -> Result<BackendResource, Err>,
+ ) -> Result<(), Err>
+ {
+ if object_store.contains_non_pending_with_id(&object_id) {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ "Object store already contains a object with this ID"
+ );
+ return Ok(());
+ }
+
+ let resource = resource_fn()?;
+
+ let res_id = self.next_id;
+
+ self.next_id.0 += 1;
+
+ let object_kind = resource.object_kind();
+
+ self.inner.insert(res_id, resource);
+
+ object_store.insert(object_id, Object::from_raw(res_id.0, object_kind));
+
+ Ok(())
+ }
+
+ fn get_resource(
+ &self,
+ object_store: &ObjectStore,
+ object_id: ObjectId,
+ object_kind: ObjectKind,
+ ) -> Option<&BackendResource>
+ {
+ let Some(object) = object_store.get_obj(&object_id) else {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ %object_kind,
+ "Object does not exist in the rendering object store"
+ );
+ return None;
+ };
+
+ if object.kind() != object_kind {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ expected_object_kind = %object_kind,
+ found_object_kind = %object.kind(),
+ "Unexpected object kind of object in object store"
+ );
+ return None;
+ }
+
+ let resource_id = BackendResourceId(object.as_raw());
+
+ let Some(resource) = self.inner.get(resource_id) else {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ %object_kind,
+ ?resource_id,
+ "Backend resource does not exist"
+ );
+ return None;
+ };
+
+ debug_assert_eq!(resource.object_kind(), object_kind);
+
+ Some(resource)
+ }
+
+ fn get_resource_mut(
+ &mut self,
+ object_store: &ObjectStore,
+ object_id: ObjectId,
+ object_kind: ObjectKind,
+ ) -> Option<&mut BackendResource>
+ {
+ let Some(object) = object_store.get_obj(&object_id) else {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ %object_kind,
+ "Object does not exist in the rendering object store"
+ );
+ return None;
+ };
+
+ if object.kind() != object_kind {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ expected_object_kind = %object_kind,
+ found_object_kind = %object.kind(),
+ "Unexpected object kind of object in object store"
+ );
+ return None;
+ }
+
+ let resource_id = BackendResourceId(object.as_raw());
+
+ let Some(resource) = self.inner.get_mut(resource_id) else {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ %object_kind,
+ ?resource_id,
+ "Backend resource does not exist"
+ );
+ return None;
+ };
+
+ debug_assert_eq!(resource.object_kind(), object_kind);
+
+ Some(resource)
+ }
+
+ fn remove_resource(
+ &mut self,
+ object_store: &mut ObjectStore,
+ object_id: ObjectId,
+ object_kind: ObjectKind,
+ curr_gl_ctx: &MaybeCurrentContextWithFns,
+ )
+ {
+ if let Some(object) = object_store.get_obj(&object_id) {
+ if object.kind() != object_kind {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ expected_object_kind = %object_kind,
+ found_object_kind = %object.kind(),
+ "Unexpected object kind"
+ );
+ return;
+ }
+ }
+
+ let Some(object) = object_store.remove(&object_id).flatten() else {
+ tracing::error!(
+ ?object_id,
+ %object_kind,
+ "Object does not exist in the rendering object store"
+ );
+ return;
+ };
+
+ let resource_id = BackendResourceId(object.as_raw());
+
+ let Some(resource) = self.inner.remove(resource_id) else {
+ cold_path();
+ tracing::error!(
+ ?object_id,
+ %object_kind,
+ ?resource_id,
+ "Backend resource does not exist"
+ );
+ return;
+ };
+
+ resource.destroy(curr_gl_ctx);
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+struct BackendResourceId(ObjectRawValue);
+
+impl intmap::IntKey for BackendResourceId
+{
+ type Int = ObjectRawValue;
+
+ const PRIME: Self::Int = <ObjectRawValue as intmap::IntKey>::PRIME;
+
+ fn into_int(self) -> Self::Int
+ {
+ self.0.into_int()
+ }
+}
+
+#[derive(Debug)]
+struct GraphicsContextSurface
+{
+ window_surface: GlutinSurface<GlutinWindowSurface>,
+ size: PhysicalSize<u32>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+struct ShaderBindingIndex(u32);
+
+impl intmap::IntKey for ShaderBindingIndex
+{
+ type Int = u32;
+
+ const PRIME: Self::Int = <u32 as intmap::IntKey>::PRIME;
+
+ fn into_int(self) -> Self::Int
+ {
+ self.0
+ }
+}
+
+#[derive(Debug)]
+enum BackendShaderBinding
+{
+ Uniform(GlBuffer<u8>),
+ Texture(GlTexture),
+}
+
+impl BackendShaderBinding
+{
+ fn bind(
+ &self,
+ gl_context: &MaybeCurrentContextWithFns,
+ binding_index: ShaderBindingIndex,
+ )
+ {
+ match self {
+ BackendShaderBinding::Uniform(uniform_buf) => {
+ uniform_buf.bind_to_indexed_target(
+ gl_context,
+ GlBufferBindingTarget::UniformBuffer,
+ binding_index.0,
+ );
+ }
+ BackendShaderBinding::Texture(texture) => {
+ texture.bind_to_texture_unit(gl_context, binding_index.0)
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+enum BackendResource
+{
+ Mesh
+ {
+ mesh: GraphicsMesh,
+ vertex_attrs_updated_for_shader: Option<ObjectId>,
+ },
+ Shader
+ {
+ program: GlShaderProgram,
+ program_metadata: ShaderProgramMetadata,
+ bindings: IntMap<ShaderBindingIndex, BackendShaderBinding>,
+ },
+ Texture
+ {
+ texture: GlTexture,
+ pixel_data_format: TexturePixelDataFormat,
+ kind: TextureKind,
+ },
+ Framebuffer
+ {
+ framebuffer: GlFramebuffer
+ },
+}
+
+impl BackendResource
+{
+ fn object_kind(&self) -> ObjectKind
+ {
+ match self {
+ Self::Mesh { .. } => ObjectKind::Mesh,
+ Self::Shader { .. } => ObjectKind::ShaderProgram,
+ Self::Texture { .. } => ObjectKind::Texture,
+ Self::Framebuffer { .. } => ObjectKind::Framebuffer,
+ }
+ }
+
+ fn destroy(&self, curr_gl_ctx: &MaybeCurrentContextWithFns)
+ {
+ match self {
+ Self::Mesh { mesh, .. } => {
+ mesh.destroy(curr_gl_ctx);
+ }
+ Self::Shader { program, .. } => {
+ program.clone().delete(curr_gl_ctx);
+ }
+ Self::Texture { texture, .. } => {
+ texture.clone().delete(curr_gl_ctx);
+ }
+ Self::Framebuffer { framebuffer } => {
+ framebuffer.clone().delete(curr_gl_ctx);
+ }
+ }
+ }
+}
+
+#[derive(Debug)]
+enum TextureKind
+{
+ Texture2D,
+ CubeMap,
+}
+
+#[derive(Debug, Default)]
+#[non_exhaustive]
+pub struct Extension {}
+
+impl crate::ecs::extension::Extension for Extension
+{
+ fn collect(self, mut collector: crate::ecs::extension::Collector<'_>)
+ {
+ collector.add_system(*RENDER_PHASE, handle_commands);
+
+ collector.add_system(*POST_RENDER_PHASE, prepare_windows);
+ collector.add_system(*POST_RENDER_PHASE, init_window_graphics);
+
+ let _ = collector.add_sole(GraphicsContext::default());
+ }
+}
+
+fn prepare_windows(
+ window_query: Query<(
+ Option<&Window>,
+ &mut WindowCreationAttributes,
+ With<TargetWindow>,
+ Without<CreationReady>,
+ Without<WindowGlConfig>,
+ Without<WindowClosed>,
+ )>,
+ windowing_context: Single<WindowingContext>,
+ graphics_props: Single<GraphicsProperties>,
+ mut actions: Actions,
+) -> Result<(), crate::Error>
+{
+ let windowing_context = windowing_context.get()?;
+ let graphics_props = graphics_props.get()?;
+
+ let Some(display_handle) = windowing_context.display_handle() else {
+ return Ok(());
+ };
+
+ for (window_ent_id, (window, mut window_creation_attrs)) in
+ window_query.iter_with_euids()
+ {
+ tracing::debug!("Preparing window entity {window_ent_id} for use in rendering");
+
+ let mut glutin_config_template_builder =
+ glutin::config::ConfigTemplateBuilder::new();
+
+ if let Some(multisampling_sample_cnt) = graphics_props.multisampling_sample_cnt {
+ glutin_config_template_builder = glutin_config_template_builder
+ .with_multisampling(multisampling_sample_cnt);
+ }
+
+ let window_handle = match window
+ .as_ref()
+ .map(|window| unsafe {
+ windowing_context.get_window_as_handle(&window.wid())
+ })
+ .flatten()
+ .transpose()
+ {
+ Ok(window_handle) => window_handle,
+ Err(err) => {
+ tracing::error!("Failed to get window handle: {err}");
+ continue;
+ }
+ };
+
+ let (new_window_creation_attrs, gl_config) = match DisplayBuilder::new()
+ .with_window_attributes(window_creation_attrs.clone())
+ .build(
+ window_handle,
+ &display_handle,
+ glutin_config_template_builder,
+ |mut cfgs| cfgs.next(),
+ ) {
+ Ok((new_window_creation_attrs, gl_config)) => {
+ (new_window_creation_attrs, gl_config)
+ }
+ Err(GlutinCompatError::WindowRequired) => {
+ actions.add_components(window_ent_id, (CreationReady,));
+ continue;
+ }
+ Err(err) => {
+ tracing::error!("Failed to create platform graphics display: {err}");
+ continue;
+ }
+ };
+
+ *window_creation_attrs = new_window_creation_attrs;
+
+ actions.add_components(window_ent_id, (WindowGlConfig { gl_config },));
+
+ if window.is_none() {
+ actions.add_components(window_ent_id, (CreationReady,));
+ }
+ }
+
+ Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+fn init_window_graphics(
+ window_query: Query<(
+ &Window,
+ &WindowGlConfig,
+ With<TargetWindow>,
+ Without<Surface>,
+ )>,
+ windowing_context: Single<WindowingContext>,
+ graphics_props: Single<GraphicsProperties>,
+ mut graphics_ctx: Single<GraphicsContext>,
+ mut actions: Actions,
+) -> Result<(), crate::Error>
+{
+ let Ok(graphics_ctx) = graphics_ctx.get_mut() else {
+ unreachable!();
+ };
+
+ let windowing_context = windowing_context.get()?;
+ let graphics_props = graphics_props.get()?;
+
+ for (window_ent_id, (window, window_gl_config)) in window_query.iter_with_euids() {
+ tracing::info!(
+ window_entity_id=%window_ent_id,
+ window_title=&*window.title,
+ "Initializing graphics for window"
+ );
+
+ let display = window_gl_config.gl_config.display();
+
+ let window_handle =
+ match unsafe { windowing_context.get_window_as_handle(&window.wid()) }
+ .transpose()
+ {
+ Ok(Some(window_handle)) => window_handle,
+ Ok(None) => {
+ tracing::error!(
+ wid = ?window.wid(),
+ entity_id = %window_ent_id,
+ "Windowing context does not contain window"
+ );
+ continue;
+ }
+ Err(err) => {
+ tracing::error!("Failed to get window handle: {err}");
+ continue;
+ }
+ };
+
+ let Ok(window_inner_size) =
+ PhysicalSize::<NonZero<u32>>::try_convert_from(window.inner_size.clone())
+ else {
+ tracing::error!(
+ "Cannot create a surface for a window with a width/height of 0",
+ );
+ continue;
+ };
+
+ let window_surface = match unsafe {
+ display.create_window_surface(
+ &window_gl_config.gl_config,
+ &glutin::surface::SurfaceAttributesBuilder::<
+ glutin::surface::WindowSurface,
+ >::new()
+ .build(
+ window_handle.as_raw(),
+ window_inner_size.width,
+ window_inner_size.height,
+ ),
+ )
+ } {
+ Ok(window_surface) => window_surface,
+ Err(err) => {
+ tracing::error!("Failed to create window surface: {err}");
+ continue;
+ }
+ };
+
+ let gl_context = match graphics_ctx.gl_context.get_or_try_insert_with_fn(|| {
+ create_gl_context(
+ &window_gl_config.gl_config,
+ &graphics_props,
+ window_handle,
+ &window_surface,
+ )
+ }) {
+ Ok(gl_context) => gl_context,
+ Err(err) => {
+ tracing::error!("Failed to create GL context: {err}");
+ continue;
+ }
+ };
+
+ if let Err(err) = gl_context.make_current(&window_surface) {
+ tracing::error!("Failed to make GL context current: {err}");
+ continue;
+ };
+
+ if let Err(err) = gl_set_viewport(
+ &gl_context,
+ &Vec2 { x: 0, y: 0 }.into(),
+ &opengl_bindings::data_types::Dimens {
+ width: window.inner_size.width,
+ height: window.inner_size.height,
+ },
+ ) {
+ tracing::error!("Failed to set viewport: {err}");
+ }
+
+ set_enabled(
+ &gl_context,
+ Capability::DepthTest,
+ graphics_props.depth_test,
+ );
+
+ set_enabled(
+ &gl_context,
+ Capability::MultiSample,
+ graphics_props.multisampling_sample_cnt.is_some(),
+ );
+
+ if graphics_props.debug {
+ enable(&gl_context, Capability::DebugOutput);
+ enable(&gl_context, Capability::DebugOutputSynchronous);
+
+ set_debug_message_callback(&gl_context, opengl_debug_message_cb);
+
+ match set_debug_message_control(
+ &gl_context,
+ None,
+ None,
+ None,
+ &[],
+ MessageIdsAction::Disable,
+ ) {
+ Ok(()) => {}
+ Err(GlSetDebugMessageControlError::TooManyIds {
+ id_cnt: _,
+ max_id_cnt: _,
+ }) => {
+ unreachable!() // No ids are given
+ }
+ }
+ }
+
+ let surface_id = SurfaceId::new_unique();
+
+ actions.add_components(
+ window_ent_id,
+ (Surface {
+ id: surface_id,
+ size: window.inner_size.clone(),
+ },),
+ );
+
+ graphics_ctx.surfaces.insert(
+ surface_id,
+ GraphicsContextSurface {
+ window_surface,
+ size: window.inner_size.clone(),
+ },
+ );
+ }
+
+ Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+fn handle_commands(
+ mut graphics_ctx: Single<GraphicsContext>,
+ mut object_store: Single<ObjectStore>,
+ mut command_queue: Single<CommandQueue>,
+ assets: Single<Assets>,
+) -> Result<(), crate::Error>
+{
+ let Ok(graphics_ctx) = graphics_ctx.get_mut() else {
+ unreachable!();
+ };
+
+ let object_store = object_store.get_mut()?;
+ let command_queue = command_queue.get_mut()?;
+ let assets = assets.get()?;
+
+ let GraphicsContext {
+ ref gl_context,
+ ref mut surfaces,
+ ref mut backend_resources,
+ } = *graphics_ctx;
+
+ let Some(gl_context) = gl_context else {
+ return Ok(());
+ };
+
+ let mut activated_gl_shader_program: Option<(
+ ObjectId,
+ GlShaderProgram,
+ ShaderProgramMetadata,
+ )> = None;
+
+ for command in command_queue.drain() {
+ let tracing_span = tracing::info_span!(
+ "handle_cmd",
+ command = %command.get_variant_reflection().name,
+ );
+ let _tracing_span_enter = tracing_span.enter();
+
+ match command {
+ Command::RemoveSurface(surface_id) => {
+ let Some(surface) = surfaces.remove(&surface_id) else {
+ tracing::error!(surface_id=?surface_id, "Surface does not exist");
+ continue;
+ };
+
+ if surface.window_surface.is_current(gl_context.context()) {
+ if let Err(err) = gl_context.context().make_not_current_in_place() {
+ tracing::error!("Failed to make GL context not current: {err}");
+ }
+
+ if let Err(err) = gl_context.make_current_surfaceless() {
+ tracing::error!("Failed to make GL context current: {err}");
+ }
+ }
+
+ drop(surface);
+ }
+ Command::MakeCurrent(surface_id) => {
+ let Some(surface) = surfaces.get(&surface_id) else {
+ tracing::error!(surface_id=?surface_id, "Surface does not exist");
+ continue;
+ };
+
+ if let Err(err) = gl_context.make_current(&surface.window_surface) {
+ tracing::error!("Failed to make graphics context current: {err}");
+ continue;
+ }
+
+ if let Err(err) = gl_set_viewport(
+ gl_context,
+ &Vec2 { x: 0, y: 0 }.into(),
+ &opengl_bindings::data_types::Dimens {
+ width: surface.size.width,
+ height: surface.size.height,
+ },
+ ) {
+ tracing::error!("Failed to set viewport: {err}");
+ }
+ }
+ Command::SetSurfaceSize(surface_id, new_surface_size) => {
+ let Some(surface) = surfaces.get_mut(&surface_id) else {
+ tracing::error!(surface_id=?surface_id, "Surface does not exist");
+ continue;
+ };
+
+ surface.size = new_surface_size;
+
+ if !surface.window_surface.is_current(gl_context.context()) {
+ continue;
+ }
+
+ if let Err(err) = gl_set_viewport(
+ gl_context,
+ &Vec2 { x: 0, y: 0 }.into(),
+ &opengl_bindings::data_types::Dimens {
+ width: surface.size.width,
+ height: surface.size.height,
+ },
+ ) {
+ tracing::error!("Failed to set viewport: {err}");
+ }
+ }
+ Command::ClearBuffers(buffer_clear_mask) => {
+ let mut clear_mask = GlBufferClearMask::empty();
+
+ clear_mask.set(
+ GlBufferClearMask::COLOR,
+ buffer_clear_mask.contains(BufferClearMask::COLOR),
+ );
+
+ clear_mask.set(
+ GlBufferClearMask::DEPTH,
+ buffer_clear_mask.contains(BufferClearMask::DEPTH),
+ );
+
+ clear_mask.set(
+ GlBufferClearMask::STENCIL,
+ buffer_clear_mask.contains(BufferClearMask::STENCIL),
+ );
+
+ clear_buffers(gl_context, clear_mask);
+ }
+ Command::SwapBuffers(surface_id) => {
+ let Some(surface) = surfaces.get(&surface_id) else {
+ tracing::error!(surface_id=?surface_id, "Surface does not exist");
+ continue;
+ };
+
+ if let Err(err) =
+ surface.window_surface.swap_buffers(gl_context.context())
+ {
+ tracing::error!("Failed to swap buffers: {err}");
+ }
+ }
+ Command::CreateShaderProgram(object_id, shader_program) => {
+ if let Err(err) = backend_resources
+ .try_create_resource::<CreateShaderError>(
+ object_store,
+ object_id,
+ || {
+ Ok(BackendResource::Shader {
+ program: create_shader_program(
+ gl_context,
+ &shader_program,
+ )?,
+ program_metadata: shader_program.metadata().clone(),
+ bindings: IntMap::with_capacity(4),
+ })
+ },
+ )
+ {
+ tracing::error!("Failed to create shader program: {err}");
+ }
+ }
+ Command::ActivateShader(shader_object_id) => {
+ let Some(resource) = backend_resources.get_resource(
+ object_store,
+ shader_object_id,
+ ObjectKind::ShaderProgram,
+ ) else {
+ continue;
+ };
+
+ let BackendResource::Shader { program, program_metadata, bindings } =
+ resource
+ else {
+ unreachable!();
+ };
+
+ program.activate(gl_context);
+
+ for (binding_index, binding) in bindings.iter() {
+ binding.bind(gl_context, binding_index);
+ }
+
+ activated_gl_shader_program =
+ Some((shader_object_id, program.clone(), program_metadata.clone()));
+ }
+ Command::SetShaderBinding(
+ shader_object_id,
+ ShaderBinding {
+ location: binding_location,
+ value: binding_value,
+ },
+ ) => {
+ let binding = set_shader_binding(
+ backend_resources,
+ object_store,
+ gl_context,
+ shader_object_id.clone(),
+ &binding_location,
+ binding_value,
+ );
+
+ if let Some(binding) = binding {
+ if Some(shader_object_id)
+ == activated_gl_shader_program
+ .as_ref()
+ .map(|(obj_id, ..)| *obj_id)
+ {
+ binding.bind(
+ gl_context,
+ ShaderBindingIndex(binding_location.binding_index),
+ );
+ }
+ }
+ }
+ Command::CreateTexture {
+ obj_id,
+ pixel_data_format,
+ creation,
+ properties,
+ } => {
+ let kind = match &creation {
+ TextureCreation::Texture2D { .. } => TextureKind::Texture2D,
+ TextureCreation::CubeMap { .. } => TextureKind::CubeMap,
+ };
+
+ if let Err(err) = backend_resources
+ .try_create_resource::<opengl_bindings::texture::Error>(
+ object_store,
+ obj_id,
+ || {
+ let gl_texture = create_gl_texture(
+ gl_context,
+ creation,
+ pixel_data_format,
+ &properties,
+ )?;
+
+ Ok(BackendResource::Texture {
+ texture: gl_texture,
+ pixel_data_format,
+ kind,
+ })
+ },
+ )
+ {
+ tracing::error!("Failed to create texture: {err}");
+ }
+ }
+ Command::UpdateTexture {
+ obj_id,
+ pixels,
+ pixel_data_format,
+ update,
+ } => {
+ let Some(texture_resource) = backend_resources.get_resource(
+ object_store,
+ obj_id,
+ ObjectKind::Texture,
+ ) else {
+ continue;
+ };
+
+ let BackendResource::Texture {
+ texture: gl_texture,
+ pixel_data_format: tex_pixel_data_format,
+ kind: tex_metadata,
+ } = texture_resource
+ else {
+ unreachable!();
+ };
+
+ if !matches!(
+ (&update, tex_metadata),
+ (TextureUpdate::Texture2D { .. }, TextureKind::Texture2D)
+ | (TextureUpdate::CubeMap { .. }, TextureKind::CubeMap)
+ ) {
+ tracing::error!(
+ texture_object_id = ?obj_id,
+ "Texture is of incorrect kind"
+ );
+ continue;
+ }
+
+ if &pixel_data_format != tex_pixel_data_format {
+ cold_path();
+ tracing::error!(
+ texture_object_id = ?obj_id,
+ expected_pixel_data_format = ?*tex_pixel_data_format,
+ found_pixel_data_format = ?pixel_data_format,
+ "Texture has incorrect pixel data format"
+ );
+ continue;
+ }
+
+ if let Err(err) = update_texture_object(
+ gl_context,
+ gl_texture,
+ pixels,
+ pixel_data_format,
+ update,
+ ) {
+ tracing::error!("Failed to update texture object: {err}");
+ }
+ }
+ Command::RemoveTexture(object_id) => {
+ backend_resources.remove_resource(
+ object_store,
+ object_id,
+ ObjectKind::Texture,
+ gl_context,
+ );
+ }
+ Command::CreateFramebuffer(object_id) => {
+ backend_resources.try_create_resource::<Infallible>(
+ object_store,
+ object_id,
+ || {
+ Ok(BackendResource::Framebuffer {
+ framebuffer: GlFramebuffer::new(gl_context),
+ })
+ },
+ );
+ }
+ Command::RemoveFramebuffer(object_id) => {
+ backend_resources.remove_resource(
+ object_store,
+ object_id,
+ ObjectKind::Framebuffer,
+ gl_context,
+ );
+ }
+ Command::UpdateFramebufferProperties {
+ object_id,
+ properties,
+ update_flags,
+ } => {
+ update_framebuffer_properties(
+ backend_resources,
+ object_store,
+ gl_context,
+ object_id,
+ properties,
+ update_flags,
+ );
+ }
+ Command::CreateMesh { obj_id, mesh, usage: mesh_usage } => {
+ let mesh = match &mesh {
+ AssetOrValue::Asset(mesh_asset) => {
+ let Some(mesh) = assets.get(&mesh_asset) else {
+ tracing::error!(
+ asset_id=?mesh_asset.id(),
+ "Mesh asset does not exist"
+ );
+ continue;
+ };
+
+ mesh
+ }
+ AssetOrValue::Value(mesh) => mesh,
+ };
+
+ if let Err(err) = backend_resources
+ .try_create_resource::<graphics_mesh::Error>(
+ object_store,
+ obj_id,
+ || {
+ Ok(BackendResource::Mesh {
+ mesh: GraphicsMesh::new(gl_context, &mesh, mesh_usage)?,
+ vertex_attrs_updated_for_shader: None,
+ })
+ },
+ )
+ {
+ tracing::error!("Failed to create mesh: {err}");
+ }
+ }
+ Command::UpdateMesh { obj_id, mesh, usage: mesh_usage } => {
+ let Some(resource) = backend_resources.get_resource_mut(
+ object_store,
+ obj_id,
+ ObjectKind::Mesh,
+ ) else {
+ continue;
+ };
+
+ let BackendResource::Mesh { mesh: graphics_mesh, .. } = resource else {
+ unreachable!();
+ };
+
+ if let Err(err) = graphics_mesh.update(gl_context, &mesh, mesh_usage) {
+ tracing::error!("Failed to update mesh: {err}");
+ }
+ }
+ Command::RemoveMesh(object_id) => {
+ backend_resources.remove_resource(
+ object_store,
+ object_id,
+ ObjectKind::Mesh,
+ gl_context,
+ );
+ }
+ Command::DrawMesh(object_id, draw_mesh_opts) => {
+ let Some(resource) = backend_resources.get_resource_mut(
+ object_store,
+ object_id,
+ ObjectKind::Mesh,
+ ) else {
+ continue;
+ };
+
+ let BackendResource::Mesh {
+ mesh: graphics_mesh,
+ vertex_attrs_updated_for_shader,
+ } = resource
+ else {
+ unreachable!();
+ };
+
+ let Some((shader_program_obj_id, _, shader_program_metadata)) =
+ &activated_gl_shader_program
+ else {
+ tracing::error!("No shader program is activated");
+ continue;
+ };
+
+ if vertex_attrs_updated_for_shader.is_none_or(
+ |vertex_attrs_updated_for_shader| {
+ vertex_attrs_updated_for_shader != *shader_program_obj_id
+ },
+ ) {
+ let Some(shader_program_vertex_desc) =
+ &shader_program_metadata.vertex_desc
+ else {
+ tracing::error!(
+ "Activated shader program does not have a vertex description"
+ );
+ continue;
+ };
+
+ if let Err(err) = graphics_mesh.update_vertex_attrs_for_shader(
+ gl_context,
+ shader_program_vertex_desc,
+ ) {
+ tracing::error!("Cannot draw mesh: {err}");
+ continue;
+ }
+
+ *vertex_attrs_updated_for_shader = Some(*shader_program_obj_id);
+ }
+
+ if let Err(err) = draw_mesh(gl_context, graphics_mesh, &draw_mesh_opts) {
+ tracing::error!("Failed to draw mesh: {err}");
+ };
+ }
+ Command::UpdateDrawProperties(properties, update_flags) => {
+ update_draw_properties(
+ backend_resources,
+ object_store,
+ gl_context,
+ properties,
+ update_flags,
+ );
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn create_gl_context(
+ gl_config: &GlutinConfig,
+ graphics_props: &GraphicsProperties,
+ window_handle: WindowHandle<'_>,
+ surface: &GlutinSurface<GlutinWindowSurface>,
+) -> Result<MaybeCurrentContextWithFns, CreateGlContextError>
+{
+ let display = gl_config.display();
+
+ let glutin_context = unsafe {
+ display.create_context(
+ gl_config,
+ &glutin::context::ContextAttributesBuilder::new()
+ .with_debug(graphics_props.debug)
+ .build(Some(window_handle.as_raw())),
+ )
+ }
+ .map_err(CreateGlContextError::CreateGlutinContext)?;
+
+ MaybeCurrentContextWithFns::new(glutin_context, &surface)
+ .map_err(CreateGlContextError::MakeContextCurrent)
+}
+
+#[derive(Debug, thiserror::Error)]
+enum CreateGlContextError
+{
+ #[error("Glutin context creation failed")]
+ CreateGlutinContext(#[source] GlutinError),
+
+ #[error("Making GL context current failed")]
+ MakeContextCurrent(#[source] GlMakeContextCurrentError),
+}
+
+#[tracing::instrument(skip_all)]
+fn update_texture_object(
+ curr_gl_ctx: &MaybeCurrentContextWithFns,
+ gl_texture: &GlTexture,
+ pixels: Box<[u8]>,
+ pixel_data_format: TexturePixelDataFormat,
+ update: TextureUpdate,
+) -> Result<(), GlTextureError>
+{
+ match update {
+ TextureUpdate::Texture2D { size, offset } => {
+ gl_texture.store_image_2d(
+ curr_gl_ctx,
+ 0,
+ offset.into(),
+ size.into(),
+ tex_pixel_data_format_into_gl(pixel_data_format),
+ &pixels,
+ )?;
+ }
+ TextureUpdate::CubeMap { size, offset, face } => {
+ gl_texture.store_image_3d(
+ curr_gl_ctx,
+ 0,
+ Vec3 {
+ x: offset.x,
+ y: offset.y,
+ z: cube_map_texture_face_to_gl(face) as u32,
+ }
+ .into(),
+ [size.width, size.height, 1],
+ tex_pixel_data_format_into_gl(pixel_data_format),
+ &pixels,
+ )?;
+ }
+ }
+
+ gl_texture.generate_mipmap(curr_gl_ctx);
+
+ Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+fn update_draw_properties(
+ backend_resources: &BackendResourceStore,
+ object_store: &ObjectStore,
+ gl_context: &MaybeCurrentContextWithFns,
+ properties: DrawProperties,
+ update_flags: DrawPropertiesUpdateFlags,
+)
+{
+ if update_flags.contains(DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG) {
+ opengl_bindings::misc::set_polygon_mode(
+ gl_context,
+ properties.polygon_mode_config.face,
+ properties.polygon_mode_config.mode,
+ );
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::BLENDING_ENABLED) {
+ set_enabled(gl_context, Capability::Blend, properties.blending_enabled);
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::BLENDING_CONFIG) {
+ gl_blending_configure(
+ gl_context,
+ GlBlendingConfig::default()
+ .with_source_factor(blending_factor_to_gl(
+ properties.blending_config.source_factor,
+ ))
+ .with_destination_factor(blending_factor_to_gl(
+ properties.blending_config.destination_factor,
+ ))
+ .with_equation(blending_equation_to_gl(
+ properties.blending_config.equation,
+ )),
+ );
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::DEPTH_TEST_ENABLED) {
+ set_enabled(
+ gl_context,
+ Capability::DepthTest,
+ properties.depth_test_enabled,
+ );
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::DEPTH_FUNCTION) {
+ gl_set_depth_function(
+ gl_context,
+ match properties.depth_function {
+ DepthFunction::Never => GlDepthFunction::Never,
+ DepthFunction::Less => GlDepthFunction::Less,
+ DepthFunction::Equal => GlDepthFunction::Equal,
+ DepthFunction::LessOrEqual => GlDepthFunction::LessOrEqual,
+ DepthFunction::Greater => GlDepthFunction::Greater,
+ DepthFunction::NotEqual => GlDepthFunction::NotEqual,
+ DepthFunction::GreaterOrEqual => GlDepthFunction::GreaterOrEqual,
+ DepthFunction::Always => GlDepthFunction::Always,
+ },
+ );
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::SCISSOR_TEST_ENABLED) {
+ set_enabled(
+ gl_context,
+ Capability::ScissorTest,
+ properties.scissor_test_enabled,
+ );
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::SCISSOR_BOX) {
+ gl_define_scissor_box(
+ gl_context,
+ properties.scissor_box.lower_left_corner_pos.into(),
+ properties
+ .scissor_box
+ .size
+ .unwrap_or_else(|| {
+ let (_, viewport_size) = gl_get_viewport(gl_context);
+
+ Dimens::<u16> {
+ width: viewport_size
+ .width
+ .try_into()
+ .expect("Viewport width too large"),
+ height: viewport_size
+ .height
+ .try_into()
+ .expect("Viewport height too large"),
+ }
+ })
+ .into(),
+ );
+ }
+
+ if update_flags.contains(DrawPropertiesUpdateFlags::FACE_CULLING_ENABLED) {
+ set_enabled(
+ gl_context,
+ Capability::CullFace,
+ properties.face_culling_enabled,
+ );
+ }
+
+ 'if_draw_fb: {
+ if update_flags.contains(DrawPropertiesUpdateFlags::DRAW_FRAMEBUFFER) {
+ let Some(framebuffer_obj_id) = properties.draw_framebuffer else {
+ gl_bind_framebuffer(gl_context, GlFramebufferTarget::Draw, None);
+ break 'if_draw_fb;
+ };
+
+ let Some(framebuffer_resource) = backend_resources.get_resource(
+ object_store,
+ framebuffer_obj_id,
+ ObjectKind::Framebuffer,
+ ) else {
+ break 'if_draw_fb;
+ };
+
+ let BackendResource::Framebuffer { framebuffer } = framebuffer_resource
+ else {
+ unreachable!();
+ };
+
+ gl_bind_framebuffer(gl_context, GlFramebufferTarget::Draw, Some(framebuffer));
+ }
+ }
+
+ 'if_read_fb: {
+ if update_flags.contains(DrawPropertiesUpdateFlags::READ_FRAMEBUFFER) {
+ let Some(framebuffer_obj_id) = properties.read_framebuffer else {
+ gl_bind_framebuffer(gl_context, GlFramebufferTarget::Read, None);
+ break 'if_read_fb;
+ };
+
+ let Some(framebuffer_resource) = backend_resources.get_resource(
+ object_store,
+ framebuffer_obj_id,
+ ObjectKind::Framebuffer,
+ ) else {
+ break 'if_read_fb;
+ };
+
+ let BackendResource::Framebuffer { framebuffer } = framebuffer_resource
+ else {
+ unreachable!();
+ };
+
+ gl_bind_framebuffer(gl_context, GlFramebufferTarget::Read, Some(framebuffer));
+ }
+ }
+}
+
+#[tracing::instrument(skip_all)]
+fn update_framebuffer_properties(
+ backend_resources: &BackendResourceStore,
+ object_store: &ObjectStore,
+ current_context: &MaybeCurrentContextWithFns,
+ framebuffer_object_id: ObjectId,
+ properties: FramebufferProperties,
+ update_flags: FramebufferPropertiesUpdateFlags,
+)
+{
+ let Some(resource) = backend_resources.get_resource(
+ object_store,
+ framebuffer_object_id,
+ ObjectKind::Framebuffer,
+ ) else {
+ return;
+ };
+
+ let BackendResource::Framebuffer { framebuffer } = resource else {
+ unreachable!();
+ };
+
+ 'if_dt: {
+ if update_flags.contains(FramebufferPropertiesUpdateFlags::DEPTH_TEXTURE) {
+ let Some(framebuffer_depth_texture) = &properties.depth_texture else {
+ framebuffer
+ .detach_texture(current_context, GlFramebufferAttachment::Depth);
+
+ break 'if_dt;
+ };
+
+ let Some(depth_texture_resource) = backend_resources.get_resource(
+ object_store,
+ framebuffer_depth_texture.object_id,
+ ObjectKind::Texture,
+ ) else {
+ break 'if_dt;
+ };
+
+ let BackendResource::Texture { texture: depth_texture, .. } =
+ depth_texture_resource
+ else {
+ unreachable!();
+ };
+
+ framebuffer.attach_texture(
+ current_context,
+ GlFramebufferAttachment::Depth,
+ depth_texture.clone(),
+ framebuffer_depth_texture.mipmap_level,
+ );
+ }
+ }
+
+ if update_flags.contains(FramebufferPropertiesUpdateFlags::COLOR_DRAW_BUFFER) {
+ framebuffer.set_draw_buffer(
+ current_context,
+ properties.color_draw_buffer.map(|color_texture_index| {
+ GlFramebufferColorAttachment(color_texture_index.0)
+ }),
+ );
+ }
+}
+
+fn set_shader_binding<'backend_resources>(
+ backend_resources: &'backend_resources mut BackendResourceStore,
+ object_store: &ObjectStore,
+ gl_context: &MaybeCurrentContextWithFns,
+ shader_object_id: ObjectId,
+ binding_location: &ShaderBindingLocation,
+ binding_value: ShaderBindingValue,
+) -> Option<&'backend_resources BackendShaderBinding>
+{
+ if let ShaderBindingValue::Texture(texture_object_id, _) = &binding_value {
+ let gl_texture = match backend_resources.get_resource(
+ object_store,
+ *texture_object_id,
+ ObjectKind::Texture,
+ ) {
+ Some(BackendResource::Texture { texture, .. }) => texture.clone(),
+ Some(_) => unreachable!(),
+ None => return None,
+ };
+
+ let shader_bindings = match backend_resources.get_resource_mut(
+ object_store,
+ shader_object_id,
+ ObjectKind::ShaderProgram,
+ ) {
+ Some(BackendResource::Shader { bindings, .. }) => bindings,
+ Some(_) => unreachable!(),
+ None => return None,
+ };
+
+ if let Some(prev_binding) =
+ shader_bindings.remove(ShaderBindingIndex(binding_location.binding_index))
+ {
+ // TODO: Textures should probably also be handled somehow here
+ if let BackendShaderBinding::Uniform(prev_binding_uniform_buf) = prev_binding
+ {
+ prev_binding_uniform_buf.delete(gl_context);
+ }
+ }
+
+ return Some(
+ shader_bindings
+ .entry(ShaderBindingIndex(binding_location.binding_index))
+ .set_or_insert_with(|| BackendShaderBinding::Texture(gl_texture.clone())),
+ );
+
+ // if Some(shader_object_id)
+ // == activated_gl_shader_program
+ // .as_ref()
+ // .map(|(obj_id, ..)| *obj_id)
+ // {
+ // BackendShaderBinding::Texture(gl_texture.clone()).bind(
+ // gl_context,
+ // ShaderBindingIndex(binding_location.binding_index),
+ // );
+ // }
+ }
+
+ let shader_bindings = match backend_resources.get_resource_mut(
+ object_store,
+ shader_object_id,
+ ObjectKind::ShaderProgram,
+ ) {
+ Some(BackendResource::Shader { bindings, .. }) => bindings,
+ Some(_) => unreachable!(),
+ None => return None,
+ };
+
+ let binding =
+ match shader_bindings.get(ShaderBindingIndex(binding_location.binding_index)) {
+ Some(binding @ BackendShaderBinding::Uniform(_)) => binding,
+ Some(_) | None => {
+ let uniform_buf = GlBuffer::<u8>::new(gl_context);
+
+ uniform_buf
+ .init(
+ gl_context,
+ binding_location.binding_size,
+ opengl_bindings::buffer::Usage::Dynamic,
+ )
+ .unwrap();
+
+ shader_bindings
+ .entry(ShaderBindingIndex(binding_location.binding_index))
+ .set_or_insert_with(|| BackendShaderBinding::Uniform(uniform_buf))
+ }
+ };
+
+ let BackendShaderBinding::Uniform(binding_uniform_buf) = binding else {
+ unreachable!();
+ };
+
+ let fvec3_value;
+ let mut color_value = [0.0; 4];
+
+ binding_uniform_buf
+ .store_at_byte_offset(
+ gl_context,
+ binding_location.byte_offset,
+ match binding_value {
+ ShaderBindingValue::Uint(ref value) => value.as_bytes(),
+ ShaderBindingValue::Int(ref value) => value.as_bytes(),
+ ShaderBindingValue::Float(ref value) => value.as_bytes(),
+ ShaderBindingValue::FVec3(value) => {
+ fvec3_value = [value.x, value.y, value.z];
+ fvec3_value.as_bytes()
+ }
+ ShaderBindingValue::Color(value) => match value {
+ Color::Rgb(value) => {
+ color_value[..3].copy_from_slice(&[value.r, value.g, value.b]);
+
+ color_value[..3].as_bytes()
+ }
+ Color::Rgba(value) => {
+ color_value[..4]
+ .copy_from_slice(&[value.r, value.g, value.b, value.a]);
+
+ color_value[..4].as_bytes()
+ }
+ },
+ ShaderBindingValue::FMat4x4(ref value) => value.items().as_bytes(),
+ ShaderBindingValue::Texture(_, _) => unreachable!(),
+ },
+ )
+ .unwrap();
+
+ Some(binding)
+
+ // if Some(shader_object_id)
+ // == activated_gl_shader_program
+ // .as_ref()
+ // .map(|(obj_id, ..)| *obj_id)
+ // {
+ // BackendShaderBinding::Uniform(binding_uniform_buf.clone()).bind(
+ // gl_context,
+ // ShaderBindingIndex(binding_location.binding_index),
+ // );
+ // }
+}
+
+fn draw_mesh(
+ current_context: &MaybeCurrentContextWithFns,
+ graphics_mesh: &GraphicsMesh,
+ opts: &DrawMeshOptions,
+) -> Result<(), GlDrawError>
+{
+ graphics_mesh.vertex_arr.bind(current_context);
+
+ if graphics_mesh.index_buffer.is_some() {
+ VertexArray::draw_elements(
+ current_context,
+ opengl_bindings::vertex_array::DrawElementsOptions {
+ primitive_kind: PrimitiveKind::Triangles,
+ element_offset: opts.element_offset,
+ element_cnt: opts.element_cnt.unwrap_or(graphics_mesh.element_cnt),
+ vertex_offset: opts.vertex_offset,
+ },
+ )?;
+ } else {
+ VertexArray::draw_arrays(
+ current_context,
+ PrimitiveKind::Triangles,
+ opts.vertex_offset,
+ opts.element_cnt.unwrap_or(graphics_mesh.element_cnt),
+ )?;
+ }
+
+ Ok(())
+}
+
+fn tex_pixel_data_format_into_gl(
+ tex_pixel_data_format: TexturePixelDataFormat,
+) -> GlTexturePixelDataFormat
+{
+ match tex_pixel_data_format {
+ TexturePixelDataFormat::Rgb(data_type) => {
+ GlTexturePixelDataFormat::Rgb(match data_type {
+ crate::rendering::RgbTextureDataType::UnsignedByte => {
+ opengl_bindings::texture::RgbDataType::UnsignedByte
+ }
+ })
+ }
+ TexturePixelDataFormat::Srgb(data_type) => {
+ GlTexturePixelDataFormat::Srgb(match data_type {
+ crate::rendering::SrgbTextureDataType::UnsignedByte => {
+ opengl_bindings::texture::SrgbDataType::UnsignedByte
+ }
+ })
+ }
+ TexturePixelDataFormat::Rgba(data_type) => {
+ GlTexturePixelDataFormat::Rgba(match data_type {
+ crate::rendering::RgbaTextureDataType::UnsignedByte => {
+ opengl_bindings::texture::RgbaDataType::UnsignedByte
+ }
+ })
+ }
+ TexturePixelDataFormat::Srgba(data_type) => {
+ GlTexturePixelDataFormat::Srgba(match data_type {
+ crate::rendering::SrgbaTextureDataType::UnsignedByte => {
+ opengl_bindings::texture::SrgbaDataType::UnsignedByte
+ }
+ })
+ }
+ TexturePixelDataFormat::Depth(data_type) => {
+ GlTexturePixelDataFormat::DepthComponent(match data_type {
+ crate::rendering::DepthTextureDataType::Float32 => {
+ opengl_bindings::texture::DepthComponentDataType::Float32
+ }
+ })
+ }
+ }
+}
+
+fn create_gl_texture(
+ curr_gl_context: &MaybeCurrentContextWithFns,
+ texture_creation: TextureCreation,
+ texture_pixel_data_format: TexturePixelDataFormat,
+ texture_properties: &TextureProperties,
+) -> Result<GlTexture, GlTextureError>
+{
+ let gl_texture = match texture_creation {
+ TextureCreation::Texture2D { size, image } => {
+ GlTexture::builder().size(size.into()).create_2d(
+ curr_gl_context,
+ image.as_deref(),
+ tex_pixel_data_format_into_gl(texture_pixel_data_format),
+ )
+ }
+ TextureCreation::CubeMap { size, images } => {
+ GlTexture::builder().size(size.into()).create_cube_map(
+ curr_gl_context,
+ images.as_ref().map(|images| {
+ images.each_ref().map(|(face, image)| {
+ (cube_map_texture_face_to_gl(*face), &**image)
+ })
+ }),
+ tex_pixel_data_format_into_gl(texture_pixel_data_format),
+ )
+ }
+ }?;
+
+ gl_texture.set_wrap(
+ curr_gl_context,
+ texture_wrapping_to_gl(texture_properties.wrap),
+ );
+
+ gl_texture.set_magnifying_filter(
+ curr_gl_context,
+ texture_filtering_to_gl(texture_properties.magnifying_filter),
+ );
+
+ gl_texture.set_minifying_filter(
+ curr_gl_context,
+ texture_filtering_to_gl(texture_properties.minifying_filter),
+ );
+
+ Ok(gl_texture)
+}
+
+fn cube_map_texture_face_to_gl(face: CubeMapTextureFace) -> GlCubeMapTextureFace
+{
+ match face {
+ CubeMapTextureFace::PositiveX => GlCubeMapTextureFace::PositiveX,
+ CubeMapTextureFace::NegativeX => GlCubeMapTextureFace::NegativeX,
+ CubeMapTextureFace::PositiveY => GlCubeMapTextureFace::PositiveY,
+ CubeMapTextureFace::NegativeY => GlCubeMapTextureFace::NegativeY,
+ CubeMapTextureFace::PositiveZ => GlCubeMapTextureFace::PositiveZ,
+ CubeMapTextureFace::NegativeZ => GlCubeMapTextureFace::NegativeZ,
+ }
+}
+
+fn create_shader_program(
+ current_context: &MaybeCurrentContextWithFns,
+ shader_program: &ShaderProgram,
+) -> Result<GlShaderProgram, CreateShaderError>
+{
+ let shader_program_reflection = shader_program.reflection(0).expect("Not possible");
+
+ let (vs_entry_point_index, vs_entry_point_reflection) = shader_program_reflection
+ .entry_points()
+ .enumerate()
+ .find(|(_, entry_point)| entry_point.stage() == ShaderStage::Vertex)
+ .ok_or_else(|| {
+ CreateShaderError::NoShaderStageEntrypointFound(ShaderStage::Vertex)
+ })?;
+
+ let vertex_shader_entry_point_code = shader_program
+ .get_entry_point_code(vs_entry_point_index.try_into().expect(
+ "Vertex shader entry point index does not fit in 32-bit unsigned int",
+ ))
+ .map_err(|err| CreateShaderError::GetShaderEntryPointCodeFailed {
+ err,
+ stage: ShaderStage::Vertex,
+ entrypoint: vs_entry_point_reflection
+ .name()
+ .map(|name| name.to_string().into())
+ .unwrap_or("(none)".into()),
+ })?;
+
+ let (fs_entry_point_index, fs_entry_point_reflection) = shader_program_reflection
+ .entry_points()
+ .enumerate()
+ .find(|(_, entry_point)| entry_point.stage() == ShaderStage::Fragment)
+ .ok_or_else(|| {
+ CreateShaderError::NoShaderStageEntrypointFound(ShaderStage::Fragment)
+ })?;
+
+ let fragment_shader_entry_point_code = shader_program
+ .get_entry_point_code(fs_entry_point_index.try_into().expect(
+ "Fragment shader entry point index does not fit in 32-bit unsigned int",
+ ))
+ .map_err(|err| CreateShaderError::GetShaderEntryPointCodeFailed {
+ err,
+ stage: ShaderStage::Fragment,
+ entrypoint: fs_entry_point_reflection
+ .name()
+ .map(|name| name.to_string().into())
+ .unwrap_or("(none)".into()),
+ })?;
+
+ let vertex_shader = GlShader::new(current_context, ShaderKind::Vertex);
+
+ vertex_shader.set_source(
+ current_context,
+ &vertex_shader_entry_point_code.as_str().unwrap(),
+ )?;
+
+ vertex_shader.compile(current_context)?;
+
+ let fragment_shader = GlShader::new(current_context, ShaderKind::Fragment);
+
+ fragment_shader.set_source(
+ current_context,
+ &fragment_shader_entry_point_code.as_str().unwrap(),
+ )?;
+
+ fragment_shader.compile(current_context)?;
+
+ let gl_shader_program = GlShaderProgram::new(current_context);
+
+ gl_shader_program.attach(current_context, &vertex_shader);
+ gl_shader_program.attach(current_context, &fragment_shader);
+
+ gl_shader_program.link(current_context)?;
+
+ Ok(gl_shader_program)
+}
+
+#[derive(Debug, thiserror::Error)]
+enum CreateShaderError
+{
+ #[error(
+ "Failed to get code of shader program entry point {entrypoint} of stage {stage:?}"
+ )]
+ GetShaderEntryPointCodeFailed
+ {
+ #[source]
+ err: ShaderError,
+ stage: ShaderStage,
+ entrypoint: Cow<'static, str>,
+ },
+
+ #[error("No entrypoint was found for shader stage {0:?}")]
+ NoShaderStageEntrypointFound(ShaderStage),
+
+ #[error(transparent)]
+ ShaderError(#[from] GlShaderError),
+}
+
+#[tracing::instrument(skip_all)]
+fn opengl_debug_message_cb(
+ source: MessageSource,
+ ty: MessageType,
+ id: u32,
+ severity: MessageSeverity,
+ message: &str,
+)
+{
+ use std::backtrace::{Backtrace, BacktraceStatus};
+
+ use tracing::{event, Level};
+
+ macro_rules! create_event {
+ ($level: expr) => {
+ event!($level, ?source, ?ty, id, ?severity, message);
+ };
+ }
+
+ if matches!(severity, MessageSeverity::Notification) {
+ return;
+ }
+
+ match ty {
+ MessageType::Error => {
+ create_event!(Level::ERROR);
+
+ let backtrace = Backtrace::capture();
+
+ if matches!(backtrace.status(), BacktraceStatus::Captured) {
+ tracing::error!("{backtrace}");
+ // event!(Level::TRACE, "{backtrace}");
+ }
+ }
+ MessageType::Other => {
+ create_event!(Level::INFO);
+ }
+ _ => {
+ create_event!(Level::WARN);
+ }
+ };
+}
+
+#[inline]
+fn texture_wrapping_to_gl(texture_wrapping: TextureWrapping) -> GlTextureWrapping
+{
+ match texture_wrapping {
+ TextureWrapping::Repeat => GlTextureWrapping::Repeat,
+ TextureWrapping::MirroredRepeat => GlTextureWrapping::MirroredRepeat,
+ TextureWrapping::ClampToEdge => GlTextureWrapping::ClampToEdge,
+ TextureWrapping::ClampToBorder => GlTextureWrapping::ClampToBorder,
+ }
+}
+
+#[inline]
+fn texture_filtering_to_gl(texture_filtering: TextureFiltering) -> GlTextureFiltering
+{
+ match texture_filtering {
+ TextureFiltering::Linear => GlTextureFiltering::Linear,
+ TextureFiltering::Nearest => GlTextureFiltering::Nearest,
+ }
+}
+
+impl<Value: ReprC + Copy> From<Vec2<Value>> for opengl_bindings::data_types::Vec2<Value>
+{
+ fn from(vec2: Vec2<Value>) -> Self
+ {
+ Self { x: vec2.x, y: vec2.y }
+ }
+}
+
+impl<Value: ReprC + IntoBytes + Copy> From<Vec3<Value>>
+ for opengl_bindings::data_types::Vec3<Value>
+{
+ fn from(vec3: Vec3<Value>) -> Self
+ {
+ Self { x: vec3.x, y: vec3.y, z: vec3.z }
+ }
+}
+
+impl<Value: Copy> From<Dimens<Value>> for opengl_bindings::data_types::Dimens<Value>
+{
+ fn from(dimens: Dimens<Value>) -> Self
+ {
+ Self {
+ width: dimens.width,
+ height: dimens.height,
+ }
+ }
+}
+
+impl From<crate::draw_flags::PolygonMode> for opengl_bindings::misc::PolygonMode
+{
+ fn from(mode: crate::draw_flags::PolygonMode) -> Self
+ {
+ match mode {
+ crate::draw_flags::PolygonMode::Point => Self::Point,
+ crate::draw_flags::PolygonMode::Fill => Self::Fill,
+ crate::draw_flags::PolygonMode::Line => Self::Line,
+ }
+ }
+}
+
+impl From<crate::draw_flags::PolygonModeFace> for opengl_bindings::misc::PolygonModeFace
+{
+ fn from(face: crate::draw_flags::PolygonModeFace) -> Self
+ {
+ match face {
+ crate::draw_flags::PolygonModeFace::Front => Self::Front,
+ crate::draw_flags::PolygonModeFace::Back => Self::Back,
+ crate::draw_flags::PolygonModeFace::FrontAndBack => Self::FrontAndBack,
+ }
+ }
+}
+
+fn blending_factor_to_gl(blending_factor: BlendingFactor) -> GlBlendingFactor
+{
+ match blending_factor {
+ BlendingFactor::Zero => GlBlendingFactor::Zero,
+ BlendingFactor::One => GlBlendingFactor::One,
+ BlendingFactor::SrcColor => GlBlendingFactor::SrcColor,
+ BlendingFactor::OneMinusSrcColor => GlBlendingFactor::OneMinusSrcColor,
+ BlendingFactor::DstColor => GlBlendingFactor::DstColor,
+ BlendingFactor::OneMinusDstColor => GlBlendingFactor::OneMinusDstColor,
+ BlendingFactor::SrcAlpha => GlBlendingFactor::SrcAlpha,
+ BlendingFactor::OneMinusSrcAlpha => GlBlendingFactor::OneMinusSrcAlpha,
+ BlendingFactor::DstAlpha => GlBlendingFactor::DstAlpha,
+ BlendingFactor::OneMinusDstAlpha => GlBlendingFactor::OneMinusDstAlpha,
+ BlendingFactor::ConstantColor => GlBlendingFactor::ConstantColor,
+ BlendingFactor::OneMinusConstantColor => GlBlendingFactor::OneMinusConstantColor,
+ BlendingFactor::ConstantAlpha => GlBlendingFactor::ConstantAlpha,
+ BlendingFactor::OneMinusConstantAlpha => GlBlendingFactor::OneMinusConstantAlpha,
+ }
+}
+
+fn blending_equation_to_gl(blending_equation: BlendingEquation) -> GlBlendingEquation
+{
+ match blending_equation {
+ BlendingEquation::Add => GlBlendingEquation::Add,
+ BlendingEquation::Subtract => GlBlendingEquation::Subtract,
+ BlendingEquation::ReverseSubtract => GlBlendingEquation::ReverseSubtract,
+ BlendingEquation::Min => GlBlendingEquation::Min,
+ BlendingEquation::Max => GlBlendingEquation::Max,
+ }
+}
+
+trait IntMapEntryExt<'map, Value>
+{
+ fn set_or_insert_with(self, func: impl FnOnce() -> Value) -> &'map mut Value;
+}
+
+impl<'map, Key, Value> IntMapEntryExt<'map, Value> for intmap::Entry<'map, Key, Value>
+where
+ Key: intmap::IntKey,
+ Value: 'map,
+{
+ fn set_or_insert_with(self, func: impl FnOnce() -> Value) -> &'map mut Value
+ {
+ match self {
+ Self::Occupied(occupied) => {
+ let value = occupied.into_mut();
+
+ *value = func();
+
+ value
+ }
+ Self::Vacant(vacant) => vacant.insert(func()),
+ }
+ }
+}
diff --git a/engine/src/rendering/backend/opengl/glutin_compat.rs b/engine/src/rendering/backend/opengl/glutin_compat.rs
new file mode 100644
index 0000000..27f82ad
--- /dev/null
+++ b/engine/src/rendering/backend/opengl/glutin_compat.rs
@@ -0,0 +1,264 @@
+// Original file:
+// https://github.com/rust-windowing/glutin/blob/
+// 0433af9018febe0696c485ed9d66c40dad41f2d4/glutin-winit/src/lib.rs
+//
+// Copyright © 2022 Kirill Chibisov
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the “Software”), to deal
+// in the Software without restriction, including without limitation the rights to
+// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+// of the Software, and to permit persons to whom the Software is furnished to do
+// so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+//! This library provides helpers for cross-platform [`glutin`] bootstrapping
+//! with [`winit`].
+
+#![deny(rust_2018_idioms)]
+#![deny(rustdoc::broken_intra_doc_links)]
+#![deny(clippy::all)]
+#![deny(missing_debug_implementations)]
+#![deny(missing_docs)]
+#![cfg_attr(clippy, deny(warnings))]
+
+use glutin::config::{Config, ConfigTemplateBuilder};
+use glutin::display::{Display, DisplayApiPreference};
+use glutin::error::Error as GlutinError;
+use glutin::prelude::*;
+use raw_window_handle::{DisplayHandle, RawWindowHandle, WindowHandle};
+
+use crate::windowing::window::CreationAttributes as WindowCreationAttributes;
+
+#[cfg(all(not(windows), not(target_os = "macos"), not(target_os = "linux")))]
+compile_error!("Unsupported platform");
+
+#[cfg(target_family = "wasm")]
+compile_error!("Wasm targets are not supported");
+
+/// The helper to perform [`Display`] creation and OpenGL platform
+/// bootstrapping with the help of [`winit`] with little to no platform specific
+/// code.
+///
+/// This is only required for the initial setup. If you want to create
+/// additional windows just use the [`finalize_window`] function and the
+/// configuration you've used either for the original window or picked with the
+/// existing [`Display`].
+///
+/// [`winit`]: winit
+/// [`Display`]: glutin::display::Display
+#[derive(Default, Debug, Clone)]
+pub struct DisplayBuilder
+{
+ preference: ApiPreference,
+ window_attributes: WindowCreationAttributes,
+}
+
+impl DisplayBuilder
+{
+ /// Create new display builder.
+ pub fn new() -> Self
+ {
+ Default::default()
+ }
+
+ /// The preference in picking the configuration.
+ #[allow(dead_code)]
+ pub fn with_preference(mut self, preference: ApiPreference) -> Self
+ {
+ self.preference = preference;
+ self
+ }
+
+ /// The window attributes to use when building a window.
+ ///
+ /// By default no window is created.
+ pub fn with_window_attributes(
+ mut self,
+ window_creation_attrs: WindowCreationAttributes,
+ ) -> Self
+ {
+ self.window_attributes = window_creation_attrs;
+ self
+ }
+
+ /// Initialize the OpenGL platform and create a compatible window to use
+ /// with it when the [`WindowAttributes`] was passed with
+ /// [`Self::with_window_attributes()`]. It's optional, since on some
+ /// platforms like `Android` it is not available early on, so you want to
+ /// find configuration and later use it with the [`finalize_window`].
+ /// But if you don't care about such platform you can always pass
+ /// [`WindowAttributes`].
+ ///
+ /// # Api-specific
+ ///
+ /// **WGL:** - [`WindowAttributes`] **must** be passed in
+ /// [`Self::with_window_attributes()`] if modern OpenGL(ES) is desired,
+ /// otherwise only builtin functions like `glClear` will be available.
+ pub fn build<ConfigPickerFn>(
+ self,
+ window_handle: Option<WindowHandle<'_>>,
+ display_handle: &DisplayHandle<'_>,
+ template_builder: ConfigTemplateBuilder,
+ config_picker_fn: ConfigPickerFn,
+ ) -> Result<(WindowCreationAttributes, Config), Error>
+ where
+ ConfigPickerFn: FnOnce(Box<dyn Iterator<Item = Config> + '_>) -> Option<Config>,
+ {
+ // XXX with WGL backend window should be created first.
+ let raw_window_handle = if cfg!(windows) {
+ let Some(window_handle) = window_handle else {
+ return Err(Error::WindowRequired);
+ };
+
+ Some(window_handle.as_raw())
+ } else {
+ None
+ };
+
+ let gl_display =
+ create_display(display_handle, self.preference, raw_window_handle)
+ .map_err(Error::CreateDisplayFailed)?;
+
+ // XXX the native window must be passed to config picker when WGL is used
+ // otherwise very limited OpenGL features will be supported.
+ let template_builder = if let Some(raw_window_handle) =
+ raw_window_handle.filter(|_| cfg!(windows))
+ {
+ template_builder.compatible_with_native_window(raw_window_handle)
+ } else {
+ template_builder
+ };
+
+ let template = template_builder.build();
+
+ // SAFETY: The RawWindowHandle passed on the config template
+ // (when cfg(windows)) will always point to a valid object since it is
+ // derived from the window_handle argument which when Some is a WindowHandle and
+ // WindowHandles always point to a valid object
+ let gl_configs = unsafe { gl_display.find_configs(template) }
+ .map_err(Error::FindConfigsFailed)?;
+
+ let picked_gl_config =
+ config_picker_fn(gl_configs).ok_or(Error::NoConfigPicked)?;
+
+ let window_attrs = cfg_select! {
+ windows => { self.window_attributes }
+ _ => {
+ finalize_window_creation_attrs(self.window_attributes, &picked_gl_config)
+ }
+ };
+
+ Ok((window_attrs, picked_gl_config))
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("Failed to create display")]
+ CreateDisplayFailed(#[source] GlutinError),
+
+ #[error("Failed to find configs")]
+ FindConfigsFailed(#[source] GlutinError),
+
+ #[error("No config was picked by config picker function")]
+ NoConfigPicked,
+
+ #[error("Window required for building display on current platform")]
+ WindowRequired,
+}
+
+fn create_display(
+ display_handle: &DisplayHandle<'_>,
+ _api_preference: ApiPreference,
+ _raw_window_handle: Option<RawWindowHandle>,
+) -> Result<Display, GlutinError>
+{
+ let preference = cfg_select! {
+ windows => {
+ match _api_preference {
+ ApiPreference::PreferEgl => {
+ DisplayApiPreference::EglThenWgl(_raw_window_handle)
+ }
+ ApiPreference::FallbackEgl => {
+ DisplayApiPreference::WglThenEgl(_raw_window_handle)
+ }
+ }
+ }
+ target_os = "linux" => {
+ match _api_preference {
+ ApiPreference::PreferEgl => DisplayApiPreference::EglThenGlx(Box::new(
+ crate::windowing::window::platform::x11::register_xlib_error_hook,
+ )),
+ ApiPreference::FallbackEgl => DisplayApiPreference::GlxThenEgl(Box::new(
+ crate::windowing::window::platform::x11::register_xlib_error_hook,
+ )),
+ }
+ }
+ target_os = "macos" => { DisplayApiPreference::Cgl }
+ };
+
+ let handle = display_handle.as_raw();
+
+ unsafe { Ok(Display::new(handle, preference)?) }
+}
+
+/// Finalize [`Window`] creation by applying the options from the [`Config`], be
+/// aware that it could remove incompatible options from the window builder like
+/// `transparency`, when the provided config doesn't support it.
+///
+/// [`Window`]: winit::window::Window
+/// [`Config`]: glutin::config::Config
+#[cfg(not(windows))]
+fn finalize_window_creation_attrs(
+ mut attributes: WindowCreationAttributes,
+ gl_config: &Config,
+) -> WindowCreationAttributes
+{
+ // Disable transparency if the end config doesn't support it.
+ if gl_config.supports_transparency() == Some(false) {
+ attributes = attributes.with_transparent(false);
+ }
+
+ #[cfg(target_os = "linux")]
+ if let Some(x11_visual) = glutin::platform::x11::X11GlConfigExt::x11_visual(gl_config)
+ {
+ return attributes.with_x_visual_id(Some(
+ x11_visual.visual_id() as crate::windowing::window::XVisualID
+ ));
+ }
+
+ attributes
+}
+
+/// Simplified version of the [`DisplayApiPreference`] which is used to simplify
+/// cross platform window creation.
+///
+/// To learn about platform differences the [`DisplayApiPreference`] variants.
+///
+/// [`DisplayApiPreference`]: glutin::display::DisplayApiPreference
+#[allow(dead_code)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
+pub enum ApiPreference
+{
+ /// Prefer `EGL` over system provider like `GLX` and `WGL`.
+ PreferEgl,
+
+ /// Fallback to `EGL` when failed to create the system profile.
+ ///
+ /// This behavior is used by default. However consider using
+ /// [`Self::PreferEgl`] if you don't care about missing EGL features.
+ #[default]
+ FallbackEgl,
+}
diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs
new file mode 100644
index 0000000..29ae01a
--- /dev/null
+++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs
@@ -0,0 +1,285 @@
+use std::hint::cold_path;
+
+use opengl_bindings::buffer::{Buffer as GlBuffer, Usage as GlBufferUsage};
+use opengl_bindings::vertex_array::{
+ AttributeFormat as GlVertexArrayAttributeFormat,
+ BindVertexBufferError as GlVertexArrayBindVertexBufferError,
+ DataType as GlVertexArrayDataType,
+ VertexArray as GlVertexArray,
+ VertexBufferSpec as GlVertexArrayVertexBufferSpec,
+};
+use opengl_bindings::MaybeCurrentContextWithFns as GlCurrentContextWithFns;
+
+use crate::mesh::vertex_buffer::VertexAttrProperties as MeshVertexAttrProperties;
+use crate::mesh::{Mesh, VertexAttrType};
+use crate::rendering::shader::{
+ VertexDescription as ShaderVertexDescription,
+ VertexInputSemName,
+};
+use crate::rendering::MeshUsage;
+use crate::util::DisplaySlice;
+
+const VERTEX_BUF_BINDING_INDEX: u32 = 0;
+
+#[derive(Debug)]
+pub struct GraphicsMesh
+{
+ /// Vertex and index buffer has to live as long as the vertex array
+ vertex_buffer: GlBuffer<u8>,
+ vertex_attr_props: Vec<MeshVertexAttrProperties>,
+ last_used_vertex_attr_cnt: u32,
+ pub index_buffer: Option<GlBuffer<u32>>,
+ pub element_cnt: u32,
+ pub vertex_arr: GlVertexArray,
+}
+
+impl GraphicsMesh
+{
+ #[tracing::instrument(skip_all)]
+ pub fn new(
+ current_context: &GlCurrentContextWithFns,
+ mesh: &Mesh,
+ mesh_usage: MeshUsage,
+ ) -> Result<Self, Error>
+ {
+ let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage);
+
+ let vertex_arr = GlVertexArray::new(current_context);
+ let vertex_buffer = GlBuffer::new(current_context);
+
+ vertex_buffer
+ .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage)
+ .map_err(Error::StoreVerticesFailed)?;
+
+ if let Err(err) = vertex_arr.bind_vertex_buffer(
+ current_context,
+ VERTEX_BUF_BINDING_INDEX,
+ &vertex_buffer,
+ GlVertexArrayVertexBufferSpec {
+ offset: 0,
+ vertex_size: mesh.vertex_buf().vertex_size(),
+ },
+ ) {
+ match err {
+ GlVertexArrayBindVertexBufferError::OffsetValueTooLarge {
+ value: _,
+ max_value: _,
+ } => unreachable!(),
+ GlVertexArrayBindVertexBufferError::VertexSizeValueTooLarge {
+ value,
+ max_value,
+ } => {
+ panic!(
+ "Size of vertex ({}) is too large. Must be less than {max_value}",
+ value
+ );
+ }
+ }
+ }
+
+ if let Some(indices) = mesh.indices() {
+ let index_buffer = GlBuffer::new(current_context);
+
+ index_buffer
+ .store(current_context, indices, buffer_usage)
+ .map_err(Error::StoreIndicesFailed)?;
+
+ vertex_arr.bind_element_buffer(current_context, &index_buffer);
+
+ return Ok(Self {
+ vertex_buffer: vertex_buffer,
+ vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(),
+ last_used_vertex_attr_cnt: 0,
+ index_buffer: Some(index_buffer),
+ element_cnt: indices
+ .len()
+ .try_into()
+ .expect("Mesh index count does not fit into a 32-bit unsigned int"),
+ vertex_arr,
+ });
+ }
+
+ Ok(Self {
+ vertex_buffer: vertex_buffer,
+ vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(),
+ last_used_vertex_attr_cnt: 0,
+ index_buffer: None,
+ element_cnt: mesh
+ .vertex_buf()
+ .len()
+ .try_into()
+ .expect("Mesh vertex count does not fit into a 32-bit unsigned int"),
+ vertex_arr,
+ })
+ }
+
+ pub fn update(
+ &mut self,
+ current_context: &GlCurrentContextWithFns,
+ mesh: &Mesh,
+ mesh_usage: MeshUsage,
+ ) -> Result<(), Error>
+ {
+ let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage);
+
+ self.vertex_buffer
+ .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage)
+ .map_err(Error::StoreVerticesFailed)?;
+
+ if let Some(indices) = mesh.indices() {
+ let index_buffer = self
+ .index_buffer
+ .get_or_insert_with(|| GlBuffer::new(current_context));
+
+ index_buffer
+ .store(current_context, indices, buffer_usage)
+ .map_err(Error::StoreIndicesFailed)?;
+
+ self.vertex_arr
+ .bind_element_buffer(current_context, &index_buffer);
+
+ self.element_cnt = indices
+ .len()
+ .try_into()
+ .expect("Mesh index count does not fit into a 32-bit unsigned int");
+
+ return Ok(());
+ }
+
+ self.element_cnt = mesh
+ .vertex_buf()
+ .len()
+ .try_into()
+ .expect("Mesh vertex count does not fit into a 32-bit unsigned int");
+
+ Ok(())
+ }
+
+ pub fn update_vertex_attrs_for_shader(
+ &mut self,
+ curr_gl_ctx: &GlCurrentContextWithFns,
+ shader_vertex_desc: &ShaderVertexDescription,
+ ) -> Result<(), VertexAttrsUpdatingError>
+ {
+ let vertex_input_cnt = u32::try_from(shader_vertex_desc.inputs.len())
+ .expect("Shader has too many vertex inputs. Count does not fit into u32");
+
+ if self.last_used_vertex_attr_cnt > vertex_input_cnt {
+ for index in vertex_input_cnt..self.last_used_vertex_attr_cnt {
+ self.vertex_arr.disable_attrib(curr_gl_ctx, index);
+ }
+ }
+
+ let mut used_vertex_attr_cnt = 0u32;
+
+ for vertex_attr_props in &self.vertex_attr_props {
+ let Some(vertex_input_desc) =
+ shader_vertex_desc.inputs.iter().find(|vertex_input_desc| {
+ vertex_input_desc
+ .semantic_name
+ .matches_vertex_label(&vertex_attr_props.label)
+ })
+ else {
+ continue;
+ };
+
+ let attrib_index: u32 = vertex_input_desc.index.try_into().unwrap();
+
+ self.vertex_arr.enable_attrib(curr_gl_ctx, attrib_index);
+
+ self.vertex_arr.set_attrib_format(
+ curr_gl_ctx,
+ attrib_index,
+ match &vertex_attr_props.ty {
+ VertexAttrType::Float32 => GlVertexArrayAttributeFormat {
+ data_type: GlVertexArrayDataType::Float,
+ count: 1,
+ normalized: false,
+ offset: vertex_attr_props.byte_offset.try_into().unwrap(),
+ },
+ VertexAttrType::Float32Array { length } => {
+ GlVertexArrayAttributeFormat {
+ data_type: GlVertexArrayDataType::Float,
+ count: (*length).try_into().unwrap(),
+ normalized: false,
+ offset: vertex_attr_props.byte_offset.try_into().unwrap(),
+ }
+ }
+ },
+ );
+
+ self.vertex_arr.set_attrib_vertex_buf_binding(
+ curr_gl_ctx,
+ attrib_index,
+ VERTEX_BUF_BINDING_INDEX,
+ );
+
+ used_vertex_attr_cnt += 1;
+ }
+
+ self.last_used_vertex_attr_cnt = used_vertex_attr_cnt;
+
+ if used_vertex_attr_cnt as usize != shader_vertex_desc.inputs.len() {
+ cold_path();
+
+ return Err(VertexAttrsUpdatingError::MissingVertexAttrs(
+ shader_vertex_desc
+ .inputs
+ .iter()
+ .filter_map(|vertex_input_desc| {
+ if self.vertex_attr_props.iter().any(|props| {
+ vertex_input_desc
+ .semantic_name
+ .matches_vertex_label(&props.label)
+ }) {
+ return None;
+ }
+
+ Some(vertex_input_desc.semantic_name.clone())
+ })
+ .collect(),
+ ));
+ }
+
+ Ok(())
+ }
+
+ pub fn destroy(&self, curr_gl_ctx: &GlCurrentContextWithFns)
+ {
+ self.vertex_arr.delete(curr_gl_ctx);
+ self.vertex_buffer.delete(curr_gl_ctx);
+
+ if let Some(index_buffer) = &self.index_buffer {
+ index_buffer.delete(curr_gl_ctx);
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("Failed to store vertices in vertex buffer")]
+ StoreVerticesFailed(#[source] opengl_bindings::buffer::Error),
+
+ #[error("Failed to store indices in index buffer")]
+ StoreIndicesFailed(#[source] opengl_bindings::buffer::Error),
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum VertexAttrsUpdatingError
+{
+ #[error(
+ "Mesh is missing equivalent vertex attribute(s) for shader's vertex inputs: {}",
+ DisplaySlice::new(.0)
+ )]
+ MissingVertexAttrs(Vec<VertexInputSemName>),
+}
+
+fn mesh_usage_to_gl_buffer_usage(mesh_usage: MeshUsage) -> GlBufferUsage
+{
+ match mesh_usage {
+ MeshUsage::Stream => GlBufferUsage::Stream,
+ MeshUsage::Static => GlBufferUsage::Static,
+ MeshUsage::Dynamic => GlBufferUsage::Dynamic,
+ }
+}
diff --git a/engine/src/rendering/blending.rs b/engine/src/rendering/blending.rs
new file mode 100644
index 0000000..9ae2f82
--- /dev/null
+++ b/engine/src/rendering/blending.rs
@@ -0,0 +1,89 @@
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct Config
+{
+ pub source_factor: Factor,
+ pub destination_factor: Factor,
+ pub equation: Equation,
+}
+
+impl Default for Config
+{
+ fn default() -> Self
+ {
+ Self {
+ source_factor: Factor::One,
+ destination_factor: Factor::Zero,
+ equation: Equation::default(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum Factor
+{
+ /// Factor will be the RGBA color `(0,0,0,0)`
+ Zero,
+
+ /// Factor will be the RGBA color `(1,1,1,1)`
+ One,
+
+ /// Factor will be the source color
+ SrcColor,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - source color`
+ OneMinusSrcColor,
+
+ /// Factor will be the destination color
+ DstColor,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - destination color`
+ OneMinusDstColor,
+
+ /// Factor will be the alpha component of the source color.
+ SrcAlpha,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - source color alpha`
+ OneMinusSrcAlpha,
+
+ /// Factor will be the alpha component of the destination color.
+ DstAlpha,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - destination color alpha`
+ OneMinusDstAlpha,
+
+ /// Factor will be the constant color
+ ConstantColor,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - constant color`
+ OneMinusConstantColor,
+
+ /// Factor will be the alpha component of the constant color.
+ ConstantAlpha,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - constant color alpha`
+ OneMinusConstantAlpha,
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+pub enum Equation
+{
+ /// The destination color and source color is added to each other in the blend
+ /// function
+ #[default]
+ Add,
+
+ /// The destination color is subtracted from the source color in the blend function
+ Subtract,
+
+ /// The source color is subtracted from the destination color in the blend function
+ ReverseSubtract,
+
+ /// The blend function will take the component-wise minimum of the destination color
+ /// and the source color
+ Min,
+
+ /// The blend function will take the component-wise maximum of the destination color
+ /// and the source color
+ Max,
+}
diff --git a/engine/src/rendering/main_render_pass.rs b/engine/src/rendering/main_render_pass.rs
new file mode 100644
index 0000000..7364024
--- /dev/null
+++ b/engine/src/rendering/main_render_pass.rs
@@ -0,0 +1,1004 @@
+use std::path::Path;
+use std::sync::LazyLock;
+
+use ecs::actions::Actions;
+use ecs::component::local::Local;
+use ecs::error::Context as _;
+use ecs::pair::ChildOf;
+use ecs::query::term::{Traverse, TraverseUp};
+use ecs::uid::Uid;
+use ecs::Component;
+
+use crate::asset::{Assets, Label as AssetLabel};
+use crate::camera::{Active as ActiveCamera, Camera};
+use crate::color::Rgba;
+use crate::data_types::dimens::{Dimens, Dimens3};
+use crate::draw_flags::{DrawFlags, NoDraw, PolygonModeConfig};
+use crate::ecs::query::term::{With, Without};
+use crate::ecs::sole::Single;
+use crate::ecs::Query;
+use crate::error;
+use crate::image::Image;
+use crate::lighting::{
+ DirectionalLight,
+ Environmental as EnvironmentalLighting,
+ PointLight,
+};
+use crate::material::{Flags as MaterialFlags, Material};
+use crate::mesh::Mesh;
+use crate::model::{MaterialSearchResult, Model};
+use crate::projection::{ClipVolume as ProjectionClipVolume, Perspective, Projection};
+use crate::rendering::object::{Id as ObjectId, Store as ObjectStore};
+use crate::rendering::shader::cursor::{
+ BindingTextureKind as ShaderBindingTextureKind,
+ BindingValue as ShaderBindingValue,
+ Cursor as ShaderCursor,
+};
+use crate::rendering::shader::{
+ Context as ShaderContext,
+ EntrypointFlags as ShaderEntrypointFlags,
+ ModuleSource as ShaderModuleSource,
+ Program as ShaderProgram,
+};
+use crate::rendering::{
+ AssetOrValue,
+ BufferClearMask,
+ Command,
+ DepthFunction,
+ DrawMeshOptions,
+ DrawProperties,
+ DrawPropertiesUpdateFlags,
+ MeshUsage,
+ RenderPass,
+ RenderPasses,
+ RgbaTextureDataType,
+ Surface,
+ TargetWindow,
+ TextureCreation,
+ TexturePixelDataFormat,
+};
+use crate::scene::{Active as ActiveScene, Scene};
+use crate::sky_box::SkyBox;
+use crate::texture::{
+ Filtering as TextureFiltering,
+ Properties as TextureProperties,
+ Texture,
+ Wrapping as TextureWrapping,
+};
+use crate::transform::Transform;
+use crate::vector::Vec3;
+use crate::windowing::window::Window;
+
+type RenderableEntity<'a> = (
+ &'a Model,
+ Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
+ Option<&'a MaterialFlags>,
+ Option<&'a Transform>,
+ Option<&'a DrawFlags>,
+ Without<NoDraw>,
+);
+
+pub static SKY_BOX_SHADER_ASSET_LABEL: LazyLock<AssetLabel> =
+ LazyLock::new(|| AssetLabel {
+ path: Path::new("").into(),
+ name: Some("sky_box_shader".into()),
+ });
+
+pub static MAIN_3D_SHADER_ASSET_LABEL: LazyLock<AssetLabel> =
+ LazyLock::new(|| AssetLabel {
+ path: Path::new("").into(),
+ name: Some("main_3d_shader".into()),
+ });
+
+struct SkyBoxIds
+{
+ texture_object: ObjectId,
+ mesh_object: ObjectId,
+}
+
+#[derive(Debug, Default, Component)]
+pub struct MiscObjectIds
+{
+ white_1x1_tex_obj_id: Option<ObjectId>,
+}
+
+#[tracing::instrument(skip_all)]
+pub fn add_main_render_pass(
+ renderable_query: Query<RenderableEntity<'_>>,
+ camera_query: Query<(
+ &Camera,
+ &Transform,
+ &ActiveCamera,
+ Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
+ )>,
+ window_query: Query<(&Window, &Surface, With<TargetWindow>)>,
+ scene_query: Query<(
+ Option<&EnvironmentalLighting>,
+ Option<&SkyBox>,
+ Option<&SkyBoxState>,
+ With<Scene>,
+ With<ActiveScene>,
+ )>,
+ point_light_query: Query<(
+ &PointLight,
+ &Transform,
+ Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
+ )>,
+ directional_light_query: Query<(
+ &DirectionalLight,
+ Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
+ )>,
+ shader_context: Single<ShaderContext>,
+ mut assets: Single<Assets>,
+ mut render_passes: Single<RenderPasses>,
+ mut object_store: Single<ObjectStore>,
+ mut actions: Actions,
+ mut misc_object_ids: Local<MiscObjectIds>,
+) -> Result<(), crate::Error>
+{
+ let assets = assets.get_mut()?;
+ let shader_context = shader_context.get()?;
+ let render_passes = render_passes.get_mut()?;
+ let object_store = object_store.get_mut()?;
+
+ let Some((scene_ent_id, (scene_env_lighting, scene_skybox, scene_skybox_state))) =
+ scene_query.iter_with_euids().next()
+ else {
+ return Ok(());
+ };
+
+ let Some((camera, camera_transform, _)) = camera_query.iter().next() else {
+ tracing::trace!("No active camera");
+ return Ok(());
+ };
+
+ let render_pass = render_passes.passes.push_front_mut(RenderPass {
+ commands: Vec::with_capacity(30),
+ draw_properties: DrawProperties::default(),
+ });
+
+ for (model, ..) in &renderable_query {
+ add_renderable_creation_commands(assets, object_store, render_pass, &model);
+ }
+
+ let shaders =
+ match get_or_create_shaders(assets, shader_context, object_store, render_pass) {
+ Ok(ShadersCreationStatus::Ready(ready_shaders)) => ready_shaders,
+ Ok(ShadersCreationStatus::ProcessingRequired) => return Ok(()),
+ Err(err) => {
+ tracing::error!("Failed to create shaders: {err}");
+ return Ok(());
+ }
+ };
+
+ let white_1x1_tex_obj_id =
+ *misc_object_ids.white_1x1_tex_obj_id.get_or_insert_with(|| {
+ create_white_1x1_texture_object(object_store, render_pass)
+ });
+
+ let main_3d_shader_cursor = ShaderCursor::new(
+ shaders
+ .main_3d_shader_program
+ .reflection(0)
+ .context("Unable to get reflection for main 3D shader")?
+ .global_params_var_layout()
+ .ok_or_else(|| {
+ crate::Error::message(
+ "Unable to get reflection for main 3D shader's global parameters",
+ )
+ })?,
+ );
+
+ add_set_3d_shader_point_light_bindings(
+ render_pass,
+ &point_light_query,
+ shaders.main_3d_shader_obj_id,
+ &main_3d_shader_cursor,
+ )?;
+
+ add_set_3d_shader_dir_lights_bindings(
+ render_pass,
+ &directional_light_query,
+ shaders.main_3d_shader_obj_id,
+ &main_3d_shader_cursor,
+ )?;
+
+ for (window, window_surface) in &window_query {
+ render_pass
+ .commands
+ .push(Command::MakeCurrent(window_surface.id));
+
+ add_set_3d_shader_camera_bindings(
+ render_pass,
+ (&camera, &camera_transform),
+ &window,
+ shaders.main_3d_shader_obj_id,
+ &main_3d_shader_cursor,
+ )?;
+
+ let mut buf_clear_mask = BufferClearMask::DEPTH;
+
+ buf_clear_mask.set(BufferClearMask::COLOR, scene_skybox.is_none());
+
+ render_pass
+ .commands
+ .push(Command::ClearBuffers(buf_clear_mask));
+
+ for (model, material_flags, transform, draw_flags) in &renderable_query {
+ let Some(model_spec) = assets.get(&model.spec_asset) else {
+ continue;
+ };
+
+ let Some(mesh_asset) = &model_spec.mesh_asset else {
+ continue;
+ };
+
+ if assets.get(mesh_asset).is_none() {
+ continue;
+ }
+
+ let model_material = match model_spec.find_first_material(&assets) {
+ MaterialSearchResult::Found(model_material_asset)
+ if let Some(model_material) = assets.get(&model_material_asset) =>
+ {
+ model_material
+ }
+ MaterialSearchResult::Found(_) | MaterialSearchResult::NotFound => {
+ continue;
+ }
+ MaterialSearchResult::NoMaterials => &Material::builder().build(),
+ };
+
+ if model_material
+ .textures()
+ .any(|texture_asset| !assets.is_loaded_and_has_type(&texture_asset))
+ {
+ continue;
+ }
+
+ add_set_3d_shader_renderable_bindings(
+ render_pass,
+ (
+ model_material,
+ material_flags.as_deref(),
+ transform.as_deref(),
+ ),
+ scene_env_lighting.as_deref(),
+ shaders.main_3d_shader_obj_id,
+ &main_3d_shader_cursor,
+ white_1x1_tex_obj_id,
+ )?;
+
+ render_pass
+ .commands
+ .push(Command::ActivateShader(shaders.main_3d_shader_obj_id));
+
+ if let Some(draw_flags) = draw_flags.as_deref().and_then(|draw_flags| {
+ if draw_flags.polygon_mode_config != PolygonModeConfig::default() {
+ Some(draw_flags)
+ } else {
+ None
+ }
+ }) {
+ render_pass.commands.push(Command::UpdateDrawProperties(
+ DrawProperties {
+ polygon_mode_config: draw_flags.polygon_mode_config.clone(),
+ ..Default::default()
+ },
+ DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG,
+ ));
+ }
+
+ render_pass.commands.push(Command::DrawMesh(
+ ObjectId::Asset(mesh_asset.id()),
+ DrawMeshOptions::default(),
+ ));
+
+ if draw_flags.as_deref().is_some_and(|draw_flags| {
+ draw_flags.polygon_mode_config != PolygonModeConfig::default()
+ }) {
+ render_pass.commands.push(Command::UpdateDrawProperties(
+ DrawProperties {
+ polygon_mode_config: PolygonModeConfig::default(),
+ ..Default::default()
+ },
+ DrawPropertiesUpdateFlags::POLYGON_MODE_CONFIG,
+ ));
+ }
+ }
+
+ if let Some(scene_skybox) = &scene_skybox {
+ let Some(sky_box_ids) = load_sky_box(
+ scene_ent_id,
+ &scene_skybox,
+ scene_skybox_state.as_deref(),
+ assets,
+ object_store,
+ render_pass,
+ &mut actions,
+ )?
+ else {
+ continue;
+ };
+
+ add_sky_box_commands(
+ render_pass,
+ &sky_box_ids,
+ &camera,
+ &camera_transform,
+ &window,
+ shaders.sky_box_shader_obj_id,
+ shaders.sky_box_shader_program,
+ )?;
+ }
+ }
+
+ Ok(())
+}
+
+fn create_white_1x1_texture_object(
+ object_store: &mut ObjectStore,
+ render_pass: &mut RenderPass,
+) -> ObjectId
+{
+ let white_1x1_tex_obj_id = ObjectId::new_sequential();
+
+ object_store.insert_pending(white_1x1_tex_obj_id);
+
+ render_pass.commands.push(Command::CreateTexture {
+ obj_id: white_1x1_tex_obj_id,
+ pixel_data_format: TexturePixelDataFormat::Rgba(
+ RgbaTextureDataType::UnsignedByte,
+ ),
+ creation: TextureCreation::Texture2D {
+ size: Dimens { width: 1, height: 1 },
+ image: Some(
+ Image::from_color(Rgba::<u8>::white(), Dimens { width: 1, height: 1 })
+ .into_bytes()
+ .into_boxed_slice(),
+ ),
+ },
+ properties: TextureProperties::default(),
+ });
+
+ white_1x1_tex_obj_id
+}
+
+#[derive(Debug)]
+enum ShadersCreationStatus<'shader_ctx>
+{
+ ProcessingRequired,
+ Ready(ReadyShaders<'shader_ctx>),
+}
+
+#[derive(Debug)]
+struct ReadyShaders<'shader_ctx>
+{
+ main_3d_shader_obj_id: ObjectId,
+ main_3d_shader_program: &'shader_ctx ShaderProgram,
+ sky_box_shader_obj_id: ObjectId,
+ sky_box_shader_program: &'shader_ctx ShaderProgram,
+}
+
+fn get_or_create_shaders<'shader_ctx>(
+ assets: &mut Assets,
+ shader_context: &'shader_ctx ShaderContext,
+ object_store: &mut ObjectStore,
+ render_pass: &mut RenderPass,
+) -> Result<ShadersCreationStatus<'shader_ctx>, crate::Error>
+{
+ let main_3d_shader_asset = assets
+ .get_handle_to_loaded::<ShaderModuleSource>(MAIN_3D_SHADER_ASSET_LABEL.clone())
+ .or_else(|| {
+ assets.store_with_label(
+ MAIN_3D_SHADER_ASSET_LABEL.clone(),
+ ShaderModuleSource {
+ name: "main_3d_shader.slang".into(),
+ file_path: Path::new("@engine/main_3d_shader").into(),
+ source: include_str!("../../res/main_3d_shader.slang").into(),
+ link_entrypoints: ShaderEntrypointFlags::VERTEX
+ | ShaderEntrypointFlags::FRAGMENT,
+ },
+ );
+
+ None
+ });
+
+ let sky_box_shader_asset = assets
+ .get_handle_to_loaded::<ShaderModuleSource>(SKY_BOX_SHADER_ASSET_LABEL.clone())
+ .or_else(|| {
+ assets.store_with_label(
+ SKY_BOX_SHADER_ASSET_LABEL.clone(),
+ ShaderModuleSource {
+ name: "sky_box_shader.slang".into(),
+ file_path: Path::new("@engine/sky_box_shader").into(),
+ source: include_str!("../../res/sky_box_shader.slang").into(),
+ link_entrypoints: ShaderEntrypointFlags::VERTEX
+ | ShaderEntrypointFlags::FRAGMENT,
+ },
+ );
+ None
+ });
+
+ let (Some(main_3d_shader_asset), Some(sky_box_shader_asset)) =
+ (main_3d_shader_asset, sky_box_shader_asset)
+ else {
+ return Ok(ShadersCreationStatus::ProcessingRequired);
+ };
+
+ let main_3d_shader_program = shader_context.get_program(&main_3d_shader_asset.id());
+
+ let main_3d_shader_program = main_3d_shader_program.ok_or_else(|| {
+ error!("Shader context doesn't have a program for main 3D shader")
+ })?;
+
+ let main_3d_shader_obj_id = ObjectId::Asset(main_3d_shader_asset.id());
+
+ if !object_store.contains_maybe_pending_with_id(&main_3d_shader_obj_id) {
+ object_store.insert_pending(main_3d_shader_obj_id);
+
+ render_pass.commands.push(Command::CreateShaderProgram(
+ main_3d_shader_obj_id,
+ main_3d_shader_program.clone(),
+ ));
+ }
+
+ let sky_box_shader_program = shader_context.get_program(&sky_box_shader_asset.id());
+
+ let sky_box_shader_program = sky_box_shader_program.ok_or_else(|| {
+ error!("Shader context doesn't have a program for sky box shader")
+ })?;
+
+ let sky_box_shader_obj_id = ObjectId::Asset(sky_box_shader_asset.id());
+
+ if !object_store.contains_maybe_pending_with_id(&sky_box_shader_obj_id) {
+ object_store.insert_pending(sky_box_shader_obj_id);
+
+ render_pass.commands.push(Command::CreateShaderProgram(
+ sky_box_shader_obj_id,
+ sky_box_shader_program.clone(),
+ ));
+ }
+
+ Ok(ShadersCreationStatus::Ready(ReadyShaders {
+ main_3d_shader_obj_id,
+ main_3d_shader_program,
+ sky_box_shader_obj_id,
+ sky_box_shader_program,
+ }))
+}
+
+fn add_renderable_creation_commands(
+ assets: &Assets,
+ object_store: &mut ObjectStore,
+ render_pass: &mut RenderPass,
+ model: &Model,
+)
+{
+ let Some(model_spec) = assets.get(&model.spec_asset) else {
+ return;
+ };
+
+ let Some(mesh_asset) = &model_spec.mesh_asset else {
+ return;
+ };
+
+ if assets.get(mesh_asset).is_none() {
+ return;
+ }
+
+ debug_assert!(model_spec.material_names.len() <= 1);
+
+ let model_material = match model_spec.find_first_material(&assets) {
+ MaterialSearchResult::Found(model_material_asset) => {
+ let Some(model_material) = assets.get(&model_material_asset) else {
+ return;
+ };
+
+ model_material
+ }
+ MaterialSearchResult::NotFound => {
+ return;
+ }
+ MaterialSearchResult::NoMaterials => &Material::builder().build(),
+ };
+
+ for texture_asset in model_material.textures() {
+ let Some(texture) = assets.get(texture_asset) else {
+ return;
+ };
+
+ let Texture::Texture2D(texture) = texture else {
+ tracing::error!(
+ texture_asset_id = ?texture_asset.id(),
+ texture_asset_label = ?assets.get_label(texture_asset),
+ "Material texture map is not 2D"
+ );
+ return;
+ };
+
+ let texture_object_id = ObjectId::Asset(texture_asset.id());
+
+ if object_store.contains_maybe_pending_with_id(&texture_object_id) {
+ return;
+ }
+
+ let Some(tex_pixel_data_format) =
+ TexturePixelDataFormat::for_image(&texture.image)
+ else {
+ tracing::error!("No texture pixel data format is available for image");
+ return;
+ };
+
+ object_store.insert_pending(texture_object_id);
+
+ render_pass.commands.push(Command::CreateTexture {
+ obj_id: texture_object_id,
+ pixel_data_format: tex_pixel_data_format,
+ creation: TextureCreation::Texture2D {
+ size: texture.image.dimensions(),
+ image: Some(texture.image.as_bytes().to_vec().into_boxed_slice()),
+ },
+ properties: texture.properties.clone(),
+ });
+ }
+
+ if !object_store.contains_maybe_pending_with_id(&ObjectId::Asset(mesh_asset.id())) {
+ object_store.insert_pending(ObjectId::Asset(mesh_asset.id()));
+
+ render_pass.commands.push(Command::CreateMesh {
+ obj_id: ObjectId::Asset(mesh_asset.id()),
+ mesh: AssetOrValue::Asset(mesh_asset.clone()),
+ usage: MeshUsage::Static,
+ });
+ }
+}
+
+fn add_set_3d_shader_point_light_bindings(
+ render_pass: &mut RenderPass,
+ point_light_query: &Query<(
+ &PointLight,
+ &Transform,
+ Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
+ )>,
+ main_3d_shader_obj_id: ObjectId,
+ main_3d_shader_cursor: &ShaderCursor<'_>,
+) -> Result<(), crate::Error>
+{
+ let lighting_shader_cursor =
+ main_3d_shader_cursor.field("Uniforms").field("lighting");
+
+ render_pass
+ .commands
+ .reserve(1 + point_light_query.iter().count() * 6);
+
+ render_pass.commands.push(Command::SetShaderBinding(
+ main_3d_shader_obj_id,
+ lighting_shader_cursor.field("point_light_cnt").binding(
+ u32::try_from(point_light_query.iter().count())
+ .expect("Point light count does not fit in 32-bit unsigned integer")
+ .into(),
+ )?,
+ ));
+
+ for (point_light_index, (point_light, point_light_transform)) in
+ point_light_query.iter().enumerate()
+ {
+ let point_light_shader_cursor = lighting_shader_cursor
+ .field("point_lights")
+ .element(point_light_index);
+
+ let phong_shader_cursor = point_light_shader_cursor.field("phong");
+
+ let attenuation_props_shader_cursor =
+ point_light_shader_cursor.field("attenuation_props");
+
+ render_pass.commands.extend(
+ [
+ phong_shader_cursor
+ .field("diffuse")
+ .binding(point_light.diffuse.to_rgb_lossy().into())?,
+ phong_shader_cursor
+ .field("specular")
+ .binding(point_light.specular.to_rgb_lossy().into())?,
+ point_light_shader_cursor.field("position").binding(
+ (point_light_transform.position + point_light.local_position).into(),
+ )?,
+ attenuation_props_shader_cursor
+ .field("constant")
+ .binding(point_light.attenuation_params.constant.into())?,
+ attenuation_props_shader_cursor
+ .field("linear")
+ .binding(point_light.attenuation_params.linear.into())?,
+ attenuation_props_shader_cursor
+ .field("quadratic")
+ .binding(point_light.attenuation_params.quadratic.into())?,
+ ]
+ .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)),
+ );
+ }
+
+ Ok(())
+}
+
+fn add_set_3d_shader_dir_lights_bindings(
+ render_pass: &mut RenderPass,
+ directional_light_query: &Query<(
+ &DirectionalLight,
+ Traverse<(With<Scene>, With<ActiveScene>), TraverseUp, ChildOf>,
+ )>,
+ main_3d_shader_obj_id: ObjectId,
+ main_3d_shader_cursor: &ShaderCursor<'_>,
+) -> Result<(), crate::Error>
+{
+ let lighting_shader_cursor =
+ main_3d_shader_cursor.field("Uniforms").field("lighting");
+
+ render_pass
+ .commands
+ .reserve(1 + directional_light_query.iter().count() * 3);
+
+ render_pass.commands.push(Command::SetShaderBinding(
+ main_3d_shader_obj_id,
+ lighting_shader_cursor
+ .field("directional_light_cnt")
+ .binding(
+ u32::try_from(directional_light_query.iter().count())
+ .expect(
+ "Directional light count does not fit in 32-bit unsigned integer",
+ )
+ .into(),
+ )?,
+ ));
+
+ for (directional_light_index, (directional_light,)) in
+ directional_light_query.iter().enumerate()
+ {
+ let directional_light_shader_cursor = lighting_shader_cursor
+ .field("directional_lights")
+ .element(directional_light_index);
+
+ let phong_shader_cursor = directional_light_shader_cursor.field("phong");
+
+ render_pass.commands.extend(
+ [
+ phong_shader_cursor
+ .field("diffuse")
+ .binding(directional_light.diffuse.to_rgb_lossy().into())?,
+ phong_shader_cursor
+ .field("specular")
+ .binding(directional_light.specular.to_rgb_lossy().into())?,
+ directional_light_shader_cursor
+ .field("direction")
+ .binding(directional_light.direction.into())?,
+ ]
+ .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)),
+ );
+ }
+
+ Ok(())
+}
+
+fn add_set_3d_shader_camera_bindings(
+ render_pass: &mut RenderPass,
+ (camera, camera_transform): (&Camera, &Transform),
+ window: &Window,
+ main_3d_shader_obj_id: ObjectId,
+ main_3d_shader_cursor: &ShaderCursor<'_>,
+) -> Result<(), crate::Error>
+{
+ let model_3d_shader_cursor =
+ main_3d_shader_cursor.field("Uniforms").field("model_3d");
+
+ let lighting_shader_cursor =
+ main_3d_shader_cursor.field("Uniforms").field("lighting");
+
+ render_pass.commands.extend(
+ [
+ model_3d_shader_cursor
+ .field("view")
+ .binding(camera.to_view_matrix(camera_transform.position).into())?,
+ model_3d_shader_cursor.field("projection").binding(
+ camera
+ .projection
+ .to_matrix_rh(window.inner_size, ProjectionClipVolume::NegOneToOne)
+ .into(),
+ )?,
+ lighting_shader_cursor
+ .field("view_pos")
+ .binding(camera_transform.position.into())?,
+ ]
+ .into_iter()
+ .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)),
+ );
+
+ Ok(())
+}
+
+fn add_set_3d_shader_renderable_bindings(
+ render_pass: &mut RenderPass,
+ renderable: (&Material, Option<&MaterialFlags>, Option<&Transform>),
+ scene_env_lighting: Option<&EnvironmentalLighting>,
+ main_3d_shader_obj_id: ObjectId,
+ main_3d_shader_cursor: &ShaderCursor<'_>,
+ white_1x1_tex_obj_id: ObjectId,
+) -> Result<(), crate::Error>
+{
+ let (material, material_flags, transform) = renderable;
+
+ let transform = match transform.as_deref() {
+ Some(transform) => transform,
+ None => &Transform::default(),
+ };
+
+ let model_matrix = transform.to_matrix();
+ let inverted_model_matrix = model_matrix.inverse();
+
+ let material_flags = material_flags
+ .as_deref()
+ .unwrap_or(&const { MaterialFlags::builder().build() });
+
+ let env_lighting = match &scene_env_lighting {
+ Some(env_lighting) => &env_lighting,
+ None => &EnvironmentalLighting::default(),
+ };
+
+ let model_3d_shader_cursor =
+ main_3d_shader_cursor.field("Uniforms").field("model_3d");
+
+ let material_shader_cursor = main_3d_shader_cursor
+ .field("Uniforms")
+ .field("lighting")
+ .field("material");
+
+ render_pass.commands.extend(
+ [
+ model_3d_shader_cursor
+ .field("model")
+ .binding(model_matrix.into())?,
+ model_3d_shader_cursor
+ .field("model_inverted")
+ .binding(inverted_model_matrix.into())?,
+ material_shader_cursor.field("ambient").binding(
+ material_flags
+ .use_ambient_color
+ .then_some(&material.ambient)
+ .unwrap_or(&env_lighting.ambient_color)
+ .to_rgb_lossy()
+ .into(),
+ )?,
+ material_shader_cursor
+ .field("diffuse")
+ .binding(material.diffuse.to_rgb_lossy().into())?,
+ material_shader_cursor
+ .field("specular")
+ .binding(material.specular.to_rgb_lossy().into())?,
+ main_3d_shader_cursor.field("ambient_map").binding(
+ ShaderBindingValue::Texture(
+ material
+ .ambient_map
+ .as_ref()
+ .map(|ambient_map| ObjectId::Asset(ambient_map.id()))
+ .unwrap_or(white_1x1_tex_obj_id),
+ ShaderBindingTextureKind::Texture2D,
+ ),
+ )?,
+ main_3d_shader_cursor.field("diffuse_map").binding(
+ ShaderBindingValue::Texture(
+ material
+ .diffuse_map
+ .as_ref()
+ .map(|diffuse_map| ObjectId::Asset(diffuse_map.id()))
+ .unwrap_or(white_1x1_tex_obj_id),
+ ShaderBindingTextureKind::Texture2D,
+ ),
+ )?,
+ main_3d_shader_cursor.field("specular_map").binding(
+ ShaderBindingValue::Texture(
+ material
+ .specular_map
+ .as_ref()
+ .map(|specular_map| ObjectId::Asset(specular_map.id()))
+ .unwrap_or(white_1x1_tex_obj_id),
+ ShaderBindingTextureKind::Texture2D,
+ ),
+ )?,
+ material_shader_cursor
+ .field("shininess")
+ .binding(material.shininess.into())?,
+ ]
+ .into_iter()
+ .map(|binding| Command::SetShaderBinding(main_3d_shader_obj_id, binding)),
+ );
+
+ Ok(())
+}
+
+fn add_sky_box_commands(
+ render_pass: &mut RenderPass,
+ sky_box_ids: &SkyBoxIds,
+ camera: &Camera,
+ camera_transform: &Transform,
+ window: &Window,
+ sky_box_shader_obj_id: ObjectId,
+ sky_box_shader_program: &ShaderProgram,
+) -> Result<(), crate::Error>
+{
+ render_pass
+ .commands
+ .push(Command::ActivateShader(sky_box_shader_obj_id));
+
+ let sky_box_shader_cursor = ShaderCursor::new(
+ sky_box_shader_program
+ .reflection(0)
+ .unwrap()
+ .global_params_var_layout()
+ .unwrap(),
+ );
+
+ let mut view = camera.to_view_matrix(camera_transform.position);
+
+ view.translate(&Vec3 { x: 0.0, y: 0.0, z: 0.0 });
+
+ let sky_box_shader_bindings = [
+ sky_box_shader_cursor
+ .field("Uniforms")
+ .field("projection")
+ .binding(
+ (match camera.projection {
+ Projection::Perspective(_) => camera.projection.to_matrix_rh(
+ window.inner_size,
+ ProjectionClipVolume::NegOneToOne,
+ ),
+ Projection::Orthographic(_) => {
+ // A orthographic projection cannot be used for a sky box
+ Perspective::default().to_matrix_rh(
+ window.inner_size.width as f32
+ / window.inner_size.height as f32,
+ ProjectionClipVolume::NegOneToOne,
+ )
+ }
+ })
+ .into(),
+ )?,
+ sky_box_shader_cursor
+ .field("Uniforms")
+ .field("view")
+ .binding(view.into())?,
+ sky_box_shader_cursor.field("cube_texture").binding(
+ ShaderBindingValue::Texture(
+ sky_box_ids.texture_object,
+ ShaderBindingTextureKind::Cube,
+ ),
+ )?,
+ ];
+
+ for binding in sky_box_shader_bindings {
+ render_pass
+ .commands
+ .push(Command::SetShaderBinding(sky_box_shader_obj_id, binding));
+ }
+
+ render_pass.commands.push(Command::UpdateDrawProperties(
+ DrawProperties {
+ depth_function: DepthFunction::LessOrEqual,
+ ..Default::default()
+ },
+ DrawPropertiesUpdateFlags::DEPTH_FUNCTION,
+ ));
+
+ render_pass.commands.push(Command::DrawMesh(
+ sky_box_ids.mesh_object,
+ DrawMeshOptions::default(),
+ ));
+
+ Ok(())
+}
+
+fn load_sky_box(
+ scene_ent_id: Uid,
+ sky_box: &SkyBox,
+ sky_box_state: Option<&SkyBoxState>,
+ assets: &mut Assets,
+ object_store: &mut ObjectStore,
+ render_pass: &mut RenderPass,
+ actions: &mut Actions,
+) -> Result<Option<SkyBoxIds>, crate::Error>
+{
+ let mesh_object_id = match &sky_box_state {
+ Some(sky_box_state) => sky_box_state.mesh_object_id,
+ None => {
+ let sky_box_mesh =
+ Mesh::cube(Dimens3 { width: 1.0, height: 1.0, depth: 1.0 });
+
+ let mesh_object_id = ObjectId::new_sequential();
+
+ object_store.insert_pending(mesh_object_id);
+
+ render_pass.commands.push(Command::CreateMesh {
+ obj_id: mesh_object_id,
+ mesh: AssetOrValue::Value(sky_box_mesh),
+ usage: MeshUsage::Static,
+ });
+
+ mesh_object_id
+ }
+ };
+
+ let texture_object_id = match &sky_box {
+ SkyBox::AssetPerCubeMapFace(cube_map_part_assets) => {
+ if let Some(sky_box_state) = sky_box_state {
+ sky_box_state.texture_object_id
+ } else {
+ let Some(part_images) = cube_map_part_assets
+ .iter()
+ .map_while(|(face, part_texture_asset)| {
+ let part_texture = assets.get(part_texture_asset)?;
+
+ match part_texture {
+ Texture::Texture2D(part_texture) => {
+ Some((*face, &part_texture.image))
+ }
+ Texture::CubeMap(_) => {
+ tracing::warn!(
+ "Cube map texture cannot be used as cube map part"
+ );
+
+ None
+ }
+ }
+ })
+ .collect::<Vec<_>>()
+ .as_array::<6>()
+ .cloned()
+ else {
+ return Ok(None);
+ };
+
+ let texture_object_id = ObjectId::new_sequential();
+
+ object_store.insert_pending(texture_object_id);
+
+ render_pass.commands.push(Command::CreateTexture {
+ obj_id: texture_object_id,
+ pixel_data_format: TexturePixelDataFormat::for_image(
+ part_images[0].1,
+ )
+ .expect("No texture pixel data format is available for image"),
+ creation: TextureCreation::CubeMap {
+ size: part_images[0].1.dimensions(),
+ images: Some(part_images.map(|(face, image)| {
+ (face, image.as_bytes().to_vec().into_boxed_slice())
+ })),
+ },
+ properties: TextureProperties::builder()
+ .minifying_filter(TextureFiltering::Linear)
+ .magnifying_filter(TextureFiltering::Linear)
+ .wrap(TextureWrapping::ClampToEdge)
+ .build(),
+ });
+
+ texture_object_id
+ }
+ }
+ };
+
+ if sky_box_state.is_none() {
+ actions.add_components(
+ scene_ent_id,
+ (SkyBoxState { texture_object_id, mesh_object_id },),
+ );
+ }
+
+ Ok(Some(SkyBoxIds {
+ texture_object: texture_object_id,
+ mesh_object: mesh_object_id,
+ }))
+}
+
+#[derive(Debug, Component)]
+pub struct SkyBoxState
+{
+ texture_object_id: ObjectId,
+ mesh_object_id: ObjectId,
+}
diff --git a/engine/src/rendering/object.rs b/engine/src/rendering/object.rs
new file mode 100644
index 0000000..61e7ed5
--- /dev/null
+++ b/engine/src/rendering/object.rs
@@ -0,0 +1,137 @@
+use std::collections::HashMap;
+use std::fmt::Display;
+use std::sync::atomic::{AtomicU64, Ordering};
+
+use crate::asset::Id as AssetId;
+use crate::ecs::Sole;
+
+pub type RawValue = u32;
+
+/// Rendering object ID.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Id
+{
+ Asset(AssetId),
+ Sequential(SequentialId),
+}
+
+impl Id
+{
+ pub fn new_sequential() -> Self
+ {
+ static NEXT_SEQUENTIAL_ID: AtomicU64 = AtomicU64::new(0);
+
+ Self::Sequential(SequentialId(
+ NEXT_SEQUENTIAL_ID.fetch_add(1, Ordering::Relaxed),
+ ))
+ }
+
+ pub fn into_asset_id(self) -> Option<AssetId>
+ {
+ match self {
+ Self::Asset(asset_id) => Some(asset_id),
+ Self::Sequential(_) => None,
+ }
+ }
+}
+
+impl From<AssetId> for Id
+{
+ fn from(asset_id: AssetId) -> Self
+ {
+ Self::Asset(asset_id)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SequentialId(u64);
+
+/// Rendering object store.
+#[derive(Debug, Default, Sole)]
+pub struct Store
+{
+ objects: HashMap<Id, Option<Object>>,
+}
+
+impl Store
+{
+ pub fn get_obj(&self, id: &Id) -> Option<&Object>
+ {
+ self.objects.get(id).and_then(|obj| obj.as_ref())
+ }
+
+ pub fn contains_maybe_pending_with_id(&self, id: &Id) -> bool
+ {
+ self.objects.contains_key(id)
+ }
+
+ pub fn contains_non_pending_with_id(&self, id: &Id) -> bool
+ {
+ self.objects.get(id).and_then(|obj| obj.as_ref()).is_some()
+ }
+
+ pub fn insert(&mut self, id: Id, object: Object)
+ {
+ self.objects.insert(id, Some(object));
+ }
+
+ pub fn insert_pending(&mut self, id: Id)
+ {
+ self.objects.insert(id, None);
+ }
+
+ pub fn remove(&mut self, id: &Id) -> Option<Option<Object>>
+ {
+ self.objects.remove(id)
+ }
+}
+
+/// Rendering object.
+#[derive(Debug, Clone)]
+pub struct Object
+{
+ raw: RawValue,
+ kind: Kind,
+}
+
+impl Object
+{
+ pub fn from_raw(raw: RawValue, kind: Kind) -> Self
+ {
+ Self { raw, kind }
+ }
+
+ pub fn as_raw(&self) -> RawValue
+ {
+ self.raw
+ }
+
+ pub fn kind(&self) -> Kind
+ {
+ self.kind
+ }
+}
+
+/// Rendering object kind.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum Kind
+{
+ Texture,
+ ShaderProgram,
+ Mesh,
+ Framebuffer,
+}
+
+impl Display for Kind
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter.write_str(match self {
+ Self::Texture => "texture",
+ Self::ShaderProgram => "shader program",
+ Self::Mesh => "mesh",
+ Self::Framebuffer => "framebuffer",
+ })
+ }
+}
diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs
new file mode 100644
index 0000000..12fc9c1
--- /dev/null
+++ b/engine/src/rendering/shader.rs
@@ -0,0 +1,1404 @@
+use std::any::type_name;
+use std::borrow::Cow;
+use std::collections::{HashMap, HashSet};
+use std::fmt::{Debug, Display, Write};
+use std::path::Path;
+use std::str::Utf8Error;
+use std::sync::Arc;
+
+use bitflags::{bitflags, bitflags_match};
+use ecs::phase::INIT as INIT_PHASE;
+use shader_slang::{
+ Blob as SlangBlob,
+ ComponentType as SlangComponentType,
+ DebugInfoLevel as SlangDebugInfoLevel,
+ EntryPoint as SlangEntryPoint,
+ GlobalSession as SlangGlobalSession,
+ Module as SlangModule,
+ ParameterCategory as SlangParameterCategory,
+ ScalarType as SlangScalarType,
+ Session as SlangSession,
+ TypeKind as SlangTypeKind,
+};
+
+use crate::asset::{
+ Assets,
+ Event as AssetEvent,
+ Handle as AssetHandle,
+ Id as AssetId,
+ Submitter as AssetSubmitter,
+ HANDLE_ASSETS_PHASE,
+};
+use crate::builder;
+use crate::ecs::pair::ChildOf;
+use crate::ecs::phase::Phase;
+use crate::ecs::sole::Single;
+use crate::ecs::{declare_entity, pair, Sole};
+use crate::mesh::vertex_buffer::VertexLabel;
+
+pub mod cursor;
+
+pub const STD_VERTEX_INPUT_SEMANTIC_NAME_POSITION: &str = "STD_POSITION";
+pub const STD_VERTEX_INPUT_SEMANTIC_NAME_NORMAL: &str = "STD_NORMAL";
+pub const STD_VERTEX_INPUT_SEMANTIC_NAME_UV: &str = "STD_UV";
+pub const STD_VERTEX_INPUT_SEMANTIC_NAME_COLOR: &str = "STD_COLOR";
+
+/// Shader module.
+#[derive(Debug)]
+pub struct ModuleSource
+{
+ pub name: Cow<'static, str>,
+ pub file_path: Cow<'static, Path>,
+ pub source: Cow<'static, str>,
+ pub link_entrypoints: EntrypointFlags,
+}
+
+bitflags! {
+ #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
+ pub struct EntrypointFlags: usize
+ {
+ const FRAGMENT = 1 << 0;
+ const VERTEX = 1 << 1;
+ }
+}
+
+#[derive(Clone)]
+pub struct Module
+{
+ inner: SlangModule,
+}
+
+impl Module
+{
+ pub fn entry_points(&self) -> impl ExactSizeIterator<Item = EntryPoint> + use<'_>
+ {
+ self.inner
+ .entry_points()
+ .map(|entry_point| EntryPoint { inner: entry_point })
+ }
+
+ pub fn get_entry_point(&self, entry_point: &str) -> Option<EntryPoint>
+ {
+ let entry_point = self.inner.find_entry_point_by_name(entry_point)?;
+
+ Some(EntryPoint { inner: entry_point })
+ }
+
+ pub fn file_path(&self) -> &str
+ {
+ self.inner.file_path()
+ }
+}
+
+pub struct EntryPoint
+{
+ inner: SlangEntryPoint,
+}
+
+impl EntryPoint
+{
+ pub fn function(&self) -> FunctionReflection<'_>
+ {
+ FunctionReflection {
+ inner: self.inner.function_reflection(),
+ }
+ }
+}
+
+pub struct FunctionReflection<'a>
+{
+ inner: &'a shader_slang::reflection::Function,
+}
+
+impl<'a> FunctionReflection<'a>
+{
+ pub fn name(&self) -> Option<&str>
+ {
+ self.inner.name()
+ }
+}
+
+pub struct EntryPointReflection<'a>
+{
+ inner: &'a shader_slang::reflection::EntryPoint,
+}
+
+impl<'a> EntryPointReflection<'a>
+{
+ pub fn name(&self) -> Option<&str>
+ {
+ self.inner.name()
+ }
+
+ pub fn name_override(&self) -> Option<&str>
+ {
+ self.inner.name_override()
+ }
+
+ pub fn stage(&self) -> Stage
+ {
+ Stage::from_slang_stage(self.inner.stage())
+ }
+
+ pub fn parameters(&self) -> impl ExactSizeIterator<Item = VariableLayout<'a>>
+ {
+ self.inner
+ .parameters()
+ .map(|param| VariableLayout { inner: param })
+ }
+
+ pub fn var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ Some(VariableLayout { inner: self.inner.var_layout()? })
+ }
+}
+
+#[derive(Clone)]
+pub struct Program
+{
+ inner: SlangComponentType,
+ metadata: ProgramMetadata,
+}
+
+impl Program
+{
+ pub fn into_linked(self) -> Result<Program, Error>
+ {
+ let linked_program = self.inner.link()?;
+
+ Ok(Program {
+ inner: linked_program,
+ metadata: self.metadata,
+ })
+ }
+
+ pub fn metadata(&self) -> &ProgramMetadata
+ {
+ &self.metadata
+ }
+
+ pub fn get_entry_point_code(&self, entry_point_index: u32) -> Result<Blob, Error>
+ {
+ let blob = self.inner.entry_point_code(entry_point_index.into(), 0)?;
+
+ Ok(Blob { inner: blob })
+ }
+
+ pub fn reflection(&self, target: u32) -> Result<ProgramReflection<'_>, Error>
+ {
+ let reflection = self.inner.layout(target as i64)?;
+
+ Ok(ProgramReflection { inner: reflection })
+ }
+}
+
+impl Debug for Program
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ formatter
+ .debug_struct(type_name::<Self>())
+ .finish_non_exhaustive()
+ }
+}
+
+pub struct ProgramReflection<'a>
+{
+ inner: &'a shader_slang::reflection::Shader,
+}
+
+impl<'a> ProgramReflection<'a>
+{
+ pub fn get_entry_point_by_index(&self, index: u32)
+ -> Option<EntryPointReflection<'a>>
+ {
+ Some(EntryPointReflection {
+ inner: self.inner.entry_point_by_index(index)?,
+ })
+ }
+
+ pub fn get_entry_point_by_name(&self, name: &str)
+ -> Option<EntryPointReflection<'a>>
+ {
+ Some(EntryPointReflection {
+ inner: self.inner.find_entry_point_by_name(name)?,
+ })
+ }
+
+ pub fn entry_points(
+ &self,
+ ) -> impl ExactSizeIterator<Item = EntryPointReflection<'a>> + use<'a>
+ {
+ self.inner
+ .entry_points()
+ .map(|entry_point| EntryPointReflection { inner: entry_point })
+ }
+
+ pub fn global_params_type_layout(&self) -> Option<TypeLayout<'a>>
+ {
+ Some(TypeLayout {
+ inner: self.inner.global_params_type_layout()?,
+ })
+ }
+
+ pub fn global_params_var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ Some(VariableLayout {
+ inner: self.inner.global_params_var_layout()?,
+ })
+ }
+
+ pub fn get_type(&self, name: &str) -> Option<TypeReflection<'a>>
+ {
+ Some(TypeReflection {
+ inner: self.inner.find_type_by_name(name)?,
+ })
+ }
+
+ pub fn get_type_layout(&self, ty: &TypeReflection<'a>) -> Option<TypeLayout<'a>>
+ {
+ Some(TypeLayout {
+ inner: self
+ .inner
+ .type_layout(&ty.inner, shader_slang::LayoutRules::Default)?,
+ })
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct VariableLayout<'a>
+{
+ inner: &'a shader_slang::reflection::VariableLayout,
+}
+
+impl<'a> VariableLayout<'a>
+{
+ pub fn name(&self) -> Option<&'a str>
+ {
+ self.inner.name()
+ }
+
+ pub fn semantic_name(&self) -> Option<&str>
+ {
+ self.inner.semantic_name()
+ }
+
+ pub fn binding_index(&self) -> u32
+ {
+ self.inner
+ .offset(shader_slang::ParameterCategory::DescriptorTableSlot) as u32
+
+ // self.inner.binding_index()
+ }
+
+ pub fn varying_input_offset(&self) -> Option<usize>
+ {
+ if !self
+ .inner
+ .categories()
+ .any(|category| category == SlangParameterCategory::VaryingInput)
+ {
+ return None;
+ }
+
+ Some(self.inner.offset(SlangParameterCategory::VaryingInput))
+ }
+
+ pub fn binding_space(&self) -> u32
+ {
+ self.inner.binding_space()
+ }
+
+ pub fn semantic_index(&self) -> usize
+ {
+ self.inner.semantic_index()
+ }
+
+ pub fn offset(&self) -> usize
+ {
+ self.inner.offset(shader_slang::ParameterCategory::Uniform)
+ }
+
+ pub fn ty(&self) -> Option<TypeReflection<'a>>
+ {
+ self.inner.ty().map(|ty| TypeReflection { inner: ty })
+ }
+
+ pub fn type_layout(&self) -> Option<TypeLayout<'a>>
+ {
+ Some(TypeLayout { inner: self.inner.type_layout()? })
+ }
+}
+
+#[derive(Clone, Copy)]
+pub struct TypeLayout<'a>
+{
+ inner: &'a shader_slang::reflection::TypeLayout,
+}
+
+impl<'a> TypeLayout<'a>
+{
+ pub fn kind(&self) -> TypeKind
+ {
+ TypeKind::from_slang_type_kind(self.inner.kind())
+ }
+
+ pub fn scalar_type(&self) -> Option<ScalarType>
+ {
+ Some(ScalarType::from_slang_scalar_type(
+ self.inner.scalar_type()?,
+ ))
+ }
+
+ pub fn resource_shape(&self) -> Option<ResourceShape>
+ {
+ Some(ResourceShape::from_bits_retain(
+ self.inner.resource_shape()? as u32,
+ ))
+ }
+
+ pub fn get_field_by_name(&self, name: &str) -> Option<VariableLayout<'a>>
+ {
+ let index = self.inner.find_field_index_by_name(name);
+
+ if index < 0 {
+ return None;
+ }
+
+ let index = u32::try_from(index.cast_unsigned()).expect("Should not happend");
+
+ let field = self.inner.field_by_index(index)?;
+
+ Some(VariableLayout { inner: field })
+ }
+
+ pub fn parameter_category(&self) -> ParameterCategory
+ {
+ ParameterCategory::from_slang_parameter_category(self.inner.parameter_category())
+ }
+
+ pub fn binding_range_descriptor_set_index(&self, index: i64) -> i64
+ {
+ self.inner.binding_range_descriptor_set_index(index)
+ }
+
+ pub fn get_field_binding_range_offset_by_name(&self, name: &str) -> Option<u64>
+ {
+ let field_index = self.inner.find_field_index_by_name(name);
+
+ if field_index < 0 {
+ return None;
+ }
+
+ let field_binding_range_offset =
+ self.inner.field_binding_range_offset(field_index);
+
+ if field_binding_range_offset < 0 {
+ return None;
+ }
+
+ Some(field_binding_range_offset.cast_unsigned())
+ }
+
+ pub fn ty(&self) -> Option<TypeReflection<'a>>
+ {
+ self.inner.ty().map(|ty| TypeReflection { inner: ty })
+ }
+
+ pub fn fields(&self) -> FieldIter<'a>
+ {
+ FieldIter {
+ type_layout: self.clone(),
+ cnt: self.field_cnt(),
+ index: 0,
+ }
+ }
+
+ pub fn field_cnt(&self) -> u32
+ {
+ self.inner.field_count()
+ }
+
+ pub fn element_cnt(&self) -> Option<usize>
+ {
+ self.inner.element_count()
+ }
+
+ pub fn row_cnt(&self) -> Option<u32>
+ {
+ self.inner.row_count()
+ }
+
+ pub fn column_cnt(&self) -> Option<u32>
+ {
+ self.inner.column_count()
+ }
+
+ pub fn element_type_layout(&self) -> Option<TypeLayout<'a>>
+ {
+ self.inner
+ .element_type_layout()
+ .map(|type_layout| TypeLayout { inner: type_layout })
+ }
+
+ pub fn element_var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ self.inner
+ .element_var_layout()
+ .map(|var_layout| VariableLayout { inner: var_layout })
+ }
+
+ pub fn container_var_layout(&self) -> Option<VariableLayout<'a>>
+ {
+ self.inner
+ .container_var_layout()
+ .map(|var_layout| VariableLayout { inner: var_layout })
+ }
+
+ pub fn uniform_size(&self) -> Option<usize>
+ {
+ // tracing::debug!(
+ // "uniform_size: {:?} categories: {:?}",
+ // self.inner.name(),
+ // self.inner.categories().collect::<Vec<_>>(),
+ // );
+
+ if !self
+ .inner
+ .categories()
+ .any(|category| category == SlangParameterCategory::Uniform)
+ {
+ return None;
+ }
+
+ // let category = self.inner.categories().next().unwrap();
+
+ // println!(
+ // "AARGH size Category: {category:?} Category count: {}",
+ // self.inner.category_count()
+ // );
+
+ // Some(self.inner.size(category))
+
+ Some(self.inner.size(SlangParameterCategory::Uniform))
+ }
+
+ pub fn stride(&self) -> usize
+ {
+ self.inner.stride(self.inner.categories().next().unwrap())
+ }
+}
+
+pub struct FieldIter<'a>
+{
+ type_layout: TypeLayout<'a>,
+ cnt: u32,
+ index: u32,
+}
+
+impl<'a> Iterator for FieldIter<'a>
+{
+ type Item = VariableLayout<'a>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ if self.index == self.cnt {
+ return None;
+ }
+
+ let Some(field) = self.type_layout.inner.field_by_index(self.index) else {
+ unreachable!();
+ };
+
+ self.index += 1;
+
+ Some(VariableLayout { inner: field })
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>)
+ {
+ let len = (self.cnt - self.index) as usize;
+
+ (len, Some(len))
+ }
+}
+
+impl ExactSizeIterator for FieldIter<'_>
+{
+ fn len(&self) -> usize
+ {
+ (self.cnt - self.index) as usize
+ }
+}
+
+impl DoubleEndedIterator for FieldIter<'_>
+{
+ fn next_back(&mut self) -> Option<Self::Item>
+ {
+ if self.cnt == 0 || self.index == self.cnt - 1 {
+ return None;
+ }
+
+ let Some(field) = self.type_layout.inner.field_by_index(self.cnt - 1) else {
+ unreachable!();
+ };
+
+ self.cnt -= 1;
+
+ Some(VariableLayout { inner: field })
+ }
+}
+
+pub struct TypeReflection<'a>
+{
+ inner: &'a shader_slang::reflection::Type,
+}
+
+impl TypeReflection<'_>
+{
+ pub fn kind(&self) -> TypeKind
+ {
+ TypeKind::from_slang_type_kind(self.inner.kind())
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum TypeKind
+{
+ None,
+ Struct,
+ Enum,
+ Array,
+ Matrix,
+ Vector,
+ Scalar,
+ ConstantBuffer,
+ Resource,
+ SamplerState,
+ TextureBuffer,
+ ShaderStorageBuffer,
+ ParameterBlock,
+ GenericTypeParameter,
+ Interface,
+ OutputStream,
+ MeshOutput,
+ Specialized,
+ Feedback,
+ Pointer,
+ DynamicResource,
+ Count,
+}
+
+impl TypeKind
+{
+ fn from_slang_type_kind(type_kind: SlangTypeKind) -> Self
+ {
+ match type_kind {
+ SlangTypeKind::None => Self::None,
+ SlangTypeKind::Struct => Self::Struct,
+ SlangTypeKind::Enum => Self::Enum,
+ SlangTypeKind::Array => Self::Array,
+ SlangTypeKind::Matrix => Self::Matrix,
+ SlangTypeKind::Vector => Self::Vector,
+ SlangTypeKind::Scalar => Self::Scalar,
+ SlangTypeKind::ConstantBuffer => Self::ConstantBuffer,
+ SlangTypeKind::Resource => Self::Resource,
+ SlangTypeKind::SamplerState => Self::SamplerState,
+ SlangTypeKind::TextureBuffer => Self::TextureBuffer,
+ SlangTypeKind::ShaderStorageBuffer => Self::ShaderStorageBuffer,
+ SlangTypeKind::ParameterBlock => Self::ParameterBlock,
+ SlangTypeKind::GenericTypeParameter => Self::GenericTypeParameter,
+ SlangTypeKind::Interface => Self::Interface,
+ SlangTypeKind::OutputStream => Self::OutputStream,
+ SlangTypeKind::MeshOutput => Self::MeshOutput,
+ SlangTypeKind::Specialized => Self::Specialized,
+ SlangTypeKind::Feedback => Self::Feedback,
+ SlangTypeKind::Pointer => Self::Pointer,
+ SlangTypeKind::DynamicResource => Self::DynamicResource,
+ SlangTypeKind::Count => Self::Count,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+#[non_exhaustive]
+pub enum ScalarType
+{
+ None,
+ Void,
+ Bool,
+ Int32,
+ Uint32,
+ Int64,
+ Uint64,
+ Float16,
+ Float32,
+ Float64,
+ Int8,
+ Uint8,
+ Int16,
+ Uint16,
+ Intptr,
+ Uintptr,
+ Bfloat16,
+ FloatE4m3,
+ FloatE5m2,
+}
+
+impl ScalarType
+{
+ fn from_slang_scalar_type(scalar_type: SlangScalarType) -> Self
+ {
+ match scalar_type {
+ SlangScalarType::None => Self::None,
+ SlangScalarType::Void => Self::Void,
+ SlangScalarType::Bool => Self::Bool,
+ SlangScalarType::Int32 => Self::Int32,
+ SlangScalarType::Uint32 => Self::Uint32,
+ SlangScalarType::Int64 => Self::Int64,
+ SlangScalarType::Uint64 => Self::Uint64,
+ SlangScalarType::Float16 => Self::Float16,
+ SlangScalarType::Float32 => Self::Float32,
+ SlangScalarType::Float64 => Self::Float64,
+ SlangScalarType::Int8 => Self::Int8,
+ SlangScalarType::Uint8 => Self::Uint8,
+ SlangScalarType::Int16 => Self::Int16,
+ SlangScalarType::Uint16 => Self::Uint16,
+ SlangScalarType::Intptr => Self::Intptr,
+ SlangScalarType::Uintptr => Self::Uintptr,
+ SlangScalarType::Bfloat16 => Self::Bfloat16,
+ SlangScalarType::FloatE4m3 => Self::FloatE4m3,
+ SlangScalarType::FloatE5m2 => Self::FloatE5m2,
+ #[allow(unreachable_patterns)]
+ _ => unimplemented!("conversion from slang scalar type"),
+ }
+ }
+}
+
+bitflags! {
+#[derive(Debug, Copy, Clone)]
+pub struct ResourceShape: u32 {
+ const BASE = shader_slang::ResourceShape::SlangResourceBaseShapeMask as u32;
+ const NONE = shader_slang::ResourceShape::SlangResourceNone as u32;
+ const TEXTURE_1D = shader_slang::ResourceShape::SlangTexture1d as u32;
+ const TEXTURE_2D = shader_slang::ResourceShape::SlangTexture2d as u32;
+ const TEXTURE_3D = shader_slang::ResourceShape::SlangTexture3d as u32;
+ const TEXTURE_CUBE = shader_slang::ResourceShape::SlangTextureCube as u32;
+ const TEXTURE_BUFFER = shader_slang::ResourceShape::SlangTextureBuffer as u32;
+ const STRUCTURED_BUFFER = shader_slang::ResourceShape::SlangStructuredBuffer as u32;
+ const BYTE_ADDRESS_BUFFER = shader_slang::ResourceShape::SlangByteAddressBuffer as u32;
+ const UNKNOWN = shader_slang::ResourceShape::SlangResourceUnknown as u32;
+ const ACCELERATION_STRUCTURE = shader_slang::ResourceShape::SlangAccelerationStructure as u32;
+ const TEXTURE_SUBPASS = shader_slang::ResourceShape::SlangTextureSubpass as u32;
+ const EXT_SHAPE = shader_slang::ResourceShape::SlangResourceExtShapeMask as u32;
+ const TEXTURE_FEEDBACK_FLAG = shader_slang::ResourceShape::SlangTextureFeedbackFlag as u32;
+ const TEXTURE_SHADOW_FLAG = shader_slang::ResourceShape::SlangTextureShadowFlag as u32;
+ const TEXTURE_ARRAY_FLAG = shader_slang::ResourceShape::SlangTextureArrayFlag as u32;
+ const TEXTURE_MULTISAMPLE_FLAG = shader_slang::ResourceShape::SlangTextureMultisampleFlag as u32;
+ const TEXTURE_COMBINED_FLAG = shader_slang::ResourceShape::SlangTextureCombinedFlag as u32;
+ const TEXTURE_1D_ARRAY = shader_slang::ResourceShape::SlangTexture1dArray as u32;
+ const TEXTURE_2D_ARRAY = shader_slang::ResourceShape::SlangTexture2dArray as u32;
+ const TEXTURE_CUBE_ARRAY = shader_slang::ResourceShape::SlangTextureCubeArray as u32;
+ const TEXTURE_2D_MULTISAMPLE = shader_slang::ResourceShape::SlangTexture2dMultisample as u32;
+ const TEXTURE_2D_MULTISAMPLE_ARRAY = shader_slang::ResourceShape::SlangTexture2dMultisampleArray as u32;
+ const TEXTURE_SUBPASS_MULTISAMPLE = shader_slang::ResourceShape::SlangTextureSubpassMultisample as u32;
+}
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub enum ParameterCategory
+{
+ None,
+ Mixed,
+ ConstantBuffer,
+ ShaderResource,
+ UnorderedAccess,
+ VaryingInput,
+ VaryingOutput,
+ SamplerState,
+ Uniform,
+ DescriptorTableSlot,
+ SpecializationConstant,
+ PushConstantBuffer,
+ RegisterSpace,
+ Generic,
+ RayPayload,
+ HitAttributes,
+ CallablePayload,
+ ShaderRecord,
+ ExistentialTypeParam,
+ ExistentialObjectParam,
+ SubElementRegisterSpace,
+ Subpass,
+ MetalArgumentBufferElement,
+ MetalAttribute,
+ MetalPayload,
+ Count,
+}
+
+impl ParameterCategory
+{
+ fn from_slang_parameter_category(parameter_category: SlangParameterCategory) -> Self
+ {
+ match parameter_category {
+ SlangParameterCategory::None => Self::None,
+ SlangParameterCategory::Mixed => Self::Mixed,
+ SlangParameterCategory::ConstantBuffer => Self::ConstantBuffer,
+ SlangParameterCategory::ShaderResource => Self::ShaderResource,
+ SlangParameterCategory::UnorderedAccess => Self::UnorderedAccess,
+ SlangParameterCategory::VaryingInput => Self::VaryingInput,
+ SlangParameterCategory::VaryingOutput => Self::VaryingOutput,
+ SlangParameterCategory::SamplerState => Self::SamplerState,
+ SlangParameterCategory::Uniform => Self::Uniform,
+ SlangParameterCategory::DescriptorTableSlot => Self::DescriptorTableSlot,
+ SlangParameterCategory::SpecializationConstant => {
+ Self::SpecializationConstant
+ }
+ SlangParameterCategory::PushConstantBuffer => Self::PushConstantBuffer,
+ SlangParameterCategory::RegisterSpace => Self::RegisterSpace,
+ SlangParameterCategory::Generic => Self::Generic,
+ SlangParameterCategory::RayPayload => Self::RayPayload,
+ SlangParameterCategory::HitAttributes => Self::HitAttributes,
+ SlangParameterCategory::CallablePayload => Self::CallablePayload,
+ SlangParameterCategory::ShaderRecord => Self::ShaderRecord,
+ SlangParameterCategory::ExistentialTypeParam => Self::ExistentialTypeParam,
+ SlangParameterCategory::ExistentialObjectParam => {
+ Self::ExistentialObjectParam
+ }
+ SlangParameterCategory::SubElementRegisterSpace => {
+ Self::SubElementRegisterSpace
+ }
+ SlangParameterCategory::Subpass => Self::Subpass,
+ SlangParameterCategory::MetalArgumentBufferElement => {
+ Self::MetalArgumentBufferElement
+ }
+ SlangParameterCategory::MetalAttribute => Self::MetalAttribute,
+ SlangParameterCategory::MetalPayload => Self::MetalPayload,
+ SlangParameterCategory::Count => Self::Count,
+ }
+ }
+}
+
+pub struct Blob
+{
+ inner: SlangBlob,
+}
+
+impl Blob
+{
+ pub fn as_bytes(&self) -> &[u8]
+ {
+ self.inner.as_slice()
+ }
+
+ pub fn as_str(&self) -> Result<&str, Utf8Error>
+ {
+ self.inner.as_str()
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum Stage
+{
+ None,
+ Vertex,
+ Hull,
+ Domain,
+ Geometry,
+ Fragment,
+ Compute,
+ RayGeneration,
+ Intersection,
+ AnyHit,
+ ClosestHit,
+ Miss,
+ Callable,
+ Mesh,
+ Amplification,
+ Dispatch,
+ Count,
+}
+
+impl Stage
+{
+ fn from_slang_stage(stage: shader_slang::Stage) -> Self
+ {
+ match stage {
+ shader_slang::Stage::None => Self::None,
+ shader_slang::Stage::Vertex => Self::Vertex,
+ shader_slang::Stage::Hull => Self::Hull,
+ shader_slang::Stage::Domain => Self::Domain,
+ shader_slang::Stage::Geometry => Self::Geometry,
+ shader_slang::Stage::Fragment => Self::Fragment,
+ shader_slang::Stage::Compute => Self::Compute,
+ shader_slang::Stage::RayGeneration => Self::RayGeneration,
+ shader_slang::Stage::Intersection => Self::Intersection,
+ shader_slang::Stage::AnyHit => Self::AnyHit,
+ shader_slang::Stage::ClosestHit => Self::ClosestHit,
+ shader_slang::Stage::Miss => Self::Miss,
+ shader_slang::Stage::Callable => Self::Callable,
+ shader_slang::Stage::Mesh => Self::Mesh,
+ shader_slang::Stage::Amplification => Self::Amplification,
+ shader_slang::Stage::Dispatch => Self::Dispatch,
+ shader_slang::Stage::Count => Self::Count,
+ }
+ }
+}
+
+builder! {
+#[builder(name = SettingsBuilder, derives=(Debug))]
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct Settings
+{
+ link_entrypoints: EntrypointFlags,
+}
+}
+
+#[derive(Sole)]
+pub struct Context
+{
+ _global_session: SlangGlobalSession,
+ session: SlangSession,
+ modules: HashMap<AssetId, Module>,
+ programs: HashMap<AssetId, Program>,
+}
+
+impl Context
+{
+ pub fn get_module(&self, asset_id: &AssetId) -> Option<&Module>
+ {
+ self.modules.get(asset_id)
+ }
+
+ pub fn get_program(&self, asset_id: &AssetId) -> Option<&Program>
+ {
+ self.programs.get(asset_id)
+ }
+
+ #[tracing::instrument(skip_all, fields(module_file = module.file_path()))]
+ pub fn compose_into_program(
+ &self,
+ module: Module,
+ link_entrypoints: EntrypointFlags,
+ ) -> Result<Program, ComposeProgramError>
+ {
+ let entry_points = link_entrypoints
+ .iter()
+ .filter_map(|entrypoint_flag| {
+ let entrypoint_name = bitflags_match!(entrypoint_flag, {
+ EntrypointFlags::VERTEX => Some("vertex_main"),
+ EntrypointFlags::FRAGMENT => Some("fragment_main"),
+ _ => None
+ })?;
+
+ let Some(entry_point) = module.get_entry_point(entrypoint_name) else {
+ return Some(Err(ComposeProgramError::EntrypointNotFoundError {
+ entrypoint_name,
+ }));
+ };
+
+ Some(Ok(entry_point))
+ })
+ .collect::<Result<Vec<_>, _>>()?;
+
+ let components = entry_points
+ .into_iter()
+ .map(|entry_point| SlangComponentType::from(entry_point.inner))
+ .chain([SlangComponentType::from(module.inner)])
+ .collect::<Vec<_>>();
+
+ let program = self
+ .session
+ .create_composite_component_type(&components)
+ .map_err(|err| ComposeProgramError::Other(Error(err)))?;
+
+ let vertex_desc = if link_entrypoints.contains(EntrypointFlags::VERTEX) {
+ // TODO: Do not have hard coded target here
+ let Ok(program_reflection) = program.layout(0) else {
+ unreachable!();
+ };
+
+ let program_reflection = ProgramReflection { inner: program_reflection };
+
+ Some(VertexDescription::new(
+ &program_reflection
+ .get_entry_point_by_name("vertex_main")
+ .expect("Not possible"),
+ )?)
+ } else {
+ None
+ };
+
+ Ok(Program {
+ inner: program,
+ metadata: ProgramMetadata { vertex_desc },
+ })
+ }
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct ProgramMetadata
+{
+ /// If the program has a entry point in the vertex stage, this field will contain a
+ /// description of the vertex type passed to the entry point.
+ pub vertex_desc: Option<VertexDescription>,
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct VertexDescription
+{
+ pub inputs: Arc<[VertexInputDescription]>,
+}
+
+impl VertexDescription
+{
+ #[tracing::instrument(skip_all, fields(vs_entry_point_name = vs_entrypoint.name()))]
+ pub fn new(
+ vs_entrypoint: &EntryPointReflection<'_>,
+ ) -> Result<Self, VertexDescriptionError>
+ {
+ if vs_entrypoint.stage() != Stage::Vertex {
+ return Err(VertexDescriptionError::EntrypointNotInVertexStage);
+ }
+
+ let inputs = VarInputDfsIter::new(vs_entrypoint).inspect(|var_input| {
+ if var_input.type_layout.kind() != TypeKind::Struct {
+ return;
+ }
+
+ let Some(semantic_name) = var_input.var_layout.semantic_name() else {
+ return;
+ };
+
+ tracing::warn!(
+ "Semantic name '{}' of '{}' will be inherited by it's fields",
+ semantic_name,
+ var_input.var_layout.name().unwrap_or("<unnamed>")
+ );
+ });
+
+ let mut seen_inputs = HashSet::<VertexInputSemName>::new();
+
+ Ok(Self {
+ inputs: inputs
+ .filter(|var_input| var_input.type_layout.kind() != TypeKind::Struct)
+ .map(|var_input| {
+ let name = var_input.var_layout.name().unwrap_or("<unnamed>");
+
+ let semantic_name =
+ var_input.var_layout.semantic_name().ok_or_else(|| {
+ VertexDescriptionError::VertexInputMissingSemanticName {
+ name: name.to_owned(),
+ }
+ })?;
+
+ let semantic_name =
+ VertexInputSemName::from_semantic_name(semantic_name);
+
+ if seen_inputs.contains(&semantic_name) {
+ return Err(
+ VertexDescriptionError::VertexInputHasOccupiedSemanticName {
+ name: name.to_owned(),
+ },
+ );
+ }
+
+ let scalar_type = match (
+ var_input.type_layout.kind(),
+ var_input.type_layout.scalar_type(),
+ ) {
+ (TypeKind::Scalar, Some(scalar_type)) => scalar_type,
+ (TypeKind::Vector, Some(scalar_type)) => scalar_type,
+ _ => {
+ return Err(
+ VertexDescriptionError::UnsupportedVertexInputType {
+ name: name.to_owned(),
+ },
+ );
+ }
+ };
+
+ seen_inputs.insert(semantic_name.clone());
+
+ Ok(VertexInputDescription {
+ semantic_name,
+ index: var_input.index,
+ type_kind: var_input.type_layout.kind(),
+ scalar_type,
+ })
+ })
+ .collect::<Result<Vec<_>, _>>()?
+ .into(),
+ })
+ }
+}
+
+#[derive(Debug)]
+#[non_exhaustive]
+pub struct VertexInputDescription
+{
+ pub semantic_name: VertexInputSemName,
+ pub index: usize,
+ pub type_kind: TypeKind,
+ pub scalar_type: ScalarType,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+#[non_exhaustive]
+pub enum VertexInputSemName
+{
+ Position,
+ Normal,
+ Uv,
+ Color,
+ Other(Box<str>),
+}
+
+impl VertexInputSemName
+{
+ fn from_semantic_name(semantic_name: &str) -> Self
+ {
+ match semantic_name {
+ STD_VERTEX_INPUT_SEMANTIC_NAME_POSITION => Self::Position,
+ STD_VERTEX_INPUT_SEMANTIC_NAME_NORMAL => Self::Normal,
+ STD_VERTEX_INPUT_SEMANTIC_NAME_UV => Self::Uv,
+ STD_VERTEX_INPUT_SEMANTIC_NAME_COLOR => Self::Color,
+ _ => Self::Other(semantic_name.to_lowercase().into_boxed_str()),
+ }
+ }
+
+ pub fn matches_vertex_label(&self, vertex_label: &VertexLabel) -> bool
+ {
+ match (self, vertex_label) {
+ (Self::Position, VertexLabel::Position)
+ | (Self::Normal, VertexLabel::Normal)
+ | (Self::Uv, VertexLabel::Uv)
+ | (Self::Color, VertexLabel::Color) => true,
+ (Self::Other(other), VertexLabel::Other(other_vertex_label)) => {
+ **other == *other_vertex_label
+ }
+ _ => false,
+ }
+ }
+}
+
+impl Display for VertexInputSemName
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ match self {
+ Self::Position => {
+ formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_POSITION)
+ }
+ Self::Normal => formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_NORMAL),
+ Self::Uv => formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_UV),
+ Self::Color => formatter.write_str(STD_VERTEX_INPUT_SEMANTIC_NAME_COLOR),
+ Self::Other(other) => {
+ for character in other.chars() {
+ formatter.write_char(character.to_ascii_uppercase())?;
+ }
+
+ Ok(())
+ }
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum VertexDescriptionError
+{
+ #[error("Entrypoint is not in vertex stage")]
+ EntrypointNotInVertexStage,
+
+ #[error("Type of vertex input '{name}' is not supported")]
+ UnsupportedVertexInputType
+ {
+ name: String
+ },
+
+ #[error("Vertex input '{name}' is missing a semantic name")]
+ VertexInputMissingSemanticName
+ {
+ name: String
+ },
+
+ #[error(
+ "Vertex input '{name}' has a semantic name already used by another vertex input"
+ )]
+ VertexInputHasOccupiedSemanticName
+ {
+ name: String
+ },
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum ComposeProgramError
+{
+ #[error("Shader module does not have a '{entrypoint_name}' entry point")]
+ EntrypointNotFoundError
+ {
+ entrypoint_name: &'static str
+ },
+
+ #[error("Failed to create vertex description")]
+ VertexDescriptionCreationFailed(#[from] VertexDescriptionError),
+
+ #[error(transparent)]
+ Other(Error),
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error(transparent)]
+pub struct Error(#[from] shader_slang::Error);
+
+struct VarInputDfsIter<'a>
+{
+ stack: Vec<(VariableLayout<'a>, TypeLayout<'a>, usize)>,
+}
+
+impl<'a> VarInputDfsIter<'a>
+{
+ fn new(entry_point: &EntryPointReflection<'a>) -> Self
+ {
+ Self {
+ stack: entry_point
+ .parameters()
+ .map(|param| {
+ let Some(param_type_layout) = param.type_layout() else {
+ // I do not know in which cases type_layout can return None
+ unimplemented!();
+ };
+
+ (param, param_type_layout, 0)
+ })
+ .collect(),
+ }
+ }
+}
+
+impl<'a> Iterator for VarInputDfsIter<'a>
+{
+ type Item = VarInput<'a>;
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let (var_layout, type_layout, index) = loop {
+ let (var_layout, type_layout, acc_offset) = self.stack.pop()?;
+
+ if let Some(offset) = var_layout.varying_input_offset() {
+ break (var_layout, type_layout, acc_offset + offset);
+ }
+ };
+
+ if type_layout.kind() == TypeKind::Struct {
+ self.stack.extend(type_layout.fields().map(|field| {
+ let Some(field_type_layout) = field.type_layout() else {
+ // I do not know in which cases type_layout can return
+ // None
+ unimplemented!();
+ };
+
+ (field, field_type_layout, index)
+ }));
+ }
+
+ Some(VarInput { var_layout, type_layout, index })
+ }
+}
+
+struct VarInput<'a>
+{
+ var_layout: VariableLayout<'a>,
+ type_layout: TypeLayout<'a>,
+ index: usize,
+}
+
+fn import_slang_asset(
+ asset_submitter: &mut AssetSubmitter<'_>,
+ file_path: &Path,
+ settings: Option<&'_ Settings>,
+) -> Result<(), ImportError>
+{
+ let file_name = file_path
+ .file_name()
+ .ok_or(ImportError::NoPathFileName)?
+ .to_str()
+ .ok_or(ImportError::PathFileNameNotUtf8)?;
+
+ let file_path_canonicalized = file_path
+ .canonicalize()
+ .map_err(ImportError::CanonicalizePathFailed)?;
+
+ asset_submitter.submit_store(ModuleSource {
+ name: file_name.to_owned().into(),
+ file_path: file_path_canonicalized.into(),
+ source: std::fs::read_to_string(file_path)
+ .map_err(ImportError::ReadFileFailed)?
+ .into(),
+ link_entrypoints: settings
+ .map(|settings| settings.link_entrypoints)
+ .unwrap_or_default(),
+ });
+
+ Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+enum ImportError
+{
+ #[error("Failed to read file")]
+ ReadFileFailed(#[source] std::io::Error),
+
+ #[error("Asset path does not have a file name")]
+ NoPathFileName,
+
+ #[error("Asset path file name is not valid UTF8")]
+ PathFileNameNotUtf8,
+
+ #[error("Failed to canonicalize asset path")]
+ CanonicalizePathFailed(#[source] std::io::Error),
+}
+
+declare_entity! {
+pub IMPORT_SHADERS_PHASE: (Phase, pair!(ChildOf, { *HANDLE_ASSETS_PHASE }));
+}
+
+pub(super) fn prepare(collector: &mut crate::ecs::extension::Collector<'_>)
+{
+ let Some(global_session) = SlangGlobalSession::new() else {
+ tracing::error!("Unable to create global shader-slang session");
+ return;
+ };
+
+ let session_options = shader_slang::CompilerOptions::default()
+ .optimization(shader_slang::OptimizationLevel::None)
+ .matrix_layout_row(true)
+ .debug_information(SlangDebugInfoLevel::Maximal)
+ .no_mangle(true);
+
+ let target_desc = shader_slang::TargetDesc::default()
+ .format(shader_slang::CompileTarget::Glsl)
+ // .format(shader_slang::CompileTarget::Spirv)
+ .profile(global_session.find_profile("glsl_330"));
+ // .profile(global_session.find_profile("spirv_1_5"));
+
+ let targets = [target_desc];
+
+ let session_desc = shader_slang::SessionDesc::default()
+ .targets(&targets)
+ .search_paths(&[""])
+ .options(&session_options);
+
+ let Some(session) = global_session.create_session(&session_desc) else {
+ tracing::error!("Failed to create shader-slang session");
+ return;
+ };
+
+ collector
+ .add_sole(Context {
+ _global_session: global_session,
+ session,
+ modules: HashMap::new(),
+ programs: HashMap::new(),
+ })
+ .ok();
+
+ collector.spawn_declared_entity(&IMPORT_SHADERS_PHASE);
+
+ collector.add_system(*INIT_PHASE, initialize);
+ collector.add_system(*IMPORT_SHADERS_PHASE, load_modules);
+}
+
+fn initialize(mut assets: Single<Assets>) -> Result<(), crate::Error>
+{
+ let assets = assets.get_mut()?;
+
+ assets.set_importer::<_, _>(["slang"], import_slang_asset);
+
+ Ok(())
+}
+
+#[tracing::instrument(skip_all)]
+fn load_modules(
+ mut context: Single<Context>,
+ assets: Single<Assets>,
+) -> Result<(), crate::Error>
+{
+ let Ok(context) = context.get_mut() else {
+ unreachable!();
+ };
+
+ let assets = assets.get()?;
+
+ for AssetEvent::Stored(asset_id, asset_label) in assets.events().last_tick_events() {
+ let asset_handle = AssetHandle::<ModuleSource>::from_id(*asset_id);
+
+ if !assets.is_loaded_and_has_type(&asset_handle) {
+ continue;
+ }
+
+ let Some(module_source) = assets.get(&asset_handle) else {
+ unreachable!();
+ };
+
+ tracing::debug!(asset_label=?asset_label, "Loading shader module");
+
+ let module = match load_module(&context.session, module_source) {
+ Ok(module) => module,
+ Err(err) => {
+ tracing::error!("Failed to load shader module: {err}");
+ continue;
+ }
+ };
+
+ context.modules.insert(*asset_id, module.clone());
+
+ if !module_source.link_entrypoints.is_empty() {
+ assert!(context.programs.get(asset_id).is_none());
+
+ let shader_program = match context
+ .compose_into_program(module, module_source.link_entrypoints)
+ {
+ Ok(shader_program) => shader_program,
+ Err(err) => {
+ tracing::error!(
+ "Failed to compose shader into program: {:#}",
+ crate::Error::new(err)
+ );
+ continue;
+ }
+ };
+
+ let linked_shader_program = match shader_program.into_linked() {
+ Ok(linked_shader_program) => linked_shader_program,
+ Err(err) => {
+ tracing::error!("Failed to link shader: {err}");
+ continue;
+ }
+ };
+
+ context.programs.insert(*asset_id, linked_shader_program);
+ }
+ }
+
+ Ok(())
+}
+
+fn load_module(
+ session: &SlangSession,
+ module_source: &ModuleSource,
+) -> Result<Module, Error>
+{
+ let module = session.load_module_from_source_string(
+ &module_source.name,
+ &module_source.file_path.to_string_lossy(),
+ &module_source.source,
+ )?;
+
+ Ok(Module { inner: module })
+}
diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs
new file mode 100644
index 0000000..49f6b47
--- /dev/null
+++ b/engine/src/rendering/shader/cursor.rs
@@ -0,0 +1,372 @@
+use std::borrow::Cow;
+use std::fmt::Display;
+use std::hint::cold_path;
+
+use circular_buffer::FixedCircularBuffer;
+
+use crate::color::{Color, Rgb, Rgba};
+use crate::data_types::matrix::Matrix;
+use crate::data_types::vector::Vec3;
+use crate::rendering::object::Id as RenderingObjectId;
+use crate::rendering::shader::{
+ ResourceShape,
+ ScalarType,
+ TypeKind,
+ TypeLayout,
+ VariableLayout,
+};
+
+/// Shader cursor
+#[derive(Clone)]
+pub struct Cursor<'a>
+{
+ type_layout: TypeLayout<'a>,
+ binding_location: BindingLocation,
+ location_path: LocationPath,
+}
+
+impl<'a> Cursor<'a>
+{
+ pub fn new(var_layout: VariableLayout<'a>) -> Self
+ {
+ let binding_location = BindingLocation {
+ binding_index: var_layout.binding_index(),
+ binding_size: 0,
+ byte_offset: var_layout.offset(),
+ };
+
+ Self {
+ type_layout: var_layout.type_layout().unwrap(),
+ binding_location,
+ location_path: LocationPath::default(),
+ }
+ }
+
+ pub fn field(&self, name: impl Into<Cow<'static, str>>) -> Self
+ {
+ let name = name.into();
+
+ let Some(field_var_layout) = self.type_layout.get_field_by_name(name.as_ref())
+ else {
+ panic!("Field '{name}' does not exist");
+ };
+
+ let field_type_kind = field_var_layout.ty().unwrap().kind();
+
+ let (new_var_layout, binding_index_offset) = match field_type_kind {
+ TypeKind::ConstantBuffer => {
+ let elem_var_layout = field_var_layout
+ .type_layout()
+ .expect("Constant buffer field has no type layout")
+ .element_var_layout()
+ .expect(
+ "Constant buffer field type layout has no element var layout",
+ );
+
+ (
+ elem_var_layout,
+ field_var_layout.binding_index() + elem_var_layout.binding_index(),
+ )
+ }
+ TypeKind::Array
+ | TypeKind::Matrix
+ | TypeKind::Scalar
+ | TypeKind::Vector
+ | TypeKind::Struct
+ | TypeKind::Resource => (field_var_layout, field_var_layout.binding_index()),
+ type_kind => unimplemented!("Type kind {type_kind:?} is not yet supported"),
+ };
+
+ let mut location_path = self.location_path.clone();
+
+ location_path.push(Location::Field(name));
+
+ Self {
+ type_layout: new_var_layout.type_layout().unwrap(),
+ binding_location: BindingLocation {
+ binding_index: self.binding_location.binding_index + binding_index_offset,
+ binding_size: if field_type_kind == TypeKind::ConstantBuffer {
+ new_var_layout
+ .type_layout()
+ .unwrap()
+ .uniform_size()
+ .unwrap()
+ } else {
+ self.binding_location.binding_size
+ },
+ byte_offset: self.binding_location.byte_offset + new_var_layout.offset(),
+ },
+ location_path,
+ }
+ }
+
+ pub fn element(mut self, index: usize) -> Self
+ {
+ let element_type_layout = self.type_layout.element_type_layout().unwrap();
+
+ self.binding_location.byte_offset += index * element_type_layout.stride();
+
+ self.type_layout = element_type_layout;
+
+ self.location_path.push(Location::Element(index));
+
+ self
+ }
+
+ pub fn binding(self, value: BindingValue) -> Result<Binding, BindingError>
+ {
+ value.validate_for_shader_type(self.type_layout, self.location_path)?;
+
+ Ok(Binding {
+ location: self.binding_location,
+ value,
+ })
+ }
+}
+
+/// Shader cursor location.
+#[derive(Debug, Clone, Default)]
+pub struct LocationPath
+{
+ locations: FixedCircularBuffer<Location, 16>,
+ is_truncated: bool,
+}
+
+impl LocationPath
+{
+ fn push(&mut self, location: Location)
+ {
+ if self.locations.push_back(location).is_some() {
+ self.is_truncated = true;
+ }
+ }
+}
+
+impl Display for LocationPath
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ if self.is_truncated {
+ write!(formatter, "(...)")?;
+ }
+
+ for location in &self.locations {
+ match location {
+ Location::Field(field) => {
+ write!(formatter, ".{field}")?;
+ }
+ Location::Element(element) => {
+ write!(formatter, "[{element}]")?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
+/// Shader cursor location.
+#[derive(Debug, Clone)]
+pub enum Location
+{
+ Field(Cow<'static, str>),
+ Element(usize),
+}
+
+impl Display for Location
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ match self {
+ Self::Field(field) => <Cow<'static, str> as Display>::fmt(field, formatter),
+ Self::Element(element) => <usize as Display>::fmt(element, formatter),
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct BindingLocation
+{
+ pub binding_index: u32,
+ pub binding_size: usize,
+ pub byte_offset: usize,
+}
+
+#[derive(Debug, Clone)]
+pub enum BindingValue
+{
+ Uint(u32),
+ Int(i32),
+ Float(f32),
+ FVec3(Vec3<f32>),
+ Color(Color<f32>),
+ FMat4x4(Matrix<f32, 4, 4>),
+ Texture(RenderingObjectId, BindingTextureKind),
+}
+
+impl BindingValue
+{
+ fn validate_for_shader_type(
+ &self,
+ ty: TypeLayout<'_>,
+ location_path: LocationPath,
+ ) -> Result<(), BindingError>
+ {
+ let ty_kind = ty.kind();
+ let scalar_ty = ty.scalar_type();
+
+ let element_scalar_ty = ty
+ .element_type_layout()
+ .and_then(|elem_ty| elem_ty.scalar_type());
+
+ let element_cnt = ty.element_cnt();
+
+ let is_valid = match self {
+ Self::Uint(_) => {
+ ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Uint32)
+ }
+ Self::Int(_) => {
+ ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Int32)
+ }
+ Self::Float(_) => {
+ ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Float32)
+ }
+ Self::FVec3(_) => {
+ ty_kind == TypeKind::Vector
+ && element_scalar_ty == Some(ScalarType::Float32)
+ && element_cnt == Some(3)
+ }
+ Self::Color(Color::Rgb(_)) => {
+ ty_kind == TypeKind::Vector
+ && element_scalar_ty == Some(ScalarType::Float32)
+ && element_cnt == Some(3)
+ }
+ Self::Color(Color::Rgba(_)) => {
+ ty_kind == TypeKind::Vector
+ && element_scalar_ty == Some(ScalarType::Float32)
+ && element_cnt == Some(4)
+ }
+ Self::FMat4x4(_) => {
+ ty_kind == TypeKind::Matrix
+ && element_scalar_ty == Some(ScalarType::Float32)
+ && ty.row_cnt() == Some(4)
+ && ty.column_cnt() == Some(4)
+ }
+ Self::Texture(_, BindingTextureKind::Texture2D) => {
+ ty_kind == TypeKind::Resource
+ && ty.resource_shape().is_some_and(|res_shape| {
+ (res_shape & ResourceShape::BASE)
+ .contains(ResourceShape::TEXTURE_2D)
+ })
+ }
+ Self::Texture(_, BindingTextureKind::Cube) => {
+ ty_kind == TypeKind::Resource
+ && ty.resource_shape().is_some_and(|res_shape| {
+ (res_shape & ResourceShape::BASE)
+ .contains(ResourceShape::TEXTURE_CUBE)
+ })
+ }
+ };
+
+ if !is_valid {
+ cold_path();
+ return Err(BindingError::IncorrectValueType {
+ value: self.clone(),
+ location_path,
+ });
+ }
+
+ Ok(())
+ }
+}
+
+impl From<u32> for BindingValue
+{
+ fn from(value: u32) -> Self
+ {
+ BindingValue::Uint(value)
+ }
+}
+
+impl From<i32> for BindingValue
+{
+ fn from(value: i32) -> Self
+ {
+ BindingValue::Int(value)
+ }
+}
+
+impl From<f32> for BindingValue
+{
+ fn from(value: f32) -> Self
+ {
+ BindingValue::Float(value)
+ }
+}
+
+impl From<Vec3<f32>> for BindingValue
+{
+ fn from(vec: Vec3<f32>) -> Self
+ {
+ BindingValue::FVec3(vec)
+ }
+}
+
+impl From<Color<f32>> for BindingValue
+{
+ fn from(color: Color<f32>) -> Self
+ {
+ BindingValue::Color(color)
+ }
+}
+impl From<Rgb<f32>> for BindingValue
+{
+ fn from(color: Rgb<f32>) -> Self
+ {
+ BindingValue::Color(color.into())
+ }
+}
+
+impl From<Rgba<f32>> for BindingValue
+{
+ fn from(color: Rgba<f32>) -> Self
+ {
+ BindingValue::Color(color.into())
+ }
+}
+
+impl From<Matrix<f32, 4, 4>> for BindingValue
+{
+ fn from(matrix: Matrix<f32, 4, 4>) -> Self
+ {
+ BindingValue::FMat4x4(matrix)
+ }
+}
+
+#[derive(Debug, Clone)]
+#[non_exhaustive]
+pub struct Binding
+{
+ pub location: BindingLocation,
+ pub value: BindingValue,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum BindingTextureKind
+{
+ Texture2D,
+ Cube,
+}
+
+#[derive(Debug, thiserror::Error)]
+#[non_exhaustive]
+pub enum BindingError
+{
+ #[error("Value {value:?} has incorrect type for value at {location_path} in shader")]
+ IncorrectValueType
+ {
+ value: BindingValue,
+ location_path: LocationPath,
+ },
+}
diff --git a/engine/src/scene.rs b/engine/src/scene.rs
new file mode 100644
index 0000000..2a66c3f
--- /dev/null
+++ b/engine/src/scene.rs
@@ -0,0 +1,8 @@
+use crate::ecs::Component;
+use crate::reflection::Reflection;
+
+#[derive(Debug, Default, Clone, Copy, Component, Reflection)]
+pub struct Scene;
+
+#[derive(Debug, Default, Clone, Copy, Component, Reflection)]
+pub struct Active;
diff --git a/engine/src/sky_box.rs b/engine/src/sky_box.rs
new file mode 100644
index 0000000..ceb82df
--- /dev/null
+++ b/engine/src/sky_box.rs
@@ -0,0 +1,11 @@
+use crate::asset::Handle as AssetHandle;
+use crate::ecs::Component;
+use crate::reflection::Reflection;
+use crate::texture::{CubeMapFace, Texture};
+
+#[derive(Debug, Clone, Component, Reflection)]
+#[non_exhaustive]
+pub enum SkyBox
+{
+ AssetPerCubeMapFace([(CubeMapFace, AssetHandle<Texture>); 6]),
+}
diff --git a/engine/src/texture.rs b/engine/src/texture.rs
index 4a4fe86..0c3e884 100644
--- a/engine/src/texture.rs
+++ b/engine/src/texture.rs
@@ -1,192 +1,39 @@
-use std::fmt::Display;
use std::path::Path;
-use std::sync::atomic::{AtomicU32, Ordering};
-use image::io::Reader as ImageReader;
-use image::{DynamicImage, ImageError, Rgb, RgbImage};
-
-use crate::color::Color;
-use crate::data_types::dimens::Dimens;
-use crate::opengl::texture::PixelDataFormat;
-use crate::util::builder;
-
-static NEXT_ID: AtomicU32 = AtomicU32::new(0);
-
-mod reexports
-{
- pub use crate::opengl::texture::{Filtering, Wrapping};
-}
-
-pub use reexports::*;
+use crate::asset::{Assets, Submitter as AssetSubmitter};
+use crate::builder;
+use crate::image::{Error as ImageError, Image};
#[derive(Debug, Clone)]
-pub struct Texture
-{
- id: Id,
- image: DynamicImage,
- pixel_data_format: PixelDataFormat,
- dimensions: Dimens<u32>,
- properties: Properties,
-}
-
-impl Texture
-{
- pub fn builder() -> Builder
- {
- Builder::default()
- }
-
- /// Opens a texture image.
- ///
- /// # Errors
- /// Will return `Err` if:
- /// - Opening the image fails
- /// - The image data is not 8-bit/color RGB
- pub fn open(path: &Path) -> Result<Self, Error>
- {
- Self::builder().open(path)
- }
-
- #[must_use]
- pub fn new_from_color(dimensions: &Dimens<u32>, color: &Color<u8>) -> Self
- {
- Self::builder().build_with_single_color(dimensions, color)
- }
-
- #[must_use]
- pub fn id(&self) -> Id
- {
- self.id
- }
-
- #[must_use]
- pub fn properties(&self) -> &Properties
- {
- &self.properties
- }
-
- pub fn properties_mut(&mut self) -> &mut Properties
- {
- &mut self.properties
- }
-
- #[must_use]
- pub fn dimensions(&self) -> &Dimens<u32>
- {
- &self.dimensions
- }
-
- #[must_use]
- pub fn pixel_data_format(&self) -> PixelDataFormat
- {
- self.pixel_data_format
- }
-
- #[must_use]
- pub fn image(&self) -> &DynamicImage
- {
- &self.image
- }
-}
-
-impl Drop for Texture
+pub enum Texture
{
- fn drop(&mut self)
- {
- NEXT_ID.fetch_sub(1, Ordering::Relaxed);
- }
+ Texture2D(Tex2D),
+ CubeMap(TexCubeMap),
}
-/// Texture builder.
-#[derive(Debug, Default, Clone)]
-pub struct Builder
+#[derive(Debug, Clone)]
+pub struct Tex2D
{
- properties: Properties,
+ pub image: Image,
+ pub properties: Properties,
}
-impl Builder
+#[derive(Debug, Clone)]
+pub struct TexCubeMap
{
- pub fn properties(mut self, properties: Properties) -> Self
- {
- self.properties = properties;
- self
- }
-
- /// Opens a image as a texture.
- ///
- /// # Errors
- /// Will return `Err` if:
- /// - Opening the image fails
- /// - Decoding the image fails
- /// - The image data is in a unsupported format
- pub fn open(&self, path: &(impl AsRef<Path> + ?Sized)) -> Result<Texture, Error>
- {
- let image = ImageReader::open(path)
- .map_err(Error::OpenImageFailed)?
- .decode()
- .map_err(Error::DecodeImageFailed)?;
-
- let pixel_data_format = match &image {
- DynamicImage::ImageRgb8(_) => PixelDataFormat::Rgb8,
- DynamicImage::ImageRgba8(_) => PixelDataFormat::Rgba8,
- _ => {
- return Err(Error::UnsupportedImageDataFormat);
- }
- };
-
- let dimensions = Dimens {
- width: image.width(),
- height: image.height(),
- };
-
- let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
-
- Ok(Texture {
- id: Id::new(id),
- image,
- pixel_data_format,
- dimensions,
- properties: self.properties.clone(),
- })
- }
-
- #[must_use]
- pub fn build_with_single_color(
- &self,
- dimensions: &Dimens<u32>,
- color: &Color<u8>,
- ) -> Texture
- {
- let image = RgbImage::from_pixel(
- dimensions.width,
- dimensions.height,
- Rgb([color.red, color.green, color.blue]),
- );
-
- let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
-
- Texture {
- id: Id::new(id),
- image: image.into(),
- pixel_data_format: PixelDataFormat::Rgb8,
- dimensions: *dimensions,
- properties: self.properties.clone(),
- }
- }
+ pub images: [(CubeMapFace, Image); 6],
+ pub properties: Properties,
}
-/// Texture error.
-#[derive(Debug, thiserror::Error)]
-pub enum Error
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CubeMapFace
{
- #[error("Failed to open texture image")]
- OpenImageFailed(#[source] std::io::Error),
-
- #[error("Failed to decode texture image")]
- DecodeImageFailed(#[source] ImageError),
-
- #[error("Unsupported image data format")]
- UnsupportedImageDataFormat,
+ PositiveX,
+ NegativeX,
+ PositiveY,
+ NegativeY,
+ PositiveZ,
+ NegativeZ,
}
builder! {
@@ -230,25 +77,67 @@ impl Default for PropertiesBuilder
}
}
-/// Texture ID.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Id
+#[non_exhaustive]
+pub enum Filtering
+{
+ Nearest,
+ Linear,
+}
+
+/// Texture wrapping.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum Wrapping
{
- id: u32,
+ Repeat,
+ MirroredRepeat,
+ ClampToEdge,
+ ClampToBorder,
}
-impl Id
+builder! {
+#[builder(name = ImportSettingsBuilder, derives=(Debug, Clone))]
+#[derive(Debug, Default, Clone)]
+#[non_exhaustive]
+pub struct ImportSettings {
+ properties: Properties,
+}
+}
+
+impl ImportSettings
{
- fn new(id: u32) -> Self
+ pub fn builder() -> ImportSettingsBuilder
{
- Self { id }
+ ImportSettingsBuilder::default()
}
}
-impl Display for Id
+impl Default for ImportSettingsBuilder
{
- fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ fn default() -> Self
{
- self.id.fmt(formatter)
+ ImportSettings::default().into()
}
}
+
+pub(crate) fn initialize(assets: &mut Assets)
+{
+ assets.set_importer::<_, _>(["png", "jpg"], import);
+}
+
+fn import(
+ asset_submitter: &mut AssetSubmitter<'_>,
+ path: &Path,
+ settings: Option<&'_ ImportSettings>,
+) -> Result<(), ImageError>
+{
+ asset_submitter.submit_store(Texture::Texture2D(Tex2D {
+ image: Image::open(path)?,
+ properties: settings
+ .map(|settings| settings.properties.clone())
+ .unwrap_or_default(),
+ }));
+
+ Ok(())
+}
diff --git a/engine/src/transform.rs b/engine/src/transform.rs
index 5e5e296..8e768a6 100644
--- a/engine/src/transform.rs
+++ b/engine/src/transform.rs
@@ -1,43 +1,49 @@
-use ecs::Component;
-
+use crate::data_types::dimens::Dimens3;
+use crate::ecs::Component;
+use crate::matrix::Matrix;
+use crate::reflection::Reflection;
use crate::vector::Vec3;
-/// A position in 3D space.
-#[derive(Debug, Default, Clone, Copy, Component)]
-pub struct Position
+#[derive(Debug, Clone, Component, Reflection)]
+#[non_exhaustive]
+pub struct Transform
{
pub position: Vec3<f32>,
+ pub scale: Dimens3<f32>,
}
-impl From<Vec3<f32>> for Position
+impl Transform
{
- fn from(position: Vec3<f32>) -> Self
+ pub fn position(mut self, position: Vec3<f32>) -> Self
{
- Self { position }
+ self.position = position;
+ self
}
-}
-/// Scaling of a 3D object.
-#[derive(Debug, Clone, Copy, Component)]
-pub struct Scale
-{
- pub scale: Vec3<f32>,
-}
+ pub fn scale(mut self, scale: Dimens3<f32>) -> Self
+ {
+ self.scale = scale;
+ self
+ }
-impl From<Vec3<f32>> for Scale
-{
- fn from(scale: Vec3<f32>) -> Self
+ pub fn to_matrix(&self) -> Matrix<f32, 4, 4>
{
- Self { scale }
+ let mut matrix = Matrix::new_identity();
+
+ matrix.translate(&self.position);
+ matrix.scale(&self.scale);
+
+ matrix
}
}
-impl Default for Scale
+impl Default for Transform
{
fn default() -> Self
{
Self {
- scale: Vec3 { x: 1.0, y: 1.0, z: 1.0 },
+ position: Vec3 { x: 0.0, y: 0.0, z: 0.0 },
+ scale: Dimens3 { width: 1.0, height: 1.0, depth: 1.0 },
}
}
}
diff --git a/engine/src/ui.rs b/engine/src/ui.rs
new file mode 100644
index 0000000..626c810
--- /dev/null
+++ b/engine/src/ui.rs
@@ -0,0 +1,2 @@
+pub mod dear_imgui;
+pub mod view;
diff --git a/engine/src/ui/dear_imgui.rs b/engine/src/ui/dear_imgui.rs
new file mode 100644
index 0000000..4da5c5f
--- /dev/null
+++ b/engine/src/ui/dear_imgui.rs
@@ -0,0 +1,1081 @@
+use std::collections::HashMap;
+use std::path::{Path, PathBuf};
+use std::sync::atomic::{AtomicU64, Ordering};
+use std::sync::LazyLock;
+
+use dear_imgui_rs::{
+ DrawCmd as ImguiDrawCmd,
+ Key as ImguiKey,
+ MouseButton as ImguiMouseButton,
+};
+use ecs::component::local::Local;
+use ecs::component::Component;
+use ecs::error::Context as _;
+use ecs::event::component::{Changed, EventMatchExt};
+use ecs::pair::Pair;
+use ecs::query::term::With;
+use ecs::sole::Single;
+use ecs::system::initializable::Initializable;
+use ecs::system::observer::Observe;
+use ecs::system::Into;
+use ecs::time::Time;
+use ecs::{Component, Query, Sole};
+use intmap::IntMap;
+
+use crate::asset::{Assets, Handle as AssetHandle, Label as AssetLabel};
+use crate::data_types::dimens::Dimens;
+use crate::input::keyboard::{Key, Keyboard};
+use crate::input::mouse::{Button as MouseButton, Buttons as MouseButtons, Mouse};
+use crate::mesh::vertex_buffer::{
+ NamedVertexAttr as MeshNamedVertexAttr,
+ VertexAttrInfo as MeshVertexAttrInfo,
+ VertexBuffer as MeshVertexBuffer,
+ VertexLabel,
+};
+use crate::mesh::{Mesh, VertexAttrType as MeshVertexAttrType};
+use crate::projection::{
+ ClipVolume as ProjectionClipVolume,
+ Orthographic as OrthographicProjection,
+};
+use crate::reflection::Reflection;
+use crate::rendering::blending::{
+ Config as RenderingBlendingConfiguration,
+ Equation as RenderingBlendingEquation,
+ Factor as RenderingBlendingFactor,
+};
+use crate::rendering::object::Id as RenderingObjectId;
+use crate::rendering::shader::cursor::{
+ BindingTextureKind as ShaderBindingTextureKind,
+ BindingValue as ShaderBindingValue,
+ Cursor as ShaderCursor,
+};
+use crate::rendering::shader::{
+ Context as ShaderContext,
+ EntrypointFlags as ShaderEntrypointFlags,
+ ModuleSource as ShaderModuleSource,
+};
+use crate::rendering::{
+ AssetOrValue,
+ Command as RenderingCommand,
+ DrawMeshOptions,
+ DrawProperties,
+ DrawPropertiesUpdateFlags,
+ MeshUsage,
+ RenderPass,
+ RenderPasses,
+ ScissorBox,
+ SrgbaTextureDataType,
+ Surface,
+ SurfaceId,
+ TextureCreation,
+ TexturePixelDataFormat,
+ TextureUpdate,
+ PRE_RENDER_PHASE,
+};
+use crate::texture::Properties as TextureProperties;
+use crate::vector::Vec2;
+use crate::windowing::dpi::PhysicalSize;
+use crate::windowing::window::Window;
+
+mod reexports
+{
+ pub use dear_imgui_rs as bindings;
+}
+
+pub use reexports::*;
+
+pub static SHADER_ASSET_LABEL: LazyLock<AssetLabel> = LazyLock::new(|| AssetLabel {
+ path: Path::new("").into(),
+ name: Some("imgui_shader".into()),
+});
+
+/// Dear Imgui context
+#[derive(Sole)]
+pub struct Context
+{
+ pub enabled: bool,
+ ctx: inner_context_wrapper::InnerContextWrapper,
+ texture_lookup: IntMap<TextureLookupId, RenderingObjectId>,
+ texture_id_lookup:
+ HashMap<dear_imgui_rs::SnapshotTextureId, dear_imgui_rs::TextureId>,
+}
+
+impl Context
+{
+ pub fn frame(&mut self) -> Option<&mut bindings::Ui>
+ {
+ if !self.enabled {
+ return None;
+ }
+
+ self.ctx.get_frame()
+ }
+
+ pub fn register_texture(
+ &mut self,
+ texture_data: bindings::OwnedTextureData,
+ ) -> bindings::ManagedTextureId
+ {
+ self.ctx.register_texture(texture_data)
+ }
+}
+
+#[derive(Debug, Default)]
+#[non_exhaustive]
+pub struct Extension
+{
+ start_enabled: bool,
+ settings_ini_file_path: Option<PathBuf>,
+}
+
+impl Extension
+{
+ pub fn with_start_enabled(mut self, start_enabled: bool) -> Self
+ {
+ self.start_enabled = start_enabled;
+ self
+ }
+
+ pub fn with_settings_ini_file(
+ mut self,
+ settings_ini_file_path: Option<PathBuf>,
+ ) -> Self
+ {
+ self.settings_ini_file_path = settings_ini_file_path;
+ self
+ }
+}
+
+impl ecs::extension::Extension for Extension
+{
+ fn collect(self, mut collector: ecs::extension::Collector<'_>)
+ {
+ let mut context = Context {
+ enabled: self.start_enabled,
+ ctx: inner_context_wrapper::InnerContextWrapper::new(),
+ texture_lookup: IntMap::with_capacity(8),
+ texture_id_lookup: HashMap::with_capacity(8),
+ };
+
+ if let Err(err) = context
+ .ctx
+ .set_settings_ini_file_path(self.settings_ini_file_path)
+ {
+ tracing::error!("Failed to set path of imgui settings ini file: {err}");
+ }
+
+ assert!(
+ dear_imgui_rs::HAS_FREETYPE,
+ "Freetype font rasterizer support is not enabled"
+ );
+
+ unsafe {
+ context.ctx.font_atlas().add_font_from_memory_ttf(
+ include_bytes!("../../res/FiraMono-Regular.ttf"),
+ 14.0,
+ Some(
+ &dear_imgui_rs::FontConfig::default()
+ .name("FiraMono-Regular.ttf")
+ .font_loader_flags(
+ dear_imgui_rs::FontLoaderFlags::BITMAP
+ | dear_imgui_rs::FontLoaderFlags::FORCE_AUTOHINT,
+ ),
+ ),
+ None,
+ );
+ }
+
+ collector.add_sole(context).ok();
+
+ collector.add_system(
+ *PRE_RENDER_PHASE,
+ update.into_system().initialize((State::default(),)),
+ );
+
+ collector.add_observer(handle_window_changed);
+ }
+}
+
+#[tracing::instrument(skip_all)]
+fn handle_window_changed(
+ observe: Observe<Pair<Changed, Window>>,
+ mut context: Single<Context>,
+)
+{
+ let Ok(context) = context.get_mut() else {
+ unreachable!();
+ };
+
+ let Some(event_match) = observe
+ .iter()
+ .find(|event_match| event_match.get_entity().has_component(TargetWindow::id()))
+ else {
+ return;
+ };
+
+ let window = event_match.get_ent_target_comp();
+
+ let hidpi_factor = window.scale_factor().round();
+
+ context
+ .ctx
+ .get_io_mut()
+ .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]);
+
+ let window_size_logical = PhysicalSize {
+ width: window.inner_size.width as f64,
+ height: window.inner_size.height as f64,
+ }
+ .to_logical::<f64>(hidpi_factor);
+
+ context.ctx.get_io_mut().set_display_size([
+ window_size_logical.width as f32,
+ window_size_logical.height as f32,
+ ]);
+}
+
+fn update(
+ target_window_query: Query<(&Window, &Surface, With<TargetWindow>)>,
+ mut context: Single<Context>,
+ time: Single<Time>,
+ mut assets: Single<Assets>,
+ mut render_passes: Single<RenderPasses>,
+ shader_context: Single<ShaderContext>,
+ keyboard: Single<Keyboard>,
+ mouse: Single<Mouse>,
+ mouse_buttons: Single<MouseButtons>,
+ mut state: Local<State>,
+) -> Result<(), crate::Error>
+{
+ let Ok(context) = context.get_mut() else {
+ unreachable!();
+ };
+
+ let assets = assets.get_mut()?;
+ let render_passes = render_passes.get_mut()?;
+ let shader_context = shader_context.get()?;
+ let keyboard = keyboard.get()?;
+ let mouse = mouse.get()?;
+ let mouse_buttons = mouse_buttons.get()?;
+
+ let Some((window, window_surface)) = target_window_query.iter().next() else {
+ return Ok(());
+ };
+
+ let curr_state = std::mem::replace(&mut *state, State::Unreachable);
+
+ match curr_state {
+ State::NotInitialized => {
+ let Some((shader_asset, mesh_obj_id, mesh)) = initialize_context(
+ context,
+ &window,
+ window_surface.id,
+ assets,
+ render_passes,
+ &shader_context,
+ ) else {
+ *state = State::NotInitialized;
+ return Ok(());
+ };
+
+ context.ctx.new_frame();
+
+ *state = State::Ready { shader_asset, mesh_obj_id, mesh };
+ }
+ State::Ready { shader_asset, mesh_obj_id, mut mesh } => {
+ if !context.enabled {
+ *state = State::Ready { shader_asset, mesh_obj_id, mesh };
+ return Ok(());
+ }
+
+ context
+ .ctx
+ .get_io_mut()
+ .set_delta_time(time.delta_time.as_secs_f32());
+
+ update_inputs(context, &window, keyboard, mouse, mouse_buttons);
+
+ add_drawing_render_pass(
+ context,
+ render_passes,
+ shader_context,
+ window_surface.id,
+ shader_asset.clone(),
+ mesh_obj_id,
+ &mut mesh,
+ )?;
+
+ context.ctx.new_frame();
+
+ *state = State::Ready { shader_asset, mesh_obj_id, mesh };
+ }
+ State::Unreachable => unreachable!(),
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, Default, Clone, Copy, Component, Reflection)]
+pub struct TargetWindow;
+
+#[derive(Debug, Default, Component)]
+enum State
+{
+ #[default]
+ NotInitialized,
+ Ready
+ {
+ shader_asset: AssetHandle<ShaderModuleSource>,
+ mesh_obj_id: RenderingObjectId,
+ mesh: Mesh,
+ },
+ Unreachable,
+}
+
+fn initialize_context(
+ context: &mut Context,
+ window: &Window,
+ window_surface_id: SurfaceId,
+ assets: &mut Assets,
+ render_passes: &mut RenderPasses,
+ shader_context: &ShaderContext,
+) -> Option<(AssetHandle<ShaderModuleSource>, RenderingObjectId, Mesh)>
+{
+ let shader_asset = if let Some(shader_asset) =
+ assets.get_handle_to_loaded::<ShaderModuleSource>(SHADER_ASSET_LABEL.clone())
+ {
+ shader_asset
+ } else {
+ assets.store_with_label(
+ SHADER_ASSET_LABEL.clone(),
+ ShaderModuleSource {
+ name: "imgui_shader.slang".into(),
+ file_path: Path::new("@engine/imgui_shader").into(),
+ source: include_str!("../../res/imgui_shader.slang").into(),
+ link_entrypoints: ShaderEntrypointFlags::VERTEX
+ | ShaderEntrypointFlags::FRAGMENT,
+ },
+ );
+
+ // We want to wait with initializing until the shader have been processed by the
+ // shader context
+ return None;
+ };
+
+ let hidpi_factor = window.scale_factor().round();
+
+ context
+ .ctx
+ .get_io_mut()
+ .set_display_framebuffer_scale([hidpi_factor as f32, hidpi_factor as f32]);
+
+ let window_size = Dimens {
+ width: window.inner_size.width as f32,
+ height: window.inner_size.height as f32,
+ };
+
+ let window_logical_size = window_size / (hidpi_factor as f32);
+
+ context
+ .ctx
+ .get_io_mut()
+ .set_display_size([window_logical_size.width, window_logical_size.height]);
+
+ let mesh_obj_id = RenderingObjectId::new_sequential();
+
+ let mesh = Mesh::builder()
+ .vertices(MeshVertexBuffer::with_capacity(
+ &[
+ MeshVertexAttrInfo {
+ label: VertexLabel::Position,
+ ty: MeshVertexAttrType::Float32Array { length: 3 },
+ },
+ MeshVertexAttrInfo {
+ label: VertexLabel::Color,
+ ty: MeshVertexAttrType::Float32Array { length: 4 },
+ },
+ MeshVertexAttrInfo {
+ label: VertexLabel::Uv,
+ ty: MeshVertexAttrType::Float32Array { length: 2 },
+ },
+ ],
+ 128,
+ ))
+ .indices([])
+ .build();
+
+ render_passes.passes.push_back(RenderPass {
+ commands: vec![
+ RenderingCommand::MakeCurrent(window_surface_id),
+ RenderingCommand::CreateShaderProgram(
+ RenderingObjectId::Asset(shader_asset.id()),
+ shader_context
+ .get_program(&shader_asset.id())
+ .expect("Not possible")
+ .clone(),
+ ),
+ RenderingCommand::ActivateShader(RenderingObjectId::Asset(shader_asset.id())),
+ RenderingCommand::CreateMesh {
+ obj_id: mesh_obj_id,
+ mesh: AssetOrValue::Value(mesh.clone()),
+ usage: MeshUsage::Stream,
+ },
+ ],
+ draw_properties: DrawProperties::default(),
+ });
+
+ Some((shader_asset, mesh_obj_id, mesh))
+}
+
+fn update_inputs(
+ context: &mut Context,
+ window: &Window,
+ keyboard: &Keyboard,
+ mouse: &Mouse,
+ mouse_buttons: &MouseButtons,
+)
+{
+ let io = context.ctx.get_io_mut();
+
+ for (key, key_state) in keyboard.new_key_states() {
+ if let Some(key) = key_to_imgui_key(key) {
+ io.add_key_event(key, key_state.is_pressed());
+ }
+ }
+
+ for character in keyboard.text_keys().chars() {
+ if character == '\u{7f}' {
+ continue;
+ }
+
+ io.add_input_character(character);
+ }
+
+ io.add_mouse_source_event(dear_imgui_rs::input::MouseSource::Mouse);
+
+ let mouse_pos = mouse.position.to_logical::<f64>(window.scale_factor());
+
+ io.add_mouse_pos_event([mouse_pos.x as f32, mouse_pos.y as f32]);
+
+ if !mouse.curr_tick_scroll_delta.is_zero() {
+ io.add_mouse_wheel_event([
+ mouse.curr_tick_scroll_delta.hor_lines,
+ mouse.curr_tick_scroll_delta.vert_lines,
+ ]);
+ }
+
+ for (mouse_button, mouse_button_state) in mouse_buttons.all_current() {
+ if let Some(mouse_button) = mouse_button_to_imgui_mouse_button(mouse_button) {
+ io.add_mouse_button_event(mouse_button, mouse_button_state.is_pressed());
+ }
+ }
+}
+
+fn add_drawing_render_pass(
+ context: &mut Context,
+ render_passes: &mut RenderPasses,
+ shader_context: &ShaderContext,
+ window_surface_id: SurfaceId,
+ shader_asset: AssetHandle<ShaderModuleSource>,
+ mesh_obj_id: RenderingObjectId,
+ mesh: &mut Mesh,
+) -> Result<(), crate::Error>
+{
+ let render_pass = render_passes.passes.push_back_mut(RenderPass {
+ commands: Vec::new(),
+ draw_properties: DrawProperties {
+ blending_enabled: true,
+ blending_config: RenderingBlendingConfiguration {
+ equation: RenderingBlendingEquation::Add,
+ source_factor: RenderingBlendingFactor::SrcAlpha,
+ destination_factor: RenderingBlendingFactor::OneMinusSrcAlpha,
+ },
+ depth_test_enabled: false,
+ face_culling_enabled: false,
+ scissor_test_enabled: true,
+ ..Default::default()
+ },
+ });
+
+ let shader_program = shader_context
+ .get_program(&shader_asset.id())
+ .expect("Not possible");
+
+ let shader_cursor = ShaderCursor::new(
+ shader_program
+ .reflection(0)
+ .unwrap()
+ .global_params_var_layout()
+ .unwrap(),
+ );
+
+ let Context {
+ enabled: _,
+ ctx,
+ texture_lookup,
+ texture_id_lookup,
+ } = context;
+
+ let mut render_frame = ctx.render();
+
+ let mut tex_feedbacks = Vec::with_capacity(render_frame.texture_requests().len());
+
+ for texture_request in render_frame.texture_requests() {
+ match texture_request.operation() {
+ dear_imgui_rs::TextureOp::Create {
+ format,
+ width,
+ height,
+ row_pitch: _,
+ pixels,
+ } => {
+ if !matches!(format, dear_imgui_rs::TextureFormat::RGBA32) {
+ unimplemented!();
+ }
+
+ let texture_lookup_id = TextureLookupId::new();
+
+ tracing::trace!(
+ snapshot_tex_id = ?texture_request.texture(),
+ tex_lookup_id = ?texture_lookup_id,
+ "Performing texture operation: Create"
+ );
+
+ let texture_object_id = RenderingObjectId::new_sequential();
+
+ render_pass.commands.push(RenderingCommand::CreateTexture {
+ obj_id: texture_object_id,
+ pixel_data_format: TexturePixelDataFormat::Srgba(
+ SrgbaTextureDataType::UnsignedByte,
+ ),
+ creation: TextureCreation::Texture2D {
+ size: Dimens { width: *width, height: *height },
+ image: Some(pixels.clone().into_boxed_slice()),
+ },
+ properties: TextureProperties::default(),
+ });
+
+ texture_lookup.insert(texture_lookup_id, texture_object_id);
+
+ texture_id_lookup
+ .insert(texture_request.texture(), texture_lookup_id.into());
+
+ let texture_feedback =
+ match texture_request.uploaded(texture_lookup_id.into()) {
+ Ok(texture_feedback) => texture_feedback,
+ Err(
+ dear_imgui_rs::TextureFeedbackError::UploadForDestroy
+ | dear_imgui_rs::TextureFeedbackError::DestroyForUpload,
+ ) => unreachable!(),
+ };
+
+ tex_feedbacks.push(texture_feedback);
+ }
+ dear_imgui_rs::TextureOp::Update { format, width: _, height: _, rects } => {
+ if !matches!(format, dear_imgui_rs::TextureFormat::RGBA32) {
+ unimplemented!();
+ }
+
+ let texture_lookup_id =
+ TextureLookupId::from(texture_id_lookup[&texture_request.texture()]);
+
+ tracing::trace!(
+ snapshot_tex_id = ?texture_request.texture(),
+ tex_lookup_id = ?texture_lookup_id,
+ "Performing texture operation: Update"
+ );
+
+ let Some(texture_object_id) = texture_lookup.get(texture_lookup_id)
+ else {
+ tracing::error!(
+ snapshot_tex_id = ?texture_request.texture(),
+ tex_lookup_id = ?texture_lookup_id,
+ "Unknown texture. Skipping update",
+ );
+ continue;
+ };
+
+ render_pass.commands.reserve(rects.len());
+
+ for rect in rects {
+ render_pass.commands.push(RenderingCommand::UpdateTexture {
+ obj_id: *texture_object_id,
+ pixels: rect.data.clone().into_boxed_slice(),
+ pixel_data_format: TexturePixelDataFormat::Srgba(
+ SrgbaTextureDataType::UnsignedByte,
+ ),
+ update: TextureUpdate::Texture2D {
+ size: Dimens::<u32> {
+ width: rect.rect.w.into(),
+ height: rect.rect.h.into(),
+ },
+ offset: Vec2 {
+ x: rect.rect.x as u32,
+ y: rect.rect.y as u32,
+ },
+ },
+ });
+ }
+
+ let texture_feedback =
+ match texture_request.uploaded(texture_lookup_id.into()) {
+ Ok(texture_feedback) => texture_feedback,
+ Err(
+ dear_imgui_rs::TextureFeedbackError::UploadForDestroy
+ | dear_imgui_rs::TextureFeedbackError::DestroyForUpload,
+ ) => unreachable!(),
+ };
+
+ tex_feedbacks.push(texture_feedback);
+ }
+ dear_imgui_rs::TextureOp::Destroy => {
+ let texture_lookup_id =
+ TextureLookupId::from(texture_id_lookup[&texture_request.texture()]);
+
+ tracing::trace!(
+ snapshot_tex_id = ?texture_request.texture(),
+ tex_lookup_id = ?texture_lookup_id,
+ "Performing texture operation: Destroy"
+ );
+
+ let Some(texture_object_id) = texture_lookup.remove(texture_lookup_id)
+ else {
+ tracing::error!(
+ texture = ?texture_lookup_id,
+ "Unknown texture. Skipping destroyal",
+ );
+ continue;
+ };
+
+ render_pass
+ .commands
+ .push(RenderingCommand::RemoveTexture(texture_object_id));
+
+ let texture_feedback = match texture_request.destroyed() {
+ Ok(texture_feedback) => texture_feedback,
+ Err(
+ dear_imgui_rs::TextureFeedbackError::UploadForDestroy
+ | dear_imgui_rs::TextureFeedbackError::DestroyForUpload,
+ ) => unreachable!(),
+ };
+
+ tex_feedbacks.push(texture_feedback);
+ }
+ }
+ }
+
+ render_frame
+ .reconcile_texture_feedback(tex_feedbacks)
+ .with_context(|| "Failed to reconcile texture feedback")?;
+
+ let draw_data = render_frame.draw_data();
+
+ let [display_width, display_height] = draw_data.display_size();
+
+ // let [scale_width, scale_height] = draw_data.framebuffer_scale;
+
+ let shader_object_id = RenderingObjectId::Asset(shader_asset.id());
+
+ render_pass.commands.extend([
+ RenderingCommand::MakeCurrent(window_surface_id),
+ RenderingCommand::ActivateShader(shader_object_id),
+ RenderingCommand::SetShaderBinding(
+ shader_object_id,
+ shader_cursor
+ .field("Uniforms")
+ .field("projection")
+ .binding(
+ OrthographicProjection::builder()
+ .near(1.0)
+ .far(-1.0)
+ .viewport_origin(Vec2 { x: 0.0, y: 1.0 })
+ .size(crate::projection::OrthographicSize::FixedSize(Dimens {
+ width: display_width,
+ height: -display_height,
+ }))
+ .build()
+ .to_matrix_rh(
+ // Window size can be 0x0 since it will not be used
+ PhysicalSize::default(),
+ ProjectionClipVolume::NegOneToOne,
+ )
+ .into(),
+ )?,
+ ),
+ ]);
+
+ for draw_list in draw_data.draw_lists() {
+ mesh.vertex_buf_mut().clear();
+
+ for vertex in draw_list.vtx_buffer() {
+ mesh.vertex_buf_mut().push((
+ MeshNamedVertexAttr::<[f32; 3]> {
+ label: VertexLabel::Position,
+ value: [vertex.pos[0], vertex.pos[1], 0.0],
+ },
+ MeshNamedVertexAttr {
+ label: VertexLabel::Color,
+ value: vertex.rgba().map(|elem| (elem as f32) / 255.0),
+ },
+ MeshNamedVertexAttr {
+ label: VertexLabel::Uv,
+ value: vertex.uv,
+ },
+ ));
+ }
+
+ mesh.set_indices(draw_list.idx_buffer().iter().map(|index| (*index).into()));
+
+ render_pass.commands.push(RenderingCommand::UpdateMesh {
+ obj_id: mesh_obj_id,
+ mesh: mesh.clone(),
+ usage: MeshUsage::Stream,
+ });
+
+ for command in draw_list.commands() {
+ match command {
+ ImguiDrawCmd::Elements { count, cmd_params } => {
+ let Some(scissor_box) =
+ calc_draw_cmd_scissor_box(draw_data, &cmd_params)
+ else {
+ continue;
+ };
+
+ let tex_id = cmd_params.texture_id;
+
+ let Some(texture_object_id) =
+ texture_lookup.get(TextureLookupId::from(tex_id))
+ else {
+ tracing::error!(
+ "Unknown texture {}. Skipping skipping draw command",
+ tex_id.id()
+ );
+ continue;
+ };
+
+ render_pass.commands.extend([
+ RenderingCommand::UpdateDrawProperties(
+ DrawProperties { scissor_box, ..Default::default() },
+ DrawPropertiesUpdateFlags::SCISSOR_BOX,
+ ),
+ RenderingCommand::SetShaderBinding(
+ shader_object_id,
+ shader_cursor.field("main_texture").binding(
+ ShaderBindingValue::Texture(
+ *texture_object_id,
+ ShaderBindingTextureKind::Texture2D,
+ ),
+ )?,
+ ),
+ RenderingCommand::DrawMesh(
+ mesh_obj_id,
+ DrawMeshOptions::builder()
+ .element_offset(cmd_params.idx_offset.try_into().unwrap())
+ .vertex_offset(cmd_params.vtx_offset.try_into().unwrap())
+ .element_cnt(count.try_into().unwrap())
+ .build(),
+ ),
+ ]);
+ }
+ _ => {}
+ }
+ }
+ }
+
+ Ok(())
+}
+
+fn calc_draw_cmd_scissor_box(
+ draw_data: &dear_imgui_rs::DrawData,
+ cmd_params: &dear_imgui_rs::DrawCmdParams,
+) -> Option<ScissorBox>
+{
+ let clip_rect = cmd_params.clip_rect;
+
+ let display_size = draw_data.display_size();
+ let display_pos = draw_data.display_pos();
+ let framebuffer_scale = draw_data.framebuffer_scale();
+
+ let clip_min_x = (clip_rect[0] - display_pos[0]) * framebuffer_scale[0];
+
+ let clip_min_y = (clip_rect[1] - display_pos[1]) * framebuffer_scale[1];
+
+ let clip_max_x = (clip_rect[2] - display_pos[0]) * framebuffer_scale[0];
+
+ let clip_max_y = (clip_rect[3] - display_pos[1]) * framebuffer_scale[1];
+
+ if clip_max_x <= clip_min_x || clip_max_y <= clip_min_y {
+ return None;
+ }
+
+ let fb_height = display_size[1] * framebuffer_scale[1];
+
+ Some(ScissorBox {
+ size: Some(Dimens {
+ width: (clip_max_x - clip_min_x) as u16,
+ height: (clip_max_y - clip_min_y) as u16,
+ }),
+ lower_left_corner_pos: Vec2 {
+ x: clip_min_x as u16,
+ y: (fb_height - clip_max_y) as u16,
+ },
+ })
+}
+
+fn key_to_imgui_key(key: Key) -> Option<ImguiKey>
+{
+ match key {
+ Key::Backquote => Some(ImguiKey::GraveAccent),
+ Key::Backslash => Some(ImguiKey::Backslash),
+ Key::BracketLeft => Some(ImguiKey::LeftBracket),
+ Key::BracketRight => Some(ImguiKey::RightBracket),
+ Key::Comma => Some(ImguiKey::Comma),
+ Key::Digit0 => Some(ImguiKey::Key0),
+ Key::Digit1 => Some(ImguiKey::Key1),
+ Key::Digit2 => Some(ImguiKey::Key2),
+ Key::Digit3 => Some(ImguiKey::Key3),
+ Key::Digit4 => Some(ImguiKey::Key4),
+ Key::Digit5 => Some(ImguiKey::Key5),
+ Key::Digit6 => Some(ImguiKey::Key6),
+ Key::Digit7 => Some(ImguiKey::Key7),
+ Key::Digit8 => Some(ImguiKey::Key8),
+ Key::Digit9 => Some(ImguiKey::Key9),
+ Key::Equal => Some(ImguiKey::Equal),
+ Key::A => Some(ImguiKey::A),
+ Key::B => Some(ImguiKey::B),
+ Key::C => Some(ImguiKey::C),
+ Key::D => Some(ImguiKey::D),
+ Key::E => Some(ImguiKey::E),
+ Key::F => Some(ImguiKey::F),
+ Key::G => Some(ImguiKey::G),
+ Key::H => Some(ImguiKey::H),
+ Key::I => Some(ImguiKey::I),
+ Key::J => Some(ImguiKey::J),
+ Key::K => Some(ImguiKey::K),
+ Key::L => Some(ImguiKey::L),
+ Key::M => Some(ImguiKey::M),
+ Key::N => Some(ImguiKey::N),
+ Key::O => Some(ImguiKey::O),
+ Key::P => Some(ImguiKey::P),
+ Key::Q => Some(ImguiKey::Q),
+ Key::R => Some(ImguiKey::R),
+ Key::S => Some(ImguiKey::S),
+ Key::T => Some(ImguiKey::T),
+ Key::U => Some(ImguiKey::U),
+ Key::V => Some(ImguiKey::V),
+ Key::W => Some(ImguiKey::W),
+ Key::X => Some(ImguiKey::X),
+ Key::Y => Some(ImguiKey::Y),
+ Key::Z => Some(ImguiKey::Z),
+ Key::Minus => Some(ImguiKey::Minus),
+ Key::Period => Some(ImguiKey::Period),
+ Key::Quote => Some(ImguiKey::Apostrophe),
+ Key::Semicolon => Some(ImguiKey::Semicolon),
+ Key::Slash => Some(ImguiKey::Slash),
+ Key::AltLeft => Some(ImguiKey::LeftAlt),
+ Key::AltRight => Some(ImguiKey::RightAlt),
+ Key::Backspace => Some(ImguiKey::Backspace),
+ Key::CapsLock => Some(ImguiKey::CapsLock),
+ Key::ControlLeft => Some(ImguiKey::LeftCtrl),
+ Key::ControlRight => Some(ImguiKey::RightCtrl),
+ Key::Enter => Some(ImguiKey::Enter),
+ Key::SuperLeft => Some(ImguiKey::LeftSuper),
+ Key::SuperRight => Some(ImguiKey::RightSuper),
+ Key::ShiftLeft => Some(ImguiKey::LeftShift),
+ Key::ShiftRight => Some(ImguiKey::RightShift),
+ Key::Space => Some(ImguiKey::Space),
+ Key::Tab => Some(ImguiKey::Tab),
+ Key::Delete => Some(ImguiKey::Delete),
+ Key::End => Some(ImguiKey::End),
+ Key::Home => Some(ImguiKey::Home),
+ Key::Insert => Some(ImguiKey::Insert),
+ Key::PageDown => Some(ImguiKey::PageDown),
+ Key::PageUp => Some(ImguiKey::PageUp),
+ Key::ArrowDown => Some(ImguiKey::DownArrow),
+ Key::ArrowLeft => Some(ImguiKey::LeftArrow),
+ Key::ArrowRight => Some(ImguiKey::RightArrow),
+ Key::ArrowUp => Some(ImguiKey::UpArrow),
+ Key::NumLock => Some(ImguiKey::NumLock),
+ Key::Numpad0 => Some(ImguiKey::Keypad0),
+ Key::Numpad1 => Some(ImguiKey::Keypad1),
+ Key::Numpad2 => Some(ImguiKey::Keypad2),
+ Key::Numpad3 => Some(ImguiKey::Keypad3),
+ Key::Numpad4 => Some(ImguiKey::Keypad4),
+ Key::Numpad5 => Some(ImguiKey::Keypad5),
+ Key::Numpad6 => Some(ImguiKey::Keypad6),
+ Key::Numpad7 => Some(ImguiKey::Keypad7),
+ Key::Numpad8 => Some(ImguiKey::Keypad8),
+ Key::Numpad9 => Some(ImguiKey::Keypad9),
+ Key::NumpadAdd => Some(ImguiKey::KeypadAdd),
+ Key::NumpadDecimal => Some(ImguiKey::KeypadDecimal),
+ Key::NumpadDivide => Some(ImguiKey::KeypadDivide),
+ Key::NumpadEnter => Some(ImguiKey::KeypadEnter),
+ Key::NumpadEqual => Some(ImguiKey::KeypadEqual),
+ Key::NumpadMultiply => Some(ImguiKey::KeypadMultiply),
+ Key::NumpadSubtract => Some(ImguiKey::KeypadSubtract),
+ Key::Escape => Some(ImguiKey::Escape),
+ Key::PrintScreen => Some(ImguiKey::PrintScreen),
+ Key::ScrollLock => Some(ImguiKey::ScrollLock),
+ Key::Pause => Some(ImguiKey::Pause),
+ Key::Meta => Some(ImguiKey::ModSuper),
+ Key::F1 => Some(ImguiKey::F1),
+ Key::F2 => Some(ImguiKey::F2),
+ Key::F3 => Some(ImguiKey::F3),
+ Key::F4 => Some(ImguiKey::F4),
+ Key::F5 => Some(ImguiKey::F5),
+ Key::F6 => Some(ImguiKey::F6),
+ Key::F7 => Some(ImguiKey::F7),
+ Key::F8 => Some(ImguiKey::F8),
+ Key::F9 => Some(ImguiKey::F9),
+ Key::F10 => Some(ImguiKey::F10),
+ Key::F11 => Some(ImguiKey::F11),
+ Key::F12 => Some(ImguiKey::F12),
+ _ => None,
+ }
+}
+
+fn mouse_button_to_imgui_mouse_button(
+ mouse_button: MouseButton,
+) -> Option<ImguiMouseButton>
+{
+ match mouse_button {
+ MouseButton::Left | MouseButton::Other(0) => Some(ImguiMouseButton::Left),
+ MouseButton::Right | MouseButton::Other(1) => Some(ImguiMouseButton::Right),
+ MouseButton::Middle | MouseButton::Other(2) => Some(ImguiMouseButton::Middle),
+ MouseButton::Other(3) => Some(ImguiMouseButton::Extra1),
+ MouseButton::Other(4) => Some(ImguiMouseButton::Extra2),
+ _ => None,
+ }
+}
+
+mod inner_context_wrapper
+{
+ use std::path::PathBuf;
+ use std::pin::Pin;
+ use std::ptr::null_mut;
+
+ pub struct InnerContextWrapper
+ {
+ ctx: Pin<Box<dear_imgui_rs::Context>>,
+ frame: *mut dear_imgui_rs::Ui,
+ _renderer_consumer: dear_imgui_rs::RendererConsumer,
+ }
+
+ impl InnerContextWrapper
+ {
+ pub fn new() -> Self
+ {
+ let mut ctx = Box::pin(dear_imgui_rs::Context::create());
+
+ let _ = ctx.set_platform_name(Some("engine-windowing"));
+ let _ = ctx.set_renderer_name(Some("engine-rendering"));
+
+ ctx.io_mut()
+ .set_backend_flags(dear_imgui_rs::BackendFlags::RENDERER_HAS_TEXTURES);
+
+ let _renderer_consumer = ctx.create_renderer_consumer().unwrap();
+
+ Self {
+ ctx,
+ frame: null_mut(),
+ _renderer_consumer,
+ }
+ }
+
+ pub fn set_settings_ini_file_path(
+ &mut self,
+ path: Option<PathBuf>,
+ ) -> Result<(), dear_imgui_rs::ImGuiError>
+ {
+ self.ctx.set_ini_filename(path)
+ }
+
+ pub fn register_texture(
+ &mut self,
+ texture_data: dear_imgui_rs::texture::OwnedTextureData,
+ ) -> dear_imgui_rs::ManagedTextureId
+ {
+ self.ctx.register_texture(texture_data)
+ }
+
+ pub fn get_io_mut(&mut self) -> &mut dear_imgui_rs::Io
+ {
+ self.ctx.io_mut()
+ }
+
+ pub fn font_atlas(&self) -> &dear_imgui_rs::FontAtlas
+ {
+ self.ctx.font_atlas()
+ }
+
+ // pub fn create_renderer_consumer(&mut self) -> dear_imgui_rs::RendererConsumer
+ // {
+ // self.ctx.create_renderer_consumer().unwrap()
+ // }
+
+ pub fn render(&mut self) -> dear_imgui_rs::RenderedFrame<'_>
+ {
+ self.ctx.render()
+ }
+
+ pub fn new_frame(&mut self)
+ {
+ let frame = &raw mut *self.ctx.frame();
+
+ self.frame = frame;
+ }
+
+ pub fn get_frame(&mut self) -> Option<&mut dear_imgui_rs::Ui>
+ {
+ unsafe { self.frame.as_mut() }
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+struct TextureLookupId
+{
+ inner: u64,
+}
+
+impl TextureLookupId
+{
+ fn new() -> Self
+ {
+ static NEXT: AtomicU64 = AtomicU64::new(1);
+
+ Self {
+ inner: NEXT.fetch_add(1, Ordering::Relaxed),
+ }
+ }
+}
+
+impl intmap::IntKey for TextureLookupId
+{
+ type Int = u64;
+
+ const PRIME: Self::Int = <u64 as intmap::IntKey>::PRIME;
+
+ fn into_int(self) -> Self::Int
+ {
+ self.inner
+ }
+}
+
+impl From<dear_imgui_rs::TextureId> for TextureLookupId
+{
+ fn from(texture_id: dear_imgui_rs::TextureId) -> Self
+ {
+ assert!(!texture_id.is_null(), "Bad texture ID: is null");
+
+ Self { inner: texture_id.id() }
+ }
+}
+
+impl From<TextureLookupId> for dear_imgui_rs::TextureId
+{
+ fn from(texture_id: TextureLookupId) -> Self
+ {
+ Self::new(texture_id.inner)
+ }
+}
diff --git a/engine/src/ui/view.rs b/engine/src/ui/view.rs
new file mode 100644
index 0000000..ed714d6
--- /dev/null
+++ b/engine/src/ui/view.rs
@@ -0,0 +1 @@
+pub mod world;
diff --git a/engine/src/ui/view/world.rs b/engine/src/ui/view/world.rs
new file mode 100644
index 0000000..81c2377
--- /dev/null
+++ b/engine/src/ui/view/world.rs
@@ -0,0 +1,2314 @@
+use std::any::{Any, TypeId};
+use std::borrow::Cow;
+use std::io::Cursor;
+use std::ops::{Deref, DerefMut};
+
+use crate::color::{Rgb, Rgba};
+use crate::ecs::actions::Actions;
+use crate::ecs::component::local::Local;
+use crate::ecs::component::{
+ Handle as ComponentHandle,
+ Info as ComponentInfo,
+ Parts as ComponentParts,
+};
+use crate::ecs::entity::{Handle as EntityHandle, Name as EntityName};
+use crate::ecs::sole::Single;
+use crate::ecs::uid::Uid;
+use crate::ecs::util::Either;
+use crate::ecs::{Component, Query, World};
+use crate::image::{Format as ImageFormat, Image};
+use crate::reflection::{
+ Enum as EnumReflection,
+ EnumVariant as EnumVariantReflection,
+ GetError as ReflectionGetError,
+ LiteralType,
+ Struct as StructReflection,
+ Type as TypeReflection,
+ Visibility,
+};
+use crate::ui::dear_imgui::{
+ bindings as dear_imgui_bindings,
+ Context as DearImguiContext,
+};
+
+const BUTTON_RED_NORMAL: [f32; 4] = [124.0, 52.0, 39.0, 255.0];
+const BUTTON_RED_HOVERED: [f32; 4] = [181.0, 76.0, 57.0, 255.0];
+const BUTTON_RED_ACTIVE: [f32; 4] = [162.0, 68.0, 51.0, 255.0];
+
+#[derive(Default, Component)]
+pub struct State
+{
+ spawning_entity: Option<Uid>,
+ popup_state: Option<PopupState>,
+ textures: Option<Textures>,
+}
+
+pub fn show(
+ query: Query<()>,
+ mut dear_imgui_context: Single<DearImguiContext>,
+ mut state: Local<State>,
+ world: &World,
+ mut actions: Actions,
+) -> Result<(), crate::Error>
+{
+ let dear_imgui_context = dear_imgui_context.get_mut()?;
+
+ let State {
+ spawning_entity,
+ popup_state,
+ textures,
+ } = &mut *state;
+
+ let textures = textures.get_or_insert_with(|| Textures::new(dear_imgui_context));
+
+ let Some(frame) = dear_imgui_context.frame() else {
+ return Ok(());
+ };
+
+ frame
+ .window("World")
+ .size([600.0, 400.0], dear_imgui_bindings::Condition::Once)
+ .build(|| {
+ create_spawn_button_widgets(
+ frame,
+ spawning_entity,
+ popup_state,
+ world,
+ &mut actions,
+ );
+
+ frame.spacing();
+ frame.spacing();
+
+ for ent_handle in &query.into_flexible_query() {
+ create_entity_widgets(
+ frame,
+ popup_state,
+ textures,
+ &ent_handle,
+ world,
+ &mut actions,
+ );
+ }
+
+ let popup_closed = match popup_state {
+ Some(PopupState::AddComponent(popup_state)) => {
+ show_add_component_popup(frame, popup_state, &mut actions)
+ }
+ Some(PopupState::RenameEntity(popup_state)) => {
+ show_rename_entity_popup(frame, popup_state, &mut actions, world)
+ }
+ _ => false,
+ };
+
+ if popup_closed {
+ *popup_state = None;
+ }
+ });
+
+ Ok(())
+}
+
+fn create_texture(
+ dear_imgui_context: &mut DearImguiContext,
+ image: Image,
+) -> dear_imgui_bindings::ManagedTextureId
+{
+ let image = image.into_rgba8();
+
+ let mut texture_data = dear_imgui_bindings::OwnedTextureData::new();
+
+ texture_data.create(
+ dear_imgui_bindings::TextureFormat::RGBA32,
+ image.dimensions().width,
+ image.dimensions().height,
+ );
+
+ texture_data.set_data(image.as_bytes());
+
+ dear_imgui_context.register_texture(texture_data)
+}
+
+fn create_spawn_button_widgets(
+ frame: &dear_imgui_bindings::Ui,
+ spawning_entity: &mut Option<Uid>,
+ popup_state: &mut Option<PopupState>,
+ world: &World,
+ actions: &mut Actions,
+)
+{
+ let _disabled_token = frame.begin_disabled_with_cond(spawning_entity.is_some());
+
+ if frame.button("Spawn") {
+ let new_ent_id = actions.spawn(());
+
+ *spawning_entity = Some(new_ent_id);
+ }
+
+ if let Some(spawning_ent) = &spawning_entity {
+ if let Some(spawned_ent) = world.get_entity(*spawning_ent) {
+ let spawned_ent_title = create_entity_title(&spawned_ent);
+
+ if enter_add_component_popup_state(
+ popup_state,
+ &spawned_ent,
+ &spawned_ent_title,
+ world,
+ ) {
+ *spawning_entity = None;
+ }
+ }
+ }
+}
+
+fn create_entity_widgets(
+ frame: &dear_imgui_bindings::Ui,
+ popup_state: &mut Option<PopupState>,
+ textures: &Textures,
+ ent_handle: &EntityHandle,
+ world: &World,
+ actions: &mut Actions,
+)
+{
+ if ent_handle.component_ids().next().is_some()
+ && !ent_handle.component_ids().any(|comp_id| {
+ world.get_entity(comp_id).is_some_and(|comp_ent| {
+ comp_ent
+ .get::<ComponentInfo>()
+ .is_some_and(|comp_info| comp_info.type_reflection.is_some())
+ })
+ })
+ {
+ return;
+ }
+
+ let ent_title = create_entity_title(&ent_handle);
+
+ let mut is_open = false;
+
+ frame
+ .table(&format!("ent_{}_table", ent_handle.uid()))
+ .headers(false)
+ .sizing_policy(dear_imgui_bindings::TableSizingPolicy::FixedFit)
+ .columns([
+ dear_imgui_bindings::TableColumnSetup::new("a")
+ .fixed_width(frame.content_region_avail_width() * 0.8),
+ dear_imgui_bindings::TableColumnSetup::new("b"),
+ dear_imgui_bindings::TableColumnSetup::new("c"),
+ dear_imgui_bindings::TableColumnSetup::new("d"),
+ ])
+ .build(|frame| {
+ frame.table_next_row();
+
+ frame.table_next_column();
+
+ is_open = frame
+ .collapsing_header(&ent_title, dear_imgui_bindings::TreeNodeFlags::NONE);
+
+ frame.table_next_column();
+
+ create_add_component_button_widget(
+ frame,
+ &ent_handle,
+ &ent_title,
+ popup_state,
+ world,
+ );
+
+ frame.table_next_column();
+
+ create_despawn_button_widget(frame, textures, ent_handle, actions);
+
+ frame.table_next_column();
+
+ create_rename_entity_button_widget(
+ frame,
+ textures,
+ ent_handle,
+ &ent_title,
+ popup_state,
+ );
+ });
+
+ if is_open {
+ for component_id in ent_handle.component_ids() {
+ if component_id.is_pair() {
+ create_pair_component_widgets(
+ frame,
+ textures,
+ ent_handle,
+ component_id,
+ world,
+ actions,
+ );
+
+ frame.spacing();
+
+ continue;
+ }
+
+ create_component_widgets(frame, ent_handle, component_id, world, actions);
+ }
+ }
+}
+
+fn create_component_widgets(
+ frame: &dear_imgui_bindings::Ui,
+ ent_handle: &EntityHandle,
+ component_id: Uid,
+ world: &World,
+ actions: &mut Actions,
+)
+{
+ let Some(component_info) = world
+ .get_entity(component_id)
+ .and_then(|comp_ent| comp_ent.get::<ComponentInfo>())
+ else {
+ return;
+ };
+
+ let Some(component_type) = component_info.type_reflection else {
+ return;
+ };
+
+ let Some(mut component) = ent_handle.get_any_by_id_mut(component_id) else {
+ unreachable!();
+ };
+
+ {
+ let _color_token = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::Button,
+ BUTTON_RED_NORMAL.map(|num| num / 255.0),
+ );
+
+ let _color_token_b = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonHovered,
+ BUTTON_RED_HOVERED.map(|num| num / 255.0),
+ );
+
+ let _color_token_c = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonActive,
+ BUTTON_RED_ACTIVE.map(|num| num / 255.0),
+ );
+
+ if frame.small_button(format!(
+ "X##world_view_entity_{}_remove_component_button_{}",
+ ent_handle.uid(),
+ component_info.name
+ )) {
+ actions.remove_components(ent_handle.uid(), [component_id]);
+ }
+ }
+
+ frame.same_line();
+
+ let mut component_changed = false;
+
+ create_item_title_widget(
+ frame,
+ component_info.name,
+ &ItemType::Reflected(component_type),
+ None,
+ );
+
+ let item_type = ItemType::Reflected(component_type);
+
+ let _token = item_type.indent_or_same_line(frame);
+
+ add_item_to_frame(
+ frame,
+ ItemRef::Mutable(&mut *component),
+ &mut component_changed,
+ ItemInfo {
+ item_title: component_info.name,
+ item_tag: &format!(
+ "world_view_entity_{}_component_{}",
+ ent_handle.uid(),
+ component_info.name,
+ ),
+ item_type_name: None,
+ item_type,
+ item_is_read_only: false,
+ },
+ &[],
+ );
+
+ frame.spacing();
+
+ if component_changed {
+ component.set_changed();
+ }
+}
+
+fn create_pair_component_widgets(
+ frame: &dear_imgui_bindings::Ui,
+ textures: &Textures,
+ ent_handle: &EntityHandle,
+ pair_id: Uid,
+ world: &World,
+ actions: &mut Actions,
+)
+{
+ {
+ let _color_token = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::Button,
+ BUTTON_RED_NORMAL.map(|num| num / 255.0),
+ );
+
+ let _color_token_b = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonHovered,
+ BUTTON_RED_HOVERED.map(|num| num / 255.0),
+ );
+
+ let _color_token_c = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonActive,
+ BUTTON_RED_ACTIVE.map(|num| num / 255.0),
+ );
+
+ if frame.small_button(format!(
+ "X##world_view_entity_{}_remove_component_button_{}",
+ ent_handle.uid(),
+ pair_id
+ )) {
+ actions.remove_components(ent_handle.uid(), [pair_id]);
+ }
+ }
+
+ frame.same_line();
+
+ let relation_comp_info_or_ent_name =
+ get_comp_info_or_ent_name(world, pair_id.relation());
+
+ let relation_name: Cow<'_, str> = match &relation_comp_info_or_ent_name {
+ Some(Either::A(comp_info)) => comp_info.name.into(),
+ Some(Either::B(ent_name)) => format!("🌐 {}", ent_name.name).into(),
+ None => format!("<unnamed> {}", pair_id.relation()).into(),
+ };
+
+ let target_comp_info_or_ent_name = get_comp_info_or_ent_name(world, pair_id.target());
+
+ let target_name: Cow<'_, str> = match &target_comp_info_or_ent_name {
+ Some(Either::A(comp_info)) => comp_info.name.into(),
+ Some(Either::B(ent_name)) => format!("🌐 {}", ent_name.name).into(),
+ None => format!("<unnamed> {}", pair_id.target()).into(),
+ };
+
+ // TODO: Make relation & target ids editable
+
+ frame.text(format!("({relation_name}, {target_name})"));
+
+ let Some(mut pair_data) = ent_handle.get_any_by_id_mut(pair_id) else {
+ unreachable!();
+ };
+
+ let pair_data_ty_id = (*pair_data).type_id();
+
+ let Some(pair_data_comp_info) = [
+ &relation_comp_info_or_ent_name,
+ &target_comp_info_or_ent_name,
+ ]
+ .map(Option::as_ref)
+ .map(|item| item.and_then(Either::as_a))
+ .into_iter()
+ .flatten()
+ .find(|comp_info| comp_info.ty_id == pair_data_ty_id) else {
+ return;
+ };
+
+ let Some(pair_data_ty) = pair_data_comp_info.type_reflection else {
+ frame.same_line();
+
+ frame.image(textures.warning_icon_tex_id, [16.0, 16.0]);
+
+ if frame.is_item_hovered() {
+ frame.tooltip_text(format!(
+ "Pair data type {} does not have reflection",
+ pair_data_comp_info.name
+ ));
+ }
+
+ return;
+ };
+
+ let _indent = frame.begin_indent();
+
+ let mut component_changed = false;
+
+ create_item_title_widget(
+ frame,
+ pair_data_comp_info.name,
+ &ItemType::Reflected(pair_data_ty),
+ None,
+ );
+
+ let item_type = ItemType::Reflected(pair_data_ty);
+
+ let _token = item_type.indent_or_same_line(frame);
+
+ add_item_to_frame(
+ frame,
+ ItemRef::Mutable(&mut *pair_data),
+ &mut component_changed,
+ ItemInfo {
+ item_title: pair_data_comp_info.name,
+ item_tag: &format!(
+ "world_view_entity_{}_pair_component_{pair_id}",
+ ent_handle.uid(),
+ ),
+ item_type_name: None,
+ item_type,
+ item_is_read_only: false,
+ },
+ &[],
+ );
+
+ // The pair component is not set as changed here since events do not support
+ // pairs as event targets
+}
+
+fn get_comp_info_or_ent_name(
+ world: &World,
+ id: Uid,
+) -> Option<Either<ComponentHandle<'_, ComponentInfo>, ComponentHandle<'_, EntityName>>>
+{
+ let ent = world.get_entity(id)?;
+
+ if let Some(component_info) = ent.get::<ComponentInfo>() {
+ return Some(Either::A(component_info));
+ }
+
+ if let Some(entity_name) = ent.get::<EntityName>() {
+ return Some(Either::B(entity_name));
+ }
+
+ None
+}
+
+fn create_add_component_button_widget(
+ frame: &dear_imgui_bindings::Ui,
+ ent_handle: &EntityHandle,
+ ent_title: &str,
+ popup_state: &mut Option<PopupState>,
+ world: &World,
+)
+{
+ let _color_token = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::Button,
+ [49.0, 89.0, 28.0, 255.0].map(|num| num / 255.0),
+ );
+
+ let _color_token_b = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonHovered,
+ [100.0, 181.0, 57.0, 255.0].map(|num| num / 255.0),
+ );
+
+ let _color_token_c = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonActive,
+ [90.0, 162.0, 51.0, 255.0].map(|num| num / 255.0),
+ );
+
+ let is_button_clicked = frame.button(format!(
+ "+##add_component_to_entity_button_{}",
+ ent_handle.uid()
+ ));
+
+ if is_button_clicked {
+ enter_add_component_popup_state(popup_state, ent_handle, ent_title, world);
+ }
+}
+
+fn create_despawn_button_widget(
+ frame: &dear_imgui_bindings::Ui,
+ textures: &Textures,
+ ent_handle: &EntityHandle,
+ actions: &mut Actions,
+)
+{
+ let _color_token = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::Button,
+ BUTTON_RED_NORMAL.map(|num| num / 255.0),
+ );
+
+ let _color_token_b = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonHovered,
+ BUTTON_RED_HOVERED.map(|num| num / 255.0),
+ );
+
+ let _color_token_c = frame.push_style_color(
+ dear_imgui_bindings::StyleColor::ButtonActive,
+ BUTTON_RED_ACTIVE.map(|num| num / 255.0),
+ );
+
+ if frame
+ .image_button_config(
+ &format!("ent_{}_despawn_button", ent_handle.uid()),
+ textures.despawn_icon_tex_id,
+ [14.0, 14.0],
+ )
+ .build()
+ {
+ actions.despawn(ent_handle.uid());
+ }
+}
+
+fn create_rename_entity_button_widget(
+ frame: &dear_imgui_bindings::Ui,
+ textures: &Textures,
+ ent_handle: &EntityHandle,
+ ent_title: &str,
+ popup_state: &mut Option<PopupState>,
+)
+{
+ if frame
+ .image_button_config(
+ &format!("ent_{}_rename_button", ent_handle.uid()),
+ textures.edit_icon_tex_id,
+ [16.0, 16.0],
+ )
+ .build()
+ {
+ enter_rename_entity_popup_state(popup_state, ent_handle, ent_title);
+ }
+}
+
+fn enter_rename_entity_popup_state(
+ popup_state: &mut Option<PopupState>,
+ ent_handle: &EntityHandle,
+ ent_title: &str,
+) -> bool
+{
+ if popup_state.is_some() {
+ return false;
+ }
+
+ *popup_state = Some(PopupState::RenameEntity(RenameEntityPopupState {
+ popup_name: format!("Rename entity {ent_title}"),
+ target_entity_id: ent_handle.uid(),
+ new_entity_name: String::with_capacity(32),
+ }));
+
+ true
+}
+
+fn show_rename_entity_popup(
+ frame: &dear_imgui_bindings::Ui,
+ popup_state: &mut RenameEntityPopupState,
+ actions: &mut Actions,
+ world: &World,
+) -> bool
+{
+ frame.open_popup(&popup_state.popup_name);
+
+ let mut is_opened = true;
+
+ let should_close =
+ frame.modal_popup_with_opened(&popup_state.popup_name, &mut is_opened, || {
+ frame.set_window_size([
+ frame.calc_text_size(&popup_state.popup_name)[0] + 32.0,
+ 120.0,
+ ]);
+
+ frame.spacing();
+ frame.spacing();
+
+ frame.text("Name:");
+ frame.same_line();
+
+ frame
+ .input_text(
+ "##rename_entity_popup_new_name_input",
+ &mut popup_state.new_entity_name,
+ )
+ .build();
+
+ frame.spacing();
+ frame.spacing();
+
+ let rename_text_size = frame.calc_text_size("Rename");
+
+ if frame.button_with_size(
+ "Rename##rename_entity_popup_rename_button",
+ [rename_text_size[0] * 2.0, rename_text_size[1] * 2.0],
+ ) {
+ let Some(target_ent) = world.get_entity(popup_state.target_entity_id)
+ else {
+ tracing::error!(
+ entity = ?popup_state.target_entity_id,
+ "Cannot rename entity: entity no longer exists"
+ );
+ return true;
+ };
+
+ if let Some(mut target_ent_name) = target_ent.get_mut::<EntityName>() {
+ target_ent_name.name = popup_state.new_entity_name.clone().into();
+ } else {
+ actions.add_components(
+ popup_state.target_entity_id,
+ (EntityName {
+ name: popup_state.new_entity_name.clone().into(),
+ },),
+ );
+ }
+
+ return true;
+ }
+
+ false
+ });
+
+ if let Some(true) = should_close {
+ return true;
+ }
+
+ if !is_opened {
+ return true;
+ }
+
+ false
+}
+
+fn enter_add_component_popup_state(
+ popup_state: &mut Option<PopupState>,
+ ent_handle: &EntityHandle,
+ ent_title: &str,
+ world: &World,
+) -> bool
+{
+ if popup_state.is_some() {
+ return false;
+ }
+
+ let popup_name = format!("Add component to {ent_title}");
+
+ let mut searchable_components = world
+ .query::<(&ComponentInfo,)>()
+ .iter_with_euids()
+ .map(|(id, (component_info,))| {
+ let user_creatable = if component_info.is_sole {
+ ComponentUserCreatable::No { reason: "is a sole" }
+ } else if component_info.type_reflection.is_none() {
+ ComponentUserCreatable::No { reason: "no type reflection" }
+ } else if component_info
+ .type_reflection
+ .is_some_and(|ty| !ty.has_default_value())
+ {
+ ComponentUserCreatable::No { reason: "no default value" }
+ } else if !get_whole_type_is_user_editable(
+ component_info.type_reflection,
+ component_info.ty_id,
+ ) {
+ ComponentUserCreatable::No {
+ reason: "whole component is not user editable",
+ }
+ } else {
+ ComponentUserCreatable::Yes
+ };
+
+ (id, component_info.clone(), user_creatable)
+ })
+ .collect::<Vec<_>>();
+
+ searchable_components.sort_by_key(|(_, _, user_creatable)| *user_creatable);
+
+ *popup_state = Some(PopupState::AddComponent(AddComponentPopupState {
+ popup_name: popup_name.clone(),
+ target_entity_id: ent_handle.uid(),
+ search_text: String::with_capacity(20),
+ searched_components: searchable_components.clone(),
+ searchable_components,
+ selected_search_result: -1,
+ new_component: None,
+ }));
+
+ true
+}
+
+fn show_add_component_popup(
+ frame: &dear_imgui_bindings::Ui,
+ add_component_popup_state: &mut AddComponentPopupState,
+ actions: &mut Actions,
+) -> bool
+{
+ frame.open_popup(&add_component_popup_state.popup_name);
+
+ let mut is_opened = true;
+
+ let should_close = frame.modal_popup_with_opened(
+ &add_component_popup_state.popup_name,
+ &mut is_opened,
+ || {
+ frame.set_window_size([700.0, 600.0]);
+
+ frame.spacing();
+ frame.spacing();
+
+ frame.text("Search:");
+ frame.same_line();
+
+ if frame
+ .input_text(
+ "##add_component_popup_search_input",
+ &mut add_component_popup_state.search_text,
+ )
+ .build()
+ {
+ add_component_popup_state.searched_components = add_component_popup_state
+ .searchable_components
+ .iter()
+ .cloned()
+ .filter(|(_, searchable_comp_info, _)| {
+ searchable_comp_info
+ .name
+ .contains(&add_component_popup_state.search_text)
+ })
+ .collect();
+
+ add_component_popup_state.selected_search_result = -1;
+ }
+
+ frame.spacing();
+ frame.spacing();
+
+ let _item_width_token = frame.push_item_width(-1.0);
+
+ if frame
+ .list_box_config("##add_component_popup_search_results")
+ .build_extended(
+ frame,
+ &mut add_component_popup_state.selected_search_result,
+ &add_component_popup_state.searched_components,
+ &|(_, comp_info, comp_user_creatable)| ListBoxItem {
+ label: comp_info.name.into(),
+ disabled: matches!(
+ comp_user_creatable,
+ ComponentUserCreatable::No { .. }
+ ),
+ hover_tooltip: match comp_user_creatable {
+ ComponentUserCreatable::Yes => None,
+ ComponentUserCreatable::No { reason } => Some(
+ format!("Component cannot be selected: {reason}").into(),
+ ),
+ },
+ },
+ )
+ {
+ let (selected_comp_id, selected_comp_info, _) = add_component_popup_state
+ .searched_components
+ [add_component_popup_state.selected_search_result as usize]
+ .clone();
+
+ let Some(selected_component_type) = selected_comp_info.type_reflection
+ else {
+ unreachable!();
+ };
+
+ let Some(selected_comp_default_value) =
+ selected_component_type.default_value()
+ else {
+ unreachable!(); // Only components with default values are selectable
+ };
+
+ add_component_popup_state.new_component = Some((
+ selected_comp_default_value,
+ selected_comp_info,
+ selected_comp_id,
+ ));
+ }
+
+ frame.spacing();
+ frame.spacing();
+ frame.spacing();
+
+ if let Some((new_component, new_component_info, _)) =
+ add_component_popup_state.new_component.as_mut()
+ {
+ let mut component_changed = false;
+
+ let Some(new_component_type) = new_component_info.type_reflection else {
+ unreachable!();
+ };
+
+ create_item_title_widget(
+ frame,
+ new_component_info.name,
+ &ItemType::Reflected(new_component_type),
+ None,
+ );
+
+ let item_type = ItemType::Reflected(new_component_type);
+
+ let _token = item_type.indent_or_same_line(frame);
+
+ add_item_to_frame(
+ frame,
+ ItemRef::Mutable(&mut **new_component),
+ &mut component_changed,
+ ItemInfo {
+ item_title: new_component_info.name,
+ item_tag: &format!(
+ "add_component_popup_component_{}",
+ new_component_info.name,
+ ),
+ item_type_name: None,
+ item_type,
+ item_is_read_only: false,
+ },
+ &[],
+ );
+
+ frame.spacing();
+ frame.spacing();
+ frame.spacing();
+ }
+
+ let _disabled = frame.begin_disabled_with_cond(
+ add_component_popup_state.new_component.is_none(),
+ );
+
+ let add_text_size = frame.calc_text_size("Add");
+
+ if frame.button_with_size(
+ "Add##add_component_popup_add_button",
+ [add_text_size[0] * 2.0, add_text_size[1] * 2.0],
+ ) {
+ if let Some((new_component_data, new_component_info, new_component_id)) =
+ add_component_popup_state.new_component.take()
+ {
+ actions.add_components(
+ add_component_popup_state.target_entity_id,
+ [ComponentParts::from_component_info(
+ &new_component_info,
+ new_component_id,
+ new_component_data,
+ )],
+ );
+
+ return true;
+ }
+ }
+
+ false
+ },
+ );
+
+ if let Some(true) = should_close {
+ return true;
+ }
+
+ if !is_opened {
+ return true;
+ }
+
+ false
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum ComponentUserCreatable
+{
+ Yes,
+ No
+ {
+ reason: &'static str,
+ },
+}
+
+struct ItemInfo<'a>
+{
+ item_title: &'a str,
+ item_tag: &'a str,
+ item_type_name: Option<&'static str>,
+ item_type: ItemType,
+ item_is_read_only: bool,
+}
+
+enum ItemType
+{
+ Reflected(&'static TypeReflection),
+ String,
+ CowStr,
+ Color(ColorItemType, LiteralType),
+}
+
+impl ItemType
+{
+ fn spans_multiple_rows(&self) -> bool
+ {
+ match self {
+ Self::Reflected(
+ TypeReflection::Struct(_)
+ | TypeReflection::Array(_)
+ | TypeReflection::Slice(_),
+ ) => true,
+ Self::Reflected(TypeReflection::Enum(enum_ty)) => !enum_ty.is_unit_only,
+ Self::Reflected(TypeReflection::Reference(ref_ty)) => {
+ ItemType::Reflected(ref_ty.ty).spans_multiple_rows()
+ }
+ Self::Color(..) => false,
+ Self::Reflected(TypeReflection::Literal(_)) | Self::String | Self::CowStr => {
+ false
+ }
+ Self::Reflected(_) => unimplemented!(),
+ }
+ }
+
+ fn indent_or_same_line<'frame>(
+ &self,
+ frame: &'frame dear_imgui_bindings::Ui,
+ ) -> Option<dear_imgui_bindings::IndentToken<'frame>>
+ {
+ if self.spans_multiple_rows() {
+ return Some(frame.begin_indent());
+ }
+
+ frame.same_line();
+
+ None
+ }
+}
+
+#[derive(Debug)]
+enum ColorItemType
+{
+ Rgb,
+ Rgba,
+}
+
+fn get_item_type(ty: Option<&'static TypeReflection>, type_id: TypeId)
+ -> Option<ItemType>
+{
+ if let Some(TypeReflection::Struct(_)) = ty {
+ if type_id == TypeId::of::<Rgb<f32>>() {
+ return Some(ItemType::Color(ColorItemType::Rgb, LiteralType::F32));
+ }
+ if type_id == TypeId::of::<Rgb<u8>>() {
+ return Some(ItemType::Color(ColorItemType::Rgb, LiteralType::U8));
+ }
+
+ if type_id == TypeId::of::<Rgba<f32>>() {
+ return Some(ItemType::Color(ColorItemType::Rgba, LiteralType::F32));
+ }
+ if type_id == TypeId::of::<Rgba<u8>>() {
+ return Some(ItemType::Color(ColorItemType::Rgba, LiteralType::U8));
+ }
+ }
+
+ if let Some(ty) = ty {
+ return Some(ItemType::Reflected(ty));
+ } else if type_id == TypeId::of::<String>() {
+ Some(ItemType::String)
+ } else if type_id == TypeId::of::<Cow<'static, str>>() {
+ Some(ItemType::CowStr)
+ } else {
+ None
+ }
+}
+
+enum ItemRef<'item>
+{
+ Mutable(&'item mut dyn Any),
+ Immutable(&'item dyn Any),
+}
+
+impl<'item> ItemRef<'item>
+{
+ fn get_struct_field(
+ &mut self,
+ ty: &StructReflection,
+ field_index: usize,
+ ) -> Result<ItemRef<'_>, ReflectionGetError>
+ {
+ match self {
+ ItemRef::Mutable(item) => {
+ (ty.try_get_field_mut)(*item, field_index).map(ItemRef::Mutable)
+ }
+ ItemRef::Immutable(item) => {
+ (ty.try_get_field)(*item, field_index).map(ItemRef::Immutable)
+ }
+ }
+ }
+
+ fn get_enum_variant_field(
+ &mut self,
+ variant: &EnumVariantReflection,
+ field_index: usize,
+ ) -> Result<ItemRef<'_>, ReflectionGetError>
+ {
+ match self {
+ ItemRef::Mutable(item) => {
+ (variant.try_get_field_mut)(*item, field_index).map(ItemRef::Mutable)
+ }
+ ItemRef::Immutable(item) => {
+ (variant.try_get_field)(*item, field_index).map(ItemRef::Immutable)
+ }
+ }
+ }
+
+ fn into_writable<T: Clone + 'static>(self) -> OwnedOrRefMut<'item, T>
+ {
+ match self {
+ ItemRef::Mutable(item) => OwnedOrRefMut::Ref(
+ (*item)
+ .downcast_mut::<T>()
+ .expect("Cannot downcast: wrong item type"),
+ ),
+ ItemRef::Immutable(item) => OwnedOrRefMut::Owned(
+ (*item)
+ .downcast_ref::<T>()
+ .expect("Cannot downcast: wrong item type")
+ .clone(),
+ ),
+ }
+ }
+}
+
+impl AsRef<dyn Any> for ItemRef<'_>
+{
+ fn as_ref(&self) -> &dyn Any
+ {
+ match self {
+ Self::Mutable(item) => &**item,
+ Self::Immutable(item) => *item,
+ }
+ }
+}
+
+#[derive(Debug)]
+pub enum OwnedOrRefMut<'a, Value>
+{
+ Ref(&'a mut Value),
+ Owned(Value),
+}
+
+impl<Value> Deref for OwnedOrRefMut<'_, Value>
+{
+ type Target = Value;
+
+ fn deref(&self) -> &Self::Target
+ {
+ match self {
+ Self::Ref(value) => *value,
+ Self::Owned(value) => value,
+ }
+ }
+}
+
+impl<Value> DerefMut for OwnedOrRefMut<'_, Value>
+{
+ fn deref_mut(&mut self) -> &mut Self::Target
+ {
+ match self {
+ Self::Ref(value) => *value,
+ Self::Owned(value) => value,
+ }
+ }
+}
+
+fn create_item_title_widget(
+ frame: &dear_imgui_bindings::Ui,
+ item_title: &str,
+ item_type: &ItemType,
+ item_type_name: Option<&str>,
+)
+{
+ if !matches!(item_type, ItemType::Reflected(TypeReflection::Reference(_))) {
+ frame.text(&item_title);
+
+ if let Some(item_type_name) = item_type_name {
+ if frame.is_item_hovered() {
+ frame.tooltip_text(item_type_name);
+ }
+ }
+ }
+}
+
+fn add_item_to_frame<'a>(
+ frame: &dear_imgui_bindings::Ui,
+ mut item: ItemRef<'_>,
+ data_changed: &mut bool,
+ ItemInfo {
+ item_title,
+ item_tag,
+ item_type_name,
+ item_type,
+ item_is_read_only,
+ }: ItemInfo<'a>,
+ prev_item_tags: &[&'a str],
+)
+{
+ let _disabled = frame.begin_disabled_with_cond(
+ !matches!(
+ item_type,
+ ItemType::Reflected(
+ TypeReflection::Struct(_)
+ | TypeReflection::Enum(EnumReflection { is_unit_only: false, .. })
+ | TypeReflection::Array(_)
+ | TypeReflection::Slice(_)
+ )
+ ) && item_is_read_only,
+ );
+
+ match item_type {
+ ItemType::Reflected(TypeReflection::Struct(struct_ty)) => {
+ create_struct_widgets(
+ frame,
+ &mut item,
+ data_changed,
+ item_tag,
+ item_is_read_only,
+ struct_ty,
+ prev_item_tags,
+ );
+ }
+ ItemType::Reflected(TypeReflection::Enum(enum_type)) => {
+ create_enum_widgets(
+ frame,
+ &mut item,
+ data_changed,
+ item_tag,
+ item_is_read_only,
+ enum_type,
+ prev_item_tags,
+ );
+ }
+ ItemType::Reflected(TypeReflection::Literal(literal_reflection)) => {
+ match literal_reflection.ty {
+ LiteralType::U8 => create_scalar_item_input::<u8>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::I8 => create_scalar_item_input::<i8>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::U16 => create_scalar_item_input::<u16>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::I16 => create_scalar_item_input::<i16>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::U32 => create_scalar_item_input::<u32>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::I32 => create_scalar_item_input::<i32>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::U64 => create_scalar_item_input::<u64>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::I64 => create_scalar_item_input::<i64>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::F32 => create_scalar_item_input::<f32>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::F64 => create_scalar_item_input::<f64>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::Usize => create_scalar_item_input::<usize>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::Isize => create_scalar_item_input::<isize>(
+ frame,
+ &mut item,
+ data_changed,
+ &item_tag,
+ &prev_item_tags,
+ ),
+ LiteralType::U128 => {
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_mut::<u128>() else {
+ unreachable!();
+ };
+
+ item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<u128>() else {
+ unreachable!();
+ };
+
+ &mut item.clone()
+ }
+ };
+
+ frame.text(item.to_string());
+ }
+ LiteralType::I128 => {
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_mut::<i128>() else {
+ unreachable!();
+ };
+
+ item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<i128>() else {
+ unreachable!();
+ };
+
+ &mut item.clone()
+ }
+ };
+
+ frame.text(item.to_string());
+ }
+ LiteralType::Bool => {
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_mut::<bool>() else {
+ unreachable!();
+ };
+
+ item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<bool>() else {
+ unreachable!();
+ };
+
+ &mut item.clone()
+ }
+ };
+
+ if frame.checkbox(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ item,
+ ) {
+ *data_changed = true;
+ }
+ }
+ LiteralType::Str => {
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_ref::<&'static str>()
+ else {
+ unreachable!();
+ };
+
+ *item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<&'static str>()
+ else {
+ unreachable!();
+ };
+
+ *item
+ }
+ };
+
+ frame.text(item);
+ }
+ _ => unimplemented!(),
+ }
+ }
+ ItemType::Reflected(TypeReflection::Array(array_type)) => {
+ let mut prev_item_tags = prev_item_tags.to_vec();
+
+ prev_item_tags.push(item_tag);
+
+ for array_item_index in 0..array_type.length {
+ let array_item_name = array_item_index.to_string();
+
+ let Ok(item_item) = (match &mut item {
+ ItemRef::Mutable(item) => {
+ (array_type.try_get_item_mut)(*item, array_item_index)
+ .map(ItemRef::Mutable)
+ }
+ ItemRef::Immutable(item) => {
+ (array_type.try_get_item)(*item, array_item_index)
+ .map(ItemRef::Immutable)
+ }
+ }) else {
+ unreachable!();
+ };
+
+ create_item_title_widget(
+ frame,
+ &array_item_name,
+ &ItemType::Reflected(array_type.item_type),
+ Some(array_type.item_type_name()),
+ );
+
+ let array_item_type = ItemType::Reflected(array_type.item_type);
+
+ let _token = array_item_type.indent_or_same_line(frame);
+
+ add_item_to_frame(
+ frame,
+ item_item,
+ data_changed,
+ ItemInfo {
+ item_title: array_item_name.as_ref(),
+ item_tag: array_item_name.as_ref(),
+ item_type_name: Some(array_type.item_type_name()),
+ item_type: array_item_type,
+ item_is_read_only,
+ },
+ &prev_item_tags,
+ );
+ }
+ }
+ ItemType::Reflected(TypeReflection::Slice(slice_type)) => {
+ let mut prev_item_tags = prev_item_tags.to_vec();
+
+ prev_item_tags.push(item_tag);
+
+ let Some(len) = (slice_type.try_get_len)(item.as_ref()) else {
+ unreachable!();
+ };
+
+ for index in 0..len {
+ let item_name = index.to_string();
+
+ let Ok(item_item) = (slice_type.try_get_item)(item.as_ref(), index)
+ else {
+ unreachable!();
+ };
+
+ let mut slice_item_changed = false;
+
+ create_item_title_widget(
+ frame,
+ &item_name,
+ &ItemType::Reflected(slice_type.item_type),
+ Some(slice_type.item_type_name()),
+ );
+
+ let slice_item_type = ItemType::Reflected(slice_type.item_type);
+
+ let _token = slice_item_type.indent_or_same_line(frame);
+
+ add_item_to_frame(
+ frame,
+ ItemRef::Immutable(item_item),
+ &mut slice_item_changed,
+ ItemInfo {
+ item_title: &item_name,
+ item_tag: &item_name,
+ item_type_name: Some(slice_type.item_type_name()),
+ item_type: slice_item_type,
+ item_is_read_only: true,
+ },
+ &prev_item_tags,
+ );
+
+ assert!(!slice_item_changed);
+ }
+ }
+ ItemType::Reflected(TypeReflection::Reference(ref_type)) => {
+ let mut derefed_changed = false;
+
+ let Some(derefed_item) = (ref_type.try_deref)(item.as_ref()) else {
+ unreachable!();
+ };
+
+ create_item_title_widget(
+ frame,
+ item_title,
+ &ItemType::Reflected(ref_type.ty),
+ item_type_name,
+ );
+
+ let ref_item_type = ItemType::Reflected(ref_type.ty);
+
+ let _token = ref_item_type.indent_or_same_line(frame);
+
+ add_item_to_frame(
+ frame,
+ ItemRef::Immutable(derefed_item),
+ &mut derefed_changed,
+ ItemInfo {
+ item_title,
+ item_tag: "derefed".into(),
+ item_type_name,
+ item_type: ref_item_type,
+ item_is_read_only: true,
+ },
+ prev_item_tags,
+ );
+
+ assert!(!derefed_changed);
+ }
+ ItemType::String => {
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_mut::<String>() else {
+ unreachable!();
+ };
+
+ item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<String>() else {
+ unreachable!();
+ };
+
+ &mut item.clone()
+ }
+ };
+
+ if frame
+ .input_text(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ item,
+ )
+ .build()
+ {
+ *data_changed = true;
+ }
+ }
+ ItemType::CowStr => {
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_mut::<Cow<'static, str>>() else {
+ unreachable!();
+ };
+
+ item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<Cow<'static, str>>() else {
+ unreachable!();
+ };
+
+ &mut item.clone()
+ }
+ };
+
+ if frame
+ .input_text(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ item.to_mut(),
+ )
+ .build()
+ {
+ *data_changed = true;
+ }
+ }
+ ItemType::Color(ColorItemType::Rgb, LiteralType::F32) => {
+ let mut item = item.into_writable::<Rgb<f32>>();
+
+ let mut scratch = [item.r, item.g, item.b];
+
+ if frame
+ .color_edit3_config(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ &mut scratch,
+ )
+ .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex)
+ .build()
+ {
+ let [new_r, new_g, new_b] = scratch;
+
+ *item = Rgb { r: new_r, g: new_g, b: new_b };
+
+ *data_changed = true;
+ }
+ }
+ ItemType::Color(ColorItemType::Rgb, LiteralType::U8) => {
+ let mut item = item.into_writable::<Rgb<u8>>();
+
+ let mut scratch = [
+ item.r as f32 / 255.0,
+ item.g as f32 / 255.0,
+ item.b as f32 / 255.0,
+ ];
+
+ if frame
+ .color_edit3_config(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ &mut scratch,
+ )
+ .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex)
+ .build()
+ {
+ let [new_r, new_g, new_b] = scratch;
+
+ *item = Rgb {
+ r: (new_r * 255.0) as u8,
+ g: (new_g * 255.0) as u8,
+ b: (new_b * 255.0) as u8,
+ };
+
+ *data_changed = true;
+ }
+ }
+ ItemType::Color(ColorItemType::Rgba, LiteralType::F32) => {
+ let mut item = item.into_writable::<Rgba<f32>>();
+
+ let mut scratch = [item.r, item.g, item.b, item.a];
+
+ if frame
+ .color_edit4_config(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ &mut scratch,
+ )
+ .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex)
+ .build()
+ {
+ let [new_r, new_g, new_b, new_a] = scratch;
+
+ *item = Rgba {
+ r: new_r,
+ g: new_g,
+ b: new_b,
+ a: new_a,
+ };
+
+ *data_changed = true;
+ }
+ }
+ ItemType::Color(ColorItemType::Rgba, LiteralType::U8) => {
+ let mut item = item.into_writable::<Rgba<u8>>();
+
+ let mut scratch = [
+ item.r as f32 / 255.0,
+ item.g as f32 / 255.0,
+ item.b as f32 / 255.0,
+ item.a as f32 / 255.0,
+ ];
+
+ if frame
+ .color_edit4_config(
+ create_item_label(&item_tag, "value_input", &prev_item_tags),
+ &mut scratch,
+ )
+ .display_mode(dear_imgui_bindings::ColorDisplayMode::Hex)
+ .build()
+ {
+ let [new_r, new_g, new_b, new_a] = scratch;
+
+ *item = Rgba {
+ r: (new_r * 255.0) as u8,
+ g: (new_g * 255.0) as u8,
+ b: (new_b * 255.0) as u8,
+ a: (new_a * 255.0) as u8,
+ };
+
+ *data_changed = true;
+ }
+ }
+ ItemType::Color(ColorItemType::Rgb, _) => unreachable!(),
+ ItemType::Color(ColorItemType::Rgba, _) => unreachable!(),
+ ItemType::Reflected(_) => unimplemented!(),
+ }
+}
+
+fn create_scalar_item_input<Scalar>(
+ frame: &dear_imgui_bindings::Ui,
+ item: &mut ItemRef<'_>,
+ data_changed: &mut bool,
+ item_tag: &str,
+ prev_item_tags: &[&str],
+) where
+ Scalar: dear_imgui_bindings::internal::DataTypeKind + Clone + 'static,
+{
+ let item = match item {
+ ItemRef::Mutable(item) => {
+ let Some(item) = (*item).downcast_mut::<Scalar>() else {
+ unreachable!();
+ };
+
+ item
+ }
+ ItemRef::Immutable(item) => {
+ let Some(item) = (*item).downcast_ref::<Scalar>() else {
+ unreachable!();
+ };
+
+ &mut item.clone()
+ }
+ };
+
+ if frame
+ .input_scalar(
+ create_item_label(item_tag, "value_input", &prev_item_tags),
+ item,
+ )
+ .build()
+ {
+ *data_changed = true;
+ }
+}
+
+fn item_layout_table<'frame>(
+ frame: &'frame dear_imgui_bindings::Ui,
+ prev_item_tags: &[&str],
+) -> dear_imgui_bindings::TableBuilder<'frame>
+{
+ frame
+ .table(create_item_label("item_layout", "table", &prev_item_tags))
+ .headers(false)
+ .sizing_policy(dear_imgui_bindings::TableSizingPolicy::StretchSame)
+ .columns([
+ dear_imgui_bindings::TableColumnSetup::new("a"),
+ dear_imgui_bindings::TableColumnSetup::new("b").stretch_weight(2.0),
+ ])
+}
+
+fn create_struct_widgets(
+ frame: &dear_imgui_bindings::Ui,
+ item: &mut ItemRef<'_>,
+ data_changed: &mut bool,
+ item_tag: &str,
+ item_is_read_only: bool,
+ struct_ty: &StructReflection,
+ prev_item_tags: &[&str],
+)
+{
+ let mut prev_item_tags = prev_item_tags.to_vec();
+
+ prev_item_tags.push(item_tag.as_ref());
+
+ let struct_fields =
+ struct_ty
+ .fields
+ .iter()
+ .enumerate()
+ .filter_map(|(field_index, field)| {
+ let field_item_type =
+ get_item_type(field.type_reflection(), field.type_id)?;
+
+ Some((field_index, field, field_item_type))
+ });
+
+ item_layout_table(frame, &prev_item_tags).build(|frame| {
+ for (field_index, field, field_item_type) in struct_fields
+ .clone()
+ .filter(|(.., field_item_type)| !field_item_type.spans_multiple_rows())
+ {
+ frame.table_next_row();
+ frame.table_next_column();
+
+ let field_name = field
+ .name
+ .map(Cow::Borrowed)
+ .unwrap_or_else(|| field_index.to_string().into());
+
+ let Ok(field_item) = item.get_struct_field(struct_ty, field_index) else {
+ unreachable!();
+ };
+
+ create_item_title_widget(
+ frame,
+ &field_name,
+ &field_item_type,
+ Some(field.type_name()),
+ );
+
+ frame.table_next_column();
+
+ add_item_to_frame(
+ frame,
+ field_item,
+ data_changed,
+ ItemInfo {
+ item_title: field_name.as_ref(),
+ item_tag: field_name.as_ref(),
+ item_type_name: Some(field.type_name()),
+ item_type: field_item_type,
+ item_is_read_only: item_is_read_only
+ || matches!(
+ field.visibility,
+ Visibility::Private | Visibility::PubScoped(_)
+ ),
+ },
+ &prev_item_tags,
+ );
+ }
+ });
+
+ for (field_index, field, field_item_type) in struct_fields
+ .filter(|(.., field_item_type)| field_item_type.spans_multiple_rows())
+ {
+ let field_name = field
+ .name
+ .map(Cow::Borrowed)
+ .unwrap_or_else(|| field_index.to_string().into());
+
+ let Ok(field_item) = item.get_struct_field(struct_ty, field_index) else {
+ unreachable!();
+ };
+
+ create_item_title_widget(
+ frame,
+ &field_name,
+ &field_item_type,
+ Some(field.type_name()),
+ );
+
+ let _token = frame.begin_indent();
+
+ add_item_to_frame(
+ frame,
+ field_item,
+ data_changed,
+ ItemInfo {
+ item_title: field_name.as_ref(),
+ item_tag: field_name.as_ref(),
+ item_type_name: Some(field.type_name()),
+ item_type: field_item_type,
+ item_is_read_only: item_is_read_only
+ || matches!(
+ field.visibility,
+ Visibility::Private | Visibility::PubScoped(_)
+ ),
+ },
+ &prev_item_tags,
+ );
+ }
+}
+
+fn create_enum_widgets(
+ frame: &dear_imgui_bindings::Ui,
+ item: &mut ItemRef<'_>,
+ data_changed: &mut bool,
+ item_tag: &str,
+ item_is_read_only: bool,
+ enum_type: &EnumReflection,
+ prev_item_tags: &[&str],
+)
+{
+ let Some(mut curr_variant_index) = (enum_type.get_variant_index)(item.as_ref())
+ else {
+ unreachable!();
+ };
+
+ if create_combo_box(
+ frame,
+ create_item_label(&item_tag, "variant_select", &prev_item_tags),
+ &mut curr_variant_index,
+ enum_type.variants,
+ |variant| {
+ let disabled = variant.fields.as_ref().and_then(|fields| {
+ fields.fields().iter().find_map(|field| {
+ let field_name = match field.name {
+ Some(field_name) => Either::A(field_name),
+ None => Either::B(field.index),
+ };
+
+ let Some(field_type) = field.type_reflection() else {
+ return Some((field_name, "has no type reflection"));
+ };
+
+ if !field_type.has_default_value() {
+ return Some((field_name, "has no default value"));
+ }
+
+ None
+ })
+ });
+
+ ComboBoxItem {
+ label: variant.name.into(),
+ disabled: disabled.is_some(),
+ hover_tooltip: disabled.map(|(bad_field_name, bad_field_reason)| {
+ format!(
+ "Variant cannot be selected since field {} {}",
+ bad_field_name, bad_field_reason
+ )
+ .into()
+ }),
+ }
+ },
+ ) {
+ 'used: {
+ let ItemRef::Mutable(item) = item else {
+ break 'used;
+ };
+
+ let new_variant = &enum_type.variants[curr_variant_index];
+
+ let Ok(()) = (new_variant.try_write_new_to)(
+ *item,
+ &mut new_variant
+ .fields
+ .iter()
+ .map(|fields| fields.fields())
+ .flatten()
+ .map(|field| {
+ let Some(field_type) = field.type_reflection() else {
+ // Variants with any field that is missing type reflection is
+ // disabled so that the user cannot select it
+ unreachable!();
+ };
+
+ let Some(default_value) = field_type.default_value() else {
+ unreachable!();
+ };
+
+ default_value
+ }),
+ ) else {
+ unreachable!();
+ };
+
+ *data_changed = true;
+ }
+ }
+
+ let mut prev_item_tags = prev_item_tags.to_vec();
+
+ prev_item_tags.push(item_tag);
+
+ let curr_variant = &enum_type.variants[curr_variant_index];
+
+ let Some(curr_variant_fields) = &curr_variant.fields else {
+ return;
+ };
+
+ let variant_fields = curr_variant_fields.fields().iter().enumerate().filter_map(
+ |(field_index, field)| {
+ let field_item_type = get_item_type(field.type_reflection(), field.type_id)?;
+
+ Some((field_index, field, field_item_type))
+ },
+ );
+
+ let variant_field_cnt = variant_fields.clone().count();
+
+ item_layout_table(frame, &prev_item_tags).build(|_| {
+ for (field_index, field, field_item_type) in variant_fields
+ .clone()
+ .filter(|(.., field_item_type)| !field_item_type.spans_multiple_rows())
+ {
+ frame.table_next_row();
+ frame.table_next_column();
+
+ let field_name: Cow<str> = match field.name {
+ Some(field_name) => field_name.into(),
+ None => field_index.to_string().into(),
+ };
+
+ let Ok(field_item) = item.get_enum_variant_field(curr_variant, field_index)
+ else {
+ unreachable!();
+ };
+
+ if variant_field_cnt != 1 || field.name.is_some() {
+ create_item_title_widget(
+ frame,
+ &field_name,
+ &field_item_type,
+ Some(field.type_name()),
+ );
+
+ frame.table_next_column();
+ }
+
+ add_item_to_frame(
+ frame,
+ field_item,
+ data_changed,
+ ItemInfo {
+ item_title: field_name.as_ref(),
+ item_tag: field_name.as_ref(),
+ item_type_name: Some(field.type_name()),
+ item_type: field_item_type,
+ item_is_read_only: item_is_read_only,
+ },
+ &prev_item_tags,
+ );
+ }
+ });
+
+ for (field_index, field, field_item_type) in variant_fields
+ .filter(|(.., field_item_type)| field_item_type.spans_multiple_rows())
+ {
+ let field_name: Cow<str> = match field.name {
+ Some(field_name) => field_name.into(),
+ None => field_index.to_string().into(),
+ };
+
+ let Ok(field_item) = item.get_enum_variant_field(curr_variant, field_index)
+ else {
+ unreachable!();
+ };
+
+ if variant_field_cnt != 1 || field.name.is_some() {
+ create_item_title_widget(
+ frame,
+ &field_name,
+ &field_item_type,
+ Some(field.type_name()),
+ );
+
+ let _token = frame.begin_indent();
+ }
+
+ add_item_to_frame(
+ frame,
+ field_item,
+ data_changed,
+ ItemInfo {
+ item_title: field_name.as_ref(),
+ item_tag: field_name.as_ref(),
+ item_type_name: Some(field.type_name()),
+ item_type: field_item_type,
+ item_is_read_only: item_is_read_only,
+ },
+ &prev_item_tags,
+ );
+ }
+}
+
+fn create_item_label(item_tag: &str, value_name: &str, prev_item_tags: &[&str])
+ -> String
+{
+ let mut item_label = String::with_capacity(
+ 2 + item_tag.len()
+ + prev_item_tags.len()
+ + prev_item_tags.iter().max().unwrap_or(&"".into()).len(),
+ );
+
+ item_label.push_str("##");
+
+ for prev_item_tag in prev_item_tags {
+ item_label.push('/');
+ item_label.push_str(prev_item_tag);
+ }
+
+ item_label.push('/');
+ item_label.push_str(item_tag);
+ item_label.push(':');
+ item_label.push_str(value_name);
+
+ item_label
+}
+
+fn get_whole_type_is_user_editable(
+ ty: Option<&'static TypeReflection>,
+ type_id: TypeId,
+) -> bool
+{
+ let Some(item_type) = get_item_type(ty, type_id) else {
+ return false;
+ };
+
+ match item_type {
+ ItemType::Reflected(TypeReflection::Struct(struct_ty)) => {
+ for field in struct_ty.fields {
+ if matches!(
+ field.visibility,
+ Visibility::Private | Visibility::PubScoped(_)
+ ) {
+ continue;
+ }
+
+ if !get_whole_type_is_user_editable(
+ field.type_reflection(),
+ field.type_id,
+ ) {
+ return false;
+ }
+ }
+
+ true
+ }
+ ItemType::Reflected(TypeReflection::Enum(enum_ty)) => {
+ enum_ty.variants.iter().any(|variant| {
+ for field in variant.fields.iter().flat_map(|fields| fields.fields()) {
+ if !get_whole_type_is_user_editable(
+ field.type_reflection(),
+ field.type_id,
+ ) {
+ return false;
+ }
+ }
+
+ true
+ })
+ }
+ ItemType::Reflected(
+ TypeReflection::Slice(_)
+ | TypeReflection::Array(_)
+ | TypeReflection::Literal(_),
+ )
+ | ItemType::String
+ | ItemType::CowStr
+ | ItemType::Color(..) => true,
+ ItemType::Reflected(_) => unimplemented!(),
+ }
+}
+
+struct ComboBoxItem<'b>
+{
+ label: Cow<'b, str>,
+ disabled: bool,
+ hover_tooltip: Option<Cow<'b, str>>,
+}
+
+fn create_combo_box<Item, ItemFn>(
+ frame: &dear_imgui_bindings::Ui,
+ label: impl AsRef<str>,
+ current_item: &mut usize,
+ items: &[Item],
+ item_fn: ItemFn,
+) -> bool
+where
+ for<'b> ItemFn: Fn(&'b Item) -> ComboBoxItem<'b>,
+{
+ let item_fn = &item_fn;
+
+ let mut result = false;
+
+ let preview_label = items.get(*current_item).map(|item| item_fn(item).label);
+
+ if let Some(combo_token) = frame.begin_combo(
+ label,
+ preview_label
+ .as_ref()
+ .map(|preview_label| preview_label.as_ref())
+ .unwrap_or(""),
+ ) {
+ for (idx, item) in items.iter().enumerate() {
+ let is_selected = idx == *current_item;
+
+ if is_selected {
+ frame.set_item_default_focus();
+ }
+
+ let ComboBoxItem {
+ label: item_label,
+ disabled: item_disabled,
+ hover_tooltip: item_hover_tooltip,
+ } = item_fn(item);
+
+ let disabled_token = frame.begin_disabled_with_cond(item_disabled);
+
+ let clicked = frame.selectable(item_label.as_ref());
+
+ if clicked {
+ *current_item = idx;
+ result = true;
+ }
+
+ disabled_token.end();
+
+ if let Some(item_hover_tooltip) = item_hover_tooltip {
+ if frame.is_item_hovered_with_flags(
+ dear_imgui_bindings::ItemHoveredFlags::ALLOW_WHEN_DISABLED,
+ ) {
+ frame.tooltip_text(item_hover_tooltip.as_ref());
+ }
+ }
+ }
+
+ combo_token.end();
+ }
+
+ result
+}
+
+struct ListBoxItem<'b>
+{
+ label: Cow<'b, str>,
+ disabled: bool,
+ hover_tooltip: Option<Cow<'b, str>>,
+}
+
+trait ListBoxExt
+{
+ fn build_extended<Item, ItemFn>(
+ self,
+ ui: &dear_imgui_bindings::Ui,
+ current_item: &mut i32,
+ items: &[Item],
+ item_fn: &ItemFn,
+ ) -> bool
+ where
+ for<'b> ItemFn: Fn(&'b Item) -> ListBoxItem<'b>;
+}
+
+impl<Label> ListBoxExt for dear_imgui_bindings::ListBox<Label>
+where
+ Label: AsRef<str>,
+{
+ fn build_extended<Item, ItemFn>(
+ self,
+ ui: &dear_imgui_bindings::Ui,
+ current_item: &mut i32,
+ items: &[Item],
+ item_fn: &ItemFn,
+ ) -> bool
+ where
+ for<'b> ItemFn: Fn(&'b Item) -> ListBoxItem<'b>,
+ {
+ let mut result = false;
+
+ let lb = self;
+
+ if let Some(_cb) = lb.begin(ui) {
+ for (idx, item) in items.iter().enumerate() {
+ if idx > i32::MAX as usize {
+ break;
+ }
+
+ let idx_i32 = idx as i32;
+
+ let ListBoxItem {
+ label: item_label,
+ disabled: item_disabled,
+ hover_tooltip: item_hover_tooltip,
+ } = item_fn(item);
+
+ let is_selected = idx_i32 == *current_item;
+
+ let disabled_token = ui.begin_disabled_with_cond(item_disabled);
+
+ if ui
+ .selectable_config(&item_label)
+ .selected(is_selected)
+ .build()
+ {
+ *current_item = idx_i32;
+ result = true;
+ }
+
+ disabled_token.end();
+
+ if let Some(item_hover_tooltip) = item_hover_tooltip {
+ if ui.is_item_hovered_with_flags(
+ dear_imgui_bindings::ItemHoveredFlags::ALLOW_WHEN_DISABLED,
+ ) {
+ ui.tooltip_text(item_hover_tooltip.as_ref());
+ }
+ }
+ }
+ }
+
+ result
+ }
+}
+
+fn create_entity_title(ent_handle: &EntityHandle) -> String
+{
+ let ent_name = ent_handle.get::<EntityName>();
+
+ format!(
+ "{} {}",
+ ent_name
+ .as_ref()
+ .map(|ent_name| ent_name.name.as_ref())
+ .unwrap_or("<unnamed>"),
+ ent_handle.uid()
+ )
+}
+
+struct Textures
+{
+ despawn_icon_tex_id: dear_imgui_bindings::ManagedTextureId,
+ warning_icon_tex_id: dear_imgui_bindings::ManagedTextureId,
+ edit_icon_tex_id: dear_imgui_bindings::ManagedTextureId,
+}
+
+impl Textures
+{
+ fn new(dear_imgui_context: &mut DearImguiContext) -> Self
+ {
+ let despawn_icon_tex_id = create_texture(
+ dear_imgui_context,
+ Image::from_reader(
+ Cursor::new(include_bytes!("../../../res/ui/delete.png")),
+ ImageFormat::Png,
+ )
+ .unwrap(),
+ );
+
+ let warning_icon_tex_id = create_texture(
+ dear_imgui_context,
+ Image::from_reader(
+ Cursor::new(include_bytes!("../../../res/ui/warning.png")),
+ ImageFormat::Png,
+ )
+ .unwrap(),
+ );
+
+ let edit_icon_tex_id = create_texture(
+ dear_imgui_context,
+ Image::from_reader(
+ Cursor::new(include_bytes!("../../../res/ui/edit.png")),
+ ImageFormat::Png,
+ )
+ .unwrap(),
+ );
+
+ Self {
+ despawn_icon_tex_id,
+ warning_icon_tex_id,
+ edit_icon_tex_id,
+ }
+ }
+}
+
+#[derive(Debug)]
+struct AddComponentPopupState
+{
+ popup_name: String,
+ target_entity_id: Uid,
+ search_text: String,
+ searched_components: Vec<(Uid, ComponentInfo, ComponentUserCreatable)>,
+ searchable_components: Vec<(Uid, ComponentInfo, ComponentUserCreatable)>,
+ selected_search_result: i32,
+ new_component: Option<(Box<dyn Any>, ComponentInfo, Uid)>,
+}
+
+#[derive(Debug)]
+struct RenameEntityPopupState
+{
+ popup_name: String,
+ target_entity_id: Uid,
+ new_entity_name: String,
+}
+
+#[derive(Debug)]
+enum PopupState
+{
+ AddComponent(AddComponentPopupState),
+ RenameEntity(RenameEntityPopupState),
+}
diff --git a/engine/src/util.rs b/engine/src/util.rs
index 0f6c78c..a7caa2a 100644
--- a/engine/src/util.rs
+++ b/engine/src/util.rs
@@ -1,17 +1,308 @@
-use std::marker::PhantomData;
-
-macro_rules! try_option {
- ($expr: expr) => {
- match $expr {
- Ok(value) => value,
- Err(err) => {
- return Some(Err(err.into()));
- }
+use std::fmt::{Debug, Display};
+
+use crate::ecs::util::StreamingIterator;
+
+pub trait OptionExt<T>
+{
+ /// Substitute for the currently experimental function
+ /// [`Option::get_or_try_insert_with`].
+ /// See https://github.com/rust-lang/rust/issues/143648
+ fn get_or_try_insert_with_fn<Err>(
+ &mut self,
+ func: impl Fn() -> Result<T, Err>,
+ ) -> Result<&mut T, Err>;
+}
+
+impl<T> OptionExt<T> for Option<T>
+{
+ fn get_or_try_insert_with_fn<Err>(
+ &mut self,
+ func: impl FnOnce() -> Result<T, Err>,
+ ) -> Result<&mut T, Err>
+ {
+ if let None = self {
+ *self = Some(func()?);
}
- };
+
+ Ok(unsafe { self.as_mut().unwrap_unchecked() })
+ }
+}
+
+pub struct DisplaySlice<'a, Item>
+{
+ slice: &'a [Item],
+}
+
+impl<'a, Item> DisplaySlice<'a, Item>
+{
+ pub fn new(slice: &'a [Item]) -> Self
+ {
+ Self { slice }
+ }
+}
+
+impl<Item> Display for DisplaySlice<'_, Item>
+where
+ Item: Display,
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ let Some(first_item) = self.slice.first() else {
+ return formatter.write_str("<empty>");
+ };
+
+ write!(formatter, "{first_item}")?;
+
+ if self.slice.len() == 1 {
+ return Ok(());
+ }
+
+ for item in &self.slice[1..self.slice.len() - 1] {
+ write!(formatter, ", {item}")?;
+ }
+
+ let Some(last_item) = self.slice.last() else {
+ unreachable!();
+ };
+
+ write!(formatter, " & {last_item}")?;
+
+ Ok(())
+ }
+}
+
+#[derive(Debug)]
+pub struct BitArray<const SIZE: usize, const BITS_PER_ITEM: usize>
+{
+ inner: [u8; SIZE],
+}
+
+impl<const SIZE: usize, const BITS_PER_ITEM: usize> BitArray<SIZE, BITS_PER_ITEM>
+{
+ const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM);
+
+ pub fn new() -> Self
+ {
+ assert!(BITS_PER_ITEM > 1);
+ assert!(BITS_PER_ITEM <= 8);
+ assert_eq!(BITS_PER_ITEM % 2, 0);
+
+ Self { inner: [0; SIZE] }
+ }
+
+ pub fn get(&self, item_index: usize) -> u8
+ {
+ let bit_index = item_index * BITS_PER_ITEM;
+
+ let byte_index = bit_index / 8;
+
+ let bit_index_in_byte = bit_index - (byte_index * 8);
+
+ (self.inner[byte_index] >> (bit_index_in_byte)) & Self::ITEM_MASK
+ }
+
+ #[tracing::instrument(skip(self))]
+ pub fn clear_and_set(&mut self, item_index: usize, item_bits: u8, clear_mask: u8)
+ {
+ let item_bits = item_bits & Self::ITEM_MASK;
+
+ let clear_mask = clear_mask & Self::ITEM_MASK;
+
+ let bit_index = item_index * BITS_PER_ITEM;
+
+ let byte_index = bit_index / 8;
+
+ let bit_index_in_byte = bit_index - (byte_index * 8);
+
+ tracing::trace!(
+ bit_index,
+ byte_index,
+ bit_index_in_byte,
+ "Setting item bits"
+ );
+
+ self.inner[byte_index] &= !(clear_mask << bit_index_in_byte);
+
+ self.inner[byte_index] |= item_bits << bit_index_in_byte;
+ }
+
+ pub fn iter_occupied(&self) -> BitArrayOccupiedIter<'_, BITS_PER_ITEM>
+ {
+ BitArrayOccupiedIter {
+ inner: self.inner.iter().enumerate().peekable(),
+ mask: u8::MAX,
+ }
+ }
+
+ pub fn iter_occupied_mut(&mut self) -> BitArrayOccupiedMutIter<'_, BITS_PER_ITEM>
+ {
+ BitArrayOccupiedMutIter {
+ inner: self.inner.iter_mut().enumerate().peekable(),
+ mask: u8::MAX,
+ }
+ }
+
+ pub fn bytes_mut(&mut self) -> &mut [u8]
+ {
+ &mut self.inner
+ }
+}
+
+impl<const SIZE: usize, const BITS_PER_ITEM: usize> Default
+ for BitArray<SIZE, BITS_PER_ITEM>
+{
+ fn default() -> Self
+ {
+ Self::new()
+ }
+}
+
+pub struct BitArrayOccupiedIter<'a, const BITS_PER_ITEM: usize>
+{
+ inner: std::iter::Peekable<std::iter::Enumerate<std::slice::Iter<'a, u8>>>,
+ mask: u8,
+}
+
+impl<const BITS_PER_ITEM: usize> BitArrayOccupiedIter<'_, BITS_PER_ITEM>
+{
+ const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM);
+}
+
+impl<const BITS_PER_ITEM: usize> Iterator for BitArrayOccupiedIter<'_, BITS_PER_ITEM>
+{
+ type Item = (usize, u8);
+
+ fn next(&mut self) -> Option<Self::Item>
+ {
+ let (byte_masked, byte_index, item_bit_index_in_byte) = loop {
+ let current = self.inner.peek().and_then(|(byte_index, byte)| {
+ let byte_masked = **byte & self.mask;
+
+ let lowest_one = byte_masked.lowest_one()?;
+
+ let item_bit_index_in_byte = match lowest_one as usize % BITS_PER_ITEM {
+ 0 => lowest_one as usize,
+ remainder => lowest_one as usize - remainder,
+ };
+
+ Some((byte_masked, byte_index, item_bit_index_in_byte))
+ });
+
+ let Some((byte_masked, byte_index, item_bit_index_in_byte)) = current else {
+ let _ = self.inner.next()?;
+
+ self.mask = u8::MAX;
+
+ continue;
+ };
+
+ break (byte_masked, byte_index, item_bit_index_in_byte);
+ };
+
+ let item_bits = (byte_masked >> (item_bit_index_in_byte)) & Self::ITEM_MASK;
+
+ self.mask &= (!Self::ITEM_MASK) << item_bit_index_in_byte;
+
+ let item_index_in_byte = item_bit_index_in_byte as usize / BITS_PER_ITEM;
+
+ let prev_bytes_item_cnt = (byte_index * 8) / BITS_PER_ITEM;
+
+ let index = prev_bytes_item_cnt + item_index_in_byte;
+
+ Some((index, item_bits))
+ }
+}
+
+pub struct BitArrayOccupiedMutIter<'a, const BITS_PER_ITEM: usize>
+{
+ inner: std::iter::Peekable<std::iter::Enumerate<std::slice::IterMut<'a, u8>>>,
+ mask: u8,
+}
+
+impl<const BITS_PER_ITEM: usize> BitArrayOccupiedMutIter<'_, BITS_PER_ITEM>
+{
+ const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM);
+}
+
+impl<const BITS_PER_ITEM: usize> StreamingIterator
+ for BitArrayOccupiedMutIter<'_, BITS_PER_ITEM>
+{
+ type Item<'a>
+ = BitArrayItemMut<'a, BITS_PER_ITEM>
+ where
+ Self: 'a;
+
+ fn streaming_next(&mut self) -> Option<Self::Item<'_>>
+ {
+ let (byte_masked, byte_index, item_bit_index_in_byte) = loop {
+ let current = self.inner.peek().and_then(|(byte_index, byte)| {
+ let byte_masked = **byte & self.mask;
+
+ let lowest_one = byte_masked.lowest_one()?;
+
+ let item_bit_index_in_byte = match lowest_one as usize % BITS_PER_ITEM {
+ 0 => lowest_one as usize,
+ remainder => lowest_one as usize - remainder,
+ };
+
+ Some((byte_masked, byte_index, item_bit_index_in_byte))
+ });
+
+ let Some((byte_masked, byte_index, item_bit_index_in_byte)) = current else {
+ let _ = self.inner.next()?;
+
+ self.mask = u8::MAX;
+
+ continue;
+ };
+
+ break (byte_masked, byte_index, item_bit_index_in_byte);
+ };
+
+ let item_bits = (byte_masked >> (item_bit_index_in_byte)) & Self::ITEM_MASK;
+
+ self.mask &= (!Self::ITEM_MASK) << item_bit_index_in_byte;
+
+ let item_index_in_byte = item_bit_index_in_byte as usize / BITS_PER_ITEM;
+
+ let prev_bytes_item_cnt = (byte_index * 8) / BITS_PER_ITEM;
+
+ let index = prev_bytes_item_cnt + item_index_in_byte;
+
+ Some(BitArrayItemMut {
+ index,
+ bits: item_bits,
+ byte: self.inner.peek_mut().unwrap().1,
+ bit_index_in_byte: item_bit_index_in_byte,
+ })
+ }
}
-pub(crate) use try_option;
+pub struct BitArrayItemMut<'a, const BITS_PER_ITEM: usize>
+{
+ pub index: usize,
+ pub bits: u8,
+ byte: &'a mut u8,
+ bit_index_in_byte: usize,
+}
+
+impl<'a, const BITS_PER_ITEM: usize> BitArrayItemMut<'a, BITS_PER_ITEM>
+{
+ const ITEM_MASK: u8 = !(u8::MAX << BITS_PER_ITEM);
+
+ pub fn clear_and_set(&mut self, new_bits: u8, clear_mask: u8)
+ {
+ let new_bits = new_bits & Self::ITEM_MASK;
+
+ let clear_mask = clear_mask & Self::ITEM_MASK;
+
+ *self.byte &= !(clear_mask << self.bit_index_in_byte);
+
+ *self.byte |= new_bits << self.bit_index_in_byte;
+
+ self.bits = new_bits;
+ }
+}
macro_rules! or {
(($($tt: tt)+) else ($($else_tt: tt)*)) => {
@@ -25,6 +316,18 @@ macro_rules! or {
pub(crate) use or;
+#[macro_export]
+macro_rules! expand_map_opt {
+ ($in: tt, no_occurance=($($no_occurance: tt)*), occurance=($($occurance: tt)*)) => {
+ $($occurance)*
+ };
+
+ (, no_occurance=($($no_occurance: tt)*), occurance=($($occurance: tt)*)) => {
+ $($no_occurance)*
+ };
+}
+
+#[macro_export]
macro_rules! builder {
(
$(#[doc = $doc: literal])*
@@ -36,7 +339,8 @@ macro_rules! builder {
$visibility: vis struct $name: ident
{
$(
- $(#[$field_attr: meta])*
+ $(#[doc = $field_doc: literal])*
+ $(#[builder(skip_generate_fn$($field_skip_generate_fn: tt)?)])?
$field_visibility: vis $field: ident: $field_type: ty,
)*
}
@@ -46,7 +350,7 @@ macro_rules! builder {
$visibility struct $name
{
$(
- $(#[$field_attr])*
+ $(#[doc = $field_doc])*
$field_visibility $field: $field_type,
)*
}
@@ -62,16 +366,23 @@ macro_rules! builder {
impl $builder_name
{
$(
- #[must_use]
- $visibility fn $field(mut self, $field: $field_type) -> Self
- {
- self.$field = $field;
- self
- }
+ $crate::expand_map_opt!(
+ $(true $($field_skip_generate_fn)?)?,
+ no_occurance=(
+ #[must_use]
+ $visibility fn $field(mut self, $field: $field_type) -> Self
+ {
+ self.$field = $field;
+ self
+ }
+ ),
+ occurance=()
+ );
)*
#[must_use]
- $visibility fn build(self) -> $name {
+ $visibility const fn build(self) -> $name
+ {
$name {
$(
$field: self.$field,
@@ -82,6 +393,7 @@ macro_rules! builder {
impl From<$name> for $builder_name
{
+ #[allow(unused_variables)]
fn from(built: $name) -> Self
{
Self {
@@ -93,77 +405,3 @@ macro_rules! builder {
}
};
}
-
-pub(crate) use builder;
-
-pub enum RefOrValue<'a, T>
-{
- Ref(&'a T),
- Value(Option<T>),
-}
-
-impl<'a, T> RefOrValue<'a, T>
-{
- pub fn get(&self) -> Option<&T>
- {
- match self {
- Self::Ref(val_ref) => Some(val_ref),
- Self::Value(val_cell) => val_cell.as_ref(),
- }
- }
-}
-
-#[derive(Debug)]
-pub struct Defer<'func, Func, Data>
-where
- Func: FnMut(&mut Data) + 'func,
-{
- func: Func,
- pub data: Data,
- _pd: PhantomData<&'func ()>,
-}
-
-impl<'func, Func, Data> Defer<'func, Func, Data>
-where
- Func: FnMut(&mut Data) + 'func,
-{
- pub fn new(data: Data, func: Func) -> Self
- {
- Self { func, data, _pd: PhantomData }
- }
-}
-
-impl<'func, Func, Data> Drop for Defer<'func, Func, Data>
-where
- Func: FnMut(&mut Data) + 'func,
-{
- fn drop(&mut self)
- {
- (self.func)(&mut self.data)
- }
-}
-
-/// Defines a function that will be called at the end of the current scope.
-///
-/// Only captured variables that are later mutably borrowed needs to specified as
-/// captures.
-macro_rules! defer {
- (|$capture: ident| {$($tt: tt)*}) => {
- // This uses the automatic temporary lifetime extension behaviour introduced
- // in Rust 1.79.0 (https://blog.rust-lang.org/2024/06/13/Rust-1.79.0.html) to
- // create a unnamable variable for the Defer struct. The variable should be
- // unnamable so that it cannot be missused and so that this macro can be used
- // multiple times without having to give it a identifier for the Defer struct
- // variable
- let Defer { data: $capture, .. } = if true {
- &Defer::new($capture, |$capture| {
- $($tt)*
- })
- }
- else {
- unreachable!();
- };
- };
-}
-
-pub(crate) use defer;
diff --git a/engine/src/window.rs b/engine/src/window.rs
deleted file mode 100644
index d342341..0000000
--- a/engine/src/window.rs
+++ /dev/null
@@ -1,753 +0,0 @@
-use std::borrow::Cow;
-use std::ffi::{CStr, CString};
-
-use bitflags::bitflags;
-use ecs::actions::Actions;
-use ecs::extension::Collector as ExtensionCollector;
-use ecs::pair::{ChildOf, Pair};
-use ecs::phase::{Phase, START as START_PHASE};
-use ecs::sole::Single;
-use ecs::{static_entity, Sole};
-use glfw::window::{Hint as WindowCreationHint, HintValue as WindowCreationHintValue};
-use glfw::WindowSize;
-use util_macros::VariantArr;
-
-use crate::data_types::dimens::Dimens;
-use crate::renderer::RENDER_PHASE;
-use crate::vector::Vec2;
-
-static_entity!(
- pub UPDATE_PHASE,
- (Phase, Pair::new::<ChildOf>(*RENDER_PHASE))
-);
-
-#[derive(Debug, Sole)]
-/// Has to be dropped last since it holds the OpenGL context.
-#[sole(drop_last)]
-pub struct Window
-{
- inner: glfw::Window,
-}
-
-impl Window
-{
- /// Returns a new Window builder.
- #[must_use]
- pub fn builder() -> Builder
- {
- Builder::default()
- }
-
- /// Sets the value of a input mode.
- ///
- /// # Errors
- /// Returns `Err` if the input mode is unsupported on the current system.
- pub fn set_input_mode(
- &self,
- input_mode: InputMode,
- enabled: bool,
- ) -> Result<(), Error>
- {
- Ok(self
- .inner
- .set_input_mode(input_mode.to_glfw_input_mode(), enabled)?)
- }
-
- /// Sets the cursor mode.
- ///
- /// # Errors
- /// If a platform error occurs.
- pub fn set_cursor_mode(&self, cursor_mode: CursorMode) -> Result<(), Error>
- {
- Ok(self
- .inner
- .set_cursor_mode(cursor_mode.to_glfw_cursor_mode())?)
- }
-
- /// Returns whether or not the window should close. Will return true when the user has
- /// attempted to close the window.
- #[must_use]
- pub fn should_close(&self) -> bool
- {
- self.inner.should_close()
- }
-
- /// Processes all pending events.
- ///
- /// # Errors
- /// If a platform error occurs.
- pub fn poll_events(&self) -> Result<(), Error>
- {
- Ok(self.inner.poll_events()?)
- }
-
- /// Swaps the front and back buffers of the window.
- ///
- /// # Errors
- /// Will return `Err` if a platform error occurs or if no OpenGL window context
- /// is present.
- pub fn swap_buffers(&self) -> Result<(), Error>
- {
- Ok(self.inner.swap_buffers()?)
- }
-
- /// Returns the size of the window.
- ///
- /// # Errors
- /// Will return `Err` if a platform error occurs.
- pub fn size(&self) -> Result<Dimens<u32>, Error>
- {
- let size = self.inner.size()?;
-
- Ok(Dimens {
- width: size.width,
- height: size.height,
- })
- }
-
- /// Returns the address of the specified OpenGL function, if it is supported by the
- /// current OpenGL context.
- ///
- /// # Errors
- /// Will return `Err` if a platform error occurs or if no current context has
- /// been set.
- ///
- /// # Panics
- /// Will panic if the `proc_name` argument contains a nul byte.
- pub fn get_proc_address(
- &self,
- proc_name: &str,
- ) -> Result<unsafe extern "C" fn(), Error>
- {
- let proc_name_c: Cow<CStr> = CStr::from_bytes_with_nul(proc_name.as_bytes())
- .map(Cow::Borrowed)
- .or_else(|_| CString::new(proc_name).map(Cow::Owned))
- .expect("OpenGL function name contains a nul byte");
-
- Ok(self.inner.get_proc_address(&proc_name_c)?)
- }
-
- /// Makes the OpenGL context of the window current for the calling thread.
- ///
- /// # Errors
- /// Will return `Err` if a platform error occurs or if no OpenGL context is
- /// present.
- pub fn make_context_current(&self) -> Result<(), Error>
- {
- Ok(self.inner.make_context_current()?)
- }
-
- /// Sets the window's framebuffer size callback.
- pub fn set_framebuffer_size_callback(&self, callback: impl Fn(Dimens<u32>) + 'static)
- {
- self.inner.set_framebuffer_size_callback(move |size| {
- callback(Dimens {
- width: size.width,
- height: size.height,
- });
- });
- }
-
- /// Sets the window's key size callback.
- pub fn set_key_callback(
- &self,
- callback: impl Fn(Key, i32, KeyState, KeyModifiers) + 'static,
- )
- {
- self.inner
- .set_key_callback(move |key, scancode, key_state, key_modifiers| {
- let Some(key_state) = KeyState::from_glfw_key_state(key_state) else {
- return;
- };
-
- callback(
- Key::from_glfw_key(key),
- scancode,
- key_state,
- KeyModifiers::from_bits_truncate(key_modifiers.bits()),
- )
- });
- }
-
- /// Sets the window's cursor position callback.
- pub fn set_cursor_pos_callback(&self, callback: impl Fn(Vec2<f64>) + 'static)
- {
- self.inner
- .set_cursor_pos_callback(move |pos| callback(Vec2 { x: pos.x, y: pos.y }));
- }
-
- /// Sets the window's mouse button callback. The given function is called when a mouse
- /// button enters a new state.
- pub fn set_mouse_button_callback(
- &self,
- callback: impl Fn(MouseButton, MouseButtonState, KeyModifiers) + 'static,
- )
- {
- self.inner.set_mouse_button_callback(
- move |mouse_button, mouse_button_state, key_modifiers| {
- callback(
- MouseButton::from_glfw_mouse_button(mouse_button),
- MouseButtonState::from_glfw_mouse_button_state(mouse_button_state),
- KeyModifiers::from_bits_truncate(key_modifiers.bits()),
- )
- },
- );
- }
-
- /// Sets the window's close callback.
- pub fn set_close_callback(&self, callback: impl Fn() + 'static)
- {
- self.inner.set_close_callback(callback);
- }
-
- /// Sets the window's focus callback. The callback is called when the window loses or
- /// gains input focus.
- pub fn set_focus_callback(&self, callback: impl Fn(bool) + 'static)
- {
- self.inner.set_focus_callback(callback);
- }
-}
-
-/// [`Window`] builder.
-#[derive(Debug, Clone, Default)]
-pub struct Builder
-{
- inner: glfw::WindowBuilder,
-}
-
-impl Builder
-{
- /// Sets whether the OpenGL context should be created in debug mode, which may
- /// provide additional error and diagnostic reporting functionality.
- pub fn opengl_debug_context(mut self, enabled: bool) -> Self
- {
- self.inner = self.inner.hint(
- WindowCreationHint::OpenGLDebugContext,
- WindowCreationHintValue::Bool(enabled),
- );
-
- self
- }
-
- /// Set the desired number of samples to use for multisampling. Zero disables
- /// multisampling.
- pub fn multisampling_sample_count(mut self, sample_count: u16) -> Self
- {
- self.inner = self.inner.hint(
- WindowCreationHint::Samples,
- WindowCreationHintValue::Number(sample_count as i32),
- );
-
- self
- }
-
- /// Creates a new window.
- ///
- /// # Errors
- /// Will return `Err` if the title contains a internal nul byte or if a platform error
- /// occurs.
- pub fn create(&self, size: Dimens<u32>, title: &str) -> Result<Window, Error>
- {
- let builder = self.inner.clone().hint(
- WindowCreationHint::OpenGLDebugContext,
- WindowCreationHintValue::Bool(true),
- );
-
- let window = builder.create(
- &WindowSize {
- width: size.width,
- height: size.height,
- },
- title,
- )?;
-
- Ok(Window { inner: window })
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArr)]
-#[variant_arr(name = KEYS)]
-pub enum Key
-{
- Space,
- Apostrophe,
- Comma,
- Minus,
- Period,
- Slash,
- Digit0,
- Digit1,
- Digit2,
- Digit3,
- Digit4,
- Digit5,
- Digit6,
- Digit7,
- Digit8,
- Digit9,
- Semicolon,
- Equal,
- A,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- I,
- J,
- K,
- L,
- M,
- N,
- O,
- P,
- Q,
- R,
- S,
- T,
- U,
- V,
- W,
- X,
- Y,
- Z,
- LeftBracket,
- Backslash,
- RightBracket,
- GraveAccent,
- World1,
- World2,
- Escape,
- Enter,
- Tab,
- Backspace,
- Insert,
- Delete,
- Right,
- Left,
- Down,
- Up,
- PageUp,
- PageDown,
- Home,
- End,
- CapsLock,
- ScrollLock,
- NumLock,
- PrintScreen,
- Pause,
- F1,
- F2,
- F3,
- F4,
- F5,
- F6,
- F7,
- F8,
- F9,
- F10,
- F11,
- F12,
- F13,
- F14,
- F15,
- F16,
- F17,
- F18,
- F19,
- F20,
- F21,
- F22,
- F23,
- F24,
- F25,
- Kp0,
- Kp1,
- Kp2,
- Kp3,
- Kp4,
- Kp5,
- Kp6,
- Kp7,
- Kp8,
- Kp9,
- KpDecimal,
- KpDivide,
- KpMultiply,
- KpSubtract,
- KpAdd,
- KpEnter,
- KpEqual,
- LeftShift,
- LeftControl,
- LeftAlt,
- LeftSuper,
- RightShift,
- RightControl,
- RightAlt,
- RightSuper,
- Menu,
-}
-
-impl Key
-{
- fn from_glfw_key(glfw_key: glfw::window::Key) -> Self
- {
- match glfw_key {
- glfw::window::Key::Space => Self::Space,
- glfw::window::Key::Apostrophe => Self::Apostrophe,
- glfw::window::Key::Comma => Self::Comma,
- glfw::window::Key::Minus => Self::Minus,
- glfw::window::Key::Period => Self::Period,
- glfw::window::Key::Slash => Self::Slash,
- glfw::window::Key::Digit0 => Self::Digit0,
- glfw::window::Key::Digit1 => Self::Digit1,
- glfw::window::Key::Digit2 => Self::Digit2,
- glfw::window::Key::Digit3 => Self::Digit3,
- glfw::window::Key::Digit4 => Self::Digit4,
- glfw::window::Key::Digit5 => Self::Digit5,
- glfw::window::Key::Digit6 => Self::Digit6,
- glfw::window::Key::Digit7 => Self::Digit7,
- glfw::window::Key::Digit8 => Self::Digit8,
- glfw::window::Key::Digit9 => Self::Digit9,
- glfw::window::Key::Semicolon => Self::Semicolon,
- glfw::window::Key::Equal => Self::Equal,
- glfw::window::Key::A => Self::A,
- glfw::window::Key::B => Self::B,
- glfw::window::Key::C => Self::C,
- glfw::window::Key::D => Self::D,
- glfw::window::Key::E => Self::E,
- glfw::window::Key::F => Self::F,
- glfw::window::Key::G => Self::G,
- glfw::window::Key::H => Self::H,
- glfw::window::Key::I => Self::I,
- glfw::window::Key::J => Self::J,
- glfw::window::Key::K => Self::K,
- glfw::window::Key::L => Self::L,
- glfw::window::Key::M => Self::M,
- glfw::window::Key::N => Self::N,
- glfw::window::Key::O => Self::O,
- glfw::window::Key::P => Self::P,
- glfw::window::Key::Q => Self::Q,
- glfw::window::Key::R => Self::R,
- glfw::window::Key::S => Self::S,
- glfw::window::Key::T => Self::T,
- glfw::window::Key::U => Self::U,
- glfw::window::Key::V => Self::V,
- glfw::window::Key::W => Self::W,
- glfw::window::Key::X => Self::X,
- glfw::window::Key::Y => Self::Y,
- glfw::window::Key::Z => Self::Z,
- glfw::window::Key::LeftBracket => Self::LeftBracket,
- glfw::window::Key::Backslash => Self::Backslash,
- glfw::window::Key::RightBracket => Self::RightBracket,
- glfw::window::Key::GraveAccent => Self::GraveAccent,
- glfw::window::Key::World1 => Self::World1,
- glfw::window::Key::World2 => Self::World2,
- glfw::window::Key::Escape => Self::Escape,
- glfw::window::Key::Enter => Self::Enter,
- glfw::window::Key::Tab => Self::Tab,
- glfw::window::Key::Backspace => Self::Backspace,
- glfw::window::Key::Insert => Self::Insert,
- glfw::window::Key::Delete => Self::Delete,
- glfw::window::Key::Right => Self::Right,
- glfw::window::Key::Left => Self::Left,
- glfw::window::Key::Down => Self::Down,
- glfw::window::Key::Up => Self::Up,
- glfw::window::Key::PageUp => Self::PageUp,
- glfw::window::Key::PageDown => Self::PageDown,
- glfw::window::Key::Home => Self::Home,
- glfw::window::Key::End => Self::End,
- glfw::window::Key::CapsLock => Self::CapsLock,
- glfw::window::Key::ScrollLock => Self::ScrollLock,
- glfw::window::Key::NumLock => Self::NumLock,
- glfw::window::Key::PrintScreen => Self::PrintScreen,
- glfw::window::Key::Pause => Self::Pause,
- glfw::window::Key::F1 => Self::F1,
- glfw::window::Key::F2 => Self::F2,
- glfw::window::Key::F3 => Self::F3,
- glfw::window::Key::F4 => Self::F4,
- glfw::window::Key::F5 => Self::F5,
- glfw::window::Key::F6 => Self::F6,
- glfw::window::Key::F7 => Self::F7,
- glfw::window::Key::F8 => Self::F8,
- glfw::window::Key::F9 => Self::F9,
- glfw::window::Key::F10 => Self::F10,
- glfw::window::Key::F11 => Self::F11,
- glfw::window::Key::F12 => Self::F12,
- glfw::window::Key::F13 => Self::F13,
- glfw::window::Key::F14 => Self::F14,
- glfw::window::Key::F15 => Self::F15,
- glfw::window::Key::F16 => Self::F16,
- glfw::window::Key::F17 => Self::F17,
- glfw::window::Key::F18 => Self::F18,
- glfw::window::Key::F19 => Self::F19,
- glfw::window::Key::F20 => Self::F20,
- glfw::window::Key::F21 => Self::F21,
- glfw::window::Key::F22 => Self::F22,
- glfw::window::Key::F23 => Self::F23,
- glfw::window::Key::F24 => Self::F24,
- glfw::window::Key::F25 => Self::F25,
- glfw::window::Key::Kp0 => Self::Kp0,
- glfw::window::Key::Kp1 => Self::Kp1,
- glfw::window::Key::Kp2 => Self::Kp2,
- glfw::window::Key::Kp3 => Self::Kp3,
- glfw::window::Key::Kp4 => Self::Kp4,
- glfw::window::Key::Kp5 => Self::Kp5,
- glfw::window::Key::Kp6 => Self::Kp6,
- glfw::window::Key::Kp7 => Self::Kp7,
- glfw::window::Key::Kp8 => Self::Kp8,
- glfw::window::Key::Kp9 => Self::Kp9,
- glfw::window::Key::KpDecimal => Self::KpDecimal,
- glfw::window::Key::KpDivide => Self::KpDivide,
- glfw::window::Key::KpMultiply => Self::KpMultiply,
- glfw::window::Key::KpSubtract => Self::KpSubtract,
- glfw::window::Key::KpAdd => Self::KpAdd,
- glfw::window::Key::KpEnter => Self::KpEnter,
- glfw::window::Key::KpEqual => Self::KpEqual,
- glfw::window::Key::LeftShift => Self::LeftShift,
- glfw::window::Key::LeftControl => Self::LeftControl,
- glfw::window::Key::LeftAlt => Self::LeftAlt,
- glfw::window::Key::LeftSuper => Self::LeftSuper,
- glfw::window::Key::RightShift => Self::RightShift,
- glfw::window::Key::RightControl => Self::RightControl,
- glfw::window::Key::RightAlt => Self::RightAlt,
- glfw::window::Key::RightSuper => Self::RightSuper,
- glfw::window::Key::Menu => Self::Menu,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum KeyState
-{
- Pressed,
- Released,
-}
-
-impl KeyState
-{
- fn from_glfw_key_state(glfw_key_state: glfw::window::KeyState) -> Option<Self>
- {
- match glfw_key_state {
- glfw::window::KeyState::Pressed => Some(Self::Pressed),
- glfw::window::KeyState::Released => Some(Self::Released),
- glfw::window::KeyState::Repeat => None,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum MouseButton
-{
- One,
- Two,
- Three,
- Four,
- Five,
- Six,
- Seven,
- Eight,
-}
-
-impl MouseButton
-{
- pub const LEFT: Self = Self::One;
- pub const MIDDLE: Self = Self::Three;
- pub const RIGHT: Self = Self::Two;
-
- fn from_glfw_mouse_button(mouse_button: glfw::window::MouseButton) -> Self
- {
- match mouse_button {
- glfw::window::MouseButton::One => Self::One,
- glfw::window::MouseButton::Two => Self::Two,
- glfw::window::MouseButton::Three => Self::Three,
- glfw::window::MouseButton::Four => Self::Four,
- glfw::window::MouseButton::Five => Self::Five,
- glfw::window::MouseButton::Six => Self::Six,
- glfw::window::MouseButton::Seven => Self::Seven,
- glfw::window::MouseButton::Eight => Self::Eight,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum MouseButtonState
-{
- Pressed,
- Released,
-}
-
-impl MouseButtonState
-{
- fn from_glfw_mouse_button_state(
- mouse_button_state: glfw::window::MouseButtonState,
- ) -> Self
- {
- match mouse_button_state {
- glfw::window::MouseButtonState::Pressed => Self::Pressed,
- glfw::window::MouseButtonState::Released => Self::Released,
- }
- }
-}
-
-bitflags! {
- #[derive(Debug, Clone, Copy)]
- pub struct KeyModifiers: i32 {
- const SHIFT = glfw::window::KeyModifiers::SHIFT.bits();
- const CONTROL = glfw::window::KeyModifiers::CONTROL.bits();
- const ALT = glfw::window::KeyModifiers::ALT.bits();
- const SUPER = glfw::window::KeyModifiers::SUPER.bits();
- const CAPS_LOCK = glfw::window::KeyModifiers::CAPS_LOCK.bits();
- const NUM_LOCK = glfw::window::KeyModifiers::NUM_LOCK.bits();
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum CursorMode
-{
- /// Hides and grabs the cursor, providing virtual and unlimited cursor movement.
- Disabled,
-
- /// Makes the cursor invisible when it is over the content area of the window but
- /// does not restrict the cursor from leaving.
- Hidden,
-
- /// Makes the cursor visible and behaving normally.
- Normal,
-}
-
-impl CursorMode
-{
- fn to_glfw_cursor_mode(self) -> glfw::window::CursorMode
- {
- match self {
- Self::Disabled => glfw::window::CursorMode::Disabled,
- Self::Hidden => glfw::window::CursorMode::Hidden,
- Self::Normal => glfw::window::CursorMode::Normal,
- }
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum InputMode
-{
- /// When the cursor is disabled, raw (unscaled and unaccelerated) mouse motion can be
- /// enabled if available.
- ///
- /// Raw mouse motion is closer to the actual motion of the mouse across a surface. It
- /// is not affected by the scaling and acceleration applied to the motion of the
- /// desktop cursor. That processing is suitable for a cursor while raw motion is
- /// better for controlling for example a 3D camera. Because of this, raw mouse motion
- /// is only provided when the cursor is disabled.
- RawMouseMotion,
-}
-
-impl InputMode
-{
- fn to_glfw_input_mode(self) -> glfw::window::InputMode
- {
- match self {
- Self::RawMouseMotion => glfw::window::InputMode::RawMouseMotion,
- }
- }
-}
-
-#[derive(Debug)]
-pub struct Extension
-{
- window_builder: Builder,
- window_size: Dimens<u32>,
- window_title: String,
-}
-
-impl Extension
-{
- #[must_use]
- pub fn new(window_builder: Builder) -> Self
- {
- Self { window_builder, ..Default::default() }
- }
-
- #[must_use]
- pub fn window_size(mut self, window_size: Dimens<u32>) -> Self
- {
- self.window_size = window_size;
-
- self
- }
-
- #[must_use]
- pub fn window_title(mut self, window_title: impl Into<String>) -> Self
- {
- self.window_title = window_title.into();
-
- self
- }
-}
-
-impl ecs::extension::Extension for Extension
-{
- fn collect(self, mut collector: ExtensionCollector<'_>)
- {
- collector.add_system(*START_PHASE, initialize);
- collector.add_system(*UPDATE_PHASE, update);
-
- let window = self
- .window_builder
- .create(self.window_size, &self.window_title)
- .unwrap();
-
- window.set_cursor_mode(CursorMode::Normal).unwrap();
-
- collector.add_sole(window).ok();
- }
-}
-
-impl Default for Extension
-{
- fn default() -> Self
- {
- Self {
- window_builder: Builder::default(),
- window_size: Dimens { width: 1920, height: 1080 },
- window_title: String::new(),
- }
- }
-}
-
-#[derive(Debug, thiserror::Error)]
-#[error(transparent)]
-pub struct Error(glfw::Error);
-
-impl From<glfw::Error> for Error
-{
- fn from(err: glfw::Error) -> Self
- {
- Self(err)
- }
-}
-
-fn initialize(window: Single<Window>, actions: Actions)
-{
- let actions_weak_ref = actions.to_weak_ref();
-
- window.set_close_callback(move || {
- let actions_weak_ref = actions_weak_ref.clone();
-
- let actions_ref = actions_weak_ref.access().expect("No world");
-
- actions_ref.to_actions().stop();
- });
-}
-
-fn update(window: Single<Window>)
-{
- window
- .swap_buffers()
- .expect("Failed to swap window buffers");
-
- window.poll_events().expect("Failed to poll window events");
-}
diff --git a/engine/src/windowing.rs b/engine/src/windowing.rs
new file mode 100644
index 0000000..2493851
--- /dev/null
+++ b/engine/src/windowing.rs
@@ -0,0 +1,1169 @@
+use std::collections::VecDeque;
+use std::hint::cold_path;
+use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::{Arc, Condvar, Mutex, Weak};
+use std::thread::Builder as ThreadBuilder;
+use std::time::Duration;
+
+use bitflags::{bitflags, bitflags_match, Flags};
+use crossbeam_queue::ArrayQueue;
+use intmap::IntMap;
+use raw_window_handle::{DisplayHandle, HandleError, HasDisplayHandle, WindowHandle};
+use winit::application::ApplicationHandler;
+use winit::error::EventLoopError;
+use winit::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
+use winit::event_loop::{
+ ActiveEventLoop,
+ ControlFlow as EventLoopControlFlow,
+ EventLoop,
+ OwnedDisplayHandle,
+};
+use winit::keyboard::PhysicalKey;
+use winit::window::{Window as WinitWindow, WindowId as WinitWindowId};
+
+use crate::ecs::actions::Actions;
+use crate::ecs::component::Component;
+use crate::ecs::entity::obtainer::Obtainer as EntityObtainer;
+use crate::ecs::event::component::{Added, Changed, EventMatchExt, Removed};
+use crate::ecs::pair::{ChildOf, Pair};
+use crate::ecs::phase::{Phase, PRE_UPDATE as PRE_UPDATE_PHASE};
+use crate::ecs::sole::Single;
+use crate::ecs::system::observer::Observe;
+use crate::ecs::uid::Uid;
+use crate::ecs::util::StreamingIterator;
+use crate::ecs::{declare_entity, pair, Query, Sole};
+use crate::util::BitArray;
+use crate::vector::Vec2;
+use crate::windowing::dpi::{PhysicalPosition, PhysicalSize, Position};
+use crate::windowing::keyboard::{Key, KeyState, Keyboard, UnknownKeyCodeError};
+use crate::windowing::monitor::Handle as MonitorHandle;
+use crate::windowing::mouse::{
+ Button as MouseButton,
+ ButtonState as MouseButtonState,
+ Buttons as MouseButtons,
+ Mouse,
+ ScrollDelta as MouseScrollDelta,
+};
+use crate::windowing::window::{
+ Closed as WindowClosed,
+ CreationAttributes as WindowCreationAttributes,
+ CreationReady as WindowCreationReady,
+ CursorGrabMode,
+ Id as WindowId,
+ Window,
+};
+use crate::{error, Error as EngineError};
+
+pub mod dpi;
+pub mod keyboard;
+pub mod monitor;
+pub mod mouse;
+pub mod window;
+
+const MESSAGE_FROM_APP_QUEUE_SIZE: usize = 512;
+
+const MESSAGE_TO_APP_QUEUE_SIZE: usize = 16; // Increase if more messages are added
+
+const TEXT_KEY_QUEUE_SIZE: usize = 255;
+
+static CONTEXT_CREATED: AtomicBool = AtomicBool::new(false);
+
+declare_entity! {
+pub PHASE: (Phase, pair!(ChildOf, { *PRE_UPDATE_PHASE }));
+}
+
+#[derive(Debug, Default)]
+#[non_exhaustive]
+pub struct Extension {}
+
+impl crate::ecs::extension::Extension for Extension
+{
+ fn collect(self, mut collector: crate::ecs::extension::Collector<'_>)
+ {
+ if !CONTEXT_CREATED.load(Ordering::Relaxed) {
+ collector.add_sole(Context::new()).ok();
+ }
+
+ collector.add_sole(Keyboard::default()).ok();
+ collector.add_sole(Mouse::default()).ok();
+ collector.add_sole(MouseButtons::default()).ok();
+
+ collector.spawn_declared_entity(&PHASE);
+
+ collector.add_system(*PHASE, update_stuff);
+
+ collector.add_observer(handle_window_changed);
+ collector.add_observer(handle_window_removed);
+ collector.add_observer(handle_window_creation_ready);
+ }
+}
+
+fn handle_window_creation_ready(
+ observe: Observe<Pair<Added, WindowCreationReady>>,
+ context: Single<Context>,
+)
+{
+ let Ok(context) = context.get() else {
+ unreachable!();
+ };
+
+ for evt_match in &observe {
+ let Some(ent) = evt_match.try_get_entity() else {
+ unreachable!();
+ };
+
+ if ent.has_component(Window::id()) || ent.has_component(WindowClosed::id()) {
+ continue;
+ }
+
+ let Some(window_creation_attrs) = ent.get::<WindowCreationAttributes>() else {
+ unreachable!();
+ };
+
+ context.try_send_message_to_app(MessageToApp::CreateWindow(
+ ent.uid(),
+ window_creation_attrs.clone(),
+ ));
+ }
+}
+
+#[tracing::instrument(skip_all)]
+fn update_stuff(
+ mut context: Single<Context>,
+ mut keyboard: Single<Keyboard>,
+ mut mouse: Single<Mouse>,
+ mut mouse_buttons: Single<MouseButtons>,
+ mut actions: Actions,
+ entity_obtainer: EntityObtainer,
+) -> Result<(), EngineError>
+{
+ let Ok(context) = context.get_mut() else {
+ unreachable!();
+ };
+
+ let keyboard = keyboard.get_mut()?;
+ let mouse = mouse.get_mut()?;
+ let mouse_buttons = mouse_buttons.get_mut()?;
+
+ if context.display_handle.is_none() {
+ cold_path();
+ actions.stop();
+ return Ok(());
+ }
+
+ keyboard.make_key_states_previous();
+
+ {
+ let Some(mut input) = context
+ .shared_state
+ .input
+ .try_lock_for(Duration::from_millis(100))
+ else {
+ tracing::error!("Locking input mutex timed out after 100ms");
+ return Ok(());
+ };
+
+ mouse.curr_tick_position_delta = input.relative_mouse_pos_delta;
+ mouse.position = input.absolute_mouse_pos.clone();
+ mouse.curr_tick_scroll_delta = input.mouse_scroll_delta.clone();
+
+ for (mouse_button, mouse_button_input) in input.mouse_buttons.iter_mut() {
+ mouse_buttons.set_previous_to_current(mouse_button);
+
+ if mouse_button_input.flags.is_all() {
+ match mouse_buttons.get_previous(mouse_button) {
+ MouseButtonState::Pressed => {
+ mouse_buttons.set(mouse_button, MouseButtonState::Released);
+
+ mouse_button_input.flags.remove(MouseButtonFlags::RELEASED);
+ }
+ MouseButtonState::Released => {
+ mouse_buttons.set(mouse_button, MouseButtonState::Pressed);
+
+ mouse_button_input.flags.remove(MouseButtonFlags::PRESSED);
+ }
+ }
+
+ continue;
+ }
+
+ let mouse_button_state = if mouse_button_input.flags.is_empty() {
+ let Some(mouse_button_state) = mouse_button_input.lagged.pop_front()
+ else {
+ continue;
+ };
+
+ mouse_button_state
+ } else {
+ bitflags_match!(mouse_button_input.flags, {
+ MouseButtonFlags::PRESSED => MouseButtonState::Pressed,
+ MouseButtonFlags::RELEASED => MouseButtonState::Released,
+ _ => unreachable!()
+ })
+ };
+
+ mouse_buttons.set(mouse_button, mouse_button_state);
+
+ mouse_button_input.flags.clear();
+ }
+
+ let mut key_updates = input.keys.iter_occupied_mut();
+
+ while let Some(mut key_item) = key_updates.streaming_next() {
+ let key = Key::KEYS[key_item.index];
+
+ if key_item.bits == KEY_PRESSED_BITS | KEY_RELEASED_BITS {
+ match keyboard.get_key_state(key) {
+ KeyState::Pressed => {
+ keyboard.set_key_state(key, KeyState::Released);
+
+ key_item.clear_and_set(KEY_PRESSED_BITS, u8::MAX);
+ }
+ KeyState::Released => {
+ keyboard.set_key_state(key, KeyState::Pressed);
+
+ key_item.clear_and_set(KEY_RELEASED_BITS, u8::MAX);
+ }
+ }
+
+ continue;
+ }
+
+ let key_state = match key_item.bits {
+ KEY_PRESSED_BITS => KeyState::Pressed,
+ KEY_RELEASED_BITS => KeyState::Released,
+ _ => unreachable!(),
+ };
+
+ keyboard.set_key_state(key, key_state);
+
+ key_item.clear_and_set(0, u8::MAX);
+ }
+
+ input.relative_mouse_pos_delta = Vec2 { x: 0.0, y: 0.0 };
+ input.mouse_scroll_delta = MouseScrollDelta { vert_lines: 0.0, hor_lines: 0.0 };
+ };
+
+ keyboard.set_text_keys(iter_array_queue(&context.shared_state.text_keys));
+
+ let Context {
+ ref mut windows, ref shared_state, ..
+ } = *context;
+
+ for message in iter_array_queue(&shared_state.message_from_app_queue) {
+ tracing::trace!(message=?message, "Received message from app");
+
+ match message {
+ MessageFromApp::WindowCreated(
+ window_ent_id,
+ winit_window,
+ window_creation_attrs,
+ ) => {
+ actions.add_components(
+ window_ent_id,
+ (Window::new(&winit_window, &window_creation_attrs),),
+ );
+
+ actions.remove_comps::<(WindowCreationReady,)>(window_ent_id);
+
+ let window_id = WindowId::from_inner(winit_window.id());
+
+ windows.insert(window_id, (winit_window, window_ent_id));
+
+ tracing::info!(
+ window_id = ?window_id,
+ window_title = %window_creation_attrs.title,
+ "Window creation completed"
+ );
+ }
+ MessageFromApp::WindowResized(window_id, new_window_size) => {
+ tracing::trace!(
+ window_id = ?window_id,
+ "Received window resized message"
+ );
+
+ let Some(window_ent_id) =
+ windows.get(window_id).map(|(_, ent_id)| ent_id)
+ else {
+ tracing::error!(
+ wid = ?window_id,
+ "Window does not exist in windowing context"
+ );
+ continue;
+ };
+
+ let Some(window_ent) = entity_obtainer.get_entity(*window_ent_id) else {
+ continue;
+ };
+
+ let Some(mut window) = window_ent.get_mut::<Window>() else {
+ continue;
+ };
+
+ window.inner_size = new_window_size;
+
+ window.set_changed();
+ }
+ MessageFromApp::WindowCloseRequested(window_id) => {
+ let Some(window_ent_id) =
+ windows.get(window_id).map(|(_, ent_id)| ent_id)
+ else {
+ tracing::error!(
+ wid = ?window_id,
+ "Window does not exist in windowing context"
+ );
+ continue;
+ };
+
+ actions.remove_comps::<(Window,)>(*window_ent_id);
+ }
+ MessageFromApp::WindowScaleFactorChanged(window_id, scale_factor) => {
+ let Some(window_ent_id) =
+ windows.get(window_id).map(|(_, ent_id)| ent_id)
+ else {
+ tracing::error!(
+ wid = ?window_id,
+ "Window does not exist in windowing context"
+ );
+ continue;
+ };
+
+ let Some(window_ent) = entity_obtainer.get_entity(*window_ent_id) else {
+ continue;
+ };
+
+ let Some(mut window) = window_ent.get_mut::<Window>() else {
+ continue;
+ };
+
+ window.set_scale_factor(scale_factor);
+
+ window.set_changed();
+ }
+ }
+ }
+
+ if shared_state.thread_panicked.load(Ordering::Relaxed) {
+ cold_path();
+ return Err(error!("Windowing app thread panicked"));
+ }
+
+ if shared_state.has_thread_err.load(Ordering::Relaxed) {
+ cold_path();
+
+ let Ok(mut err) = shared_state.thread_err.try_lock() else {
+ // The windowing app thread always unlocks the thread_err mutex before
+ // setting the has_thread_err atomic
+ unreachable!();
+ };
+
+ let Some(err) = err.take() else {
+ // The has_thread_err atomic is set to true so this option is always
+ // Some
+ unreachable!();
+ };
+
+ return Err(err.into());
+ }
+
+ Ok(())
+}
+
+fn handle_window_changed(
+ observe: Observe<'_, Pair<Changed, Window>>,
+ context: Single<Context>,
+)
+{
+ let Ok(context) = context.get() else {
+ unreachable!();
+ };
+
+ for evt_match in &observe {
+ let window_ent_id = evt_match.entity_id();
+
+ let mut window = evt_match.get_ent_target_comp_mut();
+
+ let Some((winit_window, _)) = context.windows.get(window.wid()) else {
+ tracing::error!(
+ wid = ?window.wid(),
+ entity_id = %window_ent_id,
+ "Window does not exist in windowing context",
+ );
+ continue;
+ };
+
+ let window_apply_results = window.apply(winit_window);
+
+ if let Err(actual_window_inner_size) = window_apply_results.inner_size_request {
+ window.inner_size = actual_window_inner_size;
+ }
+
+ context.try_send_message_to_app(MessageToApp::SetWindowCursorGrabMode(
+ window.wid(),
+ window.cursor_grab_mode,
+ ));
+ }
+}
+
+fn handle_window_removed(
+ observe: Observe<Pair<Removed, Window>>,
+ window_query: Query<(&Window,)>,
+ mut context: Single<Context>,
+ mut actions: Actions,
+)
+{
+ let Ok(context) = context.get_mut() else {
+ unreachable!();
+ };
+
+ for evt_match in &observe {
+ let window = evt_match.get_ent_target_comp();
+
+ context.windows.remove(window.wid());
+
+ actions.add_components(evt_match.entity_id(), (WindowClosed,));
+ }
+
+ if window_query.iter().count() == 1 {
+ actions.stop();
+ }
+}
+
+#[derive(Debug, Sole)]
+pub struct Context
+{
+ shared_state: Arc<SharedState>,
+ display_handle: Option<OwnedDisplayHandle>,
+ windows: IntMap<WindowId, (Arc<WinitWindow>, Uid)>,
+ available_monitors: Vec<MonitorHandle>,
+ primary_monitor: Option<MonitorHandle>,
+}
+
+impl Context
+{
+ pub fn display_handle(&self) -> Option<DisplayHandle<'_>>
+ {
+ self.display_handle.as_ref()?.display_handle().ok()
+ }
+
+ /// Returns the specified window as a window handle, if it exists.
+ ///
+ /// # Safety
+ /// The Window handle must only be used with thread safe APIs.
+ pub unsafe fn get_window_as_handle(
+ &self,
+ window_id: &WindowId,
+ ) -> Option<Result<WindowHandle<'_>, HandleError>>
+ {
+ self.windows.get(*window_id).map(|(winit_window, _)| {
+ #[cfg(windows)]
+ {
+ use winit::platform::windows::WindowExtWindows;
+
+ // SAFETY: I don't care
+ unsafe { winit_window.window_handle_any_thread() }
+ }
+
+ #[cfg(not(windows))]
+ {
+ use raw_window_handle::HasWindowHandle;
+
+ winit_window.window_handle()
+ }
+ })
+ }
+
+ /// Returns the last known primary monitor. This monitor may or may not exist any
+ /// longer and it may or may not be the primary monitor any longer.
+ pub fn get_primary_monitor(&self) -> Option<&MonitorHandle>
+ {
+ self.primary_monitor.as_ref()
+ }
+
+ /// Returns an iterator of the last known available monitors. These monitors may or
+ /// may not exist any longer.
+ pub fn available_monitors(&self) -> impl Iterator<Item = &MonitorHandle>
+ {
+ self.available_monitors.iter()
+ }
+
+ fn try_send_message_to_app(&self, message: MessageToApp)
+ {
+ if self
+ .shared_state
+ .message_to_app_queue
+ .push(message)
+ .is_err()
+ {
+ tracing::error!(
+ "Failed to send message. Queue for messages to windowing app is full"
+ );
+ }
+ }
+}
+
+impl Context
+{
+ /// Returns a new windowing context.
+ ///
+ /// # Panics
+ /// Will panic if a windowing context have already been created.
+ pub fn new() -> Self
+ {
+ if CONTEXT_CREATED.swap(true, Ordering::Relaxed) {
+ panic!("A windowing context have already been created");
+ }
+
+ let shared_state = Arc::new(SharedState::default());
+ let shared_state_b = shared_state.clone();
+
+ if let Err(err) = ThreadBuilder::new()
+ .name("windowing app".to_string())
+ .spawn(move || {
+ let shared_state_c = shared_state_b.clone();
+
+ match catch_unwind(move || {
+ let mut app = App {
+ shared_state: shared_state_b,
+ windows: IntMap::with_capacity(1),
+ };
+
+ let event_loop = match create_event_loop() {
+ Ok(event_loop) => event_loop,
+ Err(err) => {
+ return Err((app, AppThreadError::CreateEventLoop(err)));
+ }
+ };
+
+ event_loop.set_control_flow(EventLoopControlFlow::Poll);
+
+ event_loop
+ .run_app(&mut app)
+ .map_err(|err| (app, AppThreadError::StartEventLoop(err)))?;
+
+ Ok(())
+ }) {
+ Ok(Ok(())) => {}
+ Ok(Err((app, err))) => {
+ {
+ let Ok(mut thread_err) =
+ app.shared_state.thread_err.try_lock()
+ else {
+ // The thread_err mutex is only locked by the main thread
+ // when the has_thread_err atomic is set to true, which
+ // it can not be set to yet
+ unreachable!();
+ };
+
+ *thread_err = Some(err);
+ }
+
+ app.shared_state
+ .has_thread_err
+ .store(true, Ordering::Relaxed);
+
+ app.shared_state.init_data_cond.notify_one();
+ }
+ Err(_) => {
+ shared_state_c
+ .thread_panicked
+ .store(true, Ordering::Relaxed);
+
+ shared_state_c.init_data_cond.notify_one();
+ }
+ };
+ })
+ {
+ tracing::error!(
+ "Failed to create windowing app thread: {:#}",
+ crate::Error::new(err)
+ );
+
+ return Self {
+ shared_state,
+ display_handle: None,
+ windows: IntMap::with_capacity(1),
+ available_monitors: Vec::with_capacity(2),
+ primary_monitor: None,
+ };
+ }
+
+ let mut init_data_lock = shared_state.init_data.lock().unwrap_or_else(|err| {
+ let mut lock = err.into_inner();
+ *lock = None;
+ lock
+ });
+
+ while init_data_lock.is_none()
+ && !shared_state.has_thread_err.load(Ordering::Relaxed)
+ && !shared_state.thread_panicked.load(Ordering::Relaxed)
+ {
+ init_data_lock = shared_state
+ .init_data_cond
+ .wait(init_data_lock)
+ .unwrap_or_else(|err| {
+ let mut lock = err.into_inner();
+ *lock = None;
+ lock
+ });
+ }
+
+ let init_data = init_data_lock.take();
+
+ drop(init_data_lock);
+
+ let Some(init_data) = init_data else {
+ if shared_state.thread_panicked.load(Ordering::Relaxed) {
+ tracing::error!("Windowing app thread panicked");
+ } else if shared_state.has_thread_err.load(Ordering::Relaxed) {
+ let Ok(mut err) = shared_state.thread_err.try_lock() else {
+ // The windowing app thread always unlocks the thread_err mutex before
+ // setting the has_thread_err atomic
+ unreachable!();
+ };
+
+ let Some(err) = err.take() else {
+ // The has_thread_err atomic is set to true so this option is always
+ // Some
+ unreachable!();
+ };
+
+ tracing::error!(
+ "Error in windowing app thread: {:#}",
+ crate::Error::new(err)
+ );
+ } else {
+ tracing::error!("Windowing app initialization aborted unexpectedly");
+ }
+
+ return Self {
+ shared_state,
+ display_handle: None,
+ windows: IntMap::with_capacity(1),
+ available_monitors: Vec::with_capacity(2),
+ primary_monitor: None,
+ };
+ };
+
+ Self {
+ shared_state,
+ display_handle: Some(init_data.display),
+ windows: IntMap::with_capacity(1),
+ available_monitors: init_data.available_monitors,
+ primary_monitor: init_data.primary_monitor,
+ }
+ }
+}
+
+impl Drop for Context
+{
+ fn drop(&mut self)
+ {
+ self.shared_state.is_dropped.store(true, Ordering::Relaxed);
+ }
+}
+
+fn create_event_loop() -> Result<EventLoop<()>, EventLoopError>
+{
+ let mut event_loop_builder = EventLoop::builder();
+
+ cfg_select! {
+ target_os = "linux" => {
+ winit::platform::x11::EventLoopBuilderExtX11::with_any_thread(
+ &mut event_loop_builder,
+ true,
+ );
+ }
+ windows => {
+ winit::platform::windows::EventLoopBuilderExtWindows::with_any_thread(
+ &mut event_loop_builder,
+ true,
+ );
+ }
+ _ => {
+ compile_error!("Unsupported platform")
+ }
+ }
+
+ event_loop_builder.build()
+}
+
+#[derive(Debug, thiserror::Error)]
+enum AppThreadError
+{
+ #[error("Event loop creation failed")]
+ CreateEventLoop(#[source] winit::error::EventLoopError),
+
+ #[error("Starting event loop failed")]
+ StartEventLoop(#[source] winit::error::EventLoopError),
+}
+
+#[derive(Debug)]
+enum MessageFromApp
+{
+ WindowCreated(Uid, Arc<WinitWindow>, WindowCreationAttributes),
+ WindowResized(WindowId, PhysicalSize<u32>),
+ WindowCloseRequested(WindowId),
+ WindowScaleFactorChanged(WindowId, f64),
+}
+
+#[derive(Debug)]
+enum MessageToApp
+{
+ CreateWindow(Uid, WindowCreationAttributes),
+ SetWindowCursorGrabMode(WindowId, CursorGrabMode),
+}
+
+#[derive(Debug)]
+struct SharedState
+{
+ message_from_app_queue: ArrayQueue<MessageFromApp>,
+ message_to_app_queue: ArrayQueue<MessageToApp>,
+ input: AssertUnwindSafe<parking_lot::Mutex<Input>>,
+ text_keys: ArrayQueue<char>,
+ is_dropped: AtomicBool,
+ thread_panicked: AtomicBool,
+ thread_err: Mutex<Option<AppThreadError>>,
+ has_thread_err: AtomicBool,
+ init_data: Mutex<Option<InitData>>,
+ init_data_cond: Condvar,
+}
+
+impl Default for SharedState
+{
+ fn default() -> Self
+ {
+ Self {
+ message_from_app_queue: ArrayQueue::new(MESSAGE_FROM_APP_QUEUE_SIZE),
+ message_to_app_queue: ArrayQueue::new(MESSAGE_TO_APP_QUEUE_SIZE),
+ input: AssertUnwindSafe(parking_lot::Mutex::default()),
+ text_keys: ArrayQueue::new(TEXT_KEY_QUEUE_SIZE),
+ is_dropped: AtomicBool::new(false),
+ thread_panicked: AtomicBool::new(false),
+ thread_err: Mutex::new(None),
+ has_thread_err: AtomicBool::new(false),
+ init_data: Mutex::new(None),
+ init_data_cond: Condvar::new(),
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Input
+{
+ relative_mouse_pos_delta: Vec2<f64>,
+ absolute_mouse_pos: PhysicalPosition<f64>,
+ mouse_scroll_delta: MouseScrollDelta,
+ mouse_buttons: IntMap<MouseButton, MouseButtonInput>,
+ keys: BitArray<{ (Key::KEYS.len() * BITS_PER_KEY).div_ceil(8) }, BITS_PER_KEY>,
+}
+
+impl Default for Input
+{
+ fn default() -> Self
+ {
+ Self {
+ relative_mouse_pos_delta: Vec2::default(),
+ absolute_mouse_pos: PhysicalPosition::default(),
+ mouse_scroll_delta: MouseScrollDelta::default(),
+ mouse_buttons: IntMap::from_iter([
+ (MouseButton::Left, MouseButtonInput::default()),
+ (MouseButton::Right, MouseButtonInput::default()),
+ (MouseButton::Middle, MouseButtonInput::default()),
+ (MouseButton::Back, MouseButtonInput::default()),
+ (MouseButton::Forward, MouseButtonInput::default()),
+ ]),
+ keys: BitArray::new(),
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct MouseButtonInput
+{
+ flags: MouseButtonFlags,
+ lagged: VecDeque<MouseButtonState>,
+}
+
+bitflags! {
+#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
+struct MouseButtonFlags: u8
+{
+ const PRESSED = 1 << 0;
+ const RELEASED = 1 << 1;
+}
+}
+
+const BITS_PER_KEY: usize = 2;
+
+const KEY_PRESSED_BITS: u8 = 0b10;
+const KEY_RELEASED_BITS: u8 = 0b01;
+
+#[derive(Debug)]
+struct InitData
+{
+ display: OwnedDisplayHandle,
+ available_monitors: Vec<MonitorHandle>,
+ primary_monitor: Option<MonitorHandle>,
+}
+
+#[derive(Debug)]
+struct App
+{
+ shared_state: Arc<SharedState>,
+ windows: IntMap<WindowId, (Weak<WinitWindow>, WindowSettings)>,
+}
+
+impl App
+{
+ #[tracing::instrument(skip_all)]
+ fn handle_received_messages(&mut self, event_loop: &ActiveEventLoop)
+ {
+ for message in iter_array_queue(&self.shared_state.message_to_app_queue) {
+ match message {
+ MessageToApp::CreateWindow(window_ent_id, window_creation_attrs) => {
+ tracing::info!(
+ "Creating window with title {}",
+ window_creation_attrs.title
+ );
+
+ let winit_window = Arc::new(
+ match event_loop.create_window(
+ window_creation_attrs.clone().into_window_attrs(),
+ ) {
+ Ok(window) => window,
+ Err(err) => {
+ tracing::error!(
+ "Failed to create window: {:#}",
+ crate::Error::new(err)
+ );
+ continue;
+ }
+ },
+ );
+
+ window_creation_attrs.apply_extra_attrs_to_window(&winit_window);
+
+ self.windows.insert(
+ WindowId::from_inner(winit_window.id()),
+ (
+ Arc::downgrade(&winit_window),
+ WindowSettings {
+ cursor_grab_mode: window_creation_attrs.cursor_grab_mode,
+ },
+ ),
+ );
+
+ self.send_message(MessageFromApp::WindowCreated(
+ window_ent_id,
+ winit_window,
+ window_creation_attrs,
+ ));
+ }
+ MessageToApp::SetWindowCursorGrabMode(window_id, cursor_grab_mode) => {
+ let Some((_, window_settings)) = self.windows.get_mut(window_id)
+ else {
+ tracing::warn!(
+ window_id=?window_id,
+ "Cannot set window cursor grab mode. Window not found"
+ );
+
+ continue;
+ };
+
+ window_settings.cursor_grab_mode = cursor_grab_mode;
+ }
+ }
+ }
+ }
+
+ #[tracing::instrument(skip_all)]
+ fn send_message(&self, message: MessageFromApp)
+ {
+ if self.shared_state.message_from_app_queue.is_full() {
+ tracing::warn!(
+ "Queue for messages to windowing app is full! Dropping oldest message"
+ );
+ }
+
+ self.shared_state.message_from_app_queue.force_push(message);
+ }
+
+ fn lock_input(&self) -> Option<parking_lot::MutexGuard<'_, Input>>
+ {
+ self.shared_state
+ .input
+ .try_lock_for(Duration::from_millis(100))
+ }
+}
+
+impl ApplicationHandler for App
+{
+ fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause)
+ {
+ match cause {
+ StartCause::Init => {
+ let available_monitors = event_loop
+ .available_monitors()
+ .map(MonitorHandle::from_winit_monitor_handle)
+ .collect::<Vec<_>>();
+
+ let Ok(mut init_data) = self.shared_state.init_data.lock() else {
+ tracing::error!("Init data mutex is poisoned, exiting event loop");
+ event_loop.exit();
+ return;
+ };
+
+ *init_data = Some(InitData {
+ display: event_loop.owned_display_handle(),
+ available_monitors: available_monitors,
+ primary_monitor: event_loop
+ .primary_monitor()
+ .map(|monitor| MonitorHandle::from_winit_monitor_handle(monitor)),
+ });
+
+ self.shared_state.init_data_cond.notify_one();
+ }
+ StartCause::Poll => {
+ if self.shared_state.is_dropped.load(Ordering::Relaxed) {
+ event_loop.exit();
+ return;
+ }
+
+ self.handle_received_messages(event_loop);
+ }
+ _ => {}
+ }
+ }
+
+ fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop)
+ {
+ for (window, _) in self.windows.values() {
+ let Some(window) = window.upgrade() else {
+ continue;
+ };
+
+ window.request_redraw();
+ }
+ }
+
+ fn resumed(&mut self, _event_loop: &ActiveEventLoop) {}
+
+ #[tracing::instrument(skip_all)]
+ fn window_event(
+ &mut self,
+ _event_loop: &ActiveEventLoop,
+ window_id: WinitWindowId,
+ event: WindowEvent,
+ )
+ {
+ match event {
+ WindowEvent::Resized(new_window_size) => {
+ self.send_message(MessageFromApp::WindowResized(
+ WindowId::from_inner(window_id),
+ new_window_size.into(),
+ ));
+ }
+ WindowEvent::CloseRequested => {
+ self.send_message(MessageFromApp::WindowCloseRequested(
+ WindowId::from_inner(window_id),
+ ));
+ }
+ WindowEvent::KeyboardInput {
+ device_id: _,
+ event: keyboard_event,
+ is_synthetic: _,
+ } => {
+ if let Some(key_text) = keyboard_event
+ .text
+ .filter(|_| keyboard_event.state.is_pressed())
+ {
+ for character in key_text.chars() {
+ if self.shared_state.text_keys.is_full() {
+ cold_path();
+ tracing::warn!(
+ "Text key queue is full. Dropping oldest character"
+ );
+ }
+
+ self.shared_state.text_keys.force_push(character);
+ }
+ }
+
+ if keyboard_event.repeat {
+ return;
+ }
+
+ let key_code = match keyboard_event.physical_key {
+ PhysicalKey::Code(key_code) => key_code,
+ PhysicalKey::Unidentified(native_key) => {
+ tracing::warn!("Ignoring unidentified key: {native_key:?}");
+ return;
+ }
+ };
+
+ let key: Key = match key_code.try_into() {
+ Ok(key) => key,
+ Err(UnknownKeyCodeError) => {
+ tracing::warn!("Ignoring key with unknown key code {key_code:?}");
+ return;
+ }
+ };
+
+ let Some(mut input) = self.lock_input() else {
+ tracing::error!("Locking input mutex timed out after 100ms");
+ return;
+ };
+
+ let key_state_bits = match keyboard_event.state.into() {
+ KeyState::Pressed => KEY_PRESSED_BITS,
+ KeyState::Released => KEY_RELEASED_BITS,
+ };
+
+ input.keys.clear_and_set(key as usize, key_state_bits, 0);
+ }
+ WindowEvent::CursorMoved { device_id: _, position } => {
+ {
+ let Some(mut input) = self.lock_input() else {
+ tracing::error!("Locking input mutex timed out after 100ms");
+ return;
+ };
+
+ input.absolute_mouse_pos = position.into();
+ }
+
+ let Some((window, window_settings)) =
+ self.windows.get(WindowId::from_inner(window_id))
+ else {
+ cold_path();
+ return;
+ };
+
+ if window_settings.cursor_grab_mode != CursorGrabMode::Locked {
+ return;
+ }
+
+ let Some(window) = window.upgrade() else {
+ cold_path();
+ return;
+ };
+
+ if !window.has_focus() {
+ return;
+ }
+
+ let window_size = window.inner_size();
+
+ if let Err(err) =
+ window.set_cursor_position(Position::Physical(PhysicalPosition {
+ x: window_size.width as i32 / 2,
+ y: window_size.height as i32 / 2,
+ }))
+ {
+ cold_path();
+ tracing::error!(
+ window_id=?window_id,
+ "Failed to lock cursor position: {:#}",
+ crate::Error::new(err)
+ );
+ };
+ }
+ WindowEvent::MouseWheel { device_id: _, delta, phase: _ } => {
+ let (hor_lines, vert_lines) = match delta {
+ winit::event::MouseScrollDelta::LineDelta(hor_lines, vert_lines) => {
+ (hor_lines, vert_lines)
+ }
+ winit::event::MouseScrollDelta::PixelDelta(pos_delta) => {
+ let hor_lines = match pos_delta.x.partial_cmp(&0.0) {
+ Some(std::cmp::Ordering::Greater) => 1.0,
+ Some(std::cmp::Ordering::Less) => -1.0,
+ _ => 0.0,
+ };
+
+ let vert_lines = match pos_delta.y.partial_cmp(&0.0) {
+ Some(std::cmp::Ordering::Greater) => 1.0,
+ Some(std::cmp::Ordering::Less) => -1.0,
+ _ => 0.0,
+ };
+
+ (hor_lines, vert_lines)
+ }
+ };
+
+ let Some(mut input) = self.lock_input() else {
+ tracing::error!("Locking input mutex timed out after 100ms");
+ return;
+ };
+
+ input.mouse_scroll_delta.hor_lines += hor_lines;
+ input.mouse_scroll_delta.vert_lines += vert_lines;
+ }
+ WindowEvent::MouseInput { device_id: _, state, button } => {
+ let Some(mut input) = self.lock_input() else {
+ tracing::error!("Locking input mutex timed out after 100ms");
+ return;
+ };
+
+ let button = MouseButton::from(button);
+ let button_state = MouseButtonState::from(state);
+
+ let button_input = input
+ .mouse_buttons
+ .entry(button)
+ .or_insert_with(|| MouseButtonInput::default());
+
+ if button_input.flags.is_all() || !button_input.lagged.is_empty() {
+ button_input.lagged.push_back(button_state);
+ return;
+ }
+
+ button_input.flags.insert(match button_state {
+ MouseButtonState::Pressed => MouseButtonFlags::PRESSED,
+ MouseButtonState::Released => MouseButtonFlags::RELEASED,
+ });
+ }
+ WindowEvent::ScaleFactorChanged { scale_factor, inner_size_writer: _ } => {
+ self.send_message(MessageFromApp::WindowScaleFactorChanged(
+ WindowId::from_inner(window_id),
+ scale_factor,
+ ));
+ }
+ _ => {}
+ }
+ }
+
+ #[tracing::instrument(skip_all)]
+ fn device_event(
+ &mut self,
+ _event_loop: &ActiveEventLoop,
+ _device_id: DeviceId,
+ device_event: DeviceEvent,
+ )
+ {
+ match device_event {
+ DeviceEvent::MouseMotion { delta } => {
+ let Some(mut input) = self.lock_input() else {
+ tracing::error!("Locking input mutex timed out after 100ms");
+ return;
+ };
+
+ input.relative_mouse_pos_delta += Vec2::from(delta);
+ }
+ _ => {}
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct WindowSettings
+{
+ cursor_grab_mode: CursorGrabMode,
+}
+
+fn iter_array_queue<Item>(
+ queue: &ArrayQueue<Item>,
+) -> impl Iterator<Item = Item> + use<'_, Item>
+{
+ (0..queue.len()).into_iter().filter_map(|_| queue.pop())
+}
diff --git a/engine/src/windowing/dpi.rs b/engine/src/windowing/dpi.rs
new file mode 100644
index 0000000..e3b1be1
--- /dev/null
+++ b/engine/src/windowing/dpi.rs
@@ -0,0 +1,217 @@
+use crate::reflection::Reflection;
+
+macro_rules! gen_struct_from_to_impls {
+ ($struct: ident, fields=($($field: ident),*)) => {
+ impl<Pixel> From<winit::dpi::$struct<Pixel>> for $struct<Pixel>
+ {
+ fn from(source: winit::dpi::$struct<Pixel>) -> Self
+ {
+ Self {
+ $($field: source.$field),*
+ }
+ }
+ }
+
+ impl<Pixel> From<$struct<Pixel>> for winit::dpi::$struct<Pixel>
+ {
+ fn from(source: $struct<Pixel>) -> Self
+ {
+ Self {
+ $($field: source.$field),*
+ }
+ }
+ }
+ };
+}
+
+macro_rules! gen_enum_from_to_impls {
+ ($enum: ident, variants=($($variant: ident),*)) => {
+ impl From<winit::dpi::$enum> for $enum
+ {
+ fn from(source: winit::dpi::$enum) -> Self
+ {
+ match source {
+ $(winit::dpi::$enum::$variant(pos) => Self::$variant(pos.into())),*
+ }
+ }
+ }
+
+ impl From<$enum> for winit::dpi::$enum
+ {
+ fn from(source: $enum) -> Self
+ {
+ match source {
+ $($enum::$variant(pos) => Self::$variant(pos.into())),*
+ }
+ }
+ }
+ };
+}
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)]
+#[reflection(impl_with_generics(<u32>, <i32>, <f32>, <f64>))]
+pub struct PhysicalPosition<Pixel>
+{
+ pub x: Pixel,
+ pub y: Pixel,
+}
+
+impl<Pixel> PhysicalPosition<Pixel>
+{
+ pub fn to_logical<LogicalPixel>(
+ &self,
+ scale_factor: f64,
+ ) -> LogicalPosition<LogicalPixel>
+ where
+ Pixel: Into<f64> + Clone,
+ LogicalPixel: From<f64>,
+ {
+ let x = self.x.clone().into();
+ let y = self.y.clone().into();
+
+ LogicalPosition {
+ x: (x / scale_factor).into(),
+ y: (y / scale_factor).into(),
+ }
+ }
+
+ pub fn try_convert_from<SourcePixel>(
+ source: PhysicalPosition<SourcePixel>,
+ ) -> Result<Self, Pixel::Error>
+ where
+ Pixel: TryFrom<SourcePixel>,
+ {
+ Ok(Self {
+ x: source.x.try_into()?,
+ y: source.y.try_into()?,
+ })
+ }
+}
+
+gen_struct_from_to_impls!(PhysicalPosition, fields = (x, y));
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)]
+#[reflection(impl_with_generics(<f64>))]
+pub struct LogicalPosition<Pixel>
+{
+ pub x: Pixel,
+ pub y: Pixel,
+}
+
+impl<Pixel> LogicalPosition<Pixel>
+{
+ pub fn try_convert_from<SourcePixel>(
+ source: LogicalPosition<SourcePixel>,
+ ) -> Result<Self, Pixel::Error>
+ where
+ Pixel: TryFrom<SourcePixel>,
+ {
+ Ok(Self {
+ x: source.x.try_into()?,
+ y: source.y.try_into()?,
+ })
+ }
+}
+
+gen_struct_from_to_impls!(LogicalPosition, fields = (x, y));
+
+#[derive(Debug, Clone, Copy, PartialEq, Reflection)]
+pub enum Position
+{
+ Physical(PhysicalPosition<i32>),
+ Logical(LogicalPosition<f64>),
+}
+
+impl Default for Position
+{
+ fn default() -> Self
+ {
+ Self::Logical(LogicalPosition::default())
+ }
+}
+
+gen_enum_from_to_impls!(Position, variants = (Physical, Logical));
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)]
+#[reflection(impl_with_generics(<u32>))]
+pub struct PhysicalSize<Pixel>
+{
+ pub width: Pixel,
+ pub height: Pixel,
+}
+
+impl<Pixel> PhysicalSize<Pixel>
+{
+ pub fn to_logical<LogicalPixel>(&self, scale_factor: f64) -> LogicalSize<LogicalPixel>
+ where
+ Pixel: Into<f64> + Clone,
+ LogicalPixel: From<f64>,
+ {
+ let width = self.width.clone().into();
+ let height = self.height.clone().into();
+
+ LogicalSize {
+ width: (width / scale_factor).into(),
+ height: (height / scale_factor).into(),
+ }
+ }
+}
+
+impl<Pixel> PhysicalSize<Pixel>
+{
+ pub fn try_convert_from<SourcePixel>(
+ source: PhysicalSize<SourcePixel>,
+ ) -> Result<Self, Pixel::Error>
+ where
+ Pixel: TryFrom<SourcePixel>,
+ {
+ Ok(Self {
+ width: source.width.try_into()?,
+ height: source.height.try_into()?,
+ })
+ }
+}
+
+gen_struct_from_to_impls!(PhysicalSize, fields = (width, height));
+
+#[derive(Debug, Default, Clone, Copy, PartialEq, Reflection)]
+#[reflection(impl_with_generics(<f64>))]
+pub struct LogicalSize<Pixel>
+{
+ pub width: Pixel,
+ pub height: Pixel,
+}
+
+impl<Pixel> LogicalSize<Pixel>
+{
+ pub fn try_convert_from<SourcePixel>(
+ source: LogicalSize<SourcePixel>,
+ ) -> Result<Self, Pixel::Error>
+ where
+ Pixel: TryFrom<SourcePixel>,
+ {
+ Ok(Self {
+ width: source.width.try_into()?,
+ height: source.height.try_into()?,
+ })
+ }
+}
+
+gen_struct_from_to_impls!(LogicalSize, fields = (width, height));
+
+#[derive(Debug, Clone, Copy, PartialEq, Reflection)]
+pub enum Size
+{
+ Physical(PhysicalSize<u32>),
+ Logical(LogicalSize<f64>),
+}
+
+impl Default for Size
+{
+ fn default() -> Self
+ {
+ Self::Logical(LogicalSize::default())
+ }
+}
+
+gen_enum_from_to_impls!(Size, variants = (Physical, Logical));
diff --git a/engine/src/windowing/keyboard.rs b/engine/src/windowing/keyboard.rs
new file mode 100644
index 0000000..dffa95e
--- /dev/null
+++ b/engine/src/windowing/keyboard.rs
@@ -0,0 +1,826 @@
+use util_macros::VariantArr;
+
+use crate::ecs::Sole;
+use crate::util::BitArray;
+
+#[derive(Debug, Default, Sole)]
+pub struct Keyboard
+{
+ keys: BitArray<{ (Key::KEYS.len() * BITS_PER_KEY).div_ceil(8) }, BITS_PER_KEY>,
+ text_keys: String,
+}
+
+impl Keyboard
+{
+ /// Returns whether the given key was just pressed this frame. This function will
+ /// return `false` if the key was also pressed the previous frame.
+ pub fn just_pressed(&self, key: Key) -> bool
+ {
+ self.get_key_state(key) == KeyState::Pressed
+ && self.get_prev_key_state(key) == KeyState::Released
+ }
+
+ /// Returns whether the given key was just released this frame. This function will
+ /// return `false` if the key was also released the previous frame.
+ pub fn just_released(&self, key: Key) -> bool
+ {
+ self.get_key_state(key) == KeyState::Released
+ && self.get_prev_key_state(key) == KeyState::Pressed
+ }
+
+ /// Returns whether the given key is currently pressed.
+ pub fn pressed(&self, key: Key) -> bool
+ {
+ self.get_key_state(key) == KeyState::Pressed
+ }
+
+ /// Returns whether the given key is currently released.
+ pub fn released(&self, key: Key) -> bool
+ {
+ self.get_key_state(key) == KeyState::Released
+ }
+
+ /// Returns a iterator of key & key state pairs where each of the keys have a
+ /// different state than they had the previous frame.
+ pub fn new_key_states(&self) -> impl Iterator<Item = (Key, KeyState)> + use<'_>
+ {
+ self.keys.iter_occupied().filter_map(|(key_index, bits)| {
+ if bits == KEY_CURR_PRESSED_BITS | KEY_PREV_PRESSED_BITS {
+ return None;
+ }
+
+ let key = Key::KEYS[key_index];
+
+ let curr_state = match bits & KEY_CURR_PRESSED_BITS {
+ KEY_CURR_PRESSED_BITS => KeyState::Pressed,
+ 0 => KeyState::Released,
+ _ => unreachable!(),
+ };
+
+ Some((key, curr_state))
+ })
+ }
+
+ #[must_use]
+ pub fn get_key_state(&self, key: Key) -> KeyState
+ {
+ let bits = self.keys.get(key as usize);
+
+ let state = match bits & KEY_CURR_PRESSED_BITS {
+ KEY_CURR_PRESSED_BITS => KeyState::Pressed,
+ 0 => KeyState::Released,
+ _ => unreachable!(),
+ };
+
+ state
+ }
+
+ #[must_use]
+ pub fn get_prev_key_state(&self, key: Key) -> KeyState
+ {
+ let bits = self.keys.get(key as usize);
+
+ let state = match bits & KEY_PREV_PRESSED_BITS {
+ KEY_PREV_PRESSED_BITS => KeyState::Pressed,
+ 0 => KeyState::Released,
+ _ => unreachable!(),
+ };
+
+ state
+ }
+
+ pub fn text_keys(&self) -> &str
+ {
+ &self.text_keys
+ }
+
+ pub fn set_key_state(&mut self, key: Key, key_state: KeyState)
+ {
+ self.keys.clear_and_set(
+ key as usize,
+ match key_state {
+ KeyState::Pressed => KEY_CURR_PRESSED_BITS,
+ KeyState::Released => 0,
+ },
+ KEY_CURR_PRESSED_BITS,
+ );
+ }
+
+ pub fn make_key_states_previous(&mut self)
+ {
+ for byte in self.keys.bytes_mut() {
+ *byte = (*byte >> 1) & 0b01010101 | *byte & 0b10101010;
+ }
+ }
+
+ pub fn set_text_keys(&mut self, text_keys: impl IntoIterator<Item = char>)
+ {
+ self.text_keys.clear();
+ self.text_keys.extend(text_keys);
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArr)]
+#[variant_arr(name = KEYS)]
+#[non_exhaustive]
+pub enum Key
+{
+ /// <kbd>`</kbd> on a US keyboard. This is also called a backtick or grave.
+ /// This is the <kbd>半角</kbd>/<kbd>全角</kbd>/<kbd>漢字</kbd>
+ /// (hankaku/zenkaku/kanji) key on Japanese keyboards
+ Backquote,
+ /// Used for both the US <kbd>\\</kbd> (on the 101-key layout) and also for the key
+ /// located between the <kbd>"</kbd> and <kbd>Enter</kbd> keys on row C of the 102-,
+ /// 104- and 106-key layouts.
+ /// Labeled <kbd>#</kbd> on a UK (102) keyboard.
+ Backslash,
+ /// <kbd>[</kbd> on a US keyboard.
+ BracketLeft,
+ /// <kbd>]</kbd> on a US keyboard.
+ BracketRight,
+ /// <kbd>,</kbd> on a US keyboard.
+ Comma,
+ /// <kbd>0</kbd> on a US keyboard.
+ Digit0,
+ /// <kbd>1</kbd> on a US keyboard.
+ Digit1,
+ /// <kbd>2</kbd> on a US keyboard.
+ Digit2,
+ /// <kbd>3</kbd> on a US keyboard.
+ Digit3,
+ /// <kbd>4</kbd> on a US keyboard.
+ Digit4,
+ /// <kbd>5</kbd> on a US keyboard.
+ Digit5,
+ /// <kbd>6</kbd> on a US keyboard.
+ Digit6,
+ /// <kbd>7</kbd> on a US keyboard.
+ Digit7,
+ /// <kbd>8</kbd> on a US keyboard.
+ Digit8,
+ /// <kbd>9</kbd> on a US keyboard.
+ Digit9,
+ /// <kbd>=</kbd> on a US keyboard.
+ Equal,
+ /// Located between the left <kbd>Shift</kbd> and <kbd>Z</kbd> keys.
+ /// Labeled <kbd>\\</kbd> on a UK keyboard.
+ IntlBackslash,
+ /// Located between the <kbd>/</kbd> and right <kbd>Shift</kbd> keys.
+ /// Labeled <kbd>\\</kbd> (ro) on a Japanese keyboard.
+ IntlRo,
+ /// Located between the <kbd>=</kbd> and <kbd>Backspace</kbd> keys.
+ /// Labeled <kbd>¥</kbd> (yen) on a Japanese keyboard. <kbd>\\</kbd> on a
+ /// Russian keyboard.
+ IntlYen,
+ /// <kbd>a</kbd> on a US keyboard.
+ /// Labeled <kbd>q</kbd> on an AZERTY (e.g., French) keyboard.
+ A,
+ /// <kbd>b</kbd> on a US keyboard.
+ B,
+ /// <kbd>c</kbd> on a US keyboard.
+ C,
+ /// <kbd>d</kbd> on a US keyboard.
+ D,
+ /// <kbd>e</kbd> on a US keyboard.
+ E,
+ /// <kbd>f</kbd> on a US keyboard.
+ F,
+ /// <kbd>g</kbd> on a US keyboard.
+ G,
+ /// <kbd>h</kbd> on a US keyboard.
+ H,
+ /// <kbd>i</kbd> on a US keyboard.
+ I,
+ /// <kbd>j</kbd> on a US keyboard.
+ J,
+ /// <kbd>k</kbd> on a US keyboard.
+ K,
+ /// <kbd>l</kbd> on a US keyboard.
+ L,
+ /// <kbd>m</kbd> on a US keyboard.
+ M,
+ /// <kbd>n</kbd> on a US keyboard.
+ N,
+ /// <kbd>o</kbd> on a US keyboard.
+ O,
+ /// <kbd>p</kbd> on a US keyboard.
+ P,
+ /// <kbd>q</kbd> on a US keyboard.
+ /// Labeled <kbd>a</kbd> on an AZERTY (e.g., French) keyboard.
+ Q,
+ /// <kbd>r</kbd> on a US keyboard.
+ R,
+ /// <kbd>s</kbd> on a US keyboard.
+ S,
+ /// <kbd>t</kbd> on a US keyboard.
+ T,
+ /// <kbd>u</kbd> on a US keyboard.
+ U,
+ /// <kbd>v</kbd> on a US keyboard.
+ V,
+ /// <kbd>w</kbd> on a US keyboard.
+ /// Labeled <kbd>z</kbd> on an AZERTY (e.g., French) keyboard.
+ W,
+ /// <kbd>x</kbd> on a US keyboard.
+ X,
+ /// <kbd>y</kbd> on a US keyboard.
+ /// Labeled <kbd>z</kbd> on a QWERTZ (e.g., German) keyboard.
+ Y,
+ /// <kbd>z</kbd> on a US keyboard.
+ /// Labeled <kbd>w</kbd> on an AZERTY (e.g., French) keyboard, and <kbd>y</kbd> on a
+ /// QWERTZ (e.g., German) keyboard.
+ Z,
+ /// <kbd>-</kbd> on a US keyboard.
+ Minus,
+ /// <kbd>.</kbd> on a US keyboard.
+ Period,
+ /// <kbd>'</kbd> on a US keyboard.
+ Quote,
+ /// <kbd>;</kbd> on a US keyboard.
+ Semicolon,
+ /// <kbd>/</kbd> on a US keyboard.
+ Slash,
+ /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>.
+ AltLeft,
+ /// <kbd>Alt</kbd>, <kbd>Option</kbd>, or <kbd>⌥</kbd>.
+ /// This is labeled <kbd>AltGr</kbd> on many keyboard layouts.
+ AltRight,
+ /// <kbd>Backspace</kbd> or <kbd>⌫</kbd>.
+ /// Labeled <kbd>Delete</kbd> on Apple keyboards.
+ Backspace,
+ /// <kbd>CapsLock</kbd> or <kbd>⇪</kbd>
+ CapsLock,
+ /// The application context menu key, which is typically found between the right
+ /// <kbd>Super</kbd> key and the right <kbd>Control</kbd> key.
+ ContextMenu,
+ /// <kbd>Control</kbd> or <kbd>⌃</kbd>
+ ControlLeft,
+ /// <kbd>Control</kbd> or <kbd>⌃</kbd>
+ ControlRight,
+ /// <kbd>Enter</kbd> or <kbd>↵</kbd>. Labeled <kbd>Return</kbd> on Apple keyboards.
+ Enter,
+ /// The Windows, <kbd>⌘</kbd>, <kbd>Command</kbd>, or other OS symbol key.
+ SuperLeft,
+ /// The Windows, <kbd>⌘</kbd>, <kbd>Command</kbd>, or other OS symbol key.
+ SuperRight,
+ /// <kbd>Shift</kbd> or <kbd>⇧</kbd>
+ ShiftLeft,
+ /// <kbd>Shift</kbd> or <kbd>⇧</kbd>
+ ShiftRight,
+ /// <kbd> </kbd> (space)
+ Space,
+ /// <kbd>Tab</kbd> or <kbd>⇥</kbd>
+ Tab,
+ /// Japanese: <kbd>変</kbd> (henkan)
+ Convert,
+ /// Japanese: <kbd>カタカナ</kbd>/<kbd>ひらがな</kbd>/<kbd>ローマ字</kbd>
+ /// (katakana/hiragana/romaji)
+ KanaMode,
+ /// Korean: HangulMode <kbd>한/영</kbd> (han/yeong)
+ ///
+ /// Japanese (Mac keyboard): <kbd>か</kbd> (kana)
+ Lang1,
+ /// Korean: Hanja <kbd>한</kbd> (hanja)
+ ///
+ /// Japanese (Mac keyboard): <kbd>英</kbd> (eisu)
+ Lang2,
+ /// Japanese (word-processing keyboard): Katakana
+ Lang3,
+ /// Japanese (word-processing keyboard): Hiragana
+ Lang4,
+ /// Japanese (word-processing keyboard): Zenkaku/Hankaku
+ Lang5,
+ /// Japanese: <kbd>無変換</kbd> (muhenkan)
+ NonConvert,
+ /// <kbd>⌦</kbd>. The forward delete key.
+ /// Note that on Apple keyboards, the key labelled <kbd>Delete</kbd> on the main part
+ /// of the keyboard is encoded as [`Backspace`].
+ ///
+ /// [`Backspace`]: Self::Backspace
+ Delete,
+ /// <kbd>Page Down</kbd>, <kbd>End</kbd>, or <kbd>↘</kbd>
+ End,
+ /// <kbd>Help</kbd>. Not present on standard PC keyboards.
+ Help,
+ /// <kbd>Home</kbd> or <kbd>↖</kbd>
+ Home,
+ /// <kbd>Insert</kbd> or <kbd>Ins</kbd>. Not present on Apple keyboards.
+ Insert,
+ /// <kbd>Page Down</kbd>, <kbd>PgDn</kbd>, or <kbd>⇟</kbd>
+ PageDown,
+ /// <kbd>Page Up</kbd>, <kbd>PgUp</kbd>, or <kbd>⇞</kbd>
+ PageUp,
+ /// <kbd>↓</kbd>
+ ArrowDown,
+ /// <kbd>←</kbd>
+ ArrowLeft,
+ /// <kbd>→</kbd>
+ ArrowRight,
+ /// <kbd>↑</kbd>
+ ArrowUp,
+ /// On the Mac, this is used for the numpad <kbd>Clear</kbd> key.
+ NumLock,
+ /// <kbd>0 Ins</kbd> on a keyboard. <kbd>0</kbd> on a phone or remote control
+ Numpad0,
+ /// <kbd>1 End</kbd> on a keyboard. <kbd>1</kbd> or <kbd>1 QZ</kbd> on a phone or
+ /// remote control
+ Numpad1,
+ /// <kbd>2 ↓</kbd> on a keyboard. <kbd>2 ABC</kbd> on a phone or remote control
+ Numpad2,
+ /// <kbd>3 PgDn</kbd> on a keyboard. <kbd>3 DEF</kbd> on a phone or remote control
+ Numpad3,
+ /// <kbd>4 ←</kbd> on a keyboard. <kbd>4 GHI</kbd> on a phone or remote control
+ Numpad4,
+ /// <kbd>5</kbd> on a keyboard. <kbd>5 JKL</kbd> on a phone or remote control
+ Numpad5,
+ /// <kbd>6 →</kbd> on a keyboard. <kbd>6 MNO</kbd> on a phone or remote control
+ Numpad6,
+ /// <kbd>7 Home</kbd> on a keyboard. <kbd>7 PQRS</kbd> or <kbd>7 PRS</kbd> on a phone
+ /// or remote control
+ Numpad7,
+ /// <kbd>8 ↑</kbd> on a keyboard. <kbd>8 TUV</kbd> on a phone or remote control
+ Numpad8,
+ /// <kbd>9 PgUp</kbd> on a keyboard. <kbd>9 WXYZ</kbd> or <kbd>9 WXY</kbd> on a phone
+ /// or remote control
+ Numpad9,
+ /// <kbd>+</kbd>
+ NumpadAdd,
+ /// Found on the Microsoft Natural Keyboard.
+ NumpadBackspace,
+ /// <kbd>C</kbd> or <kbd>A</kbd> (All Clear). Also for use with numpads that have a
+ /// <kbd>Clear</kbd> key that is separate from the <kbd>NumLock</kbd> key. On the
+ /// Mac, the numpad <kbd>Clear</kbd> key is encoded as [`NumLock`].
+ ///
+ /// [`NumLock`]: Self::NumLock
+ NumpadClear,
+ /// <kbd>C</kbd> (Clear Entry)
+ NumpadClearEntry,
+ /// <kbd>,</kbd> (thousands separator). For locales where the thousands separator
+ /// is a "." (e.g., Brazil), this key may generate a <kbd>.</kbd>.
+ NumpadComma,
+ /// <kbd>. Del</kbd>. For locales where the decimal separator is "," (e.g.,
+ /// Brazil), this key may generate a <kbd>,</kbd>.
+ NumpadDecimal,
+ /// <kbd>/</kbd>
+ NumpadDivide,
+ NumpadEnter,
+ /// <kbd>=</kbd>
+ NumpadEqual,
+ /// <kbd>#</kbd> on a phone or remote control device. This key is typically found
+ /// below the <kbd>9</kbd> key and to the right of the <kbd>0</kbd> key.
+ NumpadHash,
+ /// <kbd>M</kbd> Add current entry to the value stored in memory.
+ NumpadMemoryAdd,
+ /// <kbd>M</kbd> Clear the value stored in memory.
+ NumpadMemoryClear,
+ /// <kbd>M</kbd> Replace the current entry with the value stored in memory.
+ NumpadMemoryRecall,
+ /// <kbd>M</kbd> Replace the value stored in memory with the current entry.
+ NumpadMemoryStore,
+ /// <kbd>M</kbd> Subtract current entry from the value stored in memory.
+ NumpadMemorySubtract,
+ /// <kbd>*</kbd> on a keyboard. For use with numpads that provide mathematical
+ /// operations (<kbd>+</kbd>, <kbd>-</kbd> <kbd>*</kbd> and <kbd>/</kbd>).
+ ///
+ /// Use `NumpadStar` for the <kbd>*</kbd> key on phones and remote controls.
+ NumpadMultiply,
+ /// <kbd>(</kbd> Found on the Microsoft Natural Keyboard.
+ NumpadParenLeft,
+ /// <kbd>)</kbd> Found on the Microsoft Natural Keyboard.
+ NumpadParenRight,
+ /// <kbd>*</kbd> on a phone or remote control device.
+ ///
+ /// This key is typically found below the <kbd>7</kbd> key and to the left of
+ /// the <kbd>0</kbd> key.
+ ///
+ /// Use <kbd>"NumpadMultiply"</kbd> for the <kbd>*</kbd> key on
+ /// numeric keypads.
+ NumpadStar,
+ /// <kbd>-</kbd>
+ NumpadSubtract,
+ /// <kbd>Esc</kbd> or <kbd>⎋</kbd>
+ Escape,
+ /// <kbd>Fn</kbd> This is typically a hardware key that does not generate a separate
+ /// code.
+ Fn,
+ /// <kbd>FLock</kbd> or <kbd>FnLock</kbd>. Function Lock key. Found on the Microsoft
+ /// Natural Keyboard.
+ FnLock,
+ /// <kbd>PrtScr SysRq</kbd> or <kbd>Print Screen</kbd>
+ PrintScreen,
+ /// <kbd>Scroll Lock</kbd>
+ ScrollLock,
+ /// <kbd>Pause Break</kbd>
+ Pause,
+ /// Some laptops place this key to the left of the <kbd>↑</kbd> key.
+ ///
+ /// This also the "back" button (triangle) on Android.
+ BrowserBack,
+ BrowserFavorites,
+ /// Some laptops place this key to the right of the <kbd>↑</kbd> key.
+ BrowserForward,
+ /// The "home" button on Android.
+ BrowserHome,
+ BrowserRefresh,
+ BrowserSearch,
+ BrowserStop,
+ /// <kbd>Eject</kbd> or <kbd>⏏</kbd>. This key is placed in the function section on
+ /// some Apple keyboards.
+ Eject,
+ /// Sometimes labelled <kbd>My Computer</kbd> on the keyboard
+ LaunchApp1,
+ /// Sometimes labelled <kbd>Calculator</kbd> on the keyboard
+ LaunchApp2,
+ LaunchMail,
+ MediaPlayPause,
+ MediaSelect,
+ MediaStop,
+ MediaTrackNext,
+ MediaTrackPrevious,
+ /// This key is placed in the function section on some Apple keyboards, replacing the
+ /// <kbd>Eject</kbd> key.
+ Power,
+ Sleep,
+ AudioVolumeDown,
+ AudioVolumeMute,
+ AudioVolumeUp,
+ WakeUp,
+ // Legacy modifier key. Also called "Super" in certain places.
+ Meta,
+ // Legacy modifier key.
+ Hyper,
+ Turbo,
+ Abort,
+ Resume,
+ Suspend,
+ /// Found on Sun’s USB keyboard.
+ Again,
+ /// Found on Sun’s USB keyboard.
+ Copy,
+ /// Found on Sun’s USB keyboard.
+ Cut,
+ /// Found on Sun’s USB keyboard.
+ Find,
+ /// Found on Sun’s USB keyboard.
+ Open,
+ /// Found on Sun’s USB keyboard.
+ Paste,
+ /// Found on Sun’s USB keyboard.
+ Props,
+ /// Found on Sun’s USB keyboard.
+ Select,
+ /// Found on Sun’s USB keyboard.
+ Undo,
+ /// Use for dedicated <kbd>ひらがな</kbd> key found on some Japanese word processing
+ /// keyboards.
+ Hiragana,
+ /// Use for dedicated <kbd>カタカナ</kbd> key found on some Japanese word processing
+ /// keyboards.
+ Katakana,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F1,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F2,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F3,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F4,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F5,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F6,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F7,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F8,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F9,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F10,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F11,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F12,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F13,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F14,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F15,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F16,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F17,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F18,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F19,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F20,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F21,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F22,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F23,
+ /// General-purpose function key.
+ /// Usually found at the top of the keyboard.
+ F24,
+ /// General-purpose function key.
+ F25,
+ /// General-purpose function key.
+ F26,
+ /// General-purpose function key.
+ F27,
+ /// General-purpose function key.
+ F28,
+ /// General-purpose function key.
+ F29,
+ /// General-purpose function key.
+ F30,
+ /// General-purpose function key.
+ F31,
+ /// General-purpose function key.
+ F32,
+ /// General-purpose function key.
+ F33,
+ /// General-purpose function key.
+ F34,
+ /// General-purpose function key.
+ F35,
+}
+
+impl TryFrom<winit::keyboard::KeyCode> for Key
+{
+ type Error = UnknownKeyCodeError;
+
+ fn try_from(key_code: winit::keyboard::KeyCode) -> Result<Self, Self::Error>
+ {
+ match key_code {
+ winit::keyboard::KeyCode::Backquote => Ok(Self::Backquote),
+ winit::keyboard::KeyCode::Backslash => Ok(Self::Backslash),
+ winit::keyboard::KeyCode::BracketLeft => Ok(Self::BracketLeft),
+ winit::keyboard::KeyCode::BracketRight => Ok(Self::BracketRight),
+ winit::keyboard::KeyCode::Comma => Ok(Self::Comma),
+ winit::keyboard::KeyCode::Digit0 => Ok(Self::Digit0),
+ winit::keyboard::KeyCode::Digit1 => Ok(Self::Digit1),
+ winit::keyboard::KeyCode::Digit2 => Ok(Self::Digit2),
+ winit::keyboard::KeyCode::Digit3 => Ok(Self::Digit3),
+ winit::keyboard::KeyCode::Digit4 => Ok(Self::Digit4),
+ winit::keyboard::KeyCode::Digit5 => Ok(Self::Digit5),
+ winit::keyboard::KeyCode::Digit6 => Ok(Self::Digit6),
+ winit::keyboard::KeyCode::Digit7 => Ok(Self::Digit7),
+ winit::keyboard::KeyCode::Digit8 => Ok(Self::Digit8),
+ winit::keyboard::KeyCode::Digit9 => Ok(Self::Digit9),
+ winit::keyboard::KeyCode::Equal => Ok(Self::Equal),
+ winit::keyboard::KeyCode::IntlBackslash => Ok(Self::IntlBackslash),
+ winit::keyboard::KeyCode::IntlRo => Ok(Self::IntlRo),
+ winit::keyboard::KeyCode::IntlYen => Ok(Self::IntlYen),
+ winit::keyboard::KeyCode::KeyA => Ok(Self::A),
+ winit::keyboard::KeyCode::KeyB => Ok(Self::B),
+ winit::keyboard::KeyCode::KeyC => Ok(Self::C),
+ winit::keyboard::KeyCode::KeyD => Ok(Self::D),
+ winit::keyboard::KeyCode::KeyE => Ok(Self::E),
+ winit::keyboard::KeyCode::KeyF => Ok(Self::F),
+ winit::keyboard::KeyCode::KeyG => Ok(Self::G),
+ winit::keyboard::KeyCode::KeyH => Ok(Self::H),
+ winit::keyboard::KeyCode::KeyI => Ok(Self::I),
+ winit::keyboard::KeyCode::KeyJ => Ok(Self::J),
+ winit::keyboard::KeyCode::KeyK => Ok(Self::K),
+ winit::keyboard::KeyCode::KeyL => Ok(Self::L),
+ winit::keyboard::KeyCode::KeyM => Ok(Self::M),
+ winit::keyboard::KeyCode::KeyN => Ok(Self::N),
+ winit::keyboard::KeyCode::KeyO => Ok(Self::O),
+ winit::keyboard::KeyCode::KeyP => Ok(Self::P),
+ winit::keyboard::KeyCode::KeyQ => Ok(Self::Q),
+ winit::keyboard::KeyCode::KeyR => Ok(Self::R),
+ winit::keyboard::KeyCode::KeyS => Ok(Self::S),
+ winit::keyboard::KeyCode::KeyT => Ok(Self::T),
+ winit::keyboard::KeyCode::KeyU => Ok(Self::U),
+ winit::keyboard::KeyCode::KeyV => Ok(Self::V),
+ winit::keyboard::KeyCode::KeyW => Ok(Self::W),
+ winit::keyboard::KeyCode::KeyX => Ok(Self::X),
+ winit::keyboard::KeyCode::KeyY => Ok(Self::Y),
+ winit::keyboard::KeyCode::KeyZ => Ok(Self::Z),
+ winit::keyboard::KeyCode::Minus => Ok(Self::Minus),
+ winit::keyboard::KeyCode::Period => Ok(Self::Period),
+ winit::keyboard::KeyCode::Quote => Ok(Self::Quote),
+ winit::keyboard::KeyCode::Semicolon => Ok(Self::Semicolon),
+ winit::keyboard::KeyCode::Slash => Ok(Self::Slash),
+ winit::keyboard::KeyCode::AltLeft => Ok(Self::AltLeft),
+ winit::keyboard::KeyCode::AltRight => Ok(Self::AltRight),
+ winit::keyboard::KeyCode::Backspace => Ok(Self::Backspace),
+ winit::keyboard::KeyCode::CapsLock => Ok(Self::CapsLock),
+ winit::keyboard::KeyCode::ContextMenu => Ok(Self::ContextMenu),
+ winit::keyboard::KeyCode::ControlLeft => Ok(Self::ControlLeft),
+ winit::keyboard::KeyCode::ControlRight => Ok(Self::ControlRight),
+ winit::keyboard::KeyCode::Enter => Ok(Self::Enter),
+ winit::keyboard::KeyCode::SuperLeft => Ok(Self::SuperLeft),
+ winit::keyboard::KeyCode::SuperRight => Ok(Self::SuperRight),
+ winit::keyboard::KeyCode::ShiftLeft => Ok(Self::ShiftLeft),
+ winit::keyboard::KeyCode::ShiftRight => Ok(Self::ShiftRight),
+ winit::keyboard::KeyCode::Space => Ok(Self::Space),
+ winit::keyboard::KeyCode::Tab => Ok(Self::Tab),
+ winit::keyboard::KeyCode::Convert => Ok(Self::Convert),
+ winit::keyboard::KeyCode::KanaMode => Ok(Self::KanaMode),
+ winit::keyboard::KeyCode::Lang1 => Ok(Self::Lang1),
+ winit::keyboard::KeyCode::Lang2 => Ok(Self::Lang2),
+ winit::keyboard::KeyCode::Lang3 => Ok(Self::Lang3),
+ winit::keyboard::KeyCode::Lang4 => Ok(Self::Lang4),
+ winit::keyboard::KeyCode::Lang5 => Ok(Self::Lang5),
+ winit::keyboard::KeyCode::NonConvert => Ok(Self::NonConvert),
+ winit::keyboard::KeyCode::Delete => Ok(Self::Delete),
+ winit::keyboard::KeyCode::End => Ok(Self::End),
+ winit::keyboard::KeyCode::Help => Ok(Self::Help),
+ winit::keyboard::KeyCode::Home => Ok(Self::Home),
+ winit::keyboard::KeyCode::Insert => Ok(Self::Insert),
+ winit::keyboard::KeyCode::PageDown => Ok(Self::PageDown),
+ winit::keyboard::KeyCode::PageUp => Ok(Self::PageUp),
+ winit::keyboard::KeyCode::ArrowDown => Ok(Self::ArrowDown),
+ winit::keyboard::KeyCode::ArrowLeft => Ok(Self::ArrowLeft),
+ winit::keyboard::KeyCode::ArrowRight => Ok(Self::ArrowRight),
+ winit::keyboard::KeyCode::ArrowUp => Ok(Self::ArrowUp),
+ winit::keyboard::KeyCode::NumLock => Ok(Self::NumLock),
+ winit::keyboard::KeyCode::Numpad0 => Ok(Self::Numpad0),
+ winit::keyboard::KeyCode::Numpad1 => Ok(Self::Numpad1),
+ winit::keyboard::KeyCode::Numpad2 => Ok(Self::Numpad2),
+ winit::keyboard::KeyCode::Numpad3 => Ok(Self::Numpad3),
+ winit::keyboard::KeyCode::Numpad4 => Ok(Self::Numpad4),
+ winit::keyboard::KeyCode::Numpad5 => Ok(Self::Numpad5),
+ winit::keyboard::KeyCode::Numpad6 => Ok(Self::Numpad6),
+ winit::keyboard::KeyCode::Numpad7 => Ok(Self::Numpad7),
+ winit::keyboard::KeyCode::Numpad8 => Ok(Self::Numpad8),
+ winit::keyboard::KeyCode::Numpad9 => Ok(Self::Numpad9),
+ winit::keyboard::KeyCode::NumpadAdd => Ok(Self::NumpadAdd),
+ winit::keyboard::KeyCode::NumpadBackspace => Ok(Self::NumpadBackspace),
+ winit::keyboard::KeyCode::NumpadClear => Ok(Self::NumpadClear),
+ winit::keyboard::KeyCode::NumpadClearEntry => Ok(Self::NumpadClearEntry),
+ winit::keyboard::KeyCode::NumpadComma => Ok(Self::NumpadComma),
+ winit::keyboard::KeyCode::NumpadDecimal => Ok(Self::NumpadDecimal),
+ winit::keyboard::KeyCode::NumpadDivide => Ok(Self::NumpadDivide),
+ winit::keyboard::KeyCode::NumpadEnter => Ok(Self::NumpadEnter),
+ winit::keyboard::KeyCode::NumpadEqual => Ok(Self::NumpadEqual),
+ winit::keyboard::KeyCode::NumpadHash => Ok(Self::NumpadHash),
+ winit::keyboard::KeyCode::NumpadMemoryAdd => Ok(Self::NumpadMemoryAdd),
+ winit::keyboard::KeyCode::NumpadMemoryClear => Ok(Self::NumpadMemoryClear),
+ winit::keyboard::KeyCode::NumpadMemoryRecall => Ok(Self::NumpadMemoryRecall),
+ winit::keyboard::KeyCode::NumpadMemoryStore => Ok(Self::NumpadMemoryStore),
+ winit::keyboard::KeyCode::NumpadMemorySubtract => {
+ Ok(Self::NumpadMemorySubtract)
+ }
+ winit::keyboard::KeyCode::NumpadMultiply => Ok(Self::NumpadMultiply),
+ winit::keyboard::KeyCode::NumpadParenLeft => Ok(Self::NumpadParenLeft),
+ winit::keyboard::KeyCode::NumpadParenRight => Ok(Self::NumpadParenRight),
+ winit::keyboard::KeyCode::NumpadStar => Ok(Self::NumpadStar),
+ winit::keyboard::KeyCode::NumpadSubtract => Ok(Self::NumpadSubtract),
+ winit::keyboard::KeyCode::Escape => Ok(Self::Escape),
+ winit::keyboard::KeyCode::Fn => Ok(Self::Fn),
+ winit::keyboard::KeyCode::FnLock => Ok(Self::FnLock),
+ winit::keyboard::KeyCode::PrintScreen => Ok(Self::PrintScreen),
+ winit::keyboard::KeyCode::ScrollLock => Ok(Self::ScrollLock),
+ winit::keyboard::KeyCode::Pause => Ok(Self::Pause),
+ winit::keyboard::KeyCode::BrowserBack => Ok(Self::BrowserBack),
+ winit::keyboard::KeyCode::BrowserFavorites => Ok(Self::BrowserFavorites),
+ winit::keyboard::KeyCode::BrowserForward => Ok(Self::BrowserForward),
+ winit::keyboard::KeyCode::BrowserHome => Ok(Self::BrowserHome),
+ winit::keyboard::KeyCode::BrowserRefresh => Ok(Self::BrowserRefresh),
+ winit::keyboard::KeyCode::BrowserSearch => Ok(Self::BrowserSearch),
+ winit::keyboard::KeyCode::BrowserStop => Ok(Self::BrowserStop),
+ winit::keyboard::KeyCode::Eject => Ok(Self::Eject),
+ winit::keyboard::KeyCode::LaunchApp1 => Ok(Self::LaunchApp1),
+ winit::keyboard::KeyCode::LaunchApp2 => Ok(Self::LaunchApp2),
+ winit::keyboard::KeyCode::LaunchMail => Ok(Self::LaunchMail),
+ winit::keyboard::KeyCode::MediaPlayPause => Ok(Self::MediaPlayPause),
+ winit::keyboard::KeyCode::MediaSelect => Ok(Self::MediaSelect),
+ winit::keyboard::KeyCode::MediaStop => Ok(Self::MediaStop),
+ winit::keyboard::KeyCode::MediaTrackNext => Ok(Self::MediaTrackNext),
+ winit::keyboard::KeyCode::MediaTrackPrevious => Ok(Self::MediaTrackPrevious),
+ winit::keyboard::KeyCode::Power => Ok(Self::Power),
+ winit::keyboard::KeyCode::Sleep => Ok(Self::Sleep),
+ winit::keyboard::KeyCode::AudioVolumeDown => Ok(Self::AudioVolumeDown),
+ winit::keyboard::KeyCode::AudioVolumeMute => Ok(Self::AudioVolumeMute),
+ winit::keyboard::KeyCode::AudioVolumeUp => Ok(Self::AudioVolumeUp),
+ winit::keyboard::KeyCode::WakeUp => Ok(Self::WakeUp),
+ winit::keyboard::KeyCode::Meta => Ok(Self::Meta),
+ winit::keyboard::KeyCode::Hyper => Ok(Self::Hyper),
+ winit::keyboard::KeyCode::Turbo => Ok(Self::Turbo),
+ winit::keyboard::KeyCode::Abort => Ok(Self::Abort),
+ winit::keyboard::KeyCode::Resume => Ok(Self::Resume),
+ winit::keyboard::KeyCode::Suspend => Ok(Self::Suspend),
+ winit::keyboard::KeyCode::Again => Ok(Self::Again),
+ winit::keyboard::KeyCode::Copy => Ok(Self::Copy),
+ winit::keyboard::KeyCode::Cut => Ok(Self::Cut),
+ winit::keyboard::KeyCode::Find => Ok(Self::Find),
+ winit::keyboard::KeyCode::Open => Ok(Self::Open),
+ winit::keyboard::KeyCode::Paste => Ok(Self::Paste),
+ winit::keyboard::KeyCode::Props => Ok(Self::Props),
+ winit::keyboard::KeyCode::Select => Ok(Self::Select),
+ winit::keyboard::KeyCode::Undo => Ok(Self::Undo),
+ winit::keyboard::KeyCode::Hiragana => Ok(Self::Hiragana),
+ winit::keyboard::KeyCode::Katakana => Ok(Self::Katakana),
+ winit::keyboard::KeyCode::F1 => Ok(Self::F1),
+ winit::keyboard::KeyCode::F2 => Ok(Self::F2),
+ winit::keyboard::KeyCode::F3 => Ok(Self::F3),
+ winit::keyboard::KeyCode::F4 => Ok(Self::F4),
+ winit::keyboard::KeyCode::F5 => Ok(Self::F5),
+ winit::keyboard::KeyCode::F6 => Ok(Self::F6),
+ winit::keyboard::KeyCode::F7 => Ok(Self::F7),
+ winit::keyboard::KeyCode::F8 => Ok(Self::F8),
+ winit::keyboard::KeyCode::F9 => Ok(Self::F9),
+ winit::keyboard::KeyCode::F10 => Ok(Self::F10),
+ winit::keyboard::KeyCode::F11 => Ok(Self::F11),
+ winit::keyboard::KeyCode::F12 => Ok(Self::F12),
+ winit::keyboard::KeyCode::F13 => Ok(Self::F13),
+ winit::keyboard::KeyCode::F14 => Ok(Self::F14),
+ winit::keyboard::KeyCode::F15 => Ok(Self::F15),
+ winit::keyboard::KeyCode::F16 => Ok(Self::F16),
+ winit::keyboard::KeyCode::F17 => Ok(Self::F17),
+ winit::keyboard::KeyCode::F18 => Ok(Self::F18),
+ winit::keyboard::KeyCode::F19 => Ok(Self::F19),
+ winit::keyboard::KeyCode::F20 => Ok(Self::F20),
+ winit::keyboard::KeyCode::F21 => Ok(Self::F21),
+ winit::keyboard::KeyCode::F22 => Ok(Self::F22),
+ winit::keyboard::KeyCode::F23 => Ok(Self::F23),
+ winit::keyboard::KeyCode::F24 => Ok(Self::F24),
+ winit::keyboard::KeyCode::F25 => Ok(Self::F25),
+ winit::keyboard::KeyCode::F26 => Ok(Self::F26),
+ winit::keyboard::KeyCode::F27 => Ok(Self::F27),
+ winit::keyboard::KeyCode::F28 => Ok(Self::F28),
+ winit::keyboard::KeyCode::F29 => Ok(Self::F29),
+ winit::keyboard::KeyCode::F30 => Ok(Self::F30),
+ winit::keyboard::KeyCode::F31 => Ok(Self::F31),
+ winit::keyboard::KeyCode::F32 => Ok(Self::F32),
+ winit::keyboard::KeyCode::F33 => Ok(Self::F33),
+ winit::keyboard::KeyCode::F34 => Ok(Self::F34),
+ winit::keyboard::KeyCode::F35 => Ok(Self::F35),
+ _ => Err(UnknownKeyCodeError),
+ }
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Unknown key code")]
+pub struct UnknownKeyCodeError;
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum KeyState
+{
+ Pressed,
+ Released,
+}
+
+impl KeyState
+{
+ #[must_use]
+ #[inline]
+ pub fn is_pressed(&self) -> bool
+ {
+ matches!(self, Self::Pressed)
+ }
+
+ #[must_use]
+ #[inline]
+ pub fn is_released(&self) -> bool
+ {
+ matches!(self, Self::Released)
+ }
+}
+
+impl From<winit::event::ElementState> for KeyState
+{
+ fn from(element_state: winit::event::ElementState) -> Self
+ {
+ match element_state {
+ winit::event::ElementState::Pressed => Self::Pressed,
+ winit::event::ElementState::Released => Self::Released,
+ }
+ }
+}
+
+const BITS_PER_KEY: usize = 2;
+
+const KEY_CURR_PRESSED_BITS: u8 = 0b10;
+const KEY_PREV_PRESSED_BITS: u8 = 0b01;
diff --git a/engine/src/windowing/monitor.rs b/engine/src/windowing/monitor.rs
new file mode 100644
index 0000000..894448a
--- /dev/null
+++ b/engine/src/windowing/monitor.rs
@@ -0,0 +1,50 @@
+use crate::windowing::dpi::{PhysicalPosition, PhysicalSize};
+
+/// Handle to a monitor that may or may not exist any longer.
+#[derive(Debug, Clone)]
+pub struct Handle
+{
+ inner: winit::monitor::MonitorHandle,
+}
+
+impl Handle
+{
+ /// Returns a human-readable name of the monitor.
+ #[inline]
+ pub fn name(&self) -> Option<String>
+ {
+ self.inner.name()
+ }
+
+ /// Returns the monitor's resolution.
+ #[inline]
+ pub fn size(&self) -> PhysicalSize<u32>
+ {
+ self.inner.size().into()
+ }
+
+ /// Returns the top-left corner position of the monitor relative to the larger full
+ /// screen area.
+ #[inline]
+ pub fn position(&self) -> PhysicalPosition<i32>
+ {
+ self.inner.position().into()
+ }
+
+ /// Returns the scale factor of the underlying monitor.
+ #[inline]
+ pub fn scale_factor(&self) -> f64
+ {
+ self.inner.scale_factor()
+ }
+}
+
+impl Handle
+{
+ pub(super) fn from_winit_monitor_handle(
+ monitor: winit::monitor::MonitorHandle,
+ ) -> Self
+ {
+ Self { inner: monitor }
+ }
+}
diff --git a/engine/src/windowing/mouse.rs b/engine/src/windowing/mouse.rs
new file mode 100644
index 0000000..3a43e79
--- /dev/null
+++ b/engine/src/windowing/mouse.rs
@@ -0,0 +1,197 @@
+use std::collections::HashMap;
+
+use crate::ecs::Sole;
+use crate::reflection::Reflection;
+use crate::vector::Vec2;
+use crate::windowing::dpi::PhysicalPosition;
+
+#[derive(Debug, Default, Clone, Sole, Reflection)]
+#[non_exhaustive]
+pub struct Mouse
+{
+ /// Change in coordinates this tick. Unit is unspecified and platforms may use
+ /// different units. Updated automatically by the [`windowing extension`].
+ ///
+ /// [`windowing extension`]: crate::windowing
+ pub curr_tick_position_delta: Vec2<f64>,
+
+ /// Coordinates in pixels relative to the top-left corner of the window. May have
+ /// been affected by cursor acceleration
+ pub position: PhysicalPosition<f64>,
+
+ pub curr_tick_scroll_delta: ScrollDelta,
+}
+
+#[derive(Debug, Clone, Default, Reflection)]
+pub struct ScrollDelta
+{
+ pub vert_lines: f32,
+ pub hor_lines: f32,
+}
+
+impl ScrollDelta
+{
+ pub fn is_zero(&self) -> bool
+ {
+ self.vert_lines == 0.0 && self.hor_lines == 0.0
+ }
+}
+
+/// Mouse buttons.
+#[derive(Debug, Default, Sole)]
+pub struct Buttons
+{
+ map: HashMap<Button, ButtonData>,
+}
+
+impl Buttons
+{
+ pub fn get(&self, button: Button) -> ButtonState
+ {
+ let Some(button_data) = self.map.get(&button) else {
+ return ButtonState::Released;
+ };
+
+ button_data.current_state
+ }
+
+ pub fn get_previous(&self, button: Button) -> ButtonState
+ {
+ let Some(button_data) = self.map.get(&button) else {
+ return ButtonState::Released;
+ };
+
+ button_data.previous_state
+ }
+
+ /// Returns a iterator that yields buttons and their current states. Only buttons with
+ /// states is included.
+ pub fn all_current(&self) -> impl Iterator<Item = (Button, ButtonState)> + use<'_>
+ {
+ self.map
+ .iter()
+ .map(|(button, button_data)| (button.clone(), button_data.current_state))
+ }
+
+ pub fn set(&mut self, button: Button, button_state: ButtonState)
+ {
+ let button_data = self.map.entry(button).or_default();
+
+ button_data.current_state = button_state;
+ }
+
+ pub fn set_previous(&mut self, button: Button, button_state: ButtonState)
+ {
+ let button_data = self.map.entry(button).or_default();
+
+ button_data.previous_state = button_state;
+ }
+
+ pub fn set_previous_to_current(&mut self, button: Button)
+ {
+ let button_data = self.map.entry(button).or_default();
+
+ button_data.previous_state = button_data.current_state;
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Button
+{
+ Left,
+ Right,
+ Middle,
+ Back,
+ Forward,
+ Other(u16),
+}
+
+impl intmap::IntKey for Button
+{
+ type Int = u32;
+
+ const PRIME: Self::Int = <u32 as intmap::IntKey>::PRIME;
+
+ fn into_int(self) -> Self::Int
+ {
+ match &self {
+ Self::Left => 0,
+ Self::Right => 1,
+ Self::Middle => 2,
+ Self::Back => 3,
+ Self::Forward => 4,
+ Self::Other(other) => 5 + *other as u32,
+ }
+ }
+}
+
+impl From<winit::event::MouseButton> for Button
+{
+ fn from(mouse_button: winit::event::MouseButton) -> Self
+ {
+ match mouse_button {
+ winit::event::MouseButton::Left => Self::Left,
+ winit::event::MouseButton::Right => Self::Right,
+ winit::event::MouseButton::Middle => Self::Middle,
+ winit::event::MouseButton::Back => Self::Back,
+ winit::event::MouseButton::Forward => Self::Forward,
+ winit::event::MouseButton::Other(other_mouse_button) => {
+ Self::Other(other_mouse_button)
+ }
+ }
+ }
+}
+
+/// Mouse button state.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum ButtonState
+{
+ Pressed,
+ Released,
+}
+
+impl ButtonState
+{
+ #[must_use]
+ #[inline]
+ pub fn is_pressed(&self) -> bool
+ {
+ matches!(self, Self::Pressed)
+ }
+
+ #[must_use]
+ #[inline]
+ pub fn is_released(&self) -> bool
+ {
+ matches!(self, Self::Released)
+ }
+}
+
+impl From<winit::event::ElementState> for ButtonState
+{
+ fn from(element_state: winit::event::ElementState) -> Self
+ {
+ match element_state {
+ winit::event::ElementState::Pressed => Self::Pressed,
+ winit::event::ElementState::Released => Self::Released,
+ }
+ }
+}
+
+#[derive(Debug)]
+struct ButtonData
+{
+ current_state: ButtonState,
+ previous_state: ButtonState,
+}
+
+impl Default for ButtonData
+{
+ fn default() -> Self
+ {
+ Self {
+ current_state: ButtonState::Released,
+ previous_state: ButtonState::Released,
+ }
+ }
+}
diff --git a/engine/src/windowing/window.rs b/engine/src/windowing/window.rs
new file mode 100644
index 0000000..6d7a464
--- /dev/null
+++ b/engine/src/windowing/window.rs
@@ -0,0 +1,291 @@
+use std::borrow::Cow;
+
+use crate::ecs::Component;
+use crate::image::Image;
+use crate::reflection::Reflection;
+use crate::windowing::dpi::{PhysicalSize, Position, Size};
+
+pub mod platform;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Id
+{
+ inner: winit::window::WindowId,
+}
+
+impl intmap::IntKey for Id
+{
+ type Int = u64;
+
+ const PRIME: Self::Int = <u64 as intmap::IntKey>::PRIME;
+
+ fn into_int(self) -> Self::Int
+ {
+ self.inner.into()
+ }
+}
+
+impl Id
+{
+ pub(crate) fn from_inner(inner: winit::window::WindowId) -> Self
+ {
+ Self { inner }
+ }
+}
+
+#[derive(Debug, Component, Clone, Reflection)]
+#[non_exhaustive]
+pub struct CreationAttributes
+{
+ pub title: Cow<'static, str>,
+ pub transparent: bool,
+ pub maximized: bool,
+ pub fullscreen: Option<Fullscreen>,
+ pub visible: bool,
+ pub resizable: bool,
+ pub position: Option<Position>,
+ pub inner_size: Option<Size>,
+ pub icon: Option<Icon>,
+
+ pub x_visual_id: Option<XVisualID>,
+
+ // These do not have equivalents in winit::window::WindowAttributes
+ pub cursor_visible: bool,
+ pub cursor_grab_mode: CursorGrabMode,
+}
+
+macro_rules! gen_creation_attrs_with_fn {
+ ($field: ident, $field_type: ty) => {
+ paste::paste! {
+ impl CreationAttributes
+ {
+ pub fn [<with_ $field>](mut self, new: impl Into<$field_type>) -> Self
+ {
+ self.$field = new.into();
+ self
+ }
+ }
+ }
+ };
+}
+
+gen_creation_attrs_with_fn!(title, Cow<'static, str>);
+gen_creation_attrs_with_fn!(transparent, bool);
+gen_creation_attrs_with_fn!(maximized, bool);
+gen_creation_attrs_with_fn!(fullscreen, Option<Fullscreen>);
+gen_creation_attrs_with_fn!(visible, bool);
+gen_creation_attrs_with_fn!(resizable, bool);
+gen_creation_attrs_with_fn!(position, Option<Position>);
+gen_creation_attrs_with_fn!(inner_size, Option<Size>);
+gen_creation_attrs_with_fn!(icon, Option<Icon>);
+
+gen_creation_attrs_with_fn!(x_visual_id, Option<XVisualID>);
+
+gen_creation_attrs_with_fn!(cursor_visible, bool);
+gen_creation_attrs_with_fn!(cursor_grab_mode, CursorGrabMode);
+
+impl CreationAttributes
+{
+ #[tracing::instrument(skip_all)]
+ pub(crate) fn into_window_attrs(self) -> winit::window::WindowAttributes
+ {
+ let mut window_attrs = winit::window::WindowAttributes::default()
+ .with_title(self.title.into_owned())
+ .with_transparent(self.transparent)
+ .with_maximized(self.maximized)
+ .with_fullscreen(match self.fullscreen {
+ Some(Fullscreen::Borderless) => {
+ Some(winit::window::Fullscreen::Borderless(None))
+ }
+ None => None,
+ })
+ .with_visible(self.visible)
+ .with_resizable(self.resizable)
+ .with_window_icon(match self.icon {
+ Some(Icon::Image(image)) => {
+ let image_dimens = image.dimensions();
+
+ let result = winit::window::Icon::from_rgba(
+ image.into_rgba8().into_bytes(),
+ image_dimens.width,
+ image_dimens.height,
+ );
+
+ if let Err(err) = &result {
+ tracing::error!("Invalid icon image: {err}");
+ }
+
+ result.ok()
+ }
+ #[cfg(windows)]
+ Some(Icon::WindowsResource(resource_name)) => {
+ use winit::platform::windows::IconExtWindows;
+
+ let result = winit::window::Icon::from_resource_name(
+ resource_name.as_ref(),
+ None,
+ );
+
+ if let Err(err) = &result {
+ tracing::error!(
+ "Invalid icon image in resource {resource_name}: {err}"
+ );
+ }
+
+ result.ok()
+ }
+ None => None,
+ });
+
+ window_attrs.position = self.position.map(Into::into);
+ window_attrs.inner_size = self.inner_size.map(Into::into);
+
+ #[cfg(target_os = "linux")]
+ if let Some(visual_id) = self.x_visual_id {
+ use winit::platform::x11::WindowAttributesExtX11;
+
+ return window_attrs.with_x11_visual(visual_id);
+ }
+
+ window_attrs
+ }
+
+ pub(crate) fn apply_extra_attrs_to_window(&self, window: &winit::window::Window)
+ {
+ window.set_cursor_visible(self.cursor_visible);
+ }
+}
+
+impl Default for CreationAttributes
+{
+ fn default() -> Self
+ {
+ Self {
+ title: "Unnamed window".into(),
+ transparent: false,
+ maximized: false,
+ fullscreen: None,
+ visible: true,
+ resizable: true,
+ position: None,
+ inner_size: None,
+ icon: None,
+ x_visual_id: None,
+ cursor_visible: true,
+ cursor_grab_mode: CursorGrabMode::None,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Reflection)]
+#[non_exhaustive]
+pub enum Fullscreen
+{
+ Borderless,
+}
+
+/// Window icon
+#[derive(Debug, Clone, Reflection)]
+#[non_exhaustive]
+pub enum Icon
+{
+ Image(Image),
+
+ #[cfg(windows)]
+ WindowsResource(Cow<'static, str>),
+}
+
+#[derive(Debug, Default, Component, Clone, Copy, Reflection)]
+pub struct CreationReady;
+
+#[derive(Debug, Component, Reflection)]
+#[non_exhaustive]
+pub struct Window
+{
+ pub title: String,
+ pub cursor_visible: bool,
+ pub cursor_grab_mode: CursorGrabMode,
+ pub inner_size: PhysicalSize<u32>,
+ wid: Id,
+ scale_factor: f64,
+}
+
+impl Window
+{
+ pub fn wid(&self) -> Id
+ {
+ self.wid
+ }
+
+ pub fn scale_factor(&self) -> f64
+ {
+ self.scale_factor
+ }
+
+ pub(crate) fn new(
+ winit_window: &winit::window::Window,
+ creation_attrs: &CreationAttributes,
+ ) -> Self
+ {
+ Self {
+ title: creation_attrs.title.clone().into_owned(),
+ cursor_visible: creation_attrs.cursor_visible,
+ cursor_grab_mode: creation_attrs.cursor_grab_mode,
+ wid: Id::from_inner(winit_window.id()),
+ inner_size: winit_window.inner_size().into(),
+ scale_factor: winit_window.scale_factor(),
+ }
+ }
+
+ #[must_use]
+ pub(crate) fn apply(&self, winit_window: &winit::window::Window) -> ApplyResults
+ {
+ winit_window.set_title(&self.title);
+ winit_window.set_cursor_visible(self.cursor_visible);
+
+ let curr_inner_size = winit_window.inner_size().clone().into();
+
+ let inner_size_request_result = match winit_window.request_inner_size(
+ winit::dpi::Size::Physical(self.inner_size.clone().into()),
+ ) {
+ // The comparison of curr_inner_size is in case the user's windowing system
+ // lies about using the requested inner size
+ None if curr_inner_size == self.inner_size => Ok(()),
+ None => Err(curr_inner_size),
+ Some(inner_size) => Err(inner_size.into()),
+ };
+
+ ApplyResults {
+ inner_size_request: inner_size_request_result,
+ }
+ }
+
+ pub(crate) fn set_scale_factor(&mut self, scale_factor: f64)
+ {
+ self.scale_factor = scale_factor;
+ }
+}
+
+#[derive(Debug, Component, Reflection)]
+pub struct Closed;
+
+#[derive(
+ Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Reflection,
+)]
+pub enum CursorGrabMode
+{
+ #[default]
+ None,
+
+ /// The cursor is locked to a specific position in the window.
+ Locked,
+}
+
+/// A unique identifier for an X11 visual.
+pub type XVisualID = u32;
+
+#[derive(Debug)]
+pub(crate) struct ApplyResults
+{
+ pub inner_size_request: Result<(), PhysicalSize<u32>>,
+}
diff --git a/engine/src/windowing/window/platform.rs b/engine/src/windowing/window/platform.rs
new file mode 100644
index 0000000..1f38f3b
--- /dev/null
+++ b/engine/src/windowing/window/platform.rs
@@ -0,0 +1,12 @@
+#[cfg(target_os = "linux")]
+pub mod x11
+{
+ use std::ffi::c_void;
+
+ pub type XlibErrorHook = Box<dyn Fn(*mut c_void, *mut c_void) -> bool + Send + Sync>;
+
+ pub fn register_xlib_error_hook(hook: XlibErrorHook)
+ {
+ winit::platform::x11::register_xlib_error_hook(hook);
+ }
+}
diff --git a/engine/src/work_queue.rs b/engine/src/work_queue.rs
new file mode 100644
index 0000000..494d2b5
--- /dev/null
+++ b/engine/src/work_queue.rs
@@ -0,0 +1,77 @@
+use std::borrow::Cow;
+use std::marker::PhantomData;
+use std::panic::catch_unwind;
+use std::sync::mpsc::{channel as mpsc_channel, Sender as MpscSender};
+use std::sync::{Arc, OnceLock};
+use std::thread::{Builder as ThreadBuilder, JoinHandle as ThreadJoinHandle};
+
+pub struct Work<UserData: Send + Sync + 'static>
+{
+ pub func: fn(UserData),
+ pub user_data: UserData,
+}
+
+#[derive(Debug)]
+pub struct WorkQueue<UserData: Send + Sync + 'static>
+{
+ work_sender: MpscSender<Work<UserData>>,
+ thread_panic: Arc<OnceLock<Box<str>>>,
+ _thread: ThreadJoinHandle<()>,
+ _pd: PhantomData<UserData>,
+}
+
+impl<UserData: Send + Sync + 'static> WorkQueue<UserData>
+{
+ pub fn new(name: &str) -> Self
+ {
+ let (work_sender, work_receiver) = mpsc_channel::<Work<UserData>>();
+
+ let thread_panic = Arc::new(OnceLock::new());
+
+ let thread_panic_b = thread_panic.clone();
+
+ Self {
+ work_sender,
+ thread_panic: thread_panic,
+ _thread: ThreadBuilder::new()
+ .name(name.to_string())
+ .spawn(move || {
+ if let Err(panic_err) = catch_unwind(|| {
+ while let Ok(work) = work_receiver.recv() {
+ (work.func)(work.user_data);
+ }
+ }) {
+ let panic_message: Cow<'static, str> =
+ if let Some(panic_message) =
+ panic_err.downcast_ref::<&'static str>()
+ {
+ (*panic_message).into()
+ } else if let Some(panic_message) =
+ panic_err.downcast_ref::<String>()
+ {
+ panic_message.clone().into()
+ } else {
+ "(unknown panic payload type)".into()
+ };
+
+ let _ = thread_panic_b
+ .set(panic_message.into_owned().into_boxed_str());
+ }
+ })
+ .expect("Failed to create work queue thread"),
+ _pd: PhantomData,
+ }
+ }
+
+ pub fn get_thread_panic(&self) -> Option<&str>
+ {
+ self.thread_panic.get().map(|thread_panic| &**thread_panic)
+ }
+
+ pub fn add_work(&self, work: Work<UserData>)
+ {
+ if self.work_sender.send(work).is_err() {
+ tracing::error!("Cannot add work to work queue. Work queue thread is dead");
+ }
+ }
+}
diff --git a/glfw/Cargo.toml b/glfw/Cargo.toml
deleted file mode 100644
index 0a47e73..0000000
--- a/glfw/Cargo.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[package]
-name = "glfw"
-version = "0.1.0"
-edition = "2021"
-
-[features]
-opengl = []
-
-[dependencies]
-libc = "0.2.148"
-thiserror = "1.0.49"
-bitflags = "2.4.0"
-util-macros = { path = "../util-macros" }
-
-[build-dependencies.bindgen]
-version = "0.68.1"
-default-features = false
-features = ["runtime"]
diff --git a/glfw/build.rs b/glfw/build.rs
deleted file mode 100644
index 18ac677..0000000
--- a/glfw/build.rs
+++ /dev/null
@@ -1,42 +0,0 @@
-use std::env;
-use std::error::Error;
-use std::fs::OpenOptions;
-use std::io::Write;
-use std::path::PathBuf;
-
-use bindgen::{Abi, MacroTypeVariation};
-
-fn main() -> Result<(), Box<dyn Error>>
-{
- println!("cargo:rustc-link-lib=glfw");
-
- println!("cargo:rerun-if-changed=glfw.h");
-
- let bindings = bindgen::Builder::default()
- .header("glfw.h")
- .clang_arg("-fretain-comments-from-system-headers")
- .generate_comments(true)
- .allowlist_function("glfw.*")
- .allowlist_type("GLFW.*")
- .allowlist_var("GLFW.*")
- .blocklist_type("GLFWglproc")
- .default_macro_constant_type(MacroTypeVariation::Signed)
- .override_abi(Abi::CUnwind, ".*")
- .generate()?;
-
- let out_path = PathBuf::from(env::var("OUT_DIR")?);
-
- let bindings_file_path = out_path.join("bindings.rs");
-
- bindings.write_to_file(&bindings_file_path)?;
-
- let mut bindings_file = OpenOptions::new().append(true).open(bindings_file_path)?;
-
- // Cannot be C-unwind :(
- writeln!(
- bindings_file,
- "pub type GLFWglproc = ::std::option::Option<unsafe extern \"C\" fn()>;"
- )?;
-
- Ok(())
-}
diff --git a/glfw/glfw.h b/glfw/glfw.h
deleted file mode 100644
index 7b435c0..0000000
--- a/glfw/glfw.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#include <GLFW/glfw3.h>
-
-#if GLFW_VERSION_MAJOR != 3
-#error "Unsupported major version of GLFW. Must be 3"
-#endif
-
diff --git a/glfw/src/ffi.rs b/glfw/src/ffi.rs
deleted file mode 100644
index d0affd0..0000000
--- a/glfw/src/ffi.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#![allow(
- non_snake_case,
- non_camel_case_types,
- non_upper_case_globals,
- unused,
- clippy::unreadable_literal
-)]
-
-include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
diff --git a/glfw/src/init.rs b/glfw/src/init.rs
deleted file mode 100644
index df5a989..0000000
--- a/glfw/src/init.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use std::ffi::c_int;
-use std::ptr::null_mut;
-
-use crate::util::is_main_thread;
-use crate::{get_glfw_error, Error};
-
-/// GLFW 3.x.x is supported
-static SUPPORTED_GLFW_VERSION_MAJOR: c_int = 3;
-
-/// Initializes GLFW and returns a initialization token.
-///
-/// # Errors
-/// Will return `Err` if
-/// - The current thread is not the main thread
-/// - A GLFW error occurs
-pub fn initialize() -> Result<Glfw, Error>
-{
- if !is_main_thread() {
- return Err(Error::NotInMainThread);
- }
-
- let mut major: c_int = 0;
-
- unsafe { crate::ffi::glfwGetVersion(&mut major, null_mut(), null_mut()) };
-
- if major != SUPPORTED_GLFW_VERSION_MAJOR {
- return Err(Error::UnsupportedVersion);
- }
-
- // SAFETY: The current thread is the main thread
- let success = unsafe { crate::ffi::glfwInit() };
-
- if success == crate::ffi::GLFW_FALSE {
- get_glfw_error()?;
- }
-
- Ok(Glfw { _priv: &() })
-}
-
-/// GLFW initialization token.
-#[derive(Debug)]
-pub struct Glfw
-{
- /// This field has two purposes
- /// - To make the struct not constructable without calling [`initialize`].
- /// - To make the struct `!Send` and `!Sync`.
- _priv: *const (),
-}
-
-impl Drop for Glfw
-{
- fn drop(&mut self)
- {
- // SAFETY: The current thread cannot be any other thread than the main thread
- // since the initialize function checks it and the GLFW initialization token is
- // neither Send or Sync
- unsafe { crate::ffi::glfwTerminate() };
- }
-}
diff --git a/glfw/src/lib.rs b/glfw/src/lib.rs
deleted file mode 100644
index f858766..0000000
--- a/glfw/src/lib.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-#![deny(clippy::all, clippy::pedantic)]
-
-use std::ffi::{c_char, CStr};
-use std::ptr::null;
-
-mod ffi;
-mod init;
-mod util;
-
-pub mod window;
-
-pub use window::{Builder as WindowBuilder, Size as WindowSize, Window};
-
-#[derive(Debug, thiserror::Error)]
-pub enum Error
-{
- /// The current thread is not the main thread.
- #[error("The current thread is not the main thread")]
- NotInMainThread,
-
- /// An internal nul byte was found in a window title.
- #[error("An internal nul byte was found in the window title")]
- InternalNulByteInWindowTitle,
-
- #[error("This version of GLFW is unsupported")]
- UnsupportedVersion,
-
- /// GLFW error.
- #[error("GLFW error {0} occured. {1}")]
- GlfwError(i32, String),
-}
-
-fn get_glfw_error() -> Result<(), Error>
-{
- let mut description_ptr: *const c_char = null();
-
- let err = unsafe { crate::ffi::glfwGetError(&mut description_ptr) };
-
- if err == crate::ffi::GLFW_NO_ERROR {
- return Ok(());
- }
-
- // SAFETY: The description is guaranteed by GLFW to be valid UTF-8
- let desc_str = unsafe {
- std::str::from_utf8_unchecked(CStr::from_ptr(description_ptr).to_bytes())
- };
-
- // The description has to be copied because it is guaranteed to be valid only
- // until the next GLFW error occurs or the GLFW library is terminated.
- let description = desc_str.to_string();
-
- Err(Error::GlfwError(err, description))
-}
diff --git a/glfw/src/util.rs b/glfw/src/util.rs
deleted file mode 100644
index f77aaf8..0000000
--- a/glfw/src/util.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use libc::{c_long, getpid, syscall, SYS_gettid};
-
-pub fn is_main_thread() -> bool
-{
- let ttid = unsafe { syscall(SYS_gettid) };
-
- let pid = c_long::from(unsafe { getpid() });
-
- ttid == pid
-}
diff --git a/glfw/src/window.rs b/glfw/src/window.rs
deleted file mode 100644
index 1e7e777..0000000
--- a/glfw/src/window.rs
+++ /dev/null
@@ -1,863 +0,0 @@
-use std::cell::RefCell;
-use std::ffi::{c_double, c_int, CString};
-use std::hint::unreachable_unchecked;
-use std::io::{stdout, Write};
-use std::ptr::null_mut;
-
-use bitflags::bitflags;
-use util_macros::{FromRepr, VariantArr};
-
-use crate::init::{initialize, Glfw};
-use crate::{get_glfw_error, Error};
-
-#[derive(Debug)]
-pub struct Window
-{
- _init: Glfw,
- handle: *mut crate::ffi::GLFWwindow,
-}
-
-impl Window
-{
- /// Makes the context of the window current for the calling thread.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW platform error occurs or if no OpenGL context is
- /// present.
- #[cfg(feature = "opengl")]
- pub fn make_context_current(&self) -> Result<(), Error>
- {
- unsafe { crate::ffi::glfwMakeContextCurrent(self.handle) };
-
- get_glfw_error()?;
-
- Ok(())
- }
-
- /// Returns the address of the specified OpenGL function, if it is supported by the
- /// current context.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW platform error occurs or if no current context has
- /// been set.
- #[cfg(feature = "opengl")]
- pub fn get_proc_address(
- &self,
- proc_name: &std::ffi::CStr,
- ) -> Result<unsafe extern "C" fn(), Error>
- {
- let proc_addr = unsafe { crate::ffi::glfwGetProcAddress(proc_name.as_ptr()) };
-
- get_glfw_error()?;
-
- // SAFETY: Is only None when a error has occured and that case is handled above
- Ok(unsafe { proc_addr.unwrap_unchecked() })
- }
-
- /// Processes all pending events.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW platform error occurs.
- pub fn poll_events(&self) -> Result<(), Error>
- {
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe { crate::ffi::glfwPollEvents() };
-
- get_glfw_error()?;
-
- Ok(())
- }
-
- /// Swaps the front and back buffers of the window.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW platform error occurs or if no OpenGL window context
- /// is present.
- pub fn swap_buffers(&self) -> Result<(), Error>
- {
- unsafe {
- crate::ffi::glfwSwapBuffers(self.handle);
- };
-
- get_glfw_error()?;
-
- Ok(())
- }
-
- /// Returns whether or not the window should close.
- #[must_use]
- pub fn should_close(&self) -> bool
- {
- let should_close = unsafe { crate::ffi::glfwWindowShouldClose(self.handle) };
-
- should_close == crate::ffi::GLFW_TRUE
- }
-
- /// Retrieves the size of the window.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW platform error occurs.
- pub fn size(&self) -> Result<Size, Error>
- {
- let mut width = 0;
- let mut height = 0;
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe { crate::ffi::glfwGetWindowSize(self.handle, &mut width, &mut height) };
-
- get_glfw_error()?;
-
- #[allow(clippy::cast_sign_loss)]
- Ok(Size {
- width: width as u32,
- height: height as u32,
- })
- }
-
- /// Sets the callback which is called when the user attempts to close the window.
- pub fn set_close_callback(&self, callback: impl Fn() + 'static)
- {
- CLOSE_CALLBACK.with_borrow_mut(|cb| {
- *cb = Some(Box::new(callback));
- });
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetWindowCloseCallback(self.handle, Some(close_callback));
- }
- }
-
- pub fn set_framebuffer_size_callback(&self, callback: impl Fn(Size) + 'static)
- {
- FRAMEBUFFER_SIZE_CB.with_borrow_mut(|framebuffer_size_cb| {
- *framebuffer_size_cb = Some(Box::new(callback));
- });
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetFramebufferSizeCallback(
- self.handle,
- Some(framebuffer_size_callback),
- );
- }
- }
-
- pub fn set_key_callback(
- &self,
- callback: impl Fn(Key, i32, KeyState, KeyModifiers) + 'static,
- )
- {
- KEY_CALLBACK.with_borrow_mut(|key_callback| {
- *key_callback = Some(Box::new(callback));
- });
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetKeyCallback(self.handle, Some(key_callback));
- }
- }
-
- /// Sets the cursor position callback.
- ///
- /// The callback is provided with the position, in screen coordinates, relative to the
- /// upper-left corner of the content area of the window.
- pub fn set_cursor_pos_callback(&self, callback: impl Fn(CursorPosition) + 'static)
- {
- CURSOR_POS_CALLBACK.with_borrow_mut(|cursor_pos_callback| {
- *cursor_pos_callback = Some(Box::new(callback));
- });
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetCursorPosCallback(self.handle, Some(cursor_pos_callback));
- }
- }
-
- /// Sets the mouse button callback.
- pub fn set_mouse_button_callback(
- &self,
- callback: impl Fn(MouseButton, MouseButtonState, KeyModifiers) + 'static,
- )
- {
- MOUSE_BUTTON_CALLBACK.with_borrow_mut(|mouse_button_callback| {
- *mouse_button_callback = Some(Box::new(callback));
- });
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetMouseButtonCallback(
- self.handle,
- Some(mouse_button_callback),
- );
- }
- }
-
- /// Sets the callback called when the window gains or loses input focus.
- pub fn set_focus_callback(&self, callback: impl Fn(bool) + 'static)
- {
- FOCUS_CALLBACK.with_borrow_mut(|focus_cb| {
- *focus_cb = Some(Box::new(callback));
- });
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetWindowFocusCallback(self.handle, Some(focus_callback));
- }
- }
-
- /// Returns the last reported state of a keyboard key.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW error occurs.
- pub fn get_key(&self, key: Key) -> Result<KeyState, Error>
- {
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- let state = unsafe { crate::ffi::glfwGetKey(self.handle, key as i32) };
-
- get_glfw_error()?;
-
- Ok(match state {
- crate::ffi::GLFW_PRESS => KeyState::Pressed,
- crate::ffi::GLFW_RELEASE => KeyState::Released,
- _ => {
- // SAFETY: glfwGetKey can only return GLFW_PRESS or GLFW_RELEASE
- unsafe {
- unreachable_unchecked();
- }
- }
- })
- }
-
- /// Returns the last reported state of a mouse button.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW error occurs.
- pub fn get_mouse_button(
- &self,
- mouse_button: MouseButton,
- ) -> Result<MouseButtonState, Error>
- {
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- let state =
- unsafe { crate::ffi::glfwGetMouseButton(self.handle, mouse_button as i32) };
-
- get_glfw_error()?;
-
- Ok(match state {
- crate::ffi::GLFW_PRESS => MouseButtonState::Pressed,
- crate::ffi::GLFW_RELEASE => MouseButtonState::Released,
- _ => {
- // SAFETY: glfwGetMouseButton can only return GLFW_PRESS or GLFW_RELEASE
- unsafe {
- unreachable_unchecked();
- }
- }
- })
- }
-
- /// Returns the position of the cursor, in screen coordinates, relative to the
- /// upper-left corner of the content area of the window.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW error occurs.
- pub fn get_cursor_position(&self) -> Result<CursorPosition, Error>
- {
- let mut x_pos = 0.0;
- let mut y_pos = 0.0;
-
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwGetCursorPos(self.handle, &mut x_pos, &mut y_pos);
- }
-
- get_glfw_error()?;
-
- Ok(CursorPosition { x: x_pos, y: y_pos })
- }
-
- /// Sets a input mode option.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW error occurs.
- pub fn set_input_mode(
- &self,
- input_mode: InputMode,
- enabled: bool,
- ) -> Result<(), Error>
- {
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetInputMode(
- self.handle,
- input_mode as i32,
- i32::from(enabled),
- );
- }
-
- get_glfw_error()?;
-
- Ok(())
- }
-
- /// Sets the cursor mode.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW error occurs.
- pub fn set_cursor_mode(&self, cursor_mode: CursorMode) -> Result<(), Error>
- {
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- unsafe {
- crate::ffi::glfwSetInputMode(
- self.handle,
- crate::ffi::GLFW_CURSOR,
- cursor_mode as i32,
- );
- }
-
- get_glfw_error()?;
-
- Ok(())
- }
-
- /// Returns whether or not raw mouse motion is supported.
- ///
- /// # Errors
- /// Will return `Err` if a GLFW error occurs.
- pub fn is_raw_mouse_motion_supported(&self) -> Result<bool, Error>
- {
- // SAFETY: The initialize function (called when the window is created) makes sure
- // the current thread is the main thread
- let supported = unsafe { crate::ffi::glfwRawMouseMotionSupported() };
-
- get_glfw_error()?;
-
- Ok(supported == crate::ffi::GLFW_TRUE)
- }
-}
-
-/// [`Window`] builder.
-#[derive(Debug, Clone, Default)]
-pub struct Builder
-{
- hints: Vec<(Hint, HintValue)>,
-}
-
-impl Builder
-{
- #[must_use]
- pub fn new() -> Self
- {
- Self { hints: Vec::new() }
- }
-
- /// Adds a window creation hint to set.
- #[must_use]
- pub fn hint(mut self, hint: Hint, value: HintValue) -> Self
- {
- self.hints.push((hint, value));
-
- self
- }
-
- /// Sets the window hints to set.
- pub fn hints(mut self, hints: impl IntoIterator<Item = (Hint, HintValue)>)
- {
- self.hints = hints.into_iter().collect();
- }
-
- /// Creates a new window.
- ///
- /// # Errors
- /// Will return `Err` if
- /// - The title contains an internal nul byte
- /// - A GLFW error occurs
- pub fn create(&self, size: &Size, title: &str) -> Result<Window, Error>
- {
- let c_title =
- CString::new(title).map_err(|_| Error::InternalNulByteInWindowTitle)?;
-
- let init = initialize()?;
-
- for (hint, value) in &self.hints {
- // SAFETY: The initialize function makes sure the current thread is the main
- // thread
- //
- // Error is not checked for after since the two possible errors
- // (GLFW_NOT_INITIALIZED and GLFW_INVALID_ENUM) cannot occur.
- unsafe {
- crate::ffi::glfwWindowHint(
- *hint as i32,
- match value {
- HintValue::Number(num) => *num,
- HintValue::Bool(boolean) => {
- if *boolean {
- crate::ffi::GLFW_TRUE
- } else {
- crate::ffi::GLFW_FALSE
- }
- }
- },
- );
- }
- }
-
- // SAFETY: The initialize function makes sure the current thread is the main
- // thread
- let handle = unsafe {
- #[allow(clippy::cast_possible_wrap)]
- crate::ffi::glfwCreateWindow(
- size.width as i32,
- size.height as i32,
- c_title.as_ptr(),
- null_mut(),
- null_mut(),
- )
- };
-
- get_glfw_error()?;
-
- Ok(Window { _init: init, handle })
- }
-}
-
-/// Window creation hint
-#[derive(Debug, Clone, Copy)]
-#[repr(i32)]
-#[non_exhaustive]
-pub enum Hint
-{
- /// Specifies whether the OpenGL context should be created in debug mode, which may
- /// provide additional error and diagnostic reporting functionality.
- ///
- /// Valid values are [`HintValue::Bool`].
- OpenGLDebugContext = crate::ffi::GLFW_OPENGL_DEBUG_CONTEXT,
-
- /// Specifies the desired number of samples to use for multisampling. Zero disables
- /// multisampling.
- ///
- /// Valid values are: [`HintValue::Number`].
- Samples = crate::ffi::GLFW_SAMPLES,
-
- /// Specifies whether the framebuffer should be double buffered. You nearly always
- /// want to use double buffering. This is a hard constraint.
- ///
- /// Valid values are [`HintValue::Bool`].
- DoubleBuffer = crate::ffi::GLFW_DOUBLEBUFFER,
-}
-
-/// Window creation hint value.
-#[derive(Debug, Clone)]
-#[non_exhaustive]
-pub enum HintValue
-{
- Number(i32),
- Bool(bool),
-}
-
-/// Window size.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub struct Size
-{
- pub width: u32,
- pub height: u32,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
-#[repr(i32)]
-pub enum InputMode
-{
- /// When sticky keys mode is enabled, the pollable state of a key will remain
- /// [`KeyState::Pressed`] until the state of that key is polled with
- /// [`Window::get_key`]. Once it has been polled, if a key release event had been
- /// processed in the meantime, the state will reset to [`KeyState::Released`],
- /// otherwise it will remain [`KeyState::Pressed`].
- StickyKeys = crate::ffi::GLFW_STICKY_KEYS,
-
- /// When sticky mouse buttons mode is enabled, the pollable state of a mouse button
- /// will remain [`MouseButtonState::Pressed`] until the state of that button is
- /// polled with [`Window::get_mouse_button`]. Once it has been polled, if a mouse
- /// button release event had been processed in the meantime, the state will reset
- /// to [`MouseButtonState::Released`], otherwise it will remain
- /// [`MouseButton::Pressed`].
- StickyMouseButtons = crate::ffi::GLFW_STICKY_MOUSE_BUTTONS,
-
- LockKeyMods = crate::ffi::GLFW_LOCK_KEY_MODS,
-
- /// When the cursor is disabled, raw (unscaled and unaccelerated) mouse motion can be
- /// enabled if available.
- ///
- /// Raw mouse motion is closer to the actual motion of the mouse across a surface. It
- /// is not affected by the scaling and acceleration applied to the motion of the
- /// desktop cursor. That processing is suitable for a cursor while raw motion is
- /// better for controlling for example a 3D camera. Because of this, raw mouse motion
- /// is only provided when the cursor is disabled.
- RawMouseMotion = crate::ffi::GLFW_RAW_MOUSE_MOTION,
-}
-
-#[derive(Debug, Clone, Copy)]
-#[repr(i32)]
-pub enum CursorMode
-{
- /// Hides and grabs the cursor, providing virtual and unlimited cursor movement. This
- /// is useful for implementing for example 3D camera controls.
- Disabled = crate::ffi::GLFW_CURSOR_DISABLED,
-
- /// Makes the cursor invisible when it is over the content area of the window but
- /// does not restrict the cursor from leaving.
- Hidden = crate::ffi::GLFW_CURSOR_HIDDEN,
-
- /// Makes the cursor visible and behaving normally.
- Normal = crate::ffi::GLFW_CURSOR_NORMAL,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromRepr, VariantArr)]
-#[variant_arr(name = KEYS)]
-#[repr(i32)]
-pub enum Key
-{
- // Unknown = crate::ffi::GLFW_KEY_UNKNOWN,
- Space = crate::ffi::GLFW_KEY_SPACE,
- Apostrophe = crate::ffi::GLFW_KEY_APOSTROPHE,
- Comma = crate::ffi::GLFW_KEY_COMMA,
- Minus = crate::ffi::GLFW_KEY_MINUS,
- Period = crate::ffi::GLFW_KEY_PERIOD,
- Slash = crate::ffi::GLFW_KEY_SLASH,
- Digit0 = crate::ffi::GLFW_KEY_0,
- Digit1 = crate::ffi::GLFW_KEY_1,
- Digit2 = crate::ffi::GLFW_KEY_2,
- Digit3 = crate::ffi::GLFW_KEY_3,
- Digit4 = crate::ffi::GLFW_KEY_4,
- Digit5 = crate::ffi::GLFW_KEY_5,
- Digit6 = crate::ffi::GLFW_KEY_6,
- Digit7 = crate::ffi::GLFW_KEY_7,
- Digit8 = crate::ffi::GLFW_KEY_8,
- Digit9 = crate::ffi::GLFW_KEY_9,
- Semicolon = crate::ffi::GLFW_KEY_SEMICOLON,
- Equal = crate::ffi::GLFW_KEY_EQUAL,
- A = crate::ffi::GLFW_KEY_A,
- B = crate::ffi::GLFW_KEY_B,
- C = crate::ffi::GLFW_KEY_C,
- D = crate::ffi::GLFW_KEY_D,
- E = crate::ffi::GLFW_KEY_E,
- F = crate::ffi::GLFW_KEY_F,
- G = crate::ffi::GLFW_KEY_G,
- H = crate::ffi::GLFW_KEY_H,
- I = crate::ffi::GLFW_KEY_I,
- J = crate::ffi::GLFW_KEY_J,
- K = crate::ffi::GLFW_KEY_K,
- L = crate::ffi::GLFW_KEY_L,
- M = crate::ffi::GLFW_KEY_M,
- N = crate::ffi::GLFW_KEY_N,
- O = crate::ffi::GLFW_KEY_O,
- P = crate::ffi::GLFW_KEY_P,
- Q = crate::ffi::GLFW_KEY_Q,
- R = crate::ffi::GLFW_KEY_R,
- S = crate::ffi::GLFW_KEY_S,
- T = crate::ffi::GLFW_KEY_T,
- U = crate::ffi::GLFW_KEY_U,
- V = crate::ffi::GLFW_KEY_V,
- W = crate::ffi::GLFW_KEY_W,
- X = crate::ffi::GLFW_KEY_X,
- Y = crate::ffi::GLFW_KEY_Y,
- Z = crate::ffi::GLFW_KEY_Z,
- LeftBracket = crate::ffi::GLFW_KEY_LEFT_BRACKET,
- Backslash = crate::ffi::GLFW_KEY_BACKSLASH,
- RightBracket = crate::ffi::GLFW_KEY_RIGHT_BRACKET,
- GraveAccent = crate::ffi::GLFW_KEY_GRAVE_ACCENT,
- World1 = crate::ffi::GLFW_KEY_WORLD_1,
- World2 = crate::ffi::GLFW_KEY_WORLD_2,
- Escape = crate::ffi::GLFW_KEY_ESCAPE,
- Enter = crate::ffi::GLFW_KEY_ENTER,
- Tab = crate::ffi::GLFW_KEY_TAB,
- Backspace = crate::ffi::GLFW_KEY_BACKSPACE,
- Insert = crate::ffi::GLFW_KEY_INSERT,
- Delete = crate::ffi::GLFW_KEY_DELETE,
- Right = crate::ffi::GLFW_KEY_RIGHT,
- Left = crate::ffi::GLFW_KEY_LEFT,
- Down = crate::ffi::GLFW_KEY_DOWN,
- Up = crate::ffi::GLFW_KEY_UP,
- PageUp = crate::ffi::GLFW_KEY_PAGE_UP,
- PageDown = crate::ffi::GLFW_KEY_PAGE_DOWN,
- Home = crate::ffi::GLFW_KEY_HOME,
- End = crate::ffi::GLFW_KEY_END,
- CapsLock = crate::ffi::GLFW_KEY_CAPS_LOCK,
- ScrollLock = crate::ffi::GLFW_KEY_SCROLL_LOCK,
- NumLock = crate::ffi::GLFW_KEY_NUM_LOCK,
- PrintScreen = crate::ffi::GLFW_KEY_PRINT_SCREEN,
- Pause = crate::ffi::GLFW_KEY_PAUSE,
- F1 = crate::ffi::GLFW_KEY_F1,
- F2 = crate::ffi::GLFW_KEY_F2,
- F3 = crate::ffi::GLFW_KEY_F3,
- F4 = crate::ffi::GLFW_KEY_F4,
- F5 = crate::ffi::GLFW_KEY_F5,
- F6 = crate::ffi::GLFW_KEY_F6,
- F7 = crate::ffi::GLFW_KEY_F7,
- F8 = crate::ffi::GLFW_KEY_F8,
- F9 = crate::ffi::GLFW_KEY_F9,
- F10 = crate::ffi::GLFW_KEY_F10,
- F11 = crate::ffi::GLFW_KEY_F11,
- F12 = crate::ffi::GLFW_KEY_F12,
- F13 = crate::ffi::GLFW_KEY_F13,
- F14 = crate::ffi::GLFW_KEY_F14,
- F15 = crate::ffi::GLFW_KEY_F15,
- F16 = crate::ffi::GLFW_KEY_F16,
- F17 = crate::ffi::GLFW_KEY_F17,
- F18 = crate::ffi::GLFW_KEY_F18,
- F19 = crate::ffi::GLFW_KEY_F19,
- F20 = crate::ffi::GLFW_KEY_F20,
- F21 = crate::ffi::GLFW_KEY_F21,
- F22 = crate::ffi::GLFW_KEY_F22,
- F23 = crate::ffi::GLFW_KEY_F23,
- F24 = crate::ffi::GLFW_KEY_F24,
- F25 = crate::ffi::GLFW_KEY_F25,
- Kp0 = crate::ffi::GLFW_KEY_KP_0,
- Kp1 = crate::ffi::GLFW_KEY_KP_1,
- Kp2 = crate::ffi::GLFW_KEY_KP_2,
- Kp3 = crate::ffi::GLFW_KEY_KP_3,
- Kp4 = crate::ffi::GLFW_KEY_KP_4,
- Kp5 = crate::ffi::GLFW_KEY_KP_5,
- Kp6 = crate::ffi::GLFW_KEY_KP_6,
- Kp7 = crate::ffi::GLFW_KEY_KP_7,
- Kp8 = crate::ffi::GLFW_KEY_KP_8,
- Kp9 = crate::ffi::GLFW_KEY_KP_9,
- KpDecimal = crate::ffi::GLFW_KEY_KP_DECIMAL,
- KpDivide = crate::ffi::GLFW_KEY_KP_DIVIDE,
- KpMultiply = crate::ffi::GLFW_KEY_KP_MULTIPLY,
- KpSubtract = crate::ffi::GLFW_KEY_KP_SUBTRACT,
- KpAdd = crate::ffi::GLFW_KEY_KP_ADD,
- KpEnter = crate::ffi::GLFW_KEY_KP_ENTER,
- KpEqual = crate::ffi::GLFW_KEY_KP_EQUAL,
- LeftShift = crate::ffi::GLFW_KEY_LEFT_SHIFT,
- LeftControl = crate::ffi::GLFW_KEY_LEFT_CONTROL,
- LeftAlt = crate::ffi::GLFW_KEY_LEFT_ALT,
- LeftSuper = crate::ffi::GLFW_KEY_LEFT_SUPER,
- RightShift = crate::ffi::GLFW_KEY_RIGHT_SHIFT,
- RightControl = crate::ffi::GLFW_KEY_RIGHT_CONTROL,
- RightAlt = crate::ffi::GLFW_KEY_RIGHT_ALT,
- RightSuper = crate::ffi::GLFW_KEY_RIGHT_SUPER,
- Menu = crate::ffi::GLFW_KEY_MENU,
-}
-
-#[derive(Debug, Clone, Copy, FromRepr)]
-#[repr(i32)]
-pub enum KeyState
-{
- Pressed = crate::ffi::GLFW_PRESS,
- Released = crate::ffi::GLFW_RELEASE,
- Repeat = crate::ffi::GLFW_REPEAT,
-}
-
-bitflags! {
- #[derive(Debug, Clone, Copy)]
- pub struct KeyModifiers: i32 {
- const SHIFT = crate::ffi::GLFW_MOD_SHIFT;
- const CONTROL = crate::ffi::GLFW_MOD_CONTROL;
- const ALT = crate::ffi::GLFW_MOD_ALT;
- const SUPER = crate::ffi::GLFW_MOD_SUPER;
- const CAPS_LOCK = crate::ffi::GLFW_MOD_CAPS_LOCK;
- const NUM_LOCK = crate::ffi::GLFW_MOD_NUM_LOCK;
- }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, FromRepr)]
-#[repr(i32)]
-pub enum MouseButton
-{
- One = crate::ffi::GLFW_MOUSE_BUTTON_1,
- Two = crate::ffi::GLFW_MOUSE_BUTTON_2,
- Three = crate::ffi::GLFW_MOUSE_BUTTON_3,
- Four = crate::ffi::GLFW_MOUSE_BUTTON_4,
- Five = crate::ffi::GLFW_MOUSE_BUTTON_5,
- Six = crate::ffi::GLFW_MOUSE_BUTTON_6,
- Seven = crate::ffi::GLFW_MOUSE_BUTTON_7,
- Eight = crate::ffi::GLFW_MOUSE_BUTTON_8,
-}
-
-impl MouseButton
-{
- pub const LEFT: Self = Self::One;
- pub const MIDDLE: Self = Self::Three;
- pub const RIGHT: Self = Self::Two;
-}
-
-#[derive(Debug, Clone, Copy, FromRepr)]
-#[repr(i32)]
-pub enum MouseButtonState
-{
- Pressed = crate::ffi::GLFW_PRESS,
- Released = crate::ffi::GLFW_RELEASE,
-}
-
-#[derive(Debug, Clone)]
-pub struct CursorPosition
-{
- pub x: f64,
- pub y: f64,
-}
-
-type CloseCallback = Box<dyn Fn()>;
-type FramebufferSizeCb = Box<dyn Fn(Size)>;
-type KeyCallback = Box<dyn Fn(Key, i32, KeyState, KeyModifiers)>;
-type CursorPositionCallback = Box<dyn Fn(CursorPosition)>;
-type MouseButtonCallback = Box<dyn Fn(MouseButton, MouseButtonState, KeyModifiers)>;
-type FocusCallback = Box<dyn Fn(bool)>;
-
-thread_local! {
-static CLOSE_CALLBACK: RefCell<Option<CloseCallback>> = RefCell::new(None);
-static FRAMEBUFFER_SIZE_CB: RefCell<Option<FramebufferSizeCb>> = RefCell::new(None);
-static KEY_CALLBACK: RefCell<Option<KeyCallback>> = RefCell::new(None);
-static CURSOR_POS_CALLBACK: RefCell<Option<CursorPositionCallback>> = RefCell::new(None);
-static MOUSE_BUTTON_CALLBACK: RefCell<Option<MouseButtonCallback>> = RefCell::new(None);
-static FOCUS_CALLBACK: RefCell<Option<FocusCallback>> = RefCell::new(None);
-}
-
-extern "C-unwind" fn close_callback(_window: *mut crate::ffi::GLFWwindow)
-{
- CLOSE_CALLBACK
- .try_with(|close_cb| {
- if let Some(cb) = close_cb.borrow().as_deref() {
- cb();
- }
- })
- .ok();
-}
-
-extern "C-unwind" fn framebuffer_size_callback(
- _window: *mut crate::ffi::GLFWwindow,
- c_width: c_int,
- c_height: c_int,
-)
-{
- // Width and height can't possibly have their sign bit set
- let width = u32::from_le_bytes(c_width.to_le_bytes());
- let height = u32::from_le_bytes(c_height.to_le_bytes());
-
- FRAMEBUFFER_SIZE_CB
- .try_with(|framebuffer_size_cb| {
- if let Some(cb) = framebuffer_size_cb.borrow().as_deref() {
- cb(Size { width, height });
- }
- })
- .ok();
-}
-
-extern "C-unwind" fn key_callback(
- _window: *mut crate::ffi::GLFWwindow,
- key_raw: c_int,
- scancode: c_int,
- action_raw: c_int,
- mods: c_int,
-)
-{
- let Some(key) = Key::from_repr(key_raw) else {
- write!(stdout(), "Unknown key {key_raw}").ok();
- return;
- };
-
- let Some(key_state) = KeyState::from_repr(action_raw) else {
- write!(stdout(), "Unknown key state {action_raw}").ok();
- return;
- };
-
- let Some(key_modifiers) = KeyModifiers::from_bits(mods) else {
- write!(
- stdout(),
- "Key modifiers {action_raw} contain one or more unknown bit(s)"
- )
- .ok();
-
- return;
- };
-
- KEY_CALLBACK
- .try_with(|key_callback| {
- if let Some(cb) = key_callback.borrow().as_deref() {
- cb(key, scancode, key_state, key_modifiers);
- }
- })
- .ok();
-}
-
-extern "C-unwind" fn cursor_pos_callback(
- _window: *mut crate::ffi::GLFWwindow,
- x_pos: c_double,
- y_pos: c_double,
-)
-{
- CURSOR_POS_CALLBACK
- .try_with(|cursor_pos_callback| {
- if let Some(cb) = cursor_pos_callback.borrow().as_deref() {
- cb(CursorPosition { x: x_pos, y: y_pos });
- }
- })
- .ok();
-}
-
-extern "C-unwind" fn mouse_button_callback(
- _window: *mut crate::ffi::GLFWwindow,
- mouse_button_raw: c_int,
- mouse_action: c_int,
- modifier_keys: c_int,
-)
-{
- let Some(mouse_button) = MouseButton::from_repr(mouse_button_raw) else {
- write!(stdout(), "Unknown mouse button {mouse_button_raw}").ok();
- return;
- };
-
- let Some(mouse_button_state) = MouseButtonState::from_repr(mouse_action) else {
- write!(stdout(), "Unknown mouse action {mouse_action}").ok();
- return;
- };
-
- let Some(key_modifiers) = KeyModifiers::from_bits(modifier_keys) else {
- write!(
- stdout(),
- "Key modifiers {modifier_keys:#b} contain one or more unknown bit(s)"
- )
- .ok();
-
- return;
- };
-
- MOUSE_BUTTON_CALLBACK
- .try_with(|mouse_button_callback| {
- if let Some(cb) = mouse_button_callback.borrow().as_deref() {
- cb(mouse_button, mouse_button_state, key_modifiers);
- }
- })
- .ok();
-}
-
-extern "C-unwind" fn focus_callback(_window: *mut crate::ffi::GLFWwindow, focused: c_int)
-{
- let is_focused = if focused == crate::ffi::GLFW_TRUE {
- true
- } else if focused == crate::ffi::GLFW_FALSE {
- false
- } else {
- write!(stdout(), "Invalid focus state {focused}").ok();
- return;
- };
-
- FOCUS_CALLBACK
- .try_with(|focus_callback| {
- if let Some(cb) = focus_callback.borrow().as_deref() {
- cb(is_focused);
- }
- })
- .ok();
-}
diff --git a/opengl-bindings/Cargo.toml b/opengl-bindings/Cargo.toml
new file mode 100644
index 0000000..ca8a400
--- /dev/null
+++ b/opengl-bindings/Cargo.toml
@@ -0,0 +1,81 @@
+[package]
+name = "opengl-bindings"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+glutin = "0.32.3"
+thiserror = "1.0.49"
+safer-ffi = "0.1.13"
+bitflags = "2.4.0"
+util-macros = { workspace = true }
+
+[build-dependencies]
+gl_generator = "=0.14.0"
+toml = "0.8.12"
+anyhow = "1.0.100"
+
+[package.metadata.build]
+gl_commands = [
+ "CreateBuffers",
+ "NamedBufferData",
+ "NamedBufferSubData",
+ "CreateVertexArrays",
+ "DrawArrays",
+ "DrawElementsBaseVertex",
+ "VertexArrayElementBuffer",
+ "VertexArrayVertexBuffer",
+ "EnableVertexArrayAttrib",
+ "DisableVertexArrayAttrib",
+ "VertexArrayAttribFormat",
+ "VertexArrayAttribBinding",
+ "BindVertexArray",
+ "TextureStorage2D",
+ "TextureSubImage2D",
+ "TextureSubImage3D",
+ "DeleteTextures",
+ "GenerateTextureMipmap",
+ "TextureParameteri",
+ "CreateTextures",
+ "BindTextureUnit",
+ "DeleteShader",
+ "CreateShader",
+ "ShaderSource",
+ "CompileShader",
+ "GetShaderiv",
+ "GetShaderInfoLog",
+ "LinkProgram",
+ "GetProgramiv",
+ "CreateProgram",
+ "AttachShader",
+ "UseProgram",
+ "GetUniformLocation",
+ "ProgramUniform1f",
+ "ProgramUniform1i",
+ "ProgramUniform3f",
+ "ProgramUniformMatrix4fv",
+ "GetProgramInfoLog",
+ "DeleteProgram",
+ "Viewport",
+ "Clear",
+ "PolygonMode",
+ "Enable",
+ "Disable",
+ "GetIntegerv",
+ "DebugMessageCallback",
+ "DebugMessageControl",
+ "DeleteVertexArrays",
+ "DeleteBuffers",
+ "BindBufferBase",
+ "BlendFunc",
+ "BlendEquation",
+ "Scissor",
+ "DepthFunc",
+ "PixelStorei",
+ "CreateFramebuffers",
+ "DeleteFramebuffers",
+ "NamedFramebufferTexture",
+ "BindFramebuffer",
+ "NamedFramebufferDrawBuffer",
+ "NamedFramebufferReadBuffer",
+]
diff --git a/opengl-bindings/build.rs b/opengl-bindings/build.rs
new file mode 100644
index 0000000..060472c
--- /dev/null
+++ b/opengl-bindings/build.rs
@@ -0,0 +1,107 @@
+use std::collections::HashSet;
+use std::env;
+use std::fs::File;
+use std::path::{Path, PathBuf};
+
+use anyhow::anyhow;
+use gl_generator::{Api, Fallbacks, Profile, Registry, StructGenerator};
+
+fn main() -> Result<(), anyhow::Error>
+{
+ println!("cargo::rerun-if-changed=build.rs");
+ println!("cargo::rerun-if-changed=Cargo.toml");
+
+ let dest = env::var("OUT_DIR")?;
+
+ let mut file = File::create(Path::new(&dest).join("bindings.rs"))?;
+
+ let mut registry = Registry::new(Api::Gl, (4, 6), Profile::Core, Fallbacks::All, []);
+
+ let mut build_metadata = get_build_metadata()?;
+
+ filter_gl_commands(&mut registry, &mut build_metadata)?;
+
+ registry.write_bindings(StructGenerator, &mut file)?;
+
+ Ok(())
+}
+
+fn filter_gl_commands(
+ registry: &mut Registry,
+ build_metadata: &mut BuildMetadata,
+) -> Result<(), anyhow::Error>
+{
+ registry
+ .cmds
+ .retain(|command| build_metadata.gl_commands.remove(&command.proto.ident));
+
+ if !build_metadata.gl_commands.is_empty() {
+ return Err(anyhow!(
+ "Invalid GL commands: [{}]",
+ build_metadata
+ .gl_commands
+ .iter()
+ .cloned()
+ .collect::<Vec<_>>()
+ .join(", ")
+ ));
+ }
+
+ Ok(())
+}
+
+fn get_build_metadata() -> Result<BuildMetadata, anyhow::Error>
+{
+ let manifest_path = PathBuf::from(std::env::var("CARGO_MANIFEST_PATH")?);
+
+ let manifest = std::fs::read_to_string(manifest_path)?.parse::<toml::Table>()?;
+
+ let package = match manifest
+ .get("package")
+ .ok_or_else(|| anyhow!("Manifest does not have a package table"))?
+ {
+ toml::Value::Table(package) => Ok(package),
+ _ => Err(anyhow!("Manifest package must be a table")),
+ }?;
+
+ let metadata = match package
+ .get("metadata")
+ .ok_or_else(|| anyhow!("Manifest does not have a package.metadata table"))?
+ {
+ toml::Value::Table(metadata) => Ok(metadata),
+ _ => Err(anyhow!("Manifest package.metadata must be a table")),
+ }?;
+
+ let build_metadata = match metadata
+ .get("build")
+ .ok_or_else(|| anyhow!("Manifest does not have a package.metadata.build table"))?
+ {
+ toml::Value::Table(build_metadata) => Ok(build_metadata),
+ _ => Err(anyhow!("Manifest package.metadata.build must be a table")),
+ }?;
+
+ let gl_command_values = match build_metadata.get("gl_commands").ok_or_else(|| {
+ anyhow!("Manifest does not have a package.metadata.build.gl_commands array")
+ })? {
+ toml::Value::Array(gl_commands) => Ok(gl_commands),
+ _ => Err(anyhow!(
+ "Manifest package.metadata.build.gl_commands must be a array"
+ )),
+ }?;
+
+ let gl_commands = gl_command_values
+ .iter()
+ .map(|gl_command_val| match gl_command_val {
+ toml::Value::String(gl_command) => Ok(gl_command.clone()),
+ _ => Err(anyhow!("GL command must be a string")),
+ })
+ .collect::<Result<HashSet<_>, _>>()?;
+
+ Ok(BuildMetadata { gl_commands })
+}
+
+#[derive(Debug)]
+struct BuildMetadata
+{
+ gl_commands: HashSet<String>,
+}
diff --git a/opengl-bindings/src/blending.rs b/opengl-bindings/src/blending.rs
new file mode 100644
index 0000000..a855c2a
--- /dev/null
+++ b/opengl-bindings/src/blending.rs
@@ -0,0 +1,180 @@
+use crate::MaybeCurrentContextWithFns;
+
+pub fn configure(
+ current_context: &MaybeCurrentContextWithFns,
+ configuration: Configuration,
+)
+{
+ unsafe {
+ current_context.fns().BlendFunc(
+ configuration.source_factor.into_gl(),
+ configuration.destination_factor.into_gl(),
+ );
+
+ current_context
+ .fns()
+ .BlendEquation(configuration.equation.into_gl());
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Configuration
+{
+ source_factor: Factor,
+ destination_factor: Factor,
+ equation: Equation,
+}
+
+impl Configuration
+{
+ pub fn with_source_factor(mut self, source_factor: Factor) -> Self
+ {
+ self.source_factor = source_factor;
+ self
+ }
+
+ pub fn with_destination_factor(mut self, destination_factor: Factor) -> Self
+ {
+ self.destination_factor = destination_factor;
+ self
+ }
+
+ pub fn with_equation(mut self, equation: Equation) -> Self
+ {
+ self.equation = equation;
+ self
+ }
+}
+
+impl Default for Configuration
+{
+ fn default() -> Self
+ {
+ Self {
+ source_factor: Factor::One,
+ destination_factor: Factor::Zero,
+ equation: Equation::default(),
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum Factor
+{
+ /// Factor will be the RGBA color `(0,0,0,0)`
+ Zero,
+
+ /// Factor will be the RGBA color `(1,1,1,1)`
+ One,
+
+ /// Factor will be the source color
+ SrcColor,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - source color`
+ OneMinusSrcColor,
+
+ /// Factor will be the destination color
+ DstColor,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - destination color`
+ OneMinusDstColor,
+
+ /// Factor will be the alpha component of the source color.
+ SrcAlpha,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - source color alpha`
+ OneMinusSrcAlpha,
+
+ /// Factor will be the alpha component of the destination color.
+ DstAlpha,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - destination color alpha`
+ OneMinusDstAlpha,
+
+ /// Factor will be the constant color
+ ConstantColor,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - constant color`
+ OneMinusConstantColor,
+
+ /// Factor will be the alpha component of the constant color.
+ ConstantAlpha,
+
+ /// Factor will be the RGBA color `(1,1,1,1) - constant color alpha`
+ OneMinusConstantAlpha,
+ // /// (i,i,i,1)
+ // SrcAlphaSaturate,
+ //
+ // /// (Rs1kR,Gs1kG,Bs1kB,As1kA)
+ // Src1Color,
+ //
+ // /// (1,1,1,1)−(Rs1kR,Gs1kG,Bs1kB,As1kA)
+ // OneMinusSrc1Color,
+ //
+ // /// (As1kA,As1kA,As1kA,As1kA)
+ // Src1Alpha,
+ //
+ // /// (1,1,1,1)−(As1kA,As1kA,As1kA,As1kA)
+ // OneMinusSrc1Alpha,
+}
+
+impl Factor
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Zero => crate::sys::ZERO,
+ Self::One => crate::sys::ONE,
+ Self::SrcColor => crate::sys::SRC_COLOR,
+ Self::OneMinusSrcColor => crate::sys::ONE_MINUS_SRC_COLOR,
+ Self::DstColor => crate::sys::DST_COLOR,
+ Self::OneMinusDstColor => crate::sys::ONE_MINUS_DST_COLOR,
+ Self::SrcAlpha => crate::sys::SRC_ALPHA,
+ Self::OneMinusSrcAlpha => crate::sys::ONE_MINUS_SRC_ALPHA,
+ Self::DstAlpha => crate::sys::DST_ALPHA,
+ Self::OneMinusDstAlpha => crate::sys::ONE_MINUS_DST_ALPHA,
+ Self::ConstantColor => crate::sys::CONSTANT_COLOR,
+ Self::OneMinusConstantColor => crate::sys::ONE_MINUS_CONSTANT_COLOR,
+ Self::ConstantAlpha => crate::sys::CONSTANT_ALPHA,
+ Self::OneMinusConstantAlpha => crate::sys::ONE_MINUS_CONSTANT_ALPHA,
+ }
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+pub enum Equation
+{
+ /// The destination color and source color is added to each other in the blend
+ /// function
+ #[default]
+ Add,
+
+ /// The destination color is subtracted from the source color in the blend function
+ Subtract,
+
+ /// The source color is subtracted from the destination color in the blend function
+ ReverseSubtract,
+
+ /// The blend function will take the component-wise minimum of the destination color
+ /// and the source color
+ Min,
+
+ /// The blend function will take the component-wise maximum of the destination color
+ /// and the source color
+ Max,
+}
+
+impl Equation
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Add => crate::sys::FUNC_ADD,
+ Self::Subtract => crate::sys::FUNC_SUBTRACT,
+ Self::ReverseSubtract => crate::sys::FUNC_REVERSE_SUBTRACT,
+ Self::Min => crate::sys::MIN,
+ Self::Max => crate::sys::MAX,
+ }
+ }
+}
diff --git a/opengl-bindings/src/buffer.rs b/opengl-bindings/src/buffer.rs
new file mode 100644
index 0000000..2567aba
--- /dev/null
+++ b/opengl-bindings/src/buffer.rs
@@ -0,0 +1,270 @@
+use std::marker::PhantomData;
+use std::mem::size_of_val;
+use std::ptr::null;
+
+use safer_ffi::layout::ReprC;
+
+use crate::MaybeCurrentContextWithFns;
+
+#[derive(Debug, Clone)]
+pub struct Buffer<Item: ReprC>
+{
+ buf: crate::sys::types::GLuint,
+ _pd: PhantomData<Item>,
+}
+
+impl<Item: ReprC> Buffer<Item>
+{
+ #[must_use]
+ pub fn new(current_context: &MaybeCurrentContextWithFns) -> Self
+ {
+ let mut buffer = crate::sys::types::GLuint::default();
+
+ unsafe {
+ current_context.fns().CreateBuffers(1, &raw mut buffer);
+ };
+
+ Self { buf: buffer, _pd: PhantomData }
+ }
+
+ /// Initializes this buffer with a size and a usage.
+ ///
+ /// # Errors
+ /// Returns `Err` if the size (in bytes) is too large.
+ pub fn init(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ size: usize,
+ usage: Usage,
+ ) -> Result<(), Error>
+ {
+ let size: crate::sys::types::GLsizeiptr =
+ size.try_into().map_err(|_| Error::SizeTooLarge {
+ size,
+ max_size: crate::sys::types::GLsizeiptr::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().NamedBufferData(
+ self.buf,
+ size,
+ null(),
+ usage.into_gl(),
+ );
+ }
+
+ Ok(())
+ }
+
+ /// Stores items in this buffer.
+ ///
+ /// # Errors
+ /// Returns `Err` if the total size (in bytes) is too large.
+ pub fn store(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ items: &[Item],
+ usage: Usage,
+ ) -> Result<(), Error>
+ {
+ let total_size = size_of_val(items);
+
+ let total_size: crate::sys::types::GLsizeiptr =
+ total_size.try_into().map_err(|_| Error::SizeTooLarge {
+ size: total_size,
+ max_size: crate::sys::types::GLsizeiptr::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().NamedBufferData(
+ self.buf,
+ total_size,
+ items.as_ptr().cast(),
+ usage.into_gl(),
+ );
+ }
+
+ Ok(())
+ }
+
+ /// Stores items in this buffer, starting at a byte offset.
+ ///
+ /// # Errors
+ /// Returns `Err` if the total size (in bytes) is too large.
+ pub fn store_at_byte_offset(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ byte_offset: usize,
+ items: &[Item],
+ // usage: Usage,
+ ) -> Result<(), Error>
+ {
+ let total_size = size_of_val(items);
+
+ let byte_offset: crate::sys::types::GLintptr =
+ byte_offset
+ .try_into()
+ .map_err(|_| Error::ByteOffsetTooLarge {
+ byte_offset,
+ max_byte_offset: crate::sys::types::GLintptr::MAX as usize,
+ })?;
+
+ let total_size: crate::sys::types::GLsizeiptr =
+ total_size.try_into().map_err(|_| Error::SizeTooLarge {
+ size: total_size,
+ max_size: crate::sys::types::GLsizeiptr::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().NamedBufferSubData(
+ self.buf,
+ byte_offset,
+ total_size,
+ items.as_ptr().cast(),
+ // usage.into_gl(),
+ );
+ }
+
+ Ok(())
+ }
+
+ /// Maps the values in the `values` slice into `Item`s which is stored into this
+ /// buffer.
+ ///
+ /// # Errors
+ /// Returns `Err` if the total size (in bytes) is too large.
+ pub fn store_mapped<Value>(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ values: &[Value],
+ usage: Usage,
+ mut map_func: impl FnMut(&Value) -> Item,
+ ) -> Result<(), Error>
+ {
+ let item_size: crate::sys::types::GLsizeiptr = const {
+ assert!(size_of::<Item>() <= crate::sys::types::GLsizeiptr::MAX as usize);
+
+ size_of::<Item>().cast_signed()
+ };
+
+ let total_size = size_of::<Item>() * values.len();
+
+ let total_size: crate::sys::types::GLsizeiptr =
+ total_size.try_into().map_err(|_| Error::SizeTooLarge {
+ size: total_size,
+ max_size: crate::sys::types::GLsizeiptr::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().NamedBufferData(
+ self.buf,
+ total_size,
+ null(),
+ usage.into_gl(),
+ );
+ }
+
+ for (index, value) in values.iter().enumerate() {
+ let item = map_func(value);
+
+ let offset = index * size_of::<Item>();
+
+ let Ok(offset_casted) = crate::sys::types::GLintptr::try_from(offset) else {
+ unreachable!(); // Reason: The total size can be casted to a GLintptr
+ // (done above) so offsets should be castable as well
+ };
+
+ unsafe {
+ current_context.fns().NamedBufferSubData(
+ self.buf,
+ offset_casted,
+ item_size,
+ (&raw const item).cast(),
+ );
+ }
+ }
+
+ Ok(())
+ }
+
+ pub fn delete(&self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context.fns().DeleteBuffers(1, &raw const self.buf);
+ }
+ }
+
+ pub fn bind_to_indexed_target(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ target: BindingTarget,
+ index: u32,
+ )
+ {
+ unsafe {
+ current_context.fns().BindBufferBase(
+ target as crate::sys::types::GLenum,
+ index,
+ self.buf,
+ );
+ }
+ }
+
+ pub(crate) fn object(&self) -> crate::sys::types::GLuint
+ {
+ self.buf
+ }
+}
+
+/// Buffer usage.
+#[derive(Debug, Clone, Copy)]
+pub enum Usage
+{
+ /// The buffer data is set only once and used by the GPU at most a few times.
+ Stream,
+
+ /// The buffer data is set only once and used many times.
+ Static,
+
+ /// The buffer data is changed a lot and used many times.
+ Dynamic,
+}
+
+impl Usage
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Stream => crate::sys::STREAM_DRAW,
+ Self::Static => crate::sys::STATIC_DRAW,
+ Self::Dynamic => crate::sys::DYNAMIC_DRAW,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u32)]
+pub enum BindingTarget
+{
+ AtomicCounterBuffer = crate::sys::ATOMIC_COUNTER_BUFFER,
+ TransformFeedbackBuffer = crate::sys::TRANSFORM_FEEDBACK_BUFFER,
+ UniformBuffer = crate::sys::UNIFORM_BUFFER,
+ ShaderStorageBuffer = crate::sys::SHADER_STORAGE_BUFFER,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("Size ({size}) is too large. Must be < {max_size}")]
+ SizeTooLarge
+ {
+ size: usize, max_size: usize
+ },
+
+ #[error("Byte offset ({byte_offset}) is too large. Must be < {max_byte_offset}")]
+ ByteOffsetTooLarge
+ {
+ byte_offset: usize,
+ max_byte_offset: usize,
+ },
+}
diff --git a/opengl-bindings/src/data_types.rs b/opengl-bindings/src/data_types.rs
new file mode 100644
index 0000000..a6c65fc
--- /dev/null
+++ b/opengl-bindings/src/data_types.rs
@@ -0,0 +1,47 @@
+use std::fmt::Display;
+
+use safer_ffi::derive_ReprC;
+use safer_ffi::layout::ReprC;
+
+#[derive(Debug, Clone)]
+#[derive_ReprC]
+#[repr(C)]
+pub struct Matrix<Value: ReprC, const ROWS: usize, const COLUMNS: usize>
+{
+ /// Items must be layed out this way for it to work with OpenGL shaders.
+ pub items: [[Value; ROWS]; COLUMNS],
+}
+
+#[derive(Debug, Clone)]
+#[derive_ReprC]
+#[repr(C)]
+pub struct Vec3<Value: ReprC>
+{
+ pub x: Value,
+ pub y: Value,
+ pub z: Value,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[derive_ReprC]
+#[repr(C)]
+pub struct Vec2<Value>
+{
+ pub x: Value,
+ pub y: Value,
+}
+
+#[derive(Debug, Clone, Copy, Default)]
+pub struct Dimens<Value>
+{
+ pub width: Value,
+ pub height: Value,
+}
+
+impl<Value: Display> Display for Dimens<Value>
+{
+ fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
+ {
+ write!(formatter, "{}x{}", self.width, self.height)
+ }
+}
diff --git a/opengl-bindings/src/debug.rs b/opengl-bindings/src/debug.rs
new file mode 100644
index 0000000..167173f
--- /dev/null
+++ b/opengl-bindings/src/debug.rs
@@ -0,0 +1,161 @@
+use std::ffi::c_void;
+use std::io::{stderr, Write};
+use std::mem::transmute;
+use std::panic::catch_unwind;
+
+use util_macros::FromRepr;
+
+use crate::MaybeCurrentContextWithFns;
+
+pub fn set_debug_message_callback(
+ current_context: &MaybeCurrentContextWithFns,
+ cb: MessageCallback,
+)
+{
+ unsafe {
+ current_context
+ .fns()
+ .DebugMessageCallback(Some(debug_message_cb), cb as *mut c_void);
+ }
+}
+
+/// Sets debug message parameters.
+///
+/// # Errors
+/// Returns `Err` if `ids` contains too many ids.
+pub fn set_debug_message_control(
+ current_context: &MaybeCurrentContextWithFns,
+ source: Option<MessageSource>,
+ ty: Option<MessageType>,
+ severity: Option<MessageSeverity>,
+ ids: &[u32],
+ ids_action: MessageIdsAction,
+) -> Result<(), SetDebugMessageControlError>
+{
+ let ids_len: crate::sys::types::GLsizei =
+ ids.len()
+ .try_into()
+ .map_err(|_| SetDebugMessageControlError::TooManyIds {
+ id_cnt: ids.len(),
+ max_id_cnt: crate::sys::types::GLsizei::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().DebugMessageControl(
+ source.map_or(crate::sys::DONT_CARE, |source| source as u32),
+ ty.map_or(crate::sys::DONT_CARE, |ty| ty as u32),
+ severity.map_or(crate::sys::DONT_CARE, |severity| severity as u32),
+ ids_len,
+ ids.as_ptr(),
+ ids_action as u8,
+ );
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum SetDebugMessageControlError
+{
+ #[error("Too many ids provided ({id_cnt}). Must be < {max_id_cnt}")]
+ TooManyIds
+ {
+ id_cnt: usize, max_id_cnt: usize
+ },
+}
+
+pub type MessageCallback = fn(
+ source: MessageSource,
+ ty: MessageType,
+ id: u32,
+ severity: MessageSeverity,
+ message: &str,
+);
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u8)] // GLboolean = u8
+pub enum MessageIdsAction
+{
+ Enable = crate::sys::TRUE,
+ Disable = crate::sys::FALSE,
+}
+
+#[derive(Debug, Clone, Copy, FromRepr)]
+#[repr(u32)] // GLenum = u32
+pub enum MessageSource
+{
+ Api = crate::sys::DEBUG_SOURCE_API,
+ WindowSystem = crate::sys::DEBUG_SOURCE_WINDOW_SYSTEM,
+ ShaderCompiler = crate::sys::DEBUG_SOURCE_SHADER_COMPILER,
+ ThirdParty = crate::sys::DEBUG_SOURCE_THIRD_PARTY,
+ Application = crate::sys::DEBUG_SOURCE_APPLICATION,
+ Other = crate::sys::DEBUG_SOURCE_OTHER,
+}
+
+#[derive(Debug, Clone, Copy, FromRepr)]
+#[repr(u32)] // GLenum = u32
+pub enum MessageType
+{
+ DeprecatedBehavior = crate::sys::DEBUG_TYPE_DEPRECATED_BEHAVIOR,
+ Error = crate::sys::DEBUG_TYPE_ERROR,
+ Marker = crate::sys::DEBUG_TYPE_MARKER,
+ Other = crate::sys::DEBUG_TYPE_OTHER,
+ Performance = crate::sys::DEBUG_TYPE_PERFORMANCE,
+ PopGroup = crate::sys::DEBUG_TYPE_POP_GROUP,
+ PushGroup = crate::sys::DEBUG_TYPE_PUSH_GROUP,
+ Portability = crate::sys::DEBUG_TYPE_PORTABILITY,
+ UndefinedBehavior = crate::sys::DEBUG_TYPE_UNDEFINED_BEHAVIOR,
+}
+
+#[derive(Debug, Clone, Copy, FromRepr)]
+#[repr(u32)] // GLenum = u32
+pub enum MessageSeverity
+{
+ High = crate::sys::DEBUG_SEVERITY_HIGH,
+ Medium = crate::sys::DEBUG_SEVERITY_MEDIUM,
+ Low = crate::sys::DEBUG_SEVERITY_LOW,
+ Notification = crate::sys::DEBUG_SEVERITY_NOTIFICATION,
+}
+
+extern "system" fn debug_message_cb(
+ source: crate::sys::types::GLenum,
+ ty: crate::sys::types::GLenum,
+ id: crate::sys::types::GLuint,
+ severity: crate::sys::types::GLenum,
+ message_length: crate::sys::types::GLsizei,
+ message: *const crate::sys::types::GLchar,
+ user_cb: *mut c_void,
+)
+{
+ let user_cb = unsafe { transmute::<*mut c_void, MessageCallback>(user_cb) };
+
+ let Ok(msg_length) = usize::try_from(message_length) else {
+ return;
+ };
+
+ // Unwinds are catched because unwinding from Rust code into foreign code is UB.
+ let res = catch_unwind(|| {
+ let msg_source = MessageSource::from_repr(source).unwrap();
+ let msg_type = MessageType::from_repr(ty).unwrap();
+ let msg_severity = MessageSeverity::from_repr(severity).unwrap();
+
+ // SAFETY: The received message should be a valid ASCII string
+ let message = unsafe {
+ std::str::from_utf8_unchecked(std::slice::from_raw_parts(
+ message.cast(),
+ msg_length,
+ ))
+ };
+
+ user_cb(msg_source, msg_type, id, msg_severity, message);
+ });
+
+ if res.is_err() {
+ // eprintln is not used since it can panic and unwinds are unwanted because
+ // unwinding from Rust code into foreign code is UB.
+ stderr()
+ .write_all(b"ERROR: Panic in debug message callback")
+ .ok();
+ println!();
+ }
+}
diff --git a/opengl-bindings/src/framebuffer.rs b/opengl-bindings/src/framebuffer.rs
new file mode 100644
index 0000000..6d16602
--- /dev/null
+++ b/opengl-bindings/src/framebuffer.rs
@@ -0,0 +1,206 @@
+use crate::texture::Texture;
+use crate::MaybeCurrentContextWithFns;
+
+#[derive(Debug, Clone)]
+pub struct Framebuffer
+{
+ inner: crate::sys::types::GLuint,
+}
+
+impl Framebuffer
+{
+ #[must_use]
+ pub fn new(current_context: &MaybeCurrentContextWithFns) -> Self
+ {
+ let mut inner = 0;
+
+ unsafe {
+ current_context.fns().CreateFramebuffers(1, &raw mut inner);
+ }
+
+ Self { inner }
+ }
+
+ /// Attaches a texture object as a logical buffer of this framebuffer object.
+ pub fn attach_texture(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ attachment: Attachment,
+ texture: Texture,
+ texture_mipmap_level: u16,
+ )
+ {
+ unsafe {
+ current_context.fns().NamedFramebufferTexture(
+ self.inner,
+ attachment.into_gl(),
+ texture.into_raw(),
+ texture_mipmap_level.into(),
+ );
+ }
+ }
+
+ /// Detaches the texture object at the specified attachment point.
+ pub fn detach_texture(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ attachment: Attachment,
+ )
+ {
+ unsafe {
+ current_context.fns().NamedFramebufferTexture(
+ self.inner,
+ attachment.into_gl(),
+ 0,
+ 0,
+ );
+ }
+ }
+
+ /// Specifies which color buffer is to be drawn into when colors are written to this
+ /// framebuffer object.
+ ///
+ /// If `buffer` is `None`, no color buffer will be written into.
+ pub fn set_draw_buffer(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ buffer: Option<ColorAttachment>,
+ )
+ {
+ unsafe {
+ current_context.fns().NamedFramebufferDrawBuffer(
+ self.inner,
+ buffer
+ .map(ColorAttachment::into_gl)
+ .unwrap_or(crate::sys::NONE),
+ );
+ }
+ }
+
+ pub fn delete(self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context
+ .fns()
+ .DeleteFramebuffers(1, &raw const self.inner);
+ }
+ }
+
+ pub fn from_raw(raw: u32) -> Self
+ {
+ Self { inner: raw }
+ }
+
+ pub fn into_raw(self) -> u32
+ {
+ self.inner
+ }
+}
+
+/// Binds a framebuffer to a framebuffer target.
+///
+/// If `framebuffer` is `None`, breaks the existing binding of a framebuffer object to
+/// the framebuffer target `target`.
+pub fn bind(
+ current_context: &MaybeCurrentContextWithFns,
+ target: Target,
+ framebuffer: Option<&Framebuffer>,
+)
+{
+ unsafe {
+ current_context.fns().BindFramebuffer(
+ target.into_gl(),
+ framebuffer
+ .map(|framebuffer| framebuffer.inner)
+ .unwrap_or(0),
+ );
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Target
+{
+ Draw,
+ Read,
+ DrawAndRead,
+}
+
+impl Target
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Draw => crate::sys::DRAW_FRAMEBUFFER,
+ Self::Read => crate::sys::READ_FRAMEBUFFER,
+ Self::DrawAndRead => crate::sys::FRAMEBUFFER,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[non_exhaustive]
+pub enum Attachment
+{
+ Color(ColorAttachment),
+ Depth,
+ Stencil,
+ DepthStencil,
+}
+
+impl Attachment
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Color(color_attachment) => color_attachment.into_gl(),
+ Self::Depth => crate::sys::DEPTH_ATTACHMENT,
+ Self::Stencil => crate::sys::STENCIL_ATTACHMENT,
+ Self::DepthStencil => crate::sys::DEPTH_STENCIL_ATTACHMENT,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct ColorAttachment(pub u8);
+
+impl ColorAttachment
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self.0 {
+ 0 => crate::sys::COLOR_ATTACHMENT0,
+ 1 => crate::sys::COLOR_ATTACHMENT1,
+ 10 => crate::sys::COLOR_ATTACHMENT10,
+ 11 => crate::sys::COLOR_ATTACHMENT11,
+ 12 => crate::sys::COLOR_ATTACHMENT12,
+ 13 => crate::sys::COLOR_ATTACHMENT13,
+ 14 => crate::sys::COLOR_ATTACHMENT14,
+ 15 => crate::sys::COLOR_ATTACHMENT15,
+ 16 => crate::sys::COLOR_ATTACHMENT16,
+ 17 => crate::sys::COLOR_ATTACHMENT17,
+ 18 => crate::sys::COLOR_ATTACHMENT18,
+ 19 => crate::sys::COLOR_ATTACHMENT19,
+ 2 => crate::sys::COLOR_ATTACHMENT2,
+ 20 => crate::sys::COLOR_ATTACHMENT20,
+ 21 => crate::sys::COLOR_ATTACHMENT21,
+ 22 => crate::sys::COLOR_ATTACHMENT22,
+ 23 => crate::sys::COLOR_ATTACHMENT23,
+ 24 => crate::sys::COLOR_ATTACHMENT24,
+ 25 => crate::sys::COLOR_ATTACHMENT25,
+ 26 => crate::sys::COLOR_ATTACHMENT26,
+ 27 => crate::sys::COLOR_ATTACHMENT27,
+ 28 => crate::sys::COLOR_ATTACHMENT28,
+ 29 => crate::sys::COLOR_ATTACHMENT29,
+ 3 => crate::sys::COLOR_ATTACHMENT3,
+ 30 => crate::sys::COLOR_ATTACHMENT30,
+ 31 => crate::sys::COLOR_ATTACHMENT31,
+ 4 => crate::sys::COLOR_ATTACHMENT4,
+ 5 => crate::sys::COLOR_ATTACHMENT5,
+ 6 => crate::sys::COLOR_ATTACHMENT6,
+ 7 => crate::sys::COLOR_ATTACHMENT7,
+ 8 => crate::sys::COLOR_ATTACHMENT8,
+ 9 => crate::sys::COLOR_ATTACHMENT9,
+ _ => panic!("Invalid color attachment index. Must be < 32"),
+ }
+ }
+}
diff --git a/opengl-bindings/src/lib.rs b/opengl-bindings/src/lib.rs
new file mode 100644
index 0000000..3733f44
--- /dev/null
+++ b/opengl-bindings/src/lib.rs
@@ -0,0 +1,143 @@
+#![deny(clippy::all, clippy::pedantic)]
+use std::ffi::CString;
+use std::process::abort;
+
+use glutin::context::{NotCurrentContext, PossiblyCurrentContext};
+use glutin::display::GetGlDisplay;
+use glutin::prelude::{GlDisplay, NotCurrentGlContext, PossiblyCurrentGlContext};
+use glutin::surface::{GlSurface, Surface, SurfaceTypeTrait};
+
+pub mod blending;
+pub mod buffer;
+pub mod data_types;
+pub mod debug;
+pub mod framebuffer;
+pub mod misc;
+pub mod shader;
+pub mod texture;
+pub mod vertex_array;
+
+pub struct MaybeCurrentContextWithFns
+{
+ context: PossiblyCurrentContext,
+ fns: Box<sys::Gl>,
+}
+
+impl MaybeCurrentContextWithFns
+{
+ /// Returns a new `ContextWithFns`.
+ ///
+ /// # Errors
+ /// Returns `Err` if making this context current fails.
+ pub fn new<SurfaceType: SurfaceTypeTrait>(
+ context: NotCurrentContext,
+ surface: &Surface<SurfaceType>,
+ ) -> Result<Self, MakeContextCurrentError>
+ {
+ let context = context
+ .make_current(surface)
+ .map_err(MakeContextCurrentError)?;
+
+ let display = context.display();
+
+ let gl = sys::Gl::load_with(|symbol| {
+ let Ok(symbol) = CString::new(symbol) else {
+ eprintln!("GL symbol contains nul byte");
+ abort();
+ };
+
+ display.get_proc_address(&symbol)
+ });
+
+ Ok(Self { context, fns: Box::new(gl) })
+ }
+
+ /// Attempts to make this context & the specified surface current in the calling
+ /// thread.
+ ///
+ /// # Errors
+ /// Returns `Err` if making this context current fails.
+ pub fn make_current<SurfaceType: SurfaceTypeTrait>(
+ &self,
+ surface: &Surface<SurfaceType>,
+ ) -> Result<(), MakeContextCurrentError>
+ {
+ if !self.context.is_current() || !surface.is_current(&self.context) {
+ self.context
+ .make_current(surface)
+ .map_err(MakeContextCurrentError)?;
+ }
+
+ Ok(())
+ }
+
+ /// Attempts to make this context current on the calling thread.
+ ///
+ /// # Errors
+ /// Returns `Err` if making this context current fails.
+ pub fn make_current_surfaceless(&self) -> Result<(), MakeContextCurrentError>
+ {
+ if !self.context.is_current() {
+ make_glutin_context_current_surfaceless(&self.context)
+ .map_err(MakeContextCurrentError)?;
+ }
+
+ Ok(())
+ }
+
+ #[must_use]
+ pub fn context(&self) -> &PossiblyCurrentContext
+ {
+ &self.context
+ }
+
+ #[inline]
+ pub(crate) fn fns(&self) -> &sys::Gl
+ {
+ debug_assert!(self.context.is_current());
+
+ &self.fns
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+#[error("Failed to make context current")]
+pub struct MakeContextCurrentError(#[source] glutin::error::Error);
+
+fn make_glutin_context_current_surfaceless(
+ context: &PossiblyCurrentContext,
+) -> Result<(), glutin::error::Error>
+{
+ #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))]
+ compile_error!("Unsupported target OS");
+
+ match context {
+ #[cfg(any(target_os = "windows", target_os = "linux"))]
+ PossiblyCurrentContext::Egl(context) => context.make_current_surfaceless(),
+
+ #[cfg(target_os = "linux")]
+ PossiblyCurrentContext::Glx(context) => context.make_current_surfaceless(),
+
+ #[cfg(target_os = "windows")]
+ PossiblyCurrentContext::Wgl(context) => context.make_current_surfaceless(),
+
+ #[cfg(target_os = "macos")]
+ PossiblyCurrentContext::Cgl(context) => context.make_current_surfaceless(),
+ }
+}
+
+mod sys
+{
+ #![allow(
+ clippy::missing_safety_doc,
+ clippy::missing_transmute_annotations,
+ clippy::too_many_arguments,
+ clippy::unused_unit,
+ clippy::upper_case_acronyms,
+ clippy::doc_markdown,
+ clippy::unreadable_literal,
+ unsafe_op_in_unsafe_fn
+ )]
+
+ include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
+}
diff --git a/opengl-bindings/src/misc.rs b/opengl-bindings/src/misc.rs
new file mode 100644
index 0000000..a0d36e3
--- /dev/null
+++ b/opengl-bindings/src/misc.rs
@@ -0,0 +1,290 @@
+use bitflags::bitflags;
+
+use crate::data_types::{Dimens, Vec2};
+use crate::MaybeCurrentContextWithFns;
+
+/// Sets the viewport.
+///
+/// The `u32` values in `position` and `size` must fit in `i32`s.
+///
+/// # Errors
+/// Returns `Err` if any value in `position` or `size` does not fit into a `i32`.
+pub fn set_viewport(
+ current_context: &MaybeCurrentContextWithFns,
+ position: &Vec2<u32>,
+ size: &Dimens<u32>,
+) -> Result<(), SetViewportError>
+{
+ let position = Vec2::<crate::sys::types::GLint> {
+ x: position.x.try_into().map_err(|_| {
+ SetViewportError::PositionXValueTooLarge {
+ value: position.x,
+ max_value: crate::sys::types::GLint::MAX as u32,
+ }
+ })?,
+ y: position.y.try_into().map_err(|_| {
+ SetViewportError::PositionYValueTooLarge {
+ value: position.y,
+ max_value: crate::sys::types::GLint::MAX as u32,
+ }
+ })?,
+ };
+
+ let size = Dimens::<crate::sys::types::GLsizei> {
+ width: size.width.try_into().map_err(|_| {
+ SetViewportError::SizeWidthValueTooLarge {
+ value: size.width,
+ max_value: crate::sys::types::GLsizei::MAX as u32,
+ }
+ })?,
+ height: size.height.try_into().map_err(|_| {
+ SetViewportError::SizeHeightValueTooLarge {
+ value: size.height,
+ max_value: crate::sys::types::GLsizei::MAX as u32,
+ }
+ })?,
+ };
+
+ unsafe {
+ current_context
+ .fns()
+ .Viewport(position.x, position.y, size.width, size.height);
+ }
+
+ Ok(())
+}
+
+pub fn get_viewport(
+ current_context: &MaybeCurrentContextWithFns,
+) -> (Vec2<u32>, Dimens<u32>)
+{
+ let mut values = [0i32; 4];
+
+ unsafe {
+ current_context
+ .fns()
+ .GetIntegerv(crate::sys::VIEWPORT, values.as_mut_ptr());
+ }
+
+ let [x, y, width, height] = values;
+
+ let pos = Vec2::<u32> {
+ x: x.try_into().expect("Negative viewport x coordinate"),
+ y: y.try_into().expect("Negative viewport y coordinate"),
+ };
+
+ let size = Dimens::<u32> {
+ width: width.try_into().expect("Negative viewport width"),
+ height: height.try_into().expect("Negative viewport height"),
+ };
+
+ (pos, size)
+}
+
+pub fn clear_buffers(current_context: &MaybeCurrentContextWithFns, mask: BufferClearMask)
+{
+ unsafe {
+ current_context.fns().Clear(mask.bits());
+ }
+}
+
+pub fn set_polygon_mode(
+ current_context: &MaybeCurrentContextWithFns,
+ face: impl Into<PolygonModeFace>,
+ mode: impl Into<PolygonMode>,
+)
+{
+ unsafe {
+ current_context
+ .fns()
+ .PolygonMode(face.into() as u32, mode.into() as u32);
+ }
+}
+
+pub fn enable(current_context: &MaybeCurrentContextWithFns, capacity: Capability)
+{
+ unsafe {
+ current_context.fns().Enable(capacity as u32);
+ }
+}
+
+pub fn disable(current_context: &MaybeCurrentContextWithFns, capability: Capability)
+{
+ unsafe {
+ current_context.fns().Disable(capability as u32);
+ }
+}
+
+pub fn set_enabled(
+ current_context: &MaybeCurrentContextWithFns,
+ capability: Capability,
+ enabled: bool,
+)
+{
+ if enabled {
+ enable(current_context, capability);
+ } else {
+ disable(current_context, capability);
+ }
+}
+
+#[must_use]
+pub fn get_context_flags(current_context: &MaybeCurrentContextWithFns) -> ContextFlags
+{
+ let mut context_flags = crate::sys::types::GLint::default();
+
+ unsafe {
+ current_context
+ .fns()
+ .GetIntegerv(crate::sys::CONTEXT_FLAGS, &raw mut context_flags);
+ }
+
+ ContextFlags::from_bits_truncate(context_flags.cast_unsigned())
+}
+
+/// Defines a rectangle, called the scissor box, in window coordinates.
+pub fn define_scissor_box(
+ current_context: &MaybeCurrentContextWithFns,
+ lower_left_corner_pos: Vec2<u16>,
+ size: Dimens<u16>,
+)
+{
+ let lower_left_corner_pos = Vec2::<crate::sys::types::GLint> {
+ x: lower_left_corner_pos.x.into(),
+ y: lower_left_corner_pos.y.into(),
+ };
+
+ let size = Dimens::<crate::sys::types::GLsizei> {
+ width: size.width.into(),
+ height: size.height.into(),
+ };
+
+ unsafe {
+ current_context.fns().Scissor(
+ lower_left_corner_pos.x,
+ lower_left_corner_pos.y,
+ size.width,
+ size.height,
+ );
+ }
+}
+
+pub fn set_depth_function(
+ current_context: &MaybeCurrentContextWithFns,
+ depth_function: DepthFunction,
+)
+{
+ unsafe {
+ current_context.fns().DepthFunc(depth_function as u32);
+ }
+}
+
+bitflags! {
+ #[derive(Debug, Clone, Copy)]
+ pub struct BufferClearMask: u32 {
+ const COLOR = crate::sys::COLOR_BUFFER_BIT;
+ const DEPTH = crate::sys::DEPTH_BUFFER_BIT;
+ const STENCIL = crate::sys::STENCIL_BUFFER_BIT;
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u32)]
+#[non_exhaustive]
+pub enum Capability
+{
+ DepthTest = crate::sys::DEPTH_TEST,
+ ScissorTest = crate::sys::SCISSOR_TEST,
+ MultiSample = crate::sys::MULTISAMPLE,
+ DebugOutput = crate::sys::DEBUG_OUTPUT,
+ DebugOutputSynchronous = crate::sys::DEBUG_OUTPUT_SYNCHRONOUS,
+ Blend = crate::sys::BLEND,
+ CullFace = crate::sys::CULL_FACE,
+}
+
+#[derive(Debug)]
+#[repr(u32)]
+pub enum PolygonMode
+{
+ Point = crate::sys::POINT,
+ Line = crate::sys::LINE,
+ Fill = crate::sys::FILL,
+}
+
+#[derive(Debug)]
+#[repr(u32)]
+pub enum PolygonModeFace
+{
+ Front = crate::sys::FRONT,
+ Back = crate::sys::BACK,
+ FrontAndBack = crate::sys::FRONT_AND_BACK,
+}
+
+bitflags! {
+#[derive(Debug, Clone, Copy)]
+pub struct ContextFlags: u32 {
+ const FORWARD_COMPATIBLE = crate::sys::CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT;
+ const DEBUG = crate::sys::CONTEXT_FLAG_DEBUG_BIT;
+ const ROBUST_ACCESS = crate::sys::CONTEXT_FLAG_ROBUST_ACCESS_BIT;
+}
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+#[repr(u32)]
+pub enum DepthFunction
+{
+ /// Never passes.
+ Never = crate::sys::NEVER,
+
+ /// Passes if the incoming depth value is less than the stored depth value.
+ #[default]
+ Less = crate::sys::LESS,
+
+ /// Passes if the incoming depth value is equal to the stored depth value.
+ Equal = crate::sys::EQUAL,
+
+ /// Passes if the incoming depth value is less than or equal to the stored depth
+ /// value.
+ LessOrEqual = crate::sys::LEQUAL,
+
+ /// Passes if the incoming depth value is greater than the stored depth value.
+ Greater = crate::sys::GREATER,
+
+ /// Passes if the incoming depth value is not equal to the stored depth value.
+ NotEqual = crate::sys::NOTEQUAL,
+
+ /// Passes if the incoming depth value is greater than or equal to the stored depth
+ /// value.
+ GreaterOrEqual = crate::sys::GEQUAL,
+
+ /// Always passes.
+ Always = crate::sys::ALWAYS,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum SetViewportError
+{
+ #[error("Position X value ({value}) is too large. Must be < {max_value}")]
+ PositionXValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error("Position Y value ({value}) is too large. Must be < {max_value}")]
+ PositionYValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error("Size width value ({value}) is too large. Must be < {max_value}")]
+ SizeWidthValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error("Size height value ({value}) is too large. Must be < {max_value}")]
+ SizeHeightValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+}
diff --git a/opengl-bindings/src/shader.rs b/opengl-bindings/src/shader.rs
new file mode 100644
index 0000000..511b00c
--- /dev/null
+++ b/opengl-bindings/src/shader.rs
@@ -0,0 +1,395 @@
+use std::ffi::CStr;
+use std::ptr::null_mut;
+
+use safer_ffi::layout::ReprC;
+
+use crate::data_types::{Matrix, Vec3};
+use crate::MaybeCurrentContextWithFns;
+
+#[derive(Debug, Clone)]
+pub struct Shader
+{
+ shader: crate::sys::types::GLuint,
+}
+
+impl Shader
+{
+ #[must_use]
+ pub fn new(current_context: &MaybeCurrentContextWithFns, kind: Kind) -> Self
+ {
+ let shader = unsafe {
+ current_context
+ .fns()
+ .CreateShader(kind as crate::sys::types::GLenum)
+ };
+
+ Self { shader }
+ }
+
+ /// Sets the source code of this shader.
+ ///
+ /// # Errors
+ /// Returns `Err` if `source` is not ASCII.
+ pub fn set_source(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ source: &str,
+ ) -> Result<(), Error>
+ {
+ if !source.is_ascii() {
+ return Err(Error::SourceNotAscii);
+ }
+
+ let length: crate::sys::types::GLint =
+ source.len().try_into().map_err(|_| Error::SourceTooLarge {
+ length: source.len(),
+ max_length: crate::sys::types::GLint::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().ShaderSource(
+ self.shader,
+ 1,
+ &source.as_ptr().cast(),
+ &raw const length,
+ );
+ }
+
+ Ok(())
+ }
+
+ /// Compiles this shader.
+ ///
+ /// # Errors
+ /// Returns `Err` if compiling fails.
+ pub fn compile(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ ) -> Result<(), Error>
+ {
+ unsafe {
+ current_context.fns().CompileShader(self.shader);
+ }
+
+ let mut compile_success = crate::sys::types::GLint::default();
+
+ unsafe {
+ current_context.fns().GetShaderiv(
+ self.shader,
+ crate::sys::COMPILE_STATUS,
+ &raw mut compile_success,
+ );
+ }
+
+ if compile_success == 0 {
+ let info_log = self.get_info_log(current_context);
+
+ return Err(Error::CompileFailed { log: info_log });
+ }
+
+ Ok(())
+ }
+
+ pub fn delete(self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context.fns().DeleteShader(self.shader);
+ }
+ }
+
+ fn get_info_log(&self, current_context: &MaybeCurrentContextWithFns) -> String
+ {
+ const BUF_SIZE: crate::sys::types::GLsizei = 512;
+
+ let mut buf = vec![crate::sys::types::GLchar::default(); BUF_SIZE as usize];
+
+ unsafe {
+ current_context.fns().GetShaderInfoLog(
+ self.shader,
+ BUF_SIZE,
+ null_mut(),
+ buf.as_mut_ptr(),
+ );
+ }
+
+ let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) };
+
+ unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) }
+ }
+}
+
+/// Shader kind.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[repr(u32)]
+pub enum Kind
+{
+ Vertex = crate::sys::VERTEX_SHADER,
+ Fragment = crate::sys::FRAGMENT_SHADER,
+}
+
+/// Shader program
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct Program
+{
+ program: crate::sys::types::GLuint,
+}
+
+impl Program
+{
+ #[must_use]
+ pub fn new(current_context: &MaybeCurrentContextWithFns) -> Self
+ {
+ let program = unsafe { current_context.fns().CreateProgram() };
+
+ Self { program }
+ }
+
+ pub fn attach(&self, current_context: &MaybeCurrentContextWithFns, shader: &Shader)
+ {
+ unsafe {
+ current_context
+ .fns()
+ .AttachShader(self.program, shader.shader);
+ }
+ }
+
+ /// Links this program.
+ ///
+ /// # Errors
+ /// Returns `Err` if linking fails.
+ pub fn link(&self, current_context: &MaybeCurrentContextWithFns)
+ -> Result<(), Error>
+ {
+ unsafe {
+ current_context.fns().LinkProgram(self.program);
+ }
+
+ let mut link_success = crate::sys::types::GLint::default();
+
+ unsafe {
+ current_context.fns().GetProgramiv(
+ self.program,
+ crate::sys::LINK_STATUS,
+ &raw mut link_success,
+ );
+ }
+
+ if link_success == 0 {
+ let info_log = self.get_info_log(current_context);
+
+ return Err(Error::LinkFailed { log: info_log });
+ }
+
+ Ok(())
+ }
+
+ pub fn activate(&self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context.fns().UseProgram(self.program);
+ }
+ }
+
+ pub fn set_uniform_at_location(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ location: UniformLocation,
+ var: &impl UniformVariable,
+ )
+ {
+ var.set(current_context, self, location);
+ }
+
+ pub fn set_uniform(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ name: &CStr,
+ var: &impl UniformVariable,
+ )
+ {
+ let location = UniformLocation(unsafe {
+ current_context
+ .fns()
+ .GetUniformLocation(self.program, name.as_ptr().cast())
+ });
+
+ var.set(current_context, self, location);
+ }
+
+ pub fn from_raw(raw: u32) -> Self
+ {
+ Self { program: raw }
+ }
+
+ pub fn into_raw(self) -> u32
+ {
+ self.program
+ }
+
+ pub fn delete(self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context.fns().DeleteProgram(self.program);
+ }
+ }
+
+ fn get_info_log(&self, current_context: &MaybeCurrentContextWithFns) -> String
+ {
+ const BUF_SIZE: crate::sys::types::GLsizei = 512;
+
+ let mut buf = vec![crate::sys::types::GLchar::default(); BUF_SIZE as usize];
+
+ unsafe {
+ current_context.fns().GetProgramInfoLog(
+ self.program,
+ BUF_SIZE,
+ null_mut(),
+ buf.as_mut_ptr(),
+ );
+ }
+
+ let info_log = unsafe { CStr::from_ptr(buf.as_ptr()) };
+
+ unsafe { String::from_utf8_unchecked(info_log.to_bytes().to_vec()) }
+ }
+}
+
+pub trait UniformVariable: ReprC + sealed::Sealed
+{
+ fn set(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ program: &Program,
+ uniform_location: UniformLocation,
+ );
+}
+
+impl UniformVariable for f32
+{
+ fn set(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ program: &Program,
+ uniform_location: UniformLocation,
+ )
+ {
+ unsafe {
+ current_context.fns().ProgramUniform1f(
+ program.program,
+ uniform_location.0,
+ *self,
+ );
+ }
+ }
+}
+
+impl sealed::Sealed for f32 {}
+
+impl UniformVariable for i32
+{
+ fn set(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ program: &Program,
+ uniform_location: UniformLocation,
+ )
+ {
+ unsafe {
+ current_context.fns().ProgramUniform1i(
+ program.program,
+ uniform_location.0,
+ *self,
+ );
+ }
+ }
+}
+
+impl sealed::Sealed for i32 {}
+
+impl UniformVariable for Vec3<f32>
+{
+ fn set(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ program: &Program,
+ uniform_location: UniformLocation,
+ )
+ {
+ unsafe {
+ current_context.fns().ProgramUniform3f(
+ program.program,
+ uniform_location.0,
+ self.x,
+ self.y,
+ self.z,
+ );
+ }
+ }
+}
+
+impl sealed::Sealed for Vec3<f32> {}
+
+impl UniformVariable for Matrix<f32, 4, 4>
+{
+ fn set(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ program: &Program,
+ uniform_location: UniformLocation,
+ )
+ {
+ unsafe {
+ current_context.fns().ProgramUniformMatrix4fv(
+ program.program,
+ uniform_location.0,
+ 1,
+ crate::sys::FALSE,
+ self.items.as_ptr().cast::<f32>(),
+ );
+ }
+ }
+}
+
+impl sealed::Sealed for Matrix<f32, 4, 4> {}
+
+#[derive(Debug, Clone)]
+pub struct UniformLocation(crate::sys::types::GLint);
+
+impl UniformLocation
+{
+ pub fn from_number(number: i32) -> Self
+ {
+ Self(number)
+ }
+}
+
+/// Shader error.
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("All characters in source are not within the ASCII range")]
+ SourceNotAscii,
+
+ #[error("Source is too large. Length ({length}) must be < {max_length}")]
+ SourceTooLarge
+ {
+ length: usize, max_length: usize
+ },
+
+ #[error("Failed to compile shader")]
+ CompileFailed
+ {
+ log: String
+ },
+
+ #[error("Failed to link shader program")]
+ LinkFailed
+ {
+ log: String
+ },
+}
+
+mod sealed
+{
+ pub trait Sealed {}
+}
diff --git a/opengl-bindings/src/texture.rs b/opengl-bindings/src/texture.rs
new file mode 100644
index 0000000..ae5b2c3
--- /dev/null
+++ b/opengl-bindings/src/texture.rs
@@ -0,0 +1,663 @@
+use crate::data_types::{Dimens, Vec2};
+use crate::MaybeCurrentContextWithFns;
+
+#[derive(Debug, Clone)]
+pub struct Builder
+{
+ size: Dimens<u32>,
+ mipmap_levels: u16,
+}
+
+impl Builder
+{
+ pub fn size(mut self, size: Dimens<u32>) -> Self
+ {
+ self.size = size;
+ self
+ }
+
+ pub fn mipmap_levels(mut self, mipmap_levels: u16) -> Self
+ {
+ self.mipmap_levels = mipmap_levels;
+ self
+ }
+
+ #[must_use]
+ pub fn create_2d<'image>(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ image: Option<&[u8]>,
+ pixel_data_format: PixelDataFormat,
+ ) -> Result<Texture, Error>
+ {
+ if let Some(image) = &image {
+ check_image_buffer_len_correct_for_size(
+ image,
+ [self.size.width, self.size.height, 1],
+ pixel_data_format,
+ )?;
+ }
+
+ let size = try_convert_size([self.size.width, self.size.height])?;
+
+ let texture = Texture::new(current_context, crate::sys::TEXTURE_2D);
+
+ texture.alloc_2d(current_context, self.mipmap_levels, pixel_data_format, size);
+
+ if let Some(image) = image {
+ texture.sub_image_2d(
+ current_context,
+ 0,
+ [0, 0],
+ size,
+ pixel_data_format,
+ image,
+ );
+
+ texture.generate_mipmap(current_context);
+ }
+
+ Ok(texture)
+ }
+
+ #[must_use]
+ pub fn create_cube_map<'image>(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ images: Option<[(CubeMapFace, &[u8]); 6]>,
+ pixel_data_format: PixelDataFormat,
+ ) -> Result<Texture, Error>
+ {
+ for (_, image) in images.iter().flatten() {
+ check_image_buffer_len_correct_for_size(
+ image.as_ref(),
+ [self.size.width, self.size.height, 1],
+ pixel_data_format,
+ )?;
+ }
+
+ let size = try_convert_size([self.size.width, self.size.height])?;
+
+ let texture = Texture::new(current_context, crate::sys::TEXTURE_CUBE_MAP);
+
+ texture.alloc_2d(current_context, self.mipmap_levels, pixel_data_format, size);
+
+ for (face, image) in images.iter().flatten() {
+ texture.sub_image_3d(
+ current_context,
+ 0,
+ [0, 0, *face as crate::sys::types::GLint],
+ [size[0], size[1], 1],
+ pixel_data_format,
+ *image,
+ );
+ }
+
+ if images.is_some() {
+ texture.generate_mipmap(current_context);
+ }
+
+ Ok(texture)
+ }
+}
+
+impl Default for Builder
+{
+ fn default() -> Self
+ {
+ Self {
+ size: Dimens::default(),
+ mipmap_levels: 1,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct Texture
+{
+ texture: crate::sys::types::GLuint,
+}
+
+impl Texture
+{
+ pub fn builder() -> Builder
+ {
+ Builder::default()
+ }
+
+ pub fn bind_to_texture_unit(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ texture_unit: u32,
+ )
+ {
+ unsafe {
+ current_context
+ .fns()
+ .BindTextureUnit(texture_unit, self.texture);
+ }
+ }
+
+ pub fn generate_mipmap(&self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context.fns().GenerateTextureMipmap(self.texture);
+ }
+ }
+
+ pub fn store_image_2d(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ mipmap_level: u16,
+ offset: Vec2<u32>,
+ size: Dimens<u32>,
+ pixel_data_format: PixelDataFormat,
+ image: &[u8],
+ ) -> Result<(), Error>
+ {
+ check_image_buffer_len_correct_for_size(
+ image,
+ [size.width, size.height, 1],
+ pixel_data_format,
+ )?;
+
+ let offset = try_convert_offset([offset.x, offset.y])?;
+ let size = try_convert_size([size.width, size.height])?;
+
+ self.sub_image_2d(
+ current_context,
+ mipmap_level,
+ offset,
+ size,
+ pixel_data_format,
+ image,
+ );
+
+ Ok(())
+ }
+
+ pub fn store_image_3d(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ mipmap_level: u16,
+ offset: [u32; 3],
+ size: [u32; 3],
+ pixel_data_format: PixelDataFormat,
+ image: &[u8],
+ ) -> Result<(), Error>
+ {
+ check_image_buffer_len_correct_for_size(image, size, pixel_data_format)?;
+
+ let offset = try_convert_offset(offset)?;
+ let size = try_convert_size(size)?;
+
+ self.sub_image_3d(
+ current_context,
+ mipmap_level,
+ offset,
+ size,
+ pixel_data_format,
+ image,
+ );
+
+ Ok(())
+ }
+
+ pub fn set_wrap(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ wrapping: Wrapping,
+ )
+ {
+ unsafe {
+ current_context.fns().TextureParameteri(
+ self.texture,
+ crate::sys::TEXTURE_WRAP_S,
+ wrapping as i32,
+ );
+
+ current_context.fns().TextureParameteri(
+ self.texture,
+ crate::sys::TEXTURE_WRAP_T,
+ wrapping as i32,
+ );
+ }
+ }
+
+ pub fn set_magnifying_filter(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ filtering: Filtering,
+ )
+ {
+ unsafe {
+ current_context.fns().TextureParameteri(
+ self.texture,
+ crate::sys::TEXTURE_MAG_FILTER,
+ filtering as i32,
+ );
+ }
+ }
+
+ pub fn set_minifying_filter(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ filtering: Filtering,
+ )
+ {
+ unsafe {
+ current_context.fns().TextureParameteri(
+ self.texture,
+ crate::sys::TEXTURE_MIN_FILTER,
+ filtering as i32,
+ );
+ }
+ }
+
+ pub fn delete(self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context
+ .fns()
+ .DeleteTextures(1, &raw const self.texture);
+ }
+ }
+
+ pub fn from_raw(raw: u32) -> Self
+ {
+ Self { texture: raw }
+ }
+
+ pub fn into_raw(self) -> u32
+ {
+ self.texture
+ }
+
+ fn new(
+ current_context: &MaybeCurrentContextWithFns,
+ target: crate::sys::types::GLenum,
+ ) -> Self
+ {
+ let mut texture = crate::sys::types::GLuint::default();
+
+ unsafe {
+ current_context
+ .fns()
+ .CreateTextures(target, 1, &raw mut texture);
+ };
+
+ Self { texture }
+ }
+
+ fn alloc_2d(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ mipmap_levels: u16,
+ pixel_data_format: PixelDataFormat,
+ size: [crate::sys::types::GLsizei; 2],
+ )
+ {
+ unsafe {
+ current_context.fns().TextureStorage2D(
+ self.texture,
+ mipmap_levels.into(),
+ pixel_data_format.into_gl_sized_internal_format(),
+ size[0],
+ size[1],
+ );
+ }
+ }
+
+ fn sub_image_2d(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ mipmap_level: u16,
+ offset: [crate::sys::types::GLint; 2],
+ size: [crate::sys::types::GLsizei; 2],
+ pixel_data_format: PixelDataFormat,
+ image: &[u8],
+ )
+ {
+ let mut is_pixel_unpack_alignment_changed = false;
+
+ if let Some(new_pixel_unpack_alignment) =
+ pixel_data_format.requires_adjust_pixel_unpack_alignment()
+ {
+ set_pixel_unpack_alignment(current_context, new_pixel_unpack_alignment);
+
+ is_pixel_unpack_alignment_changed = true;
+ }
+
+ unsafe {
+ current_context.fns().TextureSubImage2D(
+ self.texture,
+ mipmap_level.into(),
+ offset[0],
+ offset[1],
+ size[0],
+ size[1],
+ pixel_data_format.into_gl_format(),
+ pixel_data_format.into_gl_data_type(),
+ image.as_ptr().cast(),
+ );
+ }
+
+ if is_pixel_unpack_alignment_changed {
+ set_pixel_unpack_alignment(current_context, PixelAlignment::default());
+ }
+ }
+
+ fn sub_image_3d(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ mipmap_level: u16,
+ offset: [crate::sys::types::GLint; 3],
+ size: [crate::sys::types::GLsizei; 3],
+ pixel_data_format: PixelDataFormat,
+ image: &[u8],
+ )
+ {
+ let mut is_pixel_unpack_alignment_changed = false;
+
+ if let Some(new_pixel_unpack_alignment) =
+ pixel_data_format.requires_adjust_pixel_unpack_alignment()
+ {
+ set_pixel_unpack_alignment(current_context, new_pixel_unpack_alignment);
+
+ is_pixel_unpack_alignment_changed = true;
+ }
+
+ unsafe {
+ current_context.fns().TextureSubImage3D(
+ self.texture,
+ mipmap_level.into(),
+ offset[0],
+ offset[1],
+ offset[2],
+ size[0],
+ size[1],
+ size[2],
+ pixel_data_format.into_gl_format(),
+ pixel_data_format.into_gl_data_type(),
+ image.as_ptr().cast(),
+ );
+ }
+
+ if is_pixel_unpack_alignment_changed {
+ set_pixel_unpack_alignment(current_context, PixelAlignment::default());
+ }
+ }
+}
+
+const fn try_cast_u32_to_i32(val: u32) -> i32
+{
+ assert!(val <= i32::MAX as u32);
+
+ val.cast_signed()
+}
+
+/// Texture wrapping.
+#[derive(Debug, Clone, Copy)]
+#[repr(i32)]
+pub enum Wrapping
+{
+ Repeat = const { try_cast_u32_to_i32(crate::sys::REPEAT) },
+ MirroredRepeat = const { try_cast_u32_to_i32(crate::sys::MIRRORED_REPEAT) },
+ ClampToEdge = const { try_cast_u32_to_i32(crate::sys::CLAMP_TO_EDGE) },
+ ClampToBorder = const { try_cast_u32_to_i32(crate::sys::CLAMP_TO_BORDER) },
+}
+
+#[derive(Debug, Clone, Copy)]
+#[repr(i32)]
+pub enum Filtering
+{
+ Nearest = const { try_cast_u32_to_i32(crate::sys::NEAREST) },
+ Linear = const { try_cast_u32_to_i32(crate::sys::LINEAR) },
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+#[repr(u32)]
+pub enum RgbDataType
+{
+ UnsignedByte = crate::sys::UNSIGNED_BYTE,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+#[repr(u32)]
+pub enum RgbaDataType
+{
+ UnsignedByte = crate::sys::UNSIGNED_BYTE,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+#[repr(u32)]
+pub enum SrgbDataType
+{
+ UnsignedByte = crate::sys::UNSIGNED_BYTE,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+#[repr(u32)]
+pub enum SrgbaDataType
+{
+ UnsignedByte = crate::sys::UNSIGNED_BYTE,
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+#[repr(u32)]
+pub enum DepthComponentDataType
+{
+ Float32 = crate::sys::FLOAT,
+}
+
+/// Texture pixel data format.
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum PixelDataFormat
+{
+ Rgb(RgbDataType),
+ Rgba(RgbaDataType),
+ Srgb(SrgbDataType),
+ Srgba(SrgbaDataType),
+ DepthComponent(DepthComponentDataType),
+}
+
+impl PixelDataFormat
+{
+ fn into_gl_sized_internal_format(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Rgb(RgbDataType::UnsignedByte) => crate::sys::RGB8,
+ Self::Rgba(RgbaDataType::UnsignedByte) => crate::sys::RGBA8,
+ Self::Srgb(SrgbDataType::UnsignedByte) => crate::sys::SRGB8,
+ Self::Srgba(SrgbaDataType::UnsignedByte) => crate::sys::SRGB8_ALPHA8,
+ Self::DepthComponent(DepthComponentDataType::Float32) => {
+ crate::sys::DEPTH_COMPONENT32F
+ }
+ }
+ }
+
+ fn into_gl_format(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Rgb(_) | Self::Srgb(_) => crate::sys::RGB,
+ Self::Rgba(_) | Self::Srgba(_) => crate::sys::RGBA,
+ Self::DepthComponent(_) => crate::sys::DEPTH_COMPONENT,
+ }
+ }
+
+ fn into_gl_data_type(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Rgb(ty) => ty as crate::sys::types::GLenum,
+ Self::Srgb(ty) => ty as crate::sys::types::GLenum,
+ Self::Rgba(ty) => ty as crate::sys::types::GLenum,
+ Self::Srgba(ty) => ty as crate::sys::types::GLenum,
+ Self::DepthComponent(ty) => ty as crate::sys::types::GLenum,
+ }
+ }
+
+ fn pixel_width(&self) -> usize
+ {
+ match self {
+ Self::Rgb(_) | Self::Srgb(_) => 3,
+ Self::Rgba(_) | Self::Srgba(_) => 4,
+ Self::DepthComponent(_) => 1,
+ }
+ }
+
+ fn requires_adjust_pixel_unpack_alignment(&self) -> Option<PixelAlignment>
+ {
+ if matches!(
+ self,
+ PixelDataFormat::Rgb(RgbDataType::UnsignedByte)
+ | PixelDataFormat::Srgb(SrgbDataType::UnsignedByte)
+ ) {
+ return Some(PixelAlignment::Byte);
+ }
+
+ None
+ }
+}
+
+impl Default for PixelDataFormat
+{
+ fn default() -> Self
+ {
+ PixelDataFormat::Rgba(RgbaDataType::UnsignedByte)
+ }
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CubeMapFace
+{
+ PositiveX = 0,
+ NegativeX = 1,
+ PositiveY = 2,
+ NegativeY = 3,
+ PositiveZ = 4,
+ NegativeZ = 5,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error
+{
+ #[error("Size value ({value}) is too large. Must be < {max_value}")]
+ ValueInSizeIsTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error("Offset value ({value}) is too large. Must be < {max_value}")]
+ ValueInOffsetIsTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error(
+ "Incorrect image buffer length for size {}x{}x{}. Expected {}, found {}",
+ size[0],
+ size[1],
+ size[2],
+ expected_buffer_len,
+ buffer_len
+ )]
+ IncorrectImageBufferLengthForSize
+ {
+ expected_buffer_len: usize,
+ buffer_len: usize,
+ size: [u32; 3],
+ },
+}
+
+fn try_convert_size<const LEN: usize>(
+ size: [u32; LEN],
+) -> Result<[crate::sys::types::GLsizei; LEN], Error>
+{
+ let mut output = [0; LEN];
+
+ for (value, output_val) in size.into_iter().zip(&mut output) {
+ *output_val = value.try_into().map_err(|_| Error::ValueInSizeIsTooLarge {
+ value: value,
+ max_value: crate::sys::types::GLsizei::MAX as u32,
+ })?;
+ }
+
+ Ok(output)
+}
+
+fn try_convert_offset<const LEN: usize>(
+ offset: [u32; LEN],
+) -> Result<[crate::sys::types::GLint; LEN], Error>
+{
+ let mut output = [0; LEN];
+
+ for (value, output_val) in offset.into_iter().zip(&mut output) {
+ *output_val = value
+ .try_into()
+ .map_err(|_| Error::ValueInOffsetIsTooLarge {
+ value: value,
+ max_value: crate::sys::types::GLint::MAX as u32,
+ })?;
+ }
+
+ Ok(output)
+}
+
+fn check_image_buffer_len_correct_for_size(
+ image: &[u8],
+ size: [u32; 3],
+ pixel_data_format: PixelDataFormat,
+) -> Result<(), Error>
+{
+ let pixel_width = pixel_data_format.pixel_width();
+
+ let [width, height, depth] = size;
+
+ let expected_buffer_len =
+ width as usize * height as usize * depth as usize * pixel_width;
+
+ if expected_buffer_len != image.len() {
+ return Err(Error::IncorrectImageBufferLengthForSize {
+ expected_buffer_len,
+ buffer_len: image.len(),
+ size,
+ });
+ }
+
+ Ok(())
+}
+
+fn set_pixel_unpack_alignment(
+ current_context: &MaybeCurrentContextWithFns,
+ alignment: PixelAlignment,
+)
+{
+ let alignment = match alignment {
+ PixelAlignment::Byte => 1,
+ // PixelAlignment::TwoBytes => 2,
+ PixelAlignment::FourBytes => 4,
+ // PixelAlignment::EightBytes => 8,
+ };
+
+ unsafe {
+ current_context
+ .fns()
+ .PixelStorei(crate::sys::UNPACK_ALIGNMENT, alignment);
+ }
+}
+
+#[derive(Debug, Default, Clone, Copy)]
+enum PixelAlignment
+{
+ Byte,
+ // TwoBytes,
+ #[default]
+ FourBytes,
+ // EightBytes,
+}
diff --git a/opengl-bindings/src/vertex_array.rs b/opengl-bindings/src/vertex_array.rs
new file mode 100644
index 0000000..84fdc42
--- /dev/null
+++ b/opengl-bindings/src/vertex_array.rs
@@ -0,0 +1,343 @@
+use std::ffi::c_void;
+
+use safer_ffi::layout::ReprC;
+
+use crate::buffer::Buffer;
+use crate::MaybeCurrentContextWithFns;
+
+#[derive(Debug, Clone)]
+pub struct VertexArray
+{
+ array: crate::sys::types::GLuint,
+}
+
+impl VertexArray
+{
+ #[must_use]
+ pub fn new(current_context: &MaybeCurrentContextWithFns) -> Self
+ {
+ let mut array = 0;
+
+ unsafe {
+ current_context.fns().CreateVertexArrays(1, &raw mut array);
+ }
+
+ Self { array }
+ }
+
+ /// Draws the currently bound vertex array.
+ ///
+ /// # Errors
+ /// Returns `Err` if:
+ /// - `vertex_offset` is too large
+ /// - `vertex_cnt` is too large
+ pub fn draw_arrays(
+ current_context: &MaybeCurrentContextWithFns,
+ primitive_kind: PrimitiveKind,
+ vertex_offset: u32,
+ vertex_cnt: u32,
+ ) -> Result<(), DrawError>
+ {
+ let vertex_offset: crate::sys::types::GLint =
+ vertex_offset.try_into().map_err(|_| {
+ DrawError::VertexOffsetValueTooLarge {
+ value: vertex_offset,
+ max_value: crate::sys::types::GLint::MAX as u32,
+ }
+ })?;
+
+ let vertex_cnt: crate::sys::types::GLsizei =
+ vertex_cnt
+ .try_into()
+ .map_err(|_| DrawError::VertexCountValueTooLarge {
+ value: vertex_cnt,
+ max_value: crate::sys::types::GLsizei::MAX as u32,
+ })?;
+
+ unsafe {
+ current_context.fns().DrawArrays(
+ primitive_kind.into_gl(),
+ vertex_offset,
+ vertex_cnt,
+ );
+ }
+
+ Ok(())
+ }
+
+ /// Draws the currently bound vertex array.
+ ///
+ /// # Errors
+ /// Returns `Err` if `cnt` is too large.
+ pub fn draw_elements(
+ current_context: &MaybeCurrentContextWithFns,
+ DrawElementsOptions {
+ primitive_kind,
+ element_offset,
+ element_cnt,
+ vertex_offset,
+ }: DrawElementsOptions,
+ ) -> Result<(), DrawError>
+ {
+ let element_cnt: crate::sys::types::GLsizei =
+ element_cnt
+ .try_into()
+ .map_err(|_| DrawError::ElementCountValueTooLarge {
+ value: element_cnt,
+ max_value: crate::sys::types::GLsizei::MAX as u32,
+ })?;
+
+ let vertex_offset: crate::sys::types::GLint =
+ vertex_offset.try_into().map_err(|_| {
+ DrawError::VertexOffsetValueTooLarge {
+ value: vertex_offset,
+ max_value: crate::sys::types::GLint::MAX as u32,
+ }
+ })?;
+
+ unsafe {
+ current_context.fns().DrawElementsBaseVertex(
+ primitive_kind.into_gl(),
+ element_cnt,
+ crate::sys::UNSIGNED_INT,
+ // TODO: Make this not sometimes UB. DrawElements expects a actual
+ // pointer to a memory location when no VBO is bound.
+ // See: https://stackoverflow.com/q/21706113
+ std::ptr::without_provenance::<c_void>(
+ element_offset as usize * size_of::<u32>(),
+ ),
+ vertex_offset,
+ );
+ }
+
+ Ok(())
+ }
+
+ pub fn bind_element_buffer(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ element_buffer: &Buffer<u32>,
+ )
+ {
+ unsafe {
+ current_context
+ .fns()
+ .VertexArrayElementBuffer(self.array, element_buffer.object());
+ }
+ }
+
+ pub fn bind_vertex_buffer<VertexBufferItem: ReprC>(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ binding_index: u32,
+ vertex_buffer: &Buffer<VertexBufferItem>,
+ vertex_buffer_spec: VertexBufferSpec,
+ ) -> Result<(), BindVertexBufferError>
+ {
+ let offset: crate::sys::types::GLintptr = vertex_buffer_spec
+ .offset
+ .try_into()
+ .map_err(|_| BindVertexBufferError::OffsetValueTooLarge {
+ value: vertex_buffer_spec.offset,
+ max_value: crate::sys::types::GLintptr::MAX as usize,
+ })?;
+
+ let vertex_size: crate::sys::types::GLsizei = vertex_buffer_spec
+ .vertex_size
+ .try_into()
+ .map_err(|_| BindVertexBufferError::VertexSizeValueTooLarge {
+ value: vertex_buffer_spec.vertex_size,
+ max_value: crate::sys::types::GLsizei::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().VertexArrayVertexBuffer(
+ self.array,
+ binding_index,
+ vertex_buffer.object(),
+ offset,
+ vertex_size,
+ );
+ }
+
+ Ok(())
+ }
+
+ pub fn enable_attrib(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ attrib_index: u32,
+ )
+ {
+ unsafe {
+ current_context.fns().EnableVertexArrayAttrib(
+ self.array,
+ attrib_index as crate::sys::types::GLuint,
+ );
+ }
+ }
+
+ pub fn disable_attrib(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ attrib_index: u32,
+ )
+ {
+ unsafe {
+ current_context.fns().DisableVertexArrayAttrib(
+ self.array,
+ attrib_index as crate::sys::types::GLuint,
+ );
+ }
+ }
+
+ pub fn set_attrib_format(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ attrib_index: u32,
+ attrib_format: AttributeFormat,
+ )
+ {
+ unsafe {
+ current_context.fns().VertexArrayAttribFormat(
+ self.array,
+ attrib_index,
+ attrib_format.count.into(),
+ attrib_format.data_type.into_gl(),
+ if attrib_format.normalized {
+ crate::sys::TRUE
+ } else {
+ crate::sys::FALSE
+ },
+ attrib_format.offset,
+ );
+ }
+ }
+
+ /// Associate a vertex attribute and a vertex buffer binding.
+ pub fn set_attrib_vertex_buf_binding(
+ &self,
+ current_context: &MaybeCurrentContextWithFns,
+ attrib_index: u32,
+ vertex_buf_binding_index: u32,
+ )
+ {
+ unsafe {
+ current_context.fns().VertexArrayAttribBinding(
+ self.array,
+ attrib_index,
+ vertex_buf_binding_index,
+ );
+ }
+ }
+
+ pub fn bind(&self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe { current_context.fns().BindVertexArray(self.array) }
+ }
+
+ pub fn delete(&self, current_context: &MaybeCurrentContextWithFns)
+ {
+ unsafe {
+ current_context
+ .fns()
+ .DeleteVertexArrays(1, &raw const self.array);
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+pub enum PrimitiveKind
+{
+ Triangles,
+}
+
+impl PrimitiveKind
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Triangles => crate::sys::TRIANGLES,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy)]
+#[non_exhaustive]
+pub enum DataType
+{
+ Float,
+}
+
+impl DataType
+{
+ fn into_gl(self) -> crate::sys::types::GLenum
+ {
+ match self {
+ Self::Float => crate::sys::FLOAT,
+ }
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct AttributeFormat
+{
+ pub data_type: DataType,
+ pub count: u8,
+ pub normalized: bool,
+ pub offset: u32,
+}
+
+#[derive(Debug, Clone)]
+pub struct VertexBufferSpec
+{
+ pub offset: usize,
+ pub vertex_size: usize,
+}
+
+#[derive(Debug, Clone)]
+pub struct DrawElementsOptions
+{
+ pub primitive_kind: PrimitiveKind,
+ pub element_offset: u32,
+ pub element_cnt: u32,
+ pub vertex_offset: u32,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum DrawError
+{
+ #[error("Vertex offset value {value} is too large. Must be < {max_value}")]
+ VertexOffsetValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error("Vertex count value {value} is too large. Must be < {max_value}")]
+ VertexCountValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+
+ #[error("Element count value {value} is too large. Must be < {max_value}")]
+ ElementCountValueTooLarge
+ {
+ value: u32, max_value: u32
+ },
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum BindVertexBufferError
+{
+ #[error("Offset value {value} is too large. Must be < {max_value}")]
+ OffsetValueTooLarge
+ {
+ value: usize, max_value: usize
+ },
+
+ #[error("Vertex size value {value} is too large. Must be < {max_value}")]
+ VertexSizeValueTooLarge
+ {
+ value: usize, max_value: usize
+ },
+}
diff --git a/res/icon.ico b/res/icon.ico
new file mode 100644
index 0000000..e0f8c4a
--- /dev/null
+++ b/res/icon.ico
Binary files differ
diff --git a/res/sky_box/cubemap_0.png b/res/sky_box/cubemap_0.png
new file mode 100644
index 0000000..5f8e75c
--- /dev/null
+++ b/res/sky_box/cubemap_0.png
Binary files differ
diff --git a/res/sky_box/cubemap_1.png b/res/sky_box/cubemap_1.png
new file mode 100644
index 0000000..4b4e594
--- /dev/null
+++ b/res/sky_box/cubemap_1.png
Binary files differ
diff --git a/res/sky_box/cubemap_2.png b/res/sky_box/cubemap_2.png
new file mode 100644
index 0000000..4233289
--- /dev/null
+++ b/res/sky_box/cubemap_2.png
Binary files differ
diff --git a/res/sky_box/cubemap_3.png b/res/sky_box/cubemap_3.png
new file mode 100644
index 0000000..fa84308
--- /dev/null
+++ b/res/sky_box/cubemap_3.png
Binary files differ
diff --git a/res/sky_box/cubemap_4.png b/res/sky_box/cubemap_4.png
new file mode 100644
index 0000000..c67b8f9
--- /dev/null
+++ b/res/sky_box/cubemap_4.png
Binary files differ
diff --git a/res/sky_box/cubemap_5.png b/res/sky_box/cubemap_5.png
new file mode 100644
index 0000000..eba15c4
--- /dev/null
+++ b/res/sky_box/cubemap_5.png
Binary files differ
diff --git a/rust-analyzer.json b/rust-analyzer.json
new file mode 100644
index 0000000..315e8fb
--- /dev/null
+++ b/rust-analyzer.json
@@ -0,0 +1,10 @@
+{
+ "cargo": {
+ "allTargets": false
+ },
+ "check": {
+ "overrideCommand": [
+ "cargo", "fcheck", "--workspace", "--message-format=json", "--keep-going"
+ ]
+ }
+}
diff --git a/rustfmt.toml b/rustfmt.toml
index df78ba9..67240b1 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,3 +1,4 @@
+edition = "2021"
max_width = 90
brace_style = "AlwaysNextLine"
group_imports = "StdExternalCrate"
diff --git a/src/main.rs b/src/main.rs
index 1d0d33a..514bf21 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,141 +1,291 @@
-use std::error::Error;
-use std::fs::read_to_string;
+#![cfg_attr(all(windows, not(debug_assertions)), windows_subsystem = "windows")]
+use std::io::Cursor;
use std::path::Path;
+use engine::asset::Assets;
use engine::camera::fly::{
Extension as FlyCameraExtension,
Fly as FlyCamera,
Options as FlyCameraOptions,
};
-use engine::camera::{Active as ActiveCamera, Camera};
-use engine::color::Color;
-use engine::data_types::dimens::Dimens;
+use engine::camera::{
+ Active as ActiveCamera,
+ Camera,
+ Controllable as ControllableCamera,
+};
+use engine::color::{Color, Rgb};
+use engine::data_types::dimens::Dimens3;
+use engine::ecs::actions::Actions;
+use engine::ecs::error::Context as _;
+use engine::ecs::pair::ChildOf;
use engine::ecs::phase::START as START_PHASE;
+use engine::ecs::query::term::With;
use engine::ecs::sole::Single;
-use engine::file_format::wavefront::mtl::parse as parse_mtl;
-use engine::file_format::wavefront::obj::parse as parse_obj;
+use engine::ecs::system::initializable::Initializable;
+use engine::ecs::system::Into;
+use engine::ecs::{pair, Query};
+use engine::image::{Format as ImageFormat, Image};
+use engine::input::keyboard::Keyboard;
use engine::input::Extension as InputExtension;
-use engine::lighting::{AttenuationParams, GlobalLight, PointLight};
-use engine::material::{Builder as MaterialBuilder, Flags as MaterialFlags};
-use engine::mesh::cube::{
- create as cube_mesh_create,
- CreationSpec as CubeMeshCreationSpec,
+use engine::lighting::DirectionalLight;
+use engine::material::Material;
+use engine::mesh::Mesh;
+use engine::model::{Materials as ModelMaterials, Model, Spec as ModelSpec};
+use engine::rendering::{
+ Extension as RenderingExtension,
+ GraphicsProperties,
+ TargetWindow as RenderingTargetWindow,
+};
+use engine::scene::{Active as ActiveScene, Scene};
+use engine::sky_box::SkyBox;
+use engine::texture::CubeMapFace;
+use engine::transform::Transform;
+use engine::ui::dear_imgui::{
+ Context as DearImguiContext,
+ Extension as DearImguiExtension,
+ TargetWindow as DearImguiTargetWindow,
};
-use engine::renderer::opengl::Extension as OpenglRendererExtension;
-use engine::transform::Position;
-use engine::vector::Vec3;
-use engine::window::{
- Builder as WindowBuilder,
- CursorMode,
- Extension as WindowExtension,
+use engine::vector::{Angles, Vec3};
+use engine::windowing::window::{
+ CreationAttributes as WindowCreationAttributes,
+ CursorGrabMode as WindowCursorGrabMode,
+ Icon as WindowIcon,
Window,
};
-use engine::Engine;
+use engine::{Engine, Error as EngineError};
use tracing::level_filters::LevelFilter;
+use tracing_subscriber::fmt::time::ChronoLocal;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::EnvFilter;
-const WINDOW_SIZE: Dimens<u32> = Dimens { width: 1920, height: 1080 };
-
-const YELLOW: Color<f32> = Color {
- red: 0.988235294118,
- green: 0.941176470588,
- blue: 0.0274509803922,
-};
+const YELLOW: Color<f32> = Color::Rgb(Rgb {
+ r: 0.988235294118,
+ g: 0.941176470588,
+ b: 0.0274509803922,
+});
const RESOURCE_DIR: &str = "res";
-fn main() -> Result<(), Box<dyn Error>>
+fn main() -> Result<(), EngineError>
{
- tracing_subscriber::registry()
- .with(tracing_subscriber::fmt::layer())
- .with(
- EnvFilter::builder()
- .with_default_directive(LevelFilter::DEBUG.into())
- .from_env()?,
- )
- .init();
+ configure_logging()?;
- let mut engine = Engine::new();
-
- let teapot_obj =
- parse_obj(&read_to_string(Path::new(RESOURCE_DIR).join("teapot.obj"))?)?;
-
- let teapot_mat_name = teapot_obj
- .faces
- .first()
- .and_then(|face| face.material_name.as_ref());
-
- let teapot_mats = teapot_obj.read_and_parse_material_libs(parse_mtl)?;
-
- let teapot_mat = teapot_mats
- .into_iter()
- .find(|mat| Some(&mat.name) == teapot_mat_name)
- .ok_or("Teapot material was not found")?;
-
- engine.spawn((
- teapot_obj.to_mesh()?,
- teapot_mat.material,
- Position::from(Vec3 { x: 1.6, y: 0.0, z: 0.0 }),
- ));
-
- engine.spawn((
- PointLight::builder()
- .position(Vec3 { x: -6.0, y: 3.0, z: 3.0 })
- .diffuse(YELLOW)
- .attenuation_params(AttenuationParams {
- linear: 0.045,
- quadratic: 0.0075,
- ..Default::default()
- })
- .build(),
- Position::from(Vec3 { x: -6.0, y: 3.0, z: 3.0 }),
- cube_mesh_create(
- CubeMeshCreationSpec::builder()
- .width(2.0)
- .height(2.0)
- .depth(2.0)
+ Engine::new()
+ .with_extension(engine::windowing::Extension::default())
+ .with_extension(
+ RenderingExtension::builder()
+ .graphics_props(
+ GraphicsProperties::builder()
+ .debug(cfg!(debug_assertions))
+ .build(),
+ )
.build(),
- |face_verts, _, _| face_verts,
+ )
+ .with_extension(InputExtension::default())
+ .with_extension(FlyCameraExtension(FlyCameraOptions {
+ mouse_sensitivity: 0.2,
+ }))
+ .with_extension(DearImguiExtension::default())
+ .with_extension(Application)
+ .start();
+
+ Ok(())
+}
+
+struct Application;
+
+impl engine::ecs::extension::Extension for Application
+{
+ fn collect(self, mut collector: engine::ecs::extension::Collector<'_>)
+ {
+ collector.add_system(*START_PHASE, init);
+
+ collector.add_system(*engine::ecs::phase::UPDATE, toggle_ui);
+
+ collector.add_system(
+ *engine::ecs::phase::UPDATE,
+ engine::ui::view::world::show
+ .into_system()
+ .initialize((engine::ui::view::world::State::default(),)),
+ );
+ }
+}
+
+fn init(mut assets: Single<Assets>, mut actions: Actions) -> Result<(), EngineError>
+{
+ let assets = assets.get_mut()?;
+
+ actions.spawn_named(
+ "Main window",
+ (
+ WindowCreationAttributes::default()
+ .with_title("Game")
+ .with_icon(Some(WindowIcon::Image(
+ Image::from_reader(
+ Cursor::new(include_bytes!("../res/icon.ico")),
+ ImageFormat::Ico,
+ )
+ .with_context(|| "Invalid icon image")?,
+ )))
+ .with_cursor_visible(false)
+ .with_cursor_grab_mode(WindowCursorGrabMode::Locked),
+ RenderingTargetWindow,
+ DearImguiTargetWindow,
),
- MaterialBuilder::new().ambient(YELLOW * 5.0).build(),
- MaterialFlags::builder().use_ambient_color(true).build(),
- ));
+ );
- engine.spawn((
- Camera::default(),
- Position {
- position: Vec3 { x: 0.0, y: 0.0, z: 3.0 },
- },
- ActiveCamera,
- FlyCamera::default(),
- ));
+ let sky_box_dir = Path::new(RESOURCE_DIR).join("sky_box");
- engine.add_sole(GlobalLight::default())?;
+ let scene_ent_id = actions.spawn_named(
+ "Scene",
+ (
+ Scene,
+ ActiveScene,
+ SkyBox::AssetPerCubeMapFace(
+ [
+ (CubeMapFace::PositiveX, "cubemap_0.png"),
+ (CubeMapFace::NegativeX, "cubemap_1.png"),
+ (CubeMapFace::PositiveY, "cubemap_2.png"),
+ (CubeMapFace::NegativeY, "cubemap_3.png"),
+ (CubeMapFace::PositiveZ, "cubemap_4.png"),
+ (CubeMapFace::NegativeZ, "cubemap_5.png"),
+ ]
+ .map(|(face, file)| (face, assets.load(sky_box_dir.join(file)))),
+ ),
+ ),
+ );
- engine.register_system(*START_PHASE, prepare_window);
+ actions.spawn_named(
+ "Sunlight",
+ (
+ DirectionalLight::builder()
+ .direction(
+ Vec3::FRONT
+ + Vec3::direction_from_deg_angles(Angles {
+ yaw: 40.0,
+ pitch: -45.0,
+ roll: 0.0,
+ }),
+ )
+ .build(),
+ pair!(ChildOf, { scene_ent_id }),
+ ),
+ );
- engine.add_extension(OpenglRendererExtension::default());
+ actions.spawn_named(
+ "Main camera",
+ (
+ Camera::default(),
+ Transform::default().position(Vec3 { x: 0.0, y: 0.0, z: 3.0 }),
+ ActiveCamera,
+ FlyCamera::default(),
+ pair!(ChildOf, { scene_ent_id }),
+ ),
+ );
- engine.add_extension(
- WindowExtension::new(WindowBuilder::default().multisampling_sample_count(8))
- .window_title("Game")
- .window_size(WINDOW_SIZE),
+ actions.spawn_named(
+ "Floor",
+ (
+ Transform::default().position(Vec3 { x: 1.6, y: -3.0, z: 0.0 }),
+ Model::new(assets.store_with_name_with("light_cube", |assets| {
+ ModelSpec::builder()
+ .mesh(assets.store_with_name(
+ "light_cube_mesh",
+ Mesh::cube(Dimens3 {
+ width: 20.0,
+ height: 0.5,
+ depth: 20.0,
+ }),
+ ))
+ .materials(ModelMaterials::direct([(
+ "surface",
+ assets.store_with_name(
+ "light_cube_surface_mat",
+ Material::builder().ambient(YELLOW * 5.0).build(),
+ ),
+ )]))
+ .material_name("surface")
+ .build()
+ })),
+ pair!(ChildOf, { scene_ent_id }),
+ ),
);
- engine.add_extension(FlyCameraExtension(FlyCameraOptions {
- mouse_sensitivity: 0.2,
- }));
+ actions.spawn_named(
+ "Big teapot",
+ (
+ Model::new(
+ assets.load::<ModelSpec>(Path::new(RESOURCE_DIR).join("teapot.obj")),
+ ),
+ Transform::default().position(Vec3 { x: 1.6, y: 0.0, z: 0.0 }),
+ pair!(ChildOf, { scene_ent_id }),
+ ),
+ );
- engine.add_extension(InputExtension::default());
+ Ok(())
+}
+
+fn configure_logging() -> Result<(), EngineError>
+{
+ if !cfg!(debug_assertions) {
+ return Ok(());
+ }
+
+ #[cfg(windows)]
+ nu_ansi_term::enable_ansi_support()
+ .map_err(EngineError::from_system_defined_win32_error)
+ .with_context(|| "Failed to enable ANSI support")?;
- engine.start();
+ tracing_subscriber::registry()
+ .with(
+ tracing_subscriber::fmt::layer()
+ .with_timer(ChronoLocal::new("%T%.6f".to_string())),
+ )
+ .with(EnvFilter::try_new("trace,winit=warn")?)
+ .with(
+ EnvFilter::builder()
+ .with_default_directive(LevelFilter::INFO.into())
+ .from_env()?,
+ )
+ .init();
Ok(())
}
-fn prepare_window(window: Single<Window>)
+fn toggle_ui(
+ window_query: Query<(&mut Window, With<DearImguiTargetWindow>)>,
+ camera_query: Query<(&mut ControllableCamera, With<Camera>, With<ActiveCamera>)>,
+ mut dear_imgui_context: Single<DearImguiContext>,
+ keyboard: Single<Keyboard>,
+) -> Result<(), EngineError>
{
- window.set_cursor_mode(CursorMode::Disabled).unwrap();
+ let dear_imgui_context = dear_imgui_context.get_mut()?;
+ let keyboard = keyboard.get()?;
+
+ let Some((mut window,)) = window_query.iter().next() else {
+ return Ok(());
+ };
+
+ let Some((mut controllable_camera,)) = camera_query.iter().next() else {
+ return Ok(());
+ };
+
+ if keyboard.just_pressed(engine::input::keyboard::Key::Escape) {
+ if dear_imgui_context.enabled {
+ dear_imgui_context.enabled = false;
+ window.cursor_visible = false;
+ window.cursor_grab_mode = WindowCursorGrabMode::Locked;
+ controllable_camera.control_enabled = true;
+ } else {
+ dear_imgui_context.enabled = true;
+ window.cursor_visible = true;
+ window.cursor_grab_mode = WindowCursorGrabMode::None;
+ controllable_camera.control_enabled = false;
+ }
+
+ window.set_changed();
+ }
+
+ Ok(())
}
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
new file mode 100644
index 0000000..337bfe3
--- /dev/null
+++ b/xtask/Cargo.toml
@@ -0,0 +1,6 @@
+[package]
+name = "xtask"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
new file mode 100644
index 0000000..8bf25ae
--- /dev/null
+++ b/xtask/src/main.rs
@@ -0,0 +1,57 @@
+use std::process::Command;
+
+fn main()
+{
+ let mut args = std::env::args();
+
+ let Some(task) = args.nth(1) else {
+ println!("Error: No task is specified");
+ return;
+ };
+
+ if task == "execute" {
+ let Some(program) = args.next() else {
+ println!("Error: No program is specified");
+ return;
+ };
+
+ if let Err(err) = Command::new(program)
+ .args(args)
+ .remove_envs(
+ // When a binary crate is run with 'cargo run', cargo sets environment
+ // variables with information about the crate, package & cargo manifest.
+ // These environment variables are unwanted when the program run here
+ // is cargo so they are removed.
+ //
+ // If cargo is the program run here and the environment variables are
+ // kept, cargo does not overwrite them, which can cause unnecessary
+ // rebuilds of dependencies
+ std::env::vars()
+ .map(|(key, _)| key)
+ .filter(|key| key.starts_with("CARGO_") && key != "CARGO_HOME"),
+ )
+ .status()
+ {
+ println!("Error: Failed to execute command: {err}");
+ }
+ } else {
+ println!("Error: Unknown task '{task}'");
+ }
+}
+
+trait CommandExt
+{
+ fn remove_envs(&mut self, keys: impl IntoIterator<Item = String>) -> &mut Self;
+}
+
+impl CommandExt for Command
+{
+ fn remove_envs(&mut self, keys: impl IntoIterator<Item = String>) -> &mut Self
+ {
+ for env_key in keys {
+ self.env_remove(env_key);
+ }
+
+ self
+ }
+}