diff options
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 @@ -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", +] @@ -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 Binary files differnew file mode 100644 index 0000000..67bbd42 --- /dev/null +++ b/engine/res/FiraMono-Regular.ttf 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 Binary files differnew file mode 100644 index 0000000..f85ba1c --- /dev/null +++ b/engine/res/ui/delete.png diff --git a/engine/res/ui/edit.png b/engine/res/ui/edit.png Binary files differnew file mode 100644 index 0000000..2431e79 --- /dev/null +++ b/engine/res/ui/edit.png diff --git a/engine/res/ui/warning.png b/engine/res/ui/warning.png Binary files differnew file mode 100644 index 0000000..4b8e6ad --- /dev/null +++ b/engine/res/ui/warning.png 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 Binary files differnew file mode 100644 index 0000000..e0f8c4a --- /dev/null +++ b/res/icon.ico diff --git a/res/sky_box/cubemap_0.png b/res/sky_box/cubemap_0.png Binary files differnew file mode 100644 index 0000000..5f8e75c --- /dev/null +++ b/res/sky_box/cubemap_0.png diff --git a/res/sky_box/cubemap_1.png b/res/sky_box/cubemap_1.png Binary files differnew file mode 100644 index 0000000..4b4e594 --- /dev/null +++ b/res/sky_box/cubemap_1.png diff --git a/res/sky_box/cubemap_2.png b/res/sky_box/cubemap_2.png Binary files differnew file mode 100644 index 0000000..4233289 --- /dev/null +++ b/res/sky_box/cubemap_2.png diff --git a/res/sky_box/cubemap_3.png b/res/sky_box/cubemap_3.png Binary files differnew file mode 100644 index 0000000..fa84308 --- /dev/null +++ b/res/sky_box/cubemap_3.png diff --git a/res/sky_box/cubemap_4.png b/res/sky_box/cubemap_4.png Binary files differnew file mode 100644 index 0000000..c67b8f9 --- /dev/null +++ b/res/sky_box/cubemap_4.png diff --git a/res/sky_box/cubemap_5.png b/res/sky_box/cubemap_5.png Binary files differnew file mode 100644 index 0000000..eba15c4 --- /dev/null +++ b/res/sky_box/cubemap_5.png 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 + } +} |
