[
  {
    "path": ".gitignore",
    "content": "/target\n**/*.rs.bk\n.idea\n*.iml\n.vscode/tasks.json\n*.blend\n*.blend1"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"oxidator\"\nversion = \"0.2.0\"\nauthors = [\"Thomas SIMON <thomas.simon.work@gmail.com>\"]\nedition = \"2018\"\ndefault-run = \"oxidator\"\n\n[features]\n#Choose one method of loading spirv out of the three. default uses the precompiled shaders, but does not support hot-reloading.\nuse_shaderc =[\"shaderc\"]\nuse_glsl_to_spirv =[\"glsl-to-spirv\"]\ndefault = []\n\n[dependencies]\nwgpu = { version = \"0.4.0\" }\nenv_logger = \"0.7.1\"\nglsl-to-spirv = {version= \"0.1\", optional= true}\nlog = \"0.4\"\npng = \"0.15\"\nwinit = \"=0.20.0-alpha4\"\nraw-window-handle = \"0.3.3\"\nimgui = \"0.2.1\"\nshaderc = {version = \"0.6\", optional = true} \nnoise = \"0.6.0\"\nnalgebra = {version=  \"0.19\", features= [\"serde-serialize\"]}\ncrossbeam-channel = \"0.3\"\nrand = \"0.7.3\"\nnotify = \"=5.0.0-pre.1\"\nbyteorder = \"1.3.2\"\ntypename = \"0.1.2\"\nbase-62 = \"0.1.1\"\nobj-rs = \"0.5\"\nspin_sleep = \"0.3.7\"\nserde = { version = \"1.0\", features = [\"derive\"] }\nbincode = \"1.2.1\"\nserde_json = \"1.0.46\"\nflate2 = \"1.0\"\nrayon = \"1.3.0\"\nfnv = \"1.0.6\"\n\n[dependencies.imgui-winit-support]\nversion = \"0.2.1\"\ndefault-features = false\nfeatures = [\"winit-20\"]\n\n[[bin]]\nname = \"oxidator\"\npath = \"src/main.rs\"\n\n[profile.release]\n#lto = true"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Thomas SIMON\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# What is this?\n\nA real time strategy game/engine written with Rust and WebGPU.\nEventually it will be able to run in a web browser thanks to WebGPU.\nThis project is inspired by Total Annihilation, Supreme Commander, [Spring Engine](https://springrts.com/) and [Zero-k](https://zero-k.info/).\n\n## Demo\n\n![Map editor](etc/map_editor.gif)\nMap editor [HQ version](https://streamable.com/9vn0z)\n\n![Unit editor](etc/unit_editor.gif)\nUnit editor [HQ version](https://streamable.com/ywr44)\n\n![Play mode](etc/play.gif)\nGameplay (35000 units) [HQ version](https://streamable.com/499j0)\n\n\n\n## Goal\n\nThe ultimate goal is to provide a modern, carefully crafted, minimal and highly constrained set of tools for players/designers to create mods (game variant) without programming knowledge. \nThose tool would be comprised of :\n- Map editor\n- Unit editor\n    * 3d model import, animation, behavior definition (simple visual programming language, non turing complete), formation ...\n- Mod(set of units) editor\n- Online repository to publish maps, units, and mods.\n- Multiplayer lobby and client/server system\n    * Where player can select a tuple of map and mod to play with others\n    * Aiming for a quite higher latency than usual (100ms + ping) between server and clients\n- High performance multithreaded renderer \n    * Aiming for 100k moving units at 60fps, 1080p with middle-end 2016 hardware\n- High performance multithreaded simulation\n    * same goal than the renderer\n\nAll in one executable.\n\nthe test for this goal would be to be able implement a [Zero-k](https://zero-k.info/) clone in this project, with all its features and play it with 32 players.\n\n## Non-Goals\n\n* General purpose modding/ turing complete scripting language.\n\n* Low latency server: the increase in dev time between 10ms latency and 100ms is huge. Not worth the effort given that I want people from all around the world to play together. Also I want games to be about strategy, not action per minute (I am getting too old for this ^^).\n\n* Hosted simulation server. I am broke, people will have to host their own server to play with others (that would just mean clicking the host button in the multi lobby, and having sufficient bandwidth). the online repository will help with discovery though.  \n\n* Deterministic engine: that makes multithreading less efficient and harder. It has nice properties (low file size for replay, debugging) but for my goals they are not worth their price. It is usually done to make networking easier and extremely low bandwidth (because peers only have to share player inputs). I have a plan to keep 80% of those properties with a non-deterministic engine (for 20% the price in dev time). \n\n* Replacing [Spring Engine](https://springrts.com/): This engine will be far more constrained than spring for performance and time reason. There will not be a scripting language like Lua to mod. However I will make sure everything that has been done in the most popular mods of spring will be doable here. \n\n## Features\n\n- Map editor\n    - [x] raise, lower, flatten, blur, noise pencil\n    - [ ] texture layer\n    - [ ] changeable map size/water level\n    - [ ] resources placing\n    - [ ] save and load from file system (try the current save, load button at your own risk)\n    - [ ] save and load from online repository  \n\n- Unit editor\n    - [x] basic imgui editor to create node hierarchy\n    - [x] joint selection (Fix, weapon aim, wheel)\n    - [x] mesh selection\n    - [x] parameter editing (speed, turn rate, health...)\n    - [ ] save/load from filesystem\n    - [ ] graphical editor\n    - [ ] integration with online repository\n\n- Mod editor\n    - [ ] N/A\n\n- Online repository\n    - [ ] N/A\n\n- Multiplayer\n    - [x] working PoC localhost tcp client/server (1/2 will fry your computer and consume 1 Mo/s) \n    - [ ] optimise to reach 300 Ko/sec with 100k units moving\n    - [ ] lobby\n    - [ ] live swapping host if current host disconnect\n    - [ ] simple chat\n    - [ ] ability to draw on the map, and tag place/units\n- Rendering \n    - [x] basic display of 3D models (with instancing)\n    - [x] basic display a heightmap (from [this blog](http://casual-effects.blogspot.com/2014/04/fast-terrain-rendering-with-continuous.html)) \n    - [x] fxaa (from [this blog](http://blog.simonrodriguez.fr/articles/30-07-2016_implementing_fxaa.html))\n    - [x] screen space reflection for water\n    - [ ] materials\n    - [ ] particles\n    - [ ] sounds\n    - [ ] animation system\n    - [ ] inverted kinematics\n\n- Simulation \n    - [x] working draft of collision detection\n    - [x] working draft of flock behavior\n    - [x] basic health and damage computation\n    - [x] construction and repair\n    - [ ] detection (visual and radar)\n    - [ ] user-defined AI for units (follow target, formation, flee, target selection etc)\n    - [ ] resource counting\n    - [ ] integrating pathfinding (I already built a working flowfield pathfinding [here](https://github.com/Ruddle/rustfield))\n    \n- UI\n    - [x] select units (picking and rectangle selection)\n    - [x] give move order\n    - [x] give build order\n    - [x] display current order (Hold LShift)\n    - [ ] give user defined, unit specific order\n    - [ ] display info about game state (current resources etc)\n    - [ ] display info about selected units\n    - [ ] display statistics\n\n\n## Supported platforms\n\n * Windows (dx12 and vulkan)\n * Linux (vulkan)\n * Mac Untested (*should just work TM*)\n\nAll thanks to WebGPU and [wgpu-rs](https://github.com/gfx-rs/wgpu-rs) (and [winit](https://github.com/rust-windowing/winit)). No efforts was made by me in this regard. If anything I posted a stupid issue there that was solved in 0.03 second.\n \n## Build\n\n```text\ngit clone https://github.com/Ruddle/oxidator\ncd oxidator\ncargo run --release\n```\n\n## Fun stuff if you clone this\n\nShaders are automatically hot-reloaded if you change any .frag or .vert file and you compiled with either shaderc \n```text\ncargo run --features use_shaderc --release\n```\nor glsl_to_spirv\n```text\ncargo run --features use_glsl_to_spirv --release\n```\n\nLogs can be enabled via the [environment variable RUST_LOG](https://github.com/sebasmagri/env_logger/).\nfor instance:\n```text\nRUST_LOG=oxidator=debug cargo run --release\n```\n\nMy go-to command when I develop:\n```text\nRUST_LOG=oxidator=debug cargo run --features use_shaderc --release\n```\n\nYou can also compile all the shaders to spirv without starting the full application. The compiled shaders will be saved in /src/shader/compiled. This folder is used when no shader compiler flag is enabled.\n\n```text\ncargo run --features use_shaderc --release compile\n```\n\n\n## Roadmap\n\nI push features that I feel like pushing in the moment. \nThis project could (and probably will) lose its only contributor any time before any playable milestone is reached."
  },
  {
    "path": "src/asset/3d/arrow.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: ''\n# www.blender.org\nmtllib arrow.mtl\no Cube\nv -0.000000 0.020000 1.000000\nv -0.000000 -0.020000 1.000000\nv 0.200000 0.020000 -1.000000\nv 0.200000 -0.020000 -1.000000\nv -0.200000 0.020000 -1.000000\nv -0.200000 -0.020000 -1.000000\nv 0.631076 -0.020000 0.361554\nv -0.648924 0.020000 0.361554\nv -0.648924 -0.020000 0.361554\nv 0.631076 0.020000 0.361554\nv 0.200000 -0.020000 0.359655\nv -0.200000 0.020000 0.359655\nv -0.200000 -0.020000 0.359655\nv 0.200000 0.020000 0.359655\nvt 0.455043 0.250000\nvt 0.625000 0.000000\nvt 0.455043 0.000000\nvt 0.375000 0.500000\nvt 0.625000 0.330043\nvt 0.375000 0.330043\nvt 0.375000 0.750000\nvt 0.625000 0.500000\nvt 0.576873 0.750000\nvt 0.576873 1.000000\nvt 0.625000 1.000000\nvt 0.625000 0.750000\nvt 0.875000 0.701874\nvt 0.625000 0.701874\nvt 0.875000 0.669957\nvt 0.625000 0.669957\nvt 0.544957 1.000000\nvt 0.544957 0.750000\nvt 0.375000 0.298127\nvt 0.625000 0.250000\nvt 0.375000 0.250000\nvt 0.375000 0.000000\nvt 0.423127 0.250000\nvt 0.423127 0.000000\nvt 0.375000 1.000000\nvt 0.875000 0.500000\nvt 0.625000 0.500000\nvt 0.625000 0.298127\nvt 0.625000 0.250000\nvt 0.625000 0.750000\nvt 0.875000 0.750000\nvn 0.0000 -1.0000 0.0000\nvn 1.0000 0.0000 0.0000\nvn 0.0000 0.0000 1.0000\nvn 0.0000 1.0000 0.0000\nvn -0.7112 0.0000 -0.7030\nvn -0.0044 0.0000 1.0000\nvn 0.7013 0.0000 -0.7128\nvn -1.0000 0.0000 0.0000\nvn 0.0042 0.0000 1.0000\nusemtl Material\ns off\nf 12/1/1 3/2/1 14/3/1\nf 6/4/2 12/5/2 13/6/2\nf 4/7/3 5/8/3 6/4/3\nf 7/9/4 9/10/4 2/11/4\nf 2/12/5 10/13/5 7/14/5\nf 7/14/6 14/15/6 11/16/6\nf 13/17/4 7/9/4 11/18/4\nf 9/19/7 1/20/7 2/21/7\nf 1/22/1 8/23/1 10/24/1\nf 6/25/4 11/18/4 4/7/4\nf 11/16/8 3/26/8 4/27/8\nf 13/6/9 8/28/9 9/19/9\nf 10/24/1 12/1/1 14/3/1\nf 12/1/1 5/29/1 3/2/1\nf 6/4/2 5/8/2 12/5/2\nf 4/7/3 3/30/3 5/8/3\nf 2/12/5 1/31/5 10/13/5\nf 7/14/6 10/13/6 14/15/6\nf 13/17/4 9/10/4 7/9/4\nf 9/19/7 8/28/7 1/20/7\nf 6/25/4 13/17/4 11/18/4\nf 11/16/8 14/15/8 3/26/8\nf 13/6/9 12/5/9 8/28/9\nf 10/24/1 8/23/1 12/1/1\n"
  },
  {
    "path": "src/asset/3d/axis_debug.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: 'axis_debug.blend'\n# www.blender.org\no Cylinder.002\nv -0.078259 0.000390 0.500086\nv -0.078259 0.000390 1.000086\nv -0.039173 0.068088 0.500086\nv -0.039173 0.068088 1.000086\nv 0.038998 0.068088 0.500086\nv 0.038998 0.068088 1.000086\nv 0.078084 0.000390 0.500086\nv 0.078084 0.000390 1.000086\nv 0.038998 -0.067308 0.500086\nv 0.038998 -0.067308 1.000086\nv -0.039173 -0.067308 0.500086\nv -0.039173 -0.067308 1.000086\nv 0.499912 0.500390 0.500086\nv 0.499912 0.500390 -0.499914\nv 0.499912 -0.499610 0.500086\nv 0.499912 -0.499610 -0.499914\nv -0.500088 0.500390 0.500086\nv -0.500088 0.500390 -0.499914\nv -0.500088 -0.499610 0.500086\nv -0.500088 -0.499610 -0.499914\nv 0.499912 0.078561 0.000086\nv 0.999912 0.078561 0.000086\nv 0.499912 0.039476 -0.067612\nv 0.999912 0.039476 -0.067612\nv 0.499912 -0.038696 -0.067612\nv 0.999912 -0.038696 -0.067612\nv 0.499912 -0.077781 0.000086\nv 0.999912 -0.077781 0.000086\nv 0.499912 -0.038696 0.067785\nv 0.999912 -0.038696 0.067785\nv 0.499912 0.039476 0.067785\nv 0.999912 0.039476 0.067785\nv -0.078259 0.500390 0.000086\nv -0.078259 1.000390 0.000086\nv -0.039173 0.500390 -0.067612\nv -0.039173 1.000390 -0.067612\nv 0.038998 0.500390 -0.067612\nv 0.038998 1.000390 -0.067612\nv 0.078084 0.500390 0.000086\nv 0.078084 1.000390 0.000086\nv 0.038998 0.500390 0.067785\nv 0.038998 1.000390 0.067785\nv -0.039173 0.500390 0.067785\nv -0.039173 1.000390 0.067785\nv 0.554715 0.440554 -0.412883\nv 0.554715 0.294033 -0.412883\nv 0.554715 0.005957 -0.062722\nv 0.554715 -0.285843 -0.412883\nv 0.554715 -0.433606 -0.412883\nv 0.554715 -0.068545 0.025439\nv 0.554715 -0.407530 0.433960\nv 0.554715 -0.259768 0.433960\nv 0.554715 0.005957 0.111117\nv 0.554715 0.270440 0.433960\nv 0.554715 0.416961 0.433960\nv 0.554715 0.079218 0.025439\nv 0.356042 0.440554 -0.412883\nv 0.356042 0.294033 -0.412883\nv 0.356042 0.005957 -0.062722\nv 0.356042 -0.285843 -0.412883\nv 0.356042 -0.433606 -0.412883\nv 0.356042 -0.068545 0.025439\nv 0.356042 -0.407530 0.433960\nv 0.356042 -0.259768 0.433960\nv 0.356042 0.005957 0.111117\nv 0.356042 0.270440 0.433960\nv 0.356042 0.416961 0.433960\nv 0.356042 0.079218 0.025439\nv 0.356042 0.440554 -0.412883\nv 0.368459 0.466882 -0.425300\nv 0.356042 0.294033 -0.412883\nv 0.368459 0.288169 -0.425300\nv 0.356042 0.005957 -0.062722\nv 0.368459 0.005896 -0.082192\nv 0.356042 -0.285843 -0.412883\nv 0.368459 -0.280028 -0.425300\nv 0.356042 -0.433606 -0.412883\nv 0.368459 -0.460108 -0.425300\nv 0.356042 -0.068545 0.025439\nv 0.368459 -0.084692 0.025454\nv 0.356042 -0.407530 0.433960\nv 0.368459 -0.433969 0.446377\nv 0.356042 -0.259768 0.433960\nv 0.368459 -0.253906 0.446377\nv 0.356042 0.005957 0.111117\nv 0.368459 0.005935 0.130683\nv 0.356042 0.270440 0.433960\nv 0.368459 0.264561 0.446377\nv 0.356042 0.416961 0.433960\nv 0.368459 0.443338 0.446377\nv 0.356042 0.079218 0.025439\nv 0.368459 0.095319 0.025428\nv 0.368459 0.466882 -0.425300\nv 0.542298 0.466882 -0.425300\nv 0.368459 0.288169 -0.425300\nv 0.542298 0.288169 -0.425300\nv 0.368459 0.005896 -0.082192\nv 0.542298 0.005896 -0.082192\nv 0.368459 -0.280028 -0.425300\nv 0.542298 -0.280028 -0.425300\nv 0.368459 -0.460108 -0.425300\nv 0.542298 -0.460108 -0.425300\nv 0.368459 -0.084692 0.025454\nv 0.542298 -0.084693 0.025454\nv 0.368459 -0.433969 0.446377\nv 0.542298 -0.433969 0.446377\nv 0.368459 -0.253906 0.446377\nv 0.542298 -0.253906 0.446377\nv 0.368459 0.005935 0.130683\nv 0.542298 0.005935 0.130683\nv 0.368459 0.264561 0.446377\nv 0.542298 0.264561 0.446377\nv 0.368459 0.443338 0.446377\nv 0.542298 0.443338 0.446377\nv 0.368459 0.095319 0.025428\nv 0.542298 0.095319 0.025428\nv 0.542298 0.466882 -0.425300\nv 0.554715 0.440554 -0.412883\nv 0.542298 0.288169 -0.425300\nv 0.554715 0.294033 -0.412883\nv 0.542298 0.005896 -0.082192\nv 0.554715 0.005957 -0.062722\nv 0.542298 -0.280028 -0.425300\nv 0.554715 -0.285843 -0.412883\nv 0.542298 -0.460108 -0.425300\nv 0.554715 -0.433606 -0.412883\nv 0.542298 -0.084693 0.025454\nv 0.554715 -0.068545 0.025439\nv 0.542298 -0.433969 0.446377\nv 0.554715 -0.407530 0.433960\nv 0.542298 -0.253906 0.446377\nv 0.554715 -0.259768 0.433960\nv 0.542298 0.005935 0.130683\nv 0.554715 0.005957 0.111117\nv 0.542298 0.264561 0.446377\nv 0.554715 0.270440 0.433960\nv 0.542298 0.443338 0.446377\nv 0.554715 0.416961 0.433960\nv 0.542298 0.095319 0.025428\nv 0.554715 0.079218 0.025439\nv -0.376351 0.550070 0.415498\nv -0.062199 0.550070 0.015669\nv -0.062199 0.550070 -0.431345\nv 0.059488 0.550070 -0.431345\nv 0.059488 0.550070 0.016911\nv 0.373639 0.550070 0.415498\nv 0.227118 0.550070 0.415498\nv -0.000114 0.550070 0.123697\nv -0.229830 0.550070 0.415498\nv -0.376351 0.351397 0.415498\nv -0.062199 0.351397 0.015669\nv -0.062199 0.351397 -0.431345\nv 0.059488 0.351397 -0.431345\nv 0.059488 0.351397 0.016910\nv 0.373639 0.351397 0.415498\nv 0.227118 0.351397 0.415498\nv -0.000114 0.351397 0.123697\nv -0.229830 0.351397 0.415498\nv -0.376351 0.351397 0.415498\nv -0.401898 0.363814 0.427915\nv -0.062199 0.351397 0.015669\nv -0.074617 0.363814 0.011374\nv -0.062199 0.351397 -0.431345\nv -0.074617 0.363814 -0.443762\nv 0.059488 0.351397 -0.431345\nv 0.071905 0.363814 -0.443762\nv 0.059488 0.351397 0.016910\nv 0.071905 0.363814 0.012605\nv 0.373639 0.351397 0.415498\nv 0.399236 0.363814 0.427915\nv 0.227118 0.351397 0.415498\nv 0.221049 0.363814 0.427915\nv -0.000114 0.351397 0.123697\nv -0.000167 0.363814 0.143839\nv -0.229830 0.351397 0.415498\nv -0.223802 0.363814 0.427915\nv -0.401898 0.363814 0.427915\nv -0.401898 0.537653 0.427915\nv -0.074617 0.363814 0.011374\nv -0.074617 0.537653 0.011374\nv -0.074617 0.363814 -0.443762\nv -0.074616 0.537653 -0.443762\nv 0.071905 0.363814 -0.443762\nv 0.071905 0.537653 -0.443762\nv 0.071905 0.363814 0.012605\nv 0.071905 0.537653 0.012605\nv 0.399236 0.363814 0.427915\nv 0.399236 0.537653 0.427915\nv 0.221049 0.363814 0.427915\nv 0.221049 0.537653 0.427915\nv -0.000167 0.363814 0.143839\nv -0.000167 0.537653 0.143839\nv -0.223802 0.363814 0.427915\nv -0.223802 0.537653 0.427915\nv -0.401898 0.537653 0.427915\nv -0.376351 0.550070 0.415498\nv -0.074617 0.537653 0.011374\nv -0.062199 0.550070 0.015669\nv -0.074616 0.537653 -0.443762\nv -0.062199 0.550070 -0.431345\nv 0.071905 0.537653 -0.443762\nv 0.059488 0.550070 -0.431345\nv 0.071905 0.537653 0.012605\nv 0.059488 0.550070 0.016911\nv 0.399236 0.537653 0.427915\nv 0.373639 0.550070 0.415498\nv 0.221049 0.537653 0.427915\nv 0.227118 0.550070 0.415498\nv -0.000167 0.537653 0.143839\nv -0.000114 0.550070 0.123697\nv -0.223802 0.537653 0.427915\nv -0.229830 0.550070 0.415498\nv 0.349194 -0.424488 0.550865\nv -0.380928 -0.424488 0.550865\nv 0.157972 0.313085 0.550865\nv -0.380928 0.313085 0.550865\nv -0.380928 0.422355 0.550865\nv 0.382720 0.422355 0.550865\nv -0.156179 -0.315218 0.550865\nv 0.349194 -0.315218 0.550865\nv 0.349194 -0.424488 0.352193\nv -0.380928 -0.424488 0.352193\nv 0.157972 0.313085 0.352193\nv -0.380928 0.313085 0.352193\nv -0.380928 0.422355 0.352193\nv 0.382720 0.422355 0.352193\nv -0.156179 -0.315218 0.352193\nv 0.349194 -0.315218 0.352193\nv 0.349194 -0.424488 0.352193\nv 0.361611 -0.436905 0.364610\nv -0.380928 -0.424488 0.352193\nv -0.405379 -0.436905 0.364610\nv 0.157972 0.313085 0.352193\nv 0.133521 0.300668 0.364610\nv -0.380928 0.313085 0.352193\nv -0.393345 0.300668 0.364610\nv -0.380928 0.422355 0.352193\nv -0.393345 0.434772 0.364610\nv 0.382720 0.422355 0.352193\nv 0.407171 0.434772 0.364610\nv -0.156179 -0.315218 0.352193\nv -0.131729 -0.302801 0.364610\nv 0.349194 -0.315218 0.352193\nv 0.361611 -0.302801 0.364610\nv 0.361611 -0.436905 0.364610\nv 0.361611 -0.436905 0.538448\nv -0.405379 -0.436905 0.364610\nv -0.405379 -0.436905 0.538448\nv 0.133521 0.300668 0.364610\nv 0.133521 0.300668 0.538448\nv -0.393345 0.300668 0.364610\nv -0.393345 0.300668 0.538448\nv -0.393345 0.434772 0.364610\nv -0.393345 0.434772 0.538448\nv 0.407171 0.434772 0.364610\nv 0.407171 0.434772 0.538448\nv -0.131729 -0.302801 0.364610\nv -0.131729 -0.302801 0.538448\nv 0.361611 -0.302801 0.364610\nv 0.361611 -0.302801 0.538448\nv 0.361611 -0.436905 0.538448\nv 0.349194 -0.424488 0.550865\nv -0.405379 -0.436905 0.538448\nv -0.380928 -0.424488 0.550865\nv 0.133521 0.300668 0.538448\nv 0.157972 0.313085 0.550865\nv -0.393345 0.300668 0.538448\nv -0.380928 0.313085 0.550865\nv -0.393345 0.434772 0.538448\nv -0.380928 0.422355 0.550865\nv 0.407171 0.434772 0.538448\nv 0.382720 0.422355 0.550865\nv -0.131729 -0.302801 0.538448\nv -0.156179 -0.315218 0.550865\nv 0.361611 -0.302801 0.538448\nv 0.349194 -0.315218 0.550865\nvt 0.831726 0.844892\nvt 0.841085 0.823278\nvt 0.831726 0.823278\nvt 0.892429 0.844892\nvt 0.881622 0.823278\nvt 0.892429 0.823278\nvt 0.841085 0.844892\nvt 0.831726 0.823278\nvt 0.841085 0.823278\nvt 0.831726 0.844892\nvt 0.822366 0.823278\nvt 0.822366 0.832556\nvt 0.831726 0.827152\nvt 0.841085 0.832556\nvt 0.881622 0.844892\nvt 0.892429 0.823278\nvt 0.881622 0.823278\nvt 0.822366 0.844892\nvt 0.822366 0.823278\nvt 0.831726 0.827152\nvt 0.822366 0.832556\nvt 0.822366 0.843363\nvt 0.225312 0.847355\nvt 0.114117 0.736160\nvt 0.225312 0.736160\nvt 0.833085 0.104738\nvt 0.936375 0.072440\nvt 0.833085 0.072440\nvt 0.060544 0.238768\nvt 0.147219 0.066745\nvt 0.147219 0.238767\nvt 0.900850 0.768835\nvt 0.762601 0.907084\nvt 0.900850 0.907084\nvt 0.140228 0.927705\nvt 0.061206 0.902996\nvt 0.140228 0.902996\nvt 0.269790 0.745180\nvt 0.158595 0.779949\nvt 0.158595 0.745180\nvt 0.190557 0.066745\nvt 0.202501 0.152756\nvt 0.190557 0.152756\nvt 0.202501 0.066745\nvt 0.223506 0.152756\nvt 0.223506 0.066745\nvt 0.232567 0.152756\nvt 0.190557 0.152756\nvt 0.179722 0.066745\nvt 0.190557 0.066745\nvt 0.158955 0.159480\nvt 0.164822 0.152756\nvt 0.170690 0.159480\nvt 0.179722 0.152756\nvt 0.158054 0.066745\nvt 0.158054 0.152756\nvt 0.147219 0.066745\nvt 0.147219 0.159480\nvt 0.158955 0.172927\nvt 0.147219 0.172927\nvt 0.895059 0.123781\nvt 0.946704 0.119744\nvt 0.895059 0.119744\nvt 0.895059 0.111669\nvt 0.946704 0.107632\nvt 0.895059 0.107632\nvt 0.895059 0.111669\nvt 0.880693 0.090775\nvt 0.876656 0.088589\nvt 0.880693 0.086402\nvt 0.946704 0.111669\nvt 0.895059 0.119744\nvt 0.946704 0.119744\nvt 0.880693 0.090775\nvt 0.888768 0.090775\nvt 0.888768 0.086402\nvt 0.114540 0.172228\nvt 0.084700 0.271120\nvt 0.061642 0.282111\nvt 0.126166 0.184444\nvt 0.190303 0.220783\nvt 0.167439 0.231681\nvt 0.137598 0.161237\nvt 0.057573 0.108538\nvt 0.126166 0.148415\nvt 0.193984 0.043516\nvt 0.080631 0.097547\nvt 0.171120 0.054414\nvt 0.118423 0.284705\nvt 0.148263 0.185814\nvt 0.095365 0.295696\nvt 0.159889 0.198029\nvt 0.224026 0.234368\nvt 0.201162 0.245267\nvt 0.171322 0.174823\nvt 0.091296 0.122124\nvt 0.159889 0.162000\nvt 0.227707 0.057101\nvt 0.114354 0.111133\nvt 0.204843 0.068000\nvt 0.600272 0.443762\nvt 0.571818 0.514908\nvt 0.546948 0.504889\nvt 0.610389 0.469969\nvt 0.674252 0.556174\nvt 0.649382 0.546155\nvt 0.620928 0.451825\nvt 0.600272 0.350859\nvt 0.620927 0.359180\nvt 0.540816 0.529686\nvt 0.569270 0.458539\nvt 0.515945 0.519666\nvt 0.579387 0.484747\nvt 0.643250 0.570952\nvt 0.618379 0.560933\nvt 0.589925 0.466603\nvt 0.569269 0.365636\nvt 0.589925 0.373958\nvt 0.416070 0.598978\nvt 0.522951 0.657033\nvt 0.399019 0.607106\nvt 0.501853 0.633537\nvt 0.546574 0.565319\nvt 0.525476 0.541823\nvt 0.638048 0.602170\nvt 0.655100 0.594043\nvt 0.522951 0.615857\nvt 0.416070 0.557802\nvt 0.399019 0.565930\nvt 0.501853 0.592361\nvt 0.546574 0.524144\nvt 0.525476 0.500647\nvt 0.638048 0.560995\nvt 0.655099 0.552867\nvt 0.841085 0.844892\nvt 0.881622 0.844892\nvt 0.822366 0.844892\nvt 0.841085 0.843363\nvt 0.831726 0.848766\nvt 0.822366 0.843363\nvt 0.892429 0.844892\nvt 0.831726 0.848766\nvt 0.841085 0.843363\nvt 0.841085 0.832556\nvt 0.114117 0.847355\nvt 0.936375 0.104738\nvt 0.060544 0.066745\nvt 0.762601 0.768835\nvt 0.061206 0.927705\nvt 0.269790 0.779949\nvt 0.232567 0.066745\nvt 0.170690 0.172927\nvt 0.158955 0.172927\nvt 0.164822 0.179651\nvt 0.147219 0.152756\nvt 0.153087 0.152756\nvt 0.158955 0.159480\nvt 0.153087 0.179651\nvt 0.946704 0.123781\nvt 0.946704 0.111669\nvt 0.888768 0.086402\nvt 0.892805 0.088589\nvt 0.888768 0.090775\nvt 0.892805 0.088589\nvt 0.880693 0.086402\nvt 0.876656 0.088589\nvt 0.201820 0.065013\nvt 0.227707 0.057101\nvt 0.229708 0.051720\nvt 0.159889 0.162000\nvt 0.204843 0.068000\nvt 0.113154 0.107277\nvt 0.157772 0.157121\nvt 0.091296 0.122124\nvt 0.114354 0.111133\nvt 0.143636 0.186169\nvt 0.085053 0.120672\nvt 0.095365 0.295696\nvt 0.148263 0.185814\nvt 0.117231 0.285994\nvt 0.089132 0.299387\nvt 0.159889 0.198029\nvt 0.118423 0.284705\nvt 0.198136 0.247429\nvt 0.157778 0.201237\nvt 0.224026 0.234368\nvt 0.201162 0.245267\nvt 0.171726 0.172774\nvt 0.226034 0.234131\nvt 0.171322 0.174823\nvt 0.172312 0.053126\nvt 0.229708 0.051720\nvt 0.200200 0.039833\nvt 0.128264 0.145233\nvt 0.201820 0.065013\nvt 0.113154 0.107277\nvt 0.157772 0.157121\nvt 0.055545 0.108785\nvt 0.083646 0.095390\nvt 0.114128 0.174281\nvt 0.085053 0.120672\nvt 0.059624 0.287500\nvt 0.143636 0.186169\nvt 0.087723 0.274106\nvt 0.089132 0.299387\nvt 0.128270 0.189350\nvt 0.117231 0.285994\nvt 0.168629 0.235541\nvt 0.157778 0.201237\nvt 0.196527 0.222243\nvt 0.198136 0.247429\nvt 0.142219 0.160886\nvt 0.226034 0.234131\nvt 0.171726 0.172774\nvt 0.193984 0.043516\nvt 0.172312 0.053126\nvt 0.200200 0.039833\nvt 0.126166 0.148415\nvt 0.171120 0.054414\nvt 0.083646 0.095390\nvt 0.128264 0.145233\nvt 0.057573 0.108538\nvt 0.080631 0.097547\nvt 0.114128 0.174281\nvt 0.055545 0.108785\nvt 0.061642 0.282111\nvt 0.114540 0.172228\nvt 0.087723 0.274106\nvt 0.059624 0.287500\nvt 0.126166 0.184444\nvt 0.084700 0.271120\nvt 0.168629 0.235541\nvt 0.128270 0.189350\nvt 0.190303 0.220783\nvt 0.167439 0.231681\nvt 0.142219 0.160886\nvt 0.196527 0.222243\nvt 0.137598 0.161237\nvt 0.593970 0.465639\nvt 0.643250 0.570952\nvt 0.649524 0.574349\nvt 0.589925 0.373958\nvt 0.589925 0.466603\nvt 0.593970 0.371310\nvt 0.569269 0.365636\nvt 0.569100 0.455875\nvt 0.569099 0.361290\nvt 0.515945 0.519666\nvt 0.569270 0.458539\nvt 0.543784 0.531751\nvt 0.513538 0.519566\nvt 0.579387 0.484747\nvt 0.540816 0.529686\nvt 0.619293 0.562170\nvt 0.581333 0.488002\nvt 0.618379 0.560933\nvt 0.621098 0.452708\nvt 0.649524 0.574349\nvt 0.676651 0.561418\nvt 0.593970 0.371310\nvt 0.593970 0.465639\nvt 0.596227 0.348360\nvt 0.621097 0.358379\nvt 0.596227 0.442944\nvt 0.569099 0.361290\nvt 0.540665 0.506635\nvt 0.569100 0.455875\nvt 0.570911 0.518820\nvt 0.513538 0.519566\nvt 0.581333 0.488002\nvt 0.543784 0.531751\nvt 0.646421 0.549240\nvt 0.608460 0.475071\nvt 0.619293 0.562170\nvt 0.674252 0.556174\nvt 0.621098 0.452708\nvt 0.676651 0.561418\nvt 0.620927 0.359180\nvt 0.620928 0.451825\nvt 0.600272 0.350859\nvt 0.621097 0.358379\nvt 0.596227 0.442944\nvt 0.596227 0.348360\nvt 0.546948 0.504889\nvt 0.600272 0.443762\nvt 0.570911 0.518820\nvt 0.540665 0.506635\nvt 0.610389 0.469969\nvt 0.571818 0.514908\nvt 0.646421 0.549240\nvt 0.608460 0.475071\nvt 0.649382 0.546155\nvt 0.394973 0.568578\nvt 0.522951 0.615857\nvt 0.399019 0.565930\nvt 0.548787 0.529313\nvt 0.525163 0.621026\nvt 0.638048 0.560995\nvt 0.546574 0.524144\nvt 0.638218 0.565341\nvt 0.655099 0.552867\nvt 0.659145 0.555366\nvt 0.525476 0.500647\nvt 0.499640 0.592339\nvt 0.523264 0.500625\nvt 0.416070 0.557802\nvt 0.501853 0.592361\nvt 0.089836 0.283127\nvt 0.110933 0.272352\nvt 0.110763 0.273152\nvt 0.525164 0.657055\nvt 0.394973 0.568578\nvt 0.394973 0.604607\nvt 0.548787 0.565342\nvt 0.525163 0.621026\nvt 0.638218 0.601370\nvt 0.548787 0.529313\nvt 0.659145 0.591395\nvt 0.638218 0.565341\nvt 0.523264 0.536654\nvt 0.659145 0.555366\nvt 0.499640 0.628367\nvt 0.523264 0.500625\nvt 0.415900 0.594632\nvt 0.499640 0.592339\nvt 0.415900 0.558603\nvt 0.522951 0.657033\nvt 0.394973 0.604607\nvt 0.399019 0.607106\nvt 0.548787 0.565342\nvt 0.525164 0.657055\nvt 0.638048 0.602170\nvt 0.546574 0.565319\nvt 0.655100 0.594043\nvt 0.638218 0.601370\nvt 0.525476 0.541823\nvt 0.659145 0.591395\nvt 0.499640 0.628367\nvt 0.523264 0.536654\nvt 0.416070 0.598978\nvt 0.501853 0.633537\nvt 0.415900 0.594632\nvt 0.415900 0.558603\nvt 0.093881 0.280479\nvn -0.8660 0.5000 0.0000\nvn 0.0000 1.0000 -0.0000\nvn 0.8660 0.5000 -0.0000\nvn 0.8660 -0.5000 -0.0000\nvn -0.0000 0.0000 1.0000\nvn 0.0000 -1.0000 0.0000\nvn -0.8660 -0.5000 0.0000\nvn 0.0000 0.0000 -1.0000\nvn -1.0000 0.0000 -0.0000\nvn 1.0000 -0.0000 0.0000\nvn 0.0000 0.8660 -0.5000\nvn 0.0000 -0.8660 -0.5000\nvn -0.0000 -0.8660 0.5000\nvn -0.0000 0.8660 0.5000\nvn -0.8660 0.0000 -0.5000\nvn 0.8660 -0.0000 -0.5000\nvn 0.8660 -0.0000 0.5000\nvn -0.8660 0.0000 0.5000\nvn -0.7417 -0.2864 -0.6065\nvn -0.9198 0.3549 -0.1674\nvn -0.8431 -0.0017 -0.5377\nvn -0.7412 0.2847 -0.6079\nvn -0.9206 -0.3537 -0.1657\nvn -0.7927 -0.6096 0.0005\nvn -0.9203 -0.3541 0.1663\nvn -0.7417 0.2863 0.6065\nvn -0.8443 -0.0006 0.5358\nvn -0.7419 -0.2869 0.6060\nvn -0.9200 0.3545 0.1669\nvn -0.7919 0.6106 -0.0004\nvn 0.0000 -0.4270 -0.9042\nvn 0.0000 0.9044 -0.4266\nvn 0.0000 -0.0031 -1.0000\nvn 0.0000 0.4241 -0.9056\nvn 0.0000 -0.9055 -0.4243\nvn 0.0000 -1.0000 0.0009\nvn 0.0000 -0.9051 0.4251\nvn 0.0000 0.4269 0.9043\nvn 0.0000 -0.0011 1.0000\nvn 0.0000 -0.4279 0.9038\nvn 0.0000 0.9048 0.4259\nvn 0.0000 1.0000 -0.0007\nvn 0.9198 0.3549 -0.1673\nvn 0.7417 -0.2864 -0.6065\nvn 0.9198 0.3549 -0.1674\nvn 0.8431 -0.0017 -0.5377\nvn 0.7412 0.2847 -0.6079\nvn 0.9206 -0.3537 -0.1657\nvn 0.7927 -0.6096 0.0005\nvn 0.9203 -0.3541 0.1663\nvn 0.7417 0.2863 0.6065\nvn 0.8443 -0.0006 0.5358\nvn 0.7419 -0.2869 0.6060\nvn 0.9200 0.3545 0.1669\nvn 0.7919 0.6106 -0.0004\nvn -0.6491 -0.7268 -0.2245\nvn -0.3602 -0.9163 0.1751\nvn -0.4082 -0.8165 -0.4082\nvn 0.4082 -0.8165 -0.4082\nvn 0.6489 -0.7269 -0.2250\nvn 0.3599 -0.9165 0.1746\nvn -0.2934 -0.7439 0.6005\nvn -0.0014 -0.8512 0.5248\nvn 0.2921 -0.7434 0.6016\nvn -0.9451 0.0000 -0.3269\nvn -0.8994 0.0000 0.4371\nvn -0.7071 0.0000 -0.7071\nvn 0.7071 0.0000 -0.7071\nvn 0.9448 0.0000 -0.3276\nvn 0.8997 0.0000 0.4364\nvn -0.4391 0.0000 0.8984\nvn -0.0026 0.0000 1.0000\nvn 0.4367 0.0000 0.8996\nvn -0.3602 0.9163 0.1751\nvn -0.6491 0.7268 -0.2245\nvn -0.4082 0.8165 -0.4082\nvn 0.4082 0.8165 -0.4082\nvn 0.6489 0.7269 -0.2250\nvn 0.3599 0.9165 0.1746\nvn -0.2934 0.7439 0.6005\nvn -0.0014 0.8512 0.5248\nvn 0.2921 0.7434 0.6016\nvn 0.4082 -0.4082 -0.8165\nvn -0.3677 -0.1867 -0.9109\nvn -0.4082 -0.4082 -0.8165\nvn -0.4082 0.4082 -0.8165\nvn 0.3677 0.1867 -0.9109\nvn 0.4082 0.4082 -0.8165\nvn -0.8916 -0.4528 0.0000\nvn 0.7071 -0.7071 0.0000\nvn -0.7071 -0.7071 0.0000\nvn -0.7071 0.7071 0.0000\nvn 0.8916 0.4528 0.0000\nvn 0.7071 0.7071 0.0000\nvn -0.3677 -0.1867 0.9109\nvn 0.4082 -0.4082 0.8165\nvn -0.3677 -0.1868 0.9109\nvn -0.4082 -0.4082 0.8165\nvn -0.4082 0.4082 0.8165\nvn 0.3677 0.1867 0.9109\nvn 0.4082 0.4082 0.8165\ns off\nf 2/1/1 3/2/1 1/3/1\nf 4/4/2 5/5/2 3/6/2\nf 6/7/3 7/8/3 5/9/3\nf 8/10/4 9/11/4 7/8/4\nf 10/12/5 8/13/5 6/14/5\nf 10/15/6 11/16/6 9/17/6\nf 12/18/7 1/3/7 11/19/7\nf 7/20/8 9/21/8 11/22/8\nf 17/23/5 15/24/5 13/25/5\nf 15/26/6 20/27/6 16/28/6\nf 19/29/9 18/30/9 20/31/9\nf 14/32/8 20/33/8 18/34/8\nf 13/35/10 16/36/10 14/37/10\nf 18/38/2 13/39/2 14/40/2\nf 22/41/11 23/42/11 21/43/11\nf 24/44/8 25/45/8 23/42/8\nf 26/46/12 27/47/12 25/45/12\nf 28/48/13 29/49/13 27/50/13\nf 30/51/10 28/52/10 26/53/10\nf 30/54/5 31/55/5 29/49/5\nf 32/56/14 21/57/14 31/55/14\nf 31/58/9 25/59/9 29/60/9\nf 33/61/15 36/62/15 35/63/15\nf 36/62/8 37/64/8 35/63/8\nf 37/64/16 40/65/16 39/66/16\nf 40/65/17 41/67/17 39/66/17\nf 42/68/2 40/69/2 38/70/2\nf 42/71/5 43/72/5 41/67/5\nf 44/73/18 33/61/18 43/72/18\nf 41/74/6 43/75/6 35/76/6\nf 50/77/10 52/78/10 51/79/10\nf 50/77/10 53/80/10 52/78/10\nf 53/80/10 55/81/10 54/82/10\nf 53/80/10 56/83/10 55/81/10\nf 50/77/10 56/83/10 53/80/10\nf 49/84/10 56/83/10 50/77/10\nf 49/84/10 47/85/10 56/83/10\nf 47/85/10 45/86/10 56/83/10\nf 49/84/10 48/87/10 47/85/10\nf 46/88/10 45/86/10 47/85/10\nf 64/89/9 62/90/9 63/91/9\nf 65/92/9 62/90/9 64/89/9\nf 67/93/9 65/92/9 66/94/9\nf 68/95/9 65/92/9 67/93/9\nf 68/95/9 62/90/9 65/92/9\nf 68/95/9 61/96/9 62/90/9\nf 59/97/9 61/96/9 68/95/9\nf 57/98/9 59/97/9 68/95/9\nf 60/99/9 61/96/9 59/97/9\nf 57/98/9 58/100/9 59/97/9\nf 145/101/2 147/102/2 146/103/2\nf 145/101/2 148/104/2 147/102/2\nf 148/104/2 141/105/2 149/106/2\nf 148/104/2 142/107/2 141/105/2\nf 145/101/2 142/107/2 148/104/2\nf 144/108/2 142/107/2 145/101/2\nf 144/108/2 143/109/2 142/107/2\nf 156/110/6 154/111/6 155/112/6\nf 157/113/6 154/111/6 156/110/6\nf 150/114/6 157/113/6 158/115/6\nf 151/116/6 157/113/6 150/114/6\nf 151/116/6 154/111/6 157/113/6\nf 151/116/6 153/117/6 154/111/6\nf 152/118/6 153/117/6 151/116/6\nf 220/119/5 214/120/5 213/121/5\nf 220/119/5 219/122/5 214/120/5\nf 219/122/5 215/123/5 214/120/5\nf 218/124/5 215/123/5 219/122/5\nf 218/124/5 216/125/5 215/123/5\nf 218/124/5 217/126/5 216/125/5\nf 222/127/8 228/128/8 221/129/8\nf 227/130/8 228/128/8 222/127/8\nf 223/131/8 227/130/8 222/127/8\nf 223/131/8 226/132/8 227/130/8\nf 224/133/8 226/132/8 223/131/8\nf 225/134/8 226/132/8 224/133/8\nf 2/1/1 4/135/1 3/2/1\nf 4/4/2 6/136/2 5/5/2\nf 6/7/3 8/10/3 7/8/3\nf 8/10/4 10/137/4 9/11/4\nf 6/14/5 4/138/5 2/139/5\nf 2/139/5 12/140/5 6/14/5\nf 12/140/5 10/12/5 6/14/5\nf 10/15/6 12/141/6 11/16/6\nf 12/18/7 2/1/7 1/3/7\nf 11/22/8 1/142/8 3/143/8\nf 3/143/8 5/144/8 11/22/8\nf 5/144/8 7/20/8 11/22/8\nf 17/23/5 19/145/5 15/24/5\nf 15/26/6 19/146/6 20/27/6\nf 19/29/9 17/147/9 18/30/9\nf 14/32/8 16/148/8 20/33/8\nf 13/35/10 15/149/10 16/36/10\nf 18/38/2 17/150/2 13/39/2\nf 22/41/11 24/44/11 23/42/11\nf 24/44/8 26/46/8 25/45/8\nf 26/46/12 28/151/12 27/47/12\nf 28/48/13 30/54/13 29/49/13\nf 26/53/10 24/152/10 32/153/10\nf 24/152/10 22/154/10 32/153/10\nf 32/153/10 30/51/10 26/53/10\nf 30/54/5 32/56/5 31/55/5\nf 32/56/14 22/155/14 21/57/14\nf 31/58/9 21/156/9 23/157/9\nf 23/157/9 25/59/9 31/58/9\nf 25/59/9 27/158/9 29/60/9\nf 33/61/15 34/159/15 36/62/15\nf 36/62/8 38/160/8 37/64/8\nf 37/64/16 38/160/16 40/65/16\nf 40/65/17 42/71/17 41/67/17\nf 38/70/2 36/161/2 42/68/2\nf 36/161/2 34/162/2 44/163/2\nf 42/68/2 36/161/2 44/163/2\nf 42/71/5 44/73/5 43/72/5\nf 44/73/18 34/159/18 33/61/18\nf 43/75/6 33/164/6 35/76/6\nf 35/76/6 37/165/6 41/74/6\nf 37/165/6 39/166/6 41/74/6\ns 1\nf 72/167/19 69/168/20 70/169/20\nf 72/167/19 73/170/21 71/171/19\nf 76/172/22 73/170/21 74/173/21\nf 76/172/22 77/174/23 75/175/22\nf 80/176/24 77/174/23 78/177/23\nf 80/176/24 81/178/25 79/179/24\nf 84/180/26 81/178/25 82/181/25\nf 84/180/26 85/182/27 83/183/26\nf 88/184/28 85/182/27 86/185/27\nf 88/184/28 89/186/29 87/187/28\nf 92/188/30 89/186/29 90/189/29\nf 92/188/30 69/168/20 91/190/30\nf 96/191/31 93/192/32 94/193/32\nf 98/194/33 95/195/31 96/191/31\nf 98/194/33 99/196/34 97/197/33\nf 102/198/35 99/196/34 100/199/34\nf 104/200/36 101/201/35 102/198/35\nf 106/202/37 103/203/36 104/200/36\nf 108/204/38 105/205/37 106/202/37\nf 110/206/39 107/207/38 108/204/38\nf 112/208/40 109/209/39 110/206/39\nf 114/210/41 111/211/40 112/208/40\nf 116/212/42 113/213/41 114/210/41\nf 94/193/32 115/214/42 116/212/42\nf 118/215/43 119/216/44 117/217/45\nf 122/218/46 119/216/44 120/219/44\nf 122/218/46 123/220/47 121/221/46\nf 126/222/48 123/220/47 124/223/47\nf 126/222/48 127/224/49 125/225/48\nf 130/226/50 127/224/49 128/227/49\nf 130/226/50 131/228/51 129/229/50\nf 134/230/52 131/228/51 132/231/51\nf 134/230/52 135/232/53 133/233/52\nf 138/234/54 135/232/53 136/235/53\nf 138/234/54 139/236/55 137/237/54\nf 118/215/43 139/236/55 140/238/55\nf 162/239/56 159/240/57 160/241/57\nf 162/239/56 163/242/58 161/243/56\nf 164/244/58 165/245/59 163/242/58\nf 168/246/60 165/245/59 166/247/59\nf 168/246/60 169/248/61 167/249/60\nf 172/250/62 169/248/61 170/251/61\nf 172/250/62 173/252/63 171/253/62\nf 176/254/64 173/252/63 174/255/63\nf 176/254/64 159/240/57 175/256/64\nf 180/257/65 177/258/66 178/259/66\nf 180/257/65 181/260/67 179/261/65\nf 184/262/68 181/260/67 182/263/67\nf 186/264/69 183/265/68 184/262/68\nf 188/266/70 185/267/69 186/264/69\nf 190/268/71 187/269/70 188/266/70\nf 190/268/71 191/270/72 189/271/71\nf 194/272/73 191/270/72 192/273/72\nf 178/259/66 193/274/73 194/272/73\nf 196/275/74 197/276/75 195/277/74\nf 200/278/76 197/276/75 198/279/75\nf 202/280/77 199/281/76 200/278/76\nf 202/280/77 203/282/78 201/283/77\nf 206/284/79 203/282/78 204/285/78\nf 206/284/79 207/286/80 205/287/79\nf 210/288/81 207/286/80 208/289/80\nf 210/288/81 211/290/82 209/291/81\nf 196/275/74 211/290/82 212/292/82\nf 230/293/83 231/294/84 229/295/83\nf 234/296/84 231/294/84 232/297/84\nf 234/296/84 235/298/85 233/299/84\nf 236/300/85 237/301/86 235/298/85\nf 238/302/86 239/303/87 237/301/86\nf 242/304/87 239/303/87 240/305/87\nf 242/304/87 243/306/88 241/307/87\nf 230/308/83 243/309/88 244/310/88\nf 248/311/89 245/312/90 246/313/90\nf 250/314/89 247/315/89 248/311/89\nf 252/316/91 249/317/89 250/314/89\nf 254/318/92 251/319/91 252/316/91\nf 256/320/93 253/321/92 254/318/92\nf 258/322/93 255/323/93 256/320/93\nf 260/324/94 257/325/93 258/322/93\nf 246/313/90 259/326/94 260/324/94\nf 264/327/95 261/328/96 262/329/96\nf 264/327/95 265/330/97 263/331/95\nf 268/332/98 265/330/97 266/333/95\nf 270/334/99 267/335/98 268/332/98\nf 272/336/100 269/337/99 270/334/99\nf 272/336/100 273/338/100 271/339/100\nf 276/340/101 273/338/100 274/341/100\nf 276/340/101 261/328/96 275/342/101\nf 72/167/19 71/171/19 69/168/20\nf 72/167/19 74/173/21 73/170/21\nf 76/172/22 75/175/22 73/170/21\nf 76/172/22 78/177/23 77/174/23\nf 80/176/24 79/179/24 77/174/23\nf 80/176/24 82/181/25 81/178/25\nf 84/180/26 83/183/26 81/178/25\nf 84/180/26 86/185/27 85/182/27\nf 88/184/28 87/187/28 85/182/27\nf 88/184/28 90/189/29 89/186/29\nf 92/188/30 91/190/30 89/186/29\nf 92/188/30 70/169/20 69/168/20\nf 96/191/31 95/195/31 93/192/32\nf 98/194/33 97/197/33 95/195/31\nf 98/194/33 100/199/34 99/196/34\nf 102/198/35 101/201/35 99/196/34\nf 104/200/36 103/203/36 101/201/35\nf 106/202/37 105/205/37 103/203/36\nf 108/204/38 107/207/38 105/205/37\nf 110/206/39 109/209/39 107/207/38\nf 112/208/40 111/211/40 109/209/39\nf 114/210/41 113/213/41 111/211/40\nf 116/212/42 115/214/42 113/213/41\nf 94/193/32 93/192/32 115/214/42\nf 118/215/43 120/219/44 119/216/44\nf 122/218/46 121/221/46 119/216/44\nf 122/218/46 124/223/47 123/220/47\nf 126/222/48 125/225/48 123/220/47\nf 126/222/48 128/227/49 127/224/49\nf 130/226/50 129/229/50 127/224/49\nf 130/226/50 132/231/51 131/228/51\nf 134/230/52 133/233/52 131/228/51\nf 134/230/52 136/235/53 135/232/53\nf 138/234/54 137/237/54 135/232/53\nf 138/234/54 140/238/55 139/236/55\nf 118/215/43 117/217/45 139/236/55\nf 162/239/56 161/243/56 159/240/57\nf 162/239/56 164/244/58 163/242/58\nf 164/244/58 166/247/59 165/245/59\nf 168/246/60 167/249/60 165/245/59\nf 168/246/60 170/251/61 169/248/61\nf 172/250/62 171/253/62 169/248/61\nf 172/250/62 174/255/63 173/252/63\nf 176/254/64 175/256/64 173/252/63\nf 176/254/64 160/241/57 159/240/57\nf 180/257/65 179/261/65 177/258/66\nf 180/257/65 182/263/67 181/260/67\nf 184/262/68 183/265/68 181/260/67\nf 186/264/69 185/267/69 183/265/68\nf 188/266/70 187/269/70 185/267/69\nf 190/268/71 189/271/71 187/269/70\nf 190/268/71 192/273/72 191/270/72\nf 194/272/73 193/274/73 191/270/72\nf 178/259/66 177/258/66 193/274/73\nf 196/275/74 198/279/75 197/276/75\nf 200/278/76 199/281/76 197/276/75\nf 202/280/77 201/283/77 199/281/76\nf 202/280/77 204/285/78 203/282/78\nf 206/284/79 205/287/79 203/282/78\nf 206/284/79 208/289/80 207/286/80\nf 210/288/81 209/291/81 207/286/80\nf 210/288/81 212/292/82 211/290/82\nf 196/275/74 195/277/74 211/290/82\nf 230/293/83 232/297/84 231/294/84\nf 234/296/84 233/299/84 231/294/84\nf 234/296/84 236/300/85 235/298/85\nf 236/300/85 238/302/86 237/301/86\nf 238/302/86 240/305/87 239/303/87\nf 242/304/87 241/307/87 239/303/87\nf 242/304/87 244/343/88 243/306/88\nf 230/308/83 229/344/83 243/309/88\nf 248/311/89 247/315/89 245/312/90\nf 250/314/89 249/317/89 247/315/89\nf 252/316/91 251/319/91 249/317/89\nf 254/318/92 253/321/92 251/319/91\nf 256/320/93 255/323/93 253/321/92\nf 258/322/93 257/325/93 255/323/93\nf 260/324/94 259/326/94 257/325/93\nf 246/313/90 245/312/90 259/326/94\nf 264/327/95 263/331/95 261/328/96\nf 264/327/95 266/333/95 265/330/97\nf 268/332/98 267/335/98 265/330/97\nf 270/334/99 269/337/99 267/335/98\nf 272/336/100 271/339/100 269/337/99\nf 272/336/100 274/341/100 273/338/100\nf 276/340/101 275/342/101 273/338/100\nf 276/340/101 262/329/96 261/328/96\n"
  },
  {
    "path": "src/asset/3d/cube.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: ''\n# www.blender.org\no Cube\nv 0.500000 0.500000 -0.500000\nv 0.500000 -0.500000 -0.500000\nv 0.500000 0.500000 0.500000\nv 0.500000 -0.500000 0.500000\nv -0.500000 0.500000 -0.500000\nv -0.500000 -0.500000 -0.500000\nv -0.500000 0.500000 0.500000\nv -0.500000 -0.500000 0.500000\nvt 0.000000 1.000000\nvt 1.000000 0.000000\nvt 1.000000 1.000000\nvt 1.000000 1.000000\nvt 0.000000 0.000000\nvt 1.000000 0.000000\nvt 0.000000 1.000000\nvt 1.000000 0.000000\nvt 1.000000 1.000000\nvt 0.000000 1.000000\nvt 0.000000 0.000000\nvt 1.000000 0.000000\nvt 0.000000 0.000000\nvt 0.000000 0.000000\nvt 1.000000 1.000000\nvt 0.000000 1.000000\nvn 0.0000 1.0000 0.0000\nvn 0.0000 0.0000 1.0000\nvn -1.0000 0.0000 0.0000\nvn 0.0000 -1.0000 0.0000\nvn 1.0000 0.0000 0.0000\nvn 0.0000 0.0000 -1.0000\ns off\nf 5/1/1 3/2/1 1/3/1\nf 3/4/2 8/5/2 4/6/2\nf 7/7/3 6/8/3 8/5/3\nf 2/9/4 8/5/4 6/10/4\nf 1/3/5 4/11/5 2/12/5\nf 5/1/6 2/12/6 6/13/6\nf 5/1/1 7/14/1 3/2/1\nf 3/4/2 7/7/2 8/5/2\nf 7/7/3 5/15/3 6/8/3\nf 2/9/4 4/6/4 8/5/4\nf 1/3/5 3/16/5 4/11/5\nf 5/1/6 1/3/6 2/12/6\n"
  },
  {
    "path": "src/asset/3d/small_sphere.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: ''\n# www.blender.org\no Icosphere\nv 0.000000 -0.250000 0.000000\nv 0.180902 -0.111805 0.131431\nv -0.069097 -0.111805 0.212662\nv -0.223607 -0.111804 0.000000\nv -0.069097 -0.111805 -0.212662\nv 0.180902 -0.111805 -0.131431\nv 0.069097 0.111805 0.212662\nv -0.180902 0.111805 0.131431\nv -0.180902 0.111805 -0.131431\nv 0.069097 0.111805 -0.212662\nv 0.223607 0.111804 0.000000\nv 0.000000 0.250000 0.000000\nv -0.040614 -0.212664 0.124999\nv 0.106331 -0.212664 0.077253\nv 0.065717 -0.131434 0.202253\nv 0.212662 -0.131434 0.000000\nv 0.106331 -0.212664 -0.077253\nv -0.131432 -0.212663 0.000000\nv -0.172047 -0.131434 0.124999\nv -0.040614 -0.212664 -0.124999\nv -0.172047 -0.131434 -0.124999\nv 0.065717 -0.131434 -0.202253\nv 0.237764 0.000000 0.077253\nv 0.237764 0.000000 -0.077253\nv 0.000000 0.000000 0.250000\nv 0.146946 0.000000 0.202254\nv -0.237764 0.000000 0.077253\nv -0.146946 0.000000 0.202254\nv -0.146946 0.000000 -0.202254\nv -0.237764 0.000000 -0.077253\nv 0.146946 0.000000 -0.202254\nv 0.000000 0.000000 -0.250000\nv 0.172047 0.131434 0.124999\nv -0.065717 0.131434 0.202253\nv -0.212662 0.131434 0.000000\nv -0.065717 0.131434 -0.202253\nv 0.172047 0.131434 -0.124999\nv 0.040614 0.212664 0.124999\nv 0.131432 0.212663 0.000000\nv -0.106331 0.212664 0.077253\nv -0.106331 0.212664 -0.077253\nv 0.040614 0.212664 -0.124999\nvt 0.389468 0.000000\nvt 0.650001 0.176206\nvt 0.450001 0.176206\nvt 0.650001 0.352414\nvt 0.750000 0.323790\nvt 0.250000 0.176208\nvt 0.049999 0.176206\nvt 1.049999 0.176206\nvt 0.849999 0.176206\nvt 0.700001 0.500000\nvt 0.450001 0.352414\nvt 0.550001 0.323789\nvt 0.500000 0.500000\nvt 0.250000 0.352416\nvt 0.350000 0.323790\nvt 0.299999 0.500000\nvt 0.049999 0.352414\nvt 0.150000 0.323790\nvt 0.100000 0.500000\nvt 0.849999 0.352414\nvt 0.949999 0.323789\nvt 0.900000 0.500000\nvt 0.600000 0.500000\nvt 0.400000 0.500000\nvt 0.200001 0.500000\nvt 0.000000 0.500000\nvt 0.799999 0.500000\nvt 0.549999 0.647586\nvt 0.650000 0.676210\nvt 0.549999 0.823794\nvt 0.349999 0.647586\nvt 0.449999 0.676211\nvt 0.349999 0.823794\nvt 0.150001 0.647586\nvt 0.250000 0.676210\nvt 0.150001 0.823794\nvt 0.950001 0.647586\nvt 1.050001 0.676211\nvt 0.950001 0.823794\nvt 0.750000 0.647584\nvt 0.850000 0.676210\nvt 0.750000 0.823792\nvt 0.867720 1.000000\nvt 1.150001 0.823794\nvt 0.050001 0.676211\nvt 1.349999 0.823794\nvt 1.000000 0.500000\nvt 1.049999 0.352414\nvn 0.0000 -1.0000 0.0000\nvn 0.4253 -0.8506 0.3090\nvn -0.1625 -0.8506 0.5000\nvn 0.7236 -0.4472 0.5257\nvn 0.8506 -0.5257 0.0000\nvn -0.5257 -0.8506 0.0000\nvn -0.1625 -0.8506 -0.5000\nvn 0.4253 -0.8506 -0.3090\nvn 0.9510 0.0000 0.3090\nvn -0.2764 -0.4472 0.8506\nvn 0.2629 -0.5257 0.8090\nvn 0.0000 0.0000 1.0000\nvn -0.8944 -0.4472 0.0000\nvn -0.6882 -0.5257 0.5000\nvn -0.9510 0.0000 0.3090\nvn -0.2764 -0.4472 -0.8506\nvn -0.6882 -0.5257 -0.5000\nvn -0.5878 0.0000 -0.8090\nvn 0.7236 -0.4472 -0.5257\nvn 0.2629 -0.5257 -0.8090\nvn 0.5878 0.0000 -0.8090\nvn 0.5878 0.0000 0.8090\nvn -0.5878 0.0000 0.8090\nvn -0.9510 0.0000 -0.3090\nvn 0.0000 0.0000 -1.0000\nvn 0.9510 0.0000 -0.3090\nvn 0.2764 0.4472 0.8506\nvn 0.6882 0.5257 0.5000\nvn 0.1625 0.8506 0.5000\nvn -0.7236 0.4472 0.5257\nvn -0.2629 0.5257 0.8090\nvn -0.4253 0.8506 0.3090\nvn -0.7236 0.4472 -0.5257\nvn -0.8506 0.5257 0.0000\nvn -0.4253 0.8506 -0.3090\nvn 0.2764 0.4472 -0.8506\nvn -0.2629 0.5257 -0.8090\nvn 0.1625 0.8506 -0.5000\nvn 0.8944 0.4472 0.0000\nvn 0.6882 0.5257 -0.5000\nvn 0.5257 0.8506 0.0000\nvn 0.0000 1.0000 0.0000\ns 1\nf 1/1/1 14/2/2 13/3/3\nf 2/4/4 14/2/2 16/5/5\nf 1/1/1 13/3/3 18/6/6\nf 1/1/1 18/6/6 20/7/7\nf 1/1/1 20/8/7 17/9/8\nf 2/4/4 16/5/5 23/10/9\nf 3/11/10 15/12/11 25/13/12\nf 4/14/13 19/15/14 27/16/15\nf 5/17/16 21/18/17 29/19/18\nf 6/20/19 22/21/20 31/22/21\nf 2/4/4 23/10/9 26/23/22\nf 3/11/10 25/13/12 28/24/23\nf 4/14/13 27/16/15 30/25/24\nf 5/17/16 29/19/18 32/26/25\nf 6/20/19 31/22/21 24/27/26\nf 7/28/27 33/29/28 38/30/29\nf 8/31/30 34/32/31 40/33/32\nf 9/34/33 35/35/34 41/36/35\nf 10/37/36 36/38/37 42/39/38\nf 11/40/39 37/41/40 39/42/41\nf 39/42/41 42/39/38 12/43/42\nf 39/42/41 37/41/40 42/39/38\nf 37/41/40 10/37/36 42/39/38\nf 42/39/38 41/44/35 12/43/42\nf 42/39/38 36/38/37 41/44/35\nf 36/45/37 9/34/33 41/36/35\nf 41/44/35 40/46/32 12/43/42\nf 41/36/35 35/35/34 40/33/32\nf 35/35/34 8/31/30 40/33/32\nf 40/46/32 38/30/29 12/43/42\nf 40/33/32 34/32/31 38/30/29\nf 34/32/31 7/28/27 38/30/29\nf 38/30/29 39/42/41 12/43/42\nf 38/30/29 33/29/28 39/42/41\nf 33/29/28 11/40/39 39/42/41\nf 24/27/26 37/41/40 11/40/39\nf 24/27/26 31/22/21 37/41/40\nf 31/22/21 10/37/36 37/41/40\nf 32/47/25 36/38/37 10/37/36\nf 32/26/25 29/19/18 36/45/37\nf 29/19/18 9/34/33 36/45/37\nf 30/25/24 35/35/34 9/34/33\nf 30/25/24 27/16/15 35/35/34\nf 27/16/15 8/31/30 35/35/34\nf 28/24/23 34/32/31 8/31/30\nf 28/24/23 25/13/12 34/32/31\nf 25/13/12 7/28/27 34/32/31\nf 26/23/22 33/29/28 7/28/27\nf 26/23/22 23/10/9 33/29/28\nf 23/10/9 11/40/39 33/29/28\nf 31/22/21 32/47/25 10/37/36\nf 31/22/21 22/21/20 32/47/25\nf 22/21/20 5/48/16 32/47/25\nf 29/19/18 30/25/24 9/34/33\nf 29/19/18 21/18/17 30/25/24\nf 21/18/17 4/14/13 30/25/24\nf 27/16/15 28/24/23 8/31/30\nf 27/16/15 19/15/14 28/24/23\nf 19/15/14 3/11/10 28/24/23\nf 25/13/12 26/23/22 7/28/27\nf 25/13/12 15/12/11 26/23/22\nf 15/12/11 2/4/4 26/23/22\nf 23/10/9 24/27/26 11/40/39\nf 23/10/9 16/5/5 24/27/26\nf 16/5/5 6/20/19 24/27/26\nf 17/9/8 22/21/20 6/20/19\nf 17/9/8 20/8/7 22/21/20\nf 20/8/7 5/48/16 22/21/20\nf 20/7/7 21/18/17 5/17/16\nf 20/7/7 18/6/6 21/18/17\nf 18/6/6 4/14/13 21/18/17\nf 18/6/6 19/15/14 4/14/13\nf 18/6/6 13/3/3 19/15/14\nf 13/3/3 3/11/10 19/15/14\nf 16/5/5 17/9/8 6/20/19\nf 16/5/5 14/2/2 17/9/8\nf 14/2/2 1/1/1 17/9/8\nf 13/3/3 15/12/11 3/11/10\nf 13/3/3 14/2/2 15/12/11\nf 14/2/2 2/4/4 15/12/11\n"
  },
  {
    "path": "src/asset/3d/tank/base.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: 'tank.blend'\n# www.blender.org\no tank-base_Cube\nv 0.645520 0.500000 0.145520\nv 0.645520 0.395520 0.250000\nv 0.750000 0.395520 0.145520\nv 0.645520 0.395520 -0.250000\nv 0.645520 0.500000 -0.145520\nv 0.750000 0.395520 -0.145520\nv 0.750000 -0.395520 0.145520\nv 0.645520 -0.395520 0.250000\nv 0.645520 -0.500000 0.145520\nv 0.750000 -0.395520 -0.145520\nv 0.645520 -0.500000 -0.145520\nv 0.645520 -0.395520 -0.250000\nv -0.645520 0.500000 0.145520\nv -0.750000 0.395520 0.145520\nv -0.645520 0.395520 0.250000\nv -0.750000 0.395520 -0.145520\nv -0.645520 0.500000 -0.145520\nv -0.645520 0.395520 -0.250000\nv -0.750000 -0.395520 0.145520\nv -0.645520 -0.500000 0.145520\nv -0.645520 -0.395520 0.250000\nv -0.645520 -0.395520 -0.250000\nv -0.645520 -0.500000 -0.145520\nv -0.750000 -0.395520 -0.145520\nvt 0.195508 0.920138\nvt 0.097603 0.830157\nvt 0.097603 0.920138\nvt 0.852376 0.070561\nvt 0.852376 0.160542\nvt 0.852376 0.070561\nvt 0.958204 0.058676\nvt 0.860299 0.058676\nvt 0.958204 0.058676\nvt 0.451048 0.929439\nvt 0.548952 0.839458\nvt 0.548952 0.929439\nvt 0.966127 0.160542\nvt 0.966127 0.070561\nvt 0.966127 0.160542\nvt 0.195508 0.209632\nvt 0.195508 0.197747\nvt 0.203431 0.197747\nvt 0.195508 0.932023\nvt 0.203431 0.920138\nvt 0.203431 0.107765\nvt 0.195508 0.107765\nvt 0.195508 0.095881\nvt 0.203431 0.830157\nvt 0.195508 0.818272\nvt 0.195508 0.830157\nvt 0.097603 0.209632\nvt 0.089680 0.197747\nvt 0.097603 0.197747\nvt 0.089680 0.920138\nvt 0.097603 0.932023\nvt 0.089680 0.107765\nvt 0.097603 0.095881\nvt 0.097603 0.107765\nvt 0.097603 0.818272\nvt 0.089680 0.830157\nvt 0.958204 0.172427\nvt 0.860299 0.058676\nvt 0.966127 0.070561\nvt 0.860299 0.172427\nvt 0.852376 0.160542\nvt 0.860299 0.172427\nvt 0.091402 0.479812\nvt 0.189307 0.467928\nvt 0.189307 0.479812\nvt 0.197230 0.569794\nvt 0.189307 0.569794\nvt 0.091402 0.569794\nvt 0.083479 0.479812\nvt 0.091402 0.581679\nvt 0.958204 0.172427\nvt 0.451048 0.839458\nvt 0.091402 0.467928\nvt 0.197230 0.479812\nvt 0.083479 0.569794\nvt 0.189307 0.581679\nvn 0.0000 0.0000 -1.0000\nvn -1.0000 0.0000 0.0000\nvn 0.0000 -1.0000 0.0000\nvn 0.0000 0.0000 1.0000\nvn 1.0000 0.0000 0.0000\nvn 0.5774 0.5774 0.5774\nvn 0.5774 0.5774 -0.5774\nvn 0.5774 -0.5774 0.5774\nvn 0.5774 -0.5774 -0.5774\nvn -0.5774 0.5774 0.5774\nvn -0.5774 0.5774 -0.5774\nvn -0.5774 -0.5774 0.5774\nvn -0.5774 -0.5774 -0.5774\nvn -0.7071 0.0000 -0.7071\nvn 0.0000 0.7071 -0.7071\nvn 0.7071 0.7071 0.0000\nvn -0.7071 -0.7071 0.0000\nvn 0.7071 -0.7071 0.0000\nvn -0.7071 0.7071 0.0000\nvn 0.0000 -0.7071 0.7071\nvn 0.7071 0.0000 0.7071\nvn 0.0000 -0.7071 -0.7071\nvn -0.7071 0.0000 0.7071\nvn 0.0000 0.7071 0.7071\nvn 0.7071 0.0000 -0.7071\nvn 0.0000 1.0000 0.0000\ns off\nf 4/1/1 22/2/1 18/3/1\nf 19/4/2 16/5/2 24/6/2\nf 9/7/3 23/8/3 11/9/3\nf 15/10/4 8/11/4 2/12/4\nf 3/13/5 10/14/5 6/15/5\nf 1/16/6 2/17/6 3/18/6\nf 4/1/7 5/19/7 6/20/7\nf 7/21/8 8/22/8 9/23/8\nf 10/24/9 11/25/9 12/26/9\nf 13/27/10 14/28/10 15/29/10\nf 16/30/11 17/31/11 18/3/11\nf 19/32/12 20/33/12 21/34/12\nf 22/2/13 23/35/13 24/36/13\nf 22/2/14 16/30/14 18/3/14\nf 18/3/15 5/19/15 4/1/15\nf 6/15/16 1/37/16 3/13/16\nf 20/38/17 24/6/17 23/8/17\nf 11/9/18 7/39/18 9/7/18\nf 17/40/19 14/41/19 13/42/19\nf 21/43/20 9/44/20 8/45/20\nf 8/45/21 3/46/21 2/47/21\nf 12/26/22 23/35/22 22/2/22\nf 15/48/23 19/49/23 21/43/23\nf 2/47/24 13/50/24 15/48/24\nf 4/1/25 10/24/25 12/26/25\nf 13/42/26 5/51/26 17/40/26\nf 4/1/1 12/26/1 22/2/1\nf 19/4/2 14/41/2 16/5/2\nf 9/7/3 20/38/3 23/8/3\nf 15/10/4 21/52/4 8/11/4\nf 3/13/5 7/39/5 10/14/5\nf 22/2/14 24/36/14 16/30/14\nf 18/3/15 17/31/15 5/19/15\nf 6/15/16 5/51/16 1/37/16\nf 20/38/17 19/4/17 24/6/17\nf 11/9/18 10/14/18 7/39/18\nf 17/40/19 16/5/19 14/41/19\nf 21/43/20 20/53/20 9/44/20\nf 8/45/21 7/54/21 3/46/21\nf 12/26/22 11/25/22 23/35/22\nf 15/48/23 14/55/23 19/49/23\nf 2/47/24 1/56/24 13/50/24\nf 4/1/25 6/20/25 10/24/25\nf 13/42/26 1/37/26 5/51/26\n"
  },
  {
    "path": "src/asset/3d/tank/canon.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: 'tank.blend'\n# www.blender.org\no Sphere_Sphere.001\nv -0.367321 -0.026418 0.622755\nv -0.440375 -0.075231 0.586362\nv -0.496289 -0.112591 0.519116\nv -0.526549 -0.132810 0.431254\nv -0.526549 -0.132810 0.336154\nv -0.440375 -0.075231 0.181046\nv -0.413958 -0.035694 0.608885\nv -0.487012 -0.065954 0.556050\nv -0.535825 -0.086173 0.476977\nv -0.552966 -0.093273 0.383704\nv -0.535825 -0.086173 0.290431\nv -0.487012 -0.065954 0.211358\nv -0.413958 -0.035694 0.158523\nv -0.374421 -0.009277 0.622755\nv -0.460594 -0.026417 0.586362\nv -0.526549 -0.039537 0.519116\nv -0.562243 -0.046637 0.431254\nv -0.562243 -0.046637 0.336154\nv -0.526549 -0.039537 0.248292\nv -0.460594 -0.026417 0.181046\nv -0.374421 -0.009277 0.144653\nv -0.421058 0.000000 0.608885\nv -0.500131 0.000000 0.556050\nv -0.552966 0.000000 0.476977\nv -0.571519 0.000000 0.383704\nv -0.552966 0.000000 0.290431\nv -0.500131 0.000000 0.211358\nv -0.421058 0.000000 0.158523\nv -0.374421 0.009277 0.622755\nv -0.460594 0.026418 0.586362\nv -0.526549 0.039537 0.519116\nv -0.562243 0.046637 0.431254\nv -0.562243 0.046637 0.336154\nv -0.526549 0.039537 0.248292\nv -0.460594 0.026418 0.181046\nv -0.374421 0.009277 0.144653\nv -0.413958 0.035694 0.608885\nv -0.487012 0.065954 0.556050\nv -0.535825 0.086173 0.476977\nv -0.552966 0.093273 0.383704\nv -0.535825 0.086173 0.290431\nv -0.487012 0.065954 0.211358\nv -0.413958 0.035694 0.158523\nv -0.367321 0.026418 0.622755\nv -0.440375 0.075231 0.586362\nv -0.496288 0.112591 0.519116\nv -0.526548 0.132810 0.431254\nv -0.526548 0.132810 0.336154\nv -0.496288 0.112591 0.248292\nv -0.440375 0.075231 0.181046\nv -0.367321 0.026418 0.144653\nv -0.393739 0.065954 0.608885\nv -0.449652 0.121867 0.556050\nv -0.487012 0.159227 0.476977\nv -0.500131 0.172346 0.383704\nv -0.487012 0.159227 0.290431\nv -0.449652 0.121867 0.211358\nv -0.393739 0.065954 0.158523\nv -0.354202 0.039537 0.622755\nv -0.403015 0.112591 0.586362\nv -0.440375 0.168504 0.519116\nv -0.460594 0.198764 0.431254\nv -0.460594 0.198764 0.336154\nv -0.440375 0.168504 0.248292\nv -0.403015 0.112591 0.181046\nv -0.354202 0.039537 0.144653\nv -0.363479 0.086173 0.608885\nv -0.393739 0.159227 0.556050\nv -0.413958 0.208041 0.476977\nv -0.421058 0.225181 0.383704\nv -0.413958 0.208041 0.290431\nv -0.393739 0.159227 0.211358\nv -0.363479 0.086173 0.158523\nv -0.337061 0.046637 0.622755\nv -0.354202 0.132810 0.586362\nv -0.367321 0.198764 0.519116\nv -0.374421 0.234458 0.431254\nv -0.374421 0.234458 0.336154\nv -0.367321 0.198764 0.248292\nv -0.354202 0.132810 0.181046\nv -0.337061 0.046637 0.144653\nv -0.327785 0.093273 0.608885\nv -0.327785 0.172346 0.556050\nv -0.327785 0.225181 0.476977\nv -0.327785 0.243735 0.383704\nv -0.327785 0.225181 0.290431\nv -0.327785 0.172346 0.211358\nv -0.327785 0.093273 0.158523\nv -0.318508 0.046637 0.622755\nv -0.301367 0.132810 0.586362\nv -0.288248 0.198764 0.519116\nv -0.281148 0.234458 0.431254\nv -0.281148 0.234458 0.336154\nv -0.288248 0.198764 0.248292\nv -0.301367 0.132810 0.181046\nv -0.318508 0.046637 0.144653\nv -0.292090 0.086173 0.608885\nv -0.261830 0.159227 0.556050\nv -0.241611 0.208041 0.476977\nv -0.234511 0.225181 0.383704\nv -0.241611 0.208041 0.290431\nv -0.261830 0.159227 0.211358\nv -0.292090 0.086173 0.158523\nv -0.327785 0.000000 0.139970\nv -0.301367 0.039537 0.622755\nv -0.252554 0.112591 0.586362\nv -0.215194 0.168504 0.519116\nv -0.194975 0.198764 0.431254\nv -0.194975 0.198764 0.336154\nv -0.215194 0.168504 0.248292\nv -0.252554 0.112591 0.181046\nv -0.301367 0.039537 0.144653\nv -0.261830 0.065954 0.608885\nv -0.205917 0.121867 0.556050\nv -0.168557 0.159227 0.476977\nv -0.155438 0.172346 0.383704\nv -0.168557 0.159227 0.290431\nv -0.205917 0.121867 0.211358\nv -0.261831 0.065954 0.158523\nv -0.288248 0.026418 0.622755\nv -0.215194 0.075231 0.586362\nv -0.159281 0.112591 0.519116\nv -0.129021 0.132810 0.431254\nv -0.129021 0.132810 0.336154\nv -0.159281 0.112591 0.248292\nv -0.215194 0.075231 0.181046\nv -0.288248 0.026418 0.144653\nv -0.241611 0.035694 0.608885\nv -0.168557 0.065954 0.556050\nv -0.119744 0.086173 0.476977\nv -0.096037 0.083271 0.383704\nv -0.119744 0.086173 0.290431\nv -0.168557 0.065954 0.211358\nv -0.241611 0.035694 0.158523\nv -0.281148 0.009277 0.622755\nv -0.194975 0.026418 0.586362\nv -0.129021 0.039537 0.519116\nv -0.096037 0.060303 0.445189\nv -0.096037 0.060303 0.322219\nv -0.129021 0.039537 0.248292\nv -0.194975 0.026418 0.181046\nv -0.281148 0.009277 0.144653\nv -0.234511 0.000000 0.608885\nv -0.155438 0.000000 0.556050\nv -0.096037 -0.000000 0.466975\nv 0.507671 0.083271 0.383704\nv -0.096037 -0.000000 0.300433\nv -0.155438 0.000000 0.211358\nv -0.234511 0.000000 0.158523\nv -0.327785 0.000000 0.627439\nv -0.281148 -0.009277 0.622755\nv -0.194975 -0.026417 0.586362\nv -0.129021 -0.039537 0.519116\nv -0.096037 -0.060303 0.445189\nv -0.096037 -0.060303 0.322219\nv -0.129021 -0.039537 0.248292\nv -0.194975 -0.026417 0.181046\nv -0.281148 -0.009277 0.144653\nv -0.241611 -0.035694 0.608885\nv -0.168557 -0.065954 0.556050\nv -0.119744 -0.086173 0.476977\nv -0.096037 -0.083271 0.383704\nv -0.119744 -0.086173 0.290431\nv -0.168557 -0.065954 0.211358\nv -0.241611 -0.035694 0.158523\nv -0.288248 -0.026417 0.622755\nv -0.215194 -0.075231 0.586362\nv -0.159281 -0.112591 0.519116\nv -0.129021 -0.132810 0.431254\nv -0.129021 -0.132810 0.336154\nv -0.159281 -0.112591 0.248292\nv -0.215194 -0.075231 0.181046\nv -0.288248 -0.026417 0.144653\nv -0.261831 -0.065954 0.608885\nv -0.205917 -0.121867 0.556050\nv -0.168557 -0.159227 0.476977\nv -0.155438 -0.172346 0.383704\nv -0.168557 -0.159227 0.290431\nv -0.205917 -0.121867 0.211358\nv -0.261831 -0.065954 0.158523\nv -0.301367 -0.039537 0.622755\nv -0.252554 -0.112591 0.586362\nv -0.215194 -0.168504 0.519116\nv -0.194975 -0.198764 0.431254\nv -0.194975 -0.198764 0.336154\nv -0.215194 -0.168504 0.248292\nv -0.252554 -0.112591 0.181046\nv -0.301367 -0.039537 0.144653\nv -0.292091 -0.086173 0.608885\nv -0.261831 -0.159227 0.556050\nv -0.241611 -0.208040 0.476977\nv -0.234511 -0.225181 0.383704\nv -0.241611 -0.208040 0.290431\nv -0.261831 -0.159227 0.211358\nv -0.292091 -0.086173 0.158523\nv -0.318508 -0.046637 0.622755\nv -0.301367 -0.132810 0.586362\nv -0.288248 -0.198764 0.519116\nv -0.281148 -0.234458 0.431254\nv -0.281148 -0.234458 0.336154\nv -0.288248 -0.198764 0.248292\nv -0.301367 -0.132810 0.181046\nv -0.318508 -0.046637 0.144653\nv -0.327785 -0.093273 0.608885\nv -0.327785 -0.172346 0.556050\nv -0.327785 -0.225181 0.476977\nv -0.327785 -0.243734 0.383704\nv -0.327785 -0.225181 0.290431\nv -0.327785 -0.172346 0.211358\nv -0.327785 -0.093273 0.158523\nv -0.337061 -0.046637 0.622755\nv -0.354202 -0.132810 0.586362\nv -0.367321 -0.198764 0.519116\nv -0.374421 -0.234458 0.431254\nv -0.374421 -0.234458 0.336154\nv -0.367321 -0.198764 0.248292\nv -0.354202 -0.132810 0.181046\nv -0.337061 -0.046637 0.144653\nv -0.363479 -0.086173 0.608885\nv -0.393739 -0.159227 0.556050\nv -0.413958 -0.208040 0.476977\nv -0.421058 -0.225181 0.383704\nv -0.413958 -0.208040 0.290431\nv -0.393739 -0.159227 0.211358\nv -0.363479 -0.086173 0.158523\nv -0.354202 -0.039537 0.622755\nv -0.403015 -0.112591 0.586362\nv -0.440375 -0.168504 0.519116\nv -0.460594 -0.198764 0.431254\nv -0.460594 -0.198764 0.336154\nv -0.440375 -0.168504 0.248292\nv -0.403015 -0.112591 0.181046\nv -0.354202 -0.039537 0.144653\nv -0.393739 -0.065954 0.608885\nv -0.449652 -0.121867 0.556050\nv -0.487012 -0.159227 0.476977\nv -0.500131 -0.172346 0.383704\nv -0.487012 -0.159227 0.290431\nv -0.449652 -0.121867 0.211358\nv -0.393739 -0.065954 0.158523\nv -0.496288 -0.112591 0.248292\nv -0.367321 -0.026417 0.144653\nv 0.507671 0.060303 0.445189\nv 0.507671 0.060303 0.322219\nv 0.507671 0.000000 0.466975\nv 0.507671 0.000000 0.300433\nv 0.507671 -0.060303 0.445189\nv 0.507671 -0.060303 0.322219\nv 0.507671 -0.083271 0.383704\nv 0.507671 0.060200 0.383704\nv 0.507671 0.043596 0.428154\nv 0.507671 0.043596 0.339254\nv 0.507671 0.000000 0.443904\nv 0.507671 0.000000 0.323504\nv 0.507671 -0.043596 0.428154\nv 0.507671 -0.043596 0.339254\nv 0.507671 -0.060200 0.383704\nv 0.230468 0.060200 0.383704\nv 0.230468 0.043596 0.428154\nv 0.230468 0.043596 0.339254\nv 0.230468 0.000000 0.443904\nv 0.230468 0.000000 0.323504\nv 0.230468 -0.043596 0.428154\nv 0.230468 -0.043596 0.339254\nv 0.230468 -0.060200 0.383704\nvt 0.117968 0.474762\nvt 0.102825 0.497425\nvt 0.099065 0.478522\nvt 0.121728 0.493665\nvt 0.113533 0.513450\nvt 0.132436 0.509690\nvt 0.102825 0.459619\nvt 0.083040 0.489230\nvt 0.086800 0.470327\nvt 0.086800 0.470327\nvt 0.072332 0.505255\nvt 0.083040 0.489230\nvt 0.097508 0.454301\nvt 0.076092 0.486352\nvt 0.148461 0.520398\nvt 0.151339 0.513450\nvt 0.167364 0.524158\nvt 0.086800 0.508133\nvt 0.086800 0.540183\nvt 0.076092 0.524158\nvt 0.140631 0.497425\nvt 0.132436 0.509690\nvt 0.121728 0.493665\nvt 0.072332 0.505255\nvt 0.099065 0.478522\nvt 0.086800 0.508133\nvt 0.102825 0.497425\nvt 0.129558 0.524158\nvt 0.148461 0.520398\nvt 0.113533 0.513450\nvt 0.113533 0.534865\nvt 0.097508 0.524158\nvt 0.151339 0.513450\nvt 0.167364 0.524158\nvt 0.076092 0.524158\nvt 0.097508 0.524158\nvt 0.129558 0.524158\nvt 0.132436 0.538625\nvt 0.148461 0.527918\nvt 0.113533 0.534865\nvt 0.148461 0.527918\nvt 0.072332 0.543061\nvt 0.102825 0.550891\nvt 0.072332 0.543061\nvt 0.068572 0.524158\nvt 0.086800 0.540183\nvt 0.083040 0.559086\nvt 0.102825 0.550891\nvt 0.099065 0.569794\nvt 0.083040 0.559086\nvt 0.132436 0.538625\nvt 0.121728 0.554651\nvt 0.151339 0.534865\nvt 0.151339 0.534865\nvt 0.121728 0.554651\nvt 0.086800 0.577989\nvt 0.086800 0.577989\nvt 0.076092 0.561964\nvt 0.140631 0.550891\nvt 0.099065 0.569794\nvt 0.102825 0.588697\nvt 0.156656 0.540183\nvt 0.117968 0.573554\nvt 0.121728 0.592457\nvt 0.102825 0.588697\nvt 0.140631 0.550891\nvt 0.117968 0.573554\nvt 0.136871 0.569794\nvt 0.156656 0.540183\nvt 0.113533 0.604722\nvt 0.097508 0.594014\nvt 0.113533 0.604722\nvt 0.140631 0.588697\nvt 0.152896 0.559086\nvt 0.121728 0.592457\nvt 0.136871 0.569794\nvt 0.163604 0.543061\nvt 0.151339 0.604722\nvt 0.132436 0.608482\nvt 0.152896 0.559086\nvt 0.140631 0.588697\nvt 0.156656 0.577989\nvt 0.132436 0.608482\nvt 0.148461 0.619190\nvt 0.129558 0.615430\nvt 0.163604 0.543061\nvt 0.167364 0.594014\nvt 0.167364 0.561964\nvt 0.151339 0.604722\nvt 0.148461 0.619190\nvt 0.156656 0.577989\nvt 0.167364 0.594014\nvt 0.183389 0.604722\nvt 0.167364 0.615430\nvt 0.167364 0.561964\nvt 0.178072 0.577989\nvt 0.167364 0.615430\nvt 0.171124 0.543061\nvt 0.194097 0.588697\nvt 0.181832 0.559086\nvt 0.183389 0.604722\nvt 0.186267 0.619190\nvt 0.171124 0.543061\nvt 0.178072 0.577989\nvt 0.186267 0.619190\nvt 0.167364 0.622950\nvt 0.194097 0.588697\nvt 0.213000 0.592457\nvt 0.202292 0.608482\nvt 0.181832 0.559086\nvt 0.197857 0.569794\nvt 0.202292 0.608482\nvt 0.221195 0.604722\nvt 0.216760 0.573554\nvt 0.194097 0.550891\nvt 0.213000 0.592457\nvt 0.178072 0.540183\nvt 0.197857 0.569794\nvt 0.221195 0.604722\nvt 0.205170 0.615430\nvt 0.178072 0.540183\nvt 0.216760 0.573554\nvt 0.235663 0.569794\nvt 0.231903 0.588697\nvt 0.194097 0.550891\nvt 0.231903 0.588697\nvt 0.247928 0.577989\nvt 0.231903 0.550891\nvt 0.202292 0.538625\nvt 0.213000 0.554651\nvt 0.235663 0.569794\nvt 0.183389 0.534865\nvt 0.213000 0.554651\nvt 0.183389 0.534865\nvt 0.247928 0.577989\nvt 0.237220 0.594014\nvt 0.247928 0.540183\nvt 0.251688 0.559086\nvt 0.221195 0.534865\nvt 0.202292 0.538625\nvt 0.251688 0.559086\nvt 0.231903 0.550891\nvt 0.237220 0.524158\nvt 0.205170 0.524158\nvt 0.186267 0.527918\nvt 0.221195 0.534865\nvt 0.247928 0.540183\nvt 0.261298 0.548600\nvt 0.261298 0.557909\nvt 0.186267 0.527918\nvt 0.261298 0.548600\nvt 0.261298 0.524158\nvt 0.205170 0.524158\nvt 0.221195 0.513450\nvt 0.261298 0.524158\nvt 0.891784 0.561601\nvt 0.945650 0.552291\nvt 0.891784 0.552291\nvt 0.247928 0.508132\nvt 0.237220 0.524158\nvt 0.202292 0.509690\nvt 0.247928 0.508132\nvt 0.186267 0.520398\nvt 0.221195 0.513450\nvt 0.891784 0.503406\nvt 0.945650 0.527849\nvt 0.891784 0.527849\nvt 0.186267 0.520398\nvt 0.891784 0.494097\nvt 0.945650 0.503406\nvt 0.231903 0.497425\nvt 0.202292 0.509690\nvt 0.213000 0.493665\nvt 0.251688 0.489230\nvt 0.891784 0.552291\nvt 0.945650 0.561601\nvt 0.231903 0.497425\nvt 0.251688 0.489230\nvt 0.261298 0.499715\nvt 0.194097 0.497425\nvt 0.235663 0.478522\nvt 0.183389 0.513450\nvt 0.213000 0.493665\nvt 0.247928 0.470327\nvt 0.261298 0.490406\nvt 0.183389 0.513450\nvt 0.235663 0.478522\nvt 0.216760 0.474762\nvt 0.261298 0.499715\nvt 0.247928 0.470327\nvt 0.197857 0.478522\nvt 0.231903 0.459619\nvt 0.216760 0.474762\nvt 0.231903 0.459619\nvt 0.194097 0.497425\nvt 0.213000 0.455859\nvt 0.178072 0.508132\nvt 0.221195 0.443594\nvt 0.237220 0.454301\nvt 0.178072 0.508133\nvt 0.213000 0.455859\nvt 0.197857 0.478522\nvt 0.221195 0.443594\nvt 0.194097 0.459619\nvt 0.178072 0.470327\nvt 0.202292 0.439834\nvt 0.194097 0.459619\nvt 0.181832 0.489230\nvt 0.183389 0.443594\nvt 0.202292 0.439834\nvt 0.181832 0.489230\nvt 0.167364 0.486352\nvt 0.171124 0.505255\nvt 0.186267 0.429126\nvt 0.205170 0.432886\nvt 0.171124 0.505255\nvt 0.178072 0.470327\nvt 0.186267 0.429126\nvt 0.167364 0.454301\nvt 0.183389 0.443594\nvt 0.156656 0.470327\nvt 0.167364 0.454301\nvt 0.151339 0.443594\nvt 0.167364 0.432886\nvt 0.167364 0.432886\nvt 0.167364 0.486352\nvt 0.152896 0.489230\nvt 0.163604 0.505255\nvt 0.156656 0.470327\nvt 0.163604 0.505255\nvt 0.148461 0.429126\nvt 0.140631 0.459619\nvt 0.148461 0.429126\nvt 0.167364 0.425366\nvt 0.151339 0.443594\nvt 0.132436 0.439834\nvt 0.140631 0.459619\nvt 0.121728 0.455859\nvt 0.132436 0.439834\nvt 0.152896 0.489230\nvt 0.129558 0.432886\nvt 0.136871 0.478522\nvt 0.156656 0.508132\nvt 0.156656 0.508133\nvt 0.136871 0.478522\nvt 0.113533 0.443594\nvt 0.113533 0.443594\nvt 0.121728 0.455859\nvt 0.102825 0.459619\nvt 0.117968 0.474762\nvt 0.140631 0.497425\nvt 0.945650 0.527849\nvt 0.891784 0.527849\nvt 0.891784 0.503406\nvt 0.945650 0.503406\nvt 0.945650 0.494097\nvt 0.945650 0.552291\nvt 0.945650 0.510178\nvt 0.945650 0.527849\nvt 0.945650 0.545519\nvt 0.945650 0.527849\nvt 0.945650 0.545519\nvt 0.945650 0.552250\nvt 0.945650 0.510178\nvt 0.945650 0.503448\nvt 0.937178 0.552250\nvt 0.937178 0.545519\nvt 0.937178 0.527849\nvt 0.937178 0.503448\nvt 0.937178 0.545519\nvt 0.937178 0.527849\nvt 0.937178 0.510178\nvt 0.937178 0.510178\nvn -0.5053 -0.5053 0.6995\nvn -0.6602 -0.2734 0.6995\nvn -0.6962 -0.4652 0.5466\nvn -0.4683 -0.3129 -0.8263\nvn -0.5524 -0.1099 -0.8263\nvn -0.3604 -0.1493 -0.9208\nvn -0.6556 -0.6556 0.3745\nvn -0.8566 -0.3548 0.3745\nvn -0.8163 -0.5454 0.1902\nvn -0.8163 -0.5454 -0.1902\nvn -0.9629 -0.1915 -0.1902\nvn -0.8566 -0.3548 -0.3745\nvn -0.7071 -0.7071 0.0000\nvn -0.9239 -0.3827 0.0000\nvn -0.1996 -0.0397 0.9791\nvn -0.1692 -0.1131 0.9791\nvn 0.0000 0.0000 1.0000\nvn -0.8213 -0.1633 0.5466\nvn -0.8213 0.1634 0.5466\nvn -0.9272 0.0000 0.3745\nvn -0.2758 -0.2758 0.9208\nvn -0.3604 -0.1493 0.9208\nvn -0.4683 -0.3129 0.8263\nvn -0.9629 -0.1915 0.1902\nvn -0.6962 -0.4652 -0.5466\nvn -0.8213 -0.1633 -0.5466\nvn -0.6602 -0.2734 -0.6995\nvn -0.3901 0.0000 -0.9208\nvn -0.1996 -0.0397 -0.9791\nvn -0.5524 -0.1099 0.8263\nvn -0.5524 0.1099 0.8263\nvn -0.7146 0.0000 0.6995\nvn -0.1692 -0.1131 -0.9791\nvn 0.0000 0.0000 -1.0000\nvn -0.9272 0.0000 -0.3745\nvn -0.7146 0.0000 -0.6995\nvn -0.3901 0.0000 0.9208\nvn -0.3604 0.1493 0.9208\nvn -0.1996 0.0397 0.9791\nvn -0.5524 0.1099 -0.8263\nvn -0.1996 0.0397 -0.9791\nvn -0.9629 0.1915 -0.1902\nvn -0.6602 0.2734 0.6995\nvn -0.9629 0.1915 0.1902\nvn -1.0000 0.0000 0.0000\nvn -0.8213 0.1633 -0.5466\nvn -0.8566 0.3548 -0.3745\nvn -0.6602 0.2734 -0.6995\nvn -0.6962 0.4652 0.5466\nvn -0.8566 0.3548 0.3745\nvn -0.3604 0.1493 -0.9208\nvn -0.4683 0.3129 0.8263\nvn -0.1692 0.1131 0.9791\nvn -0.1692 0.1131 -0.9791\nvn -0.4683 0.3129 -0.8263\nvn -0.8163 0.5454 -0.1902\nvn -0.8163 0.5454 0.1902\nvn -0.9239 0.3827 0.0000\nvn -0.2758 0.2758 0.9208\nvn -0.6962 0.4652 -0.5466\nvn -0.6556 0.6556 -0.3745\nvn -0.1131 0.1692 0.9791\nvn -0.5053 0.5053 -0.6995\nvn -0.4652 0.6962 0.5466\nvn -0.6556 0.6556 0.3745\nvn -0.2758 0.2758 -0.9208\nvn -0.5053 0.5053 0.6995\nvn -0.3129 0.4683 0.8263\nvn -0.1131 0.1692 -0.9791\nvn -0.5454 0.8163 0.1902\nvn -0.7071 0.7071 0.0000\nvn -0.5454 0.8163 -0.1902\nvn -0.2734 0.6602 0.6995\nvn -0.1493 0.3604 0.9208\nvn -0.4652 0.6962 -0.5466\nvn -0.3129 0.4683 -0.8263\nvn -0.0397 0.1996 0.9791\nvn -0.1633 0.8213 0.5466\nvn -0.3548 0.8566 0.3745\nvn -0.1493 0.3604 -0.9208\nvn -0.2734 0.6602 -0.6995\nvn -0.1099 0.5524 0.8263\nvn -0.3548 0.8566 -0.3745\nvn -0.1915 0.9629 0.1902\nvn -0.3827 0.9239 0.0000\nvn -0.0397 0.1996 -0.9791\nvn 0.0000 0.7146 0.6995\nvn 0.0000 0.3901 0.9208\nvn -0.1633 0.8213 -0.5466\nvn -0.1915 0.9629 -0.1902\nvn -0.1099 0.5524 -0.8263\nvn 0.0000 0.7146 -0.6995\nvn 0.1634 0.8213 0.5466\nvn 0.0000 0.9272 0.3745\nvn 0.0000 0.3901 -0.9208\nvn 0.1099 0.5524 0.8263\nvn 0.0000 0.9272 -0.3745\nvn 0.0397 0.1996 -0.9791\nvn 0.2734 0.6602 0.6995\nvn 0.1493 0.3604 0.9208\nvn 0.1634 0.8213 -0.5466\nvn 0.1915 0.9629 -0.1902\nvn 0.0397 0.1996 0.9791\nvn 0.1099 0.5524 -0.8263\nvn 0.1915 0.9629 0.1902\nvn 0.0000 1.0000 0.0000\nvn 0.2734 0.6602 -0.6995\nvn 0.4652 0.6962 0.5466\nvn 0.3548 0.8566 0.3745\nvn 0.1493 0.3604 -0.9208\nvn 0.3129 0.4683 0.8263\nvn 0.3548 0.8566 -0.3745\nvn 0.5454 0.8163 -0.1902\nvn 0.5053 0.5053 0.6995\nvn 0.2758 0.2758 0.9208\nvn 0.4652 0.6962 -0.5466\nvn 0.1131 0.1692 0.9791\nvn 0.3129 0.4683 -0.8263\nvn 0.5454 0.8163 0.1902\nvn 0.3827 0.9239 0.0000\nvn 0.1131 0.1692 -0.9791\nvn 0.5053 0.5053 -0.6995\nvn 0.6962 0.4652 0.5466\nvn 0.6556 0.6556 0.3745\nvn 0.2758 0.2758 -0.9208\nvn 0.6556 0.6556 -0.3745\nvn 0.8050 0.5620 -0.1899\nvn 0.6602 0.2734 0.6995\nvn 0.3604 0.1493 0.9208\nvn 0.4683 0.3129 0.8263\nvn 0.6962 0.4652 -0.5466\nvn 0.1692 0.1131 0.9791\nvn 0.4683 0.3129 -0.8263\nvn 0.1692 0.1131 -0.9791\nvn 0.8050 0.5620 0.1899\nvn 0.7071 0.7071 0.0000\nvn 0.8169 0.1643 0.5528\nvn 0.8322 0.3716 0.4115\nvn 0.5524 0.1099 0.8263\nvn 0.3604 0.1493 -0.9208\nvn 0.8322 0.3716 -0.4115\nvn 0.6602 0.2734 -0.6995\nvn 0.7146 0.0000 0.6995\nvn 0.3901 0.0000 0.9208\nvn 0.1996 0.0397 0.9791\nvn 0.5524 0.1099 -0.8263\nvn 0.8169 0.1643 -0.5528\nvn 0.5832 0.5606 0.5878\nvn 0.5667 0.8239 0.0000\nvn 0.1996 0.0397 -0.9791\nvn 0.5832 0.5606 -0.5878\nvn 0.5658 0.0000 0.8245\nvn 0.3901 0.0000 -0.9208\nvn 0.5524 -0.1099 0.8263\nvn 0.5658 0.0000 -0.8245\nvn 0.6237 0.5497 -0.5557\nvn 0.8169 -0.1643 0.5528\nvn 0.7146 0.0000 -0.6995\nvn 0.3604 -0.1493 0.9208\nvn 0.8169 -0.1643 -0.5528\nvn 0.1996 -0.0397 0.9791\nvn 0.5524 -0.1099 -0.8263\nvn 0.5832 -0.5606 0.5878\nvn 0.6380 0.0000 0.7700\nvn 0.1996 -0.0397 -0.9791\nvn 0.5667 -0.8239 0.0000\nvn 0.6237 -0.5497 0.5557\nvn 0.6602 -0.2734 0.6995\nvn 0.3604 -0.1493 -0.9208\nvn 0.4683 -0.3129 0.8263\nvn 0.8322 -0.3716 -0.4115\nvn 0.6362 0.7715 0.0000\nvn 0.6602 -0.2734 -0.6995\nvn 0.8322 -0.3716 0.4115\nvn 0.2758 -0.2758 0.9208\nvn 0.6962 -0.4652 -0.5466\nvn 0.1692 -0.1131 0.9791\nvn 0.4683 -0.3129 -0.8263\nvn 0.8050 -0.5620 0.1899\nvn 0.1692 -0.1131 -0.9791\nvn 0.6962 -0.4652 0.5466\nvn 0.5053 -0.5053 0.6995\nvn 0.5832 -0.5606 -0.5878\nvn 0.8050 -0.5620 -0.1899\nvn 0.3129 -0.4683 0.8263\nvn 0.6556 -0.6556 -0.3745\nvn 0.5053 -0.5053 -0.6995\nvn 0.6556 -0.6556 0.3745\nvn 0.2758 -0.2758 -0.9208\nvn 0.4652 -0.6962 -0.5466\nvn 0.1131 -0.1692 0.9791\nvn 0.5454 -0.8163 0.1902\nvn 0.7071 -0.7071 0.0000\nvn 0.1131 -0.1692 -0.9791\nvn 0.4652 -0.6962 0.5466\nvn 0.3129 -0.4683 -0.8263\nvn 0.5454 -0.8163 -0.1902\nvn 0.2734 -0.6602 0.6995\nvn 0.1099 -0.5524 0.8263\nvn 0.3548 -0.8566 -0.3745\nvn 0.2734 -0.6602 -0.6995\nvn 0.1493 -0.3604 0.9208\nvn 0.1633 -0.8213 0.5466\nvn 0.3548 -0.8566 0.3745\nvn 0.1493 -0.3604 -0.9208\nvn 0.0000 -0.3901 0.9208\nvn 0.0397 -0.1996 0.9791\nvn 0.1915 -0.9629 0.1902\nvn 0.3827 -0.9239 0.0000\nvn 0.0397 -0.1996 -0.9791\nvn 0.1099 -0.5524 -0.8263\nvn 0.1915 -0.9629 -0.1902\nvn 0.0000 -0.7146 0.6995\nvn 0.1633 -0.8213 -0.5466\nvn -0.1099 -0.5524 0.8263\nvn 0.0000 -0.7146 -0.6995\nvn -0.1634 -0.8213 0.5466\nvn 0.0000 -0.9272 0.3745\nvn 0.0000 -0.9272 -0.3745\nvn 0.0000 -0.3901 -0.9208\nvn -0.1493 -0.3604 0.9208\nvn -0.0397 -0.1996 0.9791\nvn -0.1099 -0.5524 -0.8263\nvn -0.0397 -0.1996 -0.9791\nvn -0.1915 -0.9629 -0.1902\nvn -0.2734 -0.6602 0.6995\nvn -0.1915 -0.9629 0.1902\nvn 0.0000 -1.0000 0.0000\nvn -0.1634 -0.8213 -0.5466\nvn -0.3548 -0.8566 -0.3745\nvn -0.2734 -0.6602 -0.6995\nvn -0.4652 -0.6962 0.5466\nvn -0.3548 -0.8566 0.3745\nvn -0.1493 -0.3604 -0.9208\nvn -0.3827 -0.9239 0.0000\nvn -0.3129 -0.4683 0.8263\nvn -0.1131 -0.1692 0.9791\nvn -0.1131 -0.1692 -0.9791\nvn -0.3129 -0.4683 -0.8263\nvn -0.5454 -0.8163 -0.1902\nvn -0.5454 -0.8163 0.1902\nvn -0.4652 -0.6962 -0.5466\nvn -0.6556 -0.6556 -0.3745\nvn -0.5053 -0.5053 -0.6995\nvn -0.2758 -0.2758 -0.9208\nvn 0.6380 0.0000 -0.7700\nvn 0.6237 -0.5497 -0.5557\nvn 0.6362 -0.7715 0.0000\nvn 0.6237 0.5497 0.5557\nvn 0.8148 0.4076 -0.4121\nvn 0.7921 0.0000 -0.6103\nvn 0.8148 -0.4076 0.4121\nvn 0.7921 0.0000 0.6103\nvn 0.8148 -0.4076 -0.4121\nvn 0.7949 -0.6066 0.0000\nvn 0.8148 0.4076 0.4121\nvn 0.7949 0.6066 0.0000\ns 1\nf 235/1/1 8/2/2 3/3/3\nf 6/4/4 20/5/5 13/6/6\nf 236/7/7 9/8/8 4/9/9\nf 5/10/10 18/11/11 11/12/12\nf 237/13/13 10/14/14 5/10/10\nf 14/15/15 1/16/16 150/17/17\nf 16/18/18 31/19/19 24/20/20\nf 234/21/21 7/22/22 2/23/23\nf 3/3/3 16/18/18 9/8/8\nf 4/9/9 17/24/24 10/14/14\nf 241/25/25 19/26/26 12/27/27\nf 13/6/6 28/28/28 21/29/29\nf 15/30/30 30/31/31 23/32/32\nf 242/33/33 21/29/29 104/34/34\nf 11/12/12 26/35/35 19/26/26\nf 9/8/8 24/20/20 17/24/24\nf 12/27/27 27/36/36 20/5/5\nf 17/24/24 18/11/11 10/14/14\nf 22/37/37 37/38/38 30/31/31\nf 29/39/39 14/15/15 150/17/17\nf 20/5/5 35/40/40 28/28/28\nf 21/29/29 36/41/41 104/34/34\nf 18/11/11 33/42/42 26/35/35\nf 23/32/32 38/43/43 31/19/19\nf 17/24/24 32/44/44 25/45/45\nf 19/26/26 34/46/46 27/36/36\nf 26/35/35 41/47/47 34/46/46\nf 27/36/36 42/48/48 35/40/40\nf 31/19/19 46/49/49 39/50/50\nf 24/20/20 39/50/50 32/44/44\nf 28/28/28 43/51/51 36/41/41\nf 32/44/44 33/42/42 25/45/45\nf 30/31/31 45/52/52 38/43/43\nf 44/53/53 29/39/39 150/17/17\nf 36/41/41 51/54/54 104/34/34\nf 35/40/40 50/55/55 43/51/51\nf 33/42/42 48/56/56 41/47/47\nf 32/44/44 47/57/57 40/58/58\nf 37/38/38 52/59/59 45/52/52\nf 34/46/46 49/60/60 42/48/48\nf 41/47/47 56/61/61 49/60/60\nf 59/62/62 44/53/53 150/17/17\nf 42/48/48 57/63/63 50/55/55\nf 46/49/49 61/64/64 54/65/65\nf 39/50/50 54/65/65 47/57/57\nf 43/51/51 58/66/66 51/54/54\nf 38/43/43 53/67/67 46/49/49\nf 45/52/52 60/68/68 53/67/67\nf 47/57/57 48/56/56 40/58/58\nf 51/54/54 66/69/69 104/34/34\nf 47/57/57 62/70/70 55/71/71\nf 48/56/56 63/72/72 56/61/61\nf 53/67/67 68/73/73 61/64/64\nf 52/59/59 67/74/74 60/68/68\nf 49/60/60 64/75/75 57/63/63\nf 50/55/55 65/76/76 58/66/66\nf 74/77/77 59/62/62 150/17/17\nf 61/64/64 76/78/78 69/79/79\nf 54/65/65 69/79/79 62/70/70\nf 58/66/66 73/80/80 66/69/69\nf 57/63/63 72/81/81 65/76/76\nf 62/70/70 63/72/72 55/71/71\nf 60/68/68 75/82/82 68/73/73\nf 56/61/61 71/83/83 64/75/75\nf 62/70/70 77/84/84 70/85/85\nf 66/69/69 81/86/86 104/34/34\nf 68/73/73 83/87/87 76/78/78\nf 67/74/74 82/88/88 75/82/82\nf 64/75/75 79/89/89 72/81/81\nf 63/72/72 78/90/90 71/83/83\nf 65/76/76 80/91/91 73/80/80\nf 72/81/81 87/92/92 80/91/91\nf 76/78/78 91/93/93 84/94/94\nf 69/79/79 84/94/94 77/84/84\nf 73/80/80 88/95/95 81/86/86\nf 77/84/84 78/90/90 70/85/85\nf 75/82/82 90/96/96 83/87/87\nf 71/83/83 86/97/97 79/89/89\nf 81/86/86 96/98/98 104/34/34\nf 83/87/87 98/99/99 91/93/93\nf 82/88/88 97/100/100 90/96/96\nf 79/89/89 94/101/101 87/92/92\nf 78/90/90 93/102/102 86/97/97\nf 89/103/103 74/77/77 150/17/17\nf 80/91/91 95/104/104 88/95/95\nf 77/84/84 92/105/105 85/106/106\nf 87/92/92 102/107/107 95/104/104\nf 91/93/93 107/108/108 99/109/109\nf 88/95/95 103/110/110 96/98/98\nf 92/105/105 93/102/102 85/106/106\nf 90/96/96 106/111/111 98/99/99\nf 84/94/94 99/109/109 92/105/105\nf 86/97/97 101/112/112 94/101/101\nf 93/102/102 109/113/113 101/112/112\nf 98/99/99 114/114/114 107/108/108\nf 97/100/100 113/115/115 106/111/111\nf 94/101/101 110/116/116 102/107/107\nf 105/117/117 89/103/103 150/17/17\nf 95/104/104 111/118/118 103/110/110\nf 92/105/105 108/119/119 100/120/120\nf 96/98/98 112/121/121 104/34/34\nf 102/107/107 118/122/122 111/118/118\nf 107/108/108 122/123/123 115/124/124\nf 108/119/119 109/113/113 100/120/120\nf 99/109/109 115/124/124 108/119/119\nf 103/110/110 119/125/125 112/121/121\nf 101/112/112 117/126/126 110/116/116\nf 109/113/113 124/127/127 117/126/126\nf 114/114/114 129/128/128 122/123/123\nf 113/115/115 128/129/129 121/130/130\nf 110/116/116 125/131/131 118/122/122\nf 120/132/132 105/117/117 150/17/17\nf 111/118/118 126/133/133 119/125/125\nf 106/111/111 121/130/130 114/114/114\nf 112/121/121 127/134/134 104/34/34\nf 108/119/119 123/135/135 116/136/136\nf 122/123/123 137/137/137 130/138/138\nf 115/124/124 130/138/138 123/135/135\nf 123/135/135 124/127/127 116/136/136\nf 121/130/130 136/139/139 129/128/128\nf 119/125/125 134/140/140 127/134/134\nf 117/126/126 132/141/141 125/131/131\nf 118/122/122 133/142/142 126/133/133\nf 129/128/128 144/143/143 137/137/137\nf 128/129/129 143/144/144 136/139/139\nf 135/145/145 120/132/132 150/17/17\nf 126/133/133 141/146/146 134/140/140\nf 125/131/131 140/147/147 133/142/142\nf 123/135/135 138/148/148 131/149/149\nf 127/134/134 142/150/150 104/34/34\nf 124/127/127 139/151/151 132/141/141\nf 130/138/138 145/152/152 138/148/148\nf 134/140/140 149/153/153 142/150/150\nf 136/139/139 152/154/154 144/143/143\nf 132/141/141 147/155/155 140/147/147\nf 131/156/149 244/157/156 139/158/151\nf 137/137/137 153/159/157 145/152/152\nf 133/142/142 148/160/158 141/146/146\nf 143/144/144 159/161/159 152/154/154\nf 140/147/147 156/162/160 148/160/158\nf 151/163/161 135/145/145 150/17/17\nf 141/146/146 157/164/162 149/153/153\nf 154/165/163 245/166/164 145/167/152\nf 142/150/150 158/168/165 104/34/34\nf 162/169/166 247/170/167 154/165/163\nf 144/143/143 160/171/168 153/159/157\nf 149/153/153 165/172/169 158/168/165\nf 152/154/154 167/173/170 160/171/168\nf 147/155/155 163/174/171 156/162/160\nf 138/175/148 146/176/172 131/156/149\nf 148/160/158 164/177/173 157/164/162\nf 145/152/152 161/178/174 154/179/163\nf 159/161/159 174/180/175 167/173/170\nf 156/162/160 171/181/176 164/177/173\nf 166/182/177 151/163/161 150/17/17\nf 157/164/162 172/183/178 165/172/169\nf 154/179/163 169/184/179 162/185/166\nf 158/168/165 173/186/180 104/34/34\nf 153/159/157 168/187/181 161/178/174\nf 160/171/168 175/188/182 168/187/181\nf 155/189/183 170/190/184 163/174/171\nf 169/184/179 170/190/184 162/185/166\nf 167/173/170 182/191/185 175/188/182\nf 163/174/171 178/192/186 171/181/176\nf 164/177/173 179/193/187 172/183/178\nf 161/178/174 176/194/188 169/184/179\nf 165/172/169 180/195/189 173/186/180\nf 171/181/176 186/196/190 179/193/187\nf 181/197/191 166/182/177 150/17/17\nf 169/184/179 184/198/192 177/199/193\nf 173/186/180 188/200/194 104/34/34\nf 168/187/181 183/201/195 176/194/188\nf 172/183/178 187/202/196 180/195/189\nf 170/190/184 185/203/197 178/192/186\nf 175/188/182 190/204/198 183/201/195\nf 184/198/192 185/203/197 177/199/193\nf 182/191/185 197/205/199 190/204/198\nf 178/192/186 193/206/200 186/196/190\nf 179/193/187 194/207/201 187/202/196\nf 174/180/175 189/208/202 182/191/185\nf 183/201/195 198/209/203 191/210/204\nf 180/195/189 195/211/205 188/200/194\nf 176/194/188 191/210/204 184/198/192\nf 189/208/202 204/212/206 197/205/199\nf 196/213/207 181/197/191 150/17/17\nf 184/198/192 199/214/208 192/215/209\nf 188/200/194 203/216/210 104/34/34\nf 187/202/196 202/217/211 195/211/205\nf 185/203/197 200/218/212 193/206/200\nf 190/204/198 205/219/213 198/209/203\nf 186/196/190 201/220/214 194/207/201\nf 197/205/199 212/221/215 205/219/213\nf 194/207/201 209/222/216 202/217/211\nf 198/209/203 213/223/217 206/224/218\nf 193/206/200 208/225/219 201/220/214\nf 191/210/204 206/224/218 199/214/208\nf 195/211/205 210/226/220 203/216/210\nf 199/214/208 200/218/212 192/215/209\nf 204/212/206 219/227/221 212/221/215\nf 211/228/222 196/213/207 150/17/17\nf 202/217/211 217/229/223 210/226/220\nf 203/216/210 218/230/224 104/34/34\nf 200/218/212 215/231/225 208/225/219\nf 205/219/213 220/232/226 213/223/217\nf 199/214/208 214/233/227 207/234/228\nf 201/220/214 216/235/229 209/222/216\nf 208/225/219 223/236/230 216/235/229\nf 209/222/216 224/237/231 217/229/223\nf 213/223/217 228/238/232 221/239/233\nf 206/224/218 221/239/233 214/233/227\nf 210/226/220 225/240/234 218/230/224\nf 207/234/228 222/241/235 215/231/225\nf 212/221/215 227/242/236 220/232/226\nf 226/243/237 211/228/222 150/17/17\nf 218/230/224 233/244/238 104/34/34\nf 217/229/223 232/245/239 225/240/234\nf 215/231/225 230/246/240 223/236/230\nf 214/233/227 229/247/241 222/241/235\nf 219/227/221 234/21/21 227/242/236\nf 216/235/229 231/248/242 224/237/231\nf 223/236/230 238/249/243 231/248/242\nf 224/237/231 239/250/244 232/245/239\nf 228/238/232 3/3/3 236/7/7\nf 221/239/233 236/7/7 229/247/241\nf 225/240/234 240/251/245 233/244/238\nf 220/232/226 235/1/1 228/238/232\nf 227/242/236 2/23/23 235/1/1\nf 222/241/235 237/13/13 230/246/240\nf 1/16/16 226/243/237 150/17/17\nf 233/244/238 242/33/33 104/34/34\nf 229/247/241 4/9/9 237/13/13\nf 230/246/240 5/10/10 238/249/243\nf 231/248/242 241/25/25 239/250/244\nf 232/245/239 6/4/4 240/251/245\nf 7/22/22 22/37/37 15/30/30\nf 13/6/6 240/251/245 6/4/4\nf 239/250/244 12/27/27 6/4/4\nf 11/12/12 238/249/243 5/10/10\nf 2/23/23 15/30/30 8/2/2\nf 8/2/2 23/32/32 16/18/18\nf 235/1/1 2/23/23 8/2/2\nf 6/4/4 12/27/27 20/5/5\nf 236/7/7 3/3/3 9/8/8\nf 5/10/10 10/14/14 18/11/11\nf 237/13/13 4/9/9 10/14/14\nf 14/15/15 7/22/22 1/16/16\nf 16/18/18 23/32/32 31/19/19\nf 234/21/21 1/16/16 7/22/22\nf 3/3/3 8/2/2 16/18/18\nf 4/9/9 9/8/8 17/24/24\nf 241/25/25 11/12/12 19/26/26\nf 13/6/6 20/5/5 28/28/28\nf 15/30/30 22/37/37 30/31/31\nf 242/33/33 13/6/6 21/29/29\nf 11/12/12 18/11/11 26/35/35\nf 9/8/8 16/18/18 24/20/20\nf 12/27/27 19/26/26 27/36/36\nf 17/24/24 25/45/45 18/11/11\nf 22/37/37 29/39/39 37/38/38\nf 29/39/39 22/37/37 14/15/15\nf 20/5/5 27/36/36 35/40/40\nf 21/29/29 28/28/28 36/41/41\nf 18/11/11 25/45/45 33/42/42\nf 23/32/32 30/31/31 38/43/43\nf 17/24/24 24/20/20 32/44/44\nf 19/26/26 26/35/35 34/46/46\nf 26/35/35 33/42/42 41/47/47\nf 27/36/36 34/46/46 42/48/48\nf 31/19/19 38/43/43 46/49/49\nf 24/20/20 31/19/19 39/50/50\nf 28/28/28 35/40/40 43/51/51\nf 32/44/44 40/58/58 33/42/42\nf 30/31/31 37/38/38 45/52/52\nf 44/53/53 37/38/38 29/39/39\nf 36/41/41 43/51/51 51/54/54\nf 35/40/40 42/48/48 50/55/55\nf 33/42/42 40/58/58 48/56/56\nf 32/44/44 39/50/50 47/57/57\nf 37/38/38 44/53/53 52/59/59\nf 34/46/46 41/47/47 49/60/60\nf 41/47/47 48/56/56 56/61/61\nf 59/62/62 52/59/59 44/53/53\nf 42/48/48 49/60/60 57/63/63\nf 46/49/49 53/67/67 61/64/64\nf 39/50/50 46/49/49 54/65/65\nf 43/51/51 50/55/55 58/66/66\nf 38/43/43 45/52/52 53/67/67\nf 45/52/52 52/59/59 60/68/68\nf 47/57/57 55/71/71 48/56/56\nf 51/54/54 58/66/66 66/69/69\nf 47/57/57 54/65/65 62/70/70\nf 48/56/56 55/71/71 63/72/72\nf 53/67/67 60/68/68 68/73/73\nf 52/59/59 59/62/62 67/74/74\nf 49/60/60 56/61/61 64/75/75\nf 50/55/55 57/63/63 65/76/76\nf 74/77/77 67/74/74 59/62/62\nf 61/64/64 68/73/73 76/78/78\nf 54/65/65 61/64/64 69/79/79\nf 58/66/66 65/76/76 73/80/80\nf 57/63/63 64/75/75 72/81/81\nf 62/70/70 70/85/85 63/72/72\nf 60/68/68 67/74/74 75/82/82\nf 56/61/61 63/72/72 71/83/83\nf 62/70/70 69/79/79 77/84/84\nf 66/69/69 73/80/80 81/86/86\nf 68/73/73 75/82/82 83/87/87\nf 67/74/74 74/77/77 82/88/88\nf 64/75/75 71/83/83 79/89/89\nf 63/72/72 70/85/85 78/90/90\nf 65/76/76 72/81/81 80/91/91\nf 72/81/81 79/89/89 87/92/92\nf 76/78/78 83/87/87 91/93/93\nf 69/79/79 76/78/78 84/94/94\nf 73/80/80 80/91/91 88/95/95\nf 77/84/84 85/106/106 78/90/90\nf 75/82/82 82/88/88 90/96/96\nf 71/83/83 78/90/90 86/97/97\nf 81/86/86 88/95/95 96/98/98\nf 83/87/87 90/96/96 98/99/99\nf 82/88/88 89/103/103 97/100/100\nf 79/89/89 86/97/97 94/101/101\nf 78/90/90 85/106/106 93/102/102\nf 89/103/103 82/88/88 74/77/77\nf 80/91/91 87/92/92 95/104/104\nf 77/84/84 84/94/94 92/105/105\nf 87/92/92 94/101/101 102/107/107\nf 91/93/93 98/99/99 107/108/108\nf 88/95/95 95/104/104 103/110/110\nf 92/105/105 100/120/120 93/102/102\nf 90/96/96 97/100/100 106/111/111\nf 84/94/94 91/93/93 99/109/109\nf 86/97/97 93/102/102 101/112/112\nf 93/102/102 100/120/120 109/113/113\nf 98/99/99 106/111/111 114/114/114\nf 97/100/100 105/117/117 113/115/115\nf 94/101/101 101/112/112 110/116/116\nf 105/117/117 97/100/100 89/103/103\nf 95/104/104 102/107/107 111/118/118\nf 92/105/105 99/109/109 108/119/119\nf 96/98/98 103/110/110 112/121/121\nf 102/107/107 110/116/116 118/122/122\nf 107/108/108 114/114/114 122/123/123\nf 108/119/119 116/136/136 109/113/113\nf 99/109/109 107/108/108 115/124/124\nf 103/110/110 111/118/118 119/125/125\nf 101/112/112 109/113/113 117/126/126\nf 109/113/113 116/136/136 124/127/127\nf 114/114/114 121/130/130 129/128/128\nf 113/115/115 120/132/132 128/129/129\nf 110/116/116 117/126/126 125/131/131\nf 120/132/132 113/115/115 105/117/117\nf 111/118/118 118/122/122 126/133/133\nf 106/111/111 113/115/115 121/130/130\nf 112/121/121 119/125/125 127/134/134\nf 108/119/119 115/124/124 123/135/135\nf 122/123/123 129/128/128 137/137/137\nf 115/124/124 122/123/123 130/138/138\nf 123/135/135 131/149/149 124/127/127\nf 121/130/130 128/129/129 136/139/139\nf 119/125/125 126/133/133 134/140/140\nf 117/126/126 124/127/127 132/141/141\nf 118/122/122 125/131/131 133/142/142\nf 129/128/128 136/139/139 144/143/143\nf 128/129/129 135/145/145 143/144/144\nf 135/145/145 128/129/129 120/132/132\nf 126/133/133 133/142/142 141/146/146\nf 125/131/131 132/141/141 140/147/147\nf 123/135/135 130/138/138 138/148/148\nf 127/134/134 134/140/140 142/150/150\nf 124/127/127 131/149/149 139/151/151\nf 130/138/138 137/137/137 145/152/152\nf 134/140/140 141/146/146 149/153/153\nf 136/139/139 143/144/144 152/154/154\nf 132/141/141 139/151/151 147/155/155\nf 139/158/151 246/252/246 147/253/155\nf 137/137/137 144/143/143 153/159/157\nf 133/142/142 140/147/147 148/160/158\nf 143/144/144 151/163/161 159/161/159\nf 140/147/147 147/155/155 156/162/160\nf 151/163/161 143/144/144 135/145/145\nf 141/146/146 148/160/158 157/164/162\nf 155/254/183 246/252/246 248/255/247\nf 142/150/150 149/153/153 158/168/165\nf 155/254/183 249/256/248 162/169/166\nf 144/143/143 152/154/154 160/171/168\nf 149/153/153 157/164/162 165/172/169\nf 152/154/154 159/161/159 167/173/170\nf 147/155/155 155/189/183 163/174/171\nf 145/167/152 243/257/249 138/175/148\nf 148/160/158 156/162/160 164/177/173\nf 145/152/152 153/159/157 161/178/174\nf 159/161/159 166/182/177 174/180/175\nf 156/162/160 163/174/171 171/181/176\nf 166/182/177 159/161/159 151/163/161\nf 157/164/162 164/177/173 172/183/178\nf 154/179/163 161/178/174 169/184/179\nf 158/168/165 165/172/169 173/186/180\nf 153/159/157 160/171/168 168/187/181\nf 160/171/168 167/173/170 175/188/182\nf 155/189/183 162/185/166 170/190/184\nf 169/184/179 177/199/193 170/190/184\nf 167/173/170 174/180/175 182/191/185\nf 163/174/171 170/190/184 178/192/186\nf 164/177/173 171/181/176 179/193/187\nf 161/178/174 168/187/181 176/194/188\nf 165/172/169 172/183/178 180/195/189\nf 171/181/176 178/192/186 186/196/190\nf 181/197/191 174/180/175 166/182/177\nf 169/184/179 176/194/188 184/198/192\nf 173/186/180 180/195/189 188/200/194\nf 168/187/181 175/188/182 183/201/195\nf 172/183/178 179/193/187 187/202/196\nf 170/190/184 177/199/193 185/203/197\nf 175/188/182 182/191/185 190/204/198\nf 184/198/192 192/215/209 185/203/197\nf 182/191/185 189/208/202 197/205/199\nf 178/192/186 185/203/197 193/206/200\nf 179/193/187 186/196/190 194/207/201\nf 174/180/175 181/197/191 189/208/202\nf 183/201/195 190/204/198 198/209/203\nf 180/195/189 187/202/196 195/211/205\nf 176/194/188 183/201/195 191/210/204\nf 189/208/202 196/213/207 204/212/206\nf 196/213/207 189/208/202 181/197/191\nf 184/198/192 191/210/204 199/214/208\nf 188/200/194 195/211/205 203/216/210\nf 187/202/196 194/207/201 202/217/211\nf 185/203/197 192/215/209 200/218/212\nf 190/204/198 197/205/199 205/219/213\nf 186/196/190 193/206/200 201/220/214\nf 197/205/199 204/212/206 212/221/215\nf 194/207/201 201/220/214 209/222/216\nf 198/209/203 205/219/213 213/223/217\nf 193/206/200 200/218/212 208/225/219\nf 191/210/204 198/209/203 206/224/218\nf 195/211/205 202/217/211 210/226/220\nf 199/214/208 207/234/228 200/218/212\nf 204/212/206 211/228/222 219/227/221\nf 211/228/222 204/212/206 196/213/207\nf 202/217/211 209/222/216 217/229/223\nf 203/216/210 210/226/220 218/230/224\nf 200/218/212 207/234/228 215/231/225\nf 205/219/213 212/221/215 220/232/226\nf 199/214/208 206/224/218 214/233/227\nf 201/220/214 208/225/219 216/235/229\nf 208/225/219 215/231/225 223/236/230\nf 209/222/216 216/235/229 224/237/231\nf 213/223/217 220/232/226 228/238/232\nf 206/224/218 213/223/217 221/239/233\nf 210/226/220 217/229/223 225/240/234\nf 207/234/228 214/233/227 222/241/235\nf 212/221/215 219/227/221 227/242/236\nf 226/243/237 219/227/221 211/228/222\nf 218/230/224 225/240/234 233/244/238\nf 217/229/223 224/237/231 232/245/239\nf 215/231/225 222/241/235 230/246/240\nf 214/233/227 221/239/233 229/247/241\nf 219/227/221 226/243/237 234/21/21\nf 216/235/229 223/236/230 231/248/242\nf 223/236/230 230/246/240 238/249/243\nf 224/237/231 231/248/242 239/250/244\nf 228/238/232 235/1/1 3/3/3\nf 221/239/233 228/238/232 236/7/7\nf 225/240/234 232/245/239 240/251/245\nf 220/232/226 227/242/236 235/1/1\nf 227/242/236 234/21/21 2/23/23\nf 222/241/235 229/247/241 237/13/13\nf 1/16/16 234/21/21 226/243/237\nf 233/244/238 240/251/245 242/33/33\nf 229/247/241 236/7/7 4/9/9\nf 230/246/240 237/13/13 5/10/10\nf 231/248/242 238/249/243 241/25/25\nf 232/245/239 239/250/244 6/4/4\nf 7/22/22 14/15/15 22/37/37\nf 13/6/6 242/33/33 240/251/245\nf 239/250/244 241/25/25 12/27/27\nf 11/12/12 241/25/25 238/249/243\nf 2/23/23 7/22/22 15/30/30\nf 8/2/2 15/30/30 23/32/32\nf 245/166/164 255/258/250 253/259/251\nf 246/252/246 252/260/252 254/261/253\nf 249/256/248 255/258/250 247/170/167\nf 146/176/172 252/260/252 244/157/156\nf 245/166/164 251/262/254 243/257/249\nf 146/176/172 251/262/254 250/263/255\nf 249/256/248 256/264/256 257/265/257\nf 246/252/246 256/264/256 248/255/247\nf 251/262/254 258/266/248 250/263/255\nf 253/259/251 259/267/247 251/262/254\nf 255/258/250 261/268/246 253/259/251\nf 256/264/256 265/269/172 257/265/257\nf 250/263/255 260/270/167 252/260/252\nf 252/260/252 262/271/164 254/261/253\nf 254/261/253 264/272/249 256/264/256\nf 255/258/250 265/269/172 263/273/156\nf 262/271/164 258/266/248 261/268/246\nf 131/156/149 146/176/172 244/157/156\nf 154/165/163 247/170/167 245/166/164\nf 162/169/166 249/256/248 247/170/167\nf 138/175/148 243/257/249 146/176/172\nf 139/158/151 244/157/156 246/252/246\nf 155/254/183 147/253/155 246/252/246\nf 155/254/183 248/255/247 249/256/248\nf 145/167/152 245/166/164 243/257/249\nf 245/166/164 247/170/167 255/258/250\nf 246/252/246 244/157/156 252/260/252\nf 249/256/248 257/265/257 255/258/250\nf 146/176/172 250/263/255 252/260/252\nf 245/166/164 253/259/251 251/262/254\nf 146/176/172 243/257/249 251/262/254\nf 249/256/248 248/255/247 256/264/256\nf 246/252/246 254/261/253 256/264/256\nf 251/262/254 259/267/247 258/266/248\nf 253/259/251 261/268/246 259/267/247\nf 255/258/250 263/273/156 261/268/246\nf 256/264/256 264/272/249 265/269/172\nf 250/263/255 258/266/248 260/270/167\nf 252/260/252 260/270/167 262/271/164\nf 254/261/253 262/271/164 264/272/249\nf 255/258/250 257/265/257 265/269/172\nf 261/268/246 263/273/156 265/269/172\nf 265/269/172 264/272/249 262/271/164\nf 262/271/164 260/270/167 258/266/248\nf 258/266/248 259/267/247 261/268/246\nf 261/268/246 265/269/172 262/271/164\n"
  },
  {
    "path": "src/asset/3d/tank/wheel.obj",
    "content": "# Blender v2.80 (sub 75) OBJ File: 'wheel.blend'\n# www.blender.org\no Cylinder\nv 0.000000 0.250000 -0.125000\nv 0.000000 0.250000 0.125000\nv 0.048773 0.245196 -0.125000\nv 0.048773 0.245196 0.125000\nv 0.095671 0.230970 -0.125000\nv 0.095671 0.230970 0.125000\nv 0.138893 0.207867 -0.125000\nv 0.138893 0.207867 0.125000\nv 0.176777 0.176777 -0.125000\nv 0.176777 0.176777 0.125000\nv 0.207868 0.138893 -0.125000\nv 0.207868 0.138893 0.125000\nv 0.230971 0.095671 -0.125000\nv 0.230971 0.095671 0.125000\nv 0.245197 0.048773 -0.125000\nv 0.245197 0.048773 0.125000\nv 0.250001 0.000000 -0.125000\nv 0.250001 0.000000 0.125000\nv 0.245197 -0.048773 -0.125000\nv 0.245197 -0.048773 0.125000\nv 0.230971 -0.095671 -0.125000\nv 0.230971 -0.095671 0.125000\nv 0.207868 -0.138893 -0.125000\nv 0.207868 -0.138893 0.125000\nv 0.176777 -0.176777 -0.125000\nv 0.176777 -0.176777 0.125000\nv 0.138893 -0.207867 -0.125000\nv 0.138893 -0.207867 0.125000\nv 0.095671 -0.230970 -0.125000\nv 0.095671 -0.230970 0.125000\nv 0.048773 -0.245196 -0.125000\nv 0.048773 -0.245196 0.125000\nv -0.000000 -0.250000 -0.125000\nv -0.000000 -0.250000 0.125000\nv -0.048773 -0.245196 -0.125000\nv -0.048773 -0.245196 0.125000\nv -0.095671 -0.230970 -0.125000\nv -0.095671 -0.230970 0.125000\nv -0.138893 -0.207867 -0.125000\nv -0.138893 -0.207867 0.125000\nv -0.176778 -0.176777 -0.125000\nv -0.176778 -0.176777 0.125000\nv -0.207868 -0.138892 -0.125000\nv -0.207868 -0.138892 0.125000\nv -0.230971 -0.095671 -0.125000\nv -0.230971 -0.095671 0.125000\nv -0.245197 -0.048772 -0.125000\nv -0.245197 -0.048772 0.125000\nv -0.250001 0.000000 -0.125000\nv -0.250001 0.000000 0.125000\nv -0.245197 0.048773 -0.125000\nv -0.245197 0.048773 0.125000\nv -0.230971 0.095671 -0.125000\nv -0.230971 0.095671 0.125000\nv -0.207868 0.138893 -0.125000\nv -0.207868 0.138893 0.125000\nv -0.176777 0.176777 -0.125000\nv -0.176777 0.176777 0.125000\nv -0.138893 0.207868 -0.125000\nv -0.138893 0.207868 0.125000\nv -0.095671 0.230970 -0.125000\nv -0.095671 0.230970 0.125000\nv -0.048772 0.245196 -0.125000\nv -0.048772 0.245196 0.125000\nv -0.000000 0.260013 -0.130006\nv -0.020424 0.274546 -0.128018\nv -0.020424 0.274546 0.128018\nv -0.000000 0.260013 0.130006\nv 0.050726 0.255017 0.130006\nv 0.073593 0.265287 0.128018\nv 0.073593 0.265287 -0.128018\nv 0.050726 0.255017 -0.130006\nv 0.086195 0.261464 -0.128018\nv 0.099503 0.240220 -0.130006\nv 0.099503 0.240220 0.130006\nv 0.086195 0.261464 0.128018\nv 0.144456 0.216193 0.130006\nv 0.169512 0.216930 0.128018\nv 0.169512 0.216930 -0.128018\nv 0.144456 0.216193 -0.130006\nv 0.179692 0.208576 -0.128018\nv 0.183857 0.183857 -0.130006\nv 0.183857 0.183857 0.130006\nv 0.179692 0.208576 0.128018\nv 0.216194 0.144455 0.130006\nv 0.239625 0.135548 0.128018\nv 0.239625 0.135548 -0.128018\nv 0.216194 0.144455 -0.130006\nv 0.245833 0.123934 -0.128018\nv 0.240221 0.099503 -0.130006\nv 0.240221 0.099503 0.130006\nv 0.245833 0.123934 0.128018\nv 0.255018 0.050726 0.130006\nv 0.273257 0.033530 0.128018\nv 0.273257 0.033530 -0.128018\nv 0.255018 0.050726 -0.130006\nv 0.274547 0.020424 -0.128018\nv 0.260014 0.000000 -0.130006\nv 0.260014 0.000000 0.130006\nv 0.274547 0.020424 0.128018\nv 0.255018 -0.050726 0.130006\nv 0.265288 -0.073593 0.128018\nv 0.265288 -0.073593 -0.128018\nv 0.255018 -0.050726 -0.130006\nv 0.261465 -0.086195 -0.128018\nv 0.240221 -0.099503 -0.130006\nv 0.240221 -0.099503 0.130006\nv 0.261465 -0.086195 0.128018\nv 0.216194 -0.144455 0.130006\nv 0.216931 -0.169512 0.128018\nv 0.216931 -0.169512 -0.128018\nv 0.216194 -0.144455 -0.130006\nv 0.208576 -0.179692 -0.128018\nv 0.183857 -0.183857 -0.130006\nv 0.183857 -0.183857 0.130006\nv 0.208576 -0.179692 0.128018\nv 0.144456 -0.216193 0.130006\nv 0.135548 -0.239624 0.128018\nv 0.135548 -0.239624 -0.128018\nv 0.144456 -0.216193 -0.130006\nv 0.123934 -0.245832 -0.128018\nv 0.099503 -0.240220 -0.130006\nv 0.099503 -0.240220 0.130006\nv 0.123934 -0.245832 0.128018\nv 0.050726 -0.255017 0.130006\nv 0.033530 -0.273256 0.128018\nv 0.033530 -0.273256 -0.128018\nv 0.050726 -0.255017 -0.130006\nv 0.020424 -0.274546 -0.128018\nv -0.000000 -0.260013 -0.130006\nv -0.000000 -0.260013 0.130006\nv 0.020424 -0.274546 0.128018\nv -0.050726 -0.255017 0.130006\nv -0.073593 -0.265287 0.128018\nv -0.073593 -0.265287 -0.128018\nv -0.050726 -0.255017 -0.130006\nv -0.086195 -0.261464 -0.128018\nv -0.099503 -0.240220 -0.130006\nv -0.099503 -0.240220 0.130006\nv -0.086195 -0.261464 0.128018\nv -0.144456 -0.216193 0.130006\nv -0.169513 -0.216930 0.128018\nv -0.169513 -0.216930 -0.128018\nv -0.144456 -0.216193 -0.130006\nv -0.179692 -0.208576 -0.128018\nv -0.183858 -0.183857 -0.130006\nv -0.183858 -0.183857 0.130006\nv -0.179692 -0.208576 0.128018\nv -0.216194 -0.144455 0.130006\nv -0.239625 -0.135548 0.128018\nv -0.239625 -0.135548 -0.128018\nv -0.216194 -0.144455 -0.130006\nv -0.245833 -0.123934 -0.128018\nv -0.240221 -0.099502 -0.130006\nv -0.240221 -0.099502 0.130006\nv -0.245833 -0.123934 0.128018\nv -0.255018 -0.050726 0.130006\nv -0.273257 -0.033529 0.128018\nv -0.273257 -0.033529 -0.128018\nv -0.255018 -0.050726 -0.130006\nv -0.274548 -0.020424 -0.128018\nv -0.260014 0.000000 -0.130006\nv -0.260014 0.000000 0.130006\nv -0.274548 -0.020424 0.128018\nv -0.255018 0.050726 0.130006\nv -0.265288 0.073593 0.128018\nv -0.265288 0.073593 -0.128018\nv -0.255018 0.050726 -0.130006\nv -0.261465 0.086195 -0.128018\nv -0.240221 0.099503 -0.130006\nv -0.240221 0.099503 0.130006\nv -0.261465 0.086195 0.128018\nv -0.216193 0.144456 0.130006\nv -0.216931 0.169512 0.128018\nv -0.216931 0.169512 -0.128018\nv -0.216193 0.144456 -0.130006\nv -0.208576 0.179692 -0.128018\nv -0.183857 0.183857 -0.130006\nv -0.183857 0.183857 0.130006\nv -0.208576 0.179692 0.128018\nv -0.144456 0.216193 0.130006\nv -0.135548 0.239624 0.128018\nv -0.135548 0.239624 -0.128018\nv -0.144456 0.216193 -0.130006\nv -0.123934 0.245832 -0.128018\nv -0.099503 0.240221 -0.130006\nv -0.099503 0.240221 0.130006\nv -0.123934 0.245832 0.128018\nv -0.050726 0.255017 0.130006\nv -0.033529 0.273256 0.128018\nv -0.033529 0.273256 -0.128018\nv -0.050726 0.255017 -0.130006\nv 0.038788 0.195002 0.101006\nv 0.000000 0.198822 0.101006\nv 0.076086 0.183688 0.101006\nv 0.110460 0.165315 0.101006\nv 0.140589 0.140589 0.101006\nv 0.165315 0.110460 0.101006\nv 0.183689 0.076086 0.101006\nv 0.195003 0.038788 0.101006\nv 0.198823 0.000000 0.101006\nv 0.195003 -0.038788 0.101006\nv 0.183689 -0.076086 0.101006\nv 0.165315 -0.110460 0.101006\nv 0.140589 -0.140589 0.101006\nv 0.110460 -0.165315 0.101006\nv 0.076086 -0.183688 0.101006\nv 0.038788 -0.195002 0.101006\nv -0.000000 -0.198822 0.101006\nv -0.038789 -0.195002 0.101006\nv -0.076086 -0.183688 0.101006\nv -0.110460 -0.165315 0.101006\nv -0.140589 -0.140589 0.101006\nv -0.165315 -0.110460 0.101006\nv -0.183689 -0.076086 0.101006\nv -0.195003 -0.038788 0.101006\nv -0.198823 0.000000 0.101006\nv -0.195003 0.038789 0.101006\nv -0.183689 0.076086 0.101006\nv -0.165315 0.110460 0.101006\nv -0.140589 0.140589 0.101006\nv -0.110460 0.165315 0.101006\nv -0.076086 0.183688 0.101006\nv -0.038788 0.195002 0.101006\nv 0.024488 0.123107 0.119050\nv 0.000000 0.125518 0.119050\nv 0.048034 0.115964 0.119050\nv 0.069735 0.104365 0.119050\nv 0.088755 0.088755 0.119050\nv 0.104365 0.069734 0.119050\nv 0.115964 0.048034 0.119050\nv 0.123107 0.024487 0.119050\nv 0.125519 0.000000 0.119050\nv 0.123107 -0.024487 0.119050\nv 0.115964 -0.048034 0.119050\nv 0.104365 -0.069734 0.119050\nv 0.088755 -0.088755 0.119050\nv 0.069734 -0.104365 0.119050\nv 0.048034 -0.115964 0.119050\nv 0.024487 -0.123106 0.119050\nv -0.000000 -0.125518 0.119050\nv -0.024488 -0.123106 0.119050\nv -0.048034 -0.115964 0.119050\nv -0.069735 -0.104365 0.119050\nv -0.088755 -0.088755 0.119050\nv -0.104365 -0.069734 0.119050\nv -0.115964 -0.048034 0.119050\nv -0.123107 -0.024487 0.119050\nv -0.125519 0.000000 0.119050\nv -0.123107 0.024488 0.119050\nv -0.115964 0.048034 0.119050\nv -0.104365 0.069734 0.119050\nv -0.088755 0.088755 0.119050\nv -0.069734 0.104365 0.119050\nv -0.048034 0.115964 0.119050\nv -0.024487 0.123107 0.119050\nv 0.015584 0.078347 0.098577\nv 0.000000 0.079882 0.098577\nv 0.030570 0.073802 0.098577\nv 0.044380 0.066420 0.098577\nv 0.056486 0.056485 0.098577\nv 0.066420 0.044380 0.098577\nv 0.073802 0.030570 0.098577\nv 0.078348 0.015584 0.098577\nv 0.079883 0.000000 0.098577\nv 0.078348 -0.015584 0.098577\nv 0.073802 -0.030570 0.098577\nv 0.066420 -0.044380 0.098577\nv 0.056486 -0.056485 0.098577\nv 0.044380 -0.066420 0.098577\nv 0.030570 -0.073802 0.098577\nv 0.015584 -0.078347 0.098577\nv -0.000000 -0.079882 0.098577\nv -0.015584 -0.078347 0.098577\nv -0.030570 -0.073802 0.098577\nv -0.044380 -0.066420 0.098577\nv -0.056486 -0.056485 0.098577\nv -0.066420 -0.044380 0.098577\nv -0.073802 -0.030570 0.098577\nv -0.078348 -0.015584 0.098577\nv -0.079883 0.000000 0.098577\nv -0.078348 0.015584 0.098577\nv -0.073802 0.030570 0.098577\nv -0.066420 0.044380 0.098577\nv -0.056485 0.056485 0.098577\nv -0.044380 0.066420 0.098577\nv -0.030570 0.073802 0.098577\nv -0.015584 0.078347 0.098577\nvt 0.097288 0.960814\nvt 0.095523 0.932572\nvt 0.097288 0.932572\nvt 0.095523 0.960814\nvt 0.095523 0.932572\nvt 0.093758 0.960814\nvt 0.091992 0.932572\nvt 0.093758 0.932572\nvt 0.075635 0.907179\nvt 0.073581 0.908865\nvt 0.073581 0.908865\nvt 0.090227 0.960814\nvt 0.088462 0.932572\nvt 0.090227 0.932572\nvt 0.049629 0.960814\nvt 0.049629 0.932572\nvt 0.049629 0.932572\nvt 0.086697 0.960814\nvt 0.084932 0.932572\nvt 0.086697 0.932572\nvt 0.093758 0.960814\nvt 0.083167 0.960814\nvt 0.081401 0.932572\nvt 0.083167 0.932572\nvt 0.077871 0.960814\nvt 0.077871 0.932572\nvt 0.077871 0.932572\nvt 0.079636 0.960814\nvt 0.079636 0.932572\nvt 0.047864 0.932572\nvt 0.047864 0.960814\nvt 0.047864 0.960814\nvt 0.076106 0.960814\nvt 0.074341 0.932572\nvt 0.076106 0.932572\nvt 0.088354 0.930975\nvt 0.085811 0.931746\nvt 0.088354 0.930975\nvt 0.072576 0.960814\nvt 0.070811 0.932572\nvt 0.072576 0.932572\nvt 0.081401 0.960814\nvt 0.081401 0.932572\nvt 0.069045 0.960814\nvt 0.067280 0.932572\nvt 0.069045 0.932572\nvt 0.071895 0.925982\nvt 0.073581 0.928036\nvt 0.073581 0.928036\nvt 0.065515 0.960814\nvt 0.063750 0.932572\nvt 0.065515 0.932572\nvt 0.054924 0.932007\nvt 0.052280 0.931746\nvt 0.052280 0.931746\nvt 0.061985 0.960814\nvt 0.060220 0.932572\nvt 0.061985 0.932572\nvt 0.079636 0.960814\nvt 0.058455 0.960814\nvt 0.056689 0.932572\nvt 0.058455 0.932572\nvt 0.912589 0.240475\nvt 0.912161 0.214329\nvt 0.912589 0.214329\nvt 0.054924 0.960814\nvt 0.053159 0.932572\nvt 0.054924 0.932572\nvt 0.095691 0.913263\nvt 0.096463 0.915806\nvt 0.095691 0.913263\nvt 0.051394 0.960814\nvt 0.051394 0.932572\nvt 0.080522 0.905155\nvt 0.077979 0.905926\nvt 0.077979 0.905926\nvt 0.046098 0.932572\nvt 0.065515 0.960814\nvt 0.198437 0.502542\nvt 0.198697 0.499897\nvt 0.198697 0.499897\nvt 0.044333 0.960814\nvt 0.042568 0.932572\nvt 0.044333 0.932572\nvt 0.898468 0.240475\nvt 0.898039 0.214329\nvt 0.898468 0.214329\nvt 0.096463 0.921095\nvt 0.085811 0.905155\nvt 0.069871 0.915806\nvt 0.060112 0.930975\nvt 0.057569 0.931746\nvt 0.057569 0.931746\nvt 0.064510 0.928036\nvt 0.062456 0.929722\nvt 0.062456 0.929722\nvt 0.066196 0.925982\nvt 0.067449 0.923638\nvt 0.066196 0.925982\nvt 0.068481 0.918450\nvt 0.068220 0.921095\nvt 0.068220 0.921095\nvt 0.067449 0.913263\nvt 0.068220 0.915806\nvt 0.068220 0.915806\nvt 0.064510 0.908865\nvt 0.066196 0.910919\nvt 0.066196 0.910919\nvt 0.060112 0.905926\nvt 0.062456 0.907179\nvt 0.062456 0.907179\nvt 0.054924 0.904894\nvt 0.057569 0.905155\nvt 0.057569 0.905155\nvt 0.052280 0.905155\nvt 0.049736 0.905926\nvt 0.052280 0.905155\nvt 0.045338 0.908865\nvt 0.047393 0.907179\nvt 0.047393 0.907179\nvt 0.042400 0.913263\nvt 0.043653 0.910919\nvt 0.043653 0.910919\nvt 0.041628 0.915806\nvt 0.041368 0.918451\nvt 0.041628 0.915806\nvt 0.041628 0.921095\nvt 0.042400 0.923638\nvt 0.041628 0.921095\nvt 0.043653 0.925982\nvt 0.045338 0.928036\nvt 0.043653 0.925982\nvt 0.049736 0.930975\nvt 0.047393 0.929722\nvt 0.047393 0.929722\nvt 0.080522 0.931746\nvt 0.083167 0.932007\nvt 0.083167 0.932007\nvt 0.046098 0.960814\nvt 0.046098 0.932572\nvt 0.061985 0.960814\nvt 0.091992 0.960814\nvt 0.091992 0.932572\nvt 0.063750 0.960814\nvt 0.063750 0.932572\nvt 0.926710 0.240475\nvt 0.926282 0.214329\nvt 0.926710 0.214329\nvt 0.060220 0.960814\nvt 0.060220 0.932572\nvt 0.076106 0.960814\nvt 0.894938 0.240475\nvt 0.894509 0.214329\nvt 0.894938 0.214329\nvt 0.094438 0.910919\nvt 0.092752 0.908865\nvt 0.092752 0.908865\nvt 0.940832 0.240475\nvt 0.940403 0.214329\nvt 0.940832 0.214329\nvt 0.044333 0.960814\nvt 0.074341 0.960814\nvt 0.074341 0.932572\nvt 0.090227 0.960814\nvt 0.075635 0.929722\nvt 0.077979 0.930975\nvt 0.077979 0.930975\nvt 0.909059 0.240475\nvt 0.908630 0.214329\nvt 0.909059 0.214329\nvt 0.092752 0.928036\nvt 0.090698 0.929722\nvt 0.092752 0.928036\nvt 0.042568 0.960814\nvt 0.042568 0.932572\nvt 0.058455 0.960814\nvt 0.088462 0.960814\nvt 0.088462 0.932572\nvt 0.071895 0.910919\nvt 0.070642 0.913263\nvt 0.070642 0.913263\nvt 0.923180 0.240475\nvt 0.922751 0.214329\nvt 0.923180 0.214329\nvt 0.056689 0.960814\nvt 0.056689 0.932572\nvt 0.072576 0.960814\nvt 0.090698 0.907179\nvt 0.088354 0.905926\nvt 0.088354 0.905926\nvt 0.937301 0.240475\nvt 0.936873 0.214329\nvt 0.937301 0.214329\nvt 0.097288 0.960814\nvt 0.070811 0.960814\nvt 0.070811 0.932572\nvt 0.086697 0.960814\nvt 0.944362 0.240475\nvt 0.943933 0.214329\nvt 0.944362 0.214329\nvt 0.905529 0.240475\nvt 0.905100 0.214329\nvt 0.905529 0.214329\nvt 0.094438 0.925982\nvt 0.095691 0.923638\nvt 0.095691 0.923638\nvt 0.054924 0.960814\nvt 0.084932 0.960814\nvt 0.084932 0.932572\nvt 0.069610 0.918451\nvt 0.069610 0.918451\nvt 0.919650 0.240475\nvt 0.919221 0.214329\nvt 0.919650 0.214329\nvt 0.053159 0.960814\nvt 0.053159 0.932572\nvt 0.069045 0.960814\nvt 0.083167 0.904894\nvt 0.083167 0.904894\nvt 0.933771 0.240475\nvt 0.933342 0.214329\nvt 0.933771 0.214329\nvt 0.067280 0.960814\nvt 0.067280 0.932572\nvt 0.083167 0.960814\nvt 0.901998 0.240475\nvt 0.901570 0.214329\nvt 0.901998 0.214329\nvt 0.096723 0.918450\nvt 0.096723 0.918450\nvt 0.051394 0.960814\nvt 0.930241 0.240475\nvt 0.929812 0.214329\nvt 0.930241 0.214329\nvt 0.069871 0.921095\nvt 0.070642 0.923638\nvt 0.070642 0.923638\nvt 0.916120 0.240475\nvt 0.915691 0.214329\nvt 0.916120 0.214329\nvt 0.890979 0.921218\nvt 0.890311 0.947568\nvt 0.890311 0.921015\nvt 0.944362 0.947365\nvt 0.945030 0.920170\nvt 0.945030 0.948413\nvt 0.944362 0.921218\nvt 0.943318 0.920170\nvt 0.943318 0.948413\nvt 0.943933 0.947365\nvt 0.940832 0.947365\nvt 0.941500 0.920170\nvt 0.941500 0.948413\nvt 0.940403 0.921218\nvt 0.941447 0.920170\nvt 0.940832 0.921218\nvt 0.940403 0.947365\nvt 0.941447 0.948413\nvt 0.939788 0.948413\nvt 0.939735 0.920170\nvt 0.939735 0.948413\nvt 0.937301 0.921218\nvt 0.937970 0.948413\nvt 0.937301 0.947365\nvt 0.936873 0.921218\nvt 0.937917 0.920170\nvt 0.936257 0.948413\nvt 0.936873 0.947365\nvt 0.936204 0.920170\nvt 0.936204 0.948413\nvt 0.933771 0.921218\nvt 0.934439 0.948413\nvt 0.933771 0.947365\nvt 0.932727 0.920170\nvt 0.934386 0.920170\nvt 0.932727 0.948413\nvt 0.933342 0.947365\nvt 0.930241 0.921218\nvt 0.930909 0.948413\nvt 0.930241 0.947365\nvt 0.929812 0.921218\nvt 0.930856 0.920170\nvt 0.929197 0.948413\nvt 0.929812 0.947365\nvt 0.929144 0.920170\nvt 0.929144 0.948413\nvt 0.926710 0.921218\nvt 0.927379 0.948413\nvt 0.926710 0.947365\nvt 0.926282 0.921218\nvt 0.927326 0.920170\nvt 0.926282 0.947365\nvt 0.927326 0.948413\nvt 0.925666 0.948413\nvt 0.925614 0.920170\nvt 0.925614 0.948413\nvt 0.923180 0.921218\nvt 0.923848 0.948413\nvt 0.923180 0.947365\nvt 0.922136 0.920170\nvt 0.923796 0.920170\nvt 0.922136 0.948413\nvt 0.922751 0.947365\nvt 0.919650 0.947365\nvt 0.920318 0.920170\nvt 0.920318 0.948413\nvt 0.919221 0.921218\nvt 0.919650 0.921218\nvt 0.919221 0.947365\nvt 0.918606 0.948413\nvt 0.918553 0.920170\nvt 0.916120 0.921218\nvt 0.916788 0.948413\nvt 0.916120 0.947365\nvt 0.915691 0.921218\nvt 0.916735 0.920170\nvt 0.915691 0.947365\nvt 0.916735 0.948413\nvt 0.915075 0.948413\nvt 0.915023 0.920170\nvt 0.915023 0.948413\nvt 0.912589 0.947365\nvt 0.913257 0.920170\nvt 0.913257 0.948413\nvt 0.912161 0.921218\nvt 0.913205 0.920170\nvt 0.912589 0.921218\nvt 0.911545 0.948413\nvt 0.912161 0.947365\nvt 0.911492 0.920170\nvt 0.909059 0.921218\nvt 0.909727 0.948413\nvt 0.909059 0.947365\nvt 0.908630 0.921218\nvt 0.909674 0.920170\nvt 0.908015 0.948413\nvt 0.908630 0.947365\nvt 0.907962 0.920170\nvt 0.905529 0.921218\nvt 0.906197 0.948413\nvt 0.905529 0.947365\nvt 0.904485 0.920170\nvt 0.906144 0.920170\nvt 0.904485 0.948413\nvt 0.905100 0.947365\nvt 0.904432 0.920170\nvt 0.904432 0.948413\nvt 0.901998 0.921218\nvt 0.902667 0.948413\nvt 0.901998 0.947365\nvt 0.901570 0.921218\nvt 0.902614 0.920170\nvt 0.900954 0.948413\nvt 0.901570 0.947365\nvt 0.900901 0.920170\nvt 0.900901 0.948413\nvt 0.898468 0.921218\nvt 0.899136 0.948413\nvt 0.898468 0.947365\nvt 0.898039 0.921218\nvt 0.899083 0.920170\nvt 0.897424 0.948413\nvt 0.898039 0.947365\nvt 0.897371 0.920170\nvt 0.894938 0.921218\nvt 0.895606 0.948413\nvt 0.894938 0.947365\nvt 0.894509 0.921218\nvt 0.895553 0.920170\nvt 0.894509 0.947365\nvt 0.893894 0.948413\nvt 0.893841 0.920170\nvt 0.893841 0.948413\nvt 0.891407 0.921218\nvt 0.892076 0.948413\nvt 0.891407 0.947365\nvt 0.892023 0.920170\nvt 0.890979 0.947365\nvt 0.892023 0.948413\nvt 0.890363 0.948413\nvt 0.891407 0.240475\nvt 0.890979 0.214329\nvt 0.891407 0.214329\nvt 0.073242 0.102763\nvt 0.075296 0.101077\nvt 0.073242 0.102763\nvt 0.199262 0.542261\nvt 0.197497 0.542261\nvt 0.197497 0.542261\nvt 0.172616 0.505085\nvt 0.171845 0.502542\nvt 0.172616 0.505085\nvt 0.192672 0.511169\nvt 0.194727 0.509483\nvt 0.194727 0.509483\nvt 0.206323 0.542261\nvt 0.204557 0.542261\nvt 0.204557 0.542261\nvt 0.173869 0.492366\nvt 0.172616 0.494710\nvt 0.172616 0.494710\nvt 0.174550 0.542261\nvt 0.172785 0.542261\nvt 0.172785 0.542261\nvt 0.213383 0.542261\nvt 0.211618 0.542261\nvt 0.211618 0.542261\nvt 0.182496 0.486602\nvt 0.179953 0.487373\nvt 0.179953 0.487373\nvt 0.181610 0.542261\nvt 0.179845 0.542261\nvt 0.179845 0.542261\nvt 0.220444 0.542261\nvt 0.218679 0.542261\nvt 0.218679 0.542261\nvt 0.192672 0.488626\nvt 0.190329 0.487373\nvt 0.190329 0.487373\nvt 0.186906 0.542261\nvt 0.188671 0.542261\nvt 0.186906 0.542261\nvt 0.227504 0.542261\nvt 0.225739 0.542261\nvt 0.225739 0.542261\nvt 0.185141 0.513454\nvt 0.182496 0.513193\nvt 0.185141 0.513454\nvt 0.198437 0.497253\nvt 0.197665 0.494710\nvt 0.197665 0.494710\nvt 0.195732 0.542261\nvt 0.193966 0.542261\nvt 0.193966 0.542261\nvt 0.175555 0.509483\nvt 0.173869 0.507429\nvt 0.175555 0.509483\nvt 0.196412 0.507429\nvt 0.197665 0.505085\nvt 0.197665 0.505085\nvt 0.202792 0.542261\nvt 0.201027 0.542261\nvt 0.201027 0.542261\nvt 0.171845 0.497253\nvt 0.171584 0.499898\nvt 0.171584 0.499898\nvt 0.190329 0.512422\nvt 0.187785 0.513193\nvt 0.190329 0.512422\nvt 0.209853 0.542261\nvt 0.208088 0.542261\nvt 0.208088 0.542261\nvt 0.177609 0.488626\nvt 0.175555 0.490312\nvt 0.175555 0.490312\nvt 0.176315 0.542261\nvt 0.178080 0.542261\nvt 0.176315 0.542261\nvt 0.216913 0.542261\nvt 0.215148 0.542261\nvt 0.215148 0.542261\nvt 0.187785 0.486602\nvt 0.185141 0.486341\nvt 0.185141 0.486341\nvt 0.183376 0.542261\nvt 0.185141 0.542261\nvt 0.183376 0.542261\nvt 0.222209 0.542261\nvt 0.223974 0.542261\nvt 0.222209 0.542261\nvt 0.196412 0.492366\nvt 0.194727 0.490312\nvt 0.194727 0.490312\nvt 0.190436 0.542261\nvt 0.192201 0.542261\nvt 0.190436 0.542261\nvt 0.179953 0.512422\nvt 0.177609 0.511169\nvt 0.179953 0.512422\nvt 0.056689 0.960814\nvt 0.058455 0.960814\nvt 0.056689 0.960814\nvt 0.114600 0.154712\nvt 0.112835 0.154712\nvt 0.112835 0.154712\nvt 0.088123 0.154712\nvt 0.089888 0.154712\nvt 0.088123 0.154712\nvt 0.096124 0.114993\nvt 0.096384 0.112348\nvt 0.096384 0.112348\nvt 0.071556 0.104817\nvt 0.070303 0.107161\nvt 0.070303 0.107161\nvt 0.111070 0.154712\nvt 0.109305 0.154712\nvt 0.109305 0.154712\nvt 0.084593 0.154712\nvt 0.086358 0.154712\nvt 0.084593 0.154712\nvt 0.095352 0.107161\nvt 0.096124 0.109704\nvt 0.095352 0.107161\nvt 0.069271 0.112348\nvt 0.069532 0.109704\nvt 0.069271 0.112348\nvt 0.107540 0.154712\nvt 0.105775 0.154712\nvt 0.105775 0.154712\nvt 0.082828 0.154712\nvt 0.081063 0.154712\nvt 0.081063 0.154712\nvt 0.094099 0.104817\nvt 0.092414 0.102763\nvt 0.092414 0.102763\nvt 0.070303 0.117536\nvt 0.069532 0.114993\nvt 0.070303 0.117536\nvt 0.104010 0.154712\nvt 0.102244 0.154712\nvt 0.102244 0.154712\nvt 0.079297 0.154712\nvt 0.077532 0.154712\nvt 0.077532 0.154712\nvt 0.090359 0.101077\nvt 0.088016 0.099824\nvt 0.088016 0.099824\nvt 0.125191 0.154712\nvt 0.123426 0.154712\nvt 0.123426 0.154712\nvt 0.073242 0.121934\nvt 0.071556 0.119880\nvt 0.073242 0.121934\nvt 0.100479 0.154712\nvt 0.098714 0.154712\nvt 0.098714 0.154712\nvt 0.088016 0.124873\nvt 0.085472 0.125644\nvt 0.088016 0.124873\nvt 0.074002 0.154712\nvt 0.075767 0.154712\nvt 0.074002 0.154712\nvt 0.085472 0.099053\nvt 0.082828 0.098792\nvt 0.082828 0.098792\nvt 0.119896 0.154712\nvt 0.121661 0.154712\nvt 0.119896 0.154712\nvt 0.075296 0.123620\nvt 0.077640 0.124873\nvt 0.077640 0.124873\nvt 0.096949 0.154712\nvt 0.095184 0.154712\nvt 0.095184 0.154712\nvt 0.090359 0.123620\nvt 0.092414 0.121934\nvt 0.092414 0.121934\nvt 0.070472 0.154712\nvt 0.072237 0.154712\nvt 0.070472 0.154712\nvt 0.077640 0.099824\nvt 0.080183 0.099053\nvt 0.077640 0.099824\nvt 0.118131 0.154712\nvt 0.116366 0.154712\nvt 0.116366 0.154712\nvt 0.082828 0.125905\nvt 0.080183 0.125644\nvt 0.082828 0.125905\nvt 0.093419 0.154712\nvt 0.091654 0.154712\nvt 0.091654 0.154712\nvt 0.094099 0.119880\nvt 0.095352 0.117536\nvt 0.095352 0.117536\nvt 0.895008 0.080332\nvt 0.902344 0.062619\nvt 0.920057 0.069956\nvt 0.068220 0.915806\nvt 0.067449 0.913263\nvt 0.067449 0.913263\nvt 0.041368 0.918451\nvt 0.041628 0.915806\nvt 0.041368 0.918451\nvt 0.079636 0.960814\nvt 0.077871 0.960814\nvt 0.077871 0.960814\nvt 0.053159 0.960814\nvt 0.054924 0.960814\nvt 0.053159 0.960814\nvt 0.066196 0.910919\nvt 0.064510 0.908865\nvt 0.064510 0.908865\nvt 0.042400 0.923638\nvt 0.041628 0.921095\nvt 0.042400 0.923638\nvt 0.076106 0.960814\nvt 0.074341 0.960814\nvt 0.074341 0.960814\nvt 0.051394 0.960814\nvt 0.049629 0.960814\nvt 0.049629 0.960814\nvt 0.062456 0.907179\nvt 0.060112 0.905926\nvt 0.060112 0.905926\nvt 0.097288 0.960814\nvt 0.095523 0.960814\nvt 0.095523 0.960814\nvt 0.043653 0.925982\nvt 0.045338 0.928036\nvt 0.045338 0.928036\nvt 0.072576 0.960814\nvt 0.070811 0.960814\nvt 0.070811 0.960814\nvt 0.057569 0.931746\nvt 0.060112 0.930975\nvt 0.060112 0.930975\nvt 0.047864 0.960814\nvt 0.046098 0.960814\nvt 0.046098 0.960814\nvt 0.057569 0.905155\nvt 0.054924 0.904894\nvt 0.054924 0.904894\nvt 0.093758 0.960814\nvt 0.091992 0.960814\nvt 0.091992 0.960814\nvt 0.047393 0.929722\nvt 0.049736 0.930975\nvt 0.049736 0.930975\nvt 0.069045 0.960814\nvt 0.067280 0.960814\nvt 0.067280 0.960814\nvt 0.062456 0.929722\nvt 0.064510 0.928036\nvt 0.064510 0.928036\nvt 0.044333 0.960814\nvt 0.042568 0.960814\nvt 0.042568 0.960814\nvt 0.049736 0.905926\nvt 0.052280 0.905155\nvt 0.049736 0.905926\nvt 0.090227 0.960814\nvt 0.088462 0.960814\nvt 0.088462 0.960814\nvt 0.052280 0.931746\nvt 0.054924 0.932007\nvt 0.054924 0.932007\nvt 0.065515 0.960814\nvt 0.063750 0.960814\nvt 0.063750 0.960814\nvt 0.066196 0.925982\nvt 0.067449 0.923638\nvt 0.067449 0.923638\nvt 0.045338 0.908865\nvt 0.047393 0.907179\nvt 0.045338 0.908865\nvt 0.086697 0.960814\nvt 0.084932 0.960814\nvt 0.084932 0.960814\nvt 0.060220 0.960814\nvt 0.061985 0.960814\nvt 0.060220 0.960814\nvt 0.068220 0.921095\nvt 0.068481 0.918450\nvt 0.068481 0.918450\nvt 0.043653 0.910919\nvt 0.042400 0.913263\nvt 0.042400 0.913263\nvt 0.081401 0.960814\nvt 0.083167 0.960814\nvt 0.081401 0.960814\nvt 0.095523 0.960814\nvt 0.075635 0.907179\nvt 0.049629 0.960814\nvt 0.093758 0.932572\nvt 0.077871 0.960814\nvt 0.047864 0.932572\nvt 0.085811 0.931746\nvt 0.081401 0.960814\nvt 0.071895 0.925982\nvt 0.054924 0.932007\nvt 0.079636 0.932572\nvt 0.912161 0.240475\nvt 0.096463 0.915806\nvt 0.080522 0.905155\nvt 0.065515 0.932572\nvt 0.198437 0.502542\nvt 0.898039 0.240475\nvt 0.090698 0.929722\nvt 0.060112 0.930975\nvt 0.064510 0.928036\nvt 0.067449 0.923638\nvt 0.068481 0.918450\nvt 0.067449 0.913263\nvt 0.064510 0.908865\nvt 0.060112 0.905926\nvt 0.054924 0.904894\nvt 0.049736 0.905926\nvt 0.045338 0.908865\nvt 0.042400 0.913263\nvt 0.041368 0.918451\nvt 0.042400 0.923638\nvt 0.045338 0.928036\nvt 0.049736 0.930975\nvt 0.080522 0.931746\nvt 0.046098 0.960814\nvt 0.061985 0.932572\nvt 0.091992 0.960814\nvt 0.063750 0.960814\nvt 0.926282 0.240475\nvt 0.060220 0.960814\nvt 0.076106 0.932572\nvt 0.894509 0.240475\nvt 0.094438 0.910919\nvt 0.940403 0.240475\nvt 0.044333 0.932572\nvt 0.074341 0.960814\nvt 0.090227 0.932572\nvt 0.075635 0.929722\nvt 0.908630 0.240475\nvt 0.042568 0.960814\nvt 0.058455 0.932572\nvt 0.088462 0.960814\nvt 0.071895 0.910919\nvt 0.922751 0.240475\nvt 0.056689 0.960814\nvt 0.072576 0.932572\nvt 0.090698 0.907179\nvt 0.936873 0.240475\nvt 0.097288 0.932572\nvt 0.070811 0.960814\nvt 0.086697 0.932572\nvt 0.943933 0.240475\nvt 0.905100 0.240475\nvt 0.094438 0.925982\nvt 0.054924 0.932572\nvt 0.084932 0.960814\nvt 0.069871 0.915806\nvt 0.919221 0.240475\nvt 0.053159 0.960814\nvt 0.069045 0.932572\nvt 0.085811 0.905155\nvt 0.933342 0.240475\nvt 0.067280 0.960814\nvt 0.083167 0.932572\nvt 0.901570 0.240475\nvt 0.096463 0.921095\nvt 0.051394 0.932572\nvt 0.929812 0.240475\nvt 0.069871 0.921095\nvt 0.915691 0.240475\nvt 0.943933 0.921218\nvt 0.939788 0.920170\nvt 0.937970 0.920170\nvt 0.936257 0.920170\nvt 0.937917 0.948413\nvt 0.933342 0.921218\nvt 0.929197 0.920170\nvt 0.927379 0.920170\nvt 0.925666 0.920170\nvt 0.922751 0.921218\nvt 0.916788 0.920170\nvt 0.915075 0.920170\nvt 0.913205 0.948413\nvt 0.906197 0.920170\nvt 0.905100 0.921218\nvt 0.906144 0.948413\nvt 0.902667 0.920170\nvt 0.900954 0.920170\nvt 0.902614 0.948413\nvt 0.893894 0.920170\nvt 0.892076 0.920170\nvt 0.890363 0.920170\nvt 0.890979 0.240475\nvt 0.075296 0.101077\nvt 0.199262 0.542261\nvt 0.171845 0.502542\nvt 0.192672 0.511169\nvt 0.206323 0.542261\nvt 0.173869 0.492366\nvt 0.174550 0.542261\nvt 0.213383 0.542261\nvt 0.182496 0.486602\nvt 0.181610 0.542261\nvt 0.220444 0.542261\nvt 0.192672 0.488626\nvt 0.188671 0.542261\nvt 0.227504 0.542261\nvt 0.182496 0.513193\nvt 0.198437 0.497253\nvt 0.195732 0.542261\nvt 0.173869 0.507429\nvt 0.196412 0.507429\nvt 0.202792 0.542261\nvt 0.171845 0.497253\nvt 0.187785 0.513193\nvt 0.209853 0.542261\nvt 0.177609 0.488626\nvt 0.178080 0.542261\nvt 0.216913 0.542261\nvt 0.187785 0.486602\nvt 0.185141 0.542261\nvt 0.223974 0.542261\nvt 0.196412 0.492366\nvt 0.192201 0.542261\nvt 0.177609 0.511169\nvt 0.058455 0.960814\nvt 0.114600 0.154712\nvt 0.089888 0.154712\nvt 0.096124 0.114993\nvt 0.071556 0.104817\nvt 0.111070 0.154712\nvt 0.086358 0.154712\nvt 0.096124 0.109704\nvt 0.069532 0.109704\nvt 0.107540 0.154712\nvt 0.082828 0.154712\nvt 0.094099 0.104817\nvt 0.069532 0.114993\nvt 0.104010 0.154712\nvt 0.079297 0.154712\nvt 0.090359 0.101077\nvt 0.125191 0.154712\nvt 0.071556 0.119880\nvt 0.100479 0.154712\nvt 0.085472 0.125644\nvt 0.075767 0.154712\nvt 0.085472 0.099053\nvt 0.121661 0.154712\nvt 0.075296 0.123620\nvt 0.096949 0.154712\nvt 0.090359 0.123620\nvt 0.072237 0.154712\nvt 0.080183 0.099053\nvt 0.118131 0.154712\nvt 0.080183 0.125644\nvt 0.093419 0.154712\nvt 0.094099 0.119880\nvt 0.912720 0.087668\nvt 0.910177 0.088440\nvt 0.907532 0.088700\nvt 0.904887 0.088440\nvt 0.902344 0.087668\nvt 0.900001 0.086415\nvt 0.897946 0.084730\nvt 0.896260 0.082675\nvt 0.894236 0.077789\nvt 0.893976 0.075144\nvt 0.894236 0.072499\nvt 0.895008 0.069956\nvt 0.896260 0.067612\nvt 0.897946 0.065558\nvt 0.900001 0.063872\nvt 0.904887 0.061848\nvt 0.907532 0.061587\nvt 0.910177 0.061848\nvt 0.912720 0.062619\nvt 0.915064 0.063872\nvt 0.917118 0.065558\nvt 0.918804 0.067612\nvt 0.920828 0.072499\nvt 0.921089 0.075144\nvt 0.920828 0.077789\nvt 0.920057 0.080332\nvt 0.918804 0.082675\nvt 0.917118 0.084730\nvt 0.915064 0.086415\nvt 0.068220 0.915806\nvt 0.041628 0.915806\nvt 0.079636 0.960814\nvt 0.054924 0.960814\nvt 0.066196 0.910919\nvt 0.041628 0.921095\nvt 0.076106 0.960814\nvt 0.051394 0.960814\nvt 0.062456 0.907179\nvt 0.097288 0.960814\nvt 0.043653 0.925982\nvt 0.072576 0.960814\nvt 0.057569 0.931746\nvt 0.047864 0.960814\nvt 0.057569 0.905155\nvt 0.093758 0.960814\nvt 0.047393 0.929722\nvt 0.069045 0.960814\nvt 0.062456 0.929722\nvt 0.044333 0.960814\nvt 0.052280 0.905155\nvt 0.090227 0.960814\nvt 0.052280 0.931746\nvt 0.065515 0.960814\nvt 0.066196 0.925982\nvt 0.047393 0.907179\nvt 0.086697 0.960814\nvt 0.061985 0.960814\nvt 0.068220 0.921095\nvt 0.043653 0.910919\nvt 0.083167 0.960814\nvn 0.0980 0.9952 0.0000\nvn -0.9808 0.1951 0.0000\nvn 0.4714 0.8819 0.0000\nvn 0.2848 0.3470 -0.8936\nvn 0.7730 0.6344 0.0000\nvn -0.5556 -0.8315 0.0000\nvn 0.9569 0.2903 0.0000\nvn 0.9239 -0.3827 0.0000\nvn 0.9952 -0.0980 0.0000\nvn 0.5556 0.8315 0.0000\nvn 0.8819 -0.4714 0.0000\nvn 0.7071 0.7071 0.0000\nvn 0.6344 -0.7730 0.0000\nvn -0.1303 -0.4296 -0.8936\nvn 0.2903 -0.9569 0.0000\nvn 0.1951 0.9808 0.0000\nvn -0.0980 -0.9952 0.0000\nvn 0.3470 -0.2848 -0.8936\nvn -0.4714 -0.8819 0.0000\nvn 0.0440 -0.4468 0.8936\nvn -0.7730 -0.6344 0.0000\nvn -0.3827 -0.9239 0.0000\nvn -0.9569 -0.2903 0.0000\nvn -0.6344 -0.7730 0.0000\nvn -0.9952 0.0980 0.0000\nvn -0.4296 0.1303 -0.8936\nvn -0.8819 0.4714 0.0000\nvn 0.1303 0.4296 -0.8936\nvn -0.6344 0.7730 0.0000\nvn -0.9239 0.3827 0.0000\nvn -0.4241 -0.0418 0.9046\nvn -0.2903 0.9569 0.0000\nvn -0.7730 0.6344 0.0000\nvn 0.0000 0.0000 -1.0000\nvn -0.1303 -0.4296 0.8936\nvn -0.2848 -0.3470 0.8936\nvn -0.3959 -0.2116 0.8936\nvn -0.4468 -0.0440 0.8936\nvn -0.4296 0.1303 0.8936\nvn -0.3470 0.2848 0.8936\nvn -0.2116 0.3959 0.8936\nvn -0.0440 0.4468 0.8936\nvn 0.1303 0.4296 0.8936\nvn 0.2848 0.3470 0.8936\nvn 0.3959 0.2116 0.8936\nvn 0.4468 0.0440 0.8936\nvn 0.4296 -0.1303 0.8936\nvn 0.3470 -0.2848 0.8936\nvn 0.2116 -0.3959 0.8936\nvn 0.0440 -0.4468 -0.8936\nvn -0.8315 -0.5556 0.0000\nvn -0.7071 0.7071 0.0000\nvn -0.8315 0.5556 0.0000\nvn 0.8315 -0.5556 0.0000\nvn 0.7730 -0.6344 0.0000\nvn 0.5556 -0.8315 0.0000\nvn -0.7071 -0.7071 0.0000\nvn -0.4714 0.8819 0.0000\nvn -0.3470 0.2848 -0.8936\nvn 0.6344 0.7730 0.0000\nvn 0.9239 0.3827 0.0000\nvn 0.8315 0.5556 0.0000\nvn 0.7071 -0.7071 0.0000\nvn 0.2116 -0.3959 -0.8936\nvn -0.8819 -0.4714 0.0000\nvn -0.2848 -0.3470 -0.8936\nvn -0.9808 -0.1951 0.0000\nvn -0.3827 0.9239 0.0000\nvn -0.5556 0.8315 0.0000\nvn 0.3959 0.2116 -0.8936\nvn 0.4714 -0.8819 0.0000\nvn 0.1951 -0.9808 0.0000\nvn -0.9239 -0.3827 0.0000\nvn -0.2116 0.3959 -0.8936\nvn 0.8819 0.4714 0.0000\nvn 1.0000 0.0000 0.0000\nvn 0.9808 0.1951 0.0000\nvn 0.3827 -0.9239 0.0000\nvn 0.2903 0.9569 0.0000\nvn -0.9952 -0.0980 0.0000\nvn -0.3959 -0.2116 -0.8936\nvn 0.0000 1.0000 0.0000\nvn -0.1951 0.9808 0.0000\nvn 0.4468 0.0440 -0.8936\nvn 0.0980 -0.9952 0.0000\nvn -0.1951 -0.9808 0.0000\nvn -1.0000 0.0000 0.0000\nvn -0.0440 0.4468 -0.8936\nvn 0.9952 0.0980 0.0000\nvn 0.9808 -0.1951 0.0000\nvn -0.0000 -1.0000 0.0000\nvn -0.9569 0.2903 0.0000\nvn -0.4468 -0.0440 -0.8936\nvn 0.3827 0.9239 0.0000\nvn 0.9569 -0.2903 0.0000\nvn 0.4296 -0.1303 -0.8936\nvn -0.2903 -0.9569 -0.0000\nvn 0.5798 0.8148 0.0000\nvn -0.4097 0.9122 0.0000\nvn 0.0348 0.1148 -0.9928\nvn 0.0348 0.1148 0.9928\nvn 0.8474 0.5309 0.0000\nvn -0.0294 0.9996 0.0000\nvn 0.0761 0.0927 -0.9928\nvn 0.0761 0.0927 0.9928\nvn 0.9861 0.1662 0.0000\nvn 0.3553 0.9347 0.0000\nvn 0.1058 0.0565 -0.9928\nvn 0.1058 0.0565 0.9928\nvn 0.9746 -0.2239 0.0000\nvn 0.6860 0.7276 0.0000\nvn 0.1193 0.0118 -0.9928\nvn 0.1193 0.0118 0.9928\nvn 0.8148 -0.5798 0.0000\nvn 0.9122 0.4097 0.0000\nvn 0.1147 -0.0348 -0.9928\nvn 0.1147 -0.0348 0.9928\nvn 0.5309 -0.8475 0.0000\nvn 0.9996 0.0294 0.0000\nvn 0.0927 -0.0761 -0.9928\nvn 0.0927 -0.0761 0.9928\nvn 0.1662 -0.9861 0.0000\nvn 0.9347 -0.3553 0.0000\nvn 0.0565 -0.1058 -0.9928\nvn 0.0565 -0.1058 0.9928\nvn -0.2239 -0.9746 0.0000\nvn 0.7276 -0.6860 0.0000\nvn 0.0118 -0.1193 -0.9928\nvn 0.0118 -0.1193 0.9928\nvn -0.5798 -0.8148 0.0000\nvn 0.4097 -0.9122 0.0000\nvn -0.0348 -0.1147 -0.9928\nvn -0.0348 -0.1148 0.9928\nvn -0.8474 -0.5309 0.0000\nvn 0.0294 -0.9996 0.0000\nvn -0.0761 -0.0927 -0.9928\nvn -0.0761 -0.0927 0.9928\nvn -0.9861 -0.1662 0.0000\nvn -0.3553 -0.9347 0.0000\nvn -0.1058 -0.0565 -0.9928\nvn -0.1058 -0.0565 0.9928\nvn -0.9746 0.2239 0.0000\nvn -0.6860 -0.7276 0.0000\nvn -0.1193 -0.0118 -0.9928\nvn -0.1193 -0.0118 0.9928\nvn -0.8148 0.5798 0.0000\nvn -0.9122 -0.4097 0.0000\nvn -0.1148 0.0348 -0.9928\nvn -0.1147 0.0348 0.9928\nvn -0.5309 0.8475 0.0000\nvn -0.9996 -0.0294 0.0000\nvn -0.0927 0.0761 -0.9928\nvn -0.0927 0.0761 0.9928\nvn -0.1662 0.9861 0.0000\nvn -0.9347 0.3553 -0.0000\nvn -0.0565 0.1058 -0.9928\nvn -0.0565 0.1058 0.9928\nvn 0.2239 0.9746 0.0000\nvn -0.7276 0.6860 0.0000\nvn -0.0118 0.1193 -0.9928\nvn -0.0118 0.1193 0.9928\nvn -0.0980 0.9952 0.0000\nvn -0.1523 -0.1856 0.9707\nvn 0.0418 0.4241 0.9046\nvn 0.4078 -0.1237 0.9046\nvn -0.2704 -0.3294 0.9046\nvn -0.2704 0.3294 0.9046\nvn 0.3759 0.2009 0.9046\nvn 0.1237 -0.4078 0.9046\nvn -0.4241 0.0418 0.9046\nvn 0.1237 0.4078 0.9046\nvn 0.3759 -0.2009 0.9046\nvn -0.3294 -0.2704 0.9046\nvn -0.2009 0.3759 0.9046\nvn 0.4078 0.1237 0.9046\nvn -0.0418 -0.4241 0.9046\nvn 0.0418 -0.4241 0.9046\nvn -0.4078 0.1237 0.9046\nvn 0.2009 0.3759 0.9046\nvn 0.3294 -0.2704 0.9046\nvn -0.3759 -0.2009 0.9046\nvn -0.1237 0.4078 0.9046\nvn 0.4241 0.0418 0.9046\nvn -0.1237 -0.4078 0.9046\nvn -0.3759 0.2009 0.9046\nvn 0.2704 0.3294 0.9046\nvn 0.2704 -0.3294 0.9046\nvn -0.4078 -0.1237 0.9046\nvn -0.0418 0.4241 0.9046\nvn 0.4241 -0.0418 0.9046\nvn -0.2009 -0.3759 0.9046\nvn -0.3294 0.2704 0.9046\nvn 0.3294 0.2704 0.9046\nvn 0.2009 -0.3759 0.9046\nvn 0.3933 0.1193 0.9117\nvn 0.2298 0.0697 0.9707\nvn -0.1856 -0.1523 0.9707\nvn 0.2390 0.0235 0.9707\nvn -0.2118 -0.1132 0.9707\nvn 0.2390 -0.0235 0.9707\nvn -0.2298 -0.0697 0.9707\nvn 0.2298 -0.0697 0.9707\nvn -0.2390 -0.0235 0.9707\nvn 0.2118 -0.1132 0.9707\nvn -0.2390 0.0235 0.9707\nvn 0.1856 -0.1523 0.9707\nvn -0.2298 0.0697 0.9707\nvn 0.1523 -0.1856 0.9707\nvn -0.2118 0.1132 0.9707\nvn 0.1132 -0.2118 0.9707\nvn 0.0235 0.2390 0.9707\nvn -0.1856 0.1523 0.9707\nvn 0.0697 -0.2298 0.9707\nvn 0.0697 0.2298 0.9707\nvn -0.1523 0.1856 0.9707\nvn 0.0235 -0.2390 0.9707\nvn 0.1132 0.2118 0.9707\nvn -0.1132 0.2118 0.9707\nvn -0.0235 -0.2390 0.9707\nvn 0.1523 0.1856 0.9707\nvn -0.0697 0.2298 0.9707\nvn -0.0697 -0.2298 0.9707\nvn 0.1856 0.1523 0.9707\nvn -0.0235 0.2390 0.9707\nvn -0.1132 -0.2118 0.9707\nvn 0.2118 0.1132 0.9707\nvn 0.0000 0.0000 1.0000\nvn -0.3933 0.1193 0.9117\nvn 0.4090 0.0403 0.9117\nvn -0.3624 0.1937 0.9117\nvn 0.4090 -0.0403 0.9117\nvn -0.3177 0.2607 0.9117\nvn 0.3933 -0.1193 0.9117\nvn -0.2607 0.3177 0.9117\nvn 0.3624 -0.1937 0.9117\nvn -0.1937 0.3624 0.9117\nvn -0.0403 -0.4090 0.9117\nvn 0.3177 -0.2607 0.9117\nvn -0.1193 0.3933 0.9117\nvn -0.1193 -0.3933 0.9117\nvn 0.2607 -0.3177 0.9117\nvn -0.0403 0.4090 0.9117\nvn -0.1937 -0.3624 0.9117\nvn 0.1937 -0.3624 0.9117\nvn 0.0403 0.4090 0.9117\nvn -0.2607 -0.3177 0.9117\nvn 0.1193 -0.3933 0.9117\nvn 0.1193 0.3933 0.9117\nvn -0.3177 -0.2607 0.9117\nvn 0.0403 -0.4090 0.9117\nvn 0.1937 0.3624 0.9117\nvn -0.3624 -0.1937 0.9117\nvn 0.2607 0.3177 0.9117\nvn -0.3933 -0.1193 0.9117\nvn 0.3177 0.2607 0.9117\nvn -0.4090 -0.0403 0.9117\nvn 0.3624 0.1937 0.9117\nvn -0.4090 0.0403 0.9117\nvn 0.8475 0.5309 0.0000\nvn 0.1148 -0.0348 -0.9928\nvn 0.1148 -0.0348 0.9928\nvn -0.0348 -0.1148 -0.9928\nvn -0.8475 -0.5309 0.0000\nvn -0.1148 0.0348 0.9928\ns off\nf 2/1/1 3/2/1 1/3/1\nf 4/4/2 72/5/2 3/2/2\nf 6/6/3 7/7/3 5/8/3\nf 39/9/4 146/10/4 41/11/4\nf 10/12/5 11/13/5 9/14/5\nf 56/15/6 176/16/6 55/17/6\nf 14/18/7 15/19/7 13/20/7\nf 5/8/8 75/21/8 6/6/8\nf 18/22/9 19/23/9 17/24/9\nf 24/25/10 112/26/10 23/27/10\nf 22/28/11 23/27/11 21/29/11\nf 57/30/12 179/31/12 58/32/12\nf 26/33/13 27/34/13 25/35/13\nf 5/36/14 72/37/14 74/38/14\nf 30/39/15 31/40/15 29/41/15\nf 20/42/16 104/43/16 19/23/16\nf 34/44/17 35/45/17 33/46/17\nf 55/47/18 178/48/18 57/49/18\nf 38/50/19 39/51/19 37/52/19\nf 2/53/20 189/54/20 64/55/20\nf 42/56/21 43/57/21 41/58/21\nf 21/29/22 107/59/22 22/28/22\nf 46/60/23 47/61/23 45/62/23\nf 142/63/24 145/64/24 143/65/24\nf 50/66/25 51/67/25 49/68/25\nf 21/69/26 104/70/26 106/71/26\nf 54/72/27 55/17/27 53/73/27\nf 35/74/28 138/75/28 37/76/28\nf 58/32/29 59/77/29 57/30/29\nf 37/52/30 139/78/30 38/50/30\nf 16/79/31 201/80/31 18/81/31\nf 62/82/32 63/83/32 61/84/32\nf 174/85/33 177/86/33 175/87/33\nf 15/88/34 31/89/34 47/90/34\nf 6/91/35 69/92/35 4/93/35\nf 10/94/36 77/95/36 8/96/36\nf 12/97/37 91/98/37 85/99/37\nf 18/100/38 93/101/38 16/102/38\nf 22/103/39 101/104/39 20/105/39\nf 26/106/40 109/107/40 24/108/40\nf 30/109/41 117/110/41 28/111/41\nf 34/112/42 125/113/42 32/114/42\nf 36/115/43 139/116/43 133/117/43\nf 42/118/44 141/119/44 40/120/44\nf 46/121/45 149/122/45 44/123/45\nf 48/124/46 163/125/46 157/126/46\nf 52/127/47 171/128/47 165/129/47\nf 56/130/48 179/131/48 173/132/48\nf 62/133/49 181/134/49 60/135/49\nf 63/136/50 65/137/50 1/138/50\nf 60/139/51 184/140/51 59/77/51\nf 41/58/52 147/141/52 42/56/52\nf 8/142/53 80/143/53 7/7/53\nf 40/144/54 144/145/54 39/51/54\nf 110/146/55 113/147/55 111/148/55\nf 44/149/56 152/150/56 43/57/56\nf 25/35/57 115/151/57 26/33/57\nf 182/152/58 185/153/58 183/154/58\nf 23/155/59 114/156/59 25/157/59\nf 78/158/60 81/159/60 79/160/60\nf 61/84/61 187/161/61 62/82/61\nf 28/162/62 120/163/62 27/34/62\nf 9/14/63 83/164/63 10/12/63\nf 59/165/64 186/166/64 61/167/64\nf 150/168/65 153/169/65 151/170/65\nf 9/171/66 80/172/66 82/173/66\nf 64/174/67 192/175/67 63/83/67\nf 45/62/68 155/176/68 46/60/68\nf 12/177/69 88/178/69 11/13/69\nf 43/179/70 154/180/70 45/181/70\nf 118/182/71 121/183/71 119/184/71\nf 48/185/72 160/186/72 47/61/72\nf 29/41/73 123/187/73 30/39/73\nf 27/188/74 122/189/74 29/190/74\nf 86/191/75 89/192/75 87/193/75\nf 1/3/76 68/194/76 2/1/76\nf 32/195/77 128/196/77 31/40/77\nf 13/20/78 91/197/78 14/18/78\nf 70/198/79 73/199/79 71/200/79\nf 158/201/80 161/202/80 159/203/80\nf 11/204/81 90/205/81 13/206/81\nf 49/68/82 163/207/82 50/66/82\nf 16/208/83 96/209/83 15/19/83\nf 47/90/84 162/210/84 49/211/84\nf 126/212/85 129/213/85 127/214/85\nf 52/215/86 168/216/86 51/67/86\nf 33/46/87 131/217/87 34/44/87\nf 31/89/88 130/218/88 33/219/88\nf 94/220/89 97/221/89 95/222/89\nf 36/223/90 136/224/90 35/45/90\nf 17/24/91 99/225/91 18/22/91\nf 166/226/92 169/227/92 167/228/92\nf 15/88/93 98/229/93 17/230/93\nf 53/73/94 171/231/94 54/72/94\nf 102/232/95 105/233/95 103/234/95\nf 51/235/96 170/236/96 53/237/96\nf 134/238/97 137/239/97 135/240/97\nf 66/241/98 68/242/98 65/243/98\nf 70/244/99 72/245/99 69/246/99\nf 71/247/100 74/248/100 72/245/100\nf 70/244/101 75/249/101 76/250/101\nf 74/248/102 76/250/102 75/249/102\nf 78/251/103 80/252/103 77/253/103\nf 81/254/104 80/255/104 79/256/104\nf 84/257/105 77/258/105 83/259/105\nf 82/260/106 84/257/106 83/261/106\nf 87/262/107 85/263/107 86/264/107\nf 89/265/108 88/266/108 87/262/108\nf 86/264/109 91/267/109 92/268/109\nf 90/269/110 92/268/110 91/270/110\nf 95/271/111 93/272/111 94/273/111\nf 95/271/112 98/274/112 96/275/112\nf 94/273/113 99/276/113 100/277/113\nf 98/274/114 100/277/114 99/276/114\nf 103/278/115 101/279/115 102/280/115\nf 105/281/116 104/282/116 103/278/116\nf 102/280/117 107/283/117 108/284/117\nf 106/285/118 108/284/118 107/286/118\nf 111/287/119 109/288/119 110/289/119\nf 113/290/120 112/291/120 111/287/120\nf 116/292/121 109/293/121 115/294/121\nf 114/295/122 116/292/122 115/296/122\nf 119/297/123 117/298/123 118/299/123\nf 119/297/124 122/300/124 120/301/124\nf 118/299/125 123/302/125 124/303/125\nf 122/300/126 124/303/126 123/302/126\nf 126/304/127 128/305/127 125/306/127\nf 129/307/128 128/305/128 127/308/128\nf 132/309/129 125/306/129 131/310/129\nf 130/311/130 132/309/130 131/310/130\nf 135/312/131 133/313/131 134/314/131\nf 137/315/132 136/316/132 135/312/132\nf 140/317/133 133/318/133 139/319/133\nf 138/320/134 140/317/134 139/321/134\nf 142/322/135 144/323/135 141/324/135\nf 145/325/136 144/326/136 143/327/136\nf 142/322/137 147/328/137 148/329/137\nf 146/330/138 148/329/138 147/328/138\nf 151/331/139 149/332/139 150/333/139\nf 153/334/140 152/335/140 151/331/140\nf 150/333/141 155/336/141 156/337/141\nf 154/338/142 156/337/142 155/336/142\nf 159/339/143 157/340/143 158/341/143\nf 159/339/144 162/342/144 160/343/144\nf 158/341/145 163/344/145 164/345/145\nf 162/346/146 164/345/146 163/347/146\nf 167/348/147 165/349/147 166/350/147\nf 169/351/148 168/352/148 167/348/148\nf 166/350/149 171/353/149 172/354/149\nf 170/355/150 172/354/150 171/356/150\nf 175/357/151 173/358/151 174/359/151\nf 177/360/152 176/361/152 175/357/152\nf 174/359/153 179/362/153 180/363/153\nf 178/364/154 180/363/154 179/362/154\nf 183/365/155 181/366/155 182/367/155\nf 185/368/156 184/369/156 183/365/156\nf 188/370/157 181/366/157 187/371/157\nf 186/372/158 188/370/158 187/373/158\nf 191/374/159 189/375/159 190/376/159\nf 66/241/160 192/377/160 191/374/160\nf 67/378/161 189/379/161 68/380/161\nf 190/381/162 66/382/162 191/383/162\nf 213/384/163 244/385/163 245/386/163\nf 34/387/164 210/388/164 36/389/164\nf 54/390/165 218/391/165 219/392/165\nf 8/393/166 197/394/166 10/395/166\nf 26/396/167 206/397/167 28/398/167\nf 44/399/168 215/400/168 46/401/168\nf 62/402/169 224/403/169 64/404/169\nf 18/405/170 202/406/170 20/407/170\nf 36/408/171 211/409/171 38/410/171\nf 54/411/172 220/412/172 56/413/172\nf 10/414/173 198/415/173 12/416/173\nf 28/417/174 207/418/174 30/419/174\nf 48/420/175 215/421/175 216/422/175\nf 2/423/176 193/424/176 4/425/176\nf 2/426/177 224/427/177 194/428/177\nf 20/429/178 203/430/178 22/431/178\nf 38/432/179 212/433/179 40/434/179\nf 58/435/180 220/436/180 221/437/180\nf 12/438/181 199/439/181 14/440/181\nf 30/441/182 208/442/182 32/443/182\nf 48/444/183 217/445/183 50/446/183\nf 6/447/184 193/448/184 195/449/184\nf 22/450/185 204/451/185 24/452/185\nf 40/453/186 213/454/186 42/455/186\nf 60/456/187 221/457/187 222/458/187\nf 14/459/188 200/460/188 16/461/188\nf 32/462/189 209/463/189 34/464/189\nf 52/465/190 217/466/190 218/467/190\nf 8/468/191 195/469/191 196/470/191\nf 24/471/192 205/472/192 26/473/192\nf 44/474/193 213/475/193 214/476/193\nf 62/477/194 222/478/194 223/479/194\nf 248/480/195 279/481/195 280/482/195\nf 199/483/196 232/484/196 200/485/196\nf 214/486/197 245/487/197 246/488/197\nf 200/489/198 233/490/198 201/491/198\nf 214/492/199 247/493/199 215/494/199\nf 201/495/200 234/496/200 202/497/200\nf 216/498/201 247/499/201 248/500/201\nf 203/501/202 234/502/202 235/503/202\nf 217/504/203 248/505/203 249/506/203\nf 203/507/204 236/508/204 204/509/204\nf 217/510/205 250/511/205 218/512/205\nf 204/513/206 237/514/206 205/515/206\nf 219/516/207 250/517/207 251/518/207\nf 205/519/208 238/520/208 206/521/208\nf 219/522/209 252/523/209 220/524/209\nf 206/525/210 239/526/210 207/527/210\nf 194/528/211 225/529/211 193/530/211\nf 221/531/212 252/532/212 253/533/212\nf 207/534/213 240/535/213 208/536/213\nf 195/537/214 225/538/214 227/539/214\nf 222/540/215 253/541/215 254/542/215\nf 208/543/216 241/544/216 209/545/216\nf 196/546/217 227/547/217 228/548/217\nf 222/549/218 255/550/218 223/551/218\nf 209/552/219 242/553/219 210/554/219\nf 196/555/220 229/556/220 197/557/220\nf 224/558/221 255/559/221 256/560/221\nf 211/561/222 242/562/222 243/563/222\nf 197/564/223 230/565/223 198/566/223\nf 194/567/224 256/568/224 226/569/224\nf 211/570/225 244/571/225 212/572/225\nf 198/573/226 231/574/226 199/575/226\nf 283/576/227 275/577/227 267/578/227\nf 234/579/228 267/580/228 235/581/228\nf 249/582/229 280/583/229 281/584/229\nf 235/585/230 268/586/230 236/587/230\nf 250/588/231 281/589/231 282/590/231\nf 236/591/232 269/592/232 237/593/232\nf 251/594/233 282/595/233 283/596/233\nf 237/597/234 270/598/234 238/599/234\nf 251/600/235 284/601/235 252/602/235\nf 238/603/236 271/604/236 239/605/236\nf 226/606/237 257/607/237 225/608/237\nf 252/609/238 285/610/238 253/611/238\nf 239/612/239 272/613/239 240/614/239\nf 225/615/240 259/616/240 227/617/240\nf 253/618/241 286/619/241 254/620/241\nf 240/621/242 273/622/242 241/623/242\nf 227/624/243 260/625/243 228/626/243\nf 254/627/244 287/628/244 255/629/244\nf 241/630/245 274/631/245 242/632/245\nf 228/633/246 261/634/246 229/635/246\nf 255/636/247 288/637/247 256/638/247\nf 243/639/248 274/640/248 275/641/248\nf 229/642/249 262/643/249 230/644/249\nf 256/645/250 258/646/250 226/647/250\nf 243/648/251 276/649/251 244/650/251\nf 230/651/252 263/652/252 231/653/252\nf 245/654/253 276/655/253 277/656/253\nf 231/657/254 264/658/254 232/659/254\nf 246/660/255 277/661/255 278/662/255\nf 232/663/256 265/664/256 233/665/256\nf 246/666/257 279/667/257 247/668/257\nf 234/669/258 265/670/258 266/671/258\nf 2/1/1 4/4/1 3/2/1\nf 4/4/2 69/672/2 72/5/2\nf 6/6/3 8/142/3 7/7/3\nf 39/9/4 144/673/4 146/10/4\nf 10/12/5 12/177/5 11/13/5\nf 56/15/6 173/674/6 176/16/6\nf 14/18/7 16/208/7 15/19/7\nf 5/8/8 74/675/8 75/21/8\nf 18/22/9 20/42/9 19/23/9\nf 24/25/10 109/676/10 112/26/10\nf 22/28/11 24/25/11 23/27/11\nf 57/30/12 178/677/12 179/31/12\nf 26/33/13 28/162/13 27/34/13\nf 5/36/14 3/678/14 72/37/14\nf 30/39/15 32/195/15 31/40/15\nf 20/42/16 101/679/16 104/43/16\nf 34/44/17 36/223/17 35/45/17\nf 55/47/18 176/680/18 178/48/18\nf 38/50/19 40/144/19 39/51/19\nf 2/53/20 68/681/20 189/54/20\nf 42/56/21 44/149/21 43/57/21\nf 21/29/22 106/682/22 107/59/22\nf 46/60/23 48/185/23 47/61/23\nf 142/63/24 148/683/24 145/64/24\nf 50/66/25 52/215/25 51/67/25\nf 21/69/26 19/684/26 104/70/26\nf 54/72/27 56/15/27 55/17/27\nf 35/74/28 136/685/28 138/75/28\nf 58/32/29 60/139/29 59/77/29\nf 37/52/30 138/686/30 139/78/30\nf 16/79/31 200/687/31 201/80/31\nf 62/82/32 64/174/32 63/83/32\nf 174/85/33 180/688/33 177/86/33\nf 63/136/34 1/138/34 3/678/34\nf 3/678/34 5/36/34 7/689/34\nf 7/689/34 9/171/34 11/204/34\nf 11/204/34 13/206/34 7/689/34\nf 13/206/34 15/88/34 7/689/34\nf 15/88/34 17/230/34 19/684/34\nf 19/684/34 21/69/34 15/88/34\nf 21/69/34 23/155/34 15/88/34\nf 23/155/34 25/157/34 31/89/34\nf 25/157/34 27/188/34 31/89/34\nf 27/188/34 29/190/34 31/89/34\nf 31/89/34 33/219/34 35/74/34\nf 35/74/34 37/76/34 39/9/34\nf 39/9/34 41/11/34 47/90/34\nf 41/11/34 43/179/34 47/90/34\nf 43/179/34 45/181/34 47/90/34\nf 47/90/34 49/211/34 51/235/34\nf 51/235/34 53/237/34 55/47/34\nf 55/47/34 57/49/34 59/165/34\nf 59/165/34 61/167/34 63/136/34\nf 63/136/34 3/678/34 7/689/34\nf 31/89/34 35/74/34 47/90/34\nf 35/74/34 39/9/34 47/90/34\nf 47/90/34 51/235/34 63/136/34\nf 51/235/34 55/47/34 63/136/34\nf 55/47/34 59/165/34 63/136/34\nf 63/136/34 7/689/34 15/88/34\nf 15/88/34 23/155/34 31/89/34\nf 63/136/34 15/88/34 47/90/34\nf 6/91/35 75/690/35 69/92/35\nf 10/94/36 83/691/36 77/95/36\nf 12/97/37 14/692/37 91/98/37\nf 18/100/38 99/693/38 93/101/38\nf 22/103/39 107/694/39 101/104/39\nf 26/106/40 115/695/40 109/107/40\nf 30/109/41 123/696/41 117/110/41\nf 34/112/42 131/697/42 125/113/42\nf 36/115/43 38/698/43 139/116/43\nf 42/118/44 147/699/44 141/119/44\nf 46/121/45 155/700/45 149/122/45\nf 48/124/46 50/701/46 163/125/46\nf 52/127/47 54/702/47 171/128/47\nf 56/130/48 58/703/48 179/131/48\nf 62/133/49 187/704/49 181/134/49\nf 63/136/50 192/705/50 65/137/50\nf 60/139/51 181/706/51 184/140/51\nf 41/58/52 146/707/52 147/141/52\nf 8/142/53 77/708/53 80/143/53\nf 40/144/54 141/709/54 144/145/54\nf 110/146/55 116/710/55 113/147/55\nf 44/149/56 149/711/56 152/150/56\nf 25/35/57 114/712/57 115/151/57\nf 182/152/58 188/713/58 185/153/58\nf 23/155/59 112/714/59 114/156/59\nf 78/158/60 84/715/60 81/159/60\nf 61/84/61 186/716/61 187/161/61\nf 28/162/62 117/717/62 120/163/62\nf 9/14/63 82/718/63 83/164/63\nf 59/165/64 184/719/64 186/166/64\nf 150/168/65 156/720/65 153/169/65\nf 9/171/66 7/689/66 80/172/66\nf 64/174/67 189/721/67 192/175/67\nf 45/62/68 154/722/68 155/176/68\nf 12/177/69 85/723/69 88/178/69\nf 43/179/70 152/724/70 154/180/70\nf 118/182/71 124/725/71 121/183/71\nf 48/185/72 157/726/72 160/186/72\nf 29/41/73 122/727/73 123/187/73\nf 27/188/74 120/728/74 122/189/74\nf 86/191/75 92/729/75 89/192/75\nf 1/3/76 65/730/76 68/194/76\nf 32/195/77 125/731/77 128/196/77\nf 13/20/78 90/732/78 91/197/78\nf 70/198/79 76/733/79 73/199/79\nf 158/201/80 164/734/80 161/202/80\nf 11/204/81 88/735/81 90/205/81\nf 49/68/82 162/736/82 163/207/82\nf 16/208/83 93/737/83 96/209/83\nf 47/90/84 160/738/84 162/210/84\nf 126/212/85 132/739/85 129/213/85\nf 52/215/86 165/740/86 168/216/86\nf 33/46/87 130/741/87 131/217/87\nf 31/89/88 128/742/88 130/218/88\nf 94/220/89 100/743/89 97/221/89\nf 36/223/90 133/744/90 136/224/90\nf 17/24/91 98/745/91 99/225/91\nf 166/226/92 172/746/92 169/227/92\nf 15/88/93 96/747/93 98/229/93\nf 53/73/94 170/748/94 171/231/94\nf 102/232/95 108/749/95 105/233/95\nf 51/235/96 168/750/96 170/236/96\nf 134/238/97 140/751/97 137/239/97\nf 66/241/98 67/378/98 68/242/98\nf 70/244/99 71/247/99 72/245/99\nf 71/247/100 73/752/100 74/248/100\nf 70/244/101 69/246/101 75/249/101\nf 74/248/259 73/752/259 76/250/259\nf 78/251/103 79/256/103 80/252/103\nf 81/254/104 82/753/104 80/255/104\nf 84/257/105 78/251/105 77/258/105\nf 82/260/106 81/254/106 84/257/106\nf 87/262/107 88/754/107 85/263/107\nf 89/265/108 90/755/108 88/266/108\nf 86/264/109 85/756/109 91/267/109\nf 90/269/110 89/265/110 92/268/110\nf 95/271/111 96/275/111 93/272/111\nf 95/271/112 97/757/112 98/274/112\nf 94/273/113 93/272/113 99/276/113\nf 98/274/114 97/757/114 100/277/114\nf 103/278/115 104/282/115 101/279/115\nf 105/281/260 106/758/260 104/282/260\nf 102/280/261 101/279/261 107/283/261\nf 106/285/118 105/281/118 108/284/118\nf 111/287/119 112/759/119 109/288/119\nf 113/290/120 114/760/120 112/291/120\nf 116/292/121 110/289/121 109/293/121\nf 114/295/122 113/290/122 116/292/122\nf 119/297/123 120/301/123 117/298/123\nf 119/297/124 121/761/124 122/300/124\nf 118/299/125 117/298/125 123/302/125\nf 122/300/126 121/761/126 124/303/126\nf 126/304/127 127/308/127 128/305/127\nf 129/307/128 130/311/128 128/305/128\nf 132/309/129 126/304/129 125/306/129\nf 130/311/130 129/307/130 132/309/130\nf 135/312/131 136/762/131 133/313/131\nf 137/315/262 138/763/262 136/316/262\nf 140/317/133 134/314/133 133/318/133\nf 138/320/263 137/315/263 140/317/263\nf 142/322/135 143/327/135 144/323/135\nf 145/325/136 146/330/136 144/326/136\nf 142/322/137 141/764/137 147/328/137\nf 146/330/138 145/325/138 148/329/138\nf 151/331/139 152/335/139 149/332/139\nf 153/334/140 154/338/140 152/335/140\nf 150/333/141 149/332/141 155/336/141\nf 154/338/142 153/334/142 156/337/142\nf 159/339/143 160/765/143 157/340/143\nf 159/339/144 161/766/144 162/342/144\nf 158/341/145 157/767/145 163/344/145\nf 162/346/146 161/766/146 164/345/146\nf 167/348/147 168/768/147 165/349/147\nf 169/351/148 170/769/148 168/352/148\nf 166/350/264 165/770/264 171/353/264\nf 170/355/150 169/351/150 172/354/150\nf 175/357/151 176/361/151 173/358/151\nf 177/360/152 178/364/152 176/361/152\nf 174/359/153 173/358/153 179/362/153\nf 178/364/154 177/360/154 180/363/154\nf 183/365/155 184/369/155 181/366/155\nf 185/368/156 186/771/156 184/369/156\nf 188/370/157 182/367/157 181/366/157\nf 186/372/158 185/368/158 188/370/158\nf 191/374/159 192/772/159 189/375/159\nf 66/241/160 65/773/160 192/377/160\nf 67/378/161 190/376/161 189/379/161\nf 190/381/162 67/774/162 66/382/162\nf 213/384/163 212/775/163 244/385/163\nf 34/387/164 209/776/164 210/388/164\nf 54/390/165 52/777/165 218/391/165\nf 8/393/166 196/778/166 197/394/166\nf 26/396/167 205/779/167 206/397/167\nf 44/399/168 214/780/168 215/400/168\nf 62/402/169 223/781/169 224/403/169\nf 18/405/170 201/782/170 202/406/170\nf 36/408/171 210/783/171 211/409/171\nf 54/411/172 219/784/172 220/412/172\nf 10/414/173 197/785/173 198/415/173\nf 28/417/174 206/786/174 207/418/174\nf 48/420/175 46/787/175 215/421/175\nf 2/423/176 194/788/176 193/424/176\nf 2/426/177 64/789/177 224/427/177\nf 20/429/178 202/790/178 203/430/178\nf 38/432/179 211/791/179 212/433/179\nf 58/435/180 56/792/180 220/436/180\nf 12/438/181 198/793/181 199/439/181\nf 30/441/182 207/794/182 208/442/182\nf 48/444/183 216/795/183 217/445/183\nf 6/447/184 4/796/184 193/448/184\nf 22/450/185 203/797/185 204/451/185\nf 40/453/186 212/798/186 213/454/186\nf 60/456/187 58/799/187 221/457/187\nf 14/459/188 199/800/188 200/460/188\nf 32/462/189 208/801/189 209/463/189\nf 52/465/190 50/802/190 217/466/190\nf 8/468/191 6/803/191 195/469/191\nf 24/471/192 204/804/192 205/472/192\nf 44/474/193 42/805/193 213/475/193\nf 62/477/194 60/806/194 222/478/194\nf 248/480/195 247/807/195 279/481/195\nf 199/483/196 231/808/196 232/484/196\nf 214/486/197 213/809/197 245/487/197\nf 200/489/198 232/810/198 233/490/198\nf 214/492/199 246/811/199 247/493/199\nf 201/495/200 233/812/200 234/496/200\nf 216/498/201 215/813/201 247/499/201\nf 203/501/202 202/814/202 234/502/202\nf 217/504/203 216/815/203 248/505/203\nf 203/507/204 235/816/204 236/508/204\nf 217/510/205 249/817/205 250/511/205\nf 204/513/206 236/818/206 237/514/206\nf 219/516/207 218/819/207 250/517/207\nf 205/519/208 237/820/208 238/520/208\nf 219/522/209 251/821/209 252/523/209\nf 206/525/210 238/822/210 239/526/210\nf 194/528/211 226/823/211 225/529/211\nf 221/531/212 220/824/212 252/532/212\nf 207/534/213 239/825/213 240/535/213\nf 195/537/214 193/826/214 225/538/214\nf 222/540/215 221/827/215 253/541/215\nf 208/543/216 240/828/216 241/544/216\nf 196/546/217 195/829/217 227/547/217\nf 222/549/218 254/830/218 255/550/218\nf 209/552/219 241/831/219 242/553/219\nf 196/555/220 228/832/220 229/556/220\nf 224/558/221 223/833/221 255/559/221\nf 211/561/222 210/834/222 242/562/222\nf 197/564/223 229/835/223 230/565/223\nf 194/567/224 224/836/224 256/568/224\nf 211/570/225 243/837/225 244/571/225\nf 198/573/226 230/838/226 231/574/226\nf 259/839/227 257/840/227 258/841/227\nf 258/841/227 288/842/227 287/843/227\nf 287/843/227 286/844/227 283/576/227\nf 286/844/227 285/845/227 283/576/227\nf 285/845/227 284/846/227 283/576/227\nf 283/576/227 282/847/227 281/848/227\nf 281/848/227 280/849/227 279/850/227\nf 279/850/227 278/851/227 275/577/227\nf 278/851/227 277/852/227 275/577/227\nf 277/852/227 276/853/227 275/577/227\nf 275/577/227 274/854/227 273/855/227\nf 273/855/227 272/856/227 271/857/227\nf 271/857/227 270/858/227 269/859/227\nf 269/859/227 268/860/227 267/578/227\nf 267/578/227 266/861/227 265/862/227\nf 265/862/227 264/863/227 267/578/227\nf 264/863/227 263/864/227 267/578/227\nf 263/864/227 262/865/227 261/866/227\nf 261/866/227 260/867/227 263/864/227\nf 260/867/227 259/839/227 263/864/227\nf 259/839/227 258/841/227 283/576/227\nf 258/841/227 287/843/227 283/576/227\nf 283/576/227 281/848/227 275/577/227\nf 281/848/227 279/850/227 275/577/227\nf 275/577/227 273/855/227 271/857/227\nf 271/857/227 269/859/227 275/577/227\nf 269/859/227 267/578/227 275/577/227\nf 267/578/227 263/864/227 259/839/227\nf 259/839/227 283/576/227 267/578/227\nf 234/579/228 266/868/228 267/580/228\nf 249/582/229 248/869/229 280/583/229\nf 235/585/230 267/870/230 268/586/230\nf 250/588/231 249/871/231 281/589/231\nf 236/591/232 268/872/232 269/592/232\nf 251/594/233 250/873/233 282/595/233\nf 237/597/234 269/874/234 270/598/234\nf 251/600/235 283/875/235 284/601/235\nf 238/603/236 270/876/236 271/604/236\nf 226/606/237 258/877/237 257/607/237\nf 252/609/238 284/878/238 285/610/238\nf 239/612/239 271/879/239 272/613/239\nf 225/615/240 257/880/240 259/616/240\nf 253/618/241 285/881/241 286/619/241\nf 240/621/242 272/882/242 273/622/242\nf 227/624/243 259/883/243 260/625/243\nf 254/627/244 286/884/244 287/628/244\nf 241/630/245 273/885/245 274/631/245\nf 228/633/246 260/886/246 261/634/246\nf 255/636/247 287/887/247 288/637/247\nf 243/639/248 242/888/248 274/640/248\nf 229/642/249 261/889/249 262/643/249\nf 256/645/250 288/890/250 258/646/250\nf 243/648/251 275/891/251 276/649/251\nf 230/651/252 262/892/252 263/652/252\nf 245/654/253 244/893/253 276/655/253\nf 231/657/254 263/894/254 264/658/254\nf 246/660/255 245/895/255 277/661/255\nf 232/663/256 264/896/256 265/664/256\nf 246/666/257 278/897/257 279/667/257\nf 234/669/258 233/898/258 265/670/258\n"
  },
  {
    "path": "src/asset/botdef/building_example.json",
    "content": "{\n  \"id\": {\n    \"value\": 1441884615969752781,\n    \"phantom\": null\n  },\n  \"file_path\": \"./src/asset/botdef/building_example.json\",\n  \"radius\": 0.5,\n  \"max_life\": 1000,\n  \"turn_accel\": 0.0,\n  \"max_turn_rate\": 0.0,\n  \"accel\": 0.0,\n  \"break_accel\": 0.0,\n  \"max_speed\": 0.0,\n  \"build_power\": 0.5,\n  \"build_dist\": 10.0,\n  \"metal_cost\": 100,\n  \"part_tree\": {\n    \"id\": {\n      \"value\": 19713591288447385,\n      \"phantom\": null\n    },\n    \"placed_mesh\": null,\n    \"placed_collider\": null,\n    \"parent_to_self\": [\n      1.0,\n      0.0,\n      0.0,\n      0.0,\n      0.0,\n      1.0,\n      0.0,\n      0.0,\n      0.0,\n      0.0,\n      1.0,\n      0.0,\n      0.0,\n      0.0,\n      0.0,\n      1.0\n    ],\n    \"joint\": \"Fix\",\n    \"children\": [\n      {\n        \"id\": {\n          \"value\": 6444314735306398051,\n          \"phantom\": null\n        },\n        \"placed_mesh\": {\n          \"trans\": [\n            1.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            1.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            1.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            1.0\n          ],\n          \"mesh_path\": \"./src/asset/3d/cube.obj\",\n          \"mesh_index\": 0\n        },\n        \"placed_collider\": null,\n        \"parent_to_self\": [\n          1.0,\n          0.0,\n          0.0,\n          0.0,\n          0.0,\n          1.0,\n          0.0,\n          0.0,\n          0.0,\n          0.0,\n          1.0,\n          0.0,\n          0.0,\n          0.0,\n          0.0,\n          1.0\n        ],\n        \"joint\": \"Fix\",\n        \"children\": []\n      }\n    ]\n  }\n}"
  },
  {
    "path": "src/asset/botdef/unit_example.json",
    "content": "{\n  \"id\": {\n    \"value\": 14418846159697527818,\n    \"phantom\": null\n  },\n  \"file_path\": \"./src/asset/botdef/unit_example.json\",\n  \"radius\": 0.5,\n  \"max_life\": 100,\n  \"turn_accel\": 0.44440976,\n  \"max_turn_rate\": 0.38327432,\n  \"accel\": 0.1,\n  \"break_accel\": 0.3,\n  \"max_speed\": 1.0,\n  \"build_power\": 0.5,\n  \"build_dist\": 10.0,\n  \"metal_cost\": 10,\n  \"part_tree\": {\n    \"id\": {\n      \"value\": 197135912884473854,\n      \"phantom\": null\n    },\n    \"placed_mesh\": null,\n    \"placed_collider\": null,\n    \"parent_to_self\": [\n      1.0,\n      0.0,\n      0.0,\n      0.0,\n      0.0,\n      1.0,\n      0.0,\n      0.0,\n      0.0,\n      0.0,\n      1.0,\n      0.0,\n      0.0,\n      0.0,\n      0.0,\n      1.0\n    ],\n    \"joint\": \"Fix\",\n    \"children\": [\n      {\n        \"id\": {\n          \"value\": 1756212347027302969,\n          \"phantom\": null\n        },\n        \"placed_mesh\": {\n          \"trans\": [\n            1.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            1.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            1.0,\n            0.0,\n            0.0,\n            0.0,\n            0.0,\n            1.0\n          ],\n          \"mesh_path\": \"./src/asset/3d/tank/base.obj\",\n          \"mesh_index\": 3\n        },\n        \"placed_collider\": null,\n        \"parent_to_self\": [\n          1.0,\n          0.0,\n          0.0,\n          0.0,\n          0.0,\n          1.0,\n          0.0,\n          0.0,\n          0.0,\n          0.0,\n          1.0,\n          0.0,\n          0.0,\n          0.0,\n          0.25,\n          1.0\n        ],\n        \"joint\": \"Fix\",\n        \"children\": [\n          {\n            \"id\": {\n              \"value\": 13491902060961674828,\n              \"phantom\": null\n            },\n            \"placed_mesh\": {\n              \"trans\": [\n                1.0,\n                0.0,\n                0.0,\n                0.0,\n                0.0,\n                1.0,\n                0.0,\n                0.0,\n                0.0,\n                0.0,\n                1.0,\n                0.0,\n                0.315,\n                0.005,\n                0.0,\n                1.0\n              ],\n              \"mesh_path\": \"./src/asset/3d/tank/canon.obj\",\n              \"mesh_index\": 5\n            },\n            \"placed_collider\": null,\n            \"parent_to_self\": [\n              1.0,\n              0.0,\n              0.0,\n              0.0,\n              0.0,\n              1.0,\n              0.0,\n              0.0,\n              0.0,\n              0.0,\n              1.0,\n              0.0,\n              -0.196,\n              0.0,\n              0.0,\n              1.0\n            ],\n            \"joint\": \"AimWeapon0\",\n            \"children\": []\n          },\n          {\n            \"id\": {\n              \"value\": 11290295935695058242,\n              \"phantom\": null\n            },\n            \"placed_mesh\": {\n              \"trans\": [\n                -0.0011959828,\n                -1.8058838e-10,\n                -0.9999993,\n                0.0,\n                2.3841828e-7,\n                1.0,\n                -4.657329e-10,\n                0.0,\n                0.9999993,\n                -2.384187e-7,\n                -0.0011959828,\n                0.0,\n                0.0,\n                0.0,\n                0.0,\n                1.0\n              ],\n              \"mesh_path\": \"./src/asset/3d/tank/wheel.obj\",\n              \"mesh_index\": 4\n            },\n            \"placed_collider\": null,\n            \"parent_to_self\": [\n              -0.99999875,\n              0.001592548,\n              0.0,\n              0.0,\n              -0.001592548,\n              -0.99999875,\n              0.0,\n              0.0,\n              0.0,\n              0.0,\n              1.0,\n              0.0,\n              -0.516,\n              -0.617,\n              0.0,\n              1.0\n            ],\n            \"joint\": \"Wheel0\",\n            \"children\": []\n          },\n          {\n            \"id\": {\n              \"value\": 6250614812945715198,\n              \"phantom\": null\n            },\n            \"placed_mesh\": {\n              \"trans\": [\n                0.010791883,\n                0.0,\n                -0.99994177,\n                0.0,\n                0.0,\n                1.0,\n                0.0,\n                0.0,\n                0.99994177,\n                0.0,\n                0.010791883,\n                0.0,\n                0.0,\n                0.0,\n                0.0,\n                1.0\n              ],\n              \"mesh_path\": \"./src/asset/3d/tank/wheel.obj\",\n              \"mesh_index\": 4\n            },\n            \"placed_collider\": null,\n            \"parent_to_self\": [\n              -0.99999875,\n              0.001592548,\n              0.0,\n              0.0,\n              -0.001592548,\n              -0.99999875,\n              0.0,\n              0.0,\n              0.0,\n              0.0,\n              1.0,\n              0.0,\n              0.518,\n              -0.619,\n              0.0,\n              1.0\n            ],\n            \"joint\": \"Wheel0\",\n            \"children\": []\n          },\n          {\n            \"id\": {\n              \"value\": 12952766970854183071,\n              \"phantom\": null\n            },\n            \"placed_mesh\": {\n              \"trans\": [\n                0.0042427215,\n                4.6193324e-7,\n                0.999991,\n                0.0,\n                -0.00010887664,\n                1.0,\n                0.0,\n                0.0,\n                -0.999991,\n                -0.000108875654,\n                0.0042427215,\n                0.0,\n                0.0,\n                0.0,\n                0.0,\n                1.0\n              ],\n              \"mesh_path\": \"./src/asset/3d/tank/wheel.obj\",\n              \"mesh_index\": 4\n            },\n            \"placed_collider\": null,\n            \"parent_to_self\": [\n              -0.99999994,\n              0.00040736992,\n              0.0,\n              0.0,\n              -0.00040736992,\n              -0.99999994,\n              0.0,\n              0.0,\n              0.0,\n              0.0,\n              1.0,\n              0.0,\n              0.523,\n              0.625,\n              0.0,\n              1.0\n            ],\n            \"joint\": \"Wheel0\",\n            \"children\": []\n          },\n          {\n            \"id\": {\n              \"value\": 833497444713801088,\n              \"phantom\": null\n            },\n            \"placed_mesh\": {\n              \"trans\": [\n                -0.00059798185,\n                -0.000007530322,\n                -0.9999998,\n                0.0,\n                0.012592674,\n                -0.9999207,\n                -4.656272e-10,\n                0.0,\n                -0.99992055,\n                -0.012592672,\n                0.00059802923,\n                0.0,\n                0.0,\n                0.0,\n                0.0,\n                1.0\n              ],\n              \"mesh_path\": \"./src/asset/3d/tank/wheel.obj\",\n              \"mesh_index\": 4\n            },\n            \"placed_collider\": null,\n            \"parent_to_self\": [\n              -0.999954,\n              -0.009592537,\n              0.0,\n              0.0,\n              0.009592537,\n              -0.999954,\n              0.0,\n              0.0,\n              0.0,\n              0.0,\n              1.0,\n              0.0,\n              -0.525,\n              0.609,\n              0.0,\n              1.0\n            ],\n            \"joint\": \"Wheel0\",\n            \"children\": []\n          }\n        ]\n      }\n    ]\n  }\n}"
  },
  {
    "path": "src/asset/map/map_example/data.json",
    "content": "{\n  \"metal_spots\": []\n}"
  },
  {
    "path": "src/botdef.rs",
    "content": "use crate::unit;\nuse crate::utils;\nuse serde::{Deserialize, Serialize};\nuse typename::TypeName;\nuse utils::Id;\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub struct BotDef {\n    pub id: Id<BotDef>,\n    pub file_path: String,\n    pub radius: f32,\n    pub max_life: i32,\n    //Movement\n    ///rad/frame²\n    pub turn_accel: f32,\n    ///rad/frame\n    pub max_turn_rate: f32,\n    ///m/frame²\n    pub accel: f32,\n    ///m/frame²\n    pub break_accel: f32,\n    ///m/frame\n    pub max_speed: f32,\n    ///metal/frame\n    pub build_power: f32,\n    ///m\n    pub build_dist: f32,\n    ///metal\n    pub metal_cost: i32,\n\n    pub part_tree: unit::PartTree,\n}\n"
  },
  {
    "path": "src/client/camera.rs",
    "content": "extern crate nalgebra as na;\nuse super::client::*;\nuse na::{Matrix4, Point3, Vector3};\n\nconst FOVY: f32 = 3.14 / 4.0;\nconst NEAR: f32 = 1.0;\nconst FAR: f32 = 8000.0;\n\npub fn create_view(pos: &Point3<f32>, dir: &Vector3<f32>) -> Matrix4<f32> {\n    Matrix4::look_at_rh(pos, &(pos + dir), &Vector3::new(0.0, 0.0, 1.0))\n}\n\npub fn create_normal(pos: &Point3<f32>, dir: &Vector3<f32>) -> Matrix4<f32> {\n    create_view(pos, dir).try_inverse().unwrap().transpose()\n}\n\npub fn create_proj(aspect_ratio: f32, near: f32) -> Matrix4<f32> {\n    let mx_projection = Matrix4::new_perspective(aspect_ratio, FOVY, near, FAR);\n    let mx_correction: Matrix4<f32> = Matrix4::new(\n        1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0,\n    );\n    mx_correction * mx_projection\n}\n\npub fn create_view_proj(\n    aspect_ratio: f32,\n    near: f32,\n    pos: &Point3<f32>,\n    dir: &Vector3<f32>,\n) -> Matrix4<f32> {\n    let mx_view = create_view(pos, dir);\n    let mx_proj = create_proj(aspect_ratio, near);\n    mx_proj * mx_view\n}\n\npub fn create_camera_uniform_vec(\n    screen_res: (u32, u32),\n    near: f32,\n    pos: &Point3<f32>,\n    dir: &Vector3<f32>,\n) -> Vec<f32> {\n    let mut res = Vec::new();\n    //ViewProj\n    let mx_total = create_view_proj(screen_res.0 as f32 / screen_res.1 as f32, near, pos, dir);\n    let mx_ref: &[f32] = mx_total.as_slice();\n    res.extend_from_slice(mx_ref);\n    //View\n    let mx_total = create_view(pos, dir);\n    let mx_ref: &[f32] = mx_total.as_slice();\n    res.extend_from_slice(mx_ref);\n    //Proj\n    let mx_total = create_proj(screen_res.0 as f32 / screen_res.1 as f32, near);\n    let mx_ref: &[f32] = mx_total.as_slice();\n    res.extend_from_slice(mx_ref);\n    //Normal\n    let mx_total = create_normal(pos, dir);\n    let mx_ref: &[f32] = mx_total.as_slice();\n    res.extend_from_slice(mx_ref);\n    res\n}\n\nimpl App {\n    pub fn rts_camera(&mut self, sim_sec: f32) {\n        use winit::event::VirtualKeyCode as Key;\n        let key_pressed = &self.input_state.key_pressed;\n        let on = |vkc| key_pressed.contains(&vkc);\n\n        let mut offset = Vector3::new(0.0, 0.0, 0.0);\n        let mut dir_offset = self.game_state.dir.clone();\n        let mut new_dir = None;\n\n        let camera_ground_height = self.heightmap_gpu.phy.z(\n            self.game_state\n                .position\n                .x\n                .max(0.0)\n                .min(self.heightmap_gpu.phy.width as f32 - 1.0),\n            self.game_state\n                .position\n                .y\n                .max(0.0)\n                .min(self.heightmap_gpu.phy.height as f32 - 1.0),\n        );\n\n        let screen_center_world_pos_fallback = self.game_state.position_smooth\n            + self.game_state.dir_smooth * (40.0 - self.game_state.position_smooth.z)\n                / self.game_state.dir_smooth.z;\n\n        let height_from_ground = self.game_state.position.z - camera_ground_height;\n        let distance_camera_middle_screen = self\n            .game_state\n            .screen_center_world_pos\n            .or(Some(screen_center_world_pos_fallback.coords))\n            .map(|scwp| (self.game_state.position.coords - scwp).magnitude())\n            .unwrap_or(height_from_ground);\n        let k =\n            (if !on(Key::LShift) { 1.0 } else { 2.0 }) * distance_camera_middle_screen.max(10.0);\n        //Game\n        if on(Key::S) {\n            offset.y -= k;\n        }\n        if on(Key::Z) {\n            offset.y += k;\n        }\n        if on(Key::Q) {\n            offset.x -= k;\n        }\n        if on(Key::D) {\n            offset.x += k;\n        }\n\n        if on(Key::LControl) {\n            if let Some(screen_center_world_pos) = self\n                .game_state\n                .screen_center_world_pos\n                .or(Some(screen_center_world_pos_fallback.coords))\n            {\n                if self.input_state.last_scroll != 0.0 {\n                    let camera_to_center =\n                        screen_center_world_pos - self.game_state.position.coords;\n\n                    let distance = camera_to_center.norm();\n\n                    let mut new_camera_to_center = camera_to_center.normalize();\n\n                    if self.input_state.last_scroll > 0.0 {\n                        new_camera_to_center.y += 1.0 * 0.30;\n                    }\n                    if self.input_state.last_scroll < 0.0 {\n                        new_camera_to_center.z -= 1.0 * 0.30;\n                    }\n                    new_camera_to_center.x = 0.0;\n\n                    new_camera_to_center = new_camera_to_center.normalize();\n                    new_camera_to_center.y = new_camera_to_center.y.max(0.01);\n\n                    new_dir = Some(new_camera_to_center);\n                    let new_pos =\n                        screen_center_world_pos - new_camera_to_center.normalize() * distance;\n                    offset += (new_pos - self.game_state.position.coords) / sim_sec;\n                }\n            } else {\n                if self.input_state.last_scroll > 0.0 {\n                    dir_offset.y += 0.010 / sim_sec;\n                }\n                if self.input_state.last_scroll < 0.0 {\n                    dir_offset.z -= 0.010 / sim_sec;\n                }\n            }\n        } else {\n            if let Some(mouse_world_pos) = self\n                .game_state\n                .mouse_world_pos\n                .or(Some(screen_center_world_pos_fallback.coords))\n            {\n                let u = (mouse_world_pos - self.game_state.position.coords).normalize();\n                offset += self.input_state.last_scroll * u * k * 0.75 * 0.320 / sim_sec;\n            } else {\n                offset.z = -self.input_state.last_scroll * k * 0.75 * 0.20 / sim_sec;\n            }\n        }\n\n        self.game_state.position += offset * sim_sec;\n        self.game_state.dir = (self.game_state.dir + dir_offset * 33.0 * sim_sec).normalize();\n\n        new_dir.map(|new_dir| {\n            self.game_state.dir = new_dir;\n        });\n\n        self.game_state.position.z = self.game_state.position.z.max(camera_ground_height + 3.0);\n\n        if self.game_state.position.coords.magnitude() > 7000.0 {\n            self.game_state.position =\n                Point3::from(7000.0 * self.game_state.position.coords.normalize());\n        }\n\n        self.game_state.position_smooth += (self.game_state.position.coords\n            - self.game_state.position_smooth.coords)\n            * sim_sec.min(0.033)\n            * 15.0;\n\n        self.game_state.dir_smooth +=\n            (self.game_state.dir - self.game_state.dir_smooth) * sim_sec.min(0.033) * 15.0;\n    }\n\n    pub fn orbit_camera(&mut self, sim_sec: f32) {\n        let to_orbit =\n            (self.unit_editor.orbit.coords - self.game_state.position_smooth.coords).normalize();\n\n        let right_vec = to_orbit.cross(&Vector3::new(0.0, 0.0, 1.0)).normalize();\n        let up_vec = -to_orbit.cross(&right_vec).normalize();\n        if self\n            .input_state\n            .mouse_pressed\n            .contains(&winit::event::MouseButton::Middle)\n        {\n            let k = 3.0;\n            let right_offset = -self.input_state.cursor_offset.0 as f32 * right_vec * sim_sec * k;\n            let up_offset = self.input_state.cursor_offset.1 as f32 * up_vec * sim_sec * k;\n            self.game_state.position += right_offset + up_offset;\n            self.game_state.position_smooth += right_offset + up_offset;\n\n            if self\n                .input_state\n                .key_pressed\n                .contains(&winit::event::VirtualKeyCode::LShift)\n            {\n                self.unit_editor.orbit += right_offset + up_offset;\n            }\n        }\n\n        let to_orbit =\n            (self.unit_editor.orbit.coords - self.game_state.position_smooth.coords).normalize();\n\n        self.game_state.dir = to_orbit;\n        self.game_state.dir_smooth = self.game_state.dir;\n\n        self.game_state.position += self.input_state.last_scroll * to_orbit * sim_sec * 15.0;\n        self.game_state.position_smooth = self.game_state.position;\n    }\n}\n"
  },
  {
    "path": "src/client/game_state.rs",
    "content": "extern crate nalgebra as na;\nuse super::heightmap_editor;\nuse crate::botdef;\nuse crate::frame::Frame;\nuse crate::mobile;\nuse crate::utils;\nuse fnv::{FnvHashMap, FnvHashSet};\nuse na::{Matrix4, Point3, Vector2, Vector3};\nuse std::time::Instant;\nuse utils::*;\n\nuse super::uitool::UiTool;\nuse crate::frame::Player;\nuse mobile::*;\n\n#[derive(Clone, Copy, Debug)]\npub struct Explosion {\n    pub position: Point3<f32>,\n    pub born_sec: f32,\n    pub death_sec: f32,\n    pub size: f32,\n    pub seed: f32,\n}\n\npub struct State {\n    pub position: Point3<f32>,\n    pub dir: Vector3<f32>,\n\n    pub position_smooth: Point3<f32>,\n    pub dir_smooth: Vector3<f32>,\n\n    pub mouse_world_pos: Option<Vector3<f32>>,\n    pub screen_center_world_pos: Option<Vector3<f32>>,\n\n    pub heightmap_editor: heightmap_editor::State,\n\n    //Data to interpolate\n    pub frame_minus_one: Frame,\n    pub frame_zero: Frame,\n    pub frame_zero_time_received: Instant,\n\n    //Interpolated from curve\n    pub kbots: Vec<(KBot, ClientKbot)>,\n    pub server_sec: f32,\n    //Extrapolated from events\n    pub explosions: Vec<Explosion>,\n    pub kinematic_projectiles_cache: FnvHashMap<Id<KinematicProjectile>, KinematicProjectile>,\n    pub kinematic_projectiles: Vec<Point3<f32>>,\n\n    pub selected: FnvHashSet<Id<KBot>>,\n    pub under_mouse: Option<Id<KBot>>,\n    pub uitool: UiTool,\n\n    pub start_time: Instant,\n    pub last_frame: Instant,\n\n    pub my_player_id: Option<Id<Player>>,\n\n    pub players: FnvHashMap<Id<Player>, Player>,\n\n    pub fps: u64,\n\n    //parameters\n    pub unit_icon_distance: f32,\n}\n\nimpl State {\n    pub fn new() -> Self {\n        State {\n            position: Point3::new(1024.0, 100.0, 50.0),\n            dir: Vector3::new(0.0, 0.3, -1.0),\n            position_smooth: Point3::new(0.0, 0.0, 30000.0),\n            dir_smooth: Vector3::new(0.0, 0.01, -1.0),\n\n            mouse_world_pos: None,\n            screen_center_world_pos: None,\n\n            heightmap_editor: heightmap_editor::State::new(),\n\n            frame_minus_one: Frame::new(),\n            frame_zero: Frame::new(),\n            frame_zero_time_received: Instant::now(),\n\n            kbots: Vec::new(),\n            kinematic_projectiles_cache: FnvHashMap::default(),\n            kinematic_projectiles: Vec::new(),\n\n            explosions: Vec::new(),\n            server_sec: 0.0,\n\n            selected: FnvHashSet::default(),\n            under_mouse: None,\n            uitool: UiTool::None,\n\n            players: FnvHashMap::default(),\n            my_player_id: None,\n\n            start_time: Instant::now(),\n            last_frame: Instant::now(),\n            fps: 144,\n            unit_icon_distance: 200.0,\n        }\n    }\n\n    pub fn handle_new_frame(&mut self, frame: Frame) {\n        let time_between = self.frame_zero_time_received.elapsed();\n        log::trace!(\"receive: NewFrame after {:?}\", time_between);\n        self.frame_zero_time_received = Instant::now();\n        self.frame_minus_one = std::mem::replace(&mut self.frame_zero, frame);\n\n        let sec = self.frame_zero.number as f32 / 10.0;\n        let mut seed = sec * 3.141592;\n\n        for explosion in self.frame_zero.explosions.iter() {\n            seed += 1.0;\n            self.explosions.push(Explosion {\n                position: explosion.position,\n                born_sec: sec,\n                death_sec: sec + explosion.life_time,\n                size: explosion.size,\n                seed,\n            });\n        }\n\n        for proj_b in self.frame_zero.kinematic_projectiles_birth.iter() {\n            self.kinematic_projectiles_cache\n                .insert(proj_b.id, proj_b.clone());\n        }\n\n        for dead in self.frame_zero.kinematic_projectiles_dead.iter() {\n            self.kinematic_projectiles_cache.remove(dead);\n        }\n\n        self.selected = self\n            .selected\n            .difference(&self.frame_zero.kbots_dead.iter().cloned().collect())\n            .copied()\n            .collect();\n\n        self.kbots = self\n            .frame_zero\n            .kbots\n            .values()\n            .map(|kbot| (kbot.clone(), ClientKbot::new(kbot.position)))\n            .collect();\n    }\n\n    pub fn interpolate(&mut self, threadpool: &rayon::ThreadPool, view_proj: &Matrix4<f32>) {\n        let elapsed = self.frame_zero_time_received.elapsed().as_secs_f64();\n        //elapsed normalize between 0 and 1 if frame arrives every 100ms (0.1s)\n        let lambda = (elapsed / 0.1) as f32;\n        let i0 = lambda;\n        let im = 1.0 - lambda;\n\n        self.server_sec =\n            (self.frame_zero.number as f32 * i0 + self.frame_minus_one.number as f32 * im) / 10.0;\n\n        log::trace!(\"server_sec {}\", self.server_sec);\n\n        use rayon::prelude::*;\n        fn test_screen(\n            id: Id<KBot>,\n            position: Point3<f32>,\n            view_proj: &Matrix4<f32>,\n        ) -> Option<(Id<KBot>, Vector2<f32>, f32)> {\n            let p = position.to_homogeneous();\n            let r = view_proj * p;\n            let margin = 1.2;\n            let rw_with_margin = r.w * margin;\n            //Keeping those of the clipped space in screen (-1 1, -1 1 , 0 1)\n            if r.z > 0.0\n                && r.x < rw_with_margin\n                && r.x > -rw_with_margin\n                && r.y < rw_with_margin\n                && r.y > -rw_with_margin\n            {\n                // log::debug!(\"z {}\", r.w);\n                // log::debug!(\"d {}\", (position.coords - cam_pos.coords).norm());\n                Some((id, Vector2::new(r.x / r.w, r.y / r.w), r.w))\n            } else {\n                None\n            }\n        }\n\n        self.explosions = self\n            .explosions\n            .iter()\n            .copied()\n            .filter(|e| e.death_sec > self.server_sec)\n            .collect();\n\n        let mut kbots = std::mem::replace(&mut self.kbots, Vec::new());\n\n        threadpool.install(|| {\n            kbots.par_chunks_mut(1000).for_each(|chunk| {\n                for (kbot_0, client_kbot0) in chunk.iter_mut() {\n                    let kbot_m_opt = self.frame_minus_one.kbots.get(&kbot_0.id);\n                    if let Some(kbot_m) = kbot_m_opt {\n                        client_kbot0.position =\n                            kbot_0.position * i0 + (im * kbot_m.position).coords;\n                    }\n\n                    let screen = test_screen(kbot_0.id, client_kbot0.position, view_proj);\n                    match screen {\n                        Some((_, screen_pos, distance_to_camera)) => {\n                            if let Some(kbot_m) = kbot_m_opt {\n                                client_kbot0.dir = kbot_0.dir * i0 + kbot_m.dir * im;\n                                client_kbot0.up = kbot_0.up * i0 + kbot_m.up * im;\n                                client_kbot0.weapon0_dir =\n                                    kbot_0.weapon0_dir * i0 + kbot_m.weapon0_dir * im;\n                                client_kbot0.wheel0_angle =\n                                    kbot_0.wheel0_angle * i0 + kbot_m.wheel0_angle * im;\n                            }\n                            client_kbot0.is_in_screen = true;\n                            let mat = utils::face_towards_dir(\n                                &client_kbot0.position.coords,\n                                &(client_kbot0.dir.normalize()),\n                                &client_kbot0.up,\n                            );\n                            client_kbot0.trans = Some(mat);\n                            client_kbot0.distance_to_camera = distance_to_camera;\n                            client_kbot0.screen_pos = screen_pos;\n                        }\n                        _ => {\n                            client_kbot0.is_in_screen = false;\n                        }\n                    }\n                }\n            });\n        });\n\n        self.kbots = kbots;\n        self.kinematic_projectiles.clear();\n\n        for kproj in self.kinematic_projectiles_cache.values_mut() {\n            let pos = kproj.position_at(self.frame_minus_one.number + 1) * im\n                + kproj.position_at(self.frame_zero.number + 1).coords * i0;\n\n            self.kinematic_projectiles.push(pos);\n        }\n\n        self.players = self.frame_zero.players.clone();\n    }\n\n    pub fn my_player(&self) -> Option<&Player> {\n        self.my_player_id\n            .map(|id| self.players.get(&id))\n            .unwrap_or(None)\n    }\n\n    pub fn near(&self) -> f32 {\n        if self.position_smooth.z > 515.0\n            || self.position_smooth.coords.x < -500.0\n            || self.position_smooth.coords.y < -500.0\n        {\n            10.0\n        } else {\n            0.3\n        }\n    }\n}\n"
  },
  {
    "path": "src/client/heightmap_editor.rs",
    "content": "use imgui::*;\nuse na::Vector3;\nuse std::collections::HashSet;\n\nuse crate::gpu_obj::heightmap_gpu;\nuse noise::{NoiseFn, Seedable};\n\n#[derive(PartialEq, Clone, Copy)]\npub enum Mode {\n    Raise,\n    Flatten,\n    Median,\n    Noise,\n    Blur,\n}\n\npub struct State {\n    pub map_path: String,\n    pub pen_radius: u32,\n    pub pen_strength: f32,\n    pub mode: Mode,\n    noise: noise::Perlin,\n    noise_freq: f64,\n    min_z: f32,\n    max_z: f32,\n}\n\nimpl State {\n    pub fn new() -> Self {\n        State {\n            map_path: \"src/asset/map/map_example\".to_owned(),\n            pen_radius: 30,\n            pen_strength: 2.0,\n            mode: Mode::Raise,\n            noise: noise::Perlin::new().set_seed(0),\n            noise_freq: 10.0,\n            min_z: 0.0,\n            max_z: heightmap_gpu::MAX_Z,\n        }\n    }\n\n    pub fn draw_ui(&mut self, ui: &Ui, heightmap_gpu: &mut heightmap_gpu::HeightmapGpu) {\n        let pen_radius = &mut self.pen_radius;\n        let pen_strength = &mut self.pen_strength;\n        let mode = &mut self.mode;\n        let noise_freq = &mut self.noise_freq;\n        let noise_seed: &mut i32 = &mut (self.noise.seed() as i32);\n        let mut update_noise = false;\n\n        let min_z = &mut self.min_z;\n        let max_z = &mut self.max_z;\n        let edit_height_window = imgui::Window::new(im_str!(\"Heightmap editor\"));\n        edit_height_window\n            .size([400.0, 300.0], imgui::Condition::FirstUseEver)\n            .position([3.0, 415.0], imgui::Condition::FirstUseEver)\n            .collapsed(false, imgui::Condition::FirstUseEver)\n            .build(&ui, || {\n                ui.radio_button(im_str!(\"Raise/Lower\"), mode, Mode::Raise);\n                ui.radio_button(im_str!(\"Flatten/Unflatten\"), mode, Mode::Flatten);\n                ui.radio_button(im_str!(\"Median\"), mode, Mode::Median);\n                ui.radio_button(im_str!(\"Blur\"), mode, Mode::Blur);\n                ui.radio_button(im_str!(\"Noise\"), mode, Mode::Noise);\n\n                if mode == &mut Mode::Noise {\n                    imgui::Slider::new(im_str!(\"noise frequency\"), 0.0_f64..=200.0)\n                        .power(3.0)\n                        .build(&ui, noise_freq);\n\n                    update_noise = ui\n                        .drag_int(im_str!(\"noise seed\"), noise_seed)\n                        .min(0)\n                        .build();\n                    ui.separator();\n                } else {\n                    ui.separator();\n                }\n\n                imgui::Slider::new(im_str!(\"pen radius\"), 1..=1000).build(&ui, pen_radius);\n                imgui::Slider::new(im_str!(\"pen strength\"), 0.0..=10.0).build(&ui, pen_strength);\n                ui.separator();\n\n                imgui::Slider::new(im_str!(\"min height\"), 0.0..=heightmap_gpu::MAX_Z)\n                    .build(&ui, min_z);\n                imgui::Slider::new(im_str!(\"max height\"), 0.0..=heightmap_gpu::MAX_Z)\n                    .build(&ui, max_z);\n\n                if ui.small_button(im_str!(\"Save\")) {\n                    Self::save(heightmap_gpu, \"src/asset/map/map_example\");\n                }\n\n                if ui.small_button(im_str!(\"Clear\")) {\n                    for i in 0..heightmap_gpu.phy.width * heightmap_gpu.phy.height {\n                        heightmap_gpu.phy.texels[i as usize] = 50.0;\n                    }\n                    heightmap_gpu.update_rect(\n                        0 as u32,\n                        0 as u32,\n                        heightmap_gpu.phy.width as u32,\n                        heightmap_gpu.phy.height as u32,\n                    );\n                }\n\n                if ui.small_button(im_str!(\"Load\")) {\n                    Self::load(heightmap_gpu, \"src/asset/map/map_example\");\n                }\n            });\n\n        // let window_selector = imgui::Window::new(im_str!(\"Map Selector\"));\n        // window_selector\n        //     .size([400.0, 200.0], imgui::Condition::FirstUseEver)\n        //     .position([400.0, 3.0], imgui::Condition::FirstUseEver)\n        //     .collapsed(false, imgui::Condition::FirstUseEver)\n        //     .build(&ui, || {\n        //         // Self::visit_dirs_for_selection(ui);\n        //     });\n\n        self.max_z = max_z.max(*min_z);\n        if update_noise {\n            self.noise = self.noise.set_seed(*noise_seed as u32);\n        }\n    }\n\n    pub fn handle_user_input(\n        &self,\n        mouse_pressed: &HashSet<winit::event::MouseButton>,\n        mouse_world_pos: &Vector3<f32>,\n        heightmap_gpu: &mut heightmap_gpu::HeightmapGpu,\n    ) {\n        log::trace!(\"heightmap_editor handle_user_input\");\n        {\n            let pen_strength = self.pen_strength\n                * if mouse_pressed.contains(&winit::event::MouseButton::Left) {\n                    1.0\n                } else if mouse_pressed.contains(&winit::event::MouseButton::Right) {\n                    -1.0\n                } else {\n                    0.0\n                };\n\n            if pen_strength != 0.0 {\n                let (x, y) = (mouse_world_pos.x, mouse_world_pos.y);\n\n                let middle_i = x.floor() as i32;\n                let middle_j = y.floor() as i32;\n\n                let pen_size = self.pen_radius as i32;\n                let pen_size2 = pen_size * pen_size;\n\n                let min_i = (middle_i - pen_size).max(0);\n                let min_j = (middle_j - pen_size).max(0);\n\n                let max_i = (middle_i + pen_size).min(heightmap_gpu.phy.width as i32 - 1);\n                let max_j = (middle_j + pen_size).min(heightmap_gpu.phy.height as i32 - 1);\n\n                let size_i = max_i - min_i + 1;\n                let size_j = max_j - min_j + 1;\n\n                if size_i > 0 && size_j > 0 {\n                    //let start = std::time::Instant::now();\n\n                    let mut pixels = Vec::with_capacity((size_i * size_j) as usize);\n                    for j in min_j..=max_j {\n                        for i in min_i..=max_i {\n                            let falloff = 1.0\n                                - (i32::pow(i - middle_i, 2) + i32::pow(j - middle_j, 2)) as f32\n                                    / pen_size2 as f32;\n\n                            pixels.push((\n                                i,\n                                j,\n                                (i + j * heightmap_gpu.phy.width as i32) as usize,\n                                falloff.max(0.0),\n                            ));\n                        }\n                    }\n\n                    match self.mode {\n                        Mode::Raise => {\n                            for (_, _, index, falloff) in pixels {\n                                let power = pen_strength * falloff;\n                                heightmap_gpu.phy.texels[index] = (heightmap_gpu.phy.texels[index]\n                                    + power)\n                                    .min(self.max_z)\n                                    .max(self.min_z);\n                            }\n                        }\n                        Mode::Flatten => {\n                            let mut average = 0.0;\n                            for (_, _, index, _) in &pixels {\n                                let z = heightmap_gpu.phy.texels[*index];\n                                average += z;\n                            }\n                            average /= (size_i * size_j) as f32;\n                            for (_, _, index, falloff) in pixels {\n                                let power = (pen_strength * falloff) / 50.0;\n                                let z = heightmap_gpu.phy.texels[index] * (1.0 - power)\n                                    + average * power;\n                                heightmap_gpu.phy.texels[index] = z.min(self.max_z).max(self.min_z);\n                            }\n                        }\n                        Mode::Noise => {\n                            for (i, j, index, falloff) in pixels {\n                                let power = pen_strength\n                                    * falloff\n                                    * self.noise.get([\n                                        (0.001 * self.noise_freq) * i as f64,\n                                        (0.001 * self.noise_freq) * j as f64,\n                                    ]) as f32;\n\n                                heightmap_gpu.phy.texels[index] = (heightmap_gpu.phy.texels[index]\n                                    + power)\n                                    .min(self.max_z)\n                                    .max(self.min_z);\n                            }\n                        }\n                        Mode::Median => {\n                            let mut new_pix = Vec::new();\n                            for (i, j, index, _) in pixels {\n                                let power = pen_strength / 10.0;\n\n                                let kernel = 4;\n                                let mut acc = Vec::new();\n\n                                for ti in (-kernel + i).max(0)\n                                    ..=(kernel + i).min(heightmap_gpu.phy.width as i32 - 1)\n                                {\n                                    for tj in (-kernel + j).max(0)\n                                        ..=(kernel + j).min(heightmap_gpu.phy.height as i32 - 1)\n                                    {\n                                        let tindex =\n                                            (ti + tj * heightmap_gpu.phy.width as i32) as usize;\n                                        acc.push(\n                                            (heightmap_gpu.phy.texels[tindex] * 1000.0 * 1000.0)\n                                                .floor()\n                                                as i128,\n                                        );\n                                    }\n                                }\n                                acc.sort();\n                                new_pix.push((\n                                    index,\n                                    heightmap_gpu.phy.texels[index] * (1.0 - power)\n                                        + power * (acc[acc.len() / 2] as f64 / 1000000.0) as f32,\n                                ));\n                            }\n                            for (index, z) in new_pix {\n                                heightmap_gpu.phy.texels[index] = z.min(self.max_z).max(self.min_z);\n                            }\n                        }\n                        Mode::Blur => {\n                            let mut new_pix = Vec::new();\n                            for (i, j, index, falloff) in pixels {\n                                let power = pen_strength * falloff / 10.0;\n\n                                let kernel = 1;\n                                let mut acc = 0.0;\n                                let mut tap = 0;\n\n                                for ti in (-kernel + i).max(0)\n                                    ..=(kernel + i).min(heightmap_gpu.phy.width as i32 - 1)\n                                {\n                                    for tj in (-kernel + j).max(0)\n                                        ..=(kernel + j).min(heightmap_gpu.phy.height as i32 - 1)\n                                    {\n                                        tap += 1;\n                                        let tindex =\n                                            (ti + tj * heightmap_gpu.phy.width as i32) as usize;\n                                        acc += heightmap_gpu.phy.texels[tindex];\n                                    }\n                                }\n                                let z = heightmap_gpu.phy.texels\n                                    [(i + j * heightmap_gpu.phy.width as i32) as usize]\n                                    * (1.0 - power)\n                                    + power * (acc / tap as f32);\n                                new_pix.push((index, z));\n                            }\n                            for (index, z) in new_pix {\n                                heightmap_gpu.phy.texels[index] = z.min(self.max_z).max(self.min_z);\n                            }\n                        }\n                    }\n\n                    heightmap_gpu.update_rect(\n                        min_i as u32,\n                        min_j as u32,\n                        size_i as u32,\n                        size_j as u32,\n                    );\n                    //                    println!(\"handle hei took {}\", start.elapsed().as_micros());\n                }\n            }\n        }\n    }\n\n    pub fn save(heightmap_gpu: &heightmap_gpu::HeightmapGpu, path: &str) {\n        use std::fs::File;\n        use std::io::BufWriter;\n        use std::path::Path;\n\n        let height_path = format!(\"{}/height.png\", path);\n        let height_path = Path::new(&height_path);\n        let file = File::create(height_path).unwrap();\n        let ref mut w = BufWriter::new(file);\n\n        let mut encoder = png::Encoder::new(\n            w,\n            heightmap_gpu.phy.width as u32,\n            heightmap_gpu.phy.height as u32,\n        );\n        encoder.set_color(png::ColorType::Grayscale);\n        encoder.set_depth(png::BitDepth::Sixteen);\n        let mut writer = encoder.write_header().unwrap();\n\n        let data: Vec<u8> = heightmap_gpu\n            .phy\n            .texels\n            .iter()\n            .map(|e| ((e / 511.0).min(1.0).max(0.0) * 65535.0) as u16)\n            .flat_map(|e| vec![(e >> 8) as u8, e as u8])\n            .collect();\n        //        let data = &data[..] ;//[255, 0, 0, 255, 0, 0, 0, 255]; // An array containing a RGBA sequence. First pixel is red and second pixel is black.\n        writer.write_image_data(&data).unwrap(); // Save\n\n        let json_path = &format!(\"{}/data.json\", path);\n\n        use std::fs::OpenOptions;\n        use std::io::prelude::*;\n        use std::io::BufReader;\n        let file = OpenOptions::new()\n            .read(true)\n            .write(true)\n            .create(true)\n            .truncate(true)\n            .open(json_path)\n            .unwrap();\n        let mut buf_w = BufWriter::new(file);\n        serde_json::to_writer_pretty(buf_w, &heightmap_gpu.phy.data);\n    }\n\n    pub fn load(heightmap_gpu: &mut heightmap_gpu::HeightmapGpu, path: &str) {\n        use byteorder::{BigEndian, ReadBytesExt};\n        use std::fs::File;\n        use std::io::Cursor;\n        let height_path = format!(\"{}/height.png\", path);\n        let mut decoder = png::Decoder::new(File::open(&height_path).unwrap());\n        decoder.set_transformations(png::Transformations::IDENTITY);\n        let (info, mut reader) = decoder.read_info().unwrap();\n        log::debug!(\"info: {:?}\", info.width);\n        log::debug!(\"height: {:?}\", info.height);\n        log::debug!(\"bit depth: {:?}\", info.bit_depth);\n        log::debug!(\"buffer size: {:?}\", info.buffer_size());\n        let mut buf = vec![0; info.buffer_size()];\n        reader.next_frame(&mut buf).unwrap();\n        // Transform buffer into 16 bits slice.\n        let mut buffer_u16 = vec![0; (info.width * info.height) as usize];\n        let mut buffer_cursor = Cursor::new(buf);\n        buffer_cursor\n            .read_u16_into::<BigEndian>(&mut buffer_u16)\n            .unwrap();\n\n        for i in 0..heightmap_gpu.phy.width * heightmap_gpu.phy.height {\n            heightmap_gpu.phy.texels[i as usize] =\n                buffer_u16[i as usize] as f32 / (65535.0 / 511.0);\n        }\n        heightmap_gpu.update_rect(\n            0 as u32,\n            0 as u32,\n            heightmap_gpu.phy.width as u32,\n            heightmap_gpu.phy.height as u32,\n        );\n    }\n}\n"
  },
  {
    "path": "src/client/input_state.rs",
    "content": "use std::collections::HashSet;\n\n#[derive(Clone, Debug)]\npub enum Drag {\n    None,\n    Start { x0: u32, y0: u32 },\n    Dragging { x0: u32, y0: u32, x1: u32, y1: u32 },\n    End { x0: u32, y0: u32, x1: u32, y1: u32 },\n}\n\n#[derive(Clone, Debug)]\npub struct InputState {\n    pub key_pressed: HashSet<winit::event::VirtualKeyCode>,\n    pub mouse_pressed: HashSet<winit::event::MouseButton>,\n    pub key_trigger: HashSet<winit::event::VirtualKeyCode>,\n    pub mouse_trigger: HashSet<winit::event::MouseButton>,\n    pub key_release: HashSet<winit::event::VirtualKeyCode>,\n    pub mouse_release: HashSet<winit::event::MouseButton>,\n    pub drag: Drag,\n    pub last_scroll: f32,\n    pub cursor_pos: (u32, u32),\n    pub cursor_offset: (i32, i32),\n}\n\nimpl InputState {\n    pub fn new() -> Self {\n        InputState {\n            key_pressed: HashSet::new(),\n            mouse_pressed: HashSet::new(),\n            key_trigger: HashSet::new(),\n            mouse_trigger: HashSet::new(),\n            key_release: HashSet::new(),\n            mouse_release: HashSet::new(),\n            last_scroll: 0.0,\n            cursor_pos: (0, 0),\n            cursor_offset: (0, 0),\n            drag: Drag::None,\n        }\n    }\n\n    pub fn update(&mut self) {\n        self.key_trigger.clear();\n        self.mouse_trigger.clear();\n        self.mouse_release.clear();\n        self.key_release.clear();\n        if let Drag::End { .. } = self.drag {\n            self.drag = Drag::None;\n        }\n        self.last_scroll = 0.0;\n        self.cursor_offset = (0, 0);\n    }\n}\n"
  },
  {
    "path": "src/client/misc.rs",
    "content": "use super::client::*;\nuse crate::*;\nuse unit_part_gpu::*;\n\nuse super::uitool::UiTool;\nimpl App {\n    pub fn clear_gpu_instance_and_game_state(&mut self) {\n        self.game_state.players.clear();\n        self.game_state.my_player_id = None;\n        self.game_state.kbots.clear();\n        self.game_state.selected.clear();\n        self.game_state.explosions.clear();\n        self.game_state.kinematic_projectiles_cache.clear();\n        // self.unit_editor.root.children.clear();\n\n        self.health_bar.update_instance(&[], &self.gpu.device);\n        self.unit_icon.update_instance(&[], &self.gpu.device);\n        self.explosion_gpu.update_instance(&[], &self.gpu.device);\n        for (model_gpu_state) in self.unit_part_gpu.states.iter_mut() {\n            match model_gpu_state {\n                ModelGpuState::Ready(model_gpu) => {\n                    model_gpu.update_instance_dirty(&[], &self.gpu.device)\n                }\n                _ => {}\n            }\n        }\n        self.kinematic_projectile_gpu\n            .update_instance_dirty(&[], &self.gpu.device);\n    }\n\n    pub fn visit_part_tree(\n        part_tree: &unit::PartTree,\n        root_trans: &Matrix4<f32>,\n        unit_part_gpu: &mut UnitPartGpu,\n        highlight_factor: f32,\n        team: f32,\n        con_completed: f32,\n        weapon0_dir: Vector3<f32>,\n        wheel0_angle: f32,\n    ) {\n        for c in part_tree.children.iter() {\n            if let Some(placed_mesh) = &c.placed_mesh {\n                let display_model = &placed_mesh;\n\n                let combined = match &c.joint {\n                    unit::Joint::Fix => root_trans * c.parent_to_self,\n                    unit::Joint::AimWeapon0 => {\n                        let comb = root_trans * c.parent_to_self;\n\n                        utils::face_towards_dir(\n                            &Vector3::new(comb[12], comb[13], comb[14]),\n                            &weapon0_dir,\n                            &Vector3::new(0.0, 0.0, 1.0),\n                        )\n                    }\n                    unit::Joint::Wheel0 => {\n                        let comb = root_trans * c.parent_to_self;\n\n                        comb * utils::face_towards_dir(\n                            &Vector3::new(0.0, 0.0, 0.0),\n                            &Vector3::new(0.0, 1.0, 0.0),\n                            &Vector3::new(f32::cos(wheel0_angle), 0.0, f32::sin(wheel0_angle)),\n                        )\n                    }\n                };\n\n                let for_display = combined * display_model.trans;\n                // log::warn!(\n                //     \"root {:?}\\nlocal {:?}\\ncombined {:?}\\n\",\n                //     root_trans,\n                //     mat,\n                //     combined\n                // );\n\n                //TODO fix performance : HALF OF TIME IS IN GET_MUT\n                match unit_part_gpu.get_mut(placed_mesh.mesh_index) {\n                    //}  get_mut(&placed_mesh.mesh_path) {\n                    ModelGpuState::Ready(generic_cpu) => {\n                        let buf = &mut generic_cpu.instance_attr_cpu_buf;\n\n                        let isometry: Isometry3<f32> = unsafe {\n                            na::convert_unchecked::<Matrix4<f32>, Isometry3<f32>>(for_display)\n                        };\n                        let euler = isometry.rotation.euler_angles();\n                        buf.push(for_display[12]);\n                        buf.push(for_display[13]);\n                        buf.push(for_display[14]);\n                        buf.push(euler.0);\n                        buf.push(euler.1);\n                        buf.push(euler.2);\n\n                        //Bit representation in decimal order\n                        //SELECTED TEAM TEAM\n                        //ex : team 5 and selected = 1 0 5\n                        let bitpacked: f32 = highlight_factor * 100. + team;\n\n                        buf.push(bitpacked);\n                        buf.push(con_completed);\n                    }\n                    _ => {}\n                }\n                Self::visit_part_tree(\n                    c,\n                    &combined,\n                    unit_part_gpu,\n                    highlight_factor,\n                    team,\n                    con_completed,\n                    weapon0_dir,\n                    wheel0_angle,\n                );\n            } else {\n                Self::visit_part_tree(\n                    c,\n                    root_trans,\n                    unit_part_gpu,\n                    highlight_factor,\n                    team,\n                    con_completed,\n                    weapon0_dir,\n                    wheel0_angle,\n                );\n            }\n        }\n    }\n\n    pub fn upload_to_gpu(&mut self, view_proj: &Matrix4<f32>, encoder: &mut wgpu::CommandEncoder) {\n        //Upload to gpu\n        let upload_to_gpu_duration = time(|| {\n            let unit_icon_distance = self.game_state.unit_icon_distance;\n\n            //generic_gpu\n            {\n                for model_gpu in self.unit_part_gpu.states.iter_mut() {\n                    match model_gpu {\n                        ModelGpuState::Ready(model_gpu) => {\n                            model_gpu.instance_attr_cpu_buf.clear();\n                        }\n                        _ => {}\n                    }\n                }\n\n                let identity = utils::face_towards_dir(\n                    &Vector3::new(300.0_f32, 100.0, 0.50),\n                    &Vector3::new(1.0, 0.0, 0.0),\n                    &Vector3::new(0.0, 0.0, 1.0),\n                ); //Matrix4::identity();\n\n                let t = self.game_state.start_time.elapsed().as_secs_f32();\n                if self.main_menu == MainMode::UnitEditor {\n                    Self::visit_part_tree(\n                        &self.unit_editor.botdef.part_tree,\n                        &identity,\n                        &mut self.unit_part_gpu,\n                        0.0,\n                        0.0,\n                        1.0,\n                        Vector3::new(f32::cos(t), f32::sin(t), f32::sin(t / 5.0) * 0.1).normalize(),\n                        (t * 2.0),\n                    );\n                }\n\n                //Kbot\n                {\n                    for (mobile, client_kbot) in\n                        self.game_state.kbots.iter_mut().filter(|e| {\n                            e.1.is_in_screen && e.1.distance_to_camera < unit_icon_distance\n                        })\n                    {\n                        let mat = client_kbot.trans.unwrap();\n\n                        let highlight_factor: f32 = match (\n                            self.game_state.selected.contains(&mobile.id),\n                            self.game_state.under_mouse == Some(mobile.id),\n                        ) {\n                            (true, false) => 1.0,\n                            (false, false) => 0.0,\n                            (false, true) => 2.0,\n                            (true, true) => 3.0,\n                        };\n\n                        let team = mobile.team;\n\n                        if let Some(botdef) =\n                            self.game_state.frame_zero.bot_defs.get(&mobile.botdef_id)\n                        {\n                            Self::visit_part_tree(\n                                &botdef.part_tree,\n                                &mat,\n                                &mut self.unit_part_gpu,\n                                highlight_factor,\n                                team as f32,\n                                mobile.con_completed,\n                                client_kbot.weapon0_dir,\n                                client_kbot.wheel0_angle,\n                            );\n                        }\n                    }\n                }\n\n                for model_gpu in self.unit_part_gpu.states.iter_mut() {\n                    match model_gpu {\n                        ModelGpuState::Ready(model_gpu) => {\n                            model_gpu.update_instance_dirty_own_buffer(&self.gpu.device);\n                        }\n                        _ => {}\n                    }\n                }\n            }\n\n            // //Kbot\n            // {\n            //     self.vertex_attr_buffer_f32.clear();\n\n            //     for mobile in self\n            //         .game_state\n            //         .kbots\n            //         .iter()\n            //         .filter(|e| e.is_in_screen && e.distance_to_camera < unit_icon_distance)\n            //     {\n            //         let mat = mobile.trans.unwrap();\n            //         let is_selected = if self.game_state.selected.contains(&mobile.id.value) {\n            //             1.0\n            //         } else {\n            //             0.0\n            //         };\n            //         let team = mobile.team;\n\n            //         self.vertex_attr_buffer_f32\n            //             .extend_from_slice(mat.as_slice());\n            //         self.vertex_attr_buffer_f32.push(is_selected);\n            //         self.vertex_attr_buffer_f32.push(team as f32)\n            //     }\n\n            //     self.kbot_gpu\n            //         .update_instance_dirty(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n            // }\n            //Kinematic Projectile\n            self.vertex_attr_buffer_f32.clear();\n            for mobile in self.game_state.kinematic_projectiles.iter() {\n                let mat = utils::face_towards_dir(\n                    &mobile.coords,\n                    &(Vector3::new(1.0, 0.0, 0.0)),\n                    &Vector3::new(0.0, 0.0, 1.0),\n                );\n\n                let isometry: Isometry3<f32> =\n                    unsafe { na::convert_unchecked::<Matrix4<f32>, Isometry3<f32>>(mat) };\n                let euler = isometry.rotation.euler_angles();\n\n                self.vertex_attr_buffer_f32.push(mat[12]);\n                self.vertex_attr_buffer_f32.push(mat[13]);\n                self.vertex_attr_buffer_f32.push(mat[14]);\n                self.vertex_attr_buffer_f32.push(euler.0);\n                self.vertex_attr_buffer_f32.push(euler.1);\n                self.vertex_attr_buffer_f32.push(euler.2);\n                self.vertex_attr_buffer_f32.push(99.);\n                self.vertex_attr_buffer_f32.push(1.0)\n            }\n\n            self.kinematic_projectile_gpu\n                .update_instance_dirty(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n\n            //Arrow\n            self.vertex_attr_buffer_f32.clear();\n            for arrow in self.game_state.frame_zero.arrows.iter() {\n                let mat = Matrix4::face_towards(\n                    &arrow.position,\n                    &arrow.end,\n                    &Vector3::new(0.0, 0.0, 1.0),\n                );\n\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(mat.as_slice());\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(&arrow.color[..3]);\n                self.vertex_attr_buffer_f32\n                    .push((arrow.end.coords - arrow.position.coords).magnitude());\n            }\n\n            self.arrow_gpu\n                .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n\n            //Unit life\n            self.vertex_attr_buffer_f32.clear();\n            for (kbot, client_kbot) in self\n                .game_state\n                .kbots\n                .iter()\n                .filter(|e| e.1.is_in_screen && e.1.distance_to_camera < unit_icon_distance)\n            {\n                let distance = (self.game_state.position_smooth.coords\n                    - client_kbot.position.coords)\n                    .magnitude();\n\n                let alpha_range = 10.0;\n                let max_dist = 100.0;\n                let alpha = (1.0 + (max_dist - distance) / alpha_range)\n                    .min(1.0)\n                    .max(0.0)\n                    .powf(2.0);\n\n                let alpha_range = 50.0;\n                let size_factor = (0.3 + (max_dist - distance) / alpha_range)\n                    .min(1.0)\n                    .max(0.3)\n                    .powf(1.0);\n\n                let botdef = self\n                    .game_state\n                    .frame_zero\n                    .bot_defs\n                    .get(&kbot.botdef_id)\n                    .unwrap();\n                let life = kbot.life as f32 / botdef.max_life as f32;\n\n                let con_completed = kbot.con_completed;\n\n                let display_life = life < 1.0;\n                let display_con_completed = con_completed < 1.0;\n                let display_one = display_life || display_con_completed;\n\n                if alpha > 0.0 && display_one {\n                    let w = self.gpu.sc_desc.width as f32;\n                    let h = self.gpu.sc_desc.height as f32;\n                    let half_size = Vector2::new(20.0 / w, 3.0 / h) * size_factor;\n\n                    // u is direction above kbot in camera space\n                    // right cross camera_to_unit = u\n                    let camera_to_unit =\n                        client_kbot.position.coords - self.game_state.position_smooth.coords;\n                    let right = Vector3::new(1.0, 0.0, 0.0);\n\n                    let u = right.cross(&camera_to_unit).normalize();\n\n                    let world_pos = client_kbot.position + u * botdef.radius * 1.5;\n                    let r = view_proj * world_pos.to_homogeneous();\n                    let r = r / r.w;\n\n                    let offset = Vector2::new(r.x, r.y);\n                    let min = offset - half_size;\n                    let max = offset + half_size;\n                    let life = kbot.life as f32 / botdef.max_life as f32;\n                    self.vertex_attr_buffer_f32\n                        .extend_from_slice(min.as_slice());\n                    self.vertex_attr_buffer_f32\n                        .extend_from_slice(max.as_slice());\n                    self.vertex_attr_buffer_f32.push(life);\n                    self.vertex_attr_buffer_f32.push(alpha);\n                    //health\n                    self.vertex_attr_buffer_f32.push(0.0);\n\n                    let mut next_bar_offset = Vector2::new(0., -3. * half_size.y);\n                    if display_con_completed {\n                        let min = min + next_bar_offset;\n                        let max = max + next_bar_offset;\n                        self.vertex_attr_buffer_f32\n                            .extend_from_slice(min.as_slice());\n                        self.vertex_attr_buffer_f32\n                            .extend_from_slice(max.as_slice());\n                        self.vertex_attr_buffer_f32.push(con_completed);\n                        self.vertex_attr_buffer_f32.push(alpha);\n                        //con_completed\n                        self.vertex_attr_buffer_f32.push(1.0);\n                        next_bar_offset += Vector2::new(0., -3. * half_size.y);\n                    }\n                }\n            }\n            self.health_bar\n                .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n\n            //Icon\n            self.vertex_attr_buffer_f32.clear();\n            for (kbot, client_kbot) in self\n                .game_state\n                .kbots\n                .iter()\n                .filter(|e| e.1.is_in_screen && e.1.distance_to_camera >= unit_icon_distance)\n            {\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(client_kbot.screen_pos.as_slice());\n                //TODO f(distance) instead of 20.0\n                let size =\n                    ((1.0 / (client_kbot.distance_to_camera / unit_icon_distance)) * 15.0).max(4.0);\n                self.vertex_attr_buffer_f32.push(size);\n\n                let is_selected = self.game_state.selected.contains(&kbot.id);\n                let team = if is_selected { -1.0 } else { kbot.team as f32 };\n                self.vertex_attr_buffer_f32.push(team);\n            }\n            self.unit_icon\n                .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n\n            //Cursor Icon\n            self.vertex_attr_buffer_f32.clear();\n            {\n                let cursor_icon_size: i32 = 48;\n                let cursor_icon_size_half = cursor_icon_size / 2;\n                let cursor_icon_size_third = cursor_icon_size / 3;\n                let (x, y) = self.input_state.cursor_pos;\n                let (x, y) = (x as i32, y as i32);\n                let (x, y) = (x + cursor_icon_size_third, y - cursor_icon_size_third);\n\n                let min_screen = Vector2::new(\n                    (x - cursor_icon_size_half) as f32 / self.gpu.sc_desc.width as f32,\n                    (y - cursor_icon_size_half) as f32 / self.gpu.sc_desc.height as f32,\n                );\n                let max_screen = Vector2::new(\n                    (x + cursor_icon_size_half) as f32 / self.gpu.sc_desc.width as f32,\n                    (y + cursor_icon_size_half) as f32 / self.gpu.sc_desc.height as f32,\n                );\n                let mut min_texture = Vector2::new(0.0, 0.0);\n                let mut max_texture = Vector2::new(0.0, 0.0);\n\n                let mut index_to_vector = |x, y| {\n                    min_texture = Vector2::new(x as f32 * 0.25, y as f32 * 0.25);\n                    max_texture = min_texture + Vector2::new(0.25, 0.25);\n                };\n\n                match self.game_state.uitool {\n                    UiTool::Repair => {\n                        index_to_vector(0, 0);\n                    }\n                    UiTool::Spawn(..) => {\n                        index_to_vector(1, 0);\n                    }\n                    UiTool::Guard => {\n                        index_to_vector(2, 1);\n                    }\n                    UiTool::Attack => {\n                        index_to_vector(1, 1);\n                    }\n                    _ => {}\n                }\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(min_screen.as_slice());\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(max_screen.as_slice());\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(min_texture.as_slice());\n                self.vertex_attr_buffer_f32\n                    .extend_from_slice(max_texture.as_slice());\n            }\n            self.cursor_icon\n                .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n\n            //Line\n            self.vertex_attr_buffer_f32.clear();\n            {\n                let mut count = 0;\n                let see_all_order = self\n                    .input_state\n                    .key_pressed\n                    .contains(&winit::event::VirtualKeyCode::LShift);\n                {\n                    for (kbot, client_kbot) in self.game_state.kbots.iter() {\n                        if see_all_order || self.game_state.selected.contains(&kbot.id) {\n                            fn add_line(\n                                view_proj: &Matrix4<f32>,\n                                buffer: &mut Vec<f32>,\n                                start: &Point3<f32>,\n                                end: &Point3<f32>,\n                                type_: f32,\n                                count: &mut i32,\n                            ) {\n                                let min = view_proj * start.to_homogeneous();\n                                let max = view_proj * end.to_homogeneous();\n                                if (min.z > 0.0\n                                    && min.x > -min.w\n                                    && min.x < min.w\n                                    && min.y > -min.w\n                                    && min.y < min.w)\n                                    || (max.z > 0.0\n                                        && max.x > -max.w\n                                        && max.x < max.w\n                                        && max.y > -max.w\n                                        && max.y < max.w)\n                                {\n                                    *count += 1;\n                                    buffer.push(min.x / min.w);\n                                    buffer.push(min.y / min.w);\n                                    buffer.push(max.x / max.w);\n                                    buffer.push(max.y / max.w);\n                                    //0.0 is move line\n                                    //1.0 is build line\n                                    buffer.push(type_);\n                                    buffer.push(0.0);\n                                }\n                            }\n\n                            if let Some(target) = kbot.move_target {\n                                add_line(\n                                    view_proj,\n                                    &mut self.vertex_attr_buffer_f32,\n                                    &client_kbot.position,\n                                    &target,\n                                    0.0,\n                                    &mut count,\n                                );\n                            }\n                            match kbot.current_command {\n                                mobile::Command::Build(id_builded) => {\n                                    for target_kbot in\n                                        self.game_state.frame_zero.kbots.get(&id_builded)\n                                    {\n                                        add_line(\n                                            view_proj,\n                                            &mut self.vertex_attr_buffer_f32,\n                                            &client_kbot.position,\n                                            &target_kbot.position,\n                                            1.0,\n                                            &mut count,\n                                        );\n                                    }\n                                }\n                                mobile::Command::Repair(id_builded) => {\n                                    for target_kbot in\n                                        self.game_state.frame_zero.kbots.get(&id_builded)\n                                    {\n                                        add_line(\n                                            view_proj,\n                                            &mut self.vertex_attr_buffer_f32,\n                                            &client_kbot.position,\n                                            &target_kbot.position,\n                                            2.0,\n                                            &mut count,\n                                        );\n                                    }\n                                }\n                                _ => {}\n                            }\n                        }\n                    }\n                    for i in (0..self.vertex_attr_buffer_f32.len()).step_by(6) {\n                        self.vertex_attr_buffer_f32[i + 5] = count as f32;\n                    }\n                }\n            }\n            self.line_gpu\n                .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n\n            //Explosions\n            self.vertex_attr_buffer_f32.clear();\n            for explosion in self.game_state.explosions.iter() {\n                let screen_pos = view_proj * explosion.position.to_homogeneous();\n                if screen_pos.z > 0.0\n                    && screen_pos.x > -screen_pos.w\n                    && screen_pos.x < screen_pos.w\n                    && screen_pos.y > -screen_pos.w\n                    && screen_pos.y < screen_pos.w\n                {\n                    self.vertex_attr_buffer_f32.push(explosion.position.x);\n                    self.vertex_attr_buffer_f32.push(explosion.position.y);\n                    self.vertex_attr_buffer_f32.push(explosion.position.z);\n\n                    self.vertex_attr_buffer_f32.push(\n                        (self.game_state.server_sec - explosion.born_sec)\n                            / (explosion.death_sec - explosion.born_sec),\n                    );\n                    self.vertex_attr_buffer_f32.push(explosion.seed);\n                    self.vertex_attr_buffer_f32.push(explosion.size);\n                }\n            }\n            self.explosion_gpu\n                .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device);\n        });\n        self.profiler\n            .mix(\"upload_to_gpu\", upload_to_gpu_duration, 20);\n    }\n}\n"
  },
  {
    "path": "src/client/mod.rs",
    "content": "use crate::*;\n\nuse na::{Isometry3, Matrix4, Point3, Vector2, Vector3, Vector4};\n\nuse gpu_obj::imgui_wgpu::Renderer;\n\nuse gpu_obj::arrow_gpu::ArrowGpu;\nuse gpu_obj::blit_texture::BlitTextureGpu;\nuse gpu_obj::gpu;\nuse gpu_obj::heightmap_gpu::HeightmapGpu;\nuse gpu_obj::model_gpu::ModelGpu;\nuse gpu_obj::trait_gpu::TraitGpu;\nuse gpu_obj::water::WaterGpu;\nuse imgui::*;\nuse imgui_winit_support;\nuse imgui_winit_support::WinitPlatform;\nmod camera;\nmod game_state;\nmod unit_part_gpu;\nuse unit_part_gpu::UnitPartGpu;\n\nmod unit_editor;\n\nmod heightmap_editor;\nmod input_state;\nmod misc;\nmod play;\nmod render;\nmod uitool;\n\nuse crate::heightmap_phy;\nuse log::info;\nuse spin_sleep::LoopHelper;\nuse std::collections::{HashMap, HashSet};\nuse std::path::{Path, PathBuf};\nuse std::time::Instant;\nuse utils::time;\nuse wgpu::{BufferMapAsyncResult, Extent3d, SwapChain, TextureFormat};\nuse winit::event::WindowEvent;\n\npub struct StartClient {\n    pub bind: String,\n}\n\npub struct StartServer {\n    pub bind: String,\n}\n\npub enum FromClient {\n    PlayerInput(frame::FrameEventFromPlayer),\n    StartServer(StartServer),\n    StartClient(StartClient),\n    DisconnectServer,\n    DisconnectClient,\n}\n\nstruct ImguiWrap {\n    imgui: imgui::Context,\n    platform: WinitPlatform,\n    renderer: Renderer,\n}\n\n#[derive(Clone)]\nenum RenderEvent {\n    ChangeMode { from: MainMode, to: MainMode },\n}\n\n#[derive(PartialEq, Clone, Copy)]\npub enum MainMode {\n    Home,\n    Play,\n    UnitEditor,\n    MapEditor,\n    MultiplayerLobby,\n}\n\n#[derive(PartialEq, Clone, Copy)]\npub enum NetMode {\n    Offline,\n    Server,\n    Client,\n}\n\npub struct App {\n    //Wgpu\n    gpu: gpu::WgpuState,\n\n    first_color_att_view: wgpu::TextureView,\n    secon_color_att_view: wgpu::TextureView,\n    normal_att_view: wgpu::TextureView,\n    forward_depth: wgpu::TextureView,\n    position_att: wgpu::Texture,\n    position_att_view: wgpu::TextureView,\n\n    heightmap_gpu: HeightmapGpu,\n    water_gpu: WaterGpu,\n    unit_part_gpu: UnitPartGpu,\n\n    arrow_gpu: ArrowGpu,\n    kinematic_projectile_gpu: ModelGpu,\n    vertex_attr_buffer_f32: Vec<f32>,\n\n    bind_group: wgpu::BindGroup,\n    bind_group_layout: wgpu::BindGroupLayout,\n\n    ub_camera_mat: wgpu::Buffer,\n\n    postfx: gpu_obj::post_fx::PostFx,\n    postfxaa: gpu_obj::post_fxaa::PostFxaa,\n    post_bicopy: gpu_obj::texture_view_bicopy::TextureViewBiCopy,\n    health_bar: gpu_obj::health_bar::HealthBarGpu,\n    line_gpu: gpu_obj::line::LineGpu,\n    cursor_icon: BlitTextureGpu,\n    unit_icon: gpu_obj::unit_icon::UnitIconGpu,\n    explosion_gpu: gpu_obj::explosion::ExplosionGpu,\n\n    game_state: game_state::State,\n    input_state: input_state::InputState,\n    imgui_wrap: ImguiWrap,\n\n    main_menu: MainMode,\n    net_mode: NetMode,\n\n    unit_editor: unit_editor::UnitEditor,\n\n    sender_to_client: crossbeam_channel::Sender<ToClient>,\n    receiver_to_client: crossbeam_channel::Receiver<ToClient>,\n\n    sender_to_event_loop: crossbeam_channel::Sender<EventLoopMsg>,\n\n    sender_from_client_to_manager: crossbeam_channel::Sender<FromClient>,\n\n    receiver_notify: crossbeam_channel::Receiver<notify::Result<notify::event::Event>>,\n    watcher: notify::RecommendedWatcher,\n\n    mailbox: Vec<RenderEvent>,\n\n    loop_helper: LoopHelper,\n    profiler: frame::ProfilerMap,\n    global_info: Option<manager::GlobalInfo>,\n    threadpool: rayon::ThreadPool,\n\n    frame_count: i32,\n}\n\nimpl App {\n    pub fn new(\n        window: winit::window::Window,\n        sender_to_client: crossbeam_channel::Sender<ToClient>,\n        receiver_to_client: crossbeam_channel::Receiver<ToClient>,\n\n        sender_to_event_loop: crossbeam_channel::Sender<EventLoopMsg>,\n        sender_from_client_to_manager: crossbeam_channel::Sender<FromClient>,\n    ) -> (Self) {\n        log::trace!(\"App init\");\n\n        let mut gpu = gpu::WgpuState::new(window);\n\n        let mut init_encoder = gpu\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });\n\n        let bind_group_layout =\n            gpu.device\n                .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n                    bindings: &[\n                        wgpu::BindGroupLayoutBinding {\n                            binding: 0,\n                            visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,\n                            ty: wgpu::BindingType::UniformBuffer { dynamic: false },\n                        },\n                        wgpu::BindGroupLayoutBinding {\n                            binding: 1,\n                            visibility: wgpu::ShaderStage::FRAGMENT,\n                            ty: wgpu::BindingType::SampledTexture {\n                                multisampled: false,\n                                dimension: wgpu::TextureViewDimension::D2,\n                            },\n                        },\n                        wgpu::BindGroupLayoutBinding {\n                            binding: 2,\n                            visibility: wgpu::ShaderStage::FRAGMENT,\n                            ty: wgpu::BindingType::Sampler,\n                        },\n                    ],\n                });\n\n        // Create the texture\n        let size = 256u32;\n        let texels = procedural_texels::create_texels(size as usize);\n        let texture_extent = wgpu::Extent3d {\n            width: size,\n            height: size,\n            depth: 1,\n        };\n        let texture = gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: texture_extent,\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n            usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,\n        });\n\n        let texture_view = texture.create_default_view();\n        let temp_buf = gpu\n            .device\n            .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&texels);\n        init_encoder.copy_buffer_to_texture(\n            wgpu::BufferCopyView {\n                buffer: &temp_buf,\n                offset: 0,\n                row_pitch: 4 * size,\n                image_height: size,\n            },\n            wgpu::TextureCopyView {\n                texture: &texture,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: 0.0,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            },\n            texture_extent,\n        );\n\n        // Create other resources\n        let sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::MirrorRepeat,\n            address_mode_v: wgpu::AddressMode::MirrorRepeat,\n            address_mode_w: wgpu::AddressMode::MirrorRepeat,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Nearest,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let mx_total = camera::create_view_proj(\n            gpu.sc_desc.width as f32 / gpu.sc_desc.height as f32,\n            1.0,\n            &Point3::new(0.0, 0.0, 0.0),\n            &Vector3::new(0.0, 0.0, 0.0),\n        );\n        let mx_ref: &[f32] = mx_total.as_slice();\n\n        let mx_view =\n            camera::create_view(&Point3::new(0.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 0.0));\n        let mx_view_ref: &[f32] = mx_view.as_slice();\n\n        let mx_normal =\n            camera::create_normal(&Point3::new(0.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 0.0));\n        let mx_normal_ref: &[f32] = mx_normal.as_slice();\n\n        let mut filler = Vec::new();\n        filler.extend_from_slice(mx_ref);\n        filler.extend_from_slice(mx_view_ref);\n        //TODO reuse camera.rs code\n        filler.extend_from_slice(mx_ref);\n        filler.extend_from_slice(mx_normal_ref);\n        filler.extend_from_slice(&[\n            0.0_f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0,\n        ]);\n        // mat4 cor_proj_view;\n        // mat4 u_View;\n        // mat4 u_proj;\n        // mat4 u_Normal;\n        // vec2 mouse_pos;\n        // vec2 resolution;\n        // vec2 inv_resolution;\n        // vec2 start_drag;\n        // float radius\n        // float pen_strength\n        // vec2 mapSize;\n\n        let ub_camera_mat = gpu\n            .device\n            .create_buffer_mapped(\n                16 * 4 + 12,\n                wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&filler[..]);\n\n        // Create bind group\n        let bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::Buffer {\n                        buffer: &ub_camera_mat,\n                        range: 0..(4 * 16 + 12) * 4,\n                    },\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&texture_view),\n                },\n                wgpu::Binding {\n                    binding: 2,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        log::trace!(\"   imgui_wrap init\");\n        let imgui_wrap = {\n            // imgui\n            let mut imgui = imgui::Context::create();\n            let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui);\n            platform.attach_window(\n                imgui.io_mut(),\n                &gpu.window,\n                imgui_winit_support::HiDpiMode::Locked(1.0),\n            );\n            imgui.set_ini_filename(None);\n\n            let font_size = (13.0 * gpu.hidpi_factor) as f32;\n            imgui.io_mut().font_global_scale = (1.0) as f32;\n\n            imgui.io_mut().mouse_draw_cursor = true;\n\n            imgui.fonts().add_font(&[FontSource::DefaultFontData {\n                config: Some(imgui::FontConfig {\n                    oversample_h: 1,\n                    pixel_snap_h: true,\n                    size_pixels: font_size,\n                    ..Default::default()\n                }),\n            }]);\n\n            // imgui <-> wgpu\n            let renderer = Renderer::new(\n                &mut imgui,\n                &mut gpu.device,\n                &mut gpu.queue,\n                gpu.sc_desc.format,\n                None,\n            );\n\n            ImguiWrap {\n                imgui,\n                platform,\n                renderer,\n            }\n        };\n\n        let format: TextureFormat = gpu.sc_desc.format;\n\n        let heightmap_gpu = HeightmapGpu::new(\n            &gpu.device,\n            &mut init_encoder,\n            format,\n            &bind_group_layout,\n            heightmap_phy::HeightmapPhy::new(2048, 2048),\n        );\n\n        let kinematic_projectile_gpu = ModelGpu::new(\n            &model::open_obj(\"./src/asset/3d/small_sphere.obj\").unwrap(),\n            &gpu.device,\n            format,\n            &bind_group_layout,\n        );\n\n        let arrow_gpu = ArrowGpu::new(\n            &model::open_obj(\"./src/asset/3d/arrow.obj\").unwrap(),\n            &gpu.device,\n            format,\n            &bind_group_layout,\n        );\n\n        let mut unit_part_gpu = UnitPartGpu::new();\n\n        unit_part_gpu.append(Path::new(\"./src/asset/3d/cube.obj\").to_owned());\n        unit_part_gpu.append(Path::new(\"./src/asset/3d/axis_debug.obj\").to_owned());\n        unit_part_gpu.append(Path::new(\"./src/asset/3d/small_sphere.obj\").to_owned());\n\n        let health_bar =\n            gpu_obj::health_bar::HealthBarGpu::new(&gpu.device, format, &bind_group_layout);\n\n        let line_gpu = gpu_obj::line::LineGpu::new(&gpu.device, format, &bind_group_layout);\n\n        let cursor_icon = BlitTextureGpu::new(\n            &mut init_encoder,\n            &gpu.device,\n            format,\n            &bind_group_layout,\n            crate::utils::ImageRGBA8::open(\"./src/asset/2d/cursor_icons.png\"),\n        );\n\n        let unit_icon =\n            gpu_obj::unit_icon::UnitIconGpu::new(&gpu.device, format, &bind_group_layout);\n\n        let depth_texture = gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: gpu.sc_desc.width,\n                height: gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Depth32Float,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,\n        });\n\n        let forward_depth = depth_texture.create_default_view();\n\n        let position_att = gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: gpu.sc_desc.width,\n                height: gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba32Float,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT\n                | wgpu::TextureUsage::SAMPLED\n                | wgpu::TextureUsage::COPY_SRC,\n        });\n\n        let position_att_view = position_att.create_default_view();\n\n        let game_state = game_state::State::new();\n\n        let (receiver_notify, watcher) = {\n            use crossbeam_channel::unbounded;\n            use notify::{watcher, RecursiveMode, Result};\n            use std::time::Duration;\n            let (tx, rx) = unbounded();\n            use notify::Watcher;\n\n            // Automatically select the best implementation for your platform.\n            // You can also access each implementation directly e.g. INotifyWatcher.\n            let mut watcher = watcher(tx, Duration::from_millis(500)).unwrap();\n\n            // Add a path to be watched. All files and directories at that path and\n            // below will be monitored for changes.\n            watcher\n                .watch(std::env::current_dir().unwrap(), RecursiveMode::Recursive)\n                .unwrap();\n            (rx, watcher)\n        };\n\n        let first_color_att = gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: gpu.sc_desc.width,\n                height: gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Bgra8UnormSrgb,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT\n                | wgpu::TextureUsage::SAMPLED\n                | wgpu::TextureUsage::COPY_SRC,\n        });\n\n        let first_color_att_view = first_color_att.create_default_view();\n\n        let secon_color_att = gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: gpu.sc_desc.width,\n                height: gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Bgra8UnormSrgb,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT\n                | wgpu::TextureUsage::SAMPLED\n                | wgpu::TextureUsage::COPY_SRC,\n        });\n\n        let secon_color_att_view = secon_color_att.create_default_view();\n\n        let normal_att = gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: gpu.sc_desc.width,\n                height: gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg16Float,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,\n        });\n\n        let normal_att_view = normal_att.create_default_view();\n\n        let explosion_gpu = gpu_obj::explosion::ExplosionGpu::new(\n            &mut init_encoder,\n            &gpu.device,\n            format,\n            &bind_group_layout,\n            &position_att_view,\n            &normal_att_view,\n        );\n\n        let water_gpu = WaterGpu::new(\n            &gpu.device,\n            format,\n            &bind_group_layout,\n            &secon_color_att_view,\n            &position_att_view,\n        );\n\n        let postfx = gpu_obj::post_fx::PostFx::new(\n            &gpu.device,\n            &bind_group_layout,\n            format,\n            &position_att_view,\n        );\n        let postfxaa = gpu_obj::post_fxaa::PostFxaa::new(\n            &gpu.device,\n            &bind_group_layout,\n            format,\n            &first_color_att_view,\n        );\n\n        let post_bicopy = gpu_obj::texture_view_bicopy::TextureViewBiCopy::new(\n            &gpu.device,\n            &bind_group_layout,\n            format,\n            &secon_color_att_view,\n        );\n\n        let mut unit_editor = unit_editor::UnitEditor::new();\n        Self::load_botdef_in_editor(\n            \"src/asset/botdef/unit_example.json\",\n            &mut unit_editor,\n            &mut unit_part_gpu,\n        );\n\n        gpu.queue.submit(&[init_encoder.finish()]);\n        // Done\n        let this = App {\n            gpu,\n\n            bind_group,\n            bind_group_layout,\n            ub_camera_mat,\n\n            unit_part_gpu,\n            kinematic_projectile_gpu,\n            arrow_gpu,\n            heightmap_gpu,\n            water_gpu,\n            vertex_attr_buffer_f32: Vec::new(),\n\n            first_color_att_view,\n            secon_color_att_view,\n            normal_att_view,\n            forward_depth,\n            position_att_view,\n            position_att,\n\n            postfx,\n            postfxaa,\n            post_bicopy,\n            health_bar,\n            line_gpu,\n            cursor_icon,\n            unit_icon,\n            explosion_gpu,\n\n            game_state,\n            input_state: input_state::InputState::new(),\n            imgui_wrap,\n            main_menu: MainMode::Home,\n            net_mode: NetMode::Offline,\n            unit_editor,\n\n            sender_to_client,\n            receiver_to_client,\n            sender_to_event_loop,\n            sender_from_client_to_manager,\n            receiver_notify,\n            watcher,\n\n            mailbox: Vec::new(),\n\n            loop_helper: LoopHelper::builder().build_with_target_rate(144.0),\n            profiler: frame::ProfilerMap::new(),\n            global_info: None,\n            threadpool: rayon::ThreadPoolBuilder::new()\n                // .num_threads(8)\n                .build()\n                .unwrap(),\n            frame_count: 0,\n        };\n\n        (this)\n    }\n\n    fn resize(&mut self) -> Option<wgpu::CommandBuffer> {\n        log::trace!(\"resize\");\n\n        let normal_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: self.gpu.sc_desc.width,\n                height: self.gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rg16Float,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED,\n        });\n\n        self.normal_att_view = normal_att.create_default_view();\n\n        let first_color_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: self.gpu.sc_desc.width,\n                height: self.gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Bgra8UnormSrgb,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT\n                | wgpu::TextureUsage::SAMPLED\n                | wgpu::TextureUsage::COPY_SRC,\n        });\n\n        self.first_color_att_view = first_color_att.create_default_view();\n\n        let secon_color_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: self.gpu.sc_desc.width,\n                height: self.gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Bgra8UnormSrgb,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT\n                | wgpu::TextureUsage::SAMPLED\n                | wgpu::TextureUsage::COPY_SRC,\n        });\n\n        self.secon_color_att_view = secon_color_att.create_default_view();\n\n        self.postfxaa\n            .update_last_pass_view(&self.gpu.device, &self.first_color_att_view);\n\n        self.post_bicopy\n            .update_last_pass_view(&self.gpu.device, &self.secon_color_att_view);\n\n        let depth_texture = self.gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: self.gpu.sc_desc.width,\n                height: self.gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Depth32Float,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,\n        });\n        self.forward_depth = depth_texture.create_default_view();\n\n        let position_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor {\n            size: wgpu::Extent3d {\n                width: self.gpu.sc_desc.width,\n                height: self.gpu.sc_desc.height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba32Float,\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT\n                | wgpu::TextureUsage::SAMPLED\n                | wgpu::TextureUsage::COPY_SRC,\n        });\n\n        self.position_att_view = position_att.create_default_view();\n\n        self.water_gpu.update_bind_group(\n            &self.gpu.device,\n            &self.secon_color_att_view,\n            &self.position_att_view,\n        );\n\n        self.postfx\n            .update_pos_att_view(&self.gpu.device, &self.position_att_view);\n        self.position_att = position_att;\n\n        self.explosion_gpu.update_bind_group(\n            &self.gpu.device,\n            &self.position_att_view,\n            &self.normal_att_view,\n        );\n\n        None\n    }\n\n    pub fn handle_winit_event(&mut self, _event: &winit::event::Event<()>) {\n        log::trace!(\"[client.rs] update {:?}\", _event);\n        use winit::event;\n\n        self.imgui_wrap.platform.handle_event(\n            self.imgui_wrap.imgui.io_mut(),\n            &self.gpu.window,\n            _event,\n        );\n\n        //Low level\n        match _event {\n            event::Event::WindowEvent {\n                event: WindowEvent::Resized(size),\n                ..\n            } => {\n                let physical = size.to_physical(self.gpu.hidpi_factor);\n                info!(\"Resizing to logical {:?} physical {:?}\", size, physical);\n                self.gpu.sc_desc.width = physical.width.round() as u32;\n                self.gpu.sc_desc.height = physical.height.round() as u32;\n                self.gpu.swap_chain = self\n                    .gpu\n                    .device\n                    .create_swap_chain(&self.gpu.surface, &self.gpu.sc_desc);\n                let command_buf = self.resize();\n                if let Some(command_buf) = command_buf {\n                    self.gpu.queue.submit(&[command_buf]);\n                }\n            }\n            event::Event::WindowEvent { event, .. } => match event {\n                WindowEvent::CloseRequested => {\n                    self.sender_to_event_loop.send(EventLoopMsg::Stop).unwrap();\n                }\n                WindowEvent::KeyboardInput {\n                    input:\n                        event::KeyboardInput {\n                            virtual_keycode: Some(vkc),\n                            state: event::ElementState::Pressed,\n                            ..\n                        },\n                    ..\n                } => {\n                    self.input_state.key_pressed.insert(vkc.clone());\n                    self.input_state.key_trigger.insert(vkc.clone());\n                }\n                WindowEvent::KeyboardInput {\n                    input:\n                        event::KeyboardInput {\n                            virtual_keycode: Some(vkc),\n                            state: event::ElementState::Released,\n                            ..\n                        },\n                    ..\n                } => {\n                    self.input_state.key_pressed.remove(vkc);\n                    self.input_state.key_release.insert(vkc.clone());\n                }\n\n                WindowEvent::MouseWheel {\n                    delta: event::MouseScrollDelta::LineDelta(_, dy),\n                    ..\n                } => {\n                    self.input_state.last_scroll = *dy;\n                }\n\n                WindowEvent::CursorMoved { position, .. } => {\n                    let position = position.to_physical(self.gpu.hidpi_factor);\n\n                    let (old_x, old_y) = self.input_state.cursor_pos;\n\n                    self.input_state.cursor_offset = (\n                        position.x as i32 - old_x as i32,\n                        position.y as i32 - old_y as i32,\n                    );\n                    self.input_state.cursor_pos = (position.x as u32, position.y as u32);\n                    match self.input_state.drag {\n                        input_state::Drag::Start { x0, y0 }\n                        | input_state::Drag::Dragging { x0, y0, .. } => {\n                            self.input_state.drag = input_state::Drag::Dragging {\n                                x0,\n                                y0,\n                                x1: self.input_state.cursor_pos.0 as u32,\n                                y1: self.input_state.cursor_pos.1 as u32,\n                            };\n                        }\n                        _ => {}\n                    }\n                }\n\n                WindowEvent::MouseInput { state, button, .. } => {\n                    if !self.imgui_wrap.imgui.io().want_capture_mouse {\n                        if let &winit::event::ElementState::Pressed = state {\n                            self.input_state.mouse_pressed.insert(*button);\n                            self.input_state.mouse_trigger.insert(*button);\n\n                            if let event::MouseButton::Left = button {\n                                self.input_state.drag = input_state::Drag::Start {\n                                    x0: self.input_state.cursor_pos.0 as u32,\n                                    y0: self.input_state.cursor_pos.1 as u32,\n                                }\n                            };\n                        } else {\n                            self.input_state.mouse_pressed.remove(button);\n                            self.input_state.mouse_release.insert(*button);\n                            if let event::MouseButton::Left = button {\n                                match self.input_state.drag {\n                                    input_state::Drag::Dragging { x0, y0, .. } => {\n                                        self.input_state.drag = input_state::Drag::End {\n                                            x0,\n                                            y0,\n                                            x1: self.input_state.cursor_pos.0 as u32,\n                                            y1: self.input_state.cursor_pos.1 as u32,\n                                        };\n                                    }\n                                    _ => {\n                                        self.input_state.drag = input_state::Drag::None;\n                                    }\n                                }\n                            }\n                        }\n                    }\n                }\n\n                _ => {\n                    // log::warn!(\"{:?}\", x);\n                }\n            },\n            _ => (),\n        }\n    }\n\n    pub fn map_read_async_msg(&mut self, vec: Vec<f32>, usage: String) {\n        let to_update = match usage.as_ref() {\n            \"screen_center_world_pos\" => &mut self.game_state.screen_center_world_pos,\n            \"mouse_world_pos\" => &mut self.game_state.mouse_world_pos,\n            _ => &mut self.game_state.mouse_world_pos,\n        };\n\n        if vec.len() == 4 && vec[0] >= 0.0 {\n            std::mem::replace(to_update, Some(Vector3::new(vec[0], vec[1], vec[2])));\n        } else {\n            std::mem::replace(to_update, None);\n        }\n    }\n\n    pub fn receive(&mut self) {\n        let msg: std::result::Result<\n            notify::Result<notify::event::Event>,\n            crossbeam_channel::TryRecvError,\n        > = self.receiver_notify.try_recv();\n\n        match msg {\n            Ok(Ok(event)) => {\n                log::trace!(\"notify {:?}\", event);\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"post_ui.frag\" || name.to_os_string() == \"post.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading post.vert/post_ui.frag\");\n                    self.postfx.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"post_fxaa.frag\"\n                            || name.to_os_string() == \"post.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading post.vert/post_fxaa.frag\");\n                    self.postfxaa.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"heightmap.frag\"\n                            || name.to_os_string() == \"heightmap.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading heightmap.vert/heightmap.frag\");\n                    self.heightmap_gpu.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"cube_instanced.frag\"\n                            || name.to_os_string() == \"cube_instanced.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading cube_instanced.vert/cube_instanced.frag\");\n                    for (model_gpu_state) in self.unit_part_gpu.states.iter_mut() {\n                        match model_gpu_state {\n                            unit_part_gpu::ModelGpuState::Ready(model_gpu) => model_gpu\n                                .reload_shader(\n                                    &self.gpu.device,\n                                    &self.bind_group_layout,\n                                    self.gpu.sc_desc.format,\n                                ),\n                            _ => {}\n                        }\n                    }\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"arrow.frag\" || name.to_os_string() == \"arrow.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading arrow.vert/arrow.frag\");\n                    self.arrow_gpu.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"health_bar.frag\"\n                            || name.to_os_string() == \"health_bar.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading health_bar.vert/health_bar.frag\");\n                    self.health_bar.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"unit_icon.frag\"\n                            || name.to_os_string() == \"unit_icon.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading unit_icon.vert/unit_icon.frag\");\n                    self.unit_icon.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"explosion.frag\"\n                            || name.to_os_string() == \"explosion.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading explosion.vert/explosion.frag\");\n                    self.explosion_gpu.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"line.frag\" || name.to_os_string() == \"line.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading line.vert/line.frag\");\n                    self.line_gpu.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"water.frag\" || name.to_os_string() == \"water.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading water.vert/water.frag\");\n                    self.water_gpu.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n\n                if event.paths.iter().any(|p| {\n                    p.file_name().iter().any(|name| {\n                        name.to_os_string() == \"blit_texture.frag\"\n                            || name.to_os_string() == \"blit_texture.vert\"\n                    })\n                }) {\n                    log::info!(\"Reloading blit_texture.vert/blit_texture.frag\");\n                    self.cursor_icon.reload_shader(\n                        &self.gpu.device,\n                        &self.bind_group_layout,\n                        self.gpu.sc_desc.format,\n                    );\n                }\n            }\n            _ => {}\n        }\n\n        let msgs: Vec<_> = self.receiver_to_client.try_iter().collect();\n        for msg in msgs {\n            {\n                match msg {\n                    ToClient::MapReadAsyncMessage { vec, usage } => {\n                        log::trace!(\"receive: MapReadAsyncMessage\");\n                        self.map_read_async_msg(vec, usage);\n                    }\n                    ToClient::NewFrame(frame) => {\n                        self.game_state.handle_new_frame(frame);\n                    }\n                    ToClient::GlobalInfo(global_info) => self.global_info = Some(global_info),\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/client/play.rs",
    "content": "use super::client::*;\nuse crate::botdef;\nuse crate::frame::FrameEventFromPlayer;\nuse crate::frame::Player;\nuse crate::*;\nuse fnv::{FnvHashMap, FnvHashSet};\nuse imgui::*;\nuse mobile::*;\nuse na::{Isometry3, Matrix4, Point3, Vector2, Vector3, Vector4};\nuse std::time::{Duration, Instant};\nuse utils::*;\n\nimpl App {\n    pub fn init_play(&mut self) {\n        match self.net_mode {\n            NetMode::Offline | NetMode::Server => {\n                self.clear_gpu_instance_and_game_state();\n                self.game_state.position =\n                    Point3::new(300.0, 100.0, self.heightmap_gpu.phy.z(300.0, 100.0) + 50.0);\n                self.game_state.dir = Vector3::new(0.0, 0.3, -1.0);\n\n                let mut player_me = Player::new();\n                let mut player_ennemy = Player::new();\n                player_ennemy.team = 1;\n\n                let mut kbots = FnvHashMap::default();\n\n                let tank_example =\n                    Self::load_botdef_on_disk(\"src/asset/botdef/unit_example.json\").unwrap();\n                let building_example =\n                    Self::load_botdef_on_disk(\"src/asset/botdef/building_example.json\").unwrap();\n\n                for i in (100..300).step_by(4) {\n                    for j in (100..500).step_by(4) {\n                        let m = mobile::KBot::new(\n                            Point3::new(i as f32, j as f32, 100.0),\n                            &tank_example,\n                            player_me.id,\n                        );\n                        player_me.kbots.insert(m.id);\n                        kbots.insert(m.id, m);\n                    }\n                }\n\n                for i in (320..520).step_by(4) {\n                    for j in (100..500).step_by(4) {\n                        let mut m = mobile::KBot::new(\n                            Point3::new(i as f32, j as f32, 100.0),\n                            &tank_example,\n                            player_ennemy.id,\n                        );\n                        m.team = 1;\n                        player_ennemy.kbots.insert(m.id);\n                        kbots.insert(m.id, m);\n                    }\n                }\n\n                log::info!(\"Starting a game with {} bots\", kbots.len());\n\n                self.game_state.my_player_id = Some(player_me.id);\n                self.game_state.players.insert(player_me.id, player_me);\n                self.game_state\n                    .players\n                    .insert(player_ennemy.id, player_ennemy);\n\n                let mut bot_defs = FnvHashMap::default();\n                bot_defs.insert(tank_example.id, tank_example);\n                bot_defs.insert(building_example.id, building_example);\n\n                let mut moddef = crate::moddef::ModDef {\n                    units_id: bot_defs.keys().copied().collect(),\n                    con_map: FnvHashMap::default(),\n                };\n\n                let replacer = FrameEventFromPlayer::ReplaceFrame(frame::Frame {\n                    number: 0,\n                    players: self.game_state.players.clone(),\n                    moddef,\n                    kbots,\n                    kbots_dead: HashSet::default(),\n                    kinematic_projectiles_dead: Vec::new(),\n                    kinematic_projectiles_birth: Vec::new(),\n                    kinematic_projectiles: self.game_state.kinematic_projectiles_cache.clone(),\n                    arrows: Vec::new(),\n                    explosions: Vec::new(),\n                    heightmap_phy: Some(self.heightmap_gpu.phy.clone()),\n                    frame_profiler: frame::ProfilerMap::new(),\n                    bot_defs,\n                });\n                let _ = self\n                    .sender_from_client_to_manager\n                    .try_send(client::FromClient::PlayerInput(replacer));\n            }\n\n            NetMode::Client => {\n                self.clear_gpu_instance_and_game_state();\n                self.game_state.position =\n                    Point3::new(300.0, 100.0, self.heightmap_gpu.phy.z(300.0, 100.0) + 50.0);\n                self.game_state.dir = Vector3::new(0.0, 0.3, -1.0);\n                self.game_state.my_player_id = self\n                    .game_state\n                    .frame_zero\n                    .players\n                    .values()\n                    .filter(|p| p.team == 1)\n                    .map(|p| p.id.clone())\n                    .next();\n            }\n        }\n    }\n\n    pub fn handle_play(\n        &mut self,\n        delta_sim_sec: f32,\n        encoder: &mut wgpu::CommandEncoder,\n        view_proj: &Matrix4<f32>,\n    ) {\n        //Interpolate\n        let interp_duration = time(|| {\n            self.game_state.interpolate(&self.threadpool, &view_proj);\n        });\n\n        // Selection on screen\n        let selection_screen = time(|| {\n            //Under_cursor\n            {\n                let (x, y) = self.input_state.cursor_pos;\n                let (x, y) = (x as i32, y as i32);\n                let (x0, y0) = (x - 7, y - 7);\n                let (x1, y1) = (x + 7, y + 7);\n\n                let min_x = (x0.min(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0;\n                let min_y = (y0.min(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0;\n                let max_x = (x0.max(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0;\n                let max_y = (y0.max(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0;\n\n                if let Some(mpos) = self.game_state.mouse_world_pos {\n                    let mut closest = None;\n                    let mut screen_only = true;\n                    let mut distance = 999999999.0_f32;\n                    for e in self.game_state.kbots.iter() {\n                        if e.1.is_in_screen {\n                            let test3d =\n                                e.1.distance_to_camera < self.game_state.unit_icon_distance;\n\n                            let dist = || (e.1.position.coords - mpos).magnitude_squared();\n                            //TODO replace 1.0 with bot radius\n                            if test3d && dist() < 1.0 && dist() < distance {\n                                closest = Some(e.0.id);\n                                distance = dist();\n                                screen_only = false\n                            } else if screen_only\n                                && e.1.screen_pos.x > min_x\n                                && e.1.screen_pos.x < max_x\n                                && e.1.screen_pos.y < max_y\n                                && e.1.screen_pos.y > min_y\n                            {\n                                let dist = {\n                                    let dx = x as f32 - e.1.screen_pos.x;\n                                    let dy = y as f32 - e.1.screen_pos.y;\n                                    (dx * dx + dy * dy).sqrt() * 3000.0\n                                };\n\n                                if dist < distance {\n                                    closest = Some(e.0.id);\n                                    distance = dist\n                                }\n                            }\n                        }\n                    }\n                    self.game_state.under_mouse = closest;\n                } else {\n                    self.game_state.under_mouse = None;\n                }\n            }\n\n            if let Some(me) = self.game_state.my_player() {\n                //Selection square\n                if let input_state::Drag::End { x0, y0, x1, y1 } = self.input_state.drag {\n                    let start_sel = std::time::Instant::now();\n                    let min_x = (x0.min(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0;\n                    let min_y = (y0.min(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0;\n                    let max_x = (x0.max(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0;\n                    let max_y = (y0.max(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0;\n                    let selected: FnvHashSet<utils::Id<KBot>> = self\n                        .game_state\n                        .kbots\n                        .iter()\n                        .filter(|e| {\n                            e.1.is_in_screen\n                                && me.kbots.contains(&e.0.id)\n                                && e.1.screen_pos.x > min_x\n                                && e.1.screen_pos.x < max_x\n                                && e.1.screen_pos.y < max_y\n                                && e.1.screen_pos.y > min_y\n                        })\n                        .map(|e| e.0.id)\n                        .collect();\n\n                    log::trace!(\"Selection took {}us\", start_sel.elapsed().as_micros());\n\n                    self.game_state.selected = selected;\n                } else if self\n                    .input_state\n                    .mouse_release\n                    .contains(&winit::event::MouseButton::Left)\n                {\n                    //Single Picking\n\n                    if let Some(id) = self.game_state.under_mouse {\n                        if me.kbots.contains(&id) {\n                            self.game_state.selected.clear();\n                            self.game_state.selected.insert(id);\n                        } else {\n                            self.game_state.selected.clear();\n                        }\n                    } else {\n                        self.game_state.selected.clear();\n                    }\n                }\n            }\n        });\n\n        self.profiler.mix(\"interp\", interp_duration, 20);\n        self.profiler.mix(\"selection_screen\", selection_screen, 20);\n    }\n}\n"
  },
  {
    "path": "src/client/render.rs",
    "content": "use super::client::*;\nuse super::uitool::UiTool;\nuse crate::frame;\nuse crate::frame::FrameEventFromPlayer;\nuse crate::frame::Player;\nuse crate::*;\nuse imgui::*;\nuse na::{IsometryMatrix3, Matrix4, Point3, Vector2, Vector3, Vector4};\nuse std::time::Duration;\nuse std::time::Instant;\nuse unit_part_gpu;\nuse unit_part_gpu::UnitPartGpu;\nuse utils::time;\nuse wgpu::{BufferMapAsyncResult, Extent3d};\n\nimpl App {\n    pub fn render(&mut self) {\n        if self.frame_count == 1 {\n            self.gpu.window.set_maximized(true);\n        }\n        self.frame_count += 1;\n\n        let frame_time = self.game_state.last_frame.elapsed();\n        self.profiler.mix(\"frame_time\", frame_time, 20);\n\n        log::trace!(\"sleep\");\n        self.loop_helper.loop_sleep();\n        self.loop_helper.loop_start();\n        log::trace!(\"render\");\n\n        let capped_frame_time = self.game_state.last_frame.elapsed();\n\n        self.profiler\n            .mix(\"capped_frame_time\", capped_frame_time, 20);\n        self.game_state.last_frame = Instant::now();\n\n        let sim_sec = capped_frame_time.as_secs_f32();\n\n        let mailbox = self.mailbox.clone();\n        self.mailbox.clear();\n\n        for mail in mailbox {\n            match mail {\n                RenderEvent::ChangeMode {\n                    from,\n                    to: MainMode::MapEditor,\n                } => {\n                    self.clear_gpu_instance_and_game_state();\n                    self.game_state.position = Point3::new(1024.0, 400.0, 1100.0);\n                    self.game_state.dir = Vector3::new(0.0, 0.3, -1.0);\n                }\n\n                RenderEvent::ChangeMode {\n                    from,\n                    to: MainMode::Play,\n                } => {\n                    self.init_play();\n                }\n\n                RenderEvent::ChangeMode {\n                    from,\n                    to: MainMode::Home,\n                } => {\n                    self.clear_gpu_instance_and_game_state();\n                    self.game_state.position = Point3::new(200.0, 100.0, 50.0);\n                    self.game_state.dir = Vector3::new(0.0, 0.3, -1.0);\n                    match self.net_mode {\n                        NetMode::Offline | NetMode::Server => {\n                            let replacer = FrameEventFromPlayer::ReplaceFrame(frame::Frame::new());\n                            let _ = self\n                                .sender_from_client_to_manager\n                                .try_send(client::FromClient::PlayerInput(replacer));\n                        }\n                        NetMode::Client => {}\n                    }\n                }\n                RenderEvent::ChangeMode {\n                    from,\n                    to: MainMode::MultiplayerLobby,\n                } => {}\n                RenderEvent::ChangeMode {\n                    from,\n                    to: MainMode::UnitEditor,\n                } => {\n                    self.init_unit_editor();\n                }\n                _ => {\n                    log::info!(\"Something else\");\n                }\n            }\n        }\n\n        if self\n            .input_state\n            .key_trigger\n            .contains(&winit::event::VirtualKeyCode::Escape)\n        {\n            let next_mode = MainMode::Home;\n            if self.main_menu != next_mode {\n                self.mailbox.push(RenderEvent::ChangeMode {\n                    from: self.main_menu,\n                    to: next_mode,\n                });\n                self.main_menu = next_mode;\n            }\n        }\n\n        let mode_with_camera = [MainMode::Play, MainMode::MapEditor];\n        // Camera Movements\n        if mode_with_camera.contains(&self.main_menu) {\n            self.rts_camera(sim_sec);\n        }\n\n        if self.main_menu == MainMode::UnitEditor {\n            self.orbit_camera(sim_sec);\n        }\n\n        let heightmap_editor_duration = time(|| {\n            if let MainMode::MapEditor = self.main_menu {\n                if let Some(mouse_world_pos) = self.game_state.mouse_world_pos {\n                    self.game_state.heightmap_editor.handle_user_input(\n                        &self.input_state.mouse_pressed,\n                        &mouse_world_pos,\n                        &mut self.heightmap_gpu,\n                    );\n                }\n            }\n        });\n\n        self.profiler\n            .mix(\"heightmap_editor\", heightmap_editor_duration, 20);\n\n        //Render\n        let mut encoder_render = self\n            .gpu\n            .device\n            .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 });\n\n        //Load pending generic gpu\n        for (index, generic_gpu_state) in self.unit_part_gpu.states.iter_mut().enumerate() {\n            if let unit_part_gpu::ModelGpuState::ToLoad(tri_list) = generic_gpu_state {\n                let mut generic_gpu = ModelGpu::new(\n                    &tri_list,\n                    &self.gpu.device,\n                    self.gpu.sc_desc.format,\n                    &self.bind_group_layout,\n                );\n                log::debug!(\"Load pending generic gpu {:?} \", index);\n                let mut generic_gpu_state_new = unit_part_gpu::ModelGpuState::Ready(generic_gpu);\n                std::mem::replace(generic_gpu_state, generic_gpu_state_new);\n            }\n        }\n\n        let view_proj = camera::create_view_proj(\n            self.gpu.sc_desc.width as f32 / self.gpu.sc_desc.height as f32,\n            self.game_state.near(),\n            &self.game_state.position_smooth,\n            &self.game_state.dir_smooth,\n        );\n        if self.main_menu == MainMode::Play {\n            self.handle_play(sim_sec, &mut encoder_render, &view_proj);\n        }\n\n        self.upload_to_gpu(&view_proj, &mut encoder_render);\n\n        let heightmap_gpu_step_duration = time(|| {\n            self.heightmap_gpu\n                .step(&self.gpu.device, &mut encoder_render);\n        });\n\n        self.profiler\n            .mix(\"heightmap_gpu_step\", heightmap_gpu_step_duration, 20);\n\n        let mut start_drag = (\n            self.input_state.cursor_pos.0 as f32,\n            self.input_state.cursor_pos.1 as f32,\n        );\n\n        if let MainMode::Play = self.main_menu {\n            if let input_state::Drag::Dragging { x0, y0, .. } = self.input_state.drag {\n                start_drag = (x0 as f32, y0 as f32);\n            }\n        }\n\n        let radius = if self.main_menu == MainMode::MapEditor {\n            self.game_state.heightmap_editor.pen_radius as f32\n        } else {\n            0.0\n        };\n\n        let mut filler = camera::create_camera_uniform_vec(\n            (self.gpu.sc_desc.width, self.gpu.sc_desc.height),\n            self.game_state.near(),\n            &self.game_state.position_smooth,\n            &self.game_state.dir_smooth,\n        );\n\n        filler.extend_from_slice(&[\n            self.input_state.cursor_pos.0 as f32,\n            self.input_state.cursor_pos.1 as f32,\n            self.gpu.sc_desc.width as f32,\n            self.gpu.sc_desc.height as f32,\n            1.0 / self.gpu.sc_desc.width as f32,\n            1.0 / self.gpu.sc_desc.height as f32,\n            start_drag.0,\n            start_drag.1,\n            radius,\n            self.game_state.heightmap_editor.pen_strength as f32,\n            self.heightmap_gpu.phy.width as f32,\n            self.heightmap_gpu.phy.height as f32,\n        ]);\n\n        let ub_camera_temp = self\n            .gpu\n            .device\n            .create_buffer_mapped(4 * 16 + 12, wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&filler[..]);\n\n        encoder_render.copy_buffer_to_buffer(\n            &ub_camera_temp,\n            0,\n            &self.ub_camera_mat,\n            0,\n            (4 * 16 + 12) * 4,\n        );\n\n        self.heightmap_gpu.update_uniform(\n            &self.gpu.device,\n            &mut encoder_render,\n            self.game_state.position_smooth.x,\n            self.game_state.position_smooth.y,\n        );\n\n        //Imgui\n        let start = Instant::now();\n\n        log::trace!(\"imgui render\");\n        self.imgui_wrap\n            .platform\n            .prepare_frame(self.imgui_wrap.imgui.io_mut(), &self.gpu.window)\n            .expect(\"Failed to prepare frame\");\n\n        let ui: Ui = self.imgui_wrap.imgui.frame();\n        {\n            let main_menu = &mut self.main_menu;\n\n            {\n                //Stat\n                let fps_before = self.game_state.fps.clone();\n                let mut_fps = &mut self.game_state.fps;\n                let profiler_logic = &self.game_state.frame_zero.frame_profiler;\n                let profiler_render = &self.profiler;\n                let stats_window = imgui::Window::new(im_str!(\"Statistics\"));\n                stats_window\n                    .size([300.0, 400.0], imgui::Condition::FirstUseEver)\n                    .position([3.0, 3.0], imgui::Condition::FirstUseEver)\n                    .collapsed(true, imgui::Condition::FirstUseEver)\n                    .resizable(true)\n                    .movable(false)\n                    .build(&ui, || {\n                        imgui::Slider::new(im_str!(\"fps cap\"), 1..=480).build(&ui, mut_fps);\n                        ui.text(im_str!(\n                            \"render: {:?}\",\n                            profiler_render.get(\"frame_time\").unwrap()\n                        ));\n                        ProgressBar::new(\n                            profiler_render.get(\"frame_time\").unwrap().as_secs_f32()\n                                / (1.0 / *mut_fps as f32),\n                        )\n                        .build(&ui);\n                        let mut others = profiler_render\n                            .hm\n                            .iter()\n                            .filter(|(n, d)| *n != \"frame_time\")\n                            .collect::<Vec<_>>();\n                        others.sort_by_key(|e| e.0);\n                        for (name, dur) in others.iter() {\n                            let name: String = format!(\"{}                          \", name)\n                                .chars()\n                                .take(23)\n                                .collect::<Vec<char>>()\n                                .into_iter()\n                                .collect();\n                            ui.text(im_str!(\" {}: {:?}\", name, dur));\n                        }\n\n                        ui.separator();\n\n                        ui.text(im_str!(\"logic: {:?}\", profiler_logic.get(\"total\").unwrap()));\n                        ProgressBar::new(\n                            profiler_logic.get(\"total\").unwrap().as_millis() as f32 / 100.0,\n                        )\n                        .build(&ui);\n\n                        let mut others = profiler_logic\n                            .hm\n                            .iter()\n                            .filter(|(n, d)| *n != \"total\")\n                            .collect::<Vec<_>>();\n                        others.sort_by_key(|e| e.0);\n                        for (name, dur) in others.iter() {\n                            let name: String = format!(\"{}                          \", name)\n                                .chars()\n                                .take(23)\n                                .collect::<Vec<char>>()\n                                .into_iter()\n                                .collect();\n                            ui.text(im_str!(\" {}: {:?}\", name, dur));\n                        }\n                    });\n\n                if fps_before != *mut_fps {\n                    self.loop_helper = LoopHelper::builder().build_with_target_rate(*mut_fps as f64)\n                }\n\n                //Global info\n                if let Some(global_info) = self.global_info {\n                    let w = 300.0;\n                    let h = 200.0;\n\n                    let info_window = imgui::Window::new(im_str!(\"Global info\"));\n\n                    info_window\n                        .size([w, h], imgui::Condition::FirstUseEver)\n                        .position(\n                            [self.gpu.sc_desc.width as f32 - w, 0.0],\n                            imgui::Condition::Always,\n                        )\n                        .collapsed(true, imgui::Condition::FirstUseEver)\n                        .resizable(true)\n                        .movable(false)\n                        .build(&ui, || {\n                            ui.text(im_str!(\"{:#?}\", global_info));\n                        });\n                }\n\n                //Main menu\n                match main_menu {\n                    MainMode::Home => {\n                        let home_window = imgui::Window::new(im_str!(\"Home\"));\n\n                        let mut next_mode = MainMode::Home;\n                        let mut exit = false;\n                        home_window\n                            // .size([w, h], imgui::Condition::Always)\n                            .position(\n                                [\n                                    (self.gpu.sc_desc.width as f32) / 2.0,\n                                    (self.gpu.sc_desc.height as f32) / 2.0,\n                                ],\n                                imgui::Condition::Always,\n                            )\n                            .position_pivot([0.5, 0.5])\n                            .title_bar(false)\n                            .always_auto_resize(true)\n                            .resizable(false)\n                            .movable(false)\n                            .scroll_bar(false)\n                            .collapsible(false)\n                            .build(&ui, || {\n                                if ui.button(im_str!(\"Play\"), [200.0_f32, 100.0]) {\n                                    next_mode = MainMode::Play;\n                                }\n                                if ui.button(im_str!(\"Map Editor\"), [200.0_f32, 100.0]) {\n                                    next_mode = MainMode::MapEditor;\n                                }\n                                if ui.button(im_str!(\"Unit Editor\"), [200.0_f32, 100.0]) {\n                                    next_mode = MainMode::UnitEditor;\n                                }\n                                if ui.button(im_str!(\"Multiplayer\"), [200.0_f32, 100.0]) {\n                                    next_mode = MainMode::MultiplayerLobby;\n                                }\n                                if ui.button(im_str!(\"Exit\"), [200.0_f32, 100.0]) {\n                                    exit = true;\n                                }\n                            });\n\n                        if exit {\n                            self.sender_to_event_loop.send(EventLoopMsg::Stop).unwrap();\n                        }\n                        if self.main_menu != next_mode {\n                            self.mailbox.push(RenderEvent::ChangeMode {\n                                from: self.main_menu,\n                                to: next_mode,\n                            });\n                            self.main_menu = next_mode;\n                        }\n                    }\n                    MainMode::Play => {\n                        if let Some(me) = self.game_state.my_player() {\n                            let resource_window = imgui::Window::new(im_str!(\"Resources\"));\n                            resource_window\n                                .size([400.0, 120.0], imgui::Condition::FirstUseEver)\n                                .position([500.0, 3.0], imgui::Condition::FirstUseEver)\n                                .collapsed(false, imgui::Condition::FirstUseEver)\n                                .build(&ui, || {\n                                    ui.text(im_str!(\"metal: {:.1}\", me.metal));\n                                    ProgressBar::new((me.metal / 500.0) as f32).build(&ui);\n                                    ui.text(im_str!(\"energy: {:.1}\", me.energy));\n                                    ProgressBar::new((me.energy / 500.0) as f32).build(&ui);\n                                });\n                        }\n\n                        let mut uitool = self.game_state.uitool;\n                        let can_be_built: Vec<_> = self\n                            .game_state\n                            .frame_zero\n                            .moddef\n                            .units_id\n                            .iter()\n                            .cloned()\n                            .collect();\n\n                        let can_be_built = &self.game_state.frame_zero.bot_defs;\n\n                        let command_window = imgui::Window::new(im_str!(\"Command\"));\n                        command_window\n                            .size([400.0, 300.0], imgui::Condition::FirstUseEver)\n                            .position([3.0, 415.0], imgui::Condition::FirstUseEver)\n                            .collapsed(false, imgui::Condition::FirstUseEver)\n                            .build(&ui, || {\n                                for can_be_built in can_be_built {\n                                    let txt = format!(\"Build {:?}\", can_be_built.1.file_path);\n                                    if ui.small_button(&im_str!(\"{}\", txt)) {\n                                        uitool = UiTool::Spawn(can_be_built.0.clone());\n                                    }\n                                }\n\n                                if ui.small_button(im_str!(\"Repair\")) {\n                                    uitool = UiTool::Repair;\n                                }\n                            });\n\n                        if self.game_state.uitool != uitool {\n                            log::debug!(\n                                \"UiTool state from {:?} to {:?}\",\n                                self.game_state.uitool,\n                                uitool\n                            );\n                            self.game_state.uitool = uitool;\n                        }\n                    }\n                    MainMode::MapEditor => {\n                        self.game_state\n                            .heightmap_editor\n                            .draw_ui(&ui, &mut self.heightmap_gpu);\n                    }\n                    MainMode::UnitEditor => {\n                        Self::draw_unit_editor_ui(\n                            &ui,\n                            &mut self.unit_editor,\n                            &mut self.unit_part_gpu,\n                        );\n                    }\n                    MainMode::MultiplayerLobby => {\n                        let w = 216.0;\n                        let h = 324.0;\n                        let home_window = imgui::Window::new(im_str!(\"Multiplayer Lobby\"));\n\n                        let mut create_server = false;\n                        let mut create_client = false;\n                        let mut disconnect_server = false;\n                        let mut disconnect_client = false;\n                        let mut next_mode = MainMode::MultiplayerLobby;\n                        if let Some(global_info) = self.global_info {\n                            home_window\n                                .size([w, h], imgui::Condition::Always)\n                                .position(\n                                    [\n                                        (self.gpu.sc_desc.width as f32 - w) / 2.0,\n                                        (self.gpu.sc_desc.height as f32 - h) / 2.0,\n                                    ],\n                                    imgui::Condition::Always,\n                                )\n                                .title_bar(false)\n                                .resizable(false)\n                                .movable(false)\n                                .collapsible(false)\n                                .build(&ui, || {\n                                    if global_info.net_server.is_none()\n                                        && global_info.net_client.is_none()\n                                    {\n                                        create_server =\n                                            ui.button(im_str!(\"Start server\"), [200.0_f32, 100.0]);\n                                        create_client =\n                                            ui.button(im_str!(\"Start client\"), [200.0_f32, 100.0]);\n                                    } else if global_info.net_server.is_some() {\n                                        disconnect_server = ui.button(\n                                            im_str!(\"Disconnect server\"),\n                                            [200.0_f32, 100.0],\n                                        );\n                                    } else if global_info.net_client.is_some() {\n                                        disconnect_client = ui.button(\n                                            im_str!(\"Disconnect client\"),\n                                            [200.0_f32, 100.0],\n                                        );\n                                    }\n\n                                    if ui.button(im_str!(\"Back\"), [200.0_f32, 100.0]) {\n                                        next_mode = MainMode::Home;\n                                    }\n                                });\n                        }\n\n                        if create_server {\n                            self.net_mode = NetMode::Server;\n                            let e = client::FromClient::StartServer(client::StartServer {\n                                bind: \"127.0.0.1:4567\".to_owned(),\n                            });\n                            let _ = self.sender_from_client_to_manager.try_send(e);\n                        }\n\n                        if create_client {\n                            self.net_mode = NetMode::Client;\n                            let e = client::FromClient::StartClient(client::StartClient {\n                                bind: \"127.0.0.1:4567\".to_owned(),\n                            });\n                            let _ = self.sender_from_client_to_manager.try_send(e);\n                        }\n                        if disconnect_server {\n                            self.net_mode = NetMode::Offline;\n                            let e = client::FromClient::DisconnectServer;\n                            let _ = self.sender_from_client_to_manager.try_send(e);\n                        }\n                        if disconnect_client {\n                            self.net_mode = NetMode::Offline;\n                            let e = client::FromClient::DisconnectClient;\n                            let _ = self.sender_from_client_to_manager.try_send(e);\n                        }\n\n                        if self.main_menu != next_mode {\n                            self.mailbox.push(RenderEvent::ChangeMode {\n                                from: self.main_menu,\n                                to: next_mode,\n                            });\n                            self.main_menu = next_mode;\n                        }\n                    }\n                }\n            }\n            self.imgui_wrap\n                .platform\n                .prepare_render(&ui, &self.gpu.window);\n        }\n        self.profiler.mix(\"imgui_render\", start.elapsed(), 20);\n\n        let frame = &self.gpu.swap_chain.get_next_texture();\n        let now = Instant::now();\n        // Pass\n        {\n            log::trace!(\"begin_render_pass\");\n            let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[\n                    wgpu::RenderPassColorAttachmentDescriptor {\n                        attachment: &self.first_color_att_view,\n                        resolve_target: None,\n                        load_op: wgpu::LoadOp::Clear,\n                        store_op: wgpu::StoreOp::Store,\n                        clear_color: wgpu::Color {\n                            r: 0.1,\n                            g: 0.2,\n                            b: 0.3,\n                            a: 1.0,\n                        },\n                    },\n                    wgpu::RenderPassColorAttachmentDescriptor {\n                        attachment: &self.position_att_view,\n                        resolve_target: None,\n                        load_op: wgpu::LoadOp::Clear,\n                        store_op: wgpu::StoreOp::Store,\n                        clear_color: wgpu::Color {\n                            r: -1.0,\n                            g: -1.0,\n                            b: -1.0,\n                            a: -1.0,\n                        },\n                    },\n                    wgpu::RenderPassColorAttachmentDescriptor {\n                        attachment: &self.normal_att_view,\n                        resolve_target: None,\n                        load_op: wgpu::LoadOp::Clear,\n                        store_op: wgpu::StoreOp::Store,\n                        clear_color: wgpu::Color {\n                            r: -1.0,\n                            g: -1.0,\n                            b: -1.0,\n                            a: -1.0,\n                        },\n                    },\n                ],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {\n                    attachment: &self.forward_depth,\n                    depth_load_op: wgpu::LoadOp::Clear,\n                    depth_store_op: wgpu::StoreOp::Store,\n                    stencil_load_op: wgpu::LoadOp::Clear,\n                    stencil_store_op: wgpu::StoreOp::Store,\n                    clear_depth: 1.0,\n                    clear_stencil: 0,\n                }),\n            });\n\n            self.heightmap_gpu.render(&mut rpass, &self.bind_group);\n            for model_gpu_state in self.unit_part_gpu.states.iter_mut() {\n                match model_gpu_state {\n                    unit_part_gpu::ModelGpuState::Ready(model_gpu) => {\n                        model_gpu.render(&mut rpass, &self.bind_group);\n                    }\n                    _ => {}\n                }\n            }\n            self.kinematic_projectile_gpu\n                .render(&mut rpass, &self.bind_group);\n            self.arrow_gpu.render(&mut rpass, &self.bind_group);\n        }\n\n        //Transparent pass\n        {\n            log::trace!(\"begin_render_pass transparent\");\n            let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {\n                    attachment: &self.first_color_att_view,\n                    resolve_target: None,\n                    load_op: wgpu::LoadOp::Load,\n                    store_op: wgpu::StoreOp::Store,\n                    clear_color: wgpu::Color {\n                        r: 0.1,\n                        g: 0.2,\n                        b: 0.3,\n                        a: 1.0,\n                    },\n                }],\n                depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {\n                    attachment: &self.forward_depth,\n                    depth_load_op: wgpu::LoadOp::Load,\n                    depth_store_op: wgpu::StoreOp::Store,\n                    stencil_load_op: wgpu::LoadOp::Clear,\n                    stencil_store_op: wgpu::StoreOp::Store,\n                    clear_depth: 1.0,\n                    clear_stencil: 0,\n                }),\n            });\n\n            self.water_gpu.render(&mut rpass, &self.bind_group);\n        }\n\n        // Post pass\n        {\n            log::trace!(\"begin_post_render_pass\");\n            let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {\n                    attachment: &self.first_color_att_view,\n                    resolve_target: None,\n                    load_op: wgpu::LoadOp::Load,\n                    store_op: wgpu::StoreOp::Store,\n                    clear_color: wgpu::Color {\n                        r: 0.1,\n                        g: 0.2,\n                        b: 0.3,\n                        a: 1.0,\n                    },\n                }],\n\n                depth_stencil_attachment: None,\n            });\n\n            self.explosion_gpu.render(&mut rpass, &self.bind_group);\n            self.postfx\n                .render(&mut rpass, &self.gpu.device, &self.bind_group);\n        }\n\n        //Post fxaa pass\n        {\n            log::trace!(\"begin_post_render_pass\");\n            let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {\n                    attachment: &self.secon_color_att_view,\n                    resolve_target: None,\n                    load_op: wgpu::LoadOp::Clear,\n                    store_op: wgpu::StoreOp::Store,\n                    clear_color: wgpu::Color {\n                        r: 0.1,\n                        g: 0.2,\n                        b: 0.3,\n                        a: 1.0,\n                    },\n                }],\n\n                depth_stencil_attachment: None,\n            });\n\n            self.postfxaa\n                .render(&mut rpass, &self.gpu.device, &self.bind_group);\n        }\n\n        //Custom Ui pass\n        {\n            log::trace!(\"begin curtom ui renderpass\");\n            let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {\n                    attachment: &self.secon_color_att_view,\n                    resolve_target: None,\n                    load_op: wgpu::LoadOp::Load,\n                    store_op: wgpu::StoreOp::Store,\n                    clear_color: wgpu::Color {\n                        r: 1.0,\n                        g: 0.2,\n                        b: 0.3,\n                        a: 1.0,\n                    },\n                }],\n\n                depth_stencil_attachment: None,\n            });\n\n            self.health_bar.render(&mut rpass, &self.bind_group);\n            self.unit_icon.render(&mut rpass, &self.bind_group);\n            self.line_gpu.render(&mut rpass, &self.bind_group);\n            self.cursor_icon.render(&mut rpass, &self.bind_group);\n        }\n\n        //Copy on frame view\n        {\n            log::trace!(\"copy on frame view render pass\");\n            let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor {\n                color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {\n                    attachment: &frame.view,\n                    resolve_target: None,\n                    load_op: wgpu::LoadOp::Load,\n                    store_op: wgpu::StoreOp::Store,\n                    clear_color: wgpu::Color {\n                        r: 1.0,\n                        g: 0.2,\n                        b: 0.3,\n                        a: 1.0,\n                    },\n                }],\n\n                depth_stencil_attachment: None,\n            });\n\n            self.post_bicopy\n                .render(&mut rpass, &self.gpu.device, &self.bind_group);\n        }\n\n        let render_pass_3d = now.elapsed();\n\n        self.profiler.mix(\"render_pass_3d\", render_pass_3d, 20);\n\n        self.imgui_wrap\n            .renderer\n            .render(ui, &self.gpu.device, &mut encoder_render, &frame.view)\n            .expect(\"Rendering failed\");\n\n        let cursor_sample_position = self\n            .gpu\n            .device\n            .create_buffer_mapped::<f32>(\n                4,\n                wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ,\n            )\n            .finish();\n\n        let screen_center_sample_position = self\n            .gpu\n            .device\n            .create_buffer_mapped::<f32>(\n                4,\n                wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ,\n            )\n            .finish();\n\n        encoder_render.copy_texture_to_buffer(\n            wgpu::TextureCopyView {\n                texture: &self.position_att,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: self\n                        .input_state\n                        .cursor_pos\n                        .0\n                        .max(0)\n                        .min(self.gpu.sc_desc.width - 1) as f32,\n                    y: self\n                        .input_state\n                        .cursor_pos\n                        .1\n                        .max(0)\n                        .min(self.gpu.sc_desc.height - 1) as f32,\n                    z: 0.0,\n                },\n            },\n            wgpu::BufferCopyView {\n                buffer: &cursor_sample_position,\n                offset: 0,\n                row_pitch: 4 * 4,\n                image_height: 1,\n            },\n            Extent3d {\n                width: 1,\n                height: 1,\n                depth: 1,\n            },\n        );\n\n        encoder_render.copy_texture_to_buffer(\n            wgpu::TextureCopyView {\n                texture: &self.position_att,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: self.gpu.sc_desc.width as f32 / 2.0,\n                    y: self.gpu.sc_desc.height as f32 / 2.0,\n                    z: 0.0,\n                },\n            },\n            wgpu::BufferCopyView {\n                buffer: &screen_center_sample_position,\n                offset: 0,\n                row_pitch: 4 * 4,\n                image_height: 1,\n            },\n            Extent3d {\n                width: 1,\n                height: 1,\n                depth: 1,\n            },\n        );\n\n        let start = Instant::now();\n        self.gpu.queue.submit(&[encoder_render.finish()]);\n        self.profiler\n            .mix(\"device queue submit\", start.elapsed(), 20);\n\n        //Handle right click\n        if let (true, Some(id), Some(mouse_world_pos)) = (\n            self.input_state\n                .mouse_trigger\n                .contains(&winit::event::MouseButton::Right),\n            self.game_state.my_player_id,\n            self.game_state.mouse_world_pos,\n        ) {\n            let orders = match self.game_state.uitool {\n                UiTool::Move | UiTool::None => vec![FrameEventFromPlayer::MoveOrder {\n                    id,\n                    selected: self.game_state.selected.clone(),\n                    mouse_world_pos,\n                }],\n\n                UiTool::Spawn(id_to_con) => {\n                    self.game_state.uitool = UiTool::None;\n\n                    vec![FrameEventFromPlayer::ConOrder {\n                        id,\n                        selected: self.game_state.selected.clone(),\n                        mouse_world_pos,\n                        botdef_id: id_to_con,\n                    }]\n                }\n\n                UiTool::Repair => {\n                    self.game_state.uitool = UiTool::None;\n\n                    if let Some(under) = self.game_state.under_mouse {\n                        vec![FrameEventFromPlayer::RepairOrder {\n                            id,\n                            selected: self.game_state.selected.clone(),\n                            to_repair: under,\n                        }]\n                    } else {\n                        vec![]\n                    }\n                }\n                _ => vec![],\n            };\n\n            for order in orders {\n                let order_type = &format!(\"{:?}\", order)[..8];\n                log::info!(\"order {} from {}\", order_type, id);\n\n                let _ = self\n                    .sender_from_client_to_manager\n                    .try_send(client::FromClient::PlayerInput(order));\n            }\n        }\n\n        self.input_state.update();\n\n        let tx = self.sender_to_client.clone();\n        cursor_sample_position.map_read_async(0, 4 * 4, move |e: BufferMapAsyncResult<&[f32]>| {\n            match e {\n                Ok(e) => {\n                    log::trace!(\"BufferMapAsyncResult callback\");\n                    let _ = tx.try_send(ToClient::MapReadAsyncMessage {\n                        vec: e.data.to_vec(),\n                        usage: \"mouse_world_pos\".to_owned(),\n                    });\n                }\n                Err(_) => {}\n            }\n        });\n\n        let tx = self.sender_to_client.clone();\n        screen_center_sample_position.map_read_async(\n            0,\n            4 * 4,\n            move |e: BufferMapAsyncResult<&[f32]>| match e {\n                Ok(e) => {\n                    log::trace!(\"BufferMapAsyncResult callback\");\n                    let _ = tx.try_send(ToClient::MapReadAsyncMessage {\n                        vec: e.data.to_vec(),\n                        usage: \"screen_center_world_pos\".to_owned(),\n                    });\n                }\n                Err(_) => {}\n            },\n        );\n    }\n}\n"
  },
  {
    "path": "src/client/uitool.rs",
    "content": "use crate::botdef;\nuse crate::*;\nuse utils::*;\n#[derive(Clone, Copy, Debug, PartialEq)]\npub enum UiTool {\n    None,\n    Move,\n    Repair,\n    Guard,\n    Attack,\n    Spawn(Id<botdef::BotDef>),\n}\n"
  },
  {
    "path": "src/client/unit_editor.rs",
    "content": "use super::client::*;\nuse crate::botdef::BotDef;\nuse crate::model::*;\nuse crate::utils::FileTree;\nuse crate::*;\nuse gpu_obj::model_gpu::ModelGpu;\nuse na::{Matrix4, Point3, Vector2, Vector3, Vector4};\nuse std::collections::{HashMap, HashSet};\nuse std::path::{Path, PathBuf};\nuse unit::*;\nuse unit_part_gpu::*;\npub struct UnitEditor {\n    pub orbit: Point3<f32>,\n    pub botdef: BotDef,\n    pub asset_dir_cached: FileTree,\n}\nimpl UnitEditor {\n    pub fn new() -> Self {\n        let root = PartTree {\n            id: utils::rand_id(),\n            children: vec![],\n            placed_mesh: None,\n            placed_collider: None,\n            parent_to_self: Matrix4::identity(),\n            joint: Joint::Fix,\n        };\n\n        let botdef = botdef::BotDef {\n            id: utils::rand_id(),\n            file_path: \"src/asset/test.json\".to_owned(),\n            radius: 0.5,\n            max_life: 100,\n            turn_accel: 1.5,\n            max_turn_rate: 1.5,\n            accel: 0.1,\n            break_accel: 0.3,\n            max_speed: 1.0,\n            build_power: 10.0,\n            build_dist: 5.0,\n            metal_cost: 100,\n            part_tree: root,\n        };\n\n        UnitEditor {\n            orbit: Point3::new(300.0, 100.0, 0.5),\n            asset_dir_cached: FileTree::Unknown,\n            botdef,\n        }\n    }\n\n    fn add_to_parts(&mut self, parent: utils::Id<PartTree>, path: PathBuf, mesh_index: usize) {\n        log::debug!(\"adding {:?} to {}\", path, parent);\n\n        match self.botdef.part_tree.find_node_mut(parent) {\n            Some(node) => node.children.push(PartTree {\n                placed_mesh: Some(PlacedMesh {\n                    trans: utils::face_towards_dir(\n                        &Vector3::new(0.0, 0.0, 0.0),\n                        &Vector3::new(1.0, 0.0, 0.0),\n                        &Vector3::new(0.0, 0.0, 1.0),\n                    ),\n                    mesh_path: path,\n                    mesh_index,\n                }),\n                placed_collider: None,\n                parent_to_self: Matrix4::identity(),\n                joint: Joint::Fix,\n                id: utils::rand_id(),\n                children: vec![],\n            }),\n            None => {}\n        }\n    }\n}\n\nimpl App {\n    pub fn init_unit_editor(&mut self) {\n        self.clear_gpu_instance_and_game_state();\n        self.game_state.position = Point3::new(300.0, 97.0, 1.0);\n        self.game_state.position_smooth = Point3::new(300.0, 97.0, 1.0);\n        self.game_state.dir = Vector3::new(0.0, 1.0, -1.0);\n        self.game_state.dir_smooth = Vector3::new(0.0, 1.0, -1.0);\n    }\n\n    pub fn draw_unit_editor_ui(\n        ui: &Ui,\n        unit_editor: &mut UnitEditor,\n        unit_part_gpu: &mut UnitPartGpu,\n    ) {\n        let path = std::path::Path::new(\"./src/asset/\");\n\n        if let FileTree::Unknown = unit_editor.asset_dir_cached {\n            log::debug!(\"Reading all assets to build file directory cache\");\n            unit_editor.asset_dir_cached = FileTree::new(path.to_owned());\n        }\n\n        let window_editor = imgui::Window::new(im_str!(\"Unit Editor\"));\n        window_editor\n            .size([400.0, 700.0], imgui::Condition::FirstUseEver)\n            .position([3.0, 12.0], imgui::Condition::FirstUseEver)\n            .collapsed(false, imgui::Condition::FirstUseEver)\n            .build(&ui, || {\n                let BotDef {\n                    id,\n                    file_path,\n                    radius,\n                    max_life,\n                    turn_accel,\n                    max_turn_rate,\n                    accel,\n                    break_accel,\n                    max_speed,\n                    build_power,\n                    build_dist,\n                    metal_cost,\n                    part_tree,\n                } = &unit_editor.botdef;\n\n                let file_path = file_path.clone();\n\n                let to_rev = 0.5 / std::f32::consts::PI;\n                let to_rad = 1.0 / to_rev;\n                let to_sec = 10.0;\n                let to_frame = 1.0 / to_sec;\n\n                let mut max_turn_rate_human = max_turn_rate * to_sec * to_rev;\n                ui.drag_float(\n                    im_str!(\"maximum turn rate (rev/sec)\"),\n                    &mut max_turn_rate_human,\n                )\n                .speed(0.01)\n                .min(0.01)\n                .max(100.0)\n                .build();\n\n                let mut turn_accel_human = turn_accel * to_sec * to_sec * to_rev;\n                ui.drag_float(\n                    im_str!(\"turn acceleration (rev/sec²)\"),\n                    &mut turn_accel_human,\n                )\n                .speed(0.01)\n                .min(0.01)\n                .max(100.0)\n                .build();\n\n                let mut max_speed_human = max_speed * to_sec;\n                ui.drag_float(im_str!(\"max speed (m/sec)\"), &mut max_speed_human)\n                    .speed(0.01)\n                    .min(0.01)\n                    .max(100.0)\n                    .build();\n\n                let mut accel_human = accel * to_sec * to_sec;\n                ui.drag_float(im_str!(\"acceleration (m/sec²)\"), &mut accel_human)\n                    .speed(0.01)\n                    .min(0.01)\n                    .max(100.0)\n                    .build();\n\n                let mut break_accel_human = break_accel * to_sec * to_sec;\n                ui.drag_float(im_str!(\"break accel (m/sec²)\"), &mut break_accel_human)\n                    .speed(0.01)\n                    .min(0.01)\n                    .max(100.0)\n                    .build();\n\n                let mut life = max_life.clone();\n                ui.drag_int(im_str!(\"health\"), &mut life).build();\n\n                let mut build_power_human = build_power * to_sec;\n                ui.drag_float(im_str!(\"build power (metal/sec)\"), &mut build_power_human)\n                    .speed(0.01)\n                    .min(0.01)\n                    .max(100.0)\n                    .build();\n\n                let mut build_dist_ = build_dist.clone();\n                ui.drag_float(im_str!(\"build distance (m)\"), &mut build_dist_)\n                    .speed(0.01)\n                    .min(0.01)\n                    .max(100.0)\n                    .build();\n\n                unit_editor.botdef.max_turn_rate = max_turn_rate_human * to_frame * to_rad;\n                unit_editor.botdef.turn_accel = turn_accel_human * to_frame * to_frame * to_rad;\n                unit_editor.botdef.max_speed = max_speed_human * to_frame;\n                unit_editor.botdef.accel = accel_human * to_frame * to_frame;\n                unit_editor.botdef.break_accel = break_accel_human * to_frame * to_frame;\n                unit_editor.botdef.max_life = life.max(0);\n                unit_editor.botdef.build_power = build_power_human * to_frame;\n                unit_editor.botdef.build_dist = build_dist_;\n                ui.separator();\n                Self::ui_part_tree(\n                    ui,\n                    &mut unit_editor.botdef.part_tree.clone(),\n                    unit_editor,\n                    true,\n                    unit_part_gpu,\n                );\n\n                if ui.button(im_str!(\"load\"), [0.0, 0.0]) {\n                    Self::load_botdef_in_editor(&file_path, unit_editor, unit_part_gpu);\n                }\n                if ui.button(im_str!(\"save\"), [0.0, 0.0]) {\n                    Self::save_botdef_on_disk(&unit_editor.botdef, &file_path);\n                    log::info!(\"Saving {:?}\", unit_editor.botdef.part_tree);\n                }\n            });\n\n        let window_selector = imgui::Window::new(im_str!(\"Unit Selector\"));\n        window_selector\n            .size([400.0, 200.0], imgui::Condition::FirstUseEver)\n            .position([400.0, 3.0], imgui::Condition::FirstUseEver)\n            .collapsed(false, imgui::Condition::FirstUseEver)\n            .build(&ui, || {\n                Self::visit_dirs_for_selection(\n                    &unit_editor.asset_dir_cached.clone(),\n                    ui,\n                    unit_editor,\n                    unit_part_gpu,\n                );\n            });\n    }\n\n    pub fn save_botdef_on_disk(bot_def: &BotDef, path: &str) {\n        use std::fs::OpenOptions;\n        use std::io::prelude::*;\n        use std::io::{BufReader, BufWriter};\n        let file = OpenOptions::new()\n            .read(true)\n            .write(true)\n            .create(true)\n            .truncate(true)\n            .open(path)\n            .unwrap();\n        let mut buf_w = BufWriter::new(file);\n        serde_json::to_writer_pretty(buf_w, bot_def);\n        // bincode::serialize_into(buf_w, bot_def);\n    }\n\n    pub fn load_botdef_on_disk(path: &str) -> serde_json::Result<BotDef> {\n        use std::fs::OpenOptions;\n        use std::io::prelude::*;\n        use std::io::{BufReader, BufWriter};\n        let file = OpenOptions::new()\n            .read(true)\n            .write(true)\n            .create(true)\n            .open(path)\n            .unwrap();\n        let mut buf_r = BufReader::new(file);\n        serde_json::from_reader(buf_r)\n    }\n\n    pub fn load_botdef_in_editor(\n        path: &str,\n        unit_editor: &mut UnitEditor,\n        unit_part_gpu: &mut unit_part_gpu::UnitPartGpu,\n    ) {\n        if let Ok(botdef) = Self::load_botdef_on_disk(path) {\n            log::info!(\"Loaded {:?}\", botdef.id);\n            unit_editor.botdef = botdef;\n\n            //Might need to clone\n            for node in unit_editor.botdef.clone().part_tree.iter() {\n                if let Some(mesh) = &node.placed_mesh {\n                    let index = unit_part_gpu.index_of_or_create_if_na(mesh.mesh_path.clone());\n\n                    //Update the mesh index, because it depend on the load order in the unit_part_gpu\n                    for node_mut in unit_editor.botdef.part_tree.find_node_mut(node.id) {\n                        for pm in &mut node_mut.placed_mesh {\n                            pm.mesh_index = index;\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    fn ui_part_tree(\n        ui: &Ui,\n        part_tree: &PartTree,\n        unit_editor: &mut UnitEditor,\n        is_root: bool,\n        unit_part_gpu: &mut UnitPartGpu,\n    ) {\n        {\n            if !is_root {\n                if ui.button(im_str!(\"remove##{:?}\", part_tree.id).as_ref(), [0.0, 0.0]) {\n                    let deleter = unit_editor.botdef.part_tree.remove_node(part_tree.id);\n                }\n            }\n\n            let add_str = im_str!(\"Add child##{:?}\", part_tree.id);\n            if ui.button(add_str.as_ref(), [0.0, 0.0]) {\n                ui.open_popup(add_str.as_ref());\n            }\n            ui.popup_modal(add_str.as_ref())\n                .always_auto_resize(true)\n                .build(|| {\n                    Self::visit_dirs_for_add_child(\n                        &unit_editor.asset_dir_cached.clone(),\n                        ui,\n                        unit_editor,\n                        unit_part_gpu,\n                        part_tree.id,\n                    );\n\n                    if ui.button(im_str!(\"Close\"), [0.0, 0.0]) {\n                        ui.close_current_popup();\n                    }\n                });\n            ui.tree_node(im_str!(\"children\").as_ref())\n                .default_open(true)\n                .build(|| {\n                    for c in part_tree.children.iter() {\n                        {\n                            ui.tree_node(im_str!(\"child##{:?}\", c.id).as_ref())\n                                .default_open(true)\n                                .build(|| {\n                                    let name = im_str!(\"child\");\n                                    ChildWindow::new(name)\n                                        .border(true)\n                                        .always_auto_resize(true)\n                                        .build(ui, || {\n                                            let ui_for_transform =\n                                            |id: String, matrix: Matrix4<f32>| -> Matrix4<f32> {\n                                                let pos = matrix * Vector4::new(0.0, 0.0, 0.0, 1.0);\n                                                let pos = pos.xyz() / pos.w;\n                                                let arr_pos: &mut [f32; 3] =\n                                                    &mut [pos.x, pos.y, pos.z];\n                                                ui.drag_float3(\n                                                    im_str!(\"position##{:?}\", id).as_ref(),\n                                                    arr_pos,\n                                                )\n                                                .speed(0.001)\n                                                .min(-3.0)\n                                                .max(3.0)\n                                                .build();\n                                                let isometry: Isometry3<f32> = unsafe {\n                                                    na::convert_unchecked::<\n                                                        Matrix4<f32>,\n                                                        Isometry3<f32>,\n                                                    >(\n                                                        matrix\n                                                    )\n                                                };\n                                                let euler = isometry.rotation.euler_angles();\n                                                let arr_angle: &mut [f32; 3] =\n                                                    &mut [euler.0, euler.1, euler.2];\n                                                ui.drag_float3(\n                                                    im_str!(\"euler angles##{:?}\", id).as_ref(),\n                                                    arr_angle,\n                                                )\n                                                .speed(0.001)\n                                                .min(-6.0)\n                                                .max(6.0)\n                                                .build();\n                                                let rotation_mat = Matrix4::from_euler_angles(\n                                                    arr_angle[0],\n                                                    arr_angle[1],\n                                                    arr_angle[2],\n                                                );\n                                                let final_mat = utils::face_towards_dir(\n                                                    &Vector3::new(\n                                                        arr_pos[0], arr_pos[1], arr_pos[2],\n                                                    ),\n                                                    &Vector3::new(1.0, 0.0, 0.0),\n                                                    &Vector3::new(0.0, 0.0, 1.0),\n                                                ) * rotation_mat;\n\n                                                final_mat\n                                            };\n\n                                            ui.text(im_str!(\"node transform:\"));\n                                            let new_parent_to_self = ui_for_transform(\n                                                format!(\"{:?}\", c.id),\n                                                c.parent_to_self,\n                                            );\n\n                                            //Joint\n                                            ui.text(im_str!(\"joint {:?}\", c.joint));\n                                            ui.same_line(0.0);\n                                            if ui.small_button(im_str!(\"swap##{:?}\", c.id).as_ref())\n                                            {\n                                                unit_editor\n                                                    .botdef\n                                                    .part_tree\n                                                    .find_node_mut(c.id)\n                                                    .unwrap()\n                                                    .joint\n                                                    .replace_with_next();\n                                            }\n\n                                            if let Some(model) = &c.placed_mesh {\n                                                ui.text(im_str!(\"mesh: {:?}\", model.mesh_path));\n                                            }\n                                            let replace_str = im_str!(\"replace mesh##{:?}\", c.id);\n                                            if ui.button(replace_str.as_ref(), [0.0, 0.0]) {\n                                                ui.open_popup(replace_str.as_ref());\n                                            }\n                                            ui.popup_modal(replace_str.as_ref())\n                                                .always_auto_resize(true)\n                                                .build(|| {\n                                                    Self::visit_dirs_for_replace_mesh(\n                                                        &unit_editor.asset_dir_cached.clone(),\n                                                        ui,\n                                                        unit_editor,\n                                                        unit_part_gpu,\n                                                        c.id,\n                                                    );\n                                                    if ui.button(im_str!(\"Close\"), [0.0, 0.0]) {\n                                                        ui.close_current_popup();\n                                                    }\n                                                });\n\n                                            ui.text(im_str!(\"mesh transform:\"));\n                                            let new_placed_mesh =\n                                                if let Some(Some(old_placed_mesh)) = unit_editor\n                                                    .botdef\n                                                    .part_tree\n                                                    .find_node(c.id)\n                                                    .map(|e| &e.placed_mesh)\n                                                {\n                                                    let new_placed_mesh = PlacedMesh {\n                                                        trans: ui_for_transform(\n                                                            format!(\"placed_mesh{:?}\", c.id),\n                                                            old_placed_mesh.trans,\n                                                        ),\n                                                        mesh_path: old_placed_mesh\n                                                            .mesh_path\n                                                            .clone(),\n                                                        mesh_index: old_placed_mesh.mesh_index,\n                                                    };\n                                                    Some(new_placed_mesh)\n                                                } else {\n                                                    None\n                                                };\n\n                                            if let Some(node) =\n                                                unit_editor.botdef.part_tree.find_node_mut(c.id)\n                                            {\n                                                node.parent_to_self = new_parent_to_self;\n                                                node.placed_mesh = new_placed_mesh;\n                                            };\n\n                                            Self::ui_part_tree(\n                                                ui,\n                                                &c,\n                                                unit_editor,\n                                                false,\n                                                unit_part_gpu,\n                                            );\n                                        })\n                                });\n                        };\n                    }\n                });\n        };\n    }\n\n    fn visit_dirs_for_add_child(\n        dir: &FileTree,\n        ui: &Ui,\n        unit_editor: &mut UnitEditor,\n        unit_part_gpu: &mut UnitPartGpu,\n        parent: utils::Id<PartTree>,\n    ) {\n        match dir {\n            FileTree::Unknown => {\n                ui.text(im_str!(\"Error reading asset file\"));\n            }\n            FileTree::Leaf { path } => {\n                let file_name = path.file_name().unwrap();\n                let extension = path.extension().unwrap();\n                if extension == \"obj\" {\n                    ui.text(im_str!(\"{:?}\", file_name));\n                    ui.same_line(0.0);\n\n                    let (index, state) = unit_part_gpu.path_get_or_create_if_na(path.to_owned());\n                    match state {\n                        ModelGpuState::Ready(_) | ModelGpuState::ToLoad(_) => {\n                            if ui.small_button(im_str!(\"add to parts##{:?}\", path).as_ref()) {\n                                log::debug!(\"add to parts {:?}\", path);\n                                unit_editor.add_to_parts(parent, path.clone(), index);\n                            }\n                            ui.same_line(0.0);\n                            if ui.small_button(im_str!(\"reload##{:?}\", path).as_ref()) {\n                                unit_part_gpu.reload(path.clone());\n                            }\n                        }\n                        ModelGpuState::Error(e) => {\n                            ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!(\"Error\"));\n                            ui.same_line(0.0);\n                            if ui.small_button(im_str!(\"reload##{:?}\", path).as_ref()) {\n                                unit_part_gpu.reload(path.clone());\n                            }\n                        }\n                    }\n                }\n            }\n            FileTree::Node { path, children } => {\n                ui.tree_node(\n                    im_str!(\"{:?}\", path.components().last().unwrap().as_os_str()).as_ref(),\n                )\n                .build(|| {\n                    for child in children {\n                        Self::visit_dirs_for_add_child(\n                            &child,\n                            ui,\n                            unit_editor,\n                            unit_part_gpu,\n                            parent,\n                        );\n                    }\n                });\n            }\n        }\n    }\n\n    fn visit_dirs_for_replace_mesh(\n        dir: &FileTree,\n        ui: &Ui,\n        unit_editor: &mut UnitEditor,\n        unit_part_gpu: &mut UnitPartGpu,\n        id_to_mesh_replace: utils::Id<PartTree>,\n    ) {\n        match dir {\n            FileTree::Unknown => {\n                ui.text(im_str!(\"Error reading asset file\"));\n            }\n            FileTree::Leaf { path } => {\n                let file_name = path.file_name().unwrap();\n                let extension = path.extension().unwrap();\n                if extension == \"obj\" {\n                    ui.text(im_str!(\"{:?}\", file_name));\n                    ui.same_line(0.0);\n\n                    let mut replace_exe = |mesh_index| {\n                        if let Some(child) = unit_editor\n                            .botdef\n                            .part_tree\n                            .find_node_mut(id_to_mesh_replace)\n                        {\n                            if let Some(old) = &child.placed_mesh {\n                                child.placed_mesh = Some(PlacedMesh {\n                                    mesh_index,\n                                    mesh_path: path.clone(),\n                                    trans: old.trans.clone(),\n                                });\n                            } else {\n                                child.placed_mesh = Some(PlacedMesh {\n                                    mesh_index,\n                                    mesh_path: path.clone(),\n                                    trans: Matrix4::identity(),\n                                });\n                            }\n                        }\n                    };\n\n                    let state = unit_part_gpu.path_get(path.to_owned());\n                    match state {\n                        None => {\n                            if ui.small_button(im_str!(\"replace with this##{:?}\", path).as_ref()) {\n                                log::debug!(\"replace with this {:?}\", path);\n                                log::debug!(\"was not open {:?}\", path);\n\n                                let index = unit_part_gpu.index_of_or_create_if_na(path.to_owned());\n                                replace_exe(index);\n                            }\n                        }\n                        Some(ModelGpuState::Ready(_)) | Some(ModelGpuState::ToLoad(_)) => {\n                            if ui.small_button(im_str!(\"replace with this##{:?}\", path).as_ref()) {\n                                log::debug!(\"replace with this {:?}\", path);\n                                let index = unit_part_gpu.index_of_or_create_if_na(path.to_owned());\n                                replace_exe(index);\n                            }\n                            ui.same_line(0.0);\n                            if ui.small_button(im_str!(\"reload##{:?}\", path).as_ref()) {\n                                unit_part_gpu.reload(path.clone());\n                            }\n                        }\n                        Some(ModelGpuState::Error(e)) => {\n                            ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!(\"Error\"));\n                            ui.same_line(0.0);\n                            if ui.small_button(im_str!(\"reload##{:?}\", path).as_ref()) {\n                                unit_part_gpu.reload(path.clone());\n                            }\n                        }\n                    }\n                }\n            }\n            FileTree::Node { path, children } => {\n                ui.tree_node(\n                    im_str!(\"{:?}\", path.components().last().unwrap().as_os_str()).as_ref(),\n                )\n                .build(|| {\n                    for child in children {\n                        Self::visit_dirs_for_replace_mesh(\n                            &child,\n                            ui,\n                            unit_editor,\n                            unit_part_gpu,\n                            id_to_mesh_replace,\n                        );\n                    }\n                });\n            }\n        }\n    }\n\n    fn visit_dirs_for_selection(\n        dir: &FileTree,\n        ui: &Ui,\n        unit_editor: &mut UnitEditor,\n        unit_part_gpu: &mut UnitPartGpu,\n    ) {\n        match dir {\n            FileTree::Unknown => {\n                ui.text(im_str!(\"Error reading asset file\"));\n            }\n            FileTree::Leaf { path } => {\n                let file_name = path.file_name().unwrap();\n                let extension = path.extension().unwrap();\n                if extension == \"json\" {\n                    ui.text(im_str!(\"{:?}\", file_name));\n                    ui.same_line(0.0);\n\n                    if ui.small_button(im_str!(\"load##{:?}\", path).as_ref()) {\n                        log::debug!(\"load botdef {:?}\", path);\n                        Self::load_botdef_in_editor(\n                            path.to_str().unwrap(),\n                            unit_editor,\n                            unit_part_gpu,\n                        );\n                    }\n                }\n            }\n            FileTree::Node { path, children } => {\n                ui.tree_node(\n                    im_str!(\"{:?}\", path.components().last().unwrap().as_os_str()).as_ref(),\n                )\n                .build(|| {\n                    for child in children {\n                        Self::visit_dirs_for_selection(&child, ui, unit_editor, unit_part_gpu);\n                    }\n                });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/client/unit_part_gpu.rs",
    "content": "use crate::gpu_obj;\nuse crate::model;\nuse gpu_obj::model_gpu::ModelGpu;\nuse std::collections::{HashMap, HashSet};\nuse std::path::{Path, PathBuf};\npub enum ModelGpuState {\n    ToLoad(model::TriangleList),\n    Ready(ModelGpu),\n    Error(String),\n}\n\npub struct UnitPartGpu {\n    pub states: Vec<ModelGpuState>,\n    path_to_index: HashMap<PathBuf, usize>,\n}\n\nimpl UnitPartGpu {\n    pub fn new() -> Self {\n        UnitPartGpu {\n            states: Vec::new(),\n            path_to_index: HashMap::new(),\n        }\n    }\n\n    pub fn index_of(&self, path: PathBuf) -> Option<&usize> {\n        self.path_to_index.get(&path)\n    }\n\n    pub fn path_get(&self, path: PathBuf) -> Option<&ModelGpuState> {\n        self.index_of(path).map(|index| &self.states[*index])\n    }\n\n    pub fn path_get_mut(&mut self, path: PathBuf) -> Option<&mut ModelGpuState> {\n        self.index_of(path)\n            .cloned()\n            .map(move |index| &mut self.states[index])\n    }\n\n    pub fn get(&self, index: usize) -> &ModelGpuState {\n        &self.states[index]\n    }\n    pub fn get_mut(&mut self, index: usize) -> &mut ModelGpuState {\n        &mut self.states[index]\n    }\n\n    fn load_at(&mut self, index: usize, path: PathBuf) {\n        self.path_to_index.insert(path.clone(), index);\n        let to_push = match crate::model::open_obj(path.to_str().unwrap()) {\n            Ok(triangle_list) => ModelGpuState::ToLoad(triangle_list.clone()),\n            Err(e) => ModelGpuState::Error(e),\n        };\n        self.states.push(to_push);\n    }\n    ///Push a new entry, regardless of if the same path is already present.\n    ///Returns the entry index\n    pub fn append(&mut self, path: PathBuf) -> usize {\n        let index = self.states.len();\n        self.load_at(index, path);\n        index\n    }\n\n    pub fn reload(&mut self, path: PathBuf) -> usize {\n        match self.index_of(path.clone()).cloned() {\n            Some(index) => {\n                self.load_at(index, path);\n                index\n            }\n            None => self.append(path),\n        }\n    }\n\n    pub fn index_of_or_create_if_na(&mut self, path: PathBuf) -> usize {\n        match self.index_of(path.clone()) {\n            Some(index) => *index,\n            None => self.append(path),\n        }\n    }\n\n    pub fn path_get_or_create_if_na(&mut self, path: PathBuf) -> (usize, &ModelGpuState) {\n        let index = self.index_of_or_create_if_na(path);\n        (index, self.get(index))\n    }\n}\n"
  },
  {
    "path": "src/frame.rs",
    "content": "extern crate nalgebra as na;\n\nuse crate::heightmap_phy;\n\nuse crate::botdef;\nuse crate::mobile;\nuse crate::moddef;\nuse crate::utils;\nuse fnv::{FnvHashMap, FnvHashSet};\nuse na::{Point3, Vector3};\nuse std::collections::HashMap;\nuse std::time::Duration;\n\nuse crate::unit;\nuse mobile::*;\nuse serde::{Deserialize, Serialize};\nuse utils::*;\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub struct Player {\n    pub id: Id<Player>,\n    pub kbots: FnvHashSet<Id<KBot>>,\n    pub team: u8,\n    pub metal: f64,\n    pub energy: f64,\n}\n\nimpl Player {\n    pub fn new() -> Self {\n        let id = utils::rand_id();\n        Player {\n            id,\n            kbots: FnvHashSet::default(),\n            team: 0,\n            metal: 500.0,\n            energy: 500.0,\n        }\n    }\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub enum FrameEventFromPlayer {\n    RepairOrder {\n        id: Id<Player>,\n        selected: FnvHashSet<Id<KBot>>,\n        to_repair: Id<KBot>,\n    },\n    ConOrder {\n        id: Id<Player>,\n        selected: FnvHashSet<Id<KBot>>,\n        mouse_world_pos: Vector3<f32>,\n        botdef_id: Id<botdef::BotDef>,\n    },\n    MoveOrder {\n        id: Id<Player>,\n        selected: FnvHashSet<Id<KBot>>,\n        mouse_world_pos: Vector3<f32>,\n    },\n    ReplaceFrame(Frame),\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct ProfilerMap {\n    pub hm: HashMap<String, std::time::Duration>,\n}\nimpl ProfilerMap {\n    pub fn new() -> Self {\n        ProfilerMap { hm: HashMap::new() }\n    }\n    pub fn mix(&mut self, s: &str, duration: Duration, last_ratio: u32) {\n        match self.hm.get_mut(&s.to_owned()) {\n            Some(val) => {\n                *val = val\n                    .checked_mul(last_ratio)\n                    .unwrap()\n                    .checked_add(duration)\n                    .unwrap()\n                    .checked_div(last_ratio + 1)\n                    .unwrap();\n            }\n            None => {\n                self.hm.insert(s.to_owned(), duration);\n            }\n        }\n    }\n\n    pub fn add(&mut self, s: &str, duration: Duration) {\n        self.hm.insert(s.to_owned(), duration);\n    }\n    pub fn get(&self, s: &str) -> Option<&Duration> {\n        self.hm.get(s)\n    }\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct DataToComputeNextFrame {\n    pub old_frame: Frame,\n    pub events: Vec<FrameEventFromPlayer>,\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct FrameUpdate {\n    pub kbots: Vec<KBot>,\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct Frame {\n    // relevant to send to client on change\n    pub kinematic_projectiles: FnvHashMap<Id<KinematicProjectile>, KinematicProjectile>,\n    pub arrows: Vec<Arrow>,\n    pub heightmap_phy: Option<heightmap_phy::HeightmapPhy>,\n    pub players: FnvHashMap<Id<Player>, Player>,\n    pub kbots: FnvHashMap<Id<KBot>, KBot>,\n    pub moddef: moddef::ModDef,\n    // relevant to send to client once\n    pub bot_defs: FnvHashMap<Id<botdef::BotDef>, botdef::BotDef>,\n    // relevant to send to client always\n    pub number: i32,\n    pub explosions: Vec<ExplosionEvent>,\n    pub kbots_dead: FnvHashSet<Id<KBot>>,\n    pub kinematic_projectiles_dead: Vec<Id<KinematicProjectile>>,\n    pub kinematic_projectiles_birth: Vec<KinematicProjectile>,\n    pub frame_profiler: ProfilerMap,\n}\n\nimpl Frame {\n    pub fn new() -> Self {\n        Frame {\n            number: 0,\n            players: FnvHashMap::default(),\n            moddef: moddef::ModDef::new(),\n            kbots: FnvHashMap::default(),\n            kinematic_projectiles: FnvHashMap::default(),\n            arrows: Vec::new(),\n            explosions: Vec::new(),\n            heightmap_phy: None,\n            frame_profiler: ProfilerMap::new(),\n            kbots_dead: FnvHashSet::default(),\n            kinematic_projectiles_dead: Vec::new(),\n            kinematic_projectiles_birth: Vec::new(),\n            bot_defs: FnvHashMap::default(),\n        }\n    }\n}\n"
  },
  {
    "path": "src/frame_server/mod.rs",
    "content": "use crate::frame::*;\n\nuse crate::botdef;\nuse crate::heightmap_phy;\nuse crate::mobile::*;\nuse crate::utils::*;\nuse crossbeam_channel::{Receiver, Sender};\nuse fnv::{FnvHashMap, FnvHashSet};\nuse na::{Matrix4, Point3, Vector2, Vector3};\nuse std::time::Instant;\n\npub enum ToFrameServer {\n    DataToComputeNextFrame(DataToComputeNextFrame),\n}\n\npub enum FromFrameServer {\n    NewFrame(Frame),\n}\n\npub struct FrameServerCache {\n    pub grid: Vec<Vec<Id<KBot>>>,\n    pub small_grid: Vec<Vec<Id<KBot>>>,\n    pub heightmap_phy: Option<heightmap_phy::HeightmapPhy>,\n}\n\nimpl FrameServerCache {\n    pub fn spawn(\n        r_to_frame_server: Receiver<ToFrameServer>,\n        s_from_frame_server: Sender<FromFrameServer>,\n    ) -> () {\n        let _ = std::thread::Builder::new()\n            .name(\"frame_server\".to_string())\n            .spawn(move || {\n                let mut fsc = FrameServerCache::new();\n                for msg in r_to_frame_server.iter() {\n                    match msg {\n                        ToFrameServer::DataToComputeNextFrame(DataToComputeNextFrame {\n                            old_frame,\n                            events,\n                        }) => {\n                            let next_frame = fsc.next_frame(old_frame, events);\n                            // let dur = utils::time(|| {\n                            //     use flate2::write::ZlibEncoder;\n                            //     use flate2::Compression;\n                            //     use std::io::prelude::*;\n                            //     let mut e = ZlibEncoder::new(Vec::new(), Compression::new(1));\n                            //     e.write_all(&vec);\n                            //     let compressed_bytes = e.finish().unwrap();\n                            //     log::info!(\"Compressed is {} bytes\", compressed_bytes.len());\n                            // });\n                            // log::info!(\"compression took {:?}\", dur);\n                            let _ = s_from_frame_server.send(FromFrameServer::NewFrame(next_frame));\n                        }\n                    }\n                }\n            });\n    }\n\n    pub fn new() -> Self {\n        FrameServerCache {\n            grid: Vec::new(),\n            small_grid: Vec::new(),\n            heightmap_phy: None,\n        }\n    }\n\n    pub fn next_frame(&mut self, old_frame: Frame, events: Vec<FrameEventFromPlayer>) -> Frame {\n        let mut frame_profiler = ProfilerMap::new();\n        let start = std::time::Instant::now();\n        log::trace!(\"Received frame {} to compute next frame\", old_frame.number);\n\n        log::trace!(\"Event {}\", events.len());\n\n        let mut replacer = None;\n        for event in events.iter() {\n            match event {\n                FrameEventFromPlayer::ReplaceFrame(frame) => {\n                    self.heightmap_phy = frame.heightmap_phy.clone();\n                    replacer = Some(frame.clone());\n                    log::trace!(\"Replacing frame\");\n                }\n                _ => {}\n            }\n        }\n\n        let mut frame = replacer.unwrap_or(old_frame);\n        frame.number += 1;\n        frame.kbots_dead.clear();\n        frame.heightmap_phy = None;\n        frame.explosions.clear();\n        frame.kinematic_projectiles_birth.clear();\n        frame.kinematic_projectiles_dead.clear();\n\n        //TODO order event by player then by type before doing any effect. This step should be deterministic\n        for event in events {\n            match event {\n                FrameEventFromPlayer::MoveOrder {\n                    id,\n                    selected,\n                    mouse_world_pos,\n                } => {\n                    //TODO Validate selected are owned by id\n                    update_mobile_target(mouse_world_pos, &selected, &mut frame.kbots);\n                }\n                FrameEventFromPlayer::ConOrder {\n                    id,\n                    selected,\n                    mouse_world_pos,\n                    botdef_id,\n                } => {\n                    //TODO Validate selected are owned by id && botdef_id is constructable by at least 1 selected\n\n                    let botdef = frame.bot_defs.get(&botdef_id).unwrap();\n                    let mut m = KBot::new(Point3::from(mouse_world_pos), botdef, id);\n                    m.team = frame.players.get(&id).unwrap().team;\n                    m.con_completed = std::f32::MIN_POSITIVE;\n                    m.life = 1;\n\n                    for selected_raw_id in &selected {\n                        for kbot in frame.kbots.get_mut(selected_raw_id) {\n                            kbot.current_command = Command::Build(m.id.clone())\n                        }\n                    }\n\n                    let player = frame.players.get_mut(&id).unwrap();\n                    player.kbots.insert(m.id);\n                    frame.kbots.insert(m.id, m);\n                }\n\n                FrameEventFromPlayer::RepairOrder {\n                    id,\n                    selected,\n                    to_repair,\n                } => {\n                    for selected_raw_id in &selected {\n                        for kbot in frame.kbots.get_mut(selected_raw_id) {\n                            kbot.current_command = Command::Repair(to_repair)\n                        }\n                    }\n                }\n                _ => {}\n            }\n        }\n\n        frame_profiler.add(\"1 handle_events\", start.elapsed());\n\n        let mut arrows = Vec::new();\n\n        let start_update_units = Instant::now();\n\n        if let Some(heightmap) = &self.heightmap_phy {\n            update_units(\n                &mut frame_profiler,\n                &mut frame.kbots,\n                &mut frame.kbots_dead,\n                &mut frame.kinematic_projectiles_dead,\n                &mut frame.kinematic_projectiles_birth,\n                &mut frame.kinematic_projectiles,\n                heightmap,\n                &mut arrows,\n                frame.number,\n                &mut frame.players,\n                &mut self.grid,\n                &mut self.small_grid,\n                &mut frame.explosions,\n                &frame.bot_defs,\n            );\n        }\n        frame_profiler.add(\"0 update_units\", start_update_units.elapsed());\n        frame_profiler.add(\"total\", start.elapsed());\n        Frame {\n            number: frame.number,\n            frame_profiler,\n            arrows,\n            ..frame\n        }\n    }\n}\n\npub fn update_mobile_target(\n    mouse_world_pos: Vector3<f32>,\n    selected: &FnvHashSet<Id<KBot>>,\n    kbots: &mut FnvHashMap<Id<KBot>, KBot>,\n) {\n    let selected_count = selected.len();\n    let formation_w = (selected_count as f32).sqrt().ceil() as i32;\n\n    let mut spot = Vec::<Vector3<f32>>::new();\n    for i in 0..formation_w {\n        for j in 0..formation_w {\n            spot.push(\n                mouse_world_pos\n                    + Vector3::new(\n                        i as f32 + 0.5 - formation_w as f32 / 2.0,\n                        j as f32 + 0.5 - formation_w as f32 / 2.0,\n                        0.0,\n                    ) * 4.0,\n            )\n        }\n    }\n\n    let mut center = Vector3::new(0.0, 0.0, 0.0);\n    let mut tap = 0.0;\n\n    let mut id_to_pos = Vec::new();\n    for &s in selected.iter() {\n        if let Some(mobile) = kbots.get(&s) {\n            id_to_pos.push((mobile.id, mobile.position.coords));\n            center += mobile.position.coords;\n            tap += 1.0;\n        }\n    }\n    center /= tap;\n\n    let axis = (mouse_world_pos - center).normalize();\n\n    let mut projected_spot: Vec<_> = spot\n        .iter()\n        .enumerate()\n        .map(|(index, v)| (index, v.dot(&axis)))\n        .collect();\n\n    projected_spot.sort_by(|(_, proj), (_, proj2)| {\n        if proj > proj2 {\n            std::cmp::Ordering::Greater\n        } else {\n            std::cmp::Ordering::Less\n        }\n    });\n\n    let mut id_to_proj: Vec<_> = id_to_pos\n        .iter()\n        .map(|(index, v)| (index, v.dot(&axis)))\n        .collect();\n\n    id_to_proj.sort_by(|(_, proj), (_, proj2)| {\n        if proj > proj2 {\n            std::cmp::Ordering::Greater\n        } else {\n            std::cmp::Ordering::Less\n        }\n    });\n\n    for ((id, _), (spot_id, _)) in id_to_proj.iter().zip(&projected_spot[..]) {\n        if let Some(mobile) = kbots.get_mut(id) {\n            log::trace!(\"New order for {}\", mobile.id);\n            mobile.move_target = Some(Point3::<f32>::from(spot[*spot_id]));\n            mobile.current_command = Command::None;\n        }\n    }\n}\n\npub fn update_units(\n    frame_profiler: &mut ProfilerMap,\n    kbots: &mut FnvHashMap<Id<KBot>, KBot>,\n    kbots_dead: &mut FnvHashSet<Id<KBot>>,\n    kinematic_projectiles_dead: &mut Vec<Id<KinematicProjectile>>,\n    kinematic_projectiles_birth: &mut Vec<KinematicProjectile>,\n    kinematic_projectiles: &mut FnvHashMap<Id<KinematicProjectile>, KinematicProjectile>,\n    heightmap_phy: &heightmap_phy::HeightmapPhy,\n    arrows: &mut Vec<Arrow>,\n    frame_count: i32,\n    players: &mut FnvHashMap<Id<Player>, Player>,\n    grid: &mut Vec<Vec<Id<KBot>>>,\n    small_grid: &mut Vec<Vec<Id<KBot>>>,\n    explosions: &mut Vec<ExplosionEvent>,\n    bot_defs: &FnvHashMap<Id<botdef::BotDef>, botdef::BotDef>,\n) {\n    let start = std::time::Instant::now();\n    let cell_size = 4;\n    let grid_w = (heightmap_phy.width / cell_size) as usize;\n    let grid_h = (heightmap_phy.height / cell_size) as usize;\n\n    if grid.len() != grid_w * grid_h {\n        std::mem::replace(grid, vec![Vec::<Id<KBot>>::new(); grid_w * grid_h]);\n    } else {\n        for zone in grid.iter_mut() {\n            zone.clear();\n        }\n    }\n\n    let grid_pos = |mobile: &KBot| -> usize {\n        let (x, y) = (mobile.position.x, mobile.position.y);\n        (x as usize / cell_size as usize) as usize\n            + (y as usize / cell_size as usize) as usize * grid_w\n    };\n\n    for (&id, mobile) in kbots.iter() {\n        let gp = grid_pos(mobile);\n        grid[gp].push(id);\n\n        for cell in &[\n            -1_i32 - grid_w as i32,\n            -(grid_w as i32),\n            1 - grid_w as i32,\n            -1,\n            1,\n            -1 + grid_w as i32,\n            grid_w as i32,\n            1 + grid_w as i32,\n        ] {\n            let cell_index = cell + gp as i32;\n            if cell_index >= 0 && (cell_index as usize) < grid_w * grid_h {\n                grid[cell_index as usize].push(id);\n            }\n        }\n    }\n\n    frame_profiler.add(\"01  grid\", start.elapsed());\n\n    //AABB for kbot and proj\n    {\n        let start = std::time::Instant::now();\n        let cell_size = 4;\n        let grid_w = (heightmap_phy.width / cell_size) as usize;\n        let grid_h = (heightmap_phy.height / cell_size) as usize;\n\n        if small_grid.len() != grid_w * grid_h {\n            std::mem::replace(small_grid, vec![Vec::<Id<KBot>>::new(); grid_w * grid_h]);\n        } else {\n            for zone in small_grid.iter_mut() {\n                zone.clear();\n            }\n        }\n\n        fn index_aabb(\n            position: Vector3<f32>,\n            radius: f32,\n            cell_size: usize,\n            grid_w: usize,\n        ) -> Vec<usize> {\n            let mut indices = Vec::new();\n            let min_x = (position.x - radius * 1.0).floor() as usize;\n            let max_x = (position.x + radius * 1.0).ceil() as usize;\n            let min_y = (position.y - radius * 1.0).floor() as usize;\n            let max_y = (position.y + radius * 1.0).ceil() as usize;\n\n            let min_x = min_x / cell_size;\n            let max_x = (max_x + 1) / cell_size;\n            let min_y = min_y / cell_size;\n            let max_y = (max_y + 1) / cell_size;\n\n            for i in (min_x..=max_x).step_by(cell_size) {\n                for j in (min_y..=max_y).step_by(cell_size) {\n                    // println!(\"INSERTION {} {} {}\", i, j, id);\n                    indices.push(i + j * grid_w);\n                }\n            }\n            indices\n        }\n\n        for (id, kbot) in kbots.iter() {\n            let radius = bot_defs.get(&kbot.botdef_id).unwrap().radius;\n            for index in index_aabb(kbot.position.coords, radius, cell_size, grid_w).iter() {\n                small_grid[*index].push(*id);\n            }\n        }\n\n        frame_profiler.add(\"03  small_grid\", start.elapsed());\n\n        let start = std::time::Instant::now();\n        //Projectile move compute\n        {\n            for proj in kinematic_projectiles.values_mut() {\n                let current_pos = proj.position_at(frame_count - 1);\n                let next_pos = proj.position_at(frame_count);\n\n                {\n                    //Slowly interpolate to not miss collisions\n                    let step_size = proj.radius * 1.0;\n                    let ul = next_pos.coords - current_pos.coords;\n                    let distance_to_travel = ul.magnitude();\n                    let u = ul / distance_to_travel;\n\n                    let mut current_interp = current_pos.coords.clone();\n                    let count = (distance_to_travel / step_size).floor() as usize;\n                    'interp: for n in 0..=count {\n                        current_interp += u * step_size;\n                        if n == count {\n                            current_interp = next_pos.coords;\n                        }\n\n                        //Checking collision with current_interp\n                        let indices = index_aabb(current_interp, proj.radius, cell_size, grid_w);\n\n                        let kbots_in_proximity: FnvHashSet<_> = indices\n                            .iter()\n                            .map(|index| small_grid[*index].clone())\n                            .flatten()\n                            .collect();\n                        // &small_grid_kbot[index];\n\n                        'bot_test: for kbot_id in kbots_in_proximity.iter() {\n                            let kbot = kbots.get_mut(kbot_id).unwrap();\n                            let distance_to_target =\n                                (kbot.position.coords - current_interp).magnitude();\n\n                            // println!(\"Distance {}\", distance_to_target);\n                            let kbot_radius = bot_defs.get(&kbot.botdef_id).unwrap().radius;\n                            if distance_to_target < (kbot_radius + proj.radius) {\n                                //Colission between Kbot and projectile\n                                kbot.life = (kbot.life - 10).max(0);\n                                proj.death_frame = frame_count;\n                                explosions.push(ExplosionEvent {\n                                    position: Point3::from(current_interp),\n                                    size: 0.5,\n                                    life_time: 0.8,\n                                });\n                                break 'interp;\n                            }\n                        }\n                    }\n                }\n\n                if proj.death_frame == frame_count {\n                    kinematic_projectiles_dead.push(proj.id);\n                }\n            }\n\n            for r in kinematic_projectiles_dead.iter() {\n                kinematic_projectiles.remove(&r);\n            }\n        }\n        frame_profiler.add(\"04  proj move\", start.elapsed());\n    }\n\n    //Projectile fire compute\n    {\n        let teams: FnvHashSet<_> = players.values().map(|p| p.team).collect();\n\n        let start = std::time::Instant::now();\n        //TODO cache\n\n        let mut id_to_team: FnvHashMap<Id<KBot>, u8> =\n            FnvHashMap::with_capacity_and_hasher(kbots.len(), Default::default());\n\n        for team in teams.iter() {\n            let team_players: Vec<_> = players.values().filter(|e| &e.team == team).collect();\n            for p in team_players.iter() {\n                for kbot in p.kbots.iter() {\n                    id_to_team.insert(*kbot, *team);\n                }\n            }\n        }\n\n        frame_profiler.add(\"05  id_to_team\", start.elapsed());\n\n        let start = std::time::Instant::now();\n        struct Shot {\n            bot: Id<KBot>,\n            target: Vector3<f32>,\n        };\n\n        let mut shots = Vec::new();\n\n        for (me, me_kbot) in kbots.iter() {\n            if me_kbot.con_completed == 1.0 {\n                let grid_pos = grid_pos(me_kbot);\n\n                let my_team = id_to_team.get(me).unwrap();\n\n                // let ennemies_in_cell = &team_to_ennemy_grid.get(my_team).unwrap()[grid_pos];\n\n                let mut ennemies_in_cell: Vec<Id<KBot>> = grid[grid_pos].clone();\n                let to_remove = ennemies_in_cell.iter().position(|e| e == me).unwrap();\n                ennemies_in_cell.remove(to_remove);\n\n                let can_shoot =// *my_team == 0&&\n                 frame_count - me_kbot.frame_last_shot > me_kbot.reload_frame_count;\n                if can_shoot {\n                    //We choose the first ennemy in the cell, we could sort by distance or something else here\n                    //TODO Configurable strategy\n                    'meloop: for potential_ennemy in ennemies_in_cell {\n                        if id_to_team.get(&potential_ennemy).unwrap() != my_team {\n                            let ennemy_kbot = kbots.get(&potential_ennemy).unwrap();\n                            if (ennemy_kbot.position.coords - me_kbot.position.coords).magnitude()\n                                < 6.0\n                            {\n                                shots.push(Shot {\n                                    bot: *me,\n                                    target: ennemy_kbot.position.coords,\n                                });\n                                break 'meloop;\n                            }\n                        }\n                    }\n                }\n            }\n        }\n\n        for shot in shots.iter() {\n            let kbot = kbots.get_mut(&shot.bot).unwrap();\n            let dir = (shot.target - kbot.position.coords).normalize();\n\n            kbot.weapon0_dir = dir;\n            kbot.frame_last_shot = frame_count;\n            let kbot_radius = bot_defs.get(&kbot.botdef_id).unwrap().radius;\n            let proj = KinematicProjectile {\n                id: rand_id(),\n                birth_frame: frame_count,\n                death_frame: frame_count + 6,\n                position_at_birth: kbot.position + dir * (kbot_radius + 0.25 + 0.01),\n                speed_per_frame_at_birth: dir * 2.0 + Vector3::new(0.0, 0.0, 0.2),\n                accel_per_frame: Vector3::new(0.0, 0.0, -0.08),\n                radius: 0.25,\n                position_cache: Vec::new(),\n                speed_cache: Vec::new(),\n            };\n            kinematic_projectiles_birth.push(proj.clone());\n            kinematic_projectiles.insert(proj.id, proj);\n        }\n        frame_profiler.add(\"07  kbot_fire\", start.elapsed());\n    }\n\n    let start = std::time::Instant::now();\n    let mobiles2 = kbots.clone();\n\n    struct BuildPart {\n        amount: f64,\n        repair: bool,\n        player: Id<Player>,\n        from: Id<KBot>,\n        to: Id<KBot>,\n    }\n    let mut build_throughputs = Vec::new();\n    //Build compute\n    for (id, mobile) in kbots.iter_mut() {\n        if mobile.con_completed >= 1.0 {\n            // Look at current_command, change move_target if necessary\n            match mobile.current_command {\n                Command::Build(to_build) => match mobiles2.get(&to_build) {\n                    Some(to_build) => {\n                        if to_build.con_completed < 1.0 {\n                            let dist =\n                                (to_build.position.coords - mobile.position.coords).magnitude();\n                            let botdef = bot_defs.get(&mobile.botdef_id).unwrap();\n                            if dist <= botdef.build_dist {\n                                mobile.move_target = None;\n                                build_throughputs.push(BuildPart {\n                                    amount: botdef.build_power as f64,\n                                    repair: false,\n                                    player: mobile.player_id,\n                                    from: *id,\n                                    to: to_build.id,\n                                })\n                            } else {\n                                mobile.move_target = Some(to_build.position);\n                            }\n                        } else {\n                            mobile.current_command = Command::None;\n                            mobile.move_target = None;\n                        }\n                    }\n                    None => {}\n                },\n                Command::Repair(to_build) => match mobiles2.get(&to_build) {\n                    Some(to_build) => {\n                        let botdef_of_to_build = bot_defs.get(&to_build.botdef_id).unwrap();\n                        if to_build.life < botdef_of_to_build.max_life\n                            || to_build.con_completed < 1.0\n                        {\n                            let dist =\n                                (to_build.position.coords - mobile.position.coords).magnitude();\n                            let botdef = bot_defs.get(&mobile.botdef_id).unwrap();\n                            if dist <= botdef.build_dist {\n                                mobile.move_target = None;\n                                build_throughputs.push(BuildPart {\n                                    amount: botdef.build_power as f64,\n                                    repair: to_build.con_completed >= 1.0,\n                                    player: mobile.player_id,\n                                    from: *id,\n                                    to: to_build.id,\n                                })\n                            } else {\n                                mobile.move_target = Some(to_build.position);\n                            }\n                        } else {\n                            mobile.current_command = Command::None;\n                            mobile.move_target = None;\n                        }\n                    }\n                    None => {}\n                },\n                _ => {}\n            }\n        }\n    }\n\n    //Compute resource usage for each player\n    struct ResourceUsage {\n        metal: f64,\n        energy: f64,\n    }\n    let mut resources_usage = FnvHashMap::<Id<Player>, ResourceUsage>::default();\n\n    for BuildPart {\n        amount,\n        to,\n        repair,\n        from,\n        player,\n    } in build_throughputs.iter()\n    {\n        let stat = resources_usage.entry(*player).or_insert(ResourceUsage {\n            metal: 0.0,\n            energy: 0.0,\n        });\n        *stat = ResourceUsage {\n            metal: stat.metal + if *repair { 0.0 } else { *amount as f64 },\n            energy: 0.0,\n        };\n    }\n    //Compute what proportion of usage is usable without negative stock\n    struct ResourceUsagePropMax {\n        metal: f64,\n        energy: f64,\n    }\n    let mut usage_props_max = FnvHashMap::<Id<Player>, ResourceUsagePropMax>::default();\n    for (player_id, player) in players.iter_mut() {\n        if let Some(ru) = resources_usage.get(player_id) {\n            let current_metal_stock = player.metal;\n            let current_energy_stock = player.energy;\n\n            let metal_needed = ru.metal;\n            let energy_needed = ru.energy;\n\n            //TODO Energy count too\n            let metal_prop_max: f64 = (current_metal_stock / metal_needed)\n                .min(current_energy_stock / energy_needed)\n                .min(1.0);\n            usage_props_max.insert(\n                *player_id,\n                ResourceUsagePropMax {\n                    metal: metal_prop_max,\n                    energy: 1.0,\n                },\n            );\n\n            player.metal = (player.metal - metal_needed * metal_prop_max).max(0.0);\n        }\n    }\n\n    //SYNC beause we modify player directly instead of creating another step next\n    //Compute build percent for each unit, refund player for overcost\n    struct ResourceSurplus {\n        metal: f64,\n        energy: f64,\n    }\n\n    let mut resources_surplus = FnvHashMap::<Id<Player>, ResourceSurplus>::default();\n    for BuildPart {\n        amount,\n        to,\n        from,\n        player,\n        repair,\n    } in build_throughputs\n    {\n        let kbot = kbots.get_mut(&to).unwrap();\n        let botdef = bot_defs.get(&kbot.botdef_id).unwrap();\n        let metal_available = amount * usage_props_max.get(&player).unwrap().metal;\n        let metal_needed = if repair {\n            0.0\n        } else {\n            (1.0 - kbot.con_completed as f64) * botdef.metal_cost as f64\n        };\n        let mut metal_used = metal_available;\n        if metal_needed > metal_available {\n            let metal_built =\n                metal_available + kbot.con_completed as f64 * botdef.metal_cost as f64;\n\n            kbot.con_completed = (metal_built / botdef.metal_cost as f64) as f32;\n        } else {\n            let metal_not_used = metal_available - metal_needed;\n            metal_used = metal_available - metal_not_used;\n            if !repair {\n                players.get_mut(&player).unwrap().metal += metal_not_used;\n                kbot.con_completed = 1.0;\n            }\n        }\n        let lambda = if repair {\n            amount as f32\n        } else {\n            metal_used as f32\n        } / botdef.metal_cost as f32;\n        kbot.life = ((kbot.life as f32 + lambda * botdef.max_life as f32).ceil() as i32)\n            .min((botdef.max_life as f32 * kbot.con_completed).ceil() as i32);\n    }\n\n    frame_profiler.add(\"01b build compute\", start.elapsed());\n\n    //Movement compute\n\n    for (id, mobile) in kbots.iter_mut() {\n        if mobile.con_completed >= 1.0 {\n            if mobile.speed.magnitude_squared() > 0.001\n                || mobile.move_target.is_some()\n                || !mobile.grounded\n            {\n                let botdef = bot_defs.get(&mobile.botdef_id).unwrap();\n                let grid_pos = grid_pos(mobile);\n                let mut neighbors_id: Vec<Id<KBot>> = grid[grid_pos].clone();\n                let to_remove = neighbors_id.iter().position(|e| e == id).unwrap();\n                neighbors_id.remove(to_remove);\n\n                let avoidance_force = avoid_neighbors_force(mobile, neighbors_id, &mobiles2) * 0.3;\n\n                let TargetForce {\n                    target_force,\n                    stop_tracking,\n                } = to_target_force(mobile, botdef);\n\n                // arrows.push(Arrow {\n                //     position: mobile.position,\n                //     color: [target_force.norm(), 0.0, 0.0, 0.0],\n                //     end: mobile.position\n                //         + Vector3::new(target_force.x * 2.0, target_force.y * 2.0, 0.0),\n                // });\n\n                // arrows.push(Arrow {\n                //     position: mobile.position,\n                //     color: [0.0, avoidance_force.norm(), 0.0, 0.0],\n                //     end: mobile.position\n                //         + Vector3::new(avoidance_force.x * 2.0, avoidance_force.y * 2.0, 0.0),\n                // });\n\n                if stop_tracking {\n                    mobile.move_target = None;\n                }\n\n                let dir = avoidance_force + target_force;\n                let dir_intensity = (avoidance_force.norm() + target_force.norm())\n                    .max(0.0)\n                    .min(1.0);\n\n                //Clamp in cone\n                let wanted_angle: Angle = dir.into();\n                let current_angle = mobile.angle;\n\n                fn clamp_abs(x: f32, max_abs: f32) -> f32 {\n                    let sign = x.signum();\n                    sign * (x.abs().min(max_abs))\n                }\n\n                let diff = (wanted_angle - (current_angle + mobile.angular_velocity.into())).rad;\n\n                mobile.angular_velocity = clamp_abs(\n                    mobile.angular_velocity + clamp_abs(diff, botdef.turn_accel),\n                    botdef.max_turn_rate,\n                );\n\n                let new_angle = current_angle + mobile.angular_velocity.into();\n                // current_angle.clamp_around(wanted_angle, mobile.angular_velocity.into());\n                mobile.angle = new_angle;\n                let new_dir: Vector2<f32> = new_angle.into();\n                mobile.dir = Vector3::new(new_dir.x, new_dir.y, 0.0);\n\n                //TODO drift factor ?\n                //drift = 1 (adherence = 0)\n                // mobile.speed = mobile.speed + mobile.dir * botdef.accel * dir_intensity;\n                //drift = 0 (adherence = 1)\n\n                let speed_scalar = mobile.speed.xy().magnitude();\n                let thrust = if speed_scalar > 0.01 {\n                    dir.normalize().dot(&(mobile.speed.xy() / speed_scalar))\n                } else {\n                    1.0\n                };\n\n                let accel = if mobile.move_target != None && thrust > 0.0 {\n                    botdef.accel * dir_intensity * thrust\n                } else {\n                    -botdef.break_accel * thrust.abs()\n                };\n\n                // arrows.push(Arrow {\n                //     position: mobile.position + Vector3::new(0.0, 0.0, 2.0),\n                //     color: [0.0, 0.0, accel, 0.0],\n                //     end: mobile.position\n                //         + Vector3::new(dir.x, dir.y, 0.0) * 4.0\n                //         + Vector3::new(0.0, 0.0, 2.0),\n                // });\n\n                // arrows.push(Arrow {\n                //     position: mobile.position + Vector3::new(0.0, 0.0, 1.0),\n                //     color: [0.0, 0.0, accel, 0.0],\n                //     end: mobile.position + mobile.dir * accel * 4.0 + Vector3::new(0.0, 0.0, 1.0),\n                // });\n\n                mobile.speed = mobile.dir * (accel + mobile.speed.magnitude()).max(0.0);\n\n                let speed = mobile.speed.magnitude();\n                if speed > botdef.max_speed {\n                    mobile.speed /= speed / botdef.max_speed;\n                }\n\n                mobile.position += mobile.speed;\n                mobile.position.x = mobile\n                    .position\n                    .x\n                    .max(0.0)\n                    .min(heightmap_phy.width as f32 - 1.0);\n                mobile.position.y = mobile\n                    .position\n                    .y\n                    .max(0.0)\n                    .min(heightmap_phy.height as f32 - 1.0);\n                mobile.position.z = heightmap_phy.z_linear(mobile.position.x, mobile.position.y);\n                mobile.grounded = true;\n                mobile.up = heightmap_phy.normal(mobile.position.x, mobile.position.y);\n\n                let y = -mobile.dir.cross(&mobile.up);\n                let x = y.cross(&mobile.up);\n                mobile.dir = x;\n\n                mobile.weapon0_dir = (mobile.weapon0_dir + mobile.dir).normalize();\n                //w = v/r\n                mobile.wheel0_angle += mobile.speed.norm() / 0.5;\n            }\n        }\n    }\n    frame_profiler.add(\"02  movement\", start.elapsed());\n\n    //Remove dead kbot\n    for (id, kbot) in kbots.iter() {\n        if kbot.life <= 0 {\n            kbots_dead.insert(*id);\n\n            explosions.push(ExplosionEvent {\n                position: Point3::from(kbot.position),\n                size: 1.0,\n                life_time: 1.2,\n            });\n        }\n    }\n\n    for id in kbots_dead.iter() {\n        kbots.remove(id);\n    }\n}\n\nfn avoid_neighbors_force(\n    me: &KBot,\n    neighbors_id: Vec<Id<KBot>>,\n    kbots: &FnvHashMap<Id<KBot>, KBot>,\n) -> Vector2<f32> {\n    // could be speed/ brake\n    // let prediction = 1.0;\n    let pos = me.position + me.speed;\n\n    let mut avoidance = Vector2::new(0.0, 0.0);\n    for other_id in neighbors_id.iter() {\n        let other = kbots.get(other_id).unwrap();\n        let o_pos = other.position + other.speed;\n\n        let to_other = (o_pos.coords - pos.coords).xy();\n        let distance = (to_other.magnitude() - 1.1).max(0.1);\n        let inv_distance = 1.0 / distance;\n        let to_other_normalized = to_other * inv_distance;\n        avoidance += -to_other_normalized * inv_distance;\n    }\n    avoidance\n}\n\nstruct TargetForce {\n    target_force: Vector2<f32>,\n    stop_tracking: bool,\n}\nfn to_target_force(me: &KBot, botdef: &botdef::BotDef) -> TargetForce {\n    if let Some(target) = me.move_target {\n        let to_target = (target.coords - (me.position.coords + me.speed)).xy();\n        let to_target_distance = to_target.norm();\n        let will_to_go_target = if to_target_distance > botdef.radius {\n            1.0\n        } else {\n            1.0 - ((botdef.radius - to_target_distance) / botdef.radius)\n        };\n\n        TargetForce {\n            target_force: (to_target / to_target_distance) * will_to_go_target,\n            stop_tracking: to_target_distance < botdef.radius / 2.0,\n        }\n    } else {\n        TargetForce {\n            target_force: Vector2::new(0.0, 0.0),\n            stop_tracking: true,\n        }\n    }\n}\n"
  },
  {
    "path": "src/glsl.rs",
    "content": "use crate::gpu_obj::glsl_compiler;\nuse std::fs::{self, DirEntry};\nuse std::io;\nuse std::path::Path;\nuse std::slice;\n\npub fn compile_all_glsl() {\n    println!(\"Compile all glsl\");\n\n    let path = std::path::Path::new(\"./src/shader/\");\n    let cb = |de: &DirEntry| {\n        let path_to_read = de.path();\n        let ext = path_to_read.extension().unwrap().to_str().unwrap();\n\n        if !ext.contains(\"spirv\") {\n            println!(\"compiling {:?}\", path_to_read);\n            let spirv = glsl_compiler::load(path_to_read.to_str().unwrap()).unwrap();\n\n            let file_name = path_to_read.file_name().unwrap();\n            let mut path_to_write = path_to_read.parent().unwrap().to_path_buf();\n            path_to_write.push(\"compiled\");\n            path_to_write.push(file_name);\n            let path_to_write = path_to_write.with_extension(format!(\"{}.spirv\", ext));\n\n            println!(\"write to {:?}\", path_to_write);\n\n            let slice_u8: Vec<u8> = spirv\n                .iter()\n                .map(|w| w.to_le_bytes().iter().copied().collect::<Vec<u8>>())\n                .flatten()\n                .collect();\n\n            std::fs::write(path_to_write, slice_u8).unwrap();\n        }\n    };\n    visit_dirs(path, &cb).unwrap();\n}\n\nfn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> {\n    if dir.is_dir() {\n        for entry in fs::read_dir(dir)? {\n            let entry = entry?;\n            let path = entry.path();\n            if path.is_dir() {\n                visit_dirs(&path, cb)?;\n            } else {\n                cb(&entry);\n            }\n        }\n    }\n    Ok(())\n}\n"
  },
  {
    "path": "src/gpu_obj/arrow_gpu.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat};\n\npub struct ArrowGpu {\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    index_count: usize,\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n}\n\nimpl ArrowGpu {\n    pub fn new(\n        triangle_list: &model::TriangleList,\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n    ) -> Self {\n        log::trace!(\"ArrowGpu new\");\n        // Create the vertex and index buffers\n        let model::TriangleList {\n            vertex_data,\n            index_data,\n        } = triangle_list;\n        let vertex_buf = device\n            .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(&vertex_data);\n\n        let index_buf = device\n            .create_buffer_mapped(index_data.len(), wgpu::BufferUsage::INDEX)\n            .fill_from_slice(&index_data);\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();;\n\n        ArrowGpu {\n            vertex_buf,\n            index_buf,\n            index_count: index_data.len(),\n            instance_buf,\n            instance_count: positions.len() as u32 / 3,\n            pipeline,\n        }\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout],\n        });\n        let vertex_size = std::mem::size_of::<model::Vertex>();\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/arrow.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/arrow.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Ccw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleList,\n            color_states: &[\n                wgpu::ColorStateDescriptor {\n                    format: format,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n                wgpu::ColorStateDescriptor {\n                    format: wgpu::TextureFormat::Rgba32Float,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n                wgpu::ColorStateDescriptor {\n                    format: wgpu::TextureFormat::Rg16Float,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n            ],\n            depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {\n                format: wgpu::TextureFormat::Depth32Float,\n                depth_write_enabled: true,\n                depth_compare: wgpu::CompareFunction::Less,\n                stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_read_mask: 0,\n                stencil_write_mask: 0,\n            }),\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[\n                wgpu::VertexBufferDescriptor {\n                    stride: vertex_size as wgpu::BufferAddress,\n                    step_mode: wgpu::InputStepMode::Vertex,\n                    attributes: &[\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 0,\n                            shader_location: 0,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float2,\n                            offset: 4 * 4,\n                            shader_location: 1,\n                        },\n                    ],\n                },\n                wgpu::VertexBufferDescriptor {\n                    stride: (4 * 20) as wgpu::BufferAddress,\n                    step_mode: wgpu::InputStepMode::Instance,\n                    attributes: &[\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 0,\n                            shader_location: 2,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 4 * 4,\n                            shader_location: 3,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 4 * 8,\n                            shader_location: 4,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 4 * 12,\n                            shader_location: 5,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 4 * 16,\n                            shader_location: 6,\n                        },\n                    ],\n                },\n            ],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"ArrowGpu render\");\n\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.set_index_buffer(&self.index_buf, 0);\n            rpass.set_vertex_buffers(0, &[(&self.vertex_buf, 0), (&self.instance_buf, 0)]);\n            rpass.draw_indexed(0..self.index_count as u32, 0, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"ArrowGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 20;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for ArrowGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(device, main_bind_group_layout, format) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/blit_texture.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse crate::utils::ImageRGBA8;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, Texture, TextureFormat, TextureView};\npub struct BlitTextureGpu {\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    bind_group: BindGroup,\n    noise_texture: Texture,\n}\n\nimpl BlitTextureGpu {\n    pub fn new(\n        init_encoder: &mut wgpu::CommandEncoder,\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n        img: ImageRGBA8,\n    ) -> Self {\n        log::trace!(\"BlitTextureGpu new\");\n\n        let texels = img.data;\n        let texture_extent = wgpu::Extent3d {\n            width: img.w,\n            height: img.h,\n            depth: 1,\n        };\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: texture_extent,\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n            usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,\n        });\n\n        let temp_buf = device\n            .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&texels);\n        init_encoder.copy_buffer_to_texture(\n            wgpu::BufferCopyView {\n                buffer: &temp_buf,\n                offset: 0,\n                row_pitch: 4 * img.w,\n                image_height: img.h,\n            },\n            wgpu::TextureCopyView {\n                texture: &texture,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: 0.0,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            },\n            texture_extent,\n        );\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let noise_texture_view = texture.create_default_view();\n        let bind_group = Self::create_bind_group(device, &bind_group_layout, &noise_texture_view);\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();\n        BlitTextureGpu {\n            instance_buf,\n            instance_count: 0,\n            pipeline,\n            bind_group,\n            bind_group_layout,\n            noise_texture: texture,\n        }\n    }\n\n    pub fn create_bind_group(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        noise_texture_view: &TextureView,\n    ) -> BindGroup {\n        // Create other resources\n        let sampler_noise = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(noise_texture_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler_noise),\n                },\n            ],\n        })\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout],\n        });\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/blit_texture.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/blit_texture.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format: format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[wgpu::VertexBufferDescriptor {\n                stride: (4 * (8)) as wgpu::BufferAddress,\n                step_mode: wgpu::InputStepMode::Instance,\n                attributes: &[\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 0,\n                        shader_location: 0,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 4 * 2,\n                        shader_location: 1,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 4 * 4,\n                        shader_location: 2,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 4 * 6,\n                        shader_location: 3,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"BlitTextureGpu render\");\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.set_bind_group(1, &self.bind_group, &[]);\n            rpass.draw(0..4, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"BlitTextureGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 8;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for BlitTextureGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/explosion.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, Texture, TextureFormat, TextureView};\n\npub struct ExplosionGpu {\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    bind_group: BindGroup,\n    noise_texture: Texture,\n}\n\nimpl ExplosionGpu {\n    pub fn new(\n        init_encoder: &mut wgpu::CommandEncoder,\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n        current_position_att: &TextureView,\n        normal_att: &TextureView,\n    ) -> Self {\n        log::trace!(\"ExplosionGpu new\");\n\n        let size = 256u32;\n        let texels = Self::open_noise();\n        let texture_extent = wgpu::Extent3d {\n            width: size,\n            height: size,\n            depth: 1,\n        };\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: texture_extent,\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::Rgba8UnormSrgb,\n            usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,\n        });\n\n        let temp_buf = device\n            .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&texels);\n        init_encoder.copy_buffer_to_texture(\n            wgpu::BufferCopyView {\n                buffer: &temp_buf,\n                offset: 0,\n                row_pitch: 4 * size,\n                image_height: size,\n            },\n            wgpu::TextureCopyView {\n                texture: &texture,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: 0.0,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            },\n            texture_extent,\n        );\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 2,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 3,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 4,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 5,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let noise_texture_view = texture.create_default_view();\n        let bind_group = Self::create_bind_group(\n            device,\n            &bind_group_layout,\n            &noise_texture_view,\n            current_position_att,\n            normal_att,\n        );\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();\n\n        ExplosionGpu {\n            instance_buf,\n            instance_count: 0,\n            pipeline,\n            bind_group,\n            bind_group_layout,\n            noise_texture: texture,\n        }\n    }\n\n    pub fn create_bind_group(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        noise_texture_view: &TextureView,\n        current_position_att: &TextureView,\n        normal_att: &TextureView,\n    ) -> BindGroup {\n        // Create other resources\n        let sampler_noise = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let sampler_position = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let sampler_normal = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(noise_texture_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler_noise),\n                },\n                wgpu::Binding {\n                    binding: 2,\n                    resource: wgpu::BindingResource::TextureView(current_position_att),\n                },\n                wgpu::Binding {\n                    binding: 3,\n                    resource: wgpu::BindingResource::Sampler(&sampler_position),\n                },\n                wgpu::Binding {\n                    binding: 4,\n                    resource: wgpu::BindingResource::TextureView(normal_att),\n                },\n                wgpu::Binding {\n                    binding: 5,\n                    resource: wgpu::BindingResource::Sampler(&sampler_normal),\n                },\n            ],\n        })\n    }\n\n    pub fn update_bind_group(\n        &mut self,\n        device: &Device,\n        current_position_att: &TextureView,\n        normal_att: &TextureView,\n    ) {\n        let noise_texture_view = self.noise_texture.create_default_view();\n        self.bind_group = Self::create_bind_group(\n            device,\n            &self.bind_group_layout,\n            &noise_texture_view,\n            current_position_att,\n            normal_att,\n        );\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout],\n        });\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/explosion.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/explosion.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format: format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::One,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[wgpu::VertexBufferDescriptor {\n                stride: (4 * (6)) as wgpu::BufferAddress,\n                step_mode: wgpu::InputStepMode::Instance,\n                attributes: &[\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float3,\n                        offset: 0,\n                        shader_location: 0,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 3,\n                        shader_location: 1,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 4,\n                        shader_location: 2,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 5,\n                        shader_location: 3,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn open_noise() -> Vec<u8> {\n        use byteorder::{BigEndian, ReadBytesExt};\n        use std::fs::File;\n\n        // The decoder is a build for reader and can be used to set various decoding options\n        // via `Transformations`. The default output transformation is `Transformations::EXPAND\n        // | Transformations::STRIP_ALPHA`.\n        let mut decoder = png::Decoder::new(File::open(r\"src/asset/2d/noise.png\").unwrap());\n        decoder.set_transformations(png::Transformations::IDENTITY);\n        let (info, mut reader) = decoder.read_info().unwrap();\n\n        // Display image metadata.\n        log::debug!(\"info: {:?}\", info.width);\n        log::debug!(\"height: {:?}\", info.height);\n        log::debug!(\"bit depth: {:?}\", info.bit_depth);\n        log::debug!(\"buffer size: {:?}\", info.buffer_size());\n\n        // Allocate the output buffer.\n        let mut buf = vec![0; info.buffer_size()];\n        // Read the next frame. Currently this function should only called once.\n        // The default options\n        reader.next_frame(&mut buf).unwrap();\n        buf\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"ExplosionGpu render\");\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.set_bind_group(1, &self.bind_group, &[]);\n            rpass.draw(0..4, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"ExplosionGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 6;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for ExplosionGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/glsl_compiler.rs",
    "content": "#[cfg(feature = \"use_shaderc\")]\nuse shaderc;\n\n#[allow(dead_code)]\npub enum ShaderStage {\n    Vertex,\n    Fragment,\n    Compute,\n}\n\nfn str_to_shader_stage(str: &str) -> ShaderStage {\n    if str.ends_with(\"vert\") {\n        ShaderStage::Vertex\n    } else if str.ends_with(\"frag\") {\n        ShaderStage::Fragment\n    } else {\n        ShaderStage::Compute\n    }\n}\n\nuse std::error;\nuse std::fmt;\nuse std::slice;\n\npub type Result<T> = std::result::Result<T, ShaderCompilationError>;\n\n#[derive(Debug, Clone)]\npub struct ShaderCompilationError {\n    pub msg: String,\n}\n\nimpl fmt::Display for ShaderCompilationError {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"shader compilation error {}\", self.msg)\n    }\n}\n\nimpl error::Error for ShaderCompilationError {\n    fn source(&self) -> Option<&(dyn error::Error + 'static)> {\n        None\n    }\n}\n\n#[cfg(feature = \"use_glsl_to_spirv\")]\npub fn load(rel_path: &str) -> Result<Vec<u32>> {\n    let stage = str_to_shader_stage(rel_path);\n    log::debug!(\"glsl_to_spirv : compiling {}\", rel_path);\n    let glsl_code = std::fs::read_to_string(std::path::Path::new(rel_path)).unwrap();\n    let ty = match stage {\n        ShaderStage::Vertex => glsl_to_spirv::ShaderType::Vertex,\n        ShaderStage::Fragment => glsl_to_spirv::ShaderType::Fragment,\n        ShaderStage::Compute => glsl_to_spirv::ShaderType::Compute,\n    };\n\n    Ok(\n        wgpu::read_spirv(glsl_to_spirv::compile(&glsl_code, ty).map_err(|e| {\n            ShaderCompilationError {\n                msg: format!(\"{}\", e),\n            }\n        })?)\n        .map_err(|e| ShaderCompilationError {\n            msg: format!(\"{}\", e),\n        })?,\n    )\n    //   wgpu::read_spirv(glsl_to_spirv::compile(&glsl_code, ty).unwrap()).unwrap()\n}\n\n#[cfg(feature = \"use_shaderc\")]\npub fn load(rel_path: &str) -> Result<Vec<u32>> {\n    let stage = str_to_shader_stage(rel_path);\n    log::debug!(\"shaderc : compiling {}\", rel_path);\n    let glsl_code = std::fs::read_to_string(std::path::Path::new(rel_path)).unwrap();\n\n    let ty = match stage {\n        ShaderStage::Vertex => shaderc::ShaderKind::Vertex,\n        ShaderStage::Fragment => shaderc::ShaderKind::Fragment,\n        ShaderStage::Compute => shaderc::ShaderKind::Compute,\n    };\n\n    let mut compiler = shaderc::Compiler::new().unwrap();\n    let mut options = shaderc::CompileOptions::new().unwrap();\n    options.add_macro_definition(\"EP\", Some(\"main\"));\n    let binary_result = compiler\n        .compile_into_spirv(&glsl_code, ty, rel_path, \"main\", Some(&options))\n        .map_err(|e| ShaderCompilationError {\n            msg: format!(\"{}\", e),\n        })?;\n\n    Ok(binary_result.as_binary().to_owned())\n}\n\n#[cfg(not(any(feature = \"use_shaderc\", feature = \"use_glsl_to_spirv\")))]\npub fn load(rel_path: &str) -> Result<Vec<u32>> {\n    let glsl_path = std::path::Path::new(rel_path);\n    let file_name = glsl_path.file_name().unwrap();\n    let ext = glsl_path.extension().unwrap().to_str().unwrap();\n\n    let mut spirv_path = glsl_path.to_path_buf().parent().unwrap().to_path_buf();\n    spirv_path.push(\"compiled\");\n    spirv_path.push(file_name);\n    let spirv_path = spirv_path.with_extension(format!(\"{}.spirv\", ext));\n\n    log::debug!(\"spirv : reading {:?}\", spirv_path);\n    let spirv = std::fs::read(spirv_path).unwrap();\n\n    use std::convert::TryInto;\n    let vec_u32: Vec<u32> = spirv\n        .chunks_exact(4)\n        .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap()))\n        .collect();\n\n    Ok(vec_u32)\n}\n"
  },
  {
    "path": "src/gpu_obj/gpu.rs",
    "content": "use wgpu::SwapChain;\n\npub struct WgpuState {\n    pub sc_desc: wgpu::SwapChainDescriptor,\n    pub device: wgpu::Device,\n    pub window: winit::window::Window,\n    pub hidpi_factor: f64,\n    pub swap_chain: SwapChain,\n    pub surface: wgpu::Surface,\n    pub queue: wgpu::Queue,\n}\n\nimpl WgpuState {\n    pub fn new(window: winit::window::Window) -> Self {\n        let (hidpi_factor, size, surface) = {\n            let hidpi_factor = window.hidpi_factor();\n\n            window.set_title(\"Oxidator\");\n            log::info!(\"hidpi scaling: {}\", hidpi_factor);\n            let size = window.inner_size().to_physical(hidpi_factor);\n            let surface = wgpu::Surface::create(&window);\n            (hidpi_factor, size, surface)\n        };\n\n        let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions {\n            power_preference: wgpu::PowerPreference::HighPerformance,\n            backends: wgpu::BackendBit::PRIMARY,\n        })\n        .unwrap();\n\n        let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor {\n            extensions: wgpu::Extensions {\n                anisotropic_filtering: false,\n            },\n            limits: wgpu::Limits::default(),\n        });\n\n        let sc_desc = wgpu::SwapChainDescriptor {\n            usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,\n            format: wgpu::TextureFormat::Bgra8UnormSrgb,\n            width: size.width.round() as u32,\n            height: size.height.round() as u32,\n            present_mode: wgpu::PresentMode::NoVsync,\n        };\n        let swap_chain = device.create_swap_chain(&surface, &sc_desc);\n\n        // let physical_wanted = winit::dpi::PhysicalSize {\n        //     width: 1281.0,\n        //     height: 720.0,\n        // };\n        // let logical_wanted = physical_wanted.to_logical(hidpi_factor);\n        // window.set_inner_size(logical_wanted);\n\n        WgpuState {\n            sc_desc,\n            device,\n            window,\n            hidpi_factor,\n            swap_chain,\n            surface,\n            queue,\n        }\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/health_bar.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat};\n\npub struct HealthBarGpu {\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n}\n\nimpl HealthBarGpu {\n    pub fn new(\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n    ) -> Self {\n        log::trace!(\"HealthBarGpu new\");\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();\n\n        HealthBarGpu {\n            instance_buf,\n            instance_count: 0,\n            pipeline,\n        }\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout],\n        });\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/health_bar.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/health_bar.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format: format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[wgpu::VertexBufferDescriptor {\n                stride: (4 * (2 + 2 + 2 + 1)) as wgpu::BufferAddress,\n                step_mode: wgpu::InputStepMode::Instance,\n                attributes: &[\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 0,\n                        shader_location: 0,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 4 * 2,\n                        shader_location: 1,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 4,\n                        shader_location: 2,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 5,\n                        shader_location: 3,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 6,\n                        shader_location: 4,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"HealthBarGpu render\");\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.draw(0..4, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"HealthBarGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 7;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for HealthBarGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(device, main_bind_group_layout, format) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/heightmap_gpu.rs",
    "content": "use super::glsl_compiler;\nuse super::heightmap_helper;\nuse crate::heightmap_phy;\n\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, RenderPipeline, Texture, TextureFormat};\nuse wgpu::{CommandEncoder, Device};\n\nconst ZONE_SIZE_MIP0: usize = 64;\nconst UPDATE_PER_STEP: usize = 300;\nconst MIP_COUNT: u32 = 5;\npub const MAX_Z: f32 = 511.0;\n\npub struct HeightmapGpu {\n    pipeline: RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    bind_group: BindGroup,\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    index_count: usize,\n    pub phy: heightmap_phy::HeightmapPhy,\n    ring_size: u32,\n    texture: Texture,\n    texture_lod: Texture,\n    uniform_buf: wgpu::Buffer,\n    zone_to_update_mip0: Vec<i32>,\n    zone_to_update_mip1: Vec<i32>,\n    zone_to_update_mip2: Vec<i32>,\n    mip4_to_update: bool,\n}\n\nimpl HeightmapGpu {\n    pub fn new(\n        device: &Device,\n        init_encoder: &mut CommandEncoder,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n        phy: heightmap_phy::HeightmapPhy,\n    ) -> Self {\n        log::trace!(\"HeightmapGpu new\");\n        let texture_view_checker = {\n            let size = 2u32;\n            let texels = crate::procedural_texels::checker(size as usize);\n            let texture_extent = wgpu::Extent3d {\n                width: size,\n                height: size,\n                depth: 1,\n            };\n            let texture = device.create_texture(&wgpu::TextureDescriptor {\n                size: texture_extent,\n                array_layer_count: 1,\n                mip_level_count: 2,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::Rgba8UnormSrgb,\n                usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,\n            });\n\n            let temp_buf = device\n                .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC)\n                .fill_from_slice(&texels);\n            init_encoder.copy_buffer_to_texture(\n                wgpu::BufferCopyView {\n                    buffer: &temp_buf,\n                    offset: 0,\n                    row_pitch: 4 * size,\n                    image_height: size,\n                },\n                wgpu::TextureCopyView {\n                    texture: &texture,\n                    mip_level: 0,\n                    array_layer: 0,\n                    origin: wgpu::Origin3d {\n                        x: 0.0,\n                        y: 0.0,\n                        z: 0.0,\n                    },\n                },\n                texture_extent,\n            );\n\n            {\n                let texture_extent = wgpu::Extent3d {\n                    width: 1,\n                    height: 1,\n                    depth: 1,\n                };\n                let temp_buf = device\n                    .create_buffer_mapped(4, wgpu::BufferUsage::COPY_SRC)\n                    .fill_from_slice(&[123, 123, 123, 255]);\n                init_encoder.copy_buffer_to_texture(\n                    wgpu::BufferCopyView {\n                        buffer: &temp_buf,\n                        offset: 0,\n                        row_pitch: 4 * 1,\n                        image_height: 1,\n                    },\n                    wgpu::TextureCopyView {\n                        texture: &texture,\n                        mip_level: 1,\n                        array_layer: 0,\n                        origin: wgpu::Origin3d {\n                            x: 0.0,\n                            y: 0.0,\n                            z: 0.0,\n                        },\n                    },\n                    texture_extent,\n                );\n            }\n\n            texture.create_default_view()\n        };\n\n        let sampler_checker = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let (texture_view_lod, texture_lod) = {\n            let width = phy.width as u32 / ZONE_SIZE_MIP0 as u32;\n            let height = phy.height as u32 / ZONE_SIZE_MIP0 as u32;\n\n            let size = phy.width as u32 * phy.height as u32;\n            let texture_extent = wgpu::Extent3d {\n                width,\n                height,\n                depth: 1,\n            };\n            let texture = device.create_texture(&wgpu::TextureDescriptor {\n                size: texture_extent,\n                array_layer_count: 1,\n                mip_level_count: 1,\n                sample_count: 1,\n                dimension: wgpu::TextureDimension::D2,\n                format: wgpu::TextureFormat::R32Float,\n                usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,\n            });\n\n            let mut texels = vec![0_f32; size as usize];\n\n            texels[0] = 4.0;\n\n            let temp_buf = device\n                .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC)\n                .fill_from_slice(&texels);\n            init_encoder.copy_buffer_to_texture(\n                wgpu::BufferCopyView {\n                    buffer: &temp_buf,\n                    offset: 0,\n                    row_pitch: 4 * width,\n                    image_height: height,\n                },\n                wgpu::TextureCopyView {\n                    texture: &texture,\n                    mip_level: 0,\n                    array_layer: 0,\n                    origin: wgpu::Origin3d {\n                        x: 0.0,\n                        y: 0.0,\n                        z: 0.0,\n                    },\n                },\n                texture_extent,\n            );\n\n            (texture.create_default_view(), texture)\n        };\n\n        let sampler_lod = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::Repeat,\n            address_mode_v: wgpu::AddressMode::Repeat,\n            address_mode_w: wgpu::AddressMode::Repeat,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::FilterMode::Nearest,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let start = std::time::Instant::now();\n\n        let texture_extent = wgpu::Extent3d {\n            width: phy.width as u32,\n            height: phy.height as u32,\n            depth: 1,\n        };\n\n        let texture = device.create_texture(&wgpu::TextureDescriptor {\n            size: texture_extent,\n            array_layer_count: 1,\n            mip_level_count: MIP_COUNT,\n            sample_count: 1,\n            dimension: wgpu::TextureDimension::D2,\n            format: wgpu::TextureFormat::R32Float,\n            usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST,\n        });\n\n        let temp_buf = device\n            .create_buffer_mapped(phy.texels.len(), wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&phy.texels);\n\n        init_encoder.copy_buffer_to_texture(\n            wgpu::BufferCopyView {\n                buffer: &temp_buf,\n                offset: 0,\n                row_pitch: 4 * phy.width as u32,\n                image_height: phy.height as u32,\n            },\n            wgpu::TextureCopyView {\n                texture: &texture,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: 0.0,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            },\n            texture_extent,\n        );\n\n        let mut mipmaper = |mip: u32| {\n            let m = 2_u32.pow(mip);\n\n            let width = phy.width as u32 / m;\n            let height = phy.height as u32 / m;\n            let texture_extent = wgpu::Extent3d {\n                width,\n                height,\n                depth: 1,\n            };\n            let mut texels2 = Vec::new();\n            for j in 0..height {\n                for i in 0..width {\n                    texels2.push(phy.texels[(i * m + (j * m) * width * m) as usize]);\n                }\n            }\n\n            let temp_buf = device\n                .create_buffer_mapped(texels2.len(), wgpu::BufferUsage::COPY_SRC)\n                .fill_from_slice(&texels2);\n\n            init_encoder.copy_buffer_to_texture(\n                wgpu::BufferCopyView {\n                    buffer: &temp_buf,\n                    offset: 0,\n                    row_pitch: 4 * width,\n                    image_height: height,\n                },\n                wgpu::TextureCopyView {\n                    texture: &texture,\n                    mip_level: mip,\n                    array_layer: 0,\n                    origin: wgpu::Origin3d {\n                        x: 0.0,\n                        y: 0.0,\n                        z: 0.0,\n                    },\n                },\n                texture_extent,\n            );\n        };\n\n        for i in 1..MIP_COUNT {\n            mipmaper(i);\n        }\n\n        let texture_view_height = texture.create_default_view();\n\n        let sampler_height = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        //Map size\n        let ring_size = 128;\n        let map_size_cam_pos = [\n            phy.width as f32,\n            phy.height as f32,\n            ring_size as f32,\n            0.0,\n            0.0,\n        ];\n\n        let uniform_buf = device\n            .create_buffer_mapped(5, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST)\n            .fill_from_slice(&map_size_cam_pos);\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::UniformBuffer { dynamic: false },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 2,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 3,\n                    visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 4,\n                    visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 5,\n                    visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 6,\n                    visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();\n\n        // Create bind group\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::Buffer {\n                        buffer: &uniform_buf,\n                        range: 0..20,\n                    },\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::TextureView(&texture_view_checker),\n                },\n                wgpu::Binding {\n                    binding: 2,\n                    resource: wgpu::BindingResource::Sampler(&sampler_checker),\n                },\n                wgpu::Binding {\n                    binding: 3,\n                    resource: wgpu::BindingResource::TextureView(&texture_view_height),\n                },\n                wgpu::Binding {\n                    binding: 4,\n                    resource: wgpu::BindingResource::Sampler(&sampler_height),\n                },\n                wgpu::Binding {\n                    binding: 5,\n                    resource: wgpu::BindingResource::TextureView(&texture_view_lod),\n                },\n                wgpu::Binding {\n                    binding: 6,\n                    resource: wgpu::BindingResource::Sampler(&sampler_lod),\n                },\n            ],\n        });\n\n        let (vertex_data, height_index_data) =\n            heightmap_helper::create_vertex_index_rings(ring_size);\n        //            heightmap::create_vertices_indices(width, height, 0.0);\n        let vertex_buf = device\n            .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(&vertex_data);\n\n        let index_buf = device\n            .create_buffer_mapped(height_index_data.len(), wgpu::BufferUsage::INDEX)\n            .fill_from_slice(&height_index_data);\n\n        let index_count = height_index_data.len();\n\n        let mut zone_to_update_mip0 = Vec::new();\n\n        for _ in (0..=phy.width).step_by(ZONE_SIZE_MIP0) {\n            for _ in (0..=phy.height).step_by(ZONE_SIZE_MIP0) {\n                zone_to_update_mip0.push(0);\n            }\n        }\n\n        let mut zone_to_update_mip1 = Vec::new();\n\n        for _ in (0..=phy.width).step_by(ZONE_SIZE_MIP0 * 2) {\n            for _ in (0..=phy.height).step_by(ZONE_SIZE_MIP0 * 2) {\n                zone_to_update_mip1.push(0);\n            }\n        }\n\n        let mut zone_to_update_mip2 = Vec::new();\n        for _ in (0..=phy.width).step_by(ZONE_SIZE_MIP0 * 4) {\n            for _ in (0..=phy.height).step_by(ZONE_SIZE_MIP0 * 4) {\n                zone_to_update_mip2.push(0);\n            }\n        }\n\n        HeightmapGpu {\n            pipeline,\n            bind_group,\n            bind_group_layout,\n            vertex_buf,\n            index_buf,\n            index_count,\n            phy,\n            ring_size,\n            texture,\n            texture_lod,\n            uniform_buf,\n            zone_to_update_mip0,\n            zone_to_update_mip1,\n            zone_to_update_mip2,\n            mip4_to_update: false,\n        }\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        // Create pipeline layout\n\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[main_bind_group_layout, bind_group_layout],\n        });\n\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/heightmap.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/heightmap.frag\")?;\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Ccw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleList,\n            color_states: &[\n                wgpu::ColorStateDescriptor {\n                    format,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n                wgpu::ColorStateDescriptor {\n                    format: wgpu::TextureFormat::Rgba32Float,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n                wgpu::ColorStateDescriptor {\n                    format: wgpu::TextureFormat::Rg16Float,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n            ],\n            depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {\n                format: wgpu::TextureFormat::Depth32Float,\n                depth_write_enabled: true,\n                depth_compare: wgpu::CompareFunction::Less,\n                stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_read_mask: 0,\n                stencil_write_mask: 0,\n            }),\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[wgpu::VertexBufferDescriptor {\n                stride: std::mem::size_of::<heightmap_helper::Vertex>() as wgpu::BufferAddress,\n                step_mode: wgpu::InputStepMode::Vertex,\n                attributes: &[\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 0,\n                        shader_location: 0,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 2,\n                        shader_location: 1,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn mipmap_update(\n        &self,\n        mip: u32,\n        device: &Device,\n        encoder: &mut CommandEncoder,\n        texture: &Texture,\n        texels: &[f32],\n        texels_width: u32,\n        i: u32,\n        j: u32,\n        width: u32,\n        height: u32,\n    ) {\n        let m = 2_u32.pow(mip);\n\n        let width = width / m;\n        let height = height / m;\n\n        let texture_extent = wgpu::Extent3d {\n            width,\n            height,\n            depth: 1,\n        };\n\n        let min_x = i / m;\n        let min_y = j / m;\n\n        let mut texels2 = Vec::new();\n        for j in min_y..(min_y + height) {\n            for i in min_x..(min_x + width) {\n                texels2.push(texels[(i * m + (j * m) * (texels_width / m) * m) as usize]);\n            }\n        }\n\n        let temp_buf = device\n            .create_buffer_mapped(texels2.len(), wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&texels2);\n\n        encoder.copy_buffer_to_texture(\n            wgpu::BufferCopyView {\n                buffer: &temp_buf,\n                offset: 0,\n                row_pitch: 4 * width,\n                image_height: height,\n            },\n            wgpu::TextureCopyView {\n                texture,\n                mip_level: mip,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: min_x as f32,\n                    y: min_y as f32,\n                    z: 0.0,\n                },\n            },\n            texture_extent,\n        );\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"HeightmapGpu render\");\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, main_bind_group, &[]);\n        rpass.set_bind_group(1, &self.bind_group, &[]);\n        rpass.set_index_buffer(&self.index_buf, 0);\n        rpass.set_vertex_buffers(0, &[(&self.vertex_buf, 0)]);\n        rpass.draw_indexed(0..(self.index_count) as u32, 0, 0..1);\n    }\n\n    pub fn update_uniform(\n        &mut self,\n        device: &Device,\n        encoder: &mut CommandEncoder,\n        camera_x: f32,\n        camera_y: f32,\n    ) {\n        log::trace!(\"HeightmapGpu update_uniform\");\n        //Map size\n        let map_size_cam_pos = [\n            self.phy.width as u32 as f32,\n            self.phy.height as u32 as f32,\n            self.ring_size as f32,\n            (camera_x.max(0.0).min(self.phy.width as u32 as f32) / 1.0),\n            (camera_y.max(0.0).min(self.phy.height as u32 as f32) / 1.0),\n        ];\n\n        let uniform_buf = device\n            .create_buffer_mapped(\n                5,\n                wgpu::BufferUsage::UNIFORM\n                    | wgpu::BufferUsage::COPY_DST\n                    | wgpu::BufferUsage::COPY_SRC,\n            )\n            .fill_from_slice(&map_size_cam_pos);\n\n        encoder.copy_buffer_to_buffer(&uniform_buf, 0, &self.uniform_buf, 0, 20);\n    }\n\n    pub fn step(&mut self, device: &Device, encoder: &mut CommandEncoder) {\n        let mut update_left = UPDATE_PER_STEP;\n        if self.mip4_to_update {\n            self.mip4_to_update = false;\n            update_left = update_left / 2;\n            for mip in 3..MIP_COUNT {\n                self.mipmap_update(\n                    mip,\n                    device,\n                    encoder,\n                    &self.texture,\n                    &self.phy.texels,\n                    self.phy.width as u32,\n                    0,\n                    0,\n                    self.phy.width as u32,\n                    self.phy.height as u32,\n                );\n            }\n        }\n\n        if update_left > 0 {\n            let mut zone_to_update_mip2 = self\n                .zone_to_update_mip2\n                .iter()\n                .enumerate()\n                .filter(|(_, b)| **b != 0)\n                .collect::<Vec<(usize, &i32)>>();\n\n            let update_to_do = zone_to_update_mip2\n                .len()\n                .min(UPDATE_PER_STEP)\n                .min(update_left);\n            update_left -= update_to_do;\n            zone_to_update_mip2.sort_by_key(|(_, i)| **i);\n\n            let indices: Vec<usize> = zone_to_update_mip2\n                .iter()\n                .map(|(index, _)| *index)\n                .collect();\n\n            for index in indices.iter().skip(update_to_do) {\n                self.zone_to_update_mip2[*index] -= 1;\n            }\n\n            for index in indices.iter().take(update_to_do) {\n                self.zone_to_update_mip2[*index] = 0;\n\n                let i = *index as u32 % (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 4) as u32);\n                let j = *index as u32 / (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 4) as u32);\n                let min_x = i * ZONE_SIZE_MIP0 as u32 * 4;\n                let min_y = j * ZONE_SIZE_MIP0 as u32 * 4;\n\n                if min_x < self.phy.width as u32 && min_y < self.phy.height as u32 {\n                    let width = (ZONE_SIZE_MIP0 as u32 * 4).min(self.phy.width as u32 - min_x);\n                    let height = (ZONE_SIZE_MIP0 as u32 * 4).min(self.phy.height as u32 - min_y);\n\n                    self.mipmap_update(\n                        2,\n                        device,\n                        encoder,\n                        &self.texture,\n                        &self.phy.texels,\n                        self.phy.width as u32,\n                        min_x,\n                        min_y,\n                        width,\n                        height,\n                    );\n                }\n            }\n        }\n\n        if update_left > 0 {\n            let mut zone_to_update_mip1 = self\n                .zone_to_update_mip1\n                .iter()\n                .enumerate()\n                .filter(|(_, b)| **b != 0)\n                .collect::<Vec<(usize, &i32)>>();\n\n            let update_to_do = zone_to_update_mip1\n                .len()\n                .min(UPDATE_PER_STEP)\n                .min(update_left);\n            update_left -= update_to_do;\n            zone_to_update_mip1.sort_by_key(|(_, i)| **i);\n\n            let indices: Vec<usize> = zone_to_update_mip1\n                .iter()\n                .map(|(index, _)| *index)\n                .collect();\n\n            for index in indices.iter().skip(update_to_do) {\n                self.zone_to_update_mip1[*index] -= 1;\n            }\n\n            for index in indices.iter().take(update_to_do) {\n                self.zone_to_update_mip1[*index] = 0;\n\n                let i = *index as u32 % (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 2) as u32);\n                let j = *index as u32 / (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 2) as u32);\n                let min_x = i * ZONE_SIZE_MIP0 as u32 * 2;\n                let min_y = j * ZONE_SIZE_MIP0 as u32 * 2;\n\n                if min_x < self.phy.width as u32 && min_y < self.phy.height as u32 {\n                    let width = (ZONE_SIZE_MIP0 as u32 * 2).min(self.phy.width as u32 - min_x);\n                    let height = (ZONE_SIZE_MIP0 as u32 * 2).min(self.phy.height as u32 - min_y);\n\n                    self.mipmap_update(\n                        1,\n                        device,\n                        encoder,\n                        &self.texture,\n                        &self.phy.texels,\n                        self.phy.width as u32,\n                        min_x,\n                        min_y,\n                        width,\n                        height,\n                    );\n                }\n            }\n        }\n\n        if update_left > 0 {\n            let mut zone_to_update_mip0 = self\n                .zone_to_update_mip0\n                .iter()\n                .enumerate()\n                .filter(|(_, b)| **b != 0)\n                .collect::<Vec<(usize, &i32)>>();\n\n            let update_to_do = zone_to_update_mip0\n                .len()\n                .min(UPDATE_PER_STEP)\n                .min(update_left);\n            update_left -= update_to_do;\n\n            zone_to_update_mip0.sort_by_key(|(_, i)| **i);\n\n            let indices: Vec<usize> = zone_to_update_mip0\n                .iter()\n                .map(|(index, _)| *index)\n                .collect();\n\n            for index in indices.iter().skip(update_to_do) {\n                self.zone_to_update_mip0[*index] -= 1;\n            }\n\n            for index in indices.iter().take(update_to_do) {\n                self.zone_to_update_mip0[*index] = 0;\n\n                let i = *index as u32 % (self.phy.width as u32 / ZONE_SIZE_MIP0 as u32);\n                let j = *index as u32 / (self.phy.width as u32 / ZONE_SIZE_MIP0 as u32);\n                let min_x = i * ZONE_SIZE_MIP0 as u32;\n                let min_y = j * ZONE_SIZE_MIP0 as u32;\n\n                if min_x < self.phy.width as u32 && min_y < self.phy.height as u32 {\n                    let width = (ZONE_SIZE_MIP0 as u32).min(self.phy.width as u32 - min_x);\n                    let height = (ZONE_SIZE_MIP0 as u32).min(self.phy.height as u32 - min_y);\n\n                    self.mipmap_update(\n                        0,\n                        device,\n                        encoder,\n                        &self.texture,\n                        &self.phy.texels,\n                        self.phy.width as u32,\n                        min_x,\n                        min_y,\n                        width,\n                        height,\n                    );\n                }\n            }\n        }\n        //Update lod texture\n\n        let width = self.phy.width as u32 / ZONE_SIZE_MIP0 as u32;\n        let height = self.phy.height as u32 / ZONE_SIZE_MIP0 as u32;\n\n        let size = width * height;\n        let texture_extent = wgpu::Extent3d {\n            width,\n            height,\n            depth: 1,\n        };\n\n        let mut lod = vec![3.0f32; size as usize];\n        for j in 0..height as usize {\n            for i in 0..width as usize {\n                if self.zone_to_update_mip0[i + j * width as usize] == 0 {\n                    lod[i + j * width as usize] = 0.0;\n                } else if self.zone_to_update_mip1[i / 2 + (j / 2) * width as usize / 2] == 0 {\n                    lod[i + j * width as usize] = 1.0;\n                } else if self.zone_to_update_mip2[i / 4 + (j / 4) * (width as usize / 4)] == 0 {\n                    lod[i + j * width as usize] = 2.0;\n                }\n            }\n        }\n\n        let temp_buf = device\n            .create_buffer_mapped(lod.len(), wgpu::BufferUsage::COPY_SRC)\n            .fill_from_slice(&lod);\n\n        encoder.copy_buffer_to_texture(\n            wgpu::BufferCopyView {\n                buffer: &temp_buf,\n                offset: 0,\n                row_pitch: 4 * width,\n                image_height: height,\n            },\n            wgpu::TextureCopyView {\n                texture: &self.texture_lod,\n                mip_level: 0,\n                array_layer: 0,\n                origin: wgpu::Origin3d {\n                    x: 0.0,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            },\n            texture_extent,\n        );\n    }\n\n    pub fn update_rect(&mut self, min_x: u32, min_y: u32, width: u32, height: u32) {\n        for i in (min_x / ZONE_SIZE_MIP0 as u32)..=(min_x + width) / ZONE_SIZE_MIP0 as u32 {\n            for j in (min_y / ZONE_SIZE_MIP0 as u32)..=(min_y + height) / ZONE_SIZE_MIP0 as u32 {\n                let width = self.phy.width as u32 / ZONE_SIZE_MIP0 as u32;\n\n                let rank = &mut self.zone_to_update_mip2[(i / 4 + (j / 4) * (width / 4)) as usize];\n                if *rank == 0 {\n                    *rank = -1;\n                }\n\n                let rank = &mut self.zone_to_update_mip1[(i / 2 + (j / 2) * (width / 2)) as usize];\n                if *rank == 0 {\n                    *rank = -1;\n                }\n\n                let rank = &mut self.zone_to_update_mip0[(i + j * width) as usize];\n                if *rank == 0 {\n                    *rank = -1;\n                }\n            }\n        }\n\n        self.mip4_to_update = true;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for HeightmapGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/heightmap_helper.rs",
    "content": "use std::collections::HashMap;\nuse std::hash::Hash;\nuse std::hash::Hasher;\n#[derive(Clone, Copy, Debug)]\npub struct Vertex {\n    _pos: [f32; 2],\n    _mip: f32,\n}\n\nimpl PartialEq for Vertex {\n    fn eq(&self, other: &Vertex) -> bool {\n        self.canonicalize() == other.canonicalize()\n    }\n}\n\nimpl Eq for Vertex {}\n\nimpl Vertex {\n    fn canonicalize(&self) -> i128 {\n        (self._pos[0] * 1024.0 * 1024.0).round() as i128\n            + (self._pos[1] * 1024.0 * 1024.0 * 1024.0 * 1024.0).round() as i128\n    }\n}\nimpl Hash for Vertex {\n    fn hash<H>(&self, state: &mut H)\n    where\n        H: Hasher,\n    {\n        self.canonicalize().hash(state);\n    }\n}\n\npub fn z(x: f32, y: f32) -> f32 {\n    // (49.0\n    //     + 30.0 * f32::sin((x + y) / 95.0)\n    //     + 15.0 * (f32::sin(x / 20.0) * f32::cos(y / 45.0 + 1.554))\n    //     + 3.0 * (f32::sin(x / 3.0 + f32::sin(x / 12.0)) * f32::cos(y / 3.3 + 1.94))\n    //     + 0.0 * 1.0 * (f32::sin(x * 3.0) * f32::cos(y * 3.3 + 1.94)))\n    50.0_f32.min(511.0).max(0.0)\n    //    100.0\n    //        + 50.0 * f32::sin(4.0 * x * std::f32::consts::PI / 1024.0)\n    //        + 50.0 * f32::cos(4.0 * y * std::f32::consts::PI / 1024.0)\n\n    // 10.0 * (0.5 + 0.5 * (f32::sin(x * 3.141592 / 2.0) * f32::cos(y * 3.141592 / 2.0)))\n}\n\npub fn create_texels(width: u32, height: u32, t: f32) -> Vec<f32> {\n    let mut texels = Vec::with_capacity((width * height) as usize);\n    for j in 0..height {\n        for i in 0..width {\n            texels.push(z(i as f32 + t, j as f32 + t));\n        }\n    }\n\n    texels\n}\n\npub fn create_vertex_index_rings(hsize: u32) -> (Vec<Vertex>, Vec<u32>) {\n    let nb_square = ((hsize - 1) * (hsize - 1)) as usize;\n    let mut vertex_data = Vec::with_capacity(nb_square * 4);\n    let mut index_data = Vec::with_capacity(nb_square * 4);\n\n    let vertex = |x: f32, y: f32| -> Vertex {\n        Vertex {\n            _pos: [x, y],\n            _mip: 0.0,\n        }\n    };\n\n    for i in 0_u32..hsize {\n        for j in 0_u32..hsize {\n            let index_a: u32 = vertex_data.len() as u32;\n            let a = vertex(i as f32, j as f32);\n            let b = vertex(i as f32 + 1.0, j as f32);\n            let c = vertex(i as f32 + 1.0, j as f32 + 1.0);\n            let d = vertex(i as f32, j as f32 + 1.0);\n            vertex_data.push(a);\n            vertex_data.push(b);\n            vertex_data.push(c);\n            vertex_data.push(d);\n\n            index_data.push(index_a);\n            index_data.push(index_a + 1);\n            index_data.push(index_a + 2);\n            index_data.push(index_a);\n            index_data.push(index_a + 2);\n            index_data.push(index_a + 3);\n        }\n    }\n\n    let vertex = |x: f32, y: f32, m: f32| -> Vertex {\n        Vertex {\n            _pos: [x, y],\n            _mip: m,\n        }\n    };\n    log::trace!(\"{}\", vertex_data.len());\n\n    enum Pass {\n        Step(i32),\n        Trans { from: i32, to: i32 },\n    }\n\n    let mut passes = vec![];\n    passes.push(Pass::Trans { from: 1, to: 2 });\n    passes.extend((0..63).into_iter().map(|_| Pass::Step(2)));\n    passes.push(Pass::Trans { from: 2, to: 4 });\n    passes.extend((0..63).into_iter().map(|_| Pass::Step(4)));\n    passes.push(Pass::Trans { from: 4, to: 8 });\n    passes.extend((0..31).into_iter().map(|_| Pass::Step(8)));\n    passes.push(Pass::Trans { from: 8, to: 16 });\n    passes.extend((0..80).into_iter().map(|_| Pass::Step(16)));\n    //    passes.push(Pass::Trans { from: 16, to: 32 });\n    //    passes.extend((0..50).into_iter().map(|e| Pass::Step(32)));\n\n    fn power_to_exp(i: i32) -> f32 {\n        match i {\n            1 => 0.0,\n            2 => 1.0,\n            4 => 2.0,\n            8 => 3.0,\n            16 => 4.0,\n            _ => 4.0,\n        }\n    }\n\n    let mut start_min = hsize as i32;\n    for pass in passes.iter() {\n        match pass {\n            Pass::Trans { from, to } => {\n                log::trace!(\"Pass::Trans {} {}\", from, to);\n\n                let m = (power_to_exp(*from) + power_to_exp(*to)) / 2.0;\n\n                log::trace!(\"m {}\", m);\n                let i = start_min;\n                let j = start_min;\n                {\n                    let index_a: u32 = vertex_data.len() as u32;\n                    let a = vertex(i as f32, j as f32, m);\n                    let b = vertex(i as f32 + *to as f32, j as f32, m);\n                    let c = vertex(i as f32 + *to as f32, j as f32 + *to as f32, m);\n                    let d = vertex(i as f32, j as f32 + *to as f32, m);\n                    vertex_data.push(a);\n                    vertex_data.push(b);\n                    vertex_data.push(c);\n                    vertex_data.push(d);\n                    index_data.push(index_a);\n                    index_data.push(index_a + 1);\n                    index_data.push(index_a + 2);\n                    index_data.push(index_a);\n                    index_data.push(index_a + 2);\n                    index_data.push(index_a + 3);\n                };\n\n                let i = start_min;\n                for j in (0..=start_min - *to).step_by(*to as usize) {\n                    let index_a: u32 = vertex_data.len() as u32;\n                    let a = vertex(i as f32, j as f32, m);\n                    let b = vertex(i as f32 + *to as f32, j as f32, m);\n                    let c = vertex(i as f32 + *to as f32, j as f32 + *to as f32, m);\n                    let d = vertex(i as f32, j as f32 + *to as f32, m);\n                    let e = vertex(i as f32, j as f32 + *from as f32, m);\n                    vertex_data.push(a);\n                    vertex_data.push(b);\n                    vertex_data.push(c);\n                    vertex_data.push(d);\n                    vertex_data.push(e);\n                    index_data.push(index_a + 4);\n                    index_data.push(index_a);\n                    index_data.push(index_a + 1);\n                    index_data.push(index_a + 4);\n                    index_data.push(index_a + 1);\n                    index_data.push(index_a + 2);\n                    index_data.push(index_a + 4);\n                    index_data.push(index_a + 2);\n                    index_data.push(index_a + 3);\n                }\n\n                let j = start_min;\n                for i in (0..=start_min - *to).step_by(*to as usize) {\n                    let index_a: u32 = vertex_data.len() as u32;\n                    let a = vertex(i as f32, j as f32, m);\n                    let b = vertex(i as f32 + *to as f32, j as f32, m);\n                    let c = vertex(i as f32 + *to as f32, j as f32 + *to as f32, m);\n                    let d = vertex(i as f32, j as f32 + *to as f32, m);\n                    let e = vertex(i as f32 + *from as f32, j as f32, m);\n                    vertex_data.push(a);\n                    vertex_data.push(b);\n                    vertex_data.push(c);\n                    vertex_data.push(d);\n                    vertex_data.push(e);\n                    index_data.push(index_a + 4);\n                    index_data.push(index_a + 1);\n                    index_data.push(index_a + 2);\n\n                    index_data.push(index_a + 4);\n                    index_data.push(index_a + 2);\n                    index_data.push(index_a + 3);\n                    index_data.push(index_a + 4);\n                    index_data.push(index_a + 3);\n                    index_data.push(index_a);\n                }\n                start_min += *to;\n            }\n\n            Pass::Step(step) => {\n                let m = power_to_exp(*step);\n                let mut make_square = |i, j, step| {\n                    let index_a: u32 = vertex_data.len() as u32;\n                    let a = vertex(i as f32, j as f32, m);\n                    let b = vertex(i as f32 + step as f32, j as f32, m);\n                    let c = vertex(i as f32 + step as f32, j as f32 + step as f32, m);\n                    let d = vertex(i as f32, j as f32 + step as f32, m);\n                    {\n                        vertex_data.push(a);\n                        vertex_data.push(b);\n                        vertex_data.push(c);\n                        vertex_data.push(d);\n                        index_data.push(index_a);\n                        index_data.push(index_a + 1);\n                        index_data.push(index_a + 2);\n                        index_data.push(index_a);\n                        index_data.push(index_a + 2);\n                        index_data.push(index_a + 3);\n                    }\n                };\n\n                let j = start_min;\n                for i in (0..=start_min).step_by(*step as usize) {\n                    make_square(i, j, *step);\n                }\n\n                let i = start_min;\n                for j in (0..start_min).step_by(*step as usize) {\n                    make_square(i, j, *step);\n                }\n                start_min += *step;\n            }\n        }\n    }\n\n    log::trace!(\"Passes Done\");\n    log::trace!(\"index_data size  {}\", index_data.len());\n    log::trace!(\"vertex_data size {}\", vertex_data.len());\n\n    {\n        let mut symmetry_vertex_data_left = Vec::new();\n\n        for &vert in vertex_data.iter() {\n            symmetry_vertex_data_left.push(Vertex {\n                _pos: [-1.0 * vert._pos[0], 1.0 * vert._pos[1]],\n                ..vert\n            });\n        }\n\n        let copie: Vec<u32> = index_data.iter().copied().collect();\n        let mut symmetry_index_data_left: Vec<u32> = copie\n            .chunks(3)\n            .into_iter()\n            .flat_map(|e| vec![e[1], e[0], e[2]])\n            .map(|i| i + vertex_data.len() as u32)\n            .collect();\n\n        for e in symmetry_index_data_left\n            .chunks_mut(6)\n            .take((hsize * hsize) as usize)\n        {\n            e[1] = e[5];\n            e[3] = e[0];\n        }\n\n        let mut symmetry_vertex_data_down = Vec::new();\n\n        for &vert in vertex_data.iter() {\n            symmetry_vertex_data_down.push(Vertex {\n                _pos: [1.0 * vert._pos[0], -1.0 * vert._pos[1]],\n                ..vert\n            });\n        }\n\n        let copie: Vec<u32> = index_data.iter().copied().collect();\n        let mut symmetry_index_data_down: Vec<u32> = copie\n            .chunks(3)\n            .into_iter()\n            .flat_map(|e| vec![e[1], e[0], e[2]])\n            .map(|i| i + 2 * vertex_data.len() as u32)\n            .collect();\n\n        for e in symmetry_index_data_down\n            .chunks_mut(6)\n            .take((hsize * hsize) as usize)\n        {\n            e[1] = e[5];\n            e[3] = e[0];\n        }\n\n        let mut symmetry_vertex_data_down_and_left = Vec::new();\n\n        for &vert in vertex_data.iter() {\n            symmetry_vertex_data_down_and_left.push(Vertex {\n                _pos: [-1.0 * vert._pos[0], -1.0 * vert._pos[1]],\n                ..vert\n            });\n        }\n\n        let copie: Vec<u32> = index_data.iter().copied().collect();\n        let symmetry_index_data_down_and_left: Vec<u32> = copie\n            .into_iter()\n            .map(|i| i + 3 * vertex_data.len() as u32)\n            .collect();\n\n        vertex_data.extend(symmetry_vertex_data_left);\n        index_data.extend(symmetry_index_data_left);\n        vertex_data.extend(symmetry_vertex_data_down);\n        index_data.extend(symmetry_index_data_down);\n        vertex_data.extend(symmetry_vertex_data_down_and_left);\n        index_data.extend(symmetry_index_data_down_and_left);\n    }\n\n    log::trace!(\"Symmetry Done\");\n    log::trace!(\"index_data size  {}\", index_data.len());\n    log::trace!(\"vertex_data size {}\", vertex_data.len());\n\n    let (vertex_data, index_data) = optimize_vertex_index(vertex_data, index_data);\n\n    (vertex_data, index_data)\n}\n\npub fn optimize_vertex_index(\n    vertex_data: Vec<Vertex>,\n    mut index_data: Vec<u32>,\n) -> (Vec<Vertex>, Vec<u32>) {\n    let start = std::time::Instant::now();\n\n    log::trace!(\"Before Optimisation\");\n    log::trace!(\"index_data size  {}\", index_data.len());\n    log::trace!(\"vertex_data size {}\", vertex_data.len());\n    let mut new_vertex_data: Vec<Vertex> = Vec::new();\n\n    let mut map: HashMap<Vertex, Option<usize>> = HashMap::new();\n\n    for v in &vertex_data {\n        map.insert(v.clone(), None);\n    }\n\n    for i in index_data.iter_mut() {\n        let v = &vertex_data[*i as usize];\n\n        if let Some(position) = map.get(v).unwrap() {\n            *i = *position as u32;\n        } else {\n            new_vertex_data.push(v.clone());\n            let new_index = new_vertex_data.len() - 1;\n            map.insert(v.clone(), Some(new_index));\n            *i = new_index as u32;\n        }\n    }\n\n    log::trace!(\"Optimisation Done\");\n    log::trace!(\"index_data size  {}\", index_data.len());\n    log::trace!(\"vertex_data size {}\", new_vertex_data.len());\n    log::trace!(\"Optimisation took {}us\", start.elapsed().as_micros());\n\n    (new_vertex_data, index_data)\n}\n"
  },
  {
    "path": "src/gpu_obj/imgui_wgpu.rs",
    "content": "use imgui::{Context, DrawCmd::Elements, DrawIdx, DrawList, DrawVert, TextureId, Textures, Ui};\nuse std::mem::size_of;\nuse wgpu::*;\n\npub type RendererResult<T> = Result<T, RendererError>;\n\n#[derive(Clone, Debug)]\npub enum RendererError {\n    BadTexture(TextureId),\n}\n\nfn get_program_link() -> (&'static str, &'static str) {\n    ((\"./src/shader/imgui.vert\"), (\"./src/shader/imgui.frag\"))\n}\n\n/// A container for a bindable texture to be used internally.\nstruct Texture {\n    bind_group: BindGroup,\n}\n\nimpl Texture {\n    /// Creates a new imgui texture from a wgpu texture.\n    fn new(texture: wgpu::Texture, layout: &BindGroupLayout, device: &Device) -> Self {\n        // Extract the texture view.\n        let view = texture.create_default_view();\n\n        // Create the texture sampler.\n        let sampler = device.create_sampler(&SamplerDescriptor {\n            address_mode_u: AddressMode::ClampToEdge,\n            address_mode_v: AddressMode::ClampToEdge,\n            address_mode_w: AddressMode::ClampToEdge,\n            mag_filter: FilterMode::Linear,\n            min_filter: FilterMode::Linear,\n            mipmap_filter: FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: CompareFunction::Always,\n        });\n\n        // Create the texture bind group from the layout.\n        let bind_group = device.create_bind_group(&BindGroupDescriptor {\n            layout,\n            bindings: &[\n                Binding {\n                    binding: 0,\n                    resource: BindingResource::TextureView(&view),\n                },\n                Binding {\n                    binding: 1,\n                    resource: BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        Texture { bind_group }\n    }\n}\n\n#[allow(dead_code)]\npub struct Renderer {\n    pipeline: RenderPipeline,\n    uniform_buffer: Buffer,\n    uniform_bind_group: BindGroup,\n    textures: Textures<Texture>,\n    texture_layout: BindGroupLayout,\n    clear_color: Option<Color>,\n}\n\nimpl Renderer {\n    /// Create an entirely new imgui wgpu renderer.\n    pub fn new(\n        imgui: &mut Context,\n        device: &mut Device,\n        queue: &mut Queue,\n        format: TextureFormat,\n        clear_color: Option<Color>,\n    ) -> Renderer {\n        let (vs_code, fs_code) = get_program_link();\n        let vs_raw = super::glsl_compiler::load(vs_code).unwrap();\n        let fs_raw = super::glsl_compiler::load(fs_code).unwrap();\n        Self::new_impl(imgui, device, queue, format, clear_color, vs_raw, fs_raw)\n    }\n\n    /// Create an entirely new imgui wgpu renderer.\n    fn new_impl(\n        imgui: &mut Context,\n        device: &mut Device,\n        queue: &mut Queue,\n        format: TextureFormat,\n        clear_color: Option<Color>,\n        vs_raw: Vec<u32>,\n        fs_raw: Vec<u32>,\n    ) -> Renderer {\n        // Load shaders.\n        let vs_module = device.create_shader_module(&vs_raw);\n        let fs_module = device.create_shader_module(&fs_raw);\n\n        // Create the uniform matrix buffer.\n        let size = 64;\n        let uniform_buffer = device.create_buffer(&BufferDescriptor {\n            size,\n            usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST,\n        });\n\n        // Create the uniform matrix buffer bind group layout.\n        let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n            bindings: &[BindGroupLayoutBinding {\n                binding: 0,\n                visibility: wgpu::ShaderStage::VERTEX,\n                ty: BindingType::UniformBuffer { dynamic: false },\n            }],\n        });\n\n        // Create the uniform matrix buffer bind group.\n        let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor {\n            layout: &uniform_layout,\n            bindings: &[Binding {\n                binding: 0,\n                resource: BindingResource::Buffer {\n                    buffer: &uniform_buffer,\n                    range: 0..size,\n                },\n            }],\n        });\n\n        // Create the texture layout for further usage.\n        let texture_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {\n            bindings: &[\n                BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: TextureViewDimension::D2,\n                    },\n                },\n                BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: BindingType::Sampler,\n                },\n            ],\n        });\n\n        // Create the render pipeline layout.\n        let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {\n            bind_group_layouts: &[&uniform_layout, &texture_layout],\n        });\n\n        // Create the render pipeline.\n        let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(RasterizationStateDescriptor {\n                front_face: FrontFace::Cw,\n                cull_mode: CullMode::None,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: PrimitiveTopology::TriangleList,\n            color_states: &[ColorStateDescriptor {\n                format,\n                color_blend: BlendDescriptor {\n                    src_factor: BlendFactor::SrcAlpha,\n                    dst_factor: BlendFactor::OneMinusSrcAlpha,\n                    operation: BlendOperation::Add,\n                },\n                alpha_blend: BlendDescriptor {\n                    src_factor: BlendFactor::OneMinusDstAlpha,\n                    dst_factor: BlendFactor::One,\n                    operation: BlendOperation::Add,\n                },\n                write_mask: ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: IndexFormat::Uint16,\n            vertex_buffers: &[VertexBufferDescriptor {\n                stride: size_of::<DrawVert>() as BufferAddress,\n                step_mode: InputStepMode::Vertex,\n                attributes: &[\n                    VertexAttributeDescriptor {\n                        format: VertexFormat::Float2,\n                        shader_location: 0,\n                        offset: 0,\n                    },\n                    VertexAttributeDescriptor {\n                        format: VertexFormat::Float2,\n                        shader_location: 1,\n                        offset: 8,\n                    },\n                    VertexAttributeDescriptor {\n                        format: VertexFormat::Uint,\n                        shader_location: 2,\n                        offset: 16,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n\n        let mut renderer = Renderer {\n            pipeline,\n            uniform_buffer,\n            uniform_bind_group,\n            textures: Textures::new(),\n            texture_layout,\n            clear_color,\n        };\n\n        // Immediately load the fon texture to the GPU.\n        renderer.reload_font_texture(imgui, device, queue);\n\n        renderer\n    }\n\n    /// Render the current imgui frame.\n    pub fn render<'a>(\n        &mut self,\n        ui: Ui<'a>,\n        device: &Device,\n        encoder: &mut CommandEncoder,\n        view: &TextureView,\n    ) -> RendererResult<()> {\n        let draw_data = ui.render();\n        let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0];\n        let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1];\n        // If the render area is <= 0, exit here and now.\n        if !(fb_width > 0.0 && fb_height > 0.0) {\n            return Ok(());\n        }\n\n        let width = draw_data.display_size[0];\n        let height = draw_data.display_size[1];\n\n        // Create and update the transform matrix for the current frame.\n        // This is required to adapt to vulkan coordinates.\n        let matrix = [\n            [2.0 / width, 0.0, 0.0, 0.0],\n            [0.0, 2.0 / height as f32, 0.0, 0.0],\n            [0.0, 0.0, -1.0, 0.0],\n            [-1.0, -1.0, 0.0, 1.0],\n        ];\n        self.update_uniform_buffer(device, encoder, &matrix);\n\n        // Start a new renderpass and prepare it properly.\n        let mut rpass = encoder.begin_render_pass(&RenderPassDescriptor {\n            color_attachments: &[RenderPassColorAttachmentDescriptor {\n                attachment: &view,\n                resolve_target: None,\n                load_op: match self.clear_color {\n                    Some(_) => LoadOp::Clear,\n                    _ => LoadOp::Load,\n                },\n                store_op: StoreOp::Store,\n                clear_color: self.clear_color.unwrap_or(Color {\n                    r: 0.0,\n                    g: 0.0,\n                    b: 0.0,\n                    a: 1.0,\n                }),\n            }],\n            depth_stencil_attachment: None,\n        });\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &self.uniform_bind_group, &[]);\n        // Execute all the imgui render work.\n        for draw_list in draw_data.draw_lists() {\n            self.render_draw_list(\n                device,\n                &mut rpass,\n                &draw_list,\n                draw_data.display_pos,\n                draw_data.framebuffer_scale,\n            )?;\n        }\n\n        Ok(())\n    }\n\n    /// Render a given `DrawList` from imgui onto a wgpu frame.\n    fn render_draw_list<'render>(\n        &mut self,\n        device: &Device,\n        rpass: &mut RenderPass<'render>,\n        draw_list: &DrawList,\n        clip_off: [f32; 2],\n        clip_scale: [f32; 2],\n    ) -> RendererResult<()> {\n        let mut start = 0;\n\n        // Make sure the current buffers are uploaded to the GPU.\n        let vertex_buffer = self.upload_vertex_buffer(device, draw_list.vtx_buffer());\n        let index_buffer = self.upload_index_buffer(device, draw_list.idx_buffer());\n\n        // Make sure the current buffers are attached to the render pass.\n        rpass.set_index_buffer(&index_buffer, 0);\n        rpass.set_vertex_buffers(0, &[(&vertex_buffer, 0)]);\n        for cmd in draw_list.commands() {\n            match cmd {\n                Elements { count, cmd_params } => {\n                    let clip_rect = [\n                        (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0],\n                        (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1],\n                        (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0],\n                        (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1],\n                    ];\n\n                    // Set the current texture bind group on the renderpass.\n                    let texture_id = cmd_params.texture_id.into();\n                    let tex = self\n                        .textures\n                        .get(texture_id)\n                        .ok_or_else(|| RendererError::BadTexture(texture_id))?;\n                    rpass.set_bind_group(1, &tex.bind_group, &[]);\n\n                    // Set scissors on the renderpass.\n                    let scissors = (\n                        clip_rect[0].max(0.0).floor() as u32,\n                        clip_rect[1].max(0.0).floor() as u32,\n                        (clip_rect[2] - clip_rect[0]).abs().ceil() as u32,\n                        (clip_rect[3] - clip_rect[1]).abs().ceil() as u32,\n                    );\n                    rpass.set_scissor_rect(scissors.0, scissors.1, scissors.2, scissors.3);\n\n                    // Draw the current batch of vertices with the renderpass.\n                    let end = start + count as u32;\n                    rpass.draw_indexed(start..end, 0, 0..1);\n                    start = end;\n                }\n                _ => {}\n            }\n        }\n        Ok(())\n    }\n\n    /// Updates the current uniform buffer containing the transform matrix.\n    fn update_uniform_buffer(\n        &mut self,\n        device: &Device,\n        encoder: &mut CommandEncoder,\n        matrix: &[[f32; 4]; 4],\n    ) {\n        // Create a new buffer.\n        let buffer = device\n            .create_buffer_mapped(16, BufferUsage::COPY_SRC)\n            .fill_from_slice(\n                matrix\n                    .iter()\n                    .flatten()\n                    .map(|f| *f)\n                    .collect::<Vec<f32>>()\n                    .as_slice(),\n            );\n        // Copy the new buffer to the real buffer.\n        encoder.copy_buffer_to_buffer(&buffer, 0, &self.uniform_buffer, 0, 64);\n    }\n\n    /// Upload the vertex buffer to the gPU.\n    fn upload_vertex_buffer(&mut self, device: &Device, vertices: &[DrawVert]) -> Buffer {\n        device\n            .create_buffer_mapped(vertices.len(), BufferUsage::VERTEX)\n            .fill_from_slice(vertices)\n    }\n\n    /// Upload the index buffer to the GPU.\n    fn upload_index_buffer(&mut self, device: &Device, indices: &[DrawIdx]) -> Buffer {\n        device\n            .create_buffer_mapped(indices.len(), BufferUsage::INDEX)\n            .fill_from_slice(indices)\n    }\n\n    /// Updates the texture on the GPU corresponding to the current imgui font atlas.\n    ///\n    /// This has to be called after loading a font.\n    pub fn reload_font_texture(\n        &mut self,\n        imgui: &mut Context,\n        device: &mut Device,\n        queue: &mut Queue,\n    ) {\n        let mut atlas = imgui.fonts();\n        let handle = atlas.build_rgba32_texture();\n        let font_texture_id =\n            self.upload_font_texture(device, queue, &handle.data, handle.width, handle.height);\n        atlas.tex_id = font_texture_id;\n    }\n\n    /// Creates and uploads a new wgpu texture made from the imgui font atlas.\n    fn upload_font_texture(\n        &mut self,\n        device: &mut Device,\n        queue: &mut Queue,\n        data: &[u8],\n        width: u32,\n        height: u32,\n    ) -> TextureId {\n        // Create the wgpu texture.\n        let texture = device.create_texture(&TextureDescriptor {\n            size: Extent3d {\n                width,\n                height,\n                depth: 1,\n            },\n            array_layer_count: 1,\n            mip_level_count: 1,\n            sample_count: 1,\n            dimension: TextureDimension::D2,\n            format: TextureFormat::Rgba8Unorm,\n            usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST,\n        });\n\n        // Upload the actual data to a wgpu buffer.\n        let bytes = data.len();\n        let buffer = device\n            .create_buffer_mapped(bytes, BufferUsage::COPY_SRC)\n            .fill_from_slice(data);\n\n        // Make sure we have an active encoder.\n        let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { todo: 0 });\n\n        // Schedule a copy from the buffer to the texture.\n        encoder.copy_buffer_to_texture(\n            BufferCopyView {\n                buffer: &buffer,\n                offset: 0,\n                row_pitch: bytes as u32 / height,\n                image_height: height,\n            },\n            TextureCopyView {\n                texture: &texture,\n                mip_level: 0,\n                array_layer: 0,\n                origin: Origin3d {\n                    x: 0.0,\n                    y: 0.0,\n                    z: 0.0,\n                },\n            },\n            Extent3d {\n                width,\n                height,\n                depth: 1,\n            },\n        );\n\n        // Resolve the actual copy process.\n        queue.submit(&[encoder.finish()]);\n\n        let texture = Texture::new(texture, &self.texture_layout, device);\n        self.textures.insert(texture)\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/line.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat};\n\npub struct LineGpu {\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n}\n\nimpl LineGpu {\n    pub fn new(\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n    ) -> Self {\n        log::trace!(\"LineGpu new\");\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();;\n\n        LineGpu {\n            instance_buf,\n            instance_count: 0,\n            pipeline,\n        }\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout],\n        });\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/line.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/line.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format: format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[wgpu::VertexBufferDescriptor {\n                stride: (4 * (2 + 2 + 2)) as wgpu::BufferAddress,\n                step_mode: wgpu::InputStepMode::Instance,\n                attributes: &[\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 0,\n                        shader_location: 0,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 4 * 2,\n                        shader_location: 1,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 4,\n                        shader_location: 2,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 5,\n                        shader_location: 3,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"LineGpu render\");\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.draw(0..4, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"LineGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 6;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for LineGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(device, main_bind_group_layout, format) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/mod.rs",
    "content": "pub mod arrow_gpu;\npub mod blit_texture;\npub mod explosion;\npub mod glsl_compiler;\npub mod gpu;\npub mod health_bar;\npub mod heightmap_gpu;\nmod heightmap_helper;\npub mod imgui_wgpu;\npub mod line;\npub mod model_gpu;\npub mod post_fx;\npub mod post_fxaa;\npub mod texture_view_bicopy;\npub mod trait_gpu;\npub mod unit_icon;\npub mod water;\n"
  },
  {
    "path": "src/gpu_obj/model_gpu.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat};\n\npub struct ModelGpu {\n    pub instance_attr_cpu_buf: Vec<f32>,\n    vertex_buf: wgpu::Buffer,\n    index_buf: wgpu::Buffer,\n    index_count: usize,\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n}\n\nimpl ModelGpu {\n    pub fn new(\n        triangle_list: &model::TriangleList,\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n    ) -> Self {\n        log::trace!(\"ModelGpu new\");\n        // Create the vertex and index buffers\n        let model::TriangleList {\n            vertex_data,\n            index_data,\n        } = triangle_list;\n        let vertex_buf = device\n            .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(&vertex_data);\n\n        let index_buf = device\n            .create_buffer_mapped(index_data.len(), wgpu::BufferUsage::INDEX)\n            .fill_from_slice(&index_data);\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();;\n\n        ModelGpu {\n            instance_attr_cpu_buf: Vec::new(),\n            vertex_buf,\n            index_buf,\n            index_count: index_data.len(),\n            instance_buf,\n            instance_count: positions.len() as u32 / 3,\n            pipeline,\n        }\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout],\n        });\n        let vertex_size = std::mem::size_of::<model::Vertex>();\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/cube_instanced.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/cube_instanced.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Ccw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleList,\n            color_states: &[\n                wgpu::ColorStateDescriptor {\n                    format: format,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n                wgpu::ColorStateDescriptor {\n                    format: wgpu::TextureFormat::Rgba32Float,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n                wgpu::ColorStateDescriptor {\n                    format: wgpu::TextureFormat::Rg16Float,\n                    color_blend: wgpu::BlendDescriptor::REPLACE,\n                    alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                    write_mask: wgpu::ColorWrite::ALL,\n                },\n            ],\n            depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {\n                format: wgpu::TextureFormat::Depth32Float,\n                depth_write_enabled: true,\n                depth_compare: wgpu::CompareFunction::Less,\n                stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_read_mask: 0,\n                stencil_write_mask: 0,\n            }),\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[\n                wgpu::VertexBufferDescriptor {\n                    stride: vertex_size as wgpu::BufferAddress,\n                    step_mode: wgpu::InputStepMode::Vertex,\n                    attributes: &[\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float4,\n                            offset: 0,\n                            shader_location: 0,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float3,\n                            offset: 4 * 4,\n                            shader_location: 1,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float2,\n                            offset: 4 * 7,\n                            shader_location: 2,\n                        },\n                    ],\n                },\n                wgpu::VertexBufferDescriptor {\n                    stride: (4 * 8) as wgpu::BufferAddress,\n                    step_mode: wgpu::InputStepMode::Instance,\n                    attributes: &[\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float3,\n                            offset: 0,\n                            shader_location: 3,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float3,\n                            offset: 4 * 3,\n                            shader_location: 4,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float,\n                            offset: 4 * 6,\n                            shader_location: 5,\n                        },\n                        wgpu::VertexAttributeDescriptor {\n                            format: wgpu::VertexFormat::Float,\n                            offset: 4 * 7,\n                            shader_location: 6,\n                        },\n                    ],\n                },\n            ],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"ModelGpu render\");\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.set_index_buffer(&self.index_buf, 0);\n            rpass.set_vertex_buffers(0, &[(&self.vertex_buf, 0), (&self.instance_buf, 0)]);\n            rpass.draw_indexed(0..self.index_count as u32, 0, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance_dirty(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"ModelGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 8;\n    }\n\n    pub fn update_instance_dirty_own_buffer(&mut self, device: &wgpu::Device) {\n        log::trace!(\"ModelGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(self.instance_attr_cpu_buf.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(&self.instance_attr_cpu_buf);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = self.instance_attr_cpu_buf.len() as u32 / 8;\n    }\n\n    pub fn update_instance(\n        &mut self,\n        instance_attr: &[f32],\n        device: &wgpu::Device,\n        encoder: &mut wgpu::CommandEncoder,\n    ) {\n        log::trace!(\"ModelGpu update_instance\");\n\n        for (i, chunk) in instance_attr.chunks(1800).enumerate() {\n            let temp_buf = device\n                .create_buffer_mapped(\n                    chunk.len(),\n                    wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_SRC,\n                )\n                .fill_from_slice(chunk);\n\n            encoder.copy_buffer_to_buffer(\n                &temp_buf,\n                0,\n                &self.instance_buf,\n                i as u64 * 1800_u64,\n                chunk.len() as u64 * 4,\n            );\n        }\n        // let temp_buf = device\n        //     .create_buffer_mapped(\n        //         instance_attr.len(),\n        //         wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_SRC,\n        //     )\n        //     .fill_from_slice(instance_attr);\n\n        // encoder.copy_buffer_to_buffer(\n        //     &temp_buf,\n        //     0,\n        //     &self.instance_buf,\n        //     0,\n        //     instance_attr.len() as u64 * 4,\n        // );\n\n        self.instance_count = instance_attr.len() as u32 / 8;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for ModelGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(device, main_bind_group_layout, format) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/post_fx.rs",
    "content": "use super::glsl_compiler;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView};\n\npub struct PostFx {\n    pipeline: wgpu::RenderPipeline,\n    bind_group: BindGroup,\n    bind_group_layout: BindGroupLayout,\n    sampler: wgpu::Sampler,\n}\n\nimpl PostFx {\n    pub fn new(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n        position_att_view: &TextureView,\n    ) -> Self {\n        // Create pipeline layout\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Nearest,\n            min_filter: wgpu::FilterMode::Nearest,\n            mipmap_filter: wgpu::FilterMode::Nearest,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&position_att_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();\n        PostFx {\n            pipeline,\n            bind_group_layout,\n            bind_group,\n            sampler,\n        }\n    }\n\n    pub fn update_pos_att_view(&mut self, device: &Device, position_att_view: &TextureView) {\n        self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &self.bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&position_att_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&self.sampler),\n                },\n            ],\n        });\n    }\n\n    fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout],\n        });\n\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/post.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/post_ui.frag\")?;\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, device: &Device, main_bind_group: &BindGroup) {\n        log::trace!(\"PostFx render\");\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &main_bind_group, &[]);\n\n        rpass.set_bind_group(1, &self.bind_group, &[]);\n        rpass.draw(0..4, 0..1);\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for PostFx {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/post_fxaa.rs",
    "content": "use super::glsl_compiler;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView};\n\npub struct PostFxaa {\n    pipeline: wgpu::RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    bind_group: BindGroup,\n}\n\nimpl PostFxaa {\n    pub fn new(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n        last_pass_view: &TextureView,\n    ) -> Self {\n        // Create pipeline layout\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&last_pass_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();\n        PostFxaa {\n            pipeline,\n            bind_group_layout,\n            bind_group,\n        }\n    }\n\n    pub fn update_last_pass_view(&mut self, device: &Device, last_pass_view: &TextureView) {\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &self.bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&last_pass_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n    }\n\n    fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout],\n        });\n\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/post.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/post_fxaa.frag\")?;\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format,\n                color_blend: wgpu::BlendDescriptor::REPLACE,\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, device: &Device, main_bind_group: &BindGroup) {\n        log::trace!(\"PostFxaa render\");\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &main_bind_group, &[]);\n        rpass.set_bind_group(1, &self.bind_group, &[]);\n        rpass.draw(0..4, 0..1);\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for PostFxaa {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/texture_view_bicopy.rs",
    "content": "use super::glsl_compiler;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView};\n\npub struct TextureViewBiCopy {\n    pipeline: wgpu::RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    bind_group: BindGroup,\n    sampler: wgpu::Sampler,\n}\n\nimpl TextureViewBiCopy {\n    pub fn new(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n        last_pass_view: &TextureView,\n    ) -> Self {\n        // Create pipeline layout\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&last_pass_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n            ],\n        });\n\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();\n        TextureViewBiCopy {\n            pipeline,\n            bind_group_layout,\n            sampler,\n            bind_group,\n        }\n    }\n\n    pub fn update_last_pass_view(&mut self, device: &Device, last_pass_view: &TextureView) {\n        self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: &self.bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(&last_pass_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&self.sampler),\n                },\n            ],\n        });\n    }\n\n    fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout],\n        });\n\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/post.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/post_bicopy.frag\")?;\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format,\n                color_blend: wgpu::BlendDescriptor::REPLACE,\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, device: &Device, main_bind_group: &BindGroup) {\n        log::trace!(\"TextureViewBiCopy render\");\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &main_bind_group, &[]);\n        rpass.set_bind_group(1, &self.bind_group, &[]);\n        rpass.draw(0..4, 0..1);\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for TextureViewBiCopy {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/trait_gpu.rs",
    "content": "use wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat};\n\npub trait TraitGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    );\n}\n"
  },
  {
    "path": "src/gpu_obj/unit_icon.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat};\n\npub struct UnitIconGpu {\n    instance_buf: wgpu::Buffer,\n    instance_count: u32,\n    pipeline: wgpu::RenderPipeline,\n}\n\nimpl UnitIconGpu {\n    pub fn new(\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n    ) -> Self {\n        log::trace!(\"UnitIconGpu new\");\n\n        let positions: Vec<f32> = Vec::new();\n\n        let instance_buf = device\n            .create_buffer_mapped(\n                positions.len(),\n                wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST,\n            )\n            .fill_from_slice(&positions);\n\n        let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();;\n\n        UnitIconGpu {\n            instance_buf,\n            instance_count: 0,\n            pipeline,\n        }\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout],\n        });\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/unit_icon.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/unit_icon.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Cw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format: format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: None,\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[wgpu::VertexBufferDescriptor {\n                stride: (4 * (4)) as wgpu::BufferAddress,\n                step_mode: wgpu::InputStepMode::Instance,\n                attributes: &[\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float2,\n                        offset: 0,\n                        shader_location: 0,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 2,\n                        shader_location: 1,\n                    },\n                    wgpu::VertexAttributeDescriptor {\n                        format: wgpu::VertexFormat::Float,\n                        offset: 4 * 3,\n                        shader_location: 2,\n                    },\n                ],\n            }],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"UnitIconGpu render\");\n        if self.instance_count > 0 {\n            rpass.set_pipeline(&self.pipeline);\n            rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]);\n            rpass.set_bind_group(0, main_bind_group, &[]);\n            rpass.draw(0..4, 0..self.instance_count as u32);\n        }\n    }\n\n    pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) {\n        log::trace!(\"UnitIconGpu update_instance\");\n        let temp_buf = device\n            .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX)\n            .fill_from_slice(instance_attr);\n\n        std::mem::replace(&mut self.instance_buf, temp_buf);\n        self.instance_count = instance_attr.len() as u32 / 4;\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for UnitIconGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(device, main_bind_group_layout, format) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/gpu_obj/water.rs",
    "content": "use super::glsl_compiler;\nuse crate::model;\nuse wgpu::Device;\nuse wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView};\n\npub struct WaterGpu {\n    pipeline: wgpu::RenderPipeline,\n    bind_group_layout: BindGroupLayout,\n    bind_group: BindGroup,\n}\n\nimpl WaterGpu {\n    pub fn new(\n        device: &Device,\n        format: TextureFormat,\n        main_bind_group_layout: &BindGroupLayout,\n        last_pass_view: &TextureView,\n        current_position_att: &TextureView,\n    ) -> Self {\n        log::trace!(\"WaterGpu new\");\n\n        let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {\n            bindings: &[\n                wgpu::BindGroupLayoutBinding {\n                    binding: 0,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 1,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 2,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::SampledTexture {\n                        multisampled: false,\n                        dimension: wgpu::TextureViewDimension::D2,\n                    },\n                },\n                wgpu::BindGroupLayoutBinding {\n                    binding: 3,\n                    visibility: wgpu::ShaderStage::FRAGMENT,\n                    ty: wgpu::BindingType::Sampler,\n                },\n            ],\n        });\n\n        let bind_group = Self::create_bind_group(\n            device,\n            &bind_group_layout,\n            last_pass_view,\n            current_position_att,\n        );\n\n        let pipeline =\n            Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format)\n                .unwrap();;\n        WaterGpu {\n            pipeline,\n            bind_group,\n            bind_group_layout,\n        }\n    }\n\n    pub fn update_bind_group(\n        &mut self,\n        device: &Device,\n        last_pass_view: &TextureView,\n        current_position_att: &TextureView,\n    ) {\n        self.bind_group = Self::create_bind_group(\n            device,\n            &self.bind_group_layout,\n            last_pass_view,\n            current_position_att,\n        );\n    }\n\n    pub fn create_bind_group(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        last_pass_view: &TextureView,\n        current_position_att: &TextureView,\n    ) -> BindGroup {\n        let sampler = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        let sampler_pos_att = device.create_sampler(&wgpu::SamplerDescriptor {\n            address_mode_u: wgpu::AddressMode::ClampToEdge,\n            address_mode_v: wgpu::AddressMode::ClampToEdge,\n            address_mode_w: wgpu::AddressMode::ClampToEdge,\n            mag_filter: wgpu::FilterMode::Linear,\n            min_filter: wgpu::FilterMode::Linear,\n            mipmap_filter: wgpu::FilterMode::Linear,\n            lod_min_clamp: -100.0,\n            lod_max_clamp: 100.0,\n            compare_function: wgpu::CompareFunction::Always,\n        });\n\n        device.create_bind_group(&wgpu::BindGroupDescriptor {\n            layout: bind_group_layout,\n            bindings: &[\n                wgpu::Binding {\n                    binding: 0,\n                    resource: wgpu::BindingResource::TextureView(last_pass_view),\n                },\n                wgpu::Binding {\n                    binding: 1,\n                    resource: wgpu::BindingResource::Sampler(&sampler),\n                },\n                wgpu::Binding {\n                    binding: 2,\n                    resource: wgpu::BindingResource::TextureView(current_position_att),\n                },\n                wgpu::Binding {\n                    binding: 3,\n                    resource: wgpu::BindingResource::Sampler(&sampler_pos_att),\n                },\n            ],\n        })\n    }\n\n    pub fn create_pipeline(\n        device: &Device,\n        bind_group_layout: &BindGroupLayout,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) -> glsl_compiler::Result<wgpu::RenderPipeline> {\n        let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {\n            bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout],\n        });\n        let vertex_size = std::mem::size_of::<model::Vertex>();\n        // Create the render pipeline\n        let vs_bytes = glsl_compiler::load(\"./src/shader/water.vert\")?;\n        let fs_bytes = glsl_compiler::load(\"./src/shader/water.frag\")?;\n\n        let vs_module = device.create_shader_module(&vs_bytes);\n        let fs_module = device.create_shader_module(&fs_bytes);\n        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {\n            layout: &pipeline_layout,\n            vertex_stage: wgpu::ProgrammableStageDescriptor {\n                module: &vs_module,\n                entry_point: \"main\",\n            },\n            fragment_stage: Some(wgpu::ProgrammableStageDescriptor {\n                module: &fs_module,\n                entry_point: \"main\",\n            }),\n            rasterization_state: Some(wgpu::RasterizationStateDescriptor {\n                front_face: wgpu::FrontFace::Ccw,\n                cull_mode: wgpu::CullMode::Back,\n                depth_bias: 0,\n                depth_bias_slope_scale: 0.0,\n                depth_bias_clamp: 0.0,\n            }),\n            primitive_topology: wgpu::PrimitiveTopology::TriangleStrip,\n            color_states: &[wgpu::ColorStateDescriptor {\n                format,\n                color_blend: wgpu::BlendDescriptor {\n                    src_factor: wgpu::BlendFactor::SrcAlpha,\n                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,\n                    operation: wgpu::BlendOperation::Add,\n                },\n                alpha_blend: wgpu::BlendDescriptor::REPLACE,\n                write_mask: wgpu::ColorWrite::ALL,\n            }],\n            depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {\n                format: wgpu::TextureFormat::Depth32Float,\n                depth_write_enabled: true,\n                depth_compare: wgpu::CompareFunction::Less,\n                stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,\n                stencil_read_mask: 0,\n                stencil_write_mask: 0,\n            }),\n            index_format: wgpu::IndexFormat::Uint32,\n            vertex_buffers: &[],\n            sample_count: 1,\n            sample_mask: !0,\n            alpha_to_coverage_enabled: false,\n        });\n        Ok(pipeline)\n    }\n\n    pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) {\n        log::trace!(\"WaterGpu render\");\n        rpass.set_pipeline(&self.pipeline);\n        rpass.set_bind_group(0, &main_bind_group, &[]);\n        rpass.set_bind_group(1, &self.bind_group, &[]);\n        rpass.draw(0..4, 0..4); //floor lwall fwall rwall\n    }\n}\n\nimpl super::trait_gpu::TraitGpu for WaterGpu {\n    fn reload_shader(\n        &mut self,\n        device: &Device,\n        main_bind_group_layout: &BindGroupLayout,\n        format: TextureFormat,\n    ) {\n        match Self::create_pipeline(\n            device,\n            &self.bind_group_layout,\n            main_bind_group_layout,\n            format,\n        ) {\n            Ok(pipeline) => self.pipeline = pipeline,\n            Err(x) => log::error!(\"{}\", x),\n        };\n    }\n}\n"
  },
  {
    "path": "src/heightmap_phy.rs",
    "content": "use na::Vector3;\nuse serde::{Deserialize, Serialize};\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct Data {\n    pub metal_spots: Vec<MetalSpot>,\n}\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct HeightmapPhy {\n    pub texels: Vec<f32>,\n    pub width: usize,\n    pub height: usize,\n    pub data: Data,\n}\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct MetalSpot {\n    metal_per_frame: f32,\n    x: usize,\n    y: usize,\n}\n\ntrait HeightMapPhyUsize {\n    fn z(&self, x: usize, y: usize) -> f32;\n}\n\nimpl HeightMapPhyUsize for HeightmapPhy {\n    fn z(&self, x: usize, y: usize) -> f32 {\n        self.texels[x + y * self.width]\n    }\n}\n\nimpl HeightmapPhy {\n    pub fn new(width: usize, height: usize) -> Self {\n        let mut texels = Vec::with_capacity((width * height) as usize);\n        for j in 0..height {\n            for i in 0..width {\n                texels.push(50.0);\n            }\n        }\n        HeightmapPhy {\n            texels,\n            width,\n            height,\n            data: Data {\n                metal_spots: Vec::new(),\n            },\n        }\n    }\n\n    ///unsafe nearest interpolation\n    #[inline]\n    pub fn z(&self, x: f32, y: f32) -> f32 {\n        let i = x as usize + (y as usize) * self.width as usize;\n        self.texels[i]\n    }\n\n    ///safe nearest interpolation\n    #[inline]\n    pub fn safe_z(&self, x: f32, y: f32) -> f32 {\n        let x = x.max(0.0).min(self.width as f32 - 1.0);\n        let y = y.max(0.0).min(self.height as f32 - 1.0);\n        self.z(x, y)\n    }\n\n    ///safe linear interpolation\n    pub fn z_linear(&self, x: f32, y: f32) -> f32 {\n        let x = x.max(0.0).min(self.width as f32 - 2.0);\n        let y = y.max(0.0).min(self.height as f32 - 2.0);\n        let imin = x.trunc() as usize;\n        let imax = imin + 1;\n        let jmin = y.trunc() as usize;\n        let jmax = self.width as usize * (jmin + 1);\n        let jmin = self.width as usize * jmin;\n\n        let a = self.texels[imin + jmin];\n        let b = self.texels[imax + jmin];\n        let c = self.texels[imax + jmax];\n        let d = self.texels[imin + jmax];\n\n        let z = a * (1.0 - x.fract()) * (1.0 - y.fract())\n            + b * (x.fract()) * (1.0 - y.fract())\n            + c * (x.fract()) * (y.fract())\n            + d * (1.0 - x.fract()) * (y.fract());\n\n        z\n    }\n\n    ///safe normal interpolation\n    pub fn normal(&self, x: f32, y: f32) -> Vector3<f32> {\n        let x = x.max(1.0).min(self.width as f32 - 2.0);\n        let y = y.max(1.0).min(self.height as f32 - 2.0);\n\n        let r = self.z_linear(x + 1.0, y);\n        let l = self.z_linear(x - 1.0, y);\n        let u = self.z_linear(x, y - 1.0);\n        let d = self.z_linear(x, y + 1.0);\n        Vector3::new(l - r, u - d, 2.0).normalize()\n    }\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "mod botdef;\nmod client;\nmod frame;\nmod frame_server;\nmod glsl;\nmod gpu_obj;\nmod heightmap_phy;\nmod manager;\nmod mobile;\nmod model;\nmod moddef;\nmod net_client;\nmod net_server;\nmod procedural_texels;\nmod unit;\n\nmod utils;\nextern crate byteorder;\nextern crate crossbeam_channel;\nextern crate nalgebra as na;\n#[cfg(feature = \"use_shaderc\")]\nextern crate shaderc;\n#[macro_use]\nextern crate typename;\nextern crate base_62;\nextern crate rayon;\nextern crate spin_sleep;\nuse crossbeam_channel::unbounded;\nuse spin_sleep::LoopHelper;\nuse winit::event::Event;\nuse winit::event_loop::ControlFlow;\n#[derive(Debug)]\npub enum ToClient {\n    MapReadAsyncMessage { vec: Vec<f32>, usage: String },\n    NewFrame(frame::Frame),\n    GlobalInfo(manager::GlobalInfo),\n}\n\npub enum EventLoopMsg {\n    Stop,\n}\nuse std::env;\nfn main() {\n    env_logger::init();\n    if let Some(x) = env::args().skip(1).next() {\n        if x == \"compile\" {\n            glsl::compile_all_glsl();\n        }\n    } else {\n        do_the_thing();\n    }\n}\n\nfn do_the_thing() {\n    let (s_to_frame_server, r_to_frame_server) = unbounded::<frame_server::ToFrameServer>();\n    let (s_from_frame_server, r_from_frame_server) = unbounded::<frame_server::FromFrameServer>();\n\n    let frame_server =\n        frame_server::FrameServerCache::spawn(r_to_frame_server, s_from_frame_server);\n\n    let (s_from_client_to_manager, r_from_client_to_manager) = unbounded::<client::FromClient>();\n    let (s_to_client, r_to_client) = unbounded::<ToClient>();\n    let s_to_client_from_manager = s_to_client.clone();\n    let manager = manager::Manager::new(\n        s_to_client_from_manager,\n        s_to_frame_server,\n        r_from_frame_server,\n        r_from_client_to_manager,\n    );\n\n    let (s_to_event_loop, r_to_event_loop) = unbounded::<EventLoopMsg>();\n    let event_loop = winit::event_loop::EventLoop::new();\n    let builder = winit::window::WindowBuilder::new();\n    let window = builder.build(&event_loop).unwrap();\n\n    let mut client = client::App::new(\n        window,\n        s_to_client,\n        r_to_client,\n        s_to_event_loop,\n        s_from_client_to_manager,\n    );\n\n    event_loop.run(move |event, _, control_flow| match event {\n        Event::WindowEvent { .. } => {\n            client.handle_winit_event(&event);\n        }\n        Event::EventsCleared => match r_to_event_loop.try_recv() {\n            Ok(EventLoopMsg::Stop) => {\n                *control_flow = ControlFlow::Exit;\n            }\n            _ => {\n                client.receive();\n                client.render();\n            }\n        },\n        _ => {}\n    });\n}\n"
  },
  {
    "path": "src/manager.rs",
    "content": "use crate::client;\nuse crate::frame;\nuse crate::frame_server;\nuse crate::net_client;\nuse crate::net_server;\nuse crate::ToClient;\nuse crossbeam_channel::{Receiver, Sender};\nuse net_client::NetClient;\nuse net_server::NetServer;\nuse spin_sleep::LoopHelper;\npub struct Manager {}\n\nimpl Manager {\n    pub fn new(\n        s_to_client_from_root_manager: Sender<crate::ToClient>,\n        s_to_frame_server: Sender<frame_server::ToFrameServer>,\n        r_from_frame_server: Receiver<frame_server::FromFrameServer>,\n        r_from_client: Receiver<client::FromClient>,\n    ) -> () {\n        let _ = std::thread::Builder::new()\n            .name(\"manager\".to_string())\n            .spawn(move || {\n                let mut global_info = GlobalInfo {\n                    manager: ManagerInfo {\n                        loop_time: std::time::Duration::from_millis(0),\n                    },\n                    net_client: None,\n                    net_server: None,\n                };\n                let mut net: Net = Net::Offline;\n\n                let frame0 = frame::Frame::new();\n                let _ =\n                    s_to_frame_server.send(frame_server::ToFrameServer::DataToComputeNextFrame(\n                        frame::DataToComputeNextFrame {\n                            old_frame: frame0.clone(),\n                            events: Vec::new(),\n                        },\n                    ));\n                let _ = s_to_client_from_root_manager.send(ToClient::NewFrame(frame0));\n\n                let mut loop_helper = LoopHelper::builder().build_with_target_rate(10.0_f64);\n                loop {\n                    log::trace!(\"loop sleep\");\n                    loop_helper.loop_sleep();\n                    global_info.manager.loop_time = loop_helper.loop_start();\n                    log::trace!(\"receive\");\n                    //Receiving new frame\n                    let mut frame = match r_from_frame_server.recv() {\n                        Ok(frame_server::FromFrameServer::NewFrame(new_frame)) => new_frame,\n                        _ => panic!(\"frame_server disconnected\"),\n                    };\n\n                    //Receiving local player event\n                    let mut player_inputs = Vec::new();\n                    for from_client in r_from_client.try_iter() {\n                        use client::FromClient;\n                        match from_client {\n                            FromClient::PlayerInput(event) => player_inputs.push(event),\n                            FromClient::StartClient(client::StartClient { bind }) => {\n                                net = Net::IsClient(NetClient::new(&bind))\n                            }\n                            FromClient::StartServer(client::StartServer { bind }) => {\n                                net = Net::IsServer(NetServer::new(&bind))\n                            }\n                            FromClient::DisconnectServer => {\n                                if let Net::IsServer(net_server) = &mut net {\n                                    net_server.kill();\n                                    global_info.net_server = None;\n                                    net = Net::Offline;\n                                }\n                            }\n                            FromClient::DisconnectClient => {\n                                if let Net::IsClient(net_client) = &mut net {\n                                    net_client.kill();\n                                    global_info.net_client = None;\n                                    net = Net::Offline;\n                                }\n                            }\n                        }\n                    }\n\n                    //If local is client : Send player events\n                    if let Net::IsClient(net_client) = &mut net {\n                        net_client.send_player_inputs(\n                            player_inputs\n                                .iter()\n                                .filter(|e| match e {\n                                    frame::FrameEventFromPlayer::ReplaceFrame(x) => false,\n                                    _ => true,\n                                })\n                                .map(|e| e.clone())\n                                .collect(),\n                        );\n                    }\n                    //If local is server : Extend with remote players\n                    else if let Net::IsServer(server) = &mut net {\n                        player_inputs.extend(server.collect_remote_players_inputs());\n                    }\n\n                    //Frame is now complete and ready to be sent\n                    let mut data_to_compute_next_frame = frame::DataToComputeNextFrame {\n                        old_frame: frame.clone(),\n                        events: player_inputs,\n                    };\n\n                    //If local is client : Get remote frame (TEMPORARY TOTAL BYPASS OF LOCAL FRAME_SERVER)\n                    if let Net::IsClient(net_client) = &mut net {\n                        data_to_compute_next_frame =\n                            net_client.collect_data_to_compute_next_frame().unwrap();\n\n                        frame = data_to_compute_next_frame.old_frame.clone();\n                    }\n                    //If local is server : Broadcast to remotes\n                    else if let Net::IsServer(server) = &mut net {\n                        server.broadcast_data_to_compute_next_frame(\n                            data_to_compute_next_frame.clone(),\n                        );\n                    }\n\n                    //Sending to local frame_server and local client\n                    let _ = s_to_frame_server.send(\n                        frame_server::ToFrameServer::DataToComputeNextFrame(\n                            data_to_compute_next_frame,\n                        ),\n                    );\n                    let _ = s_to_client_from_root_manager.send(ToClient::NewFrame(frame));\n\n                    //Gathering and sending GlobalInfo\n                    if let Net::IsClient(net_client) = &mut net {\n                        global_info.net_client = Some(net_client.get_info());\n                    } else if let Net::IsServer(server) = &mut net {\n                        global_info.net_server = Some(server.get_info());\n                    }\n                    let _ = s_to_client_from_root_manager.send(ToClient::GlobalInfo(global_info));\n                }\n            });\n    }\n}\n\nenum Net {\n    Offline,\n    IsServer(NetServer),\n    IsClient(NetClient),\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct ManagerInfo {\n    loop_time: std::time::Duration,\n}\n\n///Info about all the components of this program\n#[derive(Debug, Clone, Copy)]\npub struct GlobalInfo {\n    pub manager: ManagerInfo,\n    pub net_server: Option<net_server::NetServerInfo>,\n    pub net_client: Option<net_client::NetClientInfo>,\n}\n"
  },
  {
    "path": "src/mobile.rs",
    "content": "use super::frame::Player;\nuse crate::botdef;\nuse crate::unit;\nuse crate::utils;\nuse na::{Matrix4, Point3, Vector2, Vector3};\nuse serde::{Deserialize, Serialize};\nuse std::path::{Path, PathBuf};\nuse typename::TypeName;\nuse utils::Id;\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub struct ExplosionEvent {\n    pub position: Point3<f32>,\n    pub size: f32,\n    pub life_time: f32,\n}\n\n#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]\npub struct Angle {\n    pub rad: f32,\n}\nimpl Angle {\n    pub fn from(x: f32, y: f32) -> Self {\n        Angle {\n            rad: f32::atan2(y, x),\n        }\n        .modulo()\n    }\n\n    pub fn new(rad: f32) -> Self {\n        Angle { rad }.modulo()\n    }\n\n    pub fn clamp_around(self, other: Angle, cone_angle: Angle) -> Self {\n        let max = other + cone_angle;\n        let min = other - cone_angle;\n        let diff = (other - self);\n        let diff = diff.rad.max(-cone_angle.rad).min(cone_angle.rad);\n        self + Angle::new(diff)\n    }\n\n    pub fn modulo(&self) -> Self {\n        Angle {\n            rad: (self.rad + std::f32::consts::PI).rem_euclid(2.0 * std::f32::consts::PI)\n                - std::f32::consts::PI,\n        }\n    }\n}\n\nimpl std::ops::Add<Angle> for Angle {\n    type Output = Angle;\n\n    fn add(self, rhs: Angle) -> Angle {\n        Angle {\n            rad: (self.rad + rhs.rad),\n        }\n        .modulo()\n    }\n}\n\nimpl std::ops::Sub<Angle> for Angle {\n    type Output = Angle;\n\n    fn sub(self, rhs: Angle) -> Angle {\n        Angle {\n            rad: (self.rad - rhs.rad),\n        }\n        .modulo()\n    }\n}\n\nimpl std::ops::Neg for Angle {\n    type Output = Angle;\n\n    fn neg(self) -> Angle {\n        Angle {\n            rad: (self.rad + std::f32::consts::PI),\n        }\n        .modulo()\n    }\n}\n\nimpl From<Vector2<f32>> for Angle {\n    fn from(dir: Vector2<f32>) -> Self {\n        Self::from(dir.x, dir.y)\n    }\n}\n\nimpl Into<Vector2<f32>> for Angle {\n    fn into(self) -> Vector2<f32> {\n        Vector2::new(f32::cos(self.rad), f32::sin(self.rad))\n    }\n}\n\nimpl From<(f32, f32)> for Angle {\n    fn from(dir: (f32, f32)) -> Self {\n        Self::from(dir.0, dir.1)\n    }\n}\n\nimpl From<f32> for Angle {\n    fn from(rad: f32) -> Self {\n        Self::new(rad)\n    }\n}\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub enum Command {\n    None,\n    Build(Id<KBot>),\n    Repair(Id<KBot>),\n}\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub struct KBot {\n    pub id: Id<KBot>,\n    pub position: Point3<f32>,\n    pub speed: Vector3<f32>,\n    pub dir: Vector3<f32>,\n    pub angle: Angle,\n    pub angular_velocity: f32,\n    pub up: Vector3<f32>,\n    pub move_target: Option<Point3<f32>>,\n    pub current_command: Command,\n    pub life: i32,\n    pub con_completed: f32,\n    pub player_id: Id<Player>,\n    pub team: u8,\n    pub grounded: bool,\n    pub frame_last_shot: i32,\n    pub weapon0_dir: Vector3<f32>,\n    pub wheel0_angle: f32,\n    pub reload_frame_count: i32,\n    pub botdef_id: Id<botdef::BotDef>,\n}\n\nimpl KBot {\n    pub fn new(position: Point3<f32>, botdef: &botdef::BotDef, player_id: Id<Player>) -> Self {\n        KBot {\n            position,\n            speed: Vector3::new(0.0, 0.0, 0.0),\n            player_id,\n            team: 0,\n            dir: Vector3::new(1.0, 0.0, 0.0),\n            angle: Angle::new(0.0),\n            up: Vector3::new(0.0, 0.0, 1.0),\n            move_target: None,\n            current_command: Command::None,\n            id: utils::rand_id(),\n            frame_last_shot: 0,\n            reload_frame_count: 3,\n            weapon0_dir: Vector3::new(1.0, 0.0, 0.0),\n            wheel0_angle: 0.0,\n            life: botdef.max_life,\n            con_completed: 1.0,\n            grounded: false,\n            botdef_id: botdef.id,\n            angular_velocity: 0.0,\n        }\n    }\n}\n\npub struct ClientKbot {\n    pub position: Point3<f32>,\n    pub dir: Vector3<f32>,\n    pub up: Vector3<f32>,\n\n    pub weapon0_dir: Vector3<f32>,\n    pub wheel0_angle: f32,\n\n    pub trans: Option<Matrix4<f32>>,\n    pub is_in_screen: bool,\n    pub distance_to_camera: f32,\n    pub screen_pos: Vector2<f32>,\n}\n\nimpl ClientKbot {\n    pub fn new(position: Point3<f32>) -> Self {\n        ClientKbot {\n            position,\n            dir: Vector3::new(1.0, 0.0, 0.0),\n            up: Vector3::new(0.0, 0.0, 1.0),\n            weapon0_dir: Vector3::new(1.0, 0.0, 0.0),\n            wheel0_angle: 0.0,\n            trans: None,\n            is_in_screen: false,\n            distance_to_camera: 0.0,\n            screen_pos: Vector2::new(0.0, 0.0),\n        }\n    }\n}\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub struct KinematicProjectile {\n    pub id: Id<KinematicProjectile>,\n    pub birth_frame: i32,\n    pub death_frame: i32,\n    pub position_at_birth: Point3<f32>,\n    pub speed_per_frame_at_birth: Vector3<f32>,\n    pub accel_per_frame: Vector3<f32>,\n    pub radius: f32,\n\n    pub position_cache: Vec<Point3<f32>>,\n    pub speed_cache: Vec<Vector3<f32>>,\n}\n\nimpl KinematicProjectile {\n    pub fn speed_at(&mut self, frame_number: i32) -> Vector3<f32> {\n        //End recursion\n        if frame_number == self.birth_frame {\n            self.speed_per_frame_at_birth\n        }\n        //Check cache\n        else if self.speed_cache.len() as i32 > frame_number - self.birth_frame {\n            self.speed_cache[(frame_number - self.birth_frame) as usize]\n        }\n        //Compute\n        else {\n            let new_speed = self.speed_at(frame_number - 1) + self.accel_per_frame;\n            self.speed_cache.push(new_speed);\n            *self.speed_cache.last().unwrap()\n        }\n    }\n    pub fn position_at(&mut self, frame_number: i32) -> Point3<f32> {\n        //End recursion\n        if frame_number == self.birth_frame {\n            self.position_at_birth\n        }\n        //Check cache\n        else if self.position_cache.len() as i32 > frame_number - self.birth_frame {\n            self.position_cache[(frame_number - self.birth_frame) as usize]\n        }\n        //Compute\n        else {\n            let new_pos = self.position_at(frame_number - 1) + self.speed_at(frame_number);\n            self.position_cache.push(new_pos);\n            *self.position_cache.last().unwrap()\n        }\n    }\n}\n\n#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]\npub struct Arrow {\n    pub position: Point3<f32>,\n    pub end: Point3<f32>,\n    pub color: [f32; 4],\n}\n\nimpl Arrow {\n    pub fn new(position: Point3<f32>, end: Point3<f32>, color: [f32; 4]) -> Self {\n        Arrow {\n            position,\n            color,\n            end,\n        }\n    }\n}\n"
  },
  {
    "path": "src/moddef.rs",
    "content": "use crate::botdef::BotDef;\nuse crate::unit;\nuse crate::utils;\nuse fnv::FnvHashMap;\nuse serde::{Deserialize, Serialize};\nuse typename::TypeName;\nuse utils::Id;\n\n#[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)]\npub struct ModDef {\n    pub units_id: Vec<Id<BotDef>>,\n    pub con_map: FnvHashMap<Id<BotDef>, Vec<Id<BotDef>>>,\n}\n\nimpl ModDef {\n    pub fn new() -> Self {\n        Self {\n            units_id: Vec::new(),\n            con_map: FnvHashMap::default(),\n        }\n    }\n}\n"
  },
  {
    "path": "src/model.rs",
    "content": "#[derive(Clone, Copy)]\npub struct Vertex {\n    _pos: [f32; 4],\n    _nor: [f32; 3],\n    _tex_coord: [f32; 2],\n}\n\n#[derive(Clone)]\npub struct TriangleList {\n    pub vertex_data: Vec<Vertex>,\n    pub index_data: Vec<u32>,\n}\n\npub fn open_obj(path: &str) -> Result<TriangleList, String> {\n    use obj::{load_obj, Obj};\n    use std::fs::File;\n    use std::io::BufReader;\n\n    let input = BufReader::new(File::open(path).expect(&format!(\"Can't open {}\", path)));\n    let model: Obj<obj::TexturedVertex> = load_obj(input).map_err(|e| format!(\"{:?}\", e))?;\n\n    let vertex_data: Vec<_> = model\n        .vertices\n        .iter()\n        .map(|v| Vertex {\n            _pos: [v.position[0], v.position[1], v.position[2], 1.0],\n            _nor: [v.normal[0], v.normal[1], v.normal[2]],\n            _tex_coord: [v.texture[0], v.texture[1]],\n        })\n        .collect();\n\n    Ok(TriangleList {\n        vertex_data,\n        index_data: model.indices.iter().map(|u| *u as u32).collect(),\n    })\n}\n"
  },
  {
    "path": "src/net_client.rs",
    "content": "use crate::frame::*;\nuse crossbeam_channel::{unbounded, Receiver, Sender};\nuse spin_sleep::LoopHelper;\nuse std::io::prelude::*;\nuse std::io::BufReader;\nuse std::net::TcpListener;\nuse std::net::TcpStream;\n\n#[derive(Debug, Clone, Copy)]\npub enum BindState {\n    Unknown,\n    Success,\n    Error,\n    Disconnected,\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct NetClientInfo {\n    bind_state: BindState,\n}\n\npub enum ToNetClientInner {\n    PlayerInput(Vec<FrameEventFromPlayer>),\n}\n\npub enum FromNetClientInner {\n    DataToComputeNextFrame(DataToComputeNextFrame),\n}\n\npub struct NetClient {\n    s: Sender<ToNetClientInner>,\n    r: Receiver<FromNetClientInner>,\n    info: NetClientInfo,\n    r_info: Receiver<NetClientInfo>,\n    s_kill: Sender<()>,\n}\n\nimpl NetClient {\n    pub fn new(bind: &str) -> Self {\n        let (s_to, r_to) = unbounded::<ToNetClientInner>();\n        let (s_from, r_from) = unbounded::<FromNetClientInner>();\n\n        let (s_info, r_info) = unbounded::<NetClientInfo>();\n\n        let (s_kill, r_kill) = unbounded::<()>();\n\n        let bind_addr = bind.to_owned();\n        std::thread::spawn(move || {\n            let s = s_from;\n            let r = r_to;\n            let s_info = s_info;\n\n            match TcpStream::connect(bind_addr) {\n                Ok(mut stream) => {\n                    s_info\n                        .try_send(NetClientInfo {\n                            bind_state: BindState::Success,\n                        })\n                        .unwrap();\n\n                    let _ = stream.set_read_timeout(Some(std::time::Duration::from_millis(2)));\n                    let _ = stream.set_nodelay(true);\n                    log::info!(\"Connection established!\");\n\n                    let mut loop_helper = LoopHelper::builder().build_with_target_rate(100.0_f64);\n                    'streamloop: loop {\n                        loop_helper.loop_sleep();\n                        loop_helper.loop_start();\n                        match r.try_recv() {\n                            Ok(ToNetClientInner::PlayerInput(fe)) => {\n                                log::trace!(\"stream: Sending local player input to remote server\");\n                                bincode::serialize_into(&mut stream, &fe).unwrap();\n                            }\n                            _ => {\n                                log::trace!(\"no player input to send\");\n                            }\n                        }\n\n                        log::trace!(\"read\");\n                        let result_bincode: bincode::Result<DataToComputeNextFrame> =\n                            bincode::deserialize_from(&mut stream);\n                        match result_bincode {\n                            Ok(data) => {\n                                log::trace!(\"   Receive Frame from remote server\");\n                                let _ =\n                                    s.try_send(FromNetClientInner::DataToComputeNextFrame(data));\n                            }\n                            x => {\n                                log::trace!(\"   Error read {:?}\", x);\n                            }\n                        }\n\n                        if let Ok(()) = r_kill.try_recv() {\n                            let _ = s_info.try_send(NetClientInfo {\n                                bind_state: BindState::Disconnected,\n                            });\n                            break 'streamloop;\n                        }\n                    }\n                    log::info!(\"Killed\");\n                }\n                _ => {\n                    s_info\n                        .try_send(NetClientInfo {\n                            bind_state: BindState::Error,\n                        })\n                        .unwrap();\n                }\n            }\n        });\n        NetClient {\n            s: s_to,\n            r: r_from,\n            r_info,\n            info: NetClientInfo {\n                bind_state: BindState::Unknown,\n            },\n            s_kill,\n        }\n    }\n\n    pub fn kill(&mut self) {\n        self.s_kill.try_send(()).unwrap();\n    }\n\n    pub fn collect_data_to_compute_next_frame(&mut self) -> Option<DataToComputeNextFrame> {\n        if self.r.is_empty() {\n            match self.r.recv() {\n                Ok(FromNetClientInner::DataToComputeNextFrame(data)) => Some(data),\n                _ => None,\n            }\n        } else {\n            match self.r.try_iter().last().unwrap() {\n                FromNetClientInner::DataToComputeNextFrame(data) => Some(data),\n            }\n        }\n    }\n\n    pub fn send_player_inputs(&mut self, player_inputs: Vec<FrameEventFromPlayer>) {\n        if player_inputs.len() > 0 {\n            log::trace!(\"net_client: Sending local player input to remote server\");\n            let _ = self\n                .s\n                .try_send(ToNetClientInner::PlayerInput(player_inputs));\n        }\n    }\n\n    pub fn get_info(&mut self) -> NetClientInfo {\n        let last = self.r_info.try_iter().last();\n        if let Some(info) = last {\n            self.info = info;\n        }\n        self.info\n    }\n}\n"
  },
  {
    "path": "src/net_server.rs",
    "content": "use crate::frame::*;\nuse crossbeam_channel::{unbounded, Receiver, Sender};\nuse spin_sleep::LoopHelper;\nuse std::io::prelude::*;\nuse std::net::TcpListener;\nuse std::net::TcpStream;\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum BindState {\n    Unknown,\n    Success,\n    Error,\n}\n\n#[derive(Debug, Clone, Copy)]\npub struct NetServerInfo {\n    bind_state: BindState,\n    number_of_client_connected: usize,\n}\n\npub enum ToNetServerInner {\n    DataToComputeNextFrame(DataToComputeNextFrame),\n}\n\npub enum FromNetServerInner {\n    PlayerInputs(Vec<FrameEventFromPlayer>),\n}\n\npub struct NetServer {\n    s_inner: Sender<ToNetServerInner>,\n    r_inner: Receiver<FromNetServerInner>,\n    info: NetServerInfo,\n    r_info: Receiver<NetServerInfo>,\n}\n\nimpl NetServer {\n    pub fn new(bind: &str) -> Self {\n        let (s_to, r_to) = unbounded::<ToNetServerInner>();\n        let (s_from, r_from) = unbounded::<FromNetServerInner>();\n\n        let (s_info, r_info) = unbounded::<NetServerInfo>();\n        let bind_addr = bind.to_owned();\n        std::thread::spawn(move || {\n            let r = r_to;\n            let s = s_from;\n\n            let s_info = s_info;\n            let mut info = NetServerInfo {\n                bind_state: BindState::Unknown,\n                number_of_client_connected: 0,\n            };\n\n            let mut net_streams = Vec::new();\n            //Thread that will give us the connected clients\n            let (s_bind_state, r_bind_state) = unbounded::<BindState>();\n            let (s_of_net_stream, r_of_net_stream) = unbounded::<NetStream>();\n            std::thread::spawn(move || match TcpListener::bind(bind_addr) {\n                Ok(listener) => {\n                    s_bind_state.send(BindState::Success).unwrap();\n                    for stream in listener.incoming() {\n                        let stream = stream.unwrap();\n                        log::info!(\"Connection established!\");\n                        let net_stream = NetStream::new(stream);\n                        s_of_net_stream.try_send(net_stream).unwrap();\n                    }\n                }\n                _ => {\n                    s_bind_state.send(BindState::Error).unwrap();\n                }\n            });\n\n            let mut loop_helper = LoopHelper::builder().build_with_target_rate(100.0_f64);\n            loop {\n                if info.bind_state == BindState::Unknown {\n                    if let Some(bind_state) = r_bind_state.try_iter().last() {\n                        info.bind_state = bind_state;\n                    }\n                }\n\n                loop_helper.loop_sleep();\n                loop_helper.loop_start();\n                let net_streams = &mut net_streams;\n                match r_of_net_stream.try_recv() {\n                    Ok(net_stream) => {\n                        log::info!(\"Connection taken care of\");\n                        net_streams.push(net_stream);\n                    }\n                    _ => {}\n                }\n\n                //Block on waiting new frames\n                match r.try_recv() {\n                    Ok(ToNetServerInner::DataToComputeNextFrame(data)) => {\n                        let bytes = bincode::serialize(&data).unwrap();\n                        for net_stream in net_streams.iter_mut() {\n                            net_stream.send_data_to_compute_next_frame(bytes.clone())\n                        }\n                    }\n                    _ => {}\n                }\n\n                //Player input\n                let mut player_inputs = Vec::new();\n                for net_stream in net_streams.iter_mut() {\n                    player_inputs.extend(net_stream.collect_remote_player_input());\n                }\n\n                let _ = s.try_send(FromNetServerInner::PlayerInputs(player_inputs));\n\n                //Info update\n                info.number_of_client_connected = net_streams.len();\n                s_info.try_send(info).unwrap();\n            }\n        });\n        NetServer {\n            s_inner: s_to,\n            r_inner: r_from,\n            info: NetServerInfo {\n                bind_state: BindState::Unknown,\n                number_of_client_connected: 0,\n            },\n            r_info,\n        }\n    }\n    pub fn kill(&mut self) {}\n\n    pub fn collect_remote_players_inputs(&mut self) -> Vec<FrameEventFromPlayer> {\n        let mut pis = Vec::new();\n        for msg in self.r_inner.try_iter() {\n            match msg {\n                FromNetServerInner::PlayerInputs(player_inputs) => pis.extend(player_inputs),\n            }\n        }\n        pis\n    }\n\n    pub fn broadcast_data_to_compute_next_frame(&mut self, data: DataToComputeNextFrame) {\n        let _ = self\n            .s_inner\n            .try_send(ToNetServerInner::DataToComputeNextFrame(data));\n    }\n\n    pub fn get_info(&mut self) -> NetServerInfo {\n        let last = self.r_info.try_iter().last();\n        if let Some(info) = last {\n            self.info = info;\n        }\n        self.info\n    }\n}\n\nenum ToNetStream {\n    DataToComputeNextFrame(Vec<u8>),\n}\n\nenum FromNetStream {\n    PlayerInput(Vec<FrameEventFromPlayer>),\n}\n\nstruct NetStream {\n    r: Receiver<FromNetStream>,\n    s: Sender<ToNetStream>,\n}\n\nimpl NetStream {\n    fn new(stream: TcpStream) -> Self {\n        let (s_to, r_to) = unbounded::<ToNetStream>();\n        let (s_from, r_from) = unbounded::<FromNetStream>();\n\n        std::thread::spawn(move || {\n            let mut stream = stream;\n            let _ = stream.set_read_timeout(Some(std::time::Duration::from_millis(2)));\n            let _ = stream.set_nodelay(true);\n            let r = r_to;\n            let s = s_from;\n            let mut loop_helper = LoopHelper::builder().build_with_target_rate(100.0_f64);\n            loop {\n                loop_helper.loop_sleep();\n                loop_helper.loop_start();\n                log::trace!(\"read\");\n                let result_bincode: bincode::Result<Vec<FrameEventFromPlayer>> =\n                    bincode::deserialize_from(&mut stream);\n                match result_bincode {\n                    Ok(player_inputs) => {\n                        log::trace!(\n                            \"   Receive player_inputs ({}) from remote client\",\n                            player_inputs.len()\n                        );\n                        let _ = s.try_send(FromNetStream::PlayerInput(player_inputs));\n                    }\n                    x => {\n                        log::trace!(\"   Error read {:?}\", x);\n                    }\n                }\n\n                //Send last frame to remote player\n                if !r.is_empty() {\n                    match r.try_iter().last().unwrap() {\n                        ToNetStream::DataToComputeNextFrame(data) => {\n                            log::debug!(\"Send frame to remote player ({} bytes)\", data.len());\n                            let _ = stream.write_all(&data).unwrap();\n                            stream.flush().unwrap();\n                        }\n                    }\n                }\n            }\n        });\n        NetStream { s: s_to, r: r_from }\n    }\n\n    pub fn collect_remote_player_input(&mut self) -> Vec<FrameEventFromPlayer> {\n        let mut pis = Vec::new();\n        for msg in self.r.try_iter() {\n            match msg {\n                FromNetStream::PlayerInput(player_inputs) => pis.extend(player_inputs),\n            }\n        }\n        pis\n    }\n    pub fn send_data_to_compute_next_frame(&mut self, data: Vec<u8>) {\n        let _ = self.s.try_send(ToNetStream::DataToComputeNextFrame(data));\n    }\n}\n"
  },
  {
    "path": "src/procedural_texels.rs",
    "content": "pub fn create_texels(size: usize) -> Vec<u8> {\n    let mut v = Vec::new();\n    for i in 0..size {\n        for j in 0..size {\n            let i = i as f32 / size as f32;\n            let j = j as f32 / size as f32;\n            v.push((i * 255.0) as u8);\n            v.push(((1.0 - j) * 255.0) as u8);\n            v.push(((1.0 - i) * j * 255.0) as u8);\n            v.push(255);\n        }\n    }\n    v\n}\n\npub fn checker(size: usize) -> Vec<u8> {\n    let mut v = Vec::new();\n    for i in 0..size {\n        for j in 0..size {\n            let pair = (i + j) % 2 == 0;\n            if pair {\n                v.push(0);\n                v.push(0);\n                v.push(0);\n            } else {\n                v.push(255);\n                v.push(255);\n                v.push(255);\n            }\n            v.push(255);\n        }\n    }\n    v\n}\n"
  },
  {
    "path": "src/shader/arrow.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec3 world_pos;\nlayout(location = 2) in vec4 v_color;\n\n\nlayout(location = 0) out vec4 o_Target;\nlayout(location = 1) out vec4 position_att;\n\nlayout(set = 0, binding = 1) uniform texture2D t_Color;\nlayout(set = 0, binding = 2) uniform sampler s_Color;\n\nvoid main() {\n    vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord);\n\n    position_att = vec4(world_pos, 0.0 );\n\n    vec3 color = v_color.rgb;\n\n    o_Target =    vec4(color,1.0);\n}\n"
  },
  {
    "path": "src/shader/arrow.vert",
    "content": "#version 450\n\nlayout(location = 0) in vec4 a_Pos;\nlayout(location = 1) in vec2 a_TexCoord;\n\nlayout(location = 2) in vec4 mata;\nlayout(location = 3) in vec4 matb;\nlayout(location = 4) in vec4 matc;\nlayout(location = 5) in vec4 matd;\nlayout(location = 6) in vec4 color;\n\n\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec3 world_pos;\nlayout(location = 2) out vec4 v_color;\n\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    v_TexCoord = a_TexCoord;\n \n    v_color = color;\n\n    float dist = color.w;\n\n    vec3 pos = a_Pos.xyz;\n    if (a_Pos.z>0.0){\n        pos.z +=dist;\n    } else if(a_Pos.z<=0.0){\n        pos.z =0;\n    }\n\n    mat4 t = mat4(mata,matb,matc,matd);\n\n    gl_Position = cor_proj_view *t* vec4(pos,1.0);\n    world_pos = a_Pos.xyz;\n}\n"
  },
  {
    "path": "src/shader/blit_texture.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 1, binding = 0) uniform texture2D t_color;\nlayout(set = 1, binding = 1) uniform sampler s_color;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    o_Target = texture(sampler2D(t_color, s_color), v_TexCoord);\n}\n"
  },
  {
    "path": "src/shader/blit_texture.vert",
    "content": "#version 450\n\n//min_screen [0,1]\nlayout(location = 0) in vec2 min_screen;\nlayout(location = 1) in vec2 max_screen;\n\n//min_tex [0,1]\nlayout(location = 2) in vec2 min_tex;\nlayout(location = 3) in vec2 max_tex;\n\nlayout(location = 0) out vec2 v_TexCoord;\n\nvoid main() {\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(1.0, 0.0); break;\n        case 1: tc = vec2(1.0, 1.0); break;\n        case 2: tc = vec2(0.0, 0.0); break;\n        case 3: tc = vec2(0.0, 1.0); break;\n    }\n\n    v_TexCoord = min_tex +tc*(max_tex-min_tex);\n    vec2 screen_px = min_screen +tc*(max_screen-min_screen);\n    gl_Position = vec4(screen_px * 2.0 - 1.0, 0.5, 1.0);\n}\n"
  },
  {
    "path": "src/shader/cube_instanced.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec3 world_pos;\nlayout(location = 2) in float v_selected;\nlayout(location = 3) in float v_team;\nlayout(location = 4) in float v_con_completed;\nlayout(location = 5) in vec3 v_world_normal;\n\nlayout(location = 0) out vec4 o_Target;\nlayout(location = 1) out vec4 position_att;\nlayout(location = 2) out vec2 o_normal;\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\nlayout(set = 0, binding = 1) uniform texture2D t_Color;\nlayout(set = 0, binding = 2) uniform sampler s_Color;\n\nvoid main() {\n    vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord);\n\n    position_att = vec4(world_pos, v_selected );\n\n    vec3 color = vec3(1);\n    if(v_team >= 99.0){\n        color = vec3(1);\n    }\n    else if( v_team == 0.0){\n        color = vec3(0,0.3,1);\n    }else if(v_team < 1.1){\n        color = vec3(1,0.0,0);\n    }\n\n\n    vec3 diffuse= mix(tex.xyz, color,0.5);;\n       //blinn phong\n    const vec3 ambientColor = vec3(0.05);\n    const vec3 diffuseColor = vec3(1.0, 1.0, 1.0);\n    const vec3 specColor = vec3(0.2);\n    vec3 lightPos = vec3(-10000,1000,12000);\n\n    vec3 vertPos = world_pos;\n    vec3 lightDir = normalize(lightPos - vertPos);\n    vec3 normal = v_world_normal;\n\n    float lambertian = max(dot(lightDir,normal), 0.0);\n    float specular = 0.0;\n\n    if(lambertian > 0.0) {\n        mat3 rot = mat3(u_View);\n        vec3 camera_pos = -u_View[3].xyz*rot;\n        vec3 viewDir = normalize( camera_pos - vertPos);\n        vec3 halfDir = normalize(lightDir + viewDir);\n        float specAngle = max(dot(halfDir, normal), 0.0);\n        specular = 1.0*pow(specAngle, 32.0);\n    }\n    \n    vec3 phong = vec3(ambientColor +\n    lambertian* diffuse +\n    specular*specColor);\n\n\n\n    if(v_con_completed < 0.9999){\n        bool diag = int((gl_FragCoord.x +gl_FragCoord.y )/3.0) %2 == 0;\n        bool hori = int((gl_FragCoord.y)/2.0) %2==0;\n\n        bool hatch = true;\n        if (length(phong) <0.5){\n            hatch = diag||hori;\n        }else if (length(phong) <0.8){\n            hatch = diag;\n        } else {\n            hatch = false;\n        } \n\n        float m = 0.3;\n        float f = 1+m;\n        if ( hatch){\n            f= 1-m;\n        }\n\n        phong = mix(vec3(ambientColor*f +\n        lambertian* diffuse*f +\n        specular*specColor*f) , vec3(0,v_con_completed,0), hatch ? 1.0:0.0);\n    }\n  \n    \n\n    o_normal = normal.xy;\n    o_Target = vec4(phong, 1.0);\n}\n"
  },
  {
    "path": "src/shader/cube_instanced.vert",
    "content": "#version 450\n\nlayout(location = 0) in vec4 a_Pos;\nlayout(location = 1) in vec3 a_normal;\nlayout(location = 2) in vec2 a_TexCoord;\n\nlayout(location = 3) in vec3 inst_pos;\nlayout(location = 4) in vec3 inst_euler;\nlayout(location = 5) in float bitpack_selected_team_na_na;\nlayout(location = 6) in float con_completed;\n\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec3 world_pos;\n\nlayout(location = 2) out float v_selected;\nlayout(location = 3) out float v_team;\nlayout(location = 4) out float v_con_completed;\nlayout(location = 5) out vec3 v_world_normal;\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    v_TexCoord = a_TexCoord;\n\n    v_selected=  floor(bitpack_selected_team_na_na/100.0);\n    v_team = round(bitpack_selected_team_na_na-v_selected*100.0);\n    // v_selected = bitpack_selected_team_na_na <= 0.0 ? 1.0 : 0.0;\n    v_con_completed = con_completed;\n\n    float sr = sin(inst_euler.x);\n    float cr = cos(inst_euler.x);\n    float sp = sin(inst_euler.y);\n    float cp = cos(inst_euler.y);\n    float sy = sin(inst_euler.z);\n    float cy = cos(inst_euler.z);\n\n    // mat4 t = mat4(\n    //     cy * cp,  cy * sp * sr - sy * cr,  cy * sp * cr + sy * sr,  inst_pos.x, \n    //     sy * cp,  sy * sp * sr + cy * cr,  sy * sp * cr - cy * sr,  inst_pos.y,\n    //              -sp,            cp * sr,            cp * cr,  inst_pos.z, \n    //              0,0,0,1);\n\n    mat4 t = mat4(\n        cy * cp,                 sy * cp,                -sp                ,0, \n        cy * sp * sr - sy * cr,  sy * sp * sr + cy * cr, cp * sr            ,0,\n        cy * sp * cr + sy * sr,  sy * sp * cr - cy * sr, cp * cr            ,0, \n        inst_pos.x,              inst_pos.y            , inst_pos.z         ,1);\n\n    mat3 tn = mat3(\n    cy * cp,                 sy * cp,                -sp                ,\n    cy * sp * sr - sy * cr,  sy * sp * sr + cy * cr, cp * sr            ,\n    cy * sp * cr + sy * sr,  sy * sp * cr - cy * sr, cp * cr            ); \n             \n\n    vec4 world_pos4 = t * a_Pos;\n    world_pos = world_pos4.xyz/world_pos4.w;\n    gl_Position = cor_proj_view*vec4(world_pos+vec3(0.0),1.0);//  cor_proj_view * t *a_Pos;\n\n    v_world_normal = tn* a_normal;\n \n}\n"
  },
  {
    "path": "src/shader/explosion.frag",
    "content": "#version 450\n\n#define EXPLOSION_SEED 1.\n\n\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec2 v_center;\nlayout(location = 2) in float v_size;\nlayout(location = 3) in float v_life;\nlayout(location = 4) in float v_seed;\nlayout(location = 5) in vec3 v_world_pos;\nlayout(location = 6) in vec2 v_screen_pos;\n\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\nlayout(set = 1, binding = 0) uniform texture2D t_noise;\nlayout(set = 1, binding = 1) uniform sampler s_noise;\n\nlayout(set = 1, binding = 2) uniform texture2D t_world_pos;\nlayout(set = 1, binding = 3) uniform sampler s_world_pos;\n\nlayout(set = 1, binding = 4) uniform texture2D t_world_normal;\nlayout(set = 1, binding = 5) uniform sampler s_world_normal;\n\nfloat expRadius;\nvec3 expCenter;\nfloat iTime = 1.0;\nvec3 iMouse;\n\n//iq's LUT 3D noise\nfloat noise( in vec3 x )\n{\n    vec3 f = fract(x);\n    vec3 p = x - f; // this avoids the floor() but doesnt affect performance for me.\n    f = f*f*(3.0-2.0*f);\n     \n    vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy;\n    vec2 rg = textureLod( sampler2D(t_noise,s_noise), (uv+ 0.5)/256.0, 0.0 ).yx;\n    return mix( rg.x, rg.y, f.z );\n}\n\n// assign colour to the media\nvec3 computeColour( float density, float radius )\n{\n\t// these are almost identical to the values used by iq\n\t\n\t// colour based on density alone. gives impression of occlusion within\n\t// the media\n\tvec3 result = mix( 1.1*vec3(1.0,0.9,0.8), vec3(0.4,0.15,0.1), density );\n\t\n\t// colour added for explosion\n\tvec3 colBottom = 3.1*vec3(1.0,0.5,0.05);\n\tvec3 colTop = 2.*vec3(0.48,0.53,0.5);\n\tresult *= mix( colBottom, colTop, min( (radius+.5)/1.7, 1.0 ) );\n\t\n\treturn result;\n}\n\n// maps 3d position to colour and density\nfloat densityFn( in vec3 p, in float r, out float rawDens, in float rayAlpha )\n{\n\t// density has dependency on mouse y coordinate (linear radial ramp)\n\tfloat mouseIn = iMouse.y;\n\tfloat mouseY = 1.0 - mouseIn;\n    float den = -0.1 - 1.5*r*(4.*mouseY+.5);\n    \n\t// offset noise based on seed\n    float t = v_seed;\n    vec3 dir = vec3(0.,1.,0.);\n    \n    // participating media    \n    float f;\n    vec3 q = p - dir* t; f  = 0.50000*noise( q );\n\tq = q*2.02 - dir* t; f += 0.25000*noise( q );\n\tq = q*2.03 - dir* t; f += 0.12500*noise( q );\n\tq = q*2.01 - dir* t; f += 0.06250*noise( q );\n\tq = q*2.02 - dir* t; f += 0.03125*noise( q );\n\t\n\t// add in noise with scale factor\n\trawDens = den + 4.0*f;\n\t\n    den = clamp( rawDens, 0.0, 1.0 );\n    \n\t// thin out the volume at the far extends of the bounding sphere to avoid\n\t// clipping with the bounding sphere\n\tden *= 1.-smoothstep(0.8,1.,r/expRadius);\n\t\n\t#ifdef CROSS_SECTION\n\tden *= smoothstep(.0,.1,-p.x);\n\t#endif\n\t\n\treturn den;\n}\n\nvec4 raymarch( in vec3 rayo, in vec3 rayd, in float expInter, in vec2 fragCoord )\n{\n    vec4 sum = vec4( 0.0 );\n     \n    float step = 0.075;\n     \n    // dither start pos to break up aliasing\n\tvec3 pos = rayo + rayd * (expInter + step*texture( sampler2D(t_noise,s_noise), fragCoord.xy/256.0 ).x);\n\t\n    for( int i=0; i<25; i++ )\n    {\n        if( sum.a > 0.99 ) continue;\n\t\t\n\t\tfloat radiusFromExpCenter = length(pos - expCenter);\n\t\t\n\t\tif( radiusFromExpCenter > expRadius+0.01 ) continue;\n\t\t\n\t\tfloat dens, rawDens;\n\t\t\n        dens = densityFn( pos, radiusFromExpCenter, rawDens, sum.a );\n\t\t\n\t\tvec4 col = vec4( computeColour(dens,radiusFromExpCenter), dens );\n\t\t\n\t\t// uniform scale density\n\t\tcol.a *= 0.6;\n\t\t\n\t\t// colour by alpha\n\t\tcol.rgb *= col.a;\n\t\t\n\t\t// alpha blend in contribution\n\t\tsum = sum + col*(1.0 - sum.a);  \n\t\t\n\t\t// take larger steps through negative densities.\n\t\t// something like using the density function as a SDF.\n\t\tfloat stepMult = 1. + 2.5*(1.-clamp(rawDens+1.,0.,1.));\n\t\t\n\t\t// step along ray\n\t\tpos += rayd * step * stepMult;\n    }\n\t\n    return clamp( sum, 0.0, 1.0 );\n}\n\n// iq's sphere intersection\nfloat iSphere(in vec3 ro, in vec3 rd, in vec4 sph)\n{\n\t//sphere at origin has equation |xyz| = r\n\t//sp |xyz|^2 = r^2.\n\t//Since |xyz| = ro + t*rd (where t is the parameter to move along the ray),\n\t//we have ro^2 + 2*ro*rd*t + t^2 - r2. This is a quadratic equation, so:\n\tvec3 oc = ro - sph.xyz; //distance ray origin - sphere center\n\t\n\tfloat b = dot(oc, rd);\n\tfloat c = dot(oc, oc) - sph.w * sph.w; //sph.w is radius\n\tfloat h = b*b - c; // delta\n\tif(h < 0.0) \n\t\treturn -1.0;\n\tfloat t = (-b - sqrt(h)); //Again a = 1.\n\n\treturn t;\n}\n\nvec3 computePixelRay( in vec2 p, out vec3 cameraPos )\n{\n    // camera orbits around explosion\n\t\n    float camRadius = 3.8;\n\t// use mouse x coord\n\tfloat a = iTime*20.;\n\tif( iMouse.z > 0. )\n\t\ta = iMouse.x;\n\tfloat theta = -(a-resolution.x)/80.;\n    float xoff = camRadius * cos(theta);\n    float zoff = camRadius * sin(theta);\n    cameraPos = vec3(xoff,expCenter.y,zoff);\n     \n    // camera target\n    vec3 target = vec3(0.,expCenter.y,0.);\n     \n    // camera frame\n    vec3 fo = normalize(target-cameraPos);\n    vec3 ri = normalize(vec3(fo.z, 0., -fo.x ));\n    vec3 up = normalize(cross(fo,ri));\n     \n    // multiplier to emulate a fov control\n    float fov = .5;\n\t\n    // ray direction\n    vec3 rayDir = normalize(fo + fov*p.x*ri + fov*p.y*up);\n\t\n\treturn rayDir;\n}\n\n\n\n\nvoid main(){\n\n\n    float life = pow(v_life,0.1); \n    float alpha_max = pow(1-v_life,1)*0.5;\n    iMouse = vec3(resolution.x/2.0,life,0.0);\n    vec2 fragCoord = v_TexCoord* resolution;\n\t// get aspect corrected normalized pixel coordinate\n    vec2 q = v_TexCoord;\n    vec2 p = -1.0 + 2.0*q;\n    p *= 1.0;\n\t//   p *= 10.0;\n    \n    expRadius = 1.75;\n\texpCenter = vec3(0.,expRadius,0.);\n\t\n\tvec3 rayDir, cameraPos;\n    rayDir = computePixelRay( p, cameraPos );\n\t\n\tvec4 col = vec4(0.);\n\t o_Target.w= 0.0;\n    // does pixel ray intersect with exp bounding sphere?\n\tfloat boundingSphereInter = iSphere( cameraPos, rayDir, vec4(expCenter,expRadius) );\n\tif( boundingSphereInter > 0. )\n\t{\n\t\t// yes, cast ray\n\t    col = raymarch( cameraPos, rayDir, boundingSphereInter,fragCoord );\n        o_Target.w = pow(length(col),4)*alpha_max;\n    }\n\t\n\n\n\t// vec3 nor_world =  texture(sampler2D(t_world_normal,s_world_normal), v_screen_pos ).xyz;\n\t// nor_world.z = sqrt(1- (nor_world.x*nor_world.x + nor_world.y*nor_world.y));\n\n\t// vec3 mesh_pos_world = texture(sampler2D(t_world_pos,s_world_pos), v_screen_pos ).xyz;\n\n\t// vec3 lightPos=  v_world_pos;\n\n\t// float dist_to_light = length(mesh_pos_world -lightPos);\n\t// float attenuation = 1*min( 0.05 * pow(v_size/ dist_to_light,2) ,1);\n\n\t// vec3 lightDir = normalize(lightPos - mesh_pos_world);\n\n    // float lambertian = max(dot(lightDir,nor_world), 0.0);\n    // float specular = 0.0;\n\n    // if(lambertian > 0.0) {\n    //     mat3 rot = mat3(u_View);\n    //     vec3 camera_pos = -u_View[3].xyz*rot;\n    //     vec3 viewDir = normalize( camera_pos - mesh_pos_world);\n    //     vec3 halfDir = normalize(lightDir + viewDir);\n    //     float specAngle = max(dot(halfDir, nor_world), 0.0);\n    //     specular = 1.0*pow(specAngle, 32.0);\n    // }\n    // vec3 diffuse= vec3(1,pow(alpha_max,1),pow(alpha_max,3));\n    // vec3 phong = attenuation*vec3(\n    // lambertian* diffuse +\n    // specular*mix(diffuse,vec3(1),0.5));\n\n\n    // smoothstep final color to add contrast\n    o_Target.xyz =  col.xyz*col.xyz*(3.0-2.0*col.xyz);\n\t// o_Target.xyz =  phong*alpha_max;\n\t// o_Target.a = 1.0;\n    \n}\n"
  },
  {
    "path": "src/shader/explosion.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec2 v_center;\nlayout(location = 2) out float v_size;\nlayout(location = 3) out float v_life;\nlayout(location = 4) out float v_seed;\nlayout(location = 5) out vec3 v_world_pos;\nlayout(location = 6) out vec2 v_screen_pos;\n\n\n\nlayout(location = 0) in vec3 pos_world;\nlayout(location = 1) in float life;\nlayout(location = 2) in float seed;\nlayout(location = 3) in float size_world;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n\n\n    vec4 hposition = vec4(pos_world, 1.0);\n    v_world_pos = pos_world + vec3(0,0,0.5);\n    vec4 sposition = cor_proj_view * hposition;\n\n    v_size = size_world*7.0;\n    //   float size_screen=  2600*v_size / sposition.w;\n    float size_screen=  260*v_size / sposition.w;\n    sposition/=sposition.w;\n\n\n    vec2 center = sposition.xy;\n\n    v_center = center;\n\n    v_life =  life;\n    v_seed =  seed*50.0;\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(1.0, 0.0); break;\n        case 1: tc = vec2(1.0, 1.0); break;\n        case 2: tc = vec2(0.0, 0.0); break;\n        case 3: tc = vec2(0.0, 1.0); break;\n    }\n    v_TexCoord = tc;\n\n    vec2 min = -inv_resolution*size_screen;\n    vec2 max = inv_resolution*size_screen;\n\n    vec2 pos =   center  ;\n     switch(gl_VertexIndex) {\n        case 0: pos += vec2(max.x, min.y); break;\n        case 1: pos += vec2(max.x, max.y); break;\n        case 2: pos += vec2(min.x, min.y); break;\n        case 3: pos += vec2(min.x, max.y); break;\n    }\n\n    v_screen_pos = pos.xy*0.5 +0.5;\n    gl_Position = vec4(pos , 0.5, 1.0);\n}\n"
  },
  {
    "path": "src/shader/health_bar.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec2 v_min;\nlayout(location = 2) in vec2 v_max;\nlayout(location = 3) in float v_life;\nlayout(location = 4) in float v_alpha;\nlayout(location = 5) in float v_type;\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\nvoid main() {\n    vec3 color = vec3(pow(1.0- v_life,0.3),pow(v_life,1.0),0.0);\n    if(v_type <= 0.0){\n    }else if (v_type <= 1.0){\n        color = vec3(0.5 + 0.13*sin(v_life*6.28*5));\n    }\n    if (v_TexCoord.x > v_life){\n        color= vec3(0);\n    }\n    o_Target = vec4(color,v_alpha);\n}\n"
  },
  {
    "path": "src/shader/health_bar.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec2 v_min;\nlayout(location = 2) out vec2 v_max;\nlayout(location = 3) out float v_life;\nlayout(location = 4) out float v_alpha;\nlayout(location = 5) out float v_type;\n\nlayout(location = 0) in vec2 min;\nlayout(location = 1) in vec2 max;\nlayout(location = 2) in float life;\nlayout(location = 3) in float alpha;\nlayout(location = 4) in float type;\n\n\nvoid main() {\n    v_min = min;\n    v_max = max;\n    v_life = life;\n    v_alpha = alpha;\n    v_type = type;\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(1.0, 0.0); break;\n        case 1: tc = vec2(1.0, 1.0); break;\n        case 2: tc = vec2(0.0, 0.0); break;\n        case 3: tc = vec2(0.0, 1.0); break;\n    }\n    v_TexCoord = tc;\n\n    vec2 pos =   vec2(0.0)  ;\n     switch(gl_VertexIndex) {\n        case 0: pos = vec2(max.x, min.y); break;\n        case 1: pos = vec2(max.x, max.y); break;\n        case 2: pos = vec2(min.x, min.y); break;\n        case 3: pos = vec2(min.x, max.y); break;\n    }\n\n    gl_Position = vec4(pos , 0.5, 1.0);\n}\n"
  },
  {
    "path": "src/shader/heightmap.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec3 color;\nlayout(location = 2) in float min_lod;\nlayout(location = 3) in float max_mip;\nlayout(location = 0) out vec4 o_Target;\nlayout(location = 1) out vec4 o_position_att;\nlayout(location = 2) out vec2 o_normal;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\n\nlayout(set = 0, binding = 1) uniform texture2D t_Color;\nlayout(set = 0, binding = 2) uniform sampler s_Color;\n\nlayout(set = 1, binding = 0) uniform MapCfg {\n    float width;\n    float height;\n    float cam_x;\n    float cam_y;\n};\nlayout(set = 1, binding = 1) uniform texture2D t_Color_checker;\nlayout(set = 1, binding = 2) uniform sampler s_Color_checker;\n\nlayout(set = 1, binding = 3) uniform texture2D height_tex;\nlayout(set = 1, binding = 4) uniform sampler height_sampler;\n\n\nconst vec3 ambientColor = vec3(0.05);\nconst vec3 diffuseColor = vec3(1.0, 1.0, 1.0);\nconst vec3 specColor = vec3(0.2);\n\nfloat linlerp(float v, float min, float max){\n    return (v-min)/(max-min);\n}\n\nvec3 normal_at(vec2 uv,float lod){\n    float r=  textureLod(sampler2D(height_tex, height_sampler),uv +vec2(1,0)/ vec2(width,height),lod ).r;\n    float l=  textureLod(sampler2D(height_tex, height_sampler),uv+ vec2(-1,0)/ vec2(width,height),lod ).r;\n    float u=  textureLod(sampler2D(height_tex, height_sampler),uv+vec2(0,1)/ vec2(width,height),lod ).r;\n    float d=  textureLod(sampler2D(height_tex, height_sampler),uv +vec2(0,-1)/ vec2(width,height),lod ).r;\n    return normalize(vec3(-(r-l), (d-u), 2));\n}\n\nfloat slope_of(vec3 normal){\n    return 1-asin(normal.z)/(3.141592/2.0);\n}\n\nvoid main() {\n    vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord);\n    vec4 tex_checker = texture(sampler2D(t_Color_checker, s_Color_checker),\n    (v_TexCoord) * vec2(width/2.0,height/2.0) + vec2(0.5/2.0));\n\n    vec2 pos_xy = v_TexCoord* vec2(width,height);\n\n    vec2 tex_coord_floor =vec2(floor(pos_xy.x),floor(pos_xy.y)) / vec2(width,height);\n\n    float lod = max(textureQueryLod(sampler2D(height_tex, height_sampler),v_TexCoord).x,min_lod);\n\n    vec3 pos = vec3(pos_xy, textureLod(sampler2D(height_tex, height_sampler),v_TexCoord,lod ).r );\n\n    vec3 normal = normal_at(v_TexCoord,lod);\n    float slope = slope_of(normal);\n\n    vec3 diffuse=  mix(vec3(0.5,0.4,0.3),tex_checker.xyz,0.041);\n\n    float ground_end= 1/90.0;\n    float grass_start= 2/90.0;\n\n    vec3 grass_color = vec3(0.45,0.7,0.2);\n    vec3 sand_color = vec3(1.0,0.8,0.7);\n\n    grass_color = mix(grass_color,sand_color, min(1,max(46.0- pos.z,0)*2 )); \n    \n    if  (slope > ground_end){\n        diffuse = mix(diffuse,grass_color, linlerp(slope,ground_end,grass_start) );\n    }\n    if (slope > grass_start){\n        diffuse= grass_color;\n    }\n    if (slope > 45/90.0){\n        diffuse = vec3(0.5);\n    }\n\n    //blinn phong\n    vec3 lightPos = vec3(-10000,1000,12000);\n\n    vec3 vertPos = pos;\n    vec3 lightDir = normalize(lightPos - vertPos);\n\n    float lambertian = max(dot(lightDir,normal), 0.0);\n    float specular = 0.0;\n\n    if(lambertian > 0.0) {\n        mat3 rot = mat3(u_View);\n        vec3 camera_pos = -u_View[3].xyz*rot;\n        vec3 viewDir = normalize( camera_pos - vertPos);\n        vec3 halfDir = normalize(lightDir + viewDir);\n        float specAngle = max(dot(halfDir, normal), 0.0);\n        specular = 1.0*pow(specAngle, 32.0);\n    }\n    \n    vec3 phong = vec3(ambientColor +\n    lambertian* diffuse +\n    specular*specColor);\n\n\n    // phong =mix(color, phong,0.1);\n    if(\n        pos.y <=0.5 || \n        pos.x <=0.5 || \n        pos.x >= width-0.5  ||\n        pos.y >= height  \n    ){\n        phong= vec3(0.1);\n    }\n\n    o_normal = normal.xy;\n    o_position_att = vec4(pos, 0.0);\n    o_Target =   vec4(phong,1.0);\n}\n"
  },
  {
    "path": "src/shader/heightmap.vert",
    "content": "#version 450\n\n\nlayout(location = 0) in vec2 a_Pos;\nlayout(location = 1) in float mip;\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec3 color;\nlayout(location = 2) out float min_lod;\nlayout(location = 3) out float max_mip;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nlayout(set = 1, binding = 0) uniform MapCfg {\n    float width;\n    float height;\n    float ring_size;\n    float cam_x;\n    float cam_y;\n};\n\n\nlayout(set = 1, binding = 3) uniform texture2D height_tex;\nlayout(set = 1, binding = 4) uniform sampler height_sampler;\n\nlayout(set = 1, binding = 5) uniform texture2D height_lod_tex;\nlayout(set = 1, binding = 6) uniform sampler height_lod_sampler;\n\nfloat floor_res(int res, float val ){\n    return floor(int(val)/res)*res;\n}\n\nvoid main() {\n\n    int res = int(round(pow(2.0,(mip))));\n    vec2 cam_pos  =   vec2(floor_res(res,cam_x), floor_res(res,cam_y)); //vec2(266.987); //\n    vec2 dim = hmap_size ;\n    vec2 pos_xy =  a_Pos.xy+cam_pos + vec2(0.5);\n    v_TexCoord =pos_xy/dim;\n    min_lod =  texture(sampler2D(height_lod_tex, height_lod_sampler),v_TexCoord).r;\n    max_mip  = max(mip, min_lod);\n    float z =  textureLod(sampler2D(height_tex, height_sampler),v_TexCoord, max_mip).r;\n    vec3 pos = vec3(pos_xy,z);\n\n    color= vec3(max_mip/4);\n    float rock_bottom = -40.0;\n    float f = fract(max_mip);\n    float stride = pow(2,ceil(max_mip));\n   \n\n   \n    if(pos_xy.x<-stride){\n        pos.x= -1.0;\n        pos.z = rock_bottom;    \n        color = vec3(0,0,1);\n    } else if(pos_xy.x<=0.0){\n        pos.x= -1.0;\n        color = vec3(1,0,0);\n    }\n\n  \n    if(pos_xy.x> width+stride){\n        pos.x= width+1;\n        pos.z = rock_bottom;    \n        color = vec3(0,1,1);\n    } else\n    if(pos_xy.x> width){\n        pos.x= width+1;\n        color = vec3(1,0,0);\n    }\n\n \n    if(pos_xy.y> height + stride){\n        pos.y= height+1;\n        pos.z = rock_bottom;  \n        color = vec3(0,0,1);  \n    }else\n    if(pos_xy.y>height){\n        pos.y= height+1;\n        color = vec3(1,0,0);\n    }\n\n     if(pos_xy.y<-stride){\n        pos.y= -1.0;\n        pos.z = rock_bottom;  \n        color = vec3(0,0,1);  \n    }else\n    if(pos_xy.y<-0.0){\n        pos.y= -1.0;\n        color = vec3(1,0,0);\n    }\n\n   \n\n    v_TexCoord =pos_xy/dim;\n\n    gl_Position = cor_proj_view * ( vec4(pos,1.0) );\n}\n"
  },
  {
    "path": "src/shader/imgui.frag",
    "content": "#version 450\n\nlayout(set = 1, binding = 0) uniform texture2D u_Texture;\nlayout(set = 1, binding = 1) uniform sampler u_Sampler;\n\nlayout(location = 0) in vec2 v_UV;\nlayout(location = 1) in vec4 v_Color;\n\nlayout(location = 0) out vec4 o_Target;\n\nvoid main() {\n  o_Target = v_Color * texture(sampler2D(u_Texture, u_Sampler), v_UV);\n}"
  },
  {
    "path": "src/shader/imgui.vert",
    "content": "#version 450\n\nlayout(set = 0, binding = 0) uniform View {\n  mat4 u_Matrix;\n};\n\nlayout(location = 0) in vec2 a_Pos;\nlayout(location = 1) in vec2 a_UV;\nlayout(location = 2) in uint a_Color;\n\nlayout(location = 0) out vec2 v_UV;\nlayout(location = 1) out vec4 v_Color;\n\n// Built-in:\n// vec4 gl_Position\n\nvoid main() {\n  v_UV = a_UV;\n  v_Color = vec4(a_Color & 0xFF, (a_Color >> 8) & 0xFF, (a_Color >> 16) & 0xFF, (a_Color >> 24) & 0xFF) / 255.0;\n  gl_Position = u_Matrix * vec4(a_Pos.xy, 0.0, 1.0);\n}"
  },
  {
    "path": "src/shader/line.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec2 v_min;\nlayout(location = 2) in vec2 v_max;\nlayout(location = 3) in float v_type;\nlayout(location = 4) in float v_count;\nlayout(location = 5) in float v_l;\nlayout(location = 6) in float v_w;\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    float alpha = 1-abs(v_TexCoord.y-0.5)/0.5;\n    alpha = pow(alpha,2);\n    alpha = min(alpha, pow(sin(v_TexCoord.x*v_l*0.2)*0.5+0.5,2)  )  ;\n\n     vec3 color = vec3(0);\n    if (v_type ==0 ){\n        color = vec3(0,0.5+ 0.5*pow(alpha,0.7),pow(alpha,0.7)*0.5);\n    } \n    else if (v_type ==1 ){\n        color = vec3(0,pow(alpha,0.7)*0.5,0.5+ 0.5*pow(alpha,0.7));\n    }\n    else if (v_type ==2 ){\n        color = vec3(0,0.5+pow(alpha,0.7)*0.5,0.5+ 0.5*pow(alpha,0.7));\n    }\n\n    \n    float calpha = pow(1.0/max(v_count-50.0,1.0),0.25);\n    alpha*=calpha;\n    o_Target = vec4(color,alpha);\n}\n"
  },
  {
    "path": "src/shader/line.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec2 v_min;\nlayout(location = 2) out vec2 v_max;\nlayout(location = 3) out float v_type;\nlayout(location = 4) out float v_count;\nlayout(location = 5) out float v_l;\nlayout(location = 6) out float v_w;\n\nlayout(location = 0) in vec2 min;\nlayout(location = 1) in vec2 max;\nlayout(location = 2) in float type;\nlayout(location = 3) in float count;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    v_min = min;\n    v_max = max;\n    v_type = type;\n    v_count = count;\n    v_l = length(max*resolution-min*resolution);\n    v_w = 8;\n\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(1.0, 0.0); break;\n        case 1: tc = vec2(1.0, 1.0); break;\n        case 2: tc = vec2(0.0, 0.0); break;\n        case 3: tc = vec2(0.0, 1.0); break;\n    }\n    v_TexCoord = tc;\n\n    vec2 u = normalize(max-min);\n    vec2 ortho = vec2(u.y,-u.x)*inv_resolution*v_w/2.0;\n\n\n    vec2 a = min -ortho;\n    vec2 b = min +ortho;\n    vec2 c = max -ortho;\n    vec2 d = max +ortho;\n\n    vec2 pos =   vec2(0.0)  ;\n     switch(gl_VertexIndex) {\n        case 0: pos = a; break;\n        case 1: pos = b; break;\n        case 2: pos = c; break;\n        case 3: pos = d; break;\n    }\n\n    gl_Position = vec4(pos , 0.5, 1.0);\n}\n"
  },
  {
    "path": "src/shader/post.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 v_TexCoord;\n\nvoid main() {\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(1.0, 0.0); break;\n        case 1: tc = vec2(1.0, 1.0); break;\n        case 2: tc = vec2(0.0, 0.0); break;\n        case 3: tc = vec2(0.0, 1.0); break;\n    }\n    v_TexCoord = tc;\n    gl_Position = vec4(tc * 2.0 - 1.0, 0.5, 1.0);\n}\n"
  },
  {
    "path": "src/shader/post_bicopy.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 1, binding = 0) uniform texture2D t_color;\nlayout(set = 1, binding = 1) uniform sampler s_color;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    vec3 color =  texture(sampler2D(t_color, s_color), v_TexCoord).rgb;\n    o_Target = vec4(color,1.0);\n}\n"
  },
  {
    "path": "src/shader/post_fxaa.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 1, binding = 0) uniform texture2D t_color;\nlayout(set = 1, binding = 1) uniform sampler s_color;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\n\nfloat rgb2luma(vec3 rgb){\n    return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114)));\n}\n\nfloat luma(vec2 offset){\n    return rgb2luma(texture(sampler2D(t_color, s_color), v_TexCoord + offset*inv_resolution).rgb);\n}\n\nvoid main() {\n\n\n    vec3 color =  texture(sampler2D(t_color, s_color), v_TexCoord).rgb;\n    float l= luma(vec2(0));\n\n    float l_n = luma(vec2(0,1));\n    float l_s = luma(vec2(0,-1));\n    float l_e = luma(vec2(1,0));\n    float l_w = luma(vec2(-1,0));\n\n    float lumaMin = min(l,min(min(l_s,l_n),min(l_e,l_w)));\n    float lumaMax = max(l,max(max(l_s,l_n),max(l_e,l_w)));\n\n    float lumaRange = lumaMax - lumaMin;\n\n    if ( lumaRange> min(0.0312 , lumaMax*0.125)){\n        float l_ne = luma(vec2(1,1));\n        float l_se = luma(vec2(1,-1));\n        float l_nw = luma(vec2(-1,1));\n        float l_sw = luma(vec2(-1,-1));\n\n        float l_ns = l_n + l_s;\n        float l_we = l_e + l_w;\n\n\n        float l_nc = l_nw + l_ne;\n        float l_sc = l_se + l_sw;\n        float l_ec = l_se + l_ne;\n        float l_wc = l_nw + l_sw;\n\n        float edgeHorizontal =  abs(-2.0 * l_w + l_wc)  +\n         abs(-2.0 * l + l_ns ) * 2.0     +\n        abs(-2.0 * l_e + l_ec);\n\n        float edgeVertical =    abs(-2.0 * l_n + l_nc)  +\n         abs(-2.0 * l + l_we) * 2.0   + \n         abs(-2.0 * l_s + l_sc);\n\n        bool isHorizontal = (edgeHorizontal >= edgeVertical);\n        // color = vec3(0.5+ (edgeVertical- edgeHorizontal)/2.0);\n\n\n\n        // Select the two neighboring texels lumas in the opposite direction to the local edge.\n        float luma1 = isHorizontal ? l_s : l_w;\n        float luma2 = isHorizontal ? l_n : l_e;\n        // Compute gradients in this direction.\n        float gradient1 = luma1 - l;\n        float gradient2 = luma2 - l;\n\n        // Which direction is the steepest ?\n        bool is1Steepest = abs(gradient1) >= abs(gradient2);\n\n        // Gradient in the corresponding direction, normalized.\n        float gradientScaled = 0.25*max(abs(gradient1),abs(gradient2));\n        // color = vec3(gradientScaled);\n\n\n\n        // Choose the step size (one pixel) according to the edge direction.\n        float stepLength = isHorizontal ? inv_resolution.y : inv_resolution.x;\n\n        // Average luma in the correct direction.\n        float lumaLocalAverage = 0.0;\n\n        if(is1Steepest){\n            // Switch the direction\n            stepLength = - stepLength;\n            lumaLocalAverage = 0.5*(luma1 + l);\n        } else {\n            lumaLocalAverage = 0.5*(luma2 + l);\n        }\n\n        // Shift UV in the correct direction by half a pixel.\n        vec2 currentUv = v_TexCoord;\n        if(isHorizontal){\n            currentUv.y += stepLength * 0.5;\n        } else {\n            currentUv.x += stepLength * 0.5;\n        }\n\n        ///First iteration exploration\n\n        // Compute offset (for each iteration step) in the right direction.\n        vec2 offset = isHorizontal ? vec2(inv_resolution.x,0.0) : vec2(0.0,inv_resolution.y);\n        // Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster.\n        vec2 uv1 = currentUv - offset;\n        vec2 uv2 = currentUv + offset;\n\n        // Read the lumas at both current extremities of the exploration segment, and compute the delta wrt to the local average luma.\n        float lumaEnd1 = rgb2luma(texture(sampler2D(t_color, s_color), uv1).rgb);\n        float lumaEnd2 = rgb2luma(texture(sampler2D(t_color, s_color), uv2).rgb);\n        lumaEnd1 -= lumaLocalAverage;\n        lumaEnd2 -= lumaLocalAverage;\n\n        // If the luma deltas at the current extremities are larger than the local gradient, we have reached the side of the edge.\n        bool reached1 = abs(lumaEnd1) >= gradientScaled;\n        bool reached2 = abs(lumaEnd2) >= gradientScaled;\n        bool reachedBoth = reached1 && reached2;\n\n        // If the side is not reached, we continue to explore in this direction.\n        if(!reached1){\n            uv1 -= offset;\n        }\n        if(!reached2){\n            uv2 += offset;\n        }  \n\n\n        // If both sides have not been reached, continue to explore.\n        if(!reachedBoth){\n\n            float QUALITY[12]= float[12](1.0,1.0,1.0,1.0,1.0,1.5, 2.0, 2.0, 2.0, 2.0, 4.0, 8.0);\n            for(int i = 2; i < 12; i++){\n                // If needed, read luma in 1st direction, compute delta.\n                if(!reached1){\n                    lumaEnd1 = rgb2luma(texture(sampler2D(t_color, s_color), uv1).rgb);\n                    lumaEnd1 = lumaEnd1 - lumaLocalAverage;\n                }\n                // If needed, read luma in opposite direction, compute delta.\n                if(!reached2){\n                    lumaEnd2 = rgb2luma(texture(sampler2D(t_color, s_color), uv2).rgb);\n                    lumaEnd2 = lumaEnd2 - lumaLocalAverage;\n                }\n                // If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge.\n                reached1 = abs(lumaEnd1) >= gradientScaled;\n                reached2 = abs(lumaEnd2) >= gradientScaled;\n                reachedBoth = reached1 && reached2;\n\n                // If the side is not reached, we continue to explore in this direction, with a variable quality.\n                if(!reached1){\n                    uv1 -= offset * QUALITY[i];\n                }\n                if(!reached2){\n                    uv2 += offset * QUALITY[i];\n                }\n\n             \n                // If both sides have been reached, stop the exploration.\n                if(reachedBoth){ break;}\n            }\n        }\n\n\n        // Compute the distances to each extremity of the edge.\n        float distance1 = isHorizontal ? (v_TexCoord.x - uv1.x) : (v_TexCoord.y - uv1.y);\n        float distance2 = isHorizontal ? (uv2.x - v_TexCoord.x) : (uv2.y - v_TexCoord.y);\n\n        // In which direction is the extremity of the edge closer ?\n        bool isDirection1 = distance1 < distance2;\n        float distanceFinal = min(distance1, distance2);\n\n        // Length of the edge.\n        float edgeThickness = (distance1 + distance2);\n\n        // UV offset: read in the direction of the closest side of the edge.\n        float pixelOffset = - distanceFinal / edgeThickness + 0.5;\n\n        // Is the luma at center smaller than the local average ?\n        bool isLumaCenterSmaller = l < lumaLocalAverage;\n\n        // If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation).\n        // (in the direction of the closer side of the edge.)\n        bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller;\n\n        // If the luma variation is incorrect, do not offset.\n        float finalOffset = correctVariation ? pixelOffset : 0.0;\n\n        // Sub-pixel shifting\n        // Full weighted average of the luma over the 3x3 neighborhood.\n        float lumaAverage = (1.0/12.0) * (2.0 * (l_ns + l_we) + l_ec + l_wc);\n        // Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood.\n        float subPixelOffset1 = clamp(abs(lumaAverage - l)/lumaRange,0.0,1.0);\n        float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1;\n        // Compute a sub-pixel offset based on this delta.\n        float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * 0.75;\n\n        // Pick the biggest of the two offsets.\n        finalOffset = max(finalOffset,subPixelOffsetFinal);\n        // color = vec3(finalOffset);\n\n        // Compute the final UV coordinates.\n        vec2 finalUv = v_TexCoord;\n        if(isHorizontal){\n            finalUv.y += finalOffset * stepLength;\n        } else {\n            finalUv.x += finalOffset * stepLength;\n        }\n\n        // Read the color at the new UV coordinates, and use it.\n        color = texture(sampler2D(t_color, s_color), finalUv).rgb;\n    }\n \n\n    o_Target = vec4(color,1.0);\n}\n"
  },
  {
    "path": "src/shader/post_ui.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 1, binding = 0) uniform texture2D t_pos;\nlayout(set = 1, binding = 1) uniform sampler s_pos;\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n\n\n    vec4 pos_attachment =  texture(sampler2D(t_pos, s_pos), v_TexCoord);\n    float alpha= 0;\n    vec3 color = vec3(1.0);\n\n    if (pen_radius >0){\n        vec3 frag_pos = pos_attachment.xyz;\n        color = vec3(1.0);\n        //Cursor circle\n        vec3 mouse_pos_world = texture(sampler2D(t_pos, s_pos), mouse_pos/resolution).xyz;\n        float dist = length(frag_pos.xy - mouse_pos_world.xy);\n        float distance_to_sphere  = length(pen_radius - dist);\n        float base = clamp(1-distance_to_sphere,0.0,1.0);\n        alpha=  pow(min(base,0.01)/0.01,2.0);\n        if (dist < pen_radius){\n            alpha= max(alpha, 0.5*pen_strength/10.0 );\n        }\n        if (mouse_pos_world.x <0.0 || frag_pos.x < 0.0 || frag_pos.y <0.0){\n            alpha = 0;\n        }\n    }\n \n    // //Unit selection\n    bool is_selected_area = pos_attachment.a >= 0.99;\n\n\n    float highlight_index= 0.0;\n    bool is_edge_area = false;\n    for(int i = -1; i<= 1; i++){\n        for(int j = -1; j<= 1; j++){\n            if (!is_edge_area && (i!= 0 || j!= 0)){\n                highlight_index = round(texture(\n                    sampler2D(t_pos, s_pos), \n                    v_TexCoord + vec2(i,j)/resolution).a);\n                is_edge_area = highlight_index> 0.0;\n            }\n        }\n    }\n\n    bool is_edge2_area = false;\n    if (!is_edge_area){\n    for(int i = -2; i<= 2; i++){\n            for(int j = -2; j<= 2; j++){\n                if ((max(i,j)==2 || min(i,j)==-2 ) && !is_edge2_area){\n                    is_edge2_area = is_edge2_area||\n                    texture(\n                        sampler2D(t_pos, s_pos),\n                        v_TexCoord + vec2(i,j)/resolution).a >=0.99;\n                }\n            }\n        }\n    }\n\n  \n    vec3 highlight_color = vec3(0,1,0);\n    if (highlight_index ==2){\n        highlight_color = vec3(0.0,0.5,1);\n    } else if (highlight_index ==3){\n        highlight_color = vec3(0.4,1,0.4);\n    } \n    \n    if (!is_selected_area && is_edge_area ){\n        alpha = 1.0;\n        color = highlight_color;\n    } else if(!is_selected_area && is_edge2_area){\n        alpha = 0.5;\n        color = mix(highlight_color,vec3(0),0.5);\n    }\n\n    if (start_drag != mouse_pos){\n        uvec2 coord_screen = uvec2(v_TexCoord*resolution);\n        vec2 min_ = vec2(min(start_drag.x,mouse_pos.x), min(start_drag.y,mouse_pos.y));\n        vec2 max_ = vec2(max(start_drag.x,mouse_pos.x), max(start_drag.y,mouse_pos.y));\n\n        if (coord_screen.x > min_.x && coord_screen.y > min_.y&&\n        coord_screen.x < max_.x && coord_screen.y < max_.y){\n            color = mix(vec3(0.0,1.0,0.3), color, 0.1);\n            alpha = max(alpha, 0.1);\n        } else if (coord_screen.x >= min_.x && coord_screen.y >= min_.y&&\n        coord_screen.x <= max_.x && coord_screen.y <= max_.y){\n            color = mix(vec3(0.0,0.7,0.1), color, 0.8);\n            alpha = max(alpha, 0.8);\n        }\n    }\n\n    o_Target = vec4(color,alpha);\n\n    // o_Target= vec4(pos_attachment.xyz,1.0);\n}\n"
  },
  {
    "path": "src/shader/unit_icon.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) in vec2 v_center;\nlayout(location = 2) in float v_size;\nlayout(location = 3) in float v_team;\n\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n\n    vec3 color = vec3(1.0);\n\n    if (v_team< 0){\n        //Unit is selected\n        color = vec3(1.0);\n    }\n    else if( v_team == 0.0){\n        color = vec3(0,0.3,1);\n    }else if(v_team < 1.1){\n        color = vec3(1,0.0,0);\n    }\n    float alpha=1;\n\n    float distance_from_center = length(v_TexCoord-vec2(0.5))*sqrt(2);\n    float circle_radius = 0.4;\n    float distance_from_circle = abs(circle_radius - distance_from_center);\n    float proximity = 1-distance_from_circle;\n    float thickness = 0.90;\n    float proximity_thick = min(proximity,thickness)/thickness;\n     alpha = pow(proximity_thick,6);\n\n    color*= pow(alpha,2);\n\n    o_Target = vec4(color,alpha);\n}\n"
  },
  {
    "path": "src/shader/unit_icon.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) out vec2 v_center;\nlayout(location = 2) out float v_size;\nlayout(location = 3) out float v_team;\n\n\nlayout(location = 0) in vec2 center;\nlayout(location = 1) in float size;\nlayout(location = 2) in float team;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    v_center = center;\n    v_size = size;\n    v_team = team;\n\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(1.0, 0.0); break;\n        case 1: tc = vec2(1.0, 1.0); break;\n        case 2: tc = vec2(0.0, 0.0); break;\n        case 3: tc = vec2(0.0, 1.0); break;\n    }\n    v_TexCoord = tc;\n\n    vec2 min = -inv_resolution*size;\n    vec2 max = inv_resolution*size;\n\n    vec2 pos =   center  ;\n     switch(gl_VertexIndex) {\n        case 0: pos += vec2(max.x, min.y); break;\n        case 1: pos += vec2(max.x, max.y); break;\n        case 2: pos += vec2(min.x, min.y); break;\n        case 3: pos += vec2(min.x, max.y); break;\n    }\n\n    gl_Position = vec4(pos , 0.5, 1.0);\n}\n"
  },
  {
    "path": "src/shader/water.frag",
    "content": "#version 450\n\nlayout(location = 0) in vec2 v_TexCoord;\nlayout(location = 1) flat in int v_floor_lwall_fwall_rwall;\n\n\nlayout(location = 0) out vec4 o_Target;\n\nlayout(set = 1, binding = 0) uniform texture2D t_color;\nlayout(set = 1, binding = 1) uniform sampler s_color;\n\n\nlayout(set = 1, binding = 2) uniform texture2D t_pos;\nlayout(set = 1, binding = 3) uniform sampler s_pos;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nint max_step = 40;\n\nvoid main() {\n    float water_level = 40;\n    vec3 world_pos = vec3(v_TexCoord*hmap_size,water_level);\n    vec4 view_pos4 = u_View* vec4(world_pos,1.0);\n    vec3 view_pos  = view_pos4.xyz/ view_pos4.w;\n    vec3 normal = vec3(0,0,1);\n    vec3 view_normal = mat3(u_Normal)*normal;\n    vec3 reflected = normalize(reflect(normalize(view_pos), normalize(view_normal)));\n    vec3 current =  view_pos;\n    vec3 current_dir = reflected*15;\n    bool found = false;\n\n\n    int j = 0;\n    if(v_floor_lwall_fwall_rwall==0)\n    for(int i =0; i<max_step;i++){\n        j=i;\n        current += current_dir;\n        vec4 projected=  u_proj* vec4(current, 1);\n        projected.xy /= projected.w;\n        projected.xy =projected.xy*0.5 +0.5;\n\n        if (projected.x < 0 ||projected.y<0 || projected.x>1 || projected.y>1 ){\n            j=max_step;\n            break;\n        }\n        vec4 world = texture(sampler2D(t_pos, s_pos), projected.xy);\n        if ( !(world.w >-0.01) ){\n            j=max_step;\n            break;\n        }\n\n        vec4 view=  u_View * vec4(world.xyz,1.0);\n        float depth = view.z/view.w;\n        if(depth>current.z ){\n            //We overshot, go back with binary search\n            found = true;\n\n            current_dir*=0.5;\n            current -= current_dir;\n            for(int k=0; k< 5; k++){\n                current_dir*=0.5;\n                vec4 projected=  u_proj* vec4(current, 1);\n                projected.xy /= projected.w;\n                projected.xy =projected.xy*0.5 +0.5;\n                vec4 world = texture(sampler2D(t_pos, s_pos), projected.xy);\n                vec4 view=  u_View * vec4(world.xyz,1.0);\n                float depth = view.z/view.w;\n                if(depth > current.z){\n                    current -= current_dir;\n                }\n                else{\n                    current += current_dir;\n                }\n            }\n            break;\n        }\n\n    }\n     \n    vec3 sky_color= vec3(0.1,0.2,0.3);\n    vec3 ref_color= sky_color;\n    if(found){\n        vec4 projected=  u_proj* vec4(current, 1);\n        projected.xy /= projected.w;\n        projected.xy =projected.xy*0.5 +0.5;\n\n        float alpha =  pow(max(abs(0.5-projected.x),\n        abs(0.5-projected.y))/0.5,4);\n        ref_color = mix(\n            texture(sampler2D(t_color, s_color), projected.xy).xyz,\n            ref_color, alpha);\n    }\n    //if wall\n     if(v_floor_lwall_fwall_rwall!=0){\n         ref_color*=ref_color;\n     }\n\n    o_Target = vec4(ref_color,1.0);\n    o_Target = vec4(vec3(float(j)/float(max_step)),1.0);\n    o_Target = vec4(vec3(float(found)/1.0),1.0);\n    vec3 water_color = vec3(0.3,0.5,1.0);\n    o_Target = vec4(mix(water_color,ref_color ,0.8),0.9);\n}\n"
  },
  {
    "path": "src/shader/water.vert",
    "content": "#version 450\n\nlayout(location = 0) out vec2 v_TexCoord;\nlayout(location = 1) flat out int v_floor_lwall_fwall_rwall;\n\nlayout(set = 0, binding = 0) uniform Locals {\n    mat4 cor_proj_view;\n    mat4 u_View;\n    mat4 u_proj;\n    mat4 u_Normal;\n    vec2 mouse_pos;\n    vec2 resolution;\n    vec2 inv_resolution;\n    vec2 start_drag;\n    float pen_radius;\n    float pen_strength;\n    vec2 hmap_size;\n};\n\nvoid main() {\n    float min = -0.00000;\n    float max = 1.0-min;\n    vec2 tc = vec2(0.0);\n    switch(gl_VertexIndex) {\n        case 0: tc = vec2(max, min); break;\n        case 1: tc = vec2(max, max); break;\n        case 2: tc = vec2(min, min); break;\n        case 3: tc = vec2(min, max); break;\n    }\n    v_TexCoord = tc;\n\n    v_floor_lwall_fwall_rwall = gl_InstanceIndex;\n    float water_level = 40; \n    vec3 pos = vec3(0); \n \n    switch(v_floor_lwall_fwall_rwall){\n        case 0: \n            pos = vec3(tc*hmap_size,water_level);\n            break;\n        case 1: \n            if(tc== vec2(max,max)){\n                tc= vec2(min,min);\n            }else if(tc== vec2(min,min)){\n                tc= vec2(max,max);\n            }\n            pos = vec3(min*hmap_size.x, tc* vec2( hmap_size.x ,water_level*1.001));\n            break;\n        case 2: \n            pos = vec3(tc.x* hmap_size.x,min*hmap_size.y, tc.y* water_level*1.001);\n            break;\n        case 3: \n            pos = vec3(max*hmap_size.x, tc* vec2( hmap_size.x ,water_level*1.001));\n            break;\n    }\n\n    \n    gl_Position = cor_proj_view *vec4(pos,1.0);\n}\n"
  },
  {
    "path": "src/unit.rs",
    "content": "use super::client::*;\nuse crate::model::*;\nuse crate::utils::FileTree;\nuse crate::*;\nuse gpu_obj::model_gpu::ModelGpu;\nuse na::{Matrix4, Point3, Vector2, Vector3};\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse std::path::{Path, PathBuf};\n\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\npub struct PlacedMesh {\n    pub trans: Matrix4<f32>,\n    pub mesh_path: PathBuf,\n    pub mesh_index: usize,\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\npub enum PlacedCollider {\n    Sphere { position: Point3<f32>, radius: f32 },\n}\n\n#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]\npub enum Joint {\n    Fix,\n    AimWeapon0,\n    Wheel0,\n}\n\nimpl Joint {\n    pub fn next(&self) -> Self {\n        match self {\n            Joint::Fix => Joint::AimWeapon0,\n            Joint::AimWeapon0 => Joint::Wheel0,\n            Joint::Wheel0 => Joint::Fix,\n        }\n    }\n\n    pub fn replace_with_next(&mut self) {\n        let next = self.next();\n        std::mem::replace(self, next);\n    }\n}\n\n#[derive(Debug, Clone, typename::TypeName, PartialEq, Serialize, Deserialize)]\npub struct PartTree {\n    pub id: utils::Id<PartTree>,\n    pub placed_mesh: Option<PlacedMesh>,\n    pub placed_collider: Option<PlacedCollider>,\n    pub parent_to_self: Matrix4<f32>,\n    pub joint: Joint,\n    pub children: Vec<PartTree>,\n}\npub struct PartTreeIter<'a> {\n    stack: Vec<&'a PartTree>,\n}\n\nimpl<'a> Iterator for PartTreeIter<'a> {\n    type Item = &'a PartTree;\n    fn next(&mut self) -> Option<&'a PartTree> {\n        let item = self.stack.pop()?;\n        for c in item.children.iter() {\n            self.stack.push(c)\n        }\n        Some(item)\n    }\n}\n\nimpl PartTree {\n    pub fn iter(&self) -> PartTreeIter {\n        PartTreeIter { stack: vec![self] }\n    }\n\n    pub fn find_node_mut(&mut self, id: utils::Id<PartTree>) -> Option<&mut PartTree> {\n        if self.id == id {\n            Some(self)\n        } else {\n            for c in self.children.iter_mut() {\n                match c.find_node_mut(id) {\n                    Some(node) => return Some(node),\n                    None => {}\n                }\n            }\n            None\n        }\n    }\n    pub fn find_node(&self, id: utils::Id<PartTree>) -> Option<&PartTree> {\n        if self.id == id {\n            Some(self)\n        } else {\n            for c in self.children.iter() {\n                match c.find_node(id) {\n                    Some(node) => return Some(node),\n                    None => {}\n                }\n            }\n            None\n        }\n    }\n\n    ///Remove a node and return the parent if successful\n    pub fn remove_node(&mut self, id: utils::Id<PartTree>) -> Option<utils::Id<PartTree>> {\n        let pos = self.children.iter().position(|e| e.id == id);\n        match pos {\n            Some(index) => {\n                self.children.remove(index);\n                Some(self.id)\n            }\n            None => {\n                let mut res = None;\n                for c in self.children.iter_mut() {\n                    res = res.or(c.remove_node(id));\n                }\n                res\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils.rs",
    "content": "use base_62::base62;\nuse na::{IsometryMatrix3, Matrix4, Point3, Vector2, Vector3, Vector4};\nuse rand::seq::SliceRandom;\nuse rand::thread_rng;\nuse serde::{Deserialize, Serialize};\nuse std::collections::{HashMap, HashSet};\nuse std::fmt;\nuse std::hash::Hash;\nuse std::hash::Hasher;\n\npub fn face_towards_dir(\n    pos: &Vector3<f32>,\n    normalized_dir: &Vector3<f32>,\n    normalized_up: &Vector3<f32>,\n) -> Matrix4<f32> {\n    let x_axis = normalized_dir;\n    let y_axis = normalized_up.cross(&x_axis);\n    let z_axis = x_axis.cross(&y_axis);\n\n    Matrix4::new(\n        x_axis.x, y_axis.x, z_axis.x, pos.x, //\n        x_axis.y, y_axis.y, z_axis.y, pos.y, //\n        x_axis.z, y_axis.z, z_axis.z, pos.z, //\n        0.0, 0.0, 0.0, 1.0,\n    )\n}\n\nconst ID_CHARS: [char; 62] = [\n    'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L',\n    'M', 'W', 'X', 'C', 'V', 'B', 'N', 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'q', 's',\n    'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'w', 'x', 'c', 'v', 'b', 'n', '0', '1', '2', '3', '4',\n    '5', '6', '7', '8', '9',\n];\nconst ID_SIZE: usize = 5;\n\ntrait IdBase {\n    type Type;\n}\n\npub type IdValue = u64;\n\n#[derive(Serialize, Deserialize)]\npub struct Id<T> {\n    pub value: IdValue,\n    phantom: std::marker::PhantomData<T>,\n}\n\nimpl<T> Id<T> {\n    pub fn new(value: IdValue) -> Self {\n        Id {\n            value,\n            phantom: std::marker::PhantomData,\n        }\n    }\n}\n\nimpl<T: typename::TypeName> fmt::Display for Id<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        write!(f, \"{:?}\", self)\n    }\n}\n\nimpl<T: typename::TypeName> fmt::Debug for Id<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        let x: [u8; 8] = unsafe { std::mem::transmute(self.value.to_le()) };\n\n        let name = format!(\"{:?}\", T::type_name());\n\n        let simple_name = name.split(\"::\").last().unwrap();\n        let simple_name = &simple_name[..simple_name.len() - 1];\n        write!(\n            f,\n            \"{}#{}\",\n            simple_name,\n            base62::encode(&x) // format!(\"{:X}\", self.value)\n        )\n    }\n}\n\nimpl<T> Clone for Id<T> {\n    fn clone(&self) -> Self {\n        Self::new(self.value)\n    }\n}\nimpl<T> Copy for Id<T> {}\n\nimpl<T> PartialEq for Id<T> {\n    fn eq(&self, other: &Self) -> bool {\n        self.value == other.value\n    }\n}\n\nimpl<T> Hash for Id<T> {\n    fn hash<H>(&self, state: &mut H)\n    where\n        H: Hasher,\n    {\n        self.value.hash(state);\n    }\n}\n\nimpl<T> Eq for Id<T> {}\n\nimpl<T> IdBase for Id<T> {\n    type Type = T;\n}\n\npub fn rand_id<T>() -> Id<T> {\n    Id::new(rand::prelude::random())\n}\n\npub fn rand_id_unsafe() -> String {\n    let mut rng = thread_rng();\n    let mut s = String::with_capacity(ID_SIZE);\n    for _ in 0..ID_SIZE {\n        s.push(*ID_CHARS.choose(&mut rng).unwrap());\n    }\n    s\n}\n\npub fn pop_set<T: Clone + Eq + std::hash::Hash>(set: &mut HashSet<T>) -> T {\n    let elt = set.iter().next().cloned().unwrap();\n    set.take(&elt).unwrap()\n}\n\npub fn time<F, K>(f: F) -> std::time::Duration\nwhere\n    F: FnOnce() -> K,\n{\n    let start = std::time::Instant::now();\n    f();\n    start.elapsed()\n}\nuse std::fs::{self, DirEntry};\nuse std::io;\nuse std::path::{Path, PathBuf};\n\n#[derive(Clone, Debug)]\npub enum FileTree {\n    Unknown,\n    Node {\n        path: PathBuf,\n        children: Vec<FileTree>,\n    },\n    Leaf {\n        path: PathBuf,\n    },\n}\n\nimpl FileTree {\n    pub fn new(path: PathBuf) -> Self {\n        if path.is_dir() {\n            let mut nodes = Vec::new();\n            for entry in fs::read_dir(path.clone()).unwrap() {\n                let entry = entry.unwrap();\n                let path = entry.path();\n                nodes.push(Self::new(path));\n            }\n            FileTree::Node {\n                path: path.to_owned(),\n                children: nodes,\n            }\n        } else {\n            FileTree::Leaf { path }\n        }\n    }\n}\n\npub struct ImageRGBA8 {\n    pub w: u32,\n    pub h: u32,\n    pub data: Vec<u8>,\n}\n\nimpl ImageRGBA8 {\n    pub fn open(path: &str) -> ImageRGBA8 {\n        use byteorder::{BigEndian, ReadBytesExt};\n        use std::fs::File;\n\n        // The decoder is a build for reader and can be used to set various decoding options\n        // via `Transformations`. The default output transformation is `Transformations::EXPAND\n        // | Transformations::STRIP_ALPHA`.\n        let mut decoder = png::Decoder::new(File::open(path).unwrap());\n        decoder.set_transformations(png::Transformations::IDENTITY);\n        let (info, mut reader) = decoder.read_info().unwrap();\n\n        // Display image metadata.\n        log::debug!(\"info: {:?}\", info.width);\n        log::debug!(\"height: {:?}\", info.height);\n        log::debug!(\"bit depth: {:?}\", info.bit_depth);\n        log::debug!(\"buffer size: {:?}\", info.buffer_size());\n\n        // Allocate the output buffer.\n        let mut buf = vec![0; info.buffer_size()];\n        // Read the next frame. Currently this function should only called once.\n        // The default options\n        reader.next_frame(&mut buf).unwrap();\n        ImageRGBA8 {\n            w: info.width,\n            h: info.height,\n            data: buf,\n        }\n    }\n}\n"
  }
]