Repository: Ruddle/oxidator Branch: master Commit: ba77b3419a1c Files: 106 Total size: 594.5 KB Directory structure: gitextract_h9fjhbh5/ ├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md └── src/ ├── asset/ │ ├── 3d/ │ │ ├── arrow.obj │ │ ├── axis_debug.obj │ │ ├── cube.obj │ │ ├── small_sphere.obj │ │ └── tank/ │ │ ├── base.obj │ │ ├── canon.obj │ │ └── wheel.obj │ ├── botdef/ │ │ ├── building_example.json │ │ └── unit_example.json │ └── map/ │ └── map_example/ │ └── data.json ├── botdef.rs ├── client/ │ ├── camera.rs │ ├── game_state.rs │ ├── heightmap_editor.rs │ ├── input_state.rs │ ├── misc.rs │ ├── mod.rs │ ├── play.rs │ ├── render.rs │ ├── uitool.rs │ ├── unit_editor.rs │ └── unit_part_gpu.rs ├── frame.rs ├── frame_server/ │ └── mod.rs ├── glsl.rs ├── gpu_obj/ │ ├── arrow_gpu.rs │ ├── blit_texture.rs │ ├── explosion.rs │ ├── glsl_compiler.rs │ ├── gpu.rs │ ├── health_bar.rs │ ├── heightmap_gpu.rs │ ├── heightmap_helper.rs │ ├── imgui_wgpu.rs │ ├── line.rs │ ├── mod.rs │ ├── model_gpu.rs │ ├── post_fx.rs │ ├── post_fxaa.rs │ ├── texture_view_bicopy.rs │ ├── trait_gpu.rs │ ├── unit_icon.rs │ └── water.rs ├── heightmap_phy.rs ├── main.rs ├── manager.rs ├── mobile.rs ├── moddef.rs ├── model.rs ├── net_client.rs ├── net_server.rs ├── procedural_texels.rs ├── shader/ │ ├── arrow.frag │ ├── arrow.vert │ ├── blit_texture.frag │ ├── blit_texture.vert │ ├── compiled/ │ │ ├── arrow.frag.spirv │ │ ├── arrow.vert.spirv │ │ ├── blit_texture.frag.spirv │ │ ├── blit_texture.vert.spirv │ │ ├── cube_instanced.frag.spirv │ │ ├── cube_instanced.vert.spirv │ │ ├── explosion.frag.spirv │ │ ├── explosion.vert.spirv │ │ ├── health_bar.frag.spirv │ │ ├── health_bar.vert.spirv │ │ ├── heightmap.frag.spirv │ │ ├── heightmap.vert.spirv │ │ ├── imgui.frag.spirv │ │ ├── imgui.vert.spirv │ │ ├── line.frag.spirv │ │ ├── line.vert.spirv │ │ ├── post.vert.spirv │ │ ├── post_bicopy.frag.spirv │ │ ├── post_fxaa.frag.spirv │ │ ├── post_ui.frag.spirv │ │ ├── unit_icon.frag.spirv │ │ ├── unit_icon.vert.spirv │ │ ├── water.frag.spirv │ │ └── water.vert.spirv │ ├── cube_instanced.frag │ ├── cube_instanced.vert │ ├── explosion.frag │ ├── explosion.vert │ ├── health_bar.frag │ ├── health_bar.vert │ ├── heightmap.frag │ ├── heightmap.vert │ ├── imgui.frag │ ├── imgui.vert │ ├── line.frag │ ├── line.vert │ ├── post.vert │ ├── post_bicopy.frag │ ├── post_fxaa.frag │ ├── post_ui.frag │ ├── unit_icon.frag │ ├── unit_icon.vert │ ├── water.frag │ └── water.vert ├── unit.rs └── utils.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ /target **/*.rs.bk .idea *.iml .vscode/tasks.json *.blend *.blend1 ================================================ FILE: Cargo.toml ================================================ [package] name = "oxidator" version = "0.2.0" authors = ["Thomas SIMON "] edition = "2018" default-run = "oxidator" [features] #Choose one method of loading spirv out of the three. default uses the precompiled shaders, but does not support hot-reloading. use_shaderc =["shaderc"] use_glsl_to_spirv =["glsl-to-spirv"] default = [] [dependencies] wgpu = { version = "0.4.0" } env_logger = "0.7.1" glsl-to-spirv = {version= "0.1", optional= true} log = "0.4" png = "0.15" winit = "=0.20.0-alpha4" raw-window-handle = "0.3.3" imgui = "0.2.1" shaderc = {version = "0.6", optional = true} noise = "0.6.0" nalgebra = {version= "0.19", features= ["serde-serialize"]} crossbeam-channel = "0.3" rand = "0.7.3" notify = "=5.0.0-pre.1" byteorder = "1.3.2" typename = "0.1.2" base-62 = "0.1.1" obj-rs = "0.5" spin_sleep = "0.3.7" serde = { version = "1.0", features = ["derive"] } bincode = "1.2.1" serde_json = "1.0.46" flate2 = "1.0" rayon = "1.3.0" fnv = "1.0.6" [dependencies.imgui-winit-support] version = "0.2.1" default-features = false features = ["winit-20"] [[bin]] name = "oxidator" path = "src/main.rs" [profile.release] #lto = true ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Thomas SIMON Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # What is this? A real time strategy game/engine written with Rust and WebGPU. Eventually it will be able to run in a web browser thanks to WebGPU. This project is inspired by Total Annihilation, Supreme Commander, [Spring Engine](https://springrts.com/) and [Zero-k](https://zero-k.info/). ## Demo ![Map editor](etc/map_editor.gif) Map editor [HQ version](https://streamable.com/9vn0z) ![Unit editor](etc/unit_editor.gif) Unit editor [HQ version](https://streamable.com/ywr44) ![Play mode](etc/play.gif) Gameplay (35000 units) [HQ version](https://streamable.com/499j0) ## Goal The 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. Those tool would be comprised of : - Map editor - Unit editor * 3d model import, animation, behavior definition (simple visual programming language, non turing complete), formation ... - Mod(set of units) editor - Online repository to publish maps, units, and mods. - Multiplayer lobby and client/server system * Where player can select a tuple of map and mod to play with others * Aiming for a quite higher latency than usual (100ms + ping) between server and clients - High performance multithreaded renderer * Aiming for 100k moving units at 60fps, 1080p with middle-end 2016 hardware - High performance multithreaded simulation * same goal than the renderer All in one executable. the 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. ## Non-Goals * General purpose modding/ turing complete scripting language. * 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 ^^). * 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. * 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). * 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. ## Features - Map editor - [x] raise, lower, flatten, blur, noise pencil - [ ] texture layer - [ ] changeable map size/water level - [ ] resources placing - [ ] save and load from file system (try the current save, load button at your own risk) - [ ] save and load from online repository - Unit editor - [x] basic imgui editor to create node hierarchy - [x] joint selection (Fix, weapon aim, wheel) - [x] mesh selection - [x] parameter editing (speed, turn rate, health...) - [ ] save/load from filesystem - [ ] graphical editor - [ ] integration with online repository - Mod editor - [ ] N/A - Online repository - [ ] N/A - Multiplayer - [x] working PoC localhost tcp client/server (1/2 will fry your computer and consume 1 Mo/s) - [ ] optimise to reach 300 Ko/sec with 100k units moving - [ ] lobby - [ ] live swapping host if current host disconnect - [ ] simple chat - [ ] ability to draw on the map, and tag place/units - Rendering - [x] basic display of 3D models (with instancing) - [x] basic display a heightmap (from [this blog](http://casual-effects.blogspot.com/2014/04/fast-terrain-rendering-with-continuous.html)) - [x] fxaa (from [this blog](http://blog.simonrodriguez.fr/articles/30-07-2016_implementing_fxaa.html)) - [x] screen space reflection for water - [ ] materials - [ ] particles - [ ] sounds - [ ] animation system - [ ] inverted kinematics - Simulation - [x] working draft of collision detection - [x] working draft of flock behavior - [x] basic health and damage computation - [x] construction and repair - [ ] detection (visual and radar) - [ ] user-defined AI for units (follow target, formation, flee, target selection etc) - [ ] resource counting - [ ] integrating pathfinding (I already built a working flowfield pathfinding [here](https://github.com/Ruddle/rustfield)) - UI - [x] select units (picking and rectangle selection) - [x] give move order - [x] give build order - [x] display current order (Hold LShift) - [ ] give user defined, unit specific order - [ ] display info about game state (current resources etc) - [ ] display info about selected units - [ ] display statistics ## Supported platforms * Windows (dx12 and vulkan) * Linux (vulkan) * Mac Untested (*should just work TM*) All 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. ## Build ```text git clone https://github.com/Ruddle/oxidator cd oxidator cargo run --release ``` ## Fun stuff if you clone this Shaders are automatically hot-reloaded if you change any .frag or .vert file and you compiled with either shaderc ```text cargo run --features use_shaderc --release ``` or glsl_to_spirv ```text cargo run --features use_glsl_to_spirv --release ``` Logs can be enabled via the [environment variable RUST_LOG](https://github.com/sebasmagri/env_logger/). for instance: ```text RUST_LOG=oxidator=debug cargo run --release ``` My go-to command when I develop: ```text RUST_LOG=oxidator=debug cargo run --features use_shaderc --release ``` You 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. ```text cargo run --features use_shaderc --release compile ``` ## Roadmap I push features that I feel like pushing in the moment. This project could (and probably will) lose its only contributor any time before any playable milestone is reached. ================================================ FILE: src/asset/3d/arrow.obj ================================================ # Blender v2.80 (sub 75) OBJ File: '' # www.blender.org mtllib arrow.mtl o Cube v -0.000000 0.020000 1.000000 v -0.000000 -0.020000 1.000000 v 0.200000 0.020000 -1.000000 v 0.200000 -0.020000 -1.000000 v -0.200000 0.020000 -1.000000 v -0.200000 -0.020000 -1.000000 v 0.631076 -0.020000 0.361554 v -0.648924 0.020000 0.361554 v -0.648924 -0.020000 0.361554 v 0.631076 0.020000 0.361554 v 0.200000 -0.020000 0.359655 v -0.200000 0.020000 0.359655 v -0.200000 -0.020000 0.359655 v 0.200000 0.020000 0.359655 vt 0.455043 0.250000 vt 0.625000 0.000000 vt 0.455043 0.000000 vt 0.375000 0.500000 vt 0.625000 0.330043 vt 0.375000 0.330043 vt 0.375000 0.750000 vt 0.625000 0.500000 vt 0.576873 0.750000 vt 0.576873 1.000000 vt 0.625000 1.000000 vt 0.625000 0.750000 vt 0.875000 0.701874 vt 0.625000 0.701874 vt 0.875000 0.669957 vt 0.625000 0.669957 vt 0.544957 1.000000 vt 0.544957 0.750000 vt 0.375000 0.298127 vt 0.625000 0.250000 vt 0.375000 0.250000 vt 0.375000 0.000000 vt 0.423127 0.250000 vt 0.423127 0.000000 vt 0.375000 1.000000 vt 0.875000 0.500000 vt 0.625000 0.500000 vt 0.625000 0.298127 vt 0.625000 0.250000 vt 0.625000 0.750000 vt 0.875000 0.750000 vn 0.0000 -1.0000 0.0000 vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 0.0000 1.0000 0.0000 vn -0.7112 0.0000 -0.7030 vn -0.0044 0.0000 1.0000 vn 0.7013 0.0000 -0.7128 vn -1.0000 0.0000 0.0000 vn 0.0042 0.0000 1.0000 usemtl Material s off f 12/1/1 3/2/1 14/3/1 f 6/4/2 12/5/2 13/6/2 f 4/7/3 5/8/3 6/4/3 f 7/9/4 9/10/4 2/11/4 f 2/12/5 10/13/5 7/14/5 f 7/14/6 14/15/6 11/16/6 f 13/17/4 7/9/4 11/18/4 f 9/19/7 1/20/7 2/21/7 f 1/22/1 8/23/1 10/24/1 f 6/25/4 11/18/4 4/7/4 f 11/16/8 3/26/8 4/27/8 f 13/6/9 8/28/9 9/19/9 f 10/24/1 12/1/1 14/3/1 f 12/1/1 5/29/1 3/2/1 f 6/4/2 5/8/2 12/5/2 f 4/7/3 3/30/3 5/8/3 f 2/12/5 1/31/5 10/13/5 f 7/14/6 10/13/6 14/15/6 f 13/17/4 9/10/4 7/9/4 f 9/19/7 8/28/7 1/20/7 f 6/25/4 13/17/4 11/18/4 f 11/16/8 14/15/8 3/26/8 f 13/6/9 12/5/9 8/28/9 f 10/24/1 8/23/1 12/1/1 ================================================ FILE: src/asset/3d/axis_debug.obj ================================================ # Blender v2.80 (sub 75) OBJ File: 'axis_debug.blend' # www.blender.org o Cylinder.002 v -0.078259 0.000390 0.500086 v -0.078259 0.000390 1.000086 v -0.039173 0.068088 0.500086 v -0.039173 0.068088 1.000086 v 0.038998 0.068088 0.500086 v 0.038998 0.068088 1.000086 v 0.078084 0.000390 0.500086 v 0.078084 0.000390 1.000086 v 0.038998 -0.067308 0.500086 v 0.038998 -0.067308 1.000086 v -0.039173 -0.067308 0.500086 v -0.039173 -0.067308 1.000086 v 0.499912 0.500390 0.500086 v 0.499912 0.500390 -0.499914 v 0.499912 -0.499610 0.500086 v 0.499912 -0.499610 -0.499914 v -0.500088 0.500390 0.500086 v -0.500088 0.500390 -0.499914 v -0.500088 -0.499610 0.500086 v -0.500088 -0.499610 -0.499914 v 0.499912 0.078561 0.000086 v 0.999912 0.078561 0.000086 v 0.499912 0.039476 -0.067612 v 0.999912 0.039476 -0.067612 v 0.499912 -0.038696 -0.067612 v 0.999912 -0.038696 -0.067612 v 0.499912 -0.077781 0.000086 v 0.999912 -0.077781 0.000086 v 0.499912 -0.038696 0.067785 v 0.999912 -0.038696 0.067785 v 0.499912 0.039476 0.067785 v 0.999912 0.039476 0.067785 v -0.078259 0.500390 0.000086 v -0.078259 1.000390 0.000086 v -0.039173 0.500390 -0.067612 v -0.039173 1.000390 -0.067612 v 0.038998 0.500390 -0.067612 v 0.038998 1.000390 -0.067612 v 0.078084 0.500390 0.000086 v 0.078084 1.000390 0.000086 v 0.038998 0.500390 0.067785 v 0.038998 1.000390 0.067785 v -0.039173 0.500390 0.067785 v -0.039173 1.000390 0.067785 v 0.554715 0.440554 -0.412883 v 0.554715 0.294033 -0.412883 v 0.554715 0.005957 -0.062722 v 0.554715 -0.285843 -0.412883 v 0.554715 -0.433606 -0.412883 v 0.554715 -0.068545 0.025439 v 0.554715 -0.407530 0.433960 v 0.554715 -0.259768 0.433960 v 0.554715 0.005957 0.111117 v 0.554715 0.270440 0.433960 v 0.554715 0.416961 0.433960 v 0.554715 0.079218 0.025439 v 0.356042 0.440554 -0.412883 v 0.356042 0.294033 -0.412883 v 0.356042 0.005957 -0.062722 v 0.356042 -0.285843 -0.412883 v 0.356042 -0.433606 -0.412883 v 0.356042 -0.068545 0.025439 v 0.356042 -0.407530 0.433960 v 0.356042 -0.259768 0.433960 v 0.356042 0.005957 0.111117 v 0.356042 0.270440 0.433960 v 0.356042 0.416961 0.433960 v 0.356042 0.079218 0.025439 v 0.356042 0.440554 -0.412883 v 0.368459 0.466882 -0.425300 v 0.356042 0.294033 -0.412883 v 0.368459 0.288169 -0.425300 v 0.356042 0.005957 -0.062722 v 0.368459 0.005896 -0.082192 v 0.356042 -0.285843 -0.412883 v 0.368459 -0.280028 -0.425300 v 0.356042 -0.433606 -0.412883 v 0.368459 -0.460108 -0.425300 v 0.356042 -0.068545 0.025439 v 0.368459 -0.084692 0.025454 v 0.356042 -0.407530 0.433960 v 0.368459 -0.433969 0.446377 v 0.356042 -0.259768 0.433960 v 0.368459 -0.253906 0.446377 v 0.356042 0.005957 0.111117 v 0.368459 0.005935 0.130683 v 0.356042 0.270440 0.433960 v 0.368459 0.264561 0.446377 v 0.356042 0.416961 0.433960 v 0.368459 0.443338 0.446377 v 0.356042 0.079218 0.025439 v 0.368459 0.095319 0.025428 v 0.368459 0.466882 -0.425300 v 0.542298 0.466882 -0.425300 v 0.368459 0.288169 -0.425300 v 0.542298 0.288169 -0.425300 v 0.368459 0.005896 -0.082192 v 0.542298 0.005896 -0.082192 v 0.368459 -0.280028 -0.425300 v 0.542298 -0.280028 -0.425300 v 0.368459 -0.460108 -0.425300 v 0.542298 -0.460108 -0.425300 v 0.368459 -0.084692 0.025454 v 0.542298 -0.084693 0.025454 v 0.368459 -0.433969 0.446377 v 0.542298 -0.433969 0.446377 v 0.368459 -0.253906 0.446377 v 0.542298 -0.253906 0.446377 v 0.368459 0.005935 0.130683 v 0.542298 0.005935 0.130683 v 0.368459 0.264561 0.446377 v 0.542298 0.264561 0.446377 v 0.368459 0.443338 0.446377 v 0.542298 0.443338 0.446377 v 0.368459 0.095319 0.025428 v 0.542298 0.095319 0.025428 v 0.542298 0.466882 -0.425300 v 0.554715 0.440554 -0.412883 v 0.542298 0.288169 -0.425300 v 0.554715 0.294033 -0.412883 v 0.542298 0.005896 -0.082192 v 0.554715 0.005957 -0.062722 v 0.542298 -0.280028 -0.425300 v 0.554715 -0.285843 -0.412883 v 0.542298 -0.460108 -0.425300 v 0.554715 -0.433606 -0.412883 v 0.542298 -0.084693 0.025454 v 0.554715 -0.068545 0.025439 v 0.542298 -0.433969 0.446377 v 0.554715 -0.407530 0.433960 v 0.542298 -0.253906 0.446377 v 0.554715 -0.259768 0.433960 v 0.542298 0.005935 0.130683 v 0.554715 0.005957 0.111117 v 0.542298 0.264561 0.446377 v 0.554715 0.270440 0.433960 v 0.542298 0.443338 0.446377 v 0.554715 0.416961 0.433960 v 0.542298 0.095319 0.025428 v 0.554715 0.079218 0.025439 v -0.376351 0.550070 0.415498 v -0.062199 0.550070 0.015669 v -0.062199 0.550070 -0.431345 v 0.059488 0.550070 -0.431345 v 0.059488 0.550070 0.016911 v 0.373639 0.550070 0.415498 v 0.227118 0.550070 0.415498 v -0.000114 0.550070 0.123697 v -0.229830 0.550070 0.415498 v -0.376351 0.351397 0.415498 v -0.062199 0.351397 0.015669 v -0.062199 0.351397 -0.431345 v 0.059488 0.351397 -0.431345 v 0.059488 0.351397 0.016910 v 0.373639 0.351397 0.415498 v 0.227118 0.351397 0.415498 v -0.000114 0.351397 0.123697 v -0.229830 0.351397 0.415498 v -0.376351 0.351397 0.415498 v -0.401898 0.363814 0.427915 v -0.062199 0.351397 0.015669 v -0.074617 0.363814 0.011374 v -0.062199 0.351397 -0.431345 v -0.074617 0.363814 -0.443762 v 0.059488 0.351397 -0.431345 v 0.071905 0.363814 -0.443762 v 0.059488 0.351397 0.016910 v 0.071905 0.363814 0.012605 v 0.373639 0.351397 0.415498 v 0.399236 0.363814 0.427915 v 0.227118 0.351397 0.415498 v 0.221049 0.363814 0.427915 v -0.000114 0.351397 0.123697 v -0.000167 0.363814 0.143839 v -0.229830 0.351397 0.415498 v -0.223802 0.363814 0.427915 v -0.401898 0.363814 0.427915 v -0.401898 0.537653 0.427915 v -0.074617 0.363814 0.011374 v -0.074617 0.537653 0.011374 v -0.074617 0.363814 -0.443762 v -0.074616 0.537653 -0.443762 v 0.071905 0.363814 -0.443762 v 0.071905 0.537653 -0.443762 v 0.071905 0.363814 0.012605 v 0.071905 0.537653 0.012605 v 0.399236 0.363814 0.427915 v 0.399236 0.537653 0.427915 v 0.221049 0.363814 0.427915 v 0.221049 0.537653 0.427915 v -0.000167 0.363814 0.143839 v -0.000167 0.537653 0.143839 v -0.223802 0.363814 0.427915 v -0.223802 0.537653 0.427915 v -0.401898 0.537653 0.427915 v -0.376351 0.550070 0.415498 v -0.074617 0.537653 0.011374 v -0.062199 0.550070 0.015669 v -0.074616 0.537653 -0.443762 v -0.062199 0.550070 -0.431345 v 0.071905 0.537653 -0.443762 v 0.059488 0.550070 -0.431345 v 0.071905 0.537653 0.012605 v 0.059488 0.550070 0.016911 v 0.399236 0.537653 0.427915 v 0.373639 0.550070 0.415498 v 0.221049 0.537653 0.427915 v 0.227118 0.550070 0.415498 v -0.000167 0.537653 0.143839 v -0.000114 0.550070 0.123697 v -0.223802 0.537653 0.427915 v -0.229830 0.550070 0.415498 v 0.349194 -0.424488 0.550865 v -0.380928 -0.424488 0.550865 v 0.157972 0.313085 0.550865 v -0.380928 0.313085 0.550865 v -0.380928 0.422355 0.550865 v 0.382720 0.422355 0.550865 v -0.156179 -0.315218 0.550865 v 0.349194 -0.315218 0.550865 v 0.349194 -0.424488 0.352193 v -0.380928 -0.424488 0.352193 v 0.157972 0.313085 0.352193 v -0.380928 0.313085 0.352193 v -0.380928 0.422355 0.352193 v 0.382720 0.422355 0.352193 v -0.156179 -0.315218 0.352193 v 0.349194 -0.315218 0.352193 v 0.349194 -0.424488 0.352193 v 0.361611 -0.436905 0.364610 v -0.380928 -0.424488 0.352193 v -0.405379 -0.436905 0.364610 v 0.157972 0.313085 0.352193 v 0.133521 0.300668 0.364610 v -0.380928 0.313085 0.352193 v -0.393345 0.300668 0.364610 v -0.380928 0.422355 0.352193 v -0.393345 0.434772 0.364610 v 0.382720 0.422355 0.352193 v 0.407171 0.434772 0.364610 v -0.156179 -0.315218 0.352193 v -0.131729 -0.302801 0.364610 v 0.349194 -0.315218 0.352193 v 0.361611 -0.302801 0.364610 v 0.361611 -0.436905 0.364610 v 0.361611 -0.436905 0.538448 v -0.405379 -0.436905 0.364610 v -0.405379 -0.436905 0.538448 v 0.133521 0.300668 0.364610 v 0.133521 0.300668 0.538448 v -0.393345 0.300668 0.364610 v -0.393345 0.300668 0.538448 v -0.393345 0.434772 0.364610 v -0.393345 0.434772 0.538448 v 0.407171 0.434772 0.364610 v 0.407171 0.434772 0.538448 v -0.131729 -0.302801 0.364610 v -0.131729 -0.302801 0.538448 v 0.361611 -0.302801 0.364610 v 0.361611 -0.302801 0.538448 v 0.361611 -0.436905 0.538448 v 0.349194 -0.424488 0.550865 v -0.405379 -0.436905 0.538448 v -0.380928 -0.424488 0.550865 v 0.133521 0.300668 0.538448 v 0.157972 0.313085 0.550865 v -0.393345 0.300668 0.538448 v -0.380928 0.313085 0.550865 v -0.393345 0.434772 0.538448 v -0.380928 0.422355 0.550865 v 0.407171 0.434772 0.538448 v 0.382720 0.422355 0.550865 v -0.131729 -0.302801 0.538448 v -0.156179 -0.315218 0.550865 v 0.361611 -0.302801 0.538448 v 0.349194 -0.315218 0.550865 vt 0.831726 0.844892 vt 0.841085 0.823278 vt 0.831726 0.823278 vt 0.892429 0.844892 vt 0.881622 0.823278 vt 0.892429 0.823278 vt 0.841085 0.844892 vt 0.831726 0.823278 vt 0.841085 0.823278 vt 0.831726 0.844892 vt 0.822366 0.823278 vt 0.822366 0.832556 vt 0.831726 0.827152 vt 0.841085 0.832556 vt 0.881622 0.844892 vt 0.892429 0.823278 vt 0.881622 0.823278 vt 0.822366 0.844892 vt 0.822366 0.823278 vt 0.831726 0.827152 vt 0.822366 0.832556 vt 0.822366 0.843363 vt 0.225312 0.847355 vt 0.114117 0.736160 vt 0.225312 0.736160 vt 0.833085 0.104738 vt 0.936375 0.072440 vt 0.833085 0.072440 vt 0.060544 0.238768 vt 0.147219 0.066745 vt 0.147219 0.238767 vt 0.900850 0.768835 vt 0.762601 0.907084 vt 0.900850 0.907084 vt 0.140228 0.927705 vt 0.061206 0.902996 vt 0.140228 0.902996 vt 0.269790 0.745180 vt 0.158595 0.779949 vt 0.158595 0.745180 vt 0.190557 0.066745 vt 0.202501 0.152756 vt 0.190557 0.152756 vt 0.202501 0.066745 vt 0.223506 0.152756 vt 0.223506 0.066745 vt 0.232567 0.152756 vt 0.190557 0.152756 vt 0.179722 0.066745 vt 0.190557 0.066745 vt 0.158955 0.159480 vt 0.164822 0.152756 vt 0.170690 0.159480 vt 0.179722 0.152756 vt 0.158054 0.066745 vt 0.158054 0.152756 vt 0.147219 0.066745 vt 0.147219 0.159480 vt 0.158955 0.172927 vt 0.147219 0.172927 vt 0.895059 0.123781 vt 0.946704 0.119744 vt 0.895059 0.119744 vt 0.895059 0.111669 vt 0.946704 0.107632 vt 0.895059 0.107632 vt 0.895059 0.111669 vt 0.880693 0.090775 vt 0.876656 0.088589 vt 0.880693 0.086402 vt 0.946704 0.111669 vt 0.895059 0.119744 vt 0.946704 0.119744 vt 0.880693 0.090775 vt 0.888768 0.090775 vt 0.888768 0.086402 vt 0.114540 0.172228 vt 0.084700 0.271120 vt 0.061642 0.282111 vt 0.126166 0.184444 vt 0.190303 0.220783 vt 0.167439 0.231681 vt 0.137598 0.161237 vt 0.057573 0.108538 vt 0.126166 0.148415 vt 0.193984 0.043516 vt 0.080631 0.097547 vt 0.171120 0.054414 vt 0.118423 0.284705 vt 0.148263 0.185814 vt 0.095365 0.295696 vt 0.159889 0.198029 vt 0.224026 0.234368 vt 0.201162 0.245267 vt 0.171322 0.174823 vt 0.091296 0.122124 vt 0.159889 0.162000 vt 0.227707 0.057101 vt 0.114354 0.111133 vt 0.204843 0.068000 vt 0.600272 0.443762 vt 0.571818 0.514908 vt 0.546948 0.504889 vt 0.610389 0.469969 vt 0.674252 0.556174 vt 0.649382 0.546155 vt 0.620928 0.451825 vt 0.600272 0.350859 vt 0.620927 0.359180 vt 0.540816 0.529686 vt 0.569270 0.458539 vt 0.515945 0.519666 vt 0.579387 0.484747 vt 0.643250 0.570952 vt 0.618379 0.560933 vt 0.589925 0.466603 vt 0.569269 0.365636 vt 0.589925 0.373958 vt 0.416070 0.598978 vt 0.522951 0.657033 vt 0.399019 0.607106 vt 0.501853 0.633537 vt 0.546574 0.565319 vt 0.525476 0.541823 vt 0.638048 0.602170 vt 0.655100 0.594043 vt 0.522951 0.615857 vt 0.416070 0.557802 vt 0.399019 0.565930 vt 0.501853 0.592361 vt 0.546574 0.524144 vt 0.525476 0.500647 vt 0.638048 0.560995 vt 0.655099 0.552867 vt 0.841085 0.844892 vt 0.881622 0.844892 vt 0.822366 0.844892 vt 0.841085 0.843363 vt 0.831726 0.848766 vt 0.822366 0.843363 vt 0.892429 0.844892 vt 0.831726 0.848766 vt 0.841085 0.843363 vt 0.841085 0.832556 vt 0.114117 0.847355 vt 0.936375 0.104738 vt 0.060544 0.066745 vt 0.762601 0.768835 vt 0.061206 0.927705 vt 0.269790 0.779949 vt 0.232567 0.066745 vt 0.170690 0.172927 vt 0.158955 0.172927 vt 0.164822 0.179651 vt 0.147219 0.152756 vt 0.153087 0.152756 vt 0.158955 0.159480 vt 0.153087 0.179651 vt 0.946704 0.123781 vt 0.946704 0.111669 vt 0.888768 0.086402 vt 0.892805 0.088589 vt 0.888768 0.090775 vt 0.892805 0.088589 vt 0.880693 0.086402 vt 0.876656 0.088589 vt 0.201820 0.065013 vt 0.227707 0.057101 vt 0.229708 0.051720 vt 0.159889 0.162000 vt 0.204843 0.068000 vt 0.113154 0.107277 vt 0.157772 0.157121 vt 0.091296 0.122124 vt 0.114354 0.111133 vt 0.143636 0.186169 vt 0.085053 0.120672 vt 0.095365 0.295696 vt 0.148263 0.185814 vt 0.117231 0.285994 vt 0.089132 0.299387 vt 0.159889 0.198029 vt 0.118423 0.284705 vt 0.198136 0.247429 vt 0.157778 0.201237 vt 0.224026 0.234368 vt 0.201162 0.245267 vt 0.171726 0.172774 vt 0.226034 0.234131 vt 0.171322 0.174823 vt 0.172312 0.053126 vt 0.229708 0.051720 vt 0.200200 0.039833 vt 0.128264 0.145233 vt 0.201820 0.065013 vt 0.113154 0.107277 vt 0.157772 0.157121 vt 0.055545 0.108785 vt 0.083646 0.095390 vt 0.114128 0.174281 vt 0.085053 0.120672 vt 0.059624 0.287500 vt 0.143636 0.186169 vt 0.087723 0.274106 vt 0.089132 0.299387 vt 0.128270 0.189350 vt 0.117231 0.285994 vt 0.168629 0.235541 vt 0.157778 0.201237 vt 0.196527 0.222243 vt 0.198136 0.247429 vt 0.142219 0.160886 vt 0.226034 0.234131 vt 0.171726 0.172774 vt 0.193984 0.043516 vt 0.172312 0.053126 vt 0.200200 0.039833 vt 0.126166 0.148415 vt 0.171120 0.054414 vt 0.083646 0.095390 vt 0.128264 0.145233 vt 0.057573 0.108538 vt 0.080631 0.097547 vt 0.114128 0.174281 vt 0.055545 0.108785 vt 0.061642 0.282111 vt 0.114540 0.172228 vt 0.087723 0.274106 vt 0.059624 0.287500 vt 0.126166 0.184444 vt 0.084700 0.271120 vt 0.168629 0.235541 vt 0.128270 0.189350 vt 0.190303 0.220783 vt 0.167439 0.231681 vt 0.142219 0.160886 vt 0.196527 0.222243 vt 0.137598 0.161237 vt 0.593970 0.465639 vt 0.643250 0.570952 vt 0.649524 0.574349 vt 0.589925 0.373958 vt 0.589925 0.466603 vt 0.593970 0.371310 vt 0.569269 0.365636 vt 0.569100 0.455875 vt 0.569099 0.361290 vt 0.515945 0.519666 vt 0.569270 0.458539 vt 0.543784 0.531751 vt 0.513538 0.519566 vt 0.579387 0.484747 vt 0.540816 0.529686 vt 0.619293 0.562170 vt 0.581333 0.488002 vt 0.618379 0.560933 vt 0.621098 0.452708 vt 0.649524 0.574349 vt 0.676651 0.561418 vt 0.593970 0.371310 vt 0.593970 0.465639 vt 0.596227 0.348360 vt 0.621097 0.358379 vt 0.596227 0.442944 vt 0.569099 0.361290 vt 0.540665 0.506635 vt 0.569100 0.455875 vt 0.570911 0.518820 vt 0.513538 0.519566 vt 0.581333 0.488002 vt 0.543784 0.531751 vt 0.646421 0.549240 vt 0.608460 0.475071 vt 0.619293 0.562170 vt 0.674252 0.556174 vt 0.621098 0.452708 vt 0.676651 0.561418 vt 0.620927 0.359180 vt 0.620928 0.451825 vt 0.600272 0.350859 vt 0.621097 0.358379 vt 0.596227 0.442944 vt 0.596227 0.348360 vt 0.546948 0.504889 vt 0.600272 0.443762 vt 0.570911 0.518820 vt 0.540665 0.506635 vt 0.610389 0.469969 vt 0.571818 0.514908 vt 0.646421 0.549240 vt 0.608460 0.475071 vt 0.649382 0.546155 vt 0.394973 0.568578 vt 0.522951 0.615857 vt 0.399019 0.565930 vt 0.548787 0.529313 vt 0.525163 0.621026 vt 0.638048 0.560995 vt 0.546574 0.524144 vt 0.638218 0.565341 vt 0.655099 0.552867 vt 0.659145 0.555366 vt 0.525476 0.500647 vt 0.499640 0.592339 vt 0.523264 0.500625 vt 0.416070 0.557802 vt 0.501853 0.592361 vt 0.089836 0.283127 vt 0.110933 0.272352 vt 0.110763 0.273152 vt 0.525164 0.657055 vt 0.394973 0.568578 vt 0.394973 0.604607 vt 0.548787 0.565342 vt 0.525163 0.621026 vt 0.638218 0.601370 vt 0.548787 0.529313 vt 0.659145 0.591395 vt 0.638218 0.565341 vt 0.523264 0.536654 vt 0.659145 0.555366 vt 0.499640 0.628367 vt 0.523264 0.500625 vt 0.415900 0.594632 vt 0.499640 0.592339 vt 0.415900 0.558603 vt 0.522951 0.657033 vt 0.394973 0.604607 vt 0.399019 0.607106 vt 0.548787 0.565342 vt 0.525164 0.657055 vt 0.638048 0.602170 vt 0.546574 0.565319 vt 0.655100 0.594043 vt 0.638218 0.601370 vt 0.525476 0.541823 vt 0.659145 0.591395 vt 0.499640 0.628367 vt 0.523264 0.536654 vt 0.416070 0.598978 vt 0.501853 0.633537 vt 0.415900 0.594632 vt 0.415900 0.558603 vt 0.093881 0.280479 vn -0.8660 0.5000 0.0000 vn 0.0000 1.0000 -0.0000 vn 0.8660 0.5000 -0.0000 vn 0.8660 -0.5000 -0.0000 vn -0.0000 0.0000 1.0000 vn 0.0000 -1.0000 0.0000 vn -0.8660 -0.5000 0.0000 vn 0.0000 0.0000 -1.0000 vn -1.0000 0.0000 -0.0000 vn 1.0000 -0.0000 0.0000 vn 0.0000 0.8660 -0.5000 vn 0.0000 -0.8660 -0.5000 vn -0.0000 -0.8660 0.5000 vn -0.0000 0.8660 0.5000 vn -0.8660 0.0000 -0.5000 vn 0.8660 -0.0000 -0.5000 vn 0.8660 -0.0000 0.5000 vn -0.8660 0.0000 0.5000 vn -0.7417 -0.2864 -0.6065 vn -0.9198 0.3549 -0.1674 vn -0.8431 -0.0017 -0.5377 vn -0.7412 0.2847 -0.6079 vn -0.9206 -0.3537 -0.1657 vn -0.7927 -0.6096 0.0005 vn -0.9203 -0.3541 0.1663 vn -0.7417 0.2863 0.6065 vn -0.8443 -0.0006 0.5358 vn -0.7419 -0.2869 0.6060 vn -0.9200 0.3545 0.1669 vn -0.7919 0.6106 -0.0004 vn 0.0000 -0.4270 -0.9042 vn 0.0000 0.9044 -0.4266 vn 0.0000 -0.0031 -1.0000 vn 0.0000 0.4241 -0.9056 vn 0.0000 -0.9055 -0.4243 vn 0.0000 -1.0000 0.0009 vn 0.0000 -0.9051 0.4251 vn 0.0000 0.4269 0.9043 vn 0.0000 -0.0011 1.0000 vn 0.0000 -0.4279 0.9038 vn 0.0000 0.9048 0.4259 vn 0.0000 1.0000 -0.0007 vn 0.9198 0.3549 -0.1673 vn 0.7417 -0.2864 -0.6065 vn 0.9198 0.3549 -0.1674 vn 0.8431 -0.0017 -0.5377 vn 0.7412 0.2847 -0.6079 vn 0.9206 -0.3537 -0.1657 vn 0.7927 -0.6096 0.0005 vn 0.9203 -0.3541 0.1663 vn 0.7417 0.2863 0.6065 vn 0.8443 -0.0006 0.5358 vn 0.7419 -0.2869 0.6060 vn 0.9200 0.3545 0.1669 vn 0.7919 0.6106 -0.0004 vn -0.6491 -0.7268 -0.2245 vn -0.3602 -0.9163 0.1751 vn -0.4082 -0.8165 -0.4082 vn 0.4082 -0.8165 -0.4082 vn 0.6489 -0.7269 -0.2250 vn 0.3599 -0.9165 0.1746 vn -0.2934 -0.7439 0.6005 vn -0.0014 -0.8512 0.5248 vn 0.2921 -0.7434 0.6016 vn -0.9451 0.0000 -0.3269 vn -0.8994 0.0000 0.4371 vn -0.7071 0.0000 -0.7071 vn 0.7071 0.0000 -0.7071 vn 0.9448 0.0000 -0.3276 vn 0.8997 0.0000 0.4364 vn -0.4391 0.0000 0.8984 vn -0.0026 0.0000 1.0000 vn 0.4367 0.0000 0.8996 vn -0.3602 0.9163 0.1751 vn -0.6491 0.7268 -0.2245 vn -0.4082 0.8165 -0.4082 vn 0.4082 0.8165 -0.4082 vn 0.6489 0.7269 -0.2250 vn 0.3599 0.9165 0.1746 vn -0.2934 0.7439 0.6005 vn -0.0014 0.8512 0.5248 vn 0.2921 0.7434 0.6016 vn 0.4082 -0.4082 -0.8165 vn -0.3677 -0.1867 -0.9109 vn -0.4082 -0.4082 -0.8165 vn -0.4082 0.4082 -0.8165 vn 0.3677 0.1867 -0.9109 vn 0.4082 0.4082 -0.8165 vn -0.8916 -0.4528 0.0000 vn 0.7071 -0.7071 0.0000 vn -0.7071 -0.7071 0.0000 vn -0.7071 0.7071 0.0000 vn 0.8916 0.4528 0.0000 vn 0.7071 0.7071 0.0000 vn -0.3677 -0.1867 0.9109 vn 0.4082 -0.4082 0.8165 vn -0.3677 -0.1868 0.9109 vn -0.4082 -0.4082 0.8165 vn -0.4082 0.4082 0.8165 vn 0.3677 0.1867 0.9109 vn 0.4082 0.4082 0.8165 s off f 2/1/1 3/2/1 1/3/1 f 4/4/2 5/5/2 3/6/2 f 6/7/3 7/8/3 5/9/3 f 8/10/4 9/11/4 7/8/4 f 10/12/5 8/13/5 6/14/5 f 10/15/6 11/16/6 9/17/6 f 12/18/7 1/3/7 11/19/7 f 7/20/8 9/21/8 11/22/8 f 17/23/5 15/24/5 13/25/5 f 15/26/6 20/27/6 16/28/6 f 19/29/9 18/30/9 20/31/9 f 14/32/8 20/33/8 18/34/8 f 13/35/10 16/36/10 14/37/10 f 18/38/2 13/39/2 14/40/2 f 22/41/11 23/42/11 21/43/11 f 24/44/8 25/45/8 23/42/8 f 26/46/12 27/47/12 25/45/12 f 28/48/13 29/49/13 27/50/13 f 30/51/10 28/52/10 26/53/10 f 30/54/5 31/55/5 29/49/5 f 32/56/14 21/57/14 31/55/14 f 31/58/9 25/59/9 29/60/9 f 33/61/15 36/62/15 35/63/15 f 36/62/8 37/64/8 35/63/8 f 37/64/16 40/65/16 39/66/16 f 40/65/17 41/67/17 39/66/17 f 42/68/2 40/69/2 38/70/2 f 42/71/5 43/72/5 41/67/5 f 44/73/18 33/61/18 43/72/18 f 41/74/6 43/75/6 35/76/6 f 50/77/10 52/78/10 51/79/10 f 50/77/10 53/80/10 52/78/10 f 53/80/10 55/81/10 54/82/10 f 53/80/10 56/83/10 55/81/10 f 50/77/10 56/83/10 53/80/10 f 49/84/10 56/83/10 50/77/10 f 49/84/10 47/85/10 56/83/10 f 47/85/10 45/86/10 56/83/10 f 49/84/10 48/87/10 47/85/10 f 46/88/10 45/86/10 47/85/10 f 64/89/9 62/90/9 63/91/9 f 65/92/9 62/90/9 64/89/9 f 67/93/9 65/92/9 66/94/9 f 68/95/9 65/92/9 67/93/9 f 68/95/9 62/90/9 65/92/9 f 68/95/9 61/96/9 62/90/9 f 59/97/9 61/96/9 68/95/9 f 57/98/9 59/97/9 68/95/9 f 60/99/9 61/96/9 59/97/9 f 57/98/9 58/100/9 59/97/9 f 145/101/2 147/102/2 146/103/2 f 145/101/2 148/104/2 147/102/2 f 148/104/2 141/105/2 149/106/2 f 148/104/2 142/107/2 141/105/2 f 145/101/2 142/107/2 148/104/2 f 144/108/2 142/107/2 145/101/2 f 144/108/2 143/109/2 142/107/2 f 156/110/6 154/111/6 155/112/6 f 157/113/6 154/111/6 156/110/6 f 150/114/6 157/113/6 158/115/6 f 151/116/6 157/113/6 150/114/6 f 151/116/6 154/111/6 157/113/6 f 151/116/6 153/117/6 154/111/6 f 152/118/6 153/117/6 151/116/6 f 220/119/5 214/120/5 213/121/5 f 220/119/5 219/122/5 214/120/5 f 219/122/5 215/123/5 214/120/5 f 218/124/5 215/123/5 219/122/5 f 218/124/5 216/125/5 215/123/5 f 218/124/5 217/126/5 216/125/5 f 222/127/8 228/128/8 221/129/8 f 227/130/8 228/128/8 222/127/8 f 223/131/8 227/130/8 222/127/8 f 223/131/8 226/132/8 227/130/8 f 224/133/8 226/132/8 223/131/8 f 225/134/8 226/132/8 224/133/8 f 2/1/1 4/135/1 3/2/1 f 4/4/2 6/136/2 5/5/2 f 6/7/3 8/10/3 7/8/3 f 8/10/4 10/137/4 9/11/4 f 6/14/5 4/138/5 2/139/5 f 2/139/5 12/140/5 6/14/5 f 12/140/5 10/12/5 6/14/5 f 10/15/6 12/141/6 11/16/6 f 12/18/7 2/1/7 1/3/7 f 11/22/8 1/142/8 3/143/8 f 3/143/8 5/144/8 11/22/8 f 5/144/8 7/20/8 11/22/8 f 17/23/5 19/145/5 15/24/5 f 15/26/6 19/146/6 20/27/6 f 19/29/9 17/147/9 18/30/9 f 14/32/8 16/148/8 20/33/8 f 13/35/10 15/149/10 16/36/10 f 18/38/2 17/150/2 13/39/2 f 22/41/11 24/44/11 23/42/11 f 24/44/8 26/46/8 25/45/8 f 26/46/12 28/151/12 27/47/12 f 28/48/13 30/54/13 29/49/13 f 26/53/10 24/152/10 32/153/10 f 24/152/10 22/154/10 32/153/10 f 32/153/10 30/51/10 26/53/10 f 30/54/5 32/56/5 31/55/5 f 32/56/14 22/155/14 21/57/14 f 31/58/9 21/156/9 23/157/9 f 23/157/9 25/59/9 31/58/9 f 25/59/9 27/158/9 29/60/9 f 33/61/15 34/159/15 36/62/15 f 36/62/8 38/160/8 37/64/8 f 37/64/16 38/160/16 40/65/16 f 40/65/17 42/71/17 41/67/17 f 38/70/2 36/161/2 42/68/2 f 36/161/2 34/162/2 44/163/2 f 42/68/2 36/161/2 44/163/2 f 42/71/5 44/73/5 43/72/5 f 44/73/18 34/159/18 33/61/18 f 43/75/6 33/164/6 35/76/6 f 35/76/6 37/165/6 41/74/6 f 37/165/6 39/166/6 41/74/6 s 1 f 72/167/19 69/168/20 70/169/20 f 72/167/19 73/170/21 71/171/19 f 76/172/22 73/170/21 74/173/21 f 76/172/22 77/174/23 75/175/22 f 80/176/24 77/174/23 78/177/23 f 80/176/24 81/178/25 79/179/24 f 84/180/26 81/178/25 82/181/25 f 84/180/26 85/182/27 83/183/26 f 88/184/28 85/182/27 86/185/27 f 88/184/28 89/186/29 87/187/28 f 92/188/30 89/186/29 90/189/29 f 92/188/30 69/168/20 91/190/30 f 96/191/31 93/192/32 94/193/32 f 98/194/33 95/195/31 96/191/31 f 98/194/33 99/196/34 97/197/33 f 102/198/35 99/196/34 100/199/34 f 104/200/36 101/201/35 102/198/35 f 106/202/37 103/203/36 104/200/36 f 108/204/38 105/205/37 106/202/37 f 110/206/39 107/207/38 108/204/38 f 112/208/40 109/209/39 110/206/39 f 114/210/41 111/211/40 112/208/40 f 116/212/42 113/213/41 114/210/41 f 94/193/32 115/214/42 116/212/42 f 118/215/43 119/216/44 117/217/45 f 122/218/46 119/216/44 120/219/44 f 122/218/46 123/220/47 121/221/46 f 126/222/48 123/220/47 124/223/47 f 126/222/48 127/224/49 125/225/48 f 130/226/50 127/224/49 128/227/49 f 130/226/50 131/228/51 129/229/50 f 134/230/52 131/228/51 132/231/51 f 134/230/52 135/232/53 133/233/52 f 138/234/54 135/232/53 136/235/53 f 138/234/54 139/236/55 137/237/54 f 118/215/43 139/236/55 140/238/55 f 162/239/56 159/240/57 160/241/57 f 162/239/56 163/242/58 161/243/56 f 164/244/58 165/245/59 163/242/58 f 168/246/60 165/245/59 166/247/59 f 168/246/60 169/248/61 167/249/60 f 172/250/62 169/248/61 170/251/61 f 172/250/62 173/252/63 171/253/62 f 176/254/64 173/252/63 174/255/63 f 176/254/64 159/240/57 175/256/64 f 180/257/65 177/258/66 178/259/66 f 180/257/65 181/260/67 179/261/65 f 184/262/68 181/260/67 182/263/67 f 186/264/69 183/265/68 184/262/68 f 188/266/70 185/267/69 186/264/69 f 190/268/71 187/269/70 188/266/70 f 190/268/71 191/270/72 189/271/71 f 194/272/73 191/270/72 192/273/72 f 178/259/66 193/274/73 194/272/73 f 196/275/74 197/276/75 195/277/74 f 200/278/76 197/276/75 198/279/75 f 202/280/77 199/281/76 200/278/76 f 202/280/77 203/282/78 201/283/77 f 206/284/79 203/282/78 204/285/78 f 206/284/79 207/286/80 205/287/79 f 210/288/81 207/286/80 208/289/80 f 210/288/81 211/290/82 209/291/81 f 196/275/74 211/290/82 212/292/82 f 230/293/83 231/294/84 229/295/83 f 234/296/84 231/294/84 232/297/84 f 234/296/84 235/298/85 233/299/84 f 236/300/85 237/301/86 235/298/85 f 238/302/86 239/303/87 237/301/86 f 242/304/87 239/303/87 240/305/87 f 242/304/87 243/306/88 241/307/87 f 230/308/83 243/309/88 244/310/88 f 248/311/89 245/312/90 246/313/90 f 250/314/89 247/315/89 248/311/89 f 252/316/91 249/317/89 250/314/89 f 254/318/92 251/319/91 252/316/91 f 256/320/93 253/321/92 254/318/92 f 258/322/93 255/323/93 256/320/93 f 260/324/94 257/325/93 258/322/93 f 246/313/90 259/326/94 260/324/94 f 264/327/95 261/328/96 262/329/96 f 264/327/95 265/330/97 263/331/95 f 268/332/98 265/330/97 266/333/95 f 270/334/99 267/335/98 268/332/98 f 272/336/100 269/337/99 270/334/99 f 272/336/100 273/338/100 271/339/100 f 276/340/101 273/338/100 274/341/100 f 276/340/101 261/328/96 275/342/101 f 72/167/19 71/171/19 69/168/20 f 72/167/19 74/173/21 73/170/21 f 76/172/22 75/175/22 73/170/21 f 76/172/22 78/177/23 77/174/23 f 80/176/24 79/179/24 77/174/23 f 80/176/24 82/181/25 81/178/25 f 84/180/26 83/183/26 81/178/25 f 84/180/26 86/185/27 85/182/27 f 88/184/28 87/187/28 85/182/27 f 88/184/28 90/189/29 89/186/29 f 92/188/30 91/190/30 89/186/29 f 92/188/30 70/169/20 69/168/20 f 96/191/31 95/195/31 93/192/32 f 98/194/33 97/197/33 95/195/31 f 98/194/33 100/199/34 99/196/34 f 102/198/35 101/201/35 99/196/34 f 104/200/36 103/203/36 101/201/35 f 106/202/37 105/205/37 103/203/36 f 108/204/38 107/207/38 105/205/37 f 110/206/39 109/209/39 107/207/38 f 112/208/40 111/211/40 109/209/39 f 114/210/41 113/213/41 111/211/40 f 116/212/42 115/214/42 113/213/41 f 94/193/32 93/192/32 115/214/42 f 118/215/43 120/219/44 119/216/44 f 122/218/46 121/221/46 119/216/44 f 122/218/46 124/223/47 123/220/47 f 126/222/48 125/225/48 123/220/47 f 126/222/48 128/227/49 127/224/49 f 130/226/50 129/229/50 127/224/49 f 130/226/50 132/231/51 131/228/51 f 134/230/52 133/233/52 131/228/51 f 134/230/52 136/235/53 135/232/53 f 138/234/54 137/237/54 135/232/53 f 138/234/54 140/238/55 139/236/55 f 118/215/43 117/217/45 139/236/55 f 162/239/56 161/243/56 159/240/57 f 162/239/56 164/244/58 163/242/58 f 164/244/58 166/247/59 165/245/59 f 168/246/60 167/249/60 165/245/59 f 168/246/60 170/251/61 169/248/61 f 172/250/62 171/253/62 169/248/61 f 172/250/62 174/255/63 173/252/63 f 176/254/64 175/256/64 173/252/63 f 176/254/64 160/241/57 159/240/57 f 180/257/65 179/261/65 177/258/66 f 180/257/65 182/263/67 181/260/67 f 184/262/68 183/265/68 181/260/67 f 186/264/69 185/267/69 183/265/68 f 188/266/70 187/269/70 185/267/69 f 190/268/71 189/271/71 187/269/70 f 190/268/71 192/273/72 191/270/72 f 194/272/73 193/274/73 191/270/72 f 178/259/66 177/258/66 193/274/73 f 196/275/74 198/279/75 197/276/75 f 200/278/76 199/281/76 197/276/75 f 202/280/77 201/283/77 199/281/76 f 202/280/77 204/285/78 203/282/78 f 206/284/79 205/287/79 203/282/78 f 206/284/79 208/289/80 207/286/80 f 210/288/81 209/291/81 207/286/80 f 210/288/81 212/292/82 211/290/82 f 196/275/74 195/277/74 211/290/82 f 230/293/83 232/297/84 231/294/84 f 234/296/84 233/299/84 231/294/84 f 234/296/84 236/300/85 235/298/85 f 236/300/85 238/302/86 237/301/86 f 238/302/86 240/305/87 239/303/87 f 242/304/87 241/307/87 239/303/87 f 242/304/87 244/343/88 243/306/88 f 230/308/83 229/344/83 243/309/88 f 248/311/89 247/315/89 245/312/90 f 250/314/89 249/317/89 247/315/89 f 252/316/91 251/319/91 249/317/89 f 254/318/92 253/321/92 251/319/91 f 256/320/93 255/323/93 253/321/92 f 258/322/93 257/325/93 255/323/93 f 260/324/94 259/326/94 257/325/93 f 246/313/90 245/312/90 259/326/94 f 264/327/95 263/331/95 261/328/96 f 264/327/95 266/333/95 265/330/97 f 268/332/98 267/335/98 265/330/97 f 270/334/99 269/337/99 267/335/98 f 272/336/100 271/339/100 269/337/99 f 272/336/100 274/341/100 273/338/100 f 276/340/101 275/342/101 273/338/100 f 276/340/101 262/329/96 261/328/96 ================================================ FILE: src/asset/3d/cube.obj ================================================ # Blender v2.80 (sub 75) OBJ File: '' # www.blender.org o Cube v 0.500000 0.500000 -0.500000 v 0.500000 -0.500000 -0.500000 v 0.500000 0.500000 0.500000 v 0.500000 -0.500000 0.500000 v -0.500000 0.500000 -0.500000 v -0.500000 -0.500000 -0.500000 v -0.500000 0.500000 0.500000 v -0.500000 -0.500000 0.500000 vt 0.000000 1.000000 vt 1.000000 0.000000 vt 1.000000 1.000000 vt 1.000000 1.000000 vt 0.000000 0.000000 vt 1.000000 0.000000 vt 0.000000 1.000000 vt 1.000000 0.000000 vt 1.000000 1.000000 vt 0.000000 1.000000 vt 0.000000 0.000000 vt 1.000000 0.000000 vt 0.000000 0.000000 vt 0.000000 0.000000 vt 1.000000 1.000000 vt 0.000000 1.000000 vn 0.0000 1.0000 0.0000 vn 0.0000 0.0000 1.0000 vn -1.0000 0.0000 0.0000 vn 0.0000 -1.0000 0.0000 vn 1.0000 0.0000 0.0000 vn 0.0000 0.0000 -1.0000 s off f 5/1/1 3/2/1 1/3/1 f 3/4/2 8/5/2 4/6/2 f 7/7/3 6/8/3 8/5/3 f 2/9/4 8/5/4 6/10/4 f 1/3/5 4/11/5 2/12/5 f 5/1/6 2/12/6 6/13/6 f 5/1/1 7/14/1 3/2/1 f 3/4/2 7/7/2 8/5/2 f 7/7/3 5/15/3 6/8/3 f 2/9/4 4/6/4 8/5/4 f 1/3/5 3/16/5 4/11/5 f 5/1/6 1/3/6 2/12/6 ================================================ FILE: src/asset/3d/small_sphere.obj ================================================ # Blender v2.80 (sub 75) OBJ File: '' # www.blender.org o Icosphere v 0.000000 -0.250000 0.000000 v 0.180902 -0.111805 0.131431 v -0.069097 -0.111805 0.212662 v -0.223607 -0.111804 0.000000 v -0.069097 -0.111805 -0.212662 v 0.180902 -0.111805 -0.131431 v 0.069097 0.111805 0.212662 v -0.180902 0.111805 0.131431 v -0.180902 0.111805 -0.131431 v 0.069097 0.111805 -0.212662 v 0.223607 0.111804 0.000000 v 0.000000 0.250000 0.000000 v -0.040614 -0.212664 0.124999 v 0.106331 -0.212664 0.077253 v 0.065717 -0.131434 0.202253 v 0.212662 -0.131434 0.000000 v 0.106331 -0.212664 -0.077253 v -0.131432 -0.212663 0.000000 v -0.172047 -0.131434 0.124999 v -0.040614 -0.212664 -0.124999 v -0.172047 -0.131434 -0.124999 v 0.065717 -0.131434 -0.202253 v 0.237764 0.000000 0.077253 v 0.237764 0.000000 -0.077253 v 0.000000 0.000000 0.250000 v 0.146946 0.000000 0.202254 v -0.237764 0.000000 0.077253 v -0.146946 0.000000 0.202254 v -0.146946 0.000000 -0.202254 v -0.237764 0.000000 -0.077253 v 0.146946 0.000000 -0.202254 v 0.000000 0.000000 -0.250000 v 0.172047 0.131434 0.124999 v -0.065717 0.131434 0.202253 v -0.212662 0.131434 0.000000 v -0.065717 0.131434 -0.202253 v 0.172047 0.131434 -0.124999 v 0.040614 0.212664 0.124999 v 0.131432 0.212663 0.000000 v -0.106331 0.212664 0.077253 v -0.106331 0.212664 -0.077253 v 0.040614 0.212664 -0.124999 vt 0.389468 0.000000 vt 0.650001 0.176206 vt 0.450001 0.176206 vt 0.650001 0.352414 vt 0.750000 0.323790 vt 0.250000 0.176208 vt 0.049999 0.176206 vt 1.049999 0.176206 vt 0.849999 0.176206 vt 0.700001 0.500000 vt 0.450001 0.352414 vt 0.550001 0.323789 vt 0.500000 0.500000 vt 0.250000 0.352416 vt 0.350000 0.323790 vt 0.299999 0.500000 vt 0.049999 0.352414 vt 0.150000 0.323790 vt 0.100000 0.500000 vt 0.849999 0.352414 vt 0.949999 0.323789 vt 0.900000 0.500000 vt 0.600000 0.500000 vt 0.400000 0.500000 vt 0.200001 0.500000 vt 0.000000 0.500000 vt 0.799999 0.500000 vt 0.549999 0.647586 vt 0.650000 0.676210 vt 0.549999 0.823794 vt 0.349999 0.647586 vt 0.449999 0.676211 vt 0.349999 0.823794 vt 0.150001 0.647586 vt 0.250000 0.676210 vt 0.150001 0.823794 vt 0.950001 0.647586 vt 1.050001 0.676211 vt 0.950001 0.823794 vt 0.750000 0.647584 vt 0.850000 0.676210 vt 0.750000 0.823792 vt 0.867720 1.000000 vt 1.150001 0.823794 vt 0.050001 0.676211 vt 1.349999 0.823794 vt 1.000000 0.500000 vt 1.049999 0.352414 vn 0.0000 -1.0000 0.0000 vn 0.4253 -0.8506 0.3090 vn -0.1625 -0.8506 0.5000 vn 0.7236 -0.4472 0.5257 vn 0.8506 -0.5257 0.0000 vn -0.5257 -0.8506 0.0000 vn -0.1625 -0.8506 -0.5000 vn 0.4253 -0.8506 -0.3090 vn 0.9510 0.0000 0.3090 vn -0.2764 -0.4472 0.8506 vn 0.2629 -0.5257 0.8090 vn 0.0000 0.0000 1.0000 vn -0.8944 -0.4472 0.0000 vn -0.6882 -0.5257 0.5000 vn -0.9510 0.0000 0.3090 vn -0.2764 -0.4472 -0.8506 vn -0.6882 -0.5257 -0.5000 vn -0.5878 0.0000 -0.8090 vn 0.7236 -0.4472 -0.5257 vn 0.2629 -0.5257 -0.8090 vn 0.5878 0.0000 -0.8090 vn 0.5878 0.0000 0.8090 vn -0.5878 0.0000 0.8090 vn -0.9510 0.0000 -0.3090 vn 0.0000 0.0000 -1.0000 vn 0.9510 0.0000 -0.3090 vn 0.2764 0.4472 0.8506 vn 0.6882 0.5257 0.5000 vn 0.1625 0.8506 0.5000 vn -0.7236 0.4472 0.5257 vn -0.2629 0.5257 0.8090 vn -0.4253 0.8506 0.3090 vn -0.7236 0.4472 -0.5257 vn -0.8506 0.5257 0.0000 vn -0.4253 0.8506 -0.3090 vn 0.2764 0.4472 -0.8506 vn -0.2629 0.5257 -0.8090 vn 0.1625 0.8506 -0.5000 vn 0.8944 0.4472 0.0000 vn 0.6882 0.5257 -0.5000 vn 0.5257 0.8506 0.0000 vn 0.0000 1.0000 0.0000 s 1 f 1/1/1 14/2/2 13/3/3 f 2/4/4 14/2/2 16/5/5 f 1/1/1 13/3/3 18/6/6 f 1/1/1 18/6/6 20/7/7 f 1/1/1 20/8/7 17/9/8 f 2/4/4 16/5/5 23/10/9 f 3/11/10 15/12/11 25/13/12 f 4/14/13 19/15/14 27/16/15 f 5/17/16 21/18/17 29/19/18 f 6/20/19 22/21/20 31/22/21 f 2/4/4 23/10/9 26/23/22 f 3/11/10 25/13/12 28/24/23 f 4/14/13 27/16/15 30/25/24 f 5/17/16 29/19/18 32/26/25 f 6/20/19 31/22/21 24/27/26 f 7/28/27 33/29/28 38/30/29 f 8/31/30 34/32/31 40/33/32 f 9/34/33 35/35/34 41/36/35 f 10/37/36 36/38/37 42/39/38 f 11/40/39 37/41/40 39/42/41 f 39/42/41 42/39/38 12/43/42 f 39/42/41 37/41/40 42/39/38 f 37/41/40 10/37/36 42/39/38 f 42/39/38 41/44/35 12/43/42 f 42/39/38 36/38/37 41/44/35 f 36/45/37 9/34/33 41/36/35 f 41/44/35 40/46/32 12/43/42 f 41/36/35 35/35/34 40/33/32 f 35/35/34 8/31/30 40/33/32 f 40/46/32 38/30/29 12/43/42 f 40/33/32 34/32/31 38/30/29 f 34/32/31 7/28/27 38/30/29 f 38/30/29 39/42/41 12/43/42 f 38/30/29 33/29/28 39/42/41 f 33/29/28 11/40/39 39/42/41 f 24/27/26 37/41/40 11/40/39 f 24/27/26 31/22/21 37/41/40 f 31/22/21 10/37/36 37/41/40 f 32/47/25 36/38/37 10/37/36 f 32/26/25 29/19/18 36/45/37 f 29/19/18 9/34/33 36/45/37 f 30/25/24 35/35/34 9/34/33 f 30/25/24 27/16/15 35/35/34 f 27/16/15 8/31/30 35/35/34 f 28/24/23 34/32/31 8/31/30 f 28/24/23 25/13/12 34/32/31 f 25/13/12 7/28/27 34/32/31 f 26/23/22 33/29/28 7/28/27 f 26/23/22 23/10/9 33/29/28 f 23/10/9 11/40/39 33/29/28 f 31/22/21 32/47/25 10/37/36 f 31/22/21 22/21/20 32/47/25 f 22/21/20 5/48/16 32/47/25 f 29/19/18 30/25/24 9/34/33 f 29/19/18 21/18/17 30/25/24 f 21/18/17 4/14/13 30/25/24 f 27/16/15 28/24/23 8/31/30 f 27/16/15 19/15/14 28/24/23 f 19/15/14 3/11/10 28/24/23 f 25/13/12 26/23/22 7/28/27 f 25/13/12 15/12/11 26/23/22 f 15/12/11 2/4/4 26/23/22 f 23/10/9 24/27/26 11/40/39 f 23/10/9 16/5/5 24/27/26 f 16/5/5 6/20/19 24/27/26 f 17/9/8 22/21/20 6/20/19 f 17/9/8 20/8/7 22/21/20 f 20/8/7 5/48/16 22/21/20 f 20/7/7 21/18/17 5/17/16 f 20/7/7 18/6/6 21/18/17 f 18/6/6 4/14/13 21/18/17 f 18/6/6 19/15/14 4/14/13 f 18/6/6 13/3/3 19/15/14 f 13/3/3 3/11/10 19/15/14 f 16/5/5 17/9/8 6/20/19 f 16/5/5 14/2/2 17/9/8 f 14/2/2 1/1/1 17/9/8 f 13/3/3 15/12/11 3/11/10 f 13/3/3 14/2/2 15/12/11 f 14/2/2 2/4/4 15/12/11 ================================================ FILE: src/asset/3d/tank/base.obj ================================================ # Blender v2.80 (sub 75) OBJ File: 'tank.blend' # www.blender.org o tank-base_Cube v 0.645520 0.500000 0.145520 v 0.645520 0.395520 0.250000 v 0.750000 0.395520 0.145520 v 0.645520 0.395520 -0.250000 v 0.645520 0.500000 -0.145520 v 0.750000 0.395520 -0.145520 v 0.750000 -0.395520 0.145520 v 0.645520 -0.395520 0.250000 v 0.645520 -0.500000 0.145520 v 0.750000 -0.395520 -0.145520 v 0.645520 -0.500000 -0.145520 v 0.645520 -0.395520 -0.250000 v -0.645520 0.500000 0.145520 v -0.750000 0.395520 0.145520 v -0.645520 0.395520 0.250000 v -0.750000 0.395520 -0.145520 v -0.645520 0.500000 -0.145520 v -0.645520 0.395520 -0.250000 v -0.750000 -0.395520 0.145520 v -0.645520 -0.500000 0.145520 v -0.645520 -0.395520 0.250000 v -0.645520 -0.395520 -0.250000 v -0.645520 -0.500000 -0.145520 v -0.750000 -0.395520 -0.145520 vt 0.195508 0.920138 vt 0.097603 0.830157 vt 0.097603 0.920138 vt 0.852376 0.070561 vt 0.852376 0.160542 vt 0.852376 0.070561 vt 0.958204 0.058676 vt 0.860299 0.058676 vt 0.958204 0.058676 vt 0.451048 0.929439 vt 0.548952 0.839458 vt 0.548952 0.929439 vt 0.966127 0.160542 vt 0.966127 0.070561 vt 0.966127 0.160542 vt 0.195508 0.209632 vt 0.195508 0.197747 vt 0.203431 0.197747 vt 0.195508 0.932023 vt 0.203431 0.920138 vt 0.203431 0.107765 vt 0.195508 0.107765 vt 0.195508 0.095881 vt 0.203431 0.830157 vt 0.195508 0.818272 vt 0.195508 0.830157 vt 0.097603 0.209632 vt 0.089680 0.197747 vt 0.097603 0.197747 vt 0.089680 0.920138 vt 0.097603 0.932023 vt 0.089680 0.107765 vt 0.097603 0.095881 vt 0.097603 0.107765 vt 0.097603 0.818272 vt 0.089680 0.830157 vt 0.958204 0.172427 vt 0.860299 0.058676 vt 0.966127 0.070561 vt 0.860299 0.172427 vt 0.852376 0.160542 vt 0.860299 0.172427 vt 0.091402 0.479812 vt 0.189307 0.467928 vt 0.189307 0.479812 vt 0.197230 0.569794 vt 0.189307 0.569794 vt 0.091402 0.569794 vt 0.083479 0.479812 vt 0.091402 0.581679 vt 0.958204 0.172427 vt 0.451048 0.839458 vt 0.091402 0.467928 vt 0.197230 0.479812 vt 0.083479 0.569794 vt 0.189307 0.581679 vn 0.0000 0.0000 -1.0000 vn -1.0000 0.0000 0.0000 vn 0.0000 -1.0000 0.0000 vn 0.0000 0.0000 1.0000 vn 1.0000 0.0000 0.0000 vn 0.5774 0.5774 0.5774 vn 0.5774 0.5774 -0.5774 vn 0.5774 -0.5774 0.5774 vn 0.5774 -0.5774 -0.5774 vn -0.5774 0.5774 0.5774 vn -0.5774 0.5774 -0.5774 vn -0.5774 -0.5774 0.5774 vn -0.5774 -0.5774 -0.5774 vn -0.7071 0.0000 -0.7071 vn 0.0000 0.7071 -0.7071 vn 0.7071 0.7071 0.0000 vn -0.7071 -0.7071 0.0000 vn 0.7071 -0.7071 0.0000 vn -0.7071 0.7071 0.0000 vn 0.0000 -0.7071 0.7071 vn 0.7071 0.0000 0.7071 vn 0.0000 -0.7071 -0.7071 vn -0.7071 0.0000 0.7071 vn 0.0000 0.7071 0.7071 vn 0.7071 0.0000 -0.7071 vn 0.0000 1.0000 0.0000 s off f 4/1/1 22/2/1 18/3/1 f 19/4/2 16/5/2 24/6/2 f 9/7/3 23/8/3 11/9/3 f 15/10/4 8/11/4 2/12/4 f 3/13/5 10/14/5 6/15/5 f 1/16/6 2/17/6 3/18/6 f 4/1/7 5/19/7 6/20/7 f 7/21/8 8/22/8 9/23/8 f 10/24/9 11/25/9 12/26/9 f 13/27/10 14/28/10 15/29/10 f 16/30/11 17/31/11 18/3/11 f 19/32/12 20/33/12 21/34/12 f 22/2/13 23/35/13 24/36/13 f 22/2/14 16/30/14 18/3/14 f 18/3/15 5/19/15 4/1/15 f 6/15/16 1/37/16 3/13/16 f 20/38/17 24/6/17 23/8/17 f 11/9/18 7/39/18 9/7/18 f 17/40/19 14/41/19 13/42/19 f 21/43/20 9/44/20 8/45/20 f 8/45/21 3/46/21 2/47/21 f 12/26/22 23/35/22 22/2/22 f 15/48/23 19/49/23 21/43/23 f 2/47/24 13/50/24 15/48/24 f 4/1/25 10/24/25 12/26/25 f 13/42/26 5/51/26 17/40/26 f 4/1/1 12/26/1 22/2/1 f 19/4/2 14/41/2 16/5/2 f 9/7/3 20/38/3 23/8/3 f 15/10/4 21/52/4 8/11/4 f 3/13/5 7/39/5 10/14/5 f 22/2/14 24/36/14 16/30/14 f 18/3/15 17/31/15 5/19/15 f 6/15/16 5/51/16 1/37/16 f 20/38/17 19/4/17 24/6/17 f 11/9/18 10/14/18 7/39/18 f 17/40/19 16/5/19 14/41/19 f 21/43/20 20/53/20 9/44/20 f 8/45/21 7/54/21 3/46/21 f 12/26/22 11/25/22 23/35/22 f 15/48/23 14/55/23 19/49/23 f 2/47/24 1/56/24 13/50/24 f 4/1/25 6/20/25 10/24/25 f 13/42/26 1/37/26 5/51/26 ================================================ FILE: src/asset/3d/tank/canon.obj ================================================ # Blender v2.80 (sub 75) OBJ File: 'tank.blend' # www.blender.org o Sphere_Sphere.001 v -0.367321 -0.026418 0.622755 v -0.440375 -0.075231 0.586362 v -0.496289 -0.112591 0.519116 v -0.526549 -0.132810 0.431254 v -0.526549 -0.132810 0.336154 v -0.440375 -0.075231 0.181046 v -0.413958 -0.035694 0.608885 v -0.487012 -0.065954 0.556050 v -0.535825 -0.086173 0.476977 v -0.552966 -0.093273 0.383704 v -0.535825 -0.086173 0.290431 v -0.487012 -0.065954 0.211358 v -0.413958 -0.035694 0.158523 v -0.374421 -0.009277 0.622755 v -0.460594 -0.026417 0.586362 v -0.526549 -0.039537 0.519116 v -0.562243 -0.046637 0.431254 v -0.562243 -0.046637 0.336154 v -0.526549 -0.039537 0.248292 v -0.460594 -0.026417 0.181046 v -0.374421 -0.009277 0.144653 v -0.421058 0.000000 0.608885 v -0.500131 0.000000 0.556050 v -0.552966 0.000000 0.476977 v -0.571519 0.000000 0.383704 v -0.552966 0.000000 0.290431 v -0.500131 0.000000 0.211358 v -0.421058 0.000000 0.158523 v -0.374421 0.009277 0.622755 v -0.460594 0.026418 0.586362 v -0.526549 0.039537 0.519116 v -0.562243 0.046637 0.431254 v -0.562243 0.046637 0.336154 v -0.526549 0.039537 0.248292 v -0.460594 0.026418 0.181046 v -0.374421 0.009277 0.144653 v -0.413958 0.035694 0.608885 v -0.487012 0.065954 0.556050 v -0.535825 0.086173 0.476977 v -0.552966 0.093273 0.383704 v -0.535825 0.086173 0.290431 v -0.487012 0.065954 0.211358 v -0.413958 0.035694 0.158523 v -0.367321 0.026418 0.622755 v -0.440375 0.075231 0.586362 v -0.496288 0.112591 0.519116 v -0.526548 0.132810 0.431254 v -0.526548 0.132810 0.336154 v -0.496288 0.112591 0.248292 v -0.440375 0.075231 0.181046 v -0.367321 0.026418 0.144653 v -0.393739 0.065954 0.608885 v -0.449652 0.121867 0.556050 v -0.487012 0.159227 0.476977 v -0.500131 0.172346 0.383704 v -0.487012 0.159227 0.290431 v -0.449652 0.121867 0.211358 v -0.393739 0.065954 0.158523 v -0.354202 0.039537 0.622755 v -0.403015 0.112591 0.586362 v -0.440375 0.168504 0.519116 v -0.460594 0.198764 0.431254 v -0.460594 0.198764 0.336154 v -0.440375 0.168504 0.248292 v -0.403015 0.112591 0.181046 v -0.354202 0.039537 0.144653 v -0.363479 0.086173 0.608885 v -0.393739 0.159227 0.556050 v -0.413958 0.208041 0.476977 v -0.421058 0.225181 0.383704 v -0.413958 0.208041 0.290431 v -0.393739 0.159227 0.211358 v -0.363479 0.086173 0.158523 v -0.337061 0.046637 0.622755 v -0.354202 0.132810 0.586362 v -0.367321 0.198764 0.519116 v -0.374421 0.234458 0.431254 v -0.374421 0.234458 0.336154 v -0.367321 0.198764 0.248292 v -0.354202 0.132810 0.181046 v -0.337061 0.046637 0.144653 v -0.327785 0.093273 0.608885 v -0.327785 0.172346 0.556050 v -0.327785 0.225181 0.476977 v -0.327785 0.243735 0.383704 v -0.327785 0.225181 0.290431 v -0.327785 0.172346 0.211358 v -0.327785 0.093273 0.158523 v -0.318508 0.046637 0.622755 v -0.301367 0.132810 0.586362 v -0.288248 0.198764 0.519116 v -0.281148 0.234458 0.431254 v -0.281148 0.234458 0.336154 v -0.288248 0.198764 0.248292 v -0.301367 0.132810 0.181046 v -0.318508 0.046637 0.144653 v -0.292090 0.086173 0.608885 v -0.261830 0.159227 0.556050 v -0.241611 0.208041 0.476977 v -0.234511 0.225181 0.383704 v -0.241611 0.208041 0.290431 v -0.261830 0.159227 0.211358 v -0.292090 0.086173 0.158523 v -0.327785 0.000000 0.139970 v -0.301367 0.039537 0.622755 v -0.252554 0.112591 0.586362 v -0.215194 0.168504 0.519116 v -0.194975 0.198764 0.431254 v -0.194975 0.198764 0.336154 v -0.215194 0.168504 0.248292 v -0.252554 0.112591 0.181046 v -0.301367 0.039537 0.144653 v -0.261830 0.065954 0.608885 v -0.205917 0.121867 0.556050 v -0.168557 0.159227 0.476977 v -0.155438 0.172346 0.383704 v -0.168557 0.159227 0.290431 v -0.205917 0.121867 0.211358 v -0.261831 0.065954 0.158523 v -0.288248 0.026418 0.622755 v -0.215194 0.075231 0.586362 v -0.159281 0.112591 0.519116 v -0.129021 0.132810 0.431254 v -0.129021 0.132810 0.336154 v -0.159281 0.112591 0.248292 v -0.215194 0.075231 0.181046 v -0.288248 0.026418 0.144653 v -0.241611 0.035694 0.608885 v -0.168557 0.065954 0.556050 v -0.119744 0.086173 0.476977 v -0.096037 0.083271 0.383704 v -0.119744 0.086173 0.290431 v -0.168557 0.065954 0.211358 v -0.241611 0.035694 0.158523 v -0.281148 0.009277 0.622755 v -0.194975 0.026418 0.586362 v -0.129021 0.039537 0.519116 v -0.096037 0.060303 0.445189 v -0.096037 0.060303 0.322219 v -0.129021 0.039537 0.248292 v -0.194975 0.026418 0.181046 v -0.281148 0.009277 0.144653 v -0.234511 0.000000 0.608885 v -0.155438 0.000000 0.556050 v -0.096037 -0.000000 0.466975 v 0.507671 0.083271 0.383704 v -0.096037 -0.000000 0.300433 v -0.155438 0.000000 0.211358 v -0.234511 0.000000 0.158523 v -0.327785 0.000000 0.627439 v -0.281148 -0.009277 0.622755 v -0.194975 -0.026417 0.586362 v -0.129021 -0.039537 0.519116 v -0.096037 -0.060303 0.445189 v -0.096037 -0.060303 0.322219 v -0.129021 -0.039537 0.248292 v -0.194975 -0.026417 0.181046 v -0.281148 -0.009277 0.144653 v -0.241611 -0.035694 0.608885 v -0.168557 -0.065954 0.556050 v -0.119744 -0.086173 0.476977 v -0.096037 -0.083271 0.383704 v -0.119744 -0.086173 0.290431 v -0.168557 -0.065954 0.211358 v -0.241611 -0.035694 0.158523 v -0.288248 -0.026417 0.622755 v -0.215194 -0.075231 0.586362 v -0.159281 -0.112591 0.519116 v -0.129021 -0.132810 0.431254 v -0.129021 -0.132810 0.336154 v -0.159281 -0.112591 0.248292 v -0.215194 -0.075231 0.181046 v -0.288248 -0.026417 0.144653 v -0.261831 -0.065954 0.608885 v -0.205917 -0.121867 0.556050 v -0.168557 -0.159227 0.476977 v -0.155438 -0.172346 0.383704 v -0.168557 -0.159227 0.290431 v -0.205917 -0.121867 0.211358 v -0.261831 -0.065954 0.158523 v -0.301367 -0.039537 0.622755 v -0.252554 -0.112591 0.586362 v -0.215194 -0.168504 0.519116 v -0.194975 -0.198764 0.431254 v -0.194975 -0.198764 0.336154 v -0.215194 -0.168504 0.248292 v -0.252554 -0.112591 0.181046 v -0.301367 -0.039537 0.144653 v -0.292091 -0.086173 0.608885 v -0.261831 -0.159227 0.556050 v -0.241611 -0.208040 0.476977 v -0.234511 -0.225181 0.383704 v -0.241611 -0.208040 0.290431 v -0.261831 -0.159227 0.211358 v -0.292091 -0.086173 0.158523 v -0.318508 -0.046637 0.622755 v -0.301367 -0.132810 0.586362 v -0.288248 -0.198764 0.519116 v -0.281148 -0.234458 0.431254 v -0.281148 -0.234458 0.336154 v -0.288248 -0.198764 0.248292 v -0.301367 -0.132810 0.181046 v -0.318508 -0.046637 0.144653 v -0.327785 -0.093273 0.608885 v -0.327785 -0.172346 0.556050 v -0.327785 -0.225181 0.476977 v -0.327785 -0.243734 0.383704 v -0.327785 -0.225181 0.290431 v -0.327785 -0.172346 0.211358 v -0.327785 -0.093273 0.158523 v -0.337061 -0.046637 0.622755 v -0.354202 -0.132810 0.586362 v -0.367321 -0.198764 0.519116 v -0.374421 -0.234458 0.431254 v -0.374421 -0.234458 0.336154 v -0.367321 -0.198764 0.248292 v -0.354202 -0.132810 0.181046 v -0.337061 -0.046637 0.144653 v -0.363479 -0.086173 0.608885 v -0.393739 -0.159227 0.556050 v -0.413958 -0.208040 0.476977 v -0.421058 -0.225181 0.383704 v -0.413958 -0.208040 0.290431 v -0.393739 -0.159227 0.211358 v -0.363479 -0.086173 0.158523 v -0.354202 -0.039537 0.622755 v -0.403015 -0.112591 0.586362 v -0.440375 -0.168504 0.519116 v -0.460594 -0.198764 0.431254 v -0.460594 -0.198764 0.336154 v -0.440375 -0.168504 0.248292 v -0.403015 -0.112591 0.181046 v -0.354202 -0.039537 0.144653 v -0.393739 -0.065954 0.608885 v -0.449652 -0.121867 0.556050 v -0.487012 -0.159227 0.476977 v -0.500131 -0.172346 0.383704 v -0.487012 -0.159227 0.290431 v -0.449652 -0.121867 0.211358 v -0.393739 -0.065954 0.158523 v -0.496288 -0.112591 0.248292 v -0.367321 -0.026417 0.144653 v 0.507671 0.060303 0.445189 v 0.507671 0.060303 0.322219 v 0.507671 0.000000 0.466975 v 0.507671 0.000000 0.300433 v 0.507671 -0.060303 0.445189 v 0.507671 -0.060303 0.322219 v 0.507671 -0.083271 0.383704 v 0.507671 0.060200 0.383704 v 0.507671 0.043596 0.428154 v 0.507671 0.043596 0.339254 v 0.507671 0.000000 0.443904 v 0.507671 0.000000 0.323504 v 0.507671 -0.043596 0.428154 v 0.507671 -0.043596 0.339254 v 0.507671 -0.060200 0.383704 v 0.230468 0.060200 0.383704 v 0.230468 0.043596 0.428154 v 0.230468 0.043596 0.339254 v 0.230468 0.000000 0.443904 v 0.230468 0.000000 0.323504 v 0.230468 -0.043596 0.428154 v 0.230468 -0.043596 0.339254 v 0.230468 -0.060200 0.383704 vt 0.117968 0.474762 vt 0.102825 0.497425 vt 0.099065 0.478522 vt 0.121728 0.493665 vt 0.113533 0.513450 vt 0.132436 0.509690 vt 0.102825 0.459619 vt 0.083040 0.489230 vt 0.086800 0.470327 vt 0.086800 0.470327 vt 0.072332 0.505255 vt 0.083040 0.489230 vt 0.097508 0.454301 vt 0.076092 0.486352 vt 0.148461 0.520398 vt 0.151339 0.513450 vt 0.167364 0.524158 vt 0.086800 0.508133 vt 0.086800 0.540183 vt 0.076092 0.524158 vt 0.140631 0.497425 vt 0.132436 0.509690 vt 0.121728 0.493665 vt 0.072332 0.505255 vt 0.099065 0.478522 vt 0.086800 0.508133 vt 0.102825 0.497425 vt 0.129558 0.524158 vt 0.148461 0.520398 vt 0.113533 0.513450 vt 0.113533 0.534865 vt 0.097508 0.524158 vt 0.151339 0.513450 vt 0.167364 0.524158 vt 0.076092 0.524158 vt 0.097508 0.524158 vt 0.129558 0.524158 vt 0.132436 0.538625 vt 0.148461 0.527918 vt 0.113533 0.534865 vt 0.148461 0.527918 vt 0.072332 0.543061 vt 0.102825 0.550891 vt 0.072332 0.543061 vt 0.068572 0.524158 vt 0.086800 0.540183 vt 0.083040 0.559086 vt 0.102825 0.550891 vt 0.099065 0.569794 vt 0.083040 0.559086 vt 0.132436 0.538625 vt 0.121728 0.554651 vt 0.151339 0.534865 vt 0.151339 0.534865 vt 0.121728 0.554651 vt 0.086800 0.577989 vt 0.086800 0.577989 vt 0.076092 0.561964 vt 0.140631 0.550891 vt 0.099065 0.569794 vt 0.102825 0.588697 vt 0.156656 0.540183 vt 0.117968 0.573554 vt 0.121728 0.592457 vt 0.102825 0.588697 vt 0.140631 0.550891 vt 0.117968 0.573554 vt 0.136871 0.569794 vt 0.156656 0.540183 vt 0.113533 0.604722 vt 0.097508 0.594014 vt 0.113533 0.604722 vt 0.140631 0.588697 vt 0.152896 0.559086 vt 0.121728 0.592457 vt 0.136871 0.569794 vt 0.163604 0.543061 vt 0.151339 0.604722 vt 0.132436 0.608482 vt 0.152896 0.559086 vt 0.140631 0.588697 vt 0.156656 0.577989 vt 0.132436 0.608482 vt 0.148461 0.619190 vt 0.129558 0.615430 vt 0.163604 0.543061 vt 0.167364 0.594014 vt 0.167364 0.561964 vt 0.151339 0.604722 vt 0.148461 0.619190 vt 0.156656 0.577989 vt 0.167364 0.594014 vt 0.183389 0.604722 vt 0.167364 0.615430 vt 0.167364 0.561964 vt 0.178072 0.577989 vt 0.167364 0.615430 vt 0.171124 0.543061 vt 0.194097 0.588697 vt 0.181832 0.559086 vt 0.183389 0.604722 vt 0.186267 0.619190 vt 0.171124 0.543061 vt 0.178072 0.577989 vt 0.186267 0.619190 vt 0.167364 0.622950 vt 0.194097 0.588697 vt 0.213000 0.592457 vt 0.202292 0.608482 vt 0.181832 0.559086 vt 0.197857 0.569794 vt 0.202292 0.608482 vt 0.221195 0.604722 vt 0.216760 0.573554 vt 0.194097 0.550891 vt 0.213000 0.592457 vt 0.178072 0.540183 vt 0.197857 0.569794 vt 0.221195 0.604722 vt 0.205170 0.615430 vt 0.178072 0.540183 vt 0.216760 0.573554 vt 0.235663 0.569794 vt 0.231903 0.588697 vt 0.194097 0.550891 vt 0.231903 0.588697 vt 0.247928 0.577989 vt 0.231903 0.550891 vt 0.202292 0.538625 vt 0.213000 0.554651 vt 0.235663 0.569794 vt 0.183389 0.534865 vt 0.213000 0.554651 vt 0.183389 0.534865 vt 0.247928 0.577989 vt 0.237220 0.594014 vt 0.247928 0.540183 vt 0.251688 0.559086 vt 0.221195 0.534865 vt 0.202292 0.538625 vt 0.251688 0.559086 vt 0.231903 0.550891 vt 0.237220 0.524158 vt 0.205170 0.524158 vt 0.186267 0.527918 vt 0.221195 0.534865 vt 0.247928 0.540183 vt 0.261298 0.548600 vt 0.261298 0.557909 vt 0.186267 0.527918 vt 0.261298 0.548600 vt 0.261298 0.524158 vt 0.205170 0.524158 vt 0.221195 0.513450 vt 0.261298 0.524158 vt 0.891784 0.561601 vt 0.945650 0.552291 vt 0.891784 0.552291 vt 0.247928 0.508132 vt 0.237220 0.524158 vt 0.202292 0.509690 vt 0.247928 0.508132 vt 0.186267 0.520398 vt 0.221195 0.513450 vt 0.891784 0.503406 vt 0.945650 0.527849 vt 0.891784 0.527849 vt 0.186267 0.520398 vt 0.891784 0.494097 vt 0.945650 0.503406 vt 0.231903 0.497425 vt 0.202292 0.509690 vt 0.213000 0.493665 vt 0.251688 0.489230 vt 0.891784 0.552291 vt 0.945650 0.561601 vt 0.231903 0.497425 vt 0.251688 0.489230 vt 0.261298 0.499715 vt 0.194097 0.497425 vt 0.235663 0.478522 vt 0.183389 0.513450 vt 0.213000 0.493665 vt 0.247928 0.470327 vt 0.261298 0.490406 vt 0.183389 0.513450 vt 0.235663 0.478522 vt 0.216760 0.474762 vt 0.261298 0.499715 vt 0.247928 0.470327 vt 0.197857 0.478522 vt 0.231903 0.459619 vt 0.216760 0.474762 vt 0.231903 0.459619 vt 0.194097 0.497425 vt 0.213000 0.455859 vt 0.178072 0.508132 vt 0.221195 0.443594 vt 0.237220 0.454301 vt 0.178072 0.508133 vt 0.213000 0.455859 vt 0.197857 0.478522 vt 0.221195 0.443594 vt 0.194097 0.459619 vt 0.178072 0.470327 vt 0.202292 0.439834 vt 0.194097 0.459619 vt 0.181832 0.489230 vt 0.183389 0.443594 vt 0.202292 0.439834 vt 0.181832 0.489230 vt 0.167364 0.486352 vt 0.171124 0.505255 vt 0.186267 0.429126 vt 0.205170 0.432886 vt 0.171124 0.505255 vt 0.178072 0.470327 vt 0.186267 0.429126 vt 0.167364 0.454301 vt 0.183389 0.443594 vt 0.156656 0.470327 vt 0.167364 0.454301 vt 0.151339 0.443594 vt 0.167364 0.432886 vt 0.167364 0.432886 vt 0.167364 0.486352 vt 0.152896 0.489230 vt 0.163604 0.505255 vt 0.156656 0.470327 vt 0.163604 0.505255 vt 0.148461 0.429126 vt 0.140631 0.459619 vt 0.148461 0.429126 vt 0.167364 0.425366 vt 0.151339 0.443594 vt 0.132436 0.439834 vt 0.140631 0.459619 vt 0.121728 0.455859 vt 0.132436 0.439834 vt 0.152896 0.489230 vt 0.129558 0.432886 vt 0.136871 0.478522 vt 0.156656 0.508132 vt 0.156656 0.508133 vt 0.136871 0.478522 vt 0.113533 0.443594 vt 0.113533 0.443594 vt 0.121728 0.455859 vt 0.102825 0.459619 vt 0.117968 0.474762 vt 0.140631 0.497425 vt 0.945650 0.527849 vt 0.891784 0.527849 vt 0.891784 0.503406 vt 0.945650 0.503406 vt 0.945650 0.494097 vt 0.945650 0.552291 vt 0.945650 0.510178 vt 0.945650 0.527849 vt 0.945650 0.545519 vt 0.945650 0.527849 vt 0.945650 0.545519 vt 0.945650 0.552250 vt 0.945650 0.510178 vt 0.945650 0.503448 vt 0.937178 0.552250 vt 0.937178 0.545519 vt 0.937178 0.527849 vt 0.937178 0.503448 vt 0.937178 0.545519 vt 0.937178 0.527849 vt 0.937178 0.510178 vt 0.937178 0.510178 vn -0.5053 -0.5053 0.6995 vn -0.6602 -0.2734 0.6995 vn -0.6962 -0.4652 0.5466 vn -0.4683 -0.3129 -0.8263 vn -0.5524 -0.1099 -0.8263 vn -0.3604 -0.1493 -0.9208 vn -0.6556 -0.6556 0.3745 vn -0.8566 -0.3548 0.3745 vn -0.8163 -0.5454 0.1902 vn -0.8163 -0.5454 -0.1902 vn -0.9629 -0.1915 -0.1902 vn -0.8566 -0.3548 -0.3745 vn -0.7071 -0.7071 0.0000 vn -0.9239 -0.3827 0.0000 vn -0.1996 -0.0397 0.9791 vn -0.1692 -0.1131 0.9791 vn 0.0000 0.0000 1.0000 vn -0.8213 -0.1633 0.5466 vn -0.8213 0.1634 0.5466 vn -0.9272 0.0000 0.3745 vn -0.2758 -0.2758 0.9208 vn -0.3604 -0.1493 0.9208 vn -0.4683 -0.3129 0.8263 vn -0.9629 -0.1915 0.1902 vn -0.6962 -0.4652 -0.5466 vn -0.8213 -0.1633 -0.5466 vn -0.6602 -0.2734 -0.6995 vn -0.3901 0.0000 -0.9208 vn -0.1996 -0.0397 -0.9791 vn -0.5524 -0.1099 0.8263 vn -0.5524 0.1099 0.8263 vn -0.7146 0.0000 0.6995 vn -0.1692 -0.1131 -0.9791 vn 0.0000 0.0000 -1.0000 vn -0.9272 0.0000 -0.3745 vn -0.7146 0.0000 -0.6995 vn -0.3901 0.0000 0.9208 vn -0.3604 0.1493 0.9208 vn -0.1996 0.0397 0.9791 vn -0.5524 0.1099 -0.8263 vn -0.1996 0.0397 -0.9791 vn -0.9629 0.1915 -0.1902 vn -0.6602 0.2734 0.6995 vn -0.9629 0.1915 0.1902 vn -1.0000 0.0000 0.0000 vn -0.8213 0.1633 -0.5466 vn -0.8566 0.3548 -0.3745 vn -0.6602 0.2734 -0.6995 vn -0.6962 0.4652 0.5466 vn -0.8566 0.3548 0.3745 vn -0.3604 0.1493 -0.9208 vn -0.4683 0.3129 0.8263 vn -0.1692 0.1131 0.9791 vn -0.1692 0.1131 -0.9791 vn -0.4683 0.3129 -0.8263 vn -0.8163 0.5454 -0.1902 vn -0.8163 0.5454 0.1902 vn -0.9239 0.3827 0.0000 vn -0.2758 0.2758 0.9208 vn -0.6962 0.4652 -0.5466 vn -0.6556 0.6556 -0.3745 vn -0.1131 0.1692 0.9791 vn -0.5053 0.5053 -0.6995 vn -0.4652 0.6962 0.5466 vn -0.6556 0.6556 0.3745 vn -0.2758 0.2758 -0.9208 vn -0.5053 0.5053 0.6995 vn -0.3129 0.4683 0.8263 vn -0.1131 0.1692 -0.9791 vn -0.5454 0.8163 0.1902 vn -0.7071 0.7071 0.0000 vn -0.5454 0.8163 -0.1902 vn -0.2734 0.6602 0.6995 vn -0.1493 0.3604 0.9208 vn -0.4652 0.6962 -0.5466 vn -0.3129 0.4683 -0.8263 vn -0.0397 0.1996 0.9791 vn -0.1633 0.8213 0.5466 vn -0.3548 0.8566 0.3745 vn -0.1493 0.3604 -0.9208 vn -0.2734 0.6602 -0.6995 vn -0.1099 0.5524 0.8263 vn -0.3548 0.8566 -0.3745 vn -0.1915 0.9629 0.1902 vn -0.3827 0.9239 0.0000 vn -0.0397 0.1996 -0.9791 vn 0.0000 0.7146 0.6995 vn 0.0000 0.3901 0.9208 vn -0.1633 0.8213 -0.5466 vn -0.1915 0.9629 -0.1902 vn -0.1099 0.5524 -0.8263 vn 0.0000 0.7146 -0.6995 vn 0.1634 0.8213 0.5466 vn 0.0000 0.9272 0.3745 vn 0.0000 0.3901 -0.9208 vn 0.1099 0.5524 0.8263 vn 0.0000 0.9272 -0.3745 vn 0.0397 0.1996 -0.9791 vn 0.2734 0.6602 0.6995 vn 0.1493 0.3604 0.9208 vn 0.1634 0.8213 -0.5466 vn 0.1915 0.9629 -0.1902 vn 0.0397 0.1996 0.9791 vn 0.1099 0.5524 -0.8263 vn 0.1915 0.9629 0.1902 vn 0.0000 1.0000 0.0000 vn 0.2734 0.6602 -0.6995 vn 0.4652 0.6962 0.5466 vn 0.3548 0.8566 0.3745 vn 0.1493 0.3604 -0.9208 vn 0.3129 0.4683 0.8263 vn 0.3548 0.8566 -0.3745 vn 0.5454 0.8163 -0.1902 vn 0.5053 0.5053 0.6995 vn 0.2758 0.2758 0.9208 vn 0.4652 0.6962 -0.5466 vn 0.1131 0.1692 0.9791 vn 0.3129 0.4683 -0.8263 vn 0.5454 0.8163 0.1902 vn 0.3827 0.9239 0.0000 vn 0.1131 0.1692 -0.9791 vn 0.5053 0.5053 -0.6995 vn 0.6962 0.4652 0.5466 vn 0.6556 0.6556 0.3745 vn 0.2758 0.2758 -0.9208 vn 0.6556 0.6556 -0.3745 vn 0.8050 0.5620 -0.1899 vn 0.6602 0.2734 0.6995 vn 0.3604 0.1493 0.9208 vn 0.4683 0.3129 0.8263 vn 0.6962 0.4652 -0.5466 vn 0.1692 0.1131 0.9791 vn 0.4683 0.3129 -0.8263 vn 0.1692 0.1131 -0.9791 vn 0.8050 0.5620 0.1899 vn 0.7071 0.7071 0.0000 vn 0.8169 0.1643 0.5528 vn 0.8322 0.3716 0.4115 vn 0.5524 0.1099 0.8263 vn 0.3604 0.1493 -0.9208 vn 0.8322 0.3716 -0.4115 vn 0.6602 0.2734 -0.6995 vn 0.7146 0.0000 0.6995 vn 0.3901 0.0000 0.9208 vn 0.1996 0.0397 0.9791 vn 0.5524 0.1099 -0.8263 vn 0.8169 0.1643 -0.5528 vn 0.5832 0.5606 0.5878 vn 0.5667 0.8239 0.0000 vn 0.1996 0.0397 -0.9791 vn 0.5832 0.5606 -0.5878 vn 0.5658 0.0000 0.8245 vn 0.3901 0.0000 -0.9208 vn 0.5524 -0.1099 0.8263 vn 0.5658 0.0000 -0.8245 vn 0.6237 0.5497 -0.5557 vn 0.8169 -0.1643 0.5528 vn 0.7146 0.0000 -0.6995 vn 0.3604 -0.1493 0.9208 vn 0.8169 -0.1643 -0.5528 vn 0.1996 -0.0397 0.9791 vn 0.5524 -0.1099 -0.8263 vn 0.5832 -0.5606 0.5878 vn 0.6380 0.0000 0.7700 vn 0.1996 -0.0397 -0.9791 vn 0.5667 -0.8239 0.0000 vn 0.6237 -0.5497 0.5557 vn 0.6602 -0.2734 0.6995 vn 0.3604 -0.1493 -0.9208 vn 0.4683 -0.3129 0.8263 vn 0.8322 -0.3716 -0.4115 vn 0.6362 0.7715 0.0000 vn 0.6602 -0.2734 -0.6995 vn 0.8322 -0.3716 0.4115 vn 0.2758 -0.2758 0.9208 vn 0.6962 -0.4652 -0.5466 vn 0.1692 -0.1131 0.9791 vn 0.4683 -0.3129 -0.8263 vn 0.8050 -0.5620 0.1899 vn 0.1692 -0.1131 -0.9791 vn 0.6962 -0.4652 0.5466 vn 0.5053 -0.5053 0.6995 vn 0.5832 -0.5606 -0.5878 vn 0.8050 -0.5620 -0.1899 vn 0.3129 -0.4683 0.8263 vn 0.6556 -0.6556 -0.3745 vn 0.5053 -0.5053 -0.6995 vn 0.6556 -0.6556 0.3745 vn 0.2758 -0.2758 -0.9208 vn 0.4652 -0.6962 -0.5466 vn 0.1131 -0.1692 0.9791 vn 0.5454 -0.8163 0.1902 vn 0.7071 -0.7071 0.0000 vn 0.1131 -0.1692 -0.9791 vn 0.4652 -0.6962 0.5466 vn 0.3129 -0.4683 -0.8263 vn 0.5454 -0.8163 -0.1902 vn 0.2734 -0.6602 0.6995 vn 0.1099 -0.5524 0.8263 vn 0.3548 -0.8566 -0.3745 vn 0.2734 -0.6602 -0.6995 vn 0.1493 -0.3604 0.9208 vn 0.1633 -0.8213 0.5466 vn 0.3548 -0.8566 0.3745 vn 0.1493 -0.3604 -0.9208 vn 0.0000 -0.3901 0.9208 vn 0.0397 -0.1996 0.9791 vn 0.1915 -0.9629 0.1902 vn 0.3827 -0.9239 0.0000 vn 0.0397 -0.1996 -0.9791 vn 0.1099 -0.5524 -0.8263 vn 0.1915 -0.9629 -0.1902 vn 0.0000 -0.7146 0.6995 vn 0.1633 -0.8213 -0.5466 vn -0.1099 -0.5524 0.8263 vn 0.0000 -0.7146 -0.6995 vn -0.1634 -0.8213 0.5466 vn 0.0000 -0.9272 0.3745 vn 0.0000 -0.9272 -0.3745 vn 0.0000 -0.3901 -0.9208 vn -0.1493 -0.3604 0.9208 vn -0.0397 -0.1996 0.9791 vn -0.1099 -0.5524 -0.8263 vn -0.0397 -0.1996 -0.9791 vn -0.1915 -0.9629 -0.1902 vn -0.2734 -0.6602 0.6995 vn -0.1915 -0.9629 0.1902 vn 0.0000 -1.0000 0.0000 vn -0.1634 -0.8213 -0.5466 vn -0.3548 -0.8566 -0.3745 vn -0.2734 -0.6602 -0.6995 vn -0.4652 -0.6962 0.5466 vn -0.3548 -0.8566 0.3745 vn -0.1493 -0.3604 -0.9208 vn -0.3827 -0.9239 0.0000 vn -0.3129 -0.4683 0.8263 vn -0.1131 -0.1692 0.9791 vn -0.1131 -0.1692 -0.9791 vn -0.3129 -0.4683 -0.8263 vn -0.5454 -0.8163 -0.1902 vn -0.5454 -0.8163 0.1902 vn -0.4652 -0.6962 -0.5466 vn -0.6556 -0.6556 -0.3745 vn -0.5053 -0.5053 -0.6995 vn -0.2758 -0.2758 -0.9208 vn 0.6380 0.0000 -0.7700 vn 0.6237 -0.5497 -0.5557 vn 0.6362 -0.7715 0.0000 vn 0.6237 0.5497 0.5557 vn 0.8148 0.4076 -0.4121 vn 0.7921 0.0000 -0.6103 vn 0.8148 -0.4076 0.4121 vn 0.7921 0.0000 0.6103 vn 0.8148 -0.4076 -0.4121 vn 0.7949 -0.6066 0.0000 vn 0.8148 0.4076 0.4121 vn 0.7949 0.6066 0.0000 s 1 f 235/1/1 8/2/2 3/3/3 f 6/4/4 20/5/5 13/6/6 f 236/7/7 9/8/8 4/9/9 f 5/10/10 18/11/11 11/12/12 f 237/13/13 10/14/14 5/10/10 f 14/15/15 1/16/16 150/17/17 f 16/18/18 31/19/19 24/20/20 f 234/21/21 7/22/22 2/23/23 f 3/3/3 16/18/18 9/8/8 f 4/9/9 17/24/24 10/14/14 f 241/25/25 19/26/26 12/27/27 f 13/6/6 28/28/28 21/29/29 f 15/30/30 30/31/31 23/32/32 f 242/33/33 21/29/29 104/34/34 f 11/12/12 26/35/35 19/26/26 f 9/8/8 24/20/20 17/24/24 f 12/27/27 27/36/36 20/5/5 f 17/24/24 18/11/11 10/14/14 f 22/37/37 37/38/38 30/31/31 f 29/39/39 14/15/15 150/17/17 f 20/5/5 35/40/40 28/28/28 f 21/29/29 36/41/41 104/34/34 f 18/11/11 33/42/42 26/35/35 f 23/32/32 38/43/43 31/19/19 f 17/24/24 32/44/44 25/45/45 f 19/26/26 34/46/46 27/36/36 f 26/35/35 41/47/47 34/46/46 f 27/36/36 42/48/48 35/40/40 f 31/19/19 46/49/49 39/50/50 f 24/20/20 39/50/50 32/44/44 f 28/28/28 43/51/51 36/41/41 f 32/44/44 33/42/42 25/45/45 f 30/31/31 45/52/52 38/43/43 f 44/53/53 29/39/39 150/17/17 f 36/41/41 51/54/54 104/34/34 f 35/40/40 50/55/55 43/51/51 f 33/42/42 48/56/56 41/47/47 f 32/44/44 47/57/57 40/58/58 f 37/38/38 52/59/59 45/52/52 f 34/46/46 49/60/60 42/48/48 f 41/47/47 56/61/61 49/60/60 f 59/62/62 44/53/53 150/17/17 f 42/48/48 57/63/63 50/55/55 f 46/49/49 61/64/64 54/65/65 f 39/50/50 54/65/65 47/57/57 f 43/51/51 58/66/66 51/54/54 f 38/43/43 53/67/67 46/49/49 f 45/52/52 60/68/68 53/67/67 f 47/57/57 48/56/56 40/58/58 f 51/54/54 66/69/69 104/34/34 f 47/57/57 62/70/70 55/71/71 f 48/56/56 63/72/72 56/61/61 f 53/67/67 68/73/73 61/64/64 f 52/59/59 67/74/74 60/68/68 f 49/60/60 64/75/75 57/63/63 f 50/55/55 65/76/76 58/66/66 f 74/77/77 59/62/62 150/17/17 f 61/64/64 76/78/78 69/79/79 f 54/65/65 69/79/79 62/70/70 f 58/66/66 73/80/80 66/69/69 f 57/63/63 72/81/81 65/76/76 f 62/70/70 63/72/72 55/71/71 f 60/68/68 75/82/82 68/73/73 f 56/61/61 71/83/83 64/75/75 f 62/70/70 77/84/84 70/85/85 f 66/69/69 81/86/86 104/34/34 f 68/73/73 83/87/87 76/78/78 f 67/74/74 82/88/88 75/82/82 f 64/75/75 79/89/89 72/81/81 f 63/72/72 78/90/90 71/83/83 f 65/76/76 80/91/91 73/80/80 f 72/81/81 87/92/92 80/91/91 f 76/78/78 91/93/93 84/94/94 f 69/79/79 84/94/94 77/84/84 f 73/80/80 88/95/95 81/86/86 f 77/84/84 78/90/90 70/85/85 f 75/82/82 90/96/96 83/87/87 f 71/83/83 86/97/97 79/89/89 f 81/86/86 96/98/98 104/34/34 f 83/87/87 98/99/99 91/93/93 f 82/88/88 97/100/100 90/96/96 f 79/89/89 94/101/101 87/92/92 f 78/90/90 93/102/102 86/97/97 f 89/103/103 74/77/77 150/17/17 f 80/91/91 95/104/104 88/95/95 f 77/84/84 92/105/105 85/106/106 f 87/92/92 102/107/107 95/104/104 f 91/93/93 107/108/108 99/109/109 f 88/95/95 103/110/110 96/98/98 f 92/105/105 93/102/102 85/106/106 f 90/96/96 106/111/111 98/99/99 f 84/94/94 99/109/109 92/105/105 f 86/97/97 101/112/112 94/101/101 f 93/102/102 109/113/113 101/112/112 f 98/99/99 114/114/114 107/108/108 f 97/100/100 113/115/115 106/111/111 f 94/101/101 110/116/116 102/107/107 f 105/117/117 89/103/103 150/17/17 f 95/104/104 111/118/118 103/110/110 f 92/105/105 108/119/119 100/120/120 f 96/98/98 112/121/121 104/34/34 f 102/107/107 118/122/122 111/118/118 f 107/108/108 122/123/123 115/124/124 f 108/119/119 109/113/113 100/120/120 f 99/109/109 115/124/124 108/119/119 f 103/110/110 119/125/125 112/121/121 f 101/112/112 117/126/126 110/116/116 f 109/113/113 124/127/127 117/126/126 f 114/114/114 129/128/128 122/123/123 f 113/115/115 128/129/129 121/130/130 f 110/116/116 125/131/131 118/122/122 f 120/132/132 105/117/117 150/17/17 f 111/118/118 126/133/133 119/125/125 f 106/111/111 121/130/130 114/114/114 f 112/121/121 127/134/134 104/34/34 f 108/119/119 123/135/135 116/136/136 f 122/123/123 137/137/137 130/138/138 f 115/124/124 130/138/138 123/135/135 f 123/135/135 124/127/127 116/136/136 f 121/130/130 136/139/139 129/128/128 f 119/125/125 134/140/140 127/134/134 f 117/126/126 132/141/141 125/131/131 f 118/122/122 133/142/142 126/133/133 f 129/128/128 144/143/143 137/137/137 f 128/129/129 143/144/144 136/139/139 f 135/145/145 120/132/132 150/17/17 f 126/133/133 141/146/146 134/140/140 f 125/131/131 140/147/147 133/142/142 f 123/135/135 138/148/148 131/149/149 f 127/134/134 142/150/150 104/34/34 f 124/127/127 139/151/151 132/141/141 f 130/138/138 145/152/152 138/148/148 f 134/140/140 149/153/153 142/150/150 f 136/139/139 152/154/154 144/143/143 f 132/141/141 147/155/155 140/147/147 f 131/156/149 244/157/156 139/158/151 f 137/137/137 153/159/157 145/152/152 f 133/142/142 148/160/158 141/146/146 f 143/144/144 159/161/159 152/154/154 f 140/147/147 156/162/160 148/160/158 f 151/163/161 135/145/145 150/17/17 f 141/146/146 157/164/162 149/153/153 f 154/165/163 245/166/164 145/167/152 f 142/150/150 158/168/165 104/34/34 f 162/169/166 247/170/167 154/165/163 f 144/143/143 160/171/168 153/159/157 f 149/153/153 165/172/169 158/168/165 f 152/154/154 167/173/170 160/171/168 f 147/155/155 163/174/171 156/162/160 f 138/175/148 146/176/172 131/156/149 f 148/160/158 164/177/173 157/164/162 f 145/152/152 161/178/174 154/179/163 f 159/161/159 174/180/175 167/173/170 f 156/162/160 171/181/176 164/177/173 f 166/182/177 151/163/161 150/17/17 f 157/164/162 172/183/178 165/172/169 f 154/179/163 169/184/179 162/185/166 f 158/168/165 173/186/180 104/34/34 f 153/159/157 168/187/181 161/178/174 f 160/171/168 175/188/182 168/187/181 f 155/189/183 170/190/184 163/174/171 f 169/184/179 170/190/184 162/185/166 f 167/173/170 182/191/185 175/188/182 f 163/174/171 178/192/186 171/181/176 f 164/177/173 179/193/187 172/183/178 f 161/178/174 176/194/188 169/184/179 f 165/172/169 180/195/189 173/186/180 f 171/181/176 186/196/190 179/193/187 f 181/197/191 166/182/177 150/17/17 f 169/184/179 184/198/192 177/199/193 f 173/186/180 188/200/194 104/34/34 f 168/187/181 183/201/195 176/194/188 f 172/183/178 187/202/196 180/195/189 f 170/190/184 185/203/197 178/192/186 f 175/188/182 190/204/198 183/201/195 f 184/198/192 185/203/197 177/199/193 f 182/191/185 197/205/199 190/204/198 f 178/192/186 193/206/200 186/196/190 f 179/193/187 194/207/201 187/202/196 f 174/180/175 189/208/202 182/191/185 f 183/201/195 198/209/203 191/210/204 f 180/195/189 195/211/205 188/200/194 f 176/194/188 191/210/204 184/198/192 f 189/208/202 204/212/206 197/205/199 f 196/213/207 181/197/191 150/17/17 f 184/198/192 199/214/208 192/215/209 f 188/200/194 203/216/210 104/34/34 f 187/202/196 202/217/211 195/211/205 f 185/203/197 200/218/212 193/206/200 f 190/204/198 205/219/213 198/209/203 f 186/196/190 201/220/214 194/207/201 f 197/205/199 212/221/215 205/219/213 f 194/207/201 209/222/216 202/217/211 f 198/209/203 213/223/217 206/224/218 f 193/206/200 208/225/219 201/220/214 f 191/210/204 206/224/218 199/214/208 f 195/211/205 210/226/220 203/216/210 f 199/214/208 200/218/212 192/215/209 f 204/212/206 219/227/221 212/221/215 f 211/228/222 196/213/207 150/17/17 f 202/217/211 217/229/223 210/226/220 f 203/216/210 218/230/224 104/34/34 f 200/218/212 215/231/225 208/225/219 f 205/219/213 220/232/226 213/223/217 f 199/214/208 214/233/227 207/234/228 f 201/220/214 216/235/229 209/222/216 f 208/225/219 223/236/230 216/235/229 f 209/222/216 224/237/231 217/229/223 f 213/223/217 228/238/232 221/239/233 f 206/224/218 221/239/233 214/233/227 f 210/226/220 225/240/234 218/230/224 f 207/234/228 222/241/235 215/231/225 f 212/221/215 227/242/236 220/232/226 f 226/243/237 211/228/222 150/17/17 f 218/230/224 233/244/238 104/34/34 f 217/229/223 232/245/239 225/240/234 f 215/231/225 230/246/240 223/236/230 f 214/233/227 229/247/241 222/241/235 f 219/227/221 234/21/21 227/242/236 f 216/235/229 231/248/242 224/237/231 f 223/236/230 238/249/243 231/248/242 f 224/237/231 239/250/244 232/245/239 f 228/238/232 3/3/3 236/7/7 f 221/239/233 236/7/7 229/247/241 f 225/240/234 240/251/245 233/244/238 f 220/232/226 235/1/1 228/238/232 f 227/242/236 2/23/23 235/1/1 f 222/241/235 237/13/13 230/246/240 f 1/16/16 226/243/237 150/17/17 f 233/244/238 242/33/33 104/34/34 f 229/247/241 4/9/9 237/13/13 f 230/246/240 5/10/10 238/249/243 f 231/248/242 241/25/25 239/250/244 f 232/245/239 6/4/4 240/251/245 f 7/22/22 22/37/37 15/30/30 f 13/6/6 240/251/245 6/4/4 f 239/250/244 12/27/27 6/4/4 f 11/12/12 238/249/243 5/10/10 f 2/23/23 15/30/30 8/2/2 f 8/2/2 23/32/32 16/18/18 f 235/1/1 2/23/23 8/2/2 f 6/4/4 12/27/27 20/5/5 f 236/7/7 3/3/3 9/8/8 f 5/10/10 10/14/14 18/11/11 f 237/13/13 4/9/9 10/14/14 f 14/15/15 7/22/22 1/16/16 f 16/18/18 23/32/32 31/19/19 f 234/21/21 1/16/16 7/22/22 f 3/3/3 8/2/2 16/18/18 f 4/9/9 9/8/8 17/24/24 f 241/25/25 11/12/12 19/26/26 f 13/6/6 20/5/5 28/28/28 f 15/30/30 22/37/37 30/31/31 f 242/33/33 13/6/6 21/29/29 f 11/12/12 18/11/11 26/35/35 f 9/8/8 16/18/18 24/20/20 f 12/27/27 19/26/26 27/36/36 f 17/24/24 25/45/45 18/11/11 f 22/37/37 29/39/39 37/38/38 f 29/39/39 22/37/37 14/15/15 f 20/5/5 27/36/36 35/40/40 f 21/29/29 28/28/28 36/41/41 f 18/11/11 25/45/45 33/42/42 f 23/32/32 30/31/31 38/43/43 f 17/24/24 24/20/20 32/44/44 f 19/26/26 26/35/35 34/46/46 f 26/35/35 33/42/42 41/47/47 f 27/36/36 34/46/46 42/48/48 f 31/19/19 38/43/43 46/49/49 f 24/20/20 31/19/19 39/50/50 f 28/28/28 35/40/40 43/51/51 f 32/44/44 40/58/58 33/42/42 f 30/31/31 37/38/38 45/52/52 f 44/53/53 37/38/38 29/39/39 f 36/41/41 43/51/51 51/54/54 f 35/40/40 42/48/48 50/55/55 f 33/42/42 40/58/58 48/56/56 f 32/44/44 39/50/50 47/57/57 f 37/38/38 44/53/53 52/59/59 f 34/46/46 41/47/47 49/60/60 f 41/47/47 48/56/56 56/61/61 f 59/62/62 52/59/59 44/53/53 f 42/48/48 49/60/60 57/63/63 f 46/49/49 53/67/67 61/64/64 f 39/50/50 46/49/49 54/65/65 f 43/51/51 50/55/55 58/66/66 f 38/43/43 45/52/52 53/67/67 f 45/52/52 52/59/59 60/68/68 f 47/57/57 55/71/71 48/56/56 f 51/54/54 58/66/66 66/69/69 f 47/57/57 54/65/65 62/70/70 f 48/56/56 55/71/71 63/72/72 f 53/67/67 60/68/68 68/73/73 f 52/59/59 59/62/62 67/74/74 f 49/60/60 56/61/61 64/75/75 f 50/55/55 57/63/63 65/76/76 f 74/77/77 67/74/74 59/62/62 f 61/64/64 68/73/73 76/78/78 f 54/65/65 61/64/64 69/79/79 f 58/66/66 65/76/76 73/80/80 f 57/63/63 64/75/75 72/81/81 f 62/70/70 70/85/85 63/72/72 f 60/68/68 67/74/74 75/82/82 f 56/61/61 63/72/72 71/83/83 f 62/70/70 69/79/79 77/84/84 f 66/69/69 73/80/80 81/86/86 f 68/73/73 75/82/82 83/87/87 f 67/74/74 74/77/77 82/88/88 f 64/75/75 71/83/83 79/89/89 f 63/72/72 70/85/85 78/90/90 f 65/76/76 72/81/81 80/91/91 f 72/81/81 79/89/89 87/92/92 f 76/78/78 83/87/87 91/93/93 f 69/79/79 76/78/78 84/94/94 f 73/80/80 80/91/91 88/95/95 f 77/84/84 85/106/106 78/90/90 f 75/82/82 82/88/88 90/96/96 f 71/83/83 78/90/90 86/97/97 f 81/86/86 88/95/95 96/98/98 f 83/87/87 90/96/96 98/99/99 f 82/88/88 89/103/103 97/100/100 f 79/89/89 86/97/97 94/101/101 f 78/90/90 85/106/106 93/102/102 f 89/103/103 82/88/88 74/77/77 f 80/91/91 87/92/92 95/104/104 f 77/84/84 84/94/94 92/105/105 f 87/92/92 94/101/101 102/107/107 f 91/93/93 98/99/99 107/108/108 f 88/95/95 95/104/104 103/110/110 f 92/105/105 100/120/120 93/102/102 f 90/96/96 97/100/100 106/111/111 f 84/94/94 91/93/93 99/109/109 f 86/97/97 93/102/102 101/112/112 f 93/102/102 100/120/120 109/113/113 f 98/99/99 106/111/111 114/114/114 f 97/100/100 105/117/117 113/115/115 f 94/101/101 101/112/112 110/116/116 f 105/117/117 97/100/100 89/103/103 f 95/104/104 102/107/107 111/118/118 f 92/105/105 99/109/109 108/119/119 f 96/98/98 103/110/110 112/121/121 f 102/107/107 110/116/116 118/122/122 f 107/108/108 114/114/114 122/123/123 f 108/119/119 116/136/136 109/113/113 f 99/109/109 107/108/108 115/124/124 f 103/110/110 111/118/118 119/125/125 f 101/112/112 109/113/113 117/126/126 f 109/113/113 116/136/136 124/127/127 f 114/114/114 121/130/130 129/128/128 f 113/115/115 120/132/132 128/129/129 f 110/116/116 117/126/126 125/131/131 f 120/132/132 113/115/115 105/117/117 f 111/118/118 118/122/122 126/133/133 f 106/111/111 113/115/115 121/130/130 f 112/121/121 119/125/125 127/134/134 f 108/119/119 115/124/124 123/135/135 f 122/123/123 129/128/128 137/137/137 f 115/124/124 122/123/123 130/138/138 f 123/135/135 131/149/149 124/127/127 f 121/130/130 128/129/129 136/139/139 f 119/125/125 126/133/133 134/140/140 f 117/126/126 124/127/127 132/141/141 f 118/122/122 125/131/131 133/142/142 f 129/128/128 136/139/139 144/143/143 f 128/129/129 135/145/145 143/144/144 f 135/145/145 128/129/129 120/132/132 f 126/133/133 133/142/142 141/146/146 f 125/131/131 132/141/141 140/147/147 f 123/135/135 130/138/138 138/148/148 f 127/134/134 134/140/140 142/150/150 f 124/127/127 131/149/149 139/151/151 f 130/138/138 137/137/137 145/152/152 f 134/140/140 141/146/146 149/153/153 f 136/139/139 143/144/144 152/154/154 f 132/141/141 139/151/151 147/155/155 f 139/158/151 246/252/246 147/253/155 f 137/137/137 144/143/143 153/159/157 f 133/142/142 140/147/147 148/160/158 f 143/144/144 151/163/161 159/161/159 f 140/147/147 147/155/155 156/162/160 f 151/163/161 143/144/144 135/145/145 f 141/146/146 148/160/158 157/164/162 f 155/254/183 246/252/246 248/255/247 f 142/150/150 149/153/153 158/168/165 f 155/254/183 249/256/248 162/169/166 f 144/143/143 152/154/154 160/171/168 f 149/153/153 157/164/162 165/172/169 f 152/154/154 159/161/159 167/173/170 f 147/155/155 155/189/183 163/174/171 f 145/167/152 243/257/249 138/175/148 f 148/160/158 156/162/160 164/177/173 f 145/152/152 153/159/157 161/178/174 f 159/161/159 166/182/177 174/180/175 f 156/162/160 163/174/171 171/181/176 f 166/182/177 159/161/159 151/163/161 f 157/164/162 164/177/173 172/183/178 f 154/179/163 161/178/174 169/184/179 f 158/168/165 165/172/169 173/186/180 f 153/159/157 160/171/168 168/187/181 f 160/171/168 167/173/170 175/188/182 f 155/189/183 162/185/166 170/190/184 f 169/184/179 177/199/193 170/190/184 f 167/173/170 174/180/175 182/191/185 f 163/174/171 170/190/184 178/192/186 f 164/177/173 171/181/176 179/193/187 f 161/178/174 168/187/181 176/194/188 f 165/172/169 172/183/178 180/195/189 f 171/181/176 178/192/186 186/196/190 f 181/197/191 174/180/175 166/182/177 f 169/184/179 176/194/188 184/198/192 f 173/186/180 180/195/189 188/200/194 f 168/187/181 175/188/182 183/201/195 f 172/183/178 179/193/187 187/202/196 f 170/190/184 177/199/193 185/203/197 f 175/188/182 182/191/185 190/204/198 f 184/198/192 192/215/209 185/203/197 f 182/191/185 189/208/202 197/205/199 f 178/192/186 185/203/197 193/206/200 f 179/193/187 186/196/190 194/207/201 f 174/180/175 181/197/191 189/208/202 f 183/201/195 190/204/198 198/209/203 f 180/195/189 187/202/196 195/211/205 f 176/194/188 183/201/195 191/210/204 f 189/208/202 196/213/207 204/212/206 f 196/213/207 189/208/202 181/197/191 f 184/198/192 191/210/204 199/214/208 f 188/200/194 195/211/205 203/216/210 f 187/202/196 194/207/201 202/217/211 f 185/203/197 192/215/209 200/218/212 f 190/204/198 197/205/199 205/219/213 f 186/196/190 193/206/200 201/220/214 f 197/205/199 204/212/206 212/221/215 f 194/207/201 201/220/214 209/222/216 f 198/209/203 205/219/213 213/223/217 f 193/206/200 200/218/212 208/225/219 f 191/210/204 198/209/203 206/224/218 f 195/211/205 202/217/211 210/226/220 f 199/214/208 207/234/228 200/218/212 f 204/212/206 211/228/222 219/227/221 f 211/228/222 204/212/206 196/213/207 f 202/217/211 209/222/216 217/229/223 f 203/216/210 210/226/220 218/230/224 f 200/218/212 207/234/228 215/231/225 f 205/219/213 212/221/215 220/232/226 f 199/214/208 206/224/218 214/233/227 f 201/220/214 208/225/219 216/235/229 f 208/225/219 215/231/225 223/236/230 f 209/222/216 216/235/229 224/237/231 f 213/223/217 220/232/226 228/238/232 f 206/224/218 213/223/217 221/239/233 f 210/226/220 217/229/223 225/240/234 f 207/234/228 214/233/227 222/241/235 f 212/221/215 219/227/221 227/242/236 f 226/243/237 219/227/221 211/228/222 f 218/230/224 225/240/234 233/244/238 f 217/229/223 224/237/231 232/245/239 f 215/231/225 222/241/235 230/246/240 f 214/233/227 221/239/233 229/247/241 f 219/227/221 226/243/237 234/21/21 f 216/235/229 223/236/230 231/248/242 f 223/236/230 230/246/240 238/249/243 f 224/237/231 231/248/242 239/250/244 f 228/238/232 235/1/1 3/3/3 f 221/239/233 228/238/232 236/7/7 f 225/240/234 232/245/239 240/251/245 f 220/232/226 227/242/236 235/1/1 f 227/242/236 234/21/21 2/23/23 f 222/241/235 229/247/241 237/13/13 f 1/16/16 234/21/21 226/243/237 f 233/244/238 240/251/245 242/33/33 f 229/247/241 236/7/7 4/9/9 f 230/246/240 237/13/13 5/10/10 f 231/248/242 238/249/243 241/25/25 f 232/245/239 239/250/244 6/4/4 f 7/22/22 14/15/15 22/37/37 f 13/6/6 242/33/33 240/251/245 f 239/250/244 241/25/25 12/27/27 f 11/12/12 241/25/25 238/249/243 f 2/23/23 7/22/22 15/30/30 f 8/2/2 15/30/30 23/32/32 f 245/166/164 255/258/250 253/259/251 f 246/252/246 252/260/252 254/261/253 f 249/256/248 255/258/250 247/170/167 f 146/176/172 252/260/252 244/157/156 f 245/166/164 251/262/254 243/257/249 f 146/176/172 251/262/254 250/263/255 f 249/256/248 256/264/256 257/265/257 f 246/252/246 256/264/256 248/255/247 f 251/262/254 258/266/248 250/263/255 f 253/259/251 259/267/247 251/262/254 f 255/258/250 261/268/246 253/259/251 f 256/264/256 265/269/172 257/265/257 f 250/263/255 260/270/167 252/260/252 f 252/260/252 262/271/164 254/261/253 f 254/261/253 264/272/249 256/264/256 f 255/258/250 265/269/172 263/273/156 f 262/271/164 258/266/248 261/268/246 f 131/156/149 146/176/172 244/157/156 f 154/165/163 247/170/167 245/166/164 f 162/169/166 249/256/248 247/170/167 f 138/175/148 243/257/249 146/176/172 f 139/158/151 244/157/156 246/252/246 f 155/254/183 147/253/155 246/252/246 f 155/254/183 248/255/247 249/256/248 f 145/167/152 245/166/164 243/257/249 f 245/166/164 247/170/167 255/258/250 f 246/252/246 244/157/156 252/260/252 f 249/256/248 257/265/257 255/258/250 f 146/176/172 250/263/255 252/260/252 f 245/166/164 253/259/251 251/262/254 f 146/176/172 243/257/249 251/262/254 f 249/256/248 248/255/247 256/264/256 f 246/252/246 254/261/253 256/264/256 f 251/262/254 259/267/247 258/266/248 f 253/259/251 261/268/246 259/267/247 f 255/258/250 263/273/156 261/268/246 f 256/264/256 264/272/249 265/269/172 f 250/263/255 258/266/248 260/270/167 f 252/260/252 260/270/167 262/271/164 f 254/261/253 262/271/164 264/272/249 f 255/258/250 257/265/257 265/269/172 f 261/268/246 263/273/156 265/269/172 f 265/269/172 264/272/249 262/271/164 f 262/271/164 260/270/167 258/266/248 f 258/266/248 259/267/247 261/268/246 f 261/268/246 265/269/172 262/271/164 ================================================ FILE: src/asset/3d/tank/wheel.obj ================================================ # Blender v2.80 (sub 75) OBJ File: 'wheel.blend' # www.blender.org o Cylinder v 0.000000 0.250000 -0.125000 v 0.000000 0.250000 0.125000 v 0.048773 0.245196 -0.125000 v 0.048773 0.245196 0.125000 v 0.095671 0.230970 -0.125000 v 0.095671 0.230970 0.125000 v 0.138893 0.207867 -0.125000 v 0.138893 0.207867 0.125000 v 0.176777 0.176777 -0.125000 v 0.176777 0.176777 0.125000 v 0.207868 0.138893 -0.125000 v 0.207868 0.138893 0.125000 v 0.230971 0.095671 -0.125000 v 0.230971 0.095671 0.125000 v 0.245197 0.048773 -0.125000 v 0.245197 0.048773 0.125000 v 0.250001 0.000000 -0.125000 v 0.250001 0.000000 0.125000 v 0.245197 -0.048773 -0.125000 v 0.245197 -0.048773 0.125000 v 0.230971 -0.095671 -0.125000 v 0.230971 -0.095671 0.125000 v 0.207868 -0.138893 -0.125000 v 0.207868 -0.138893 0.125000 v 0.176777 -0.176777 -0.125000 v 0.176777 -0.176777 0.125000 v 0.138893 -0.207867 -0.125000 v 0.138893 -0.207867 0.125000 v 0.095671 -0.230970 -0.125000 v 0.095671 -0.230970 0.125000 v 0.048773 -0.245196 -0.125000 v 0.048773 -0.245196 0.125000 v -0.000000 -0.250000 -0.125000 v -0.000000 -0.250000 0.125000 v -0.048773 -0.245196 -0.125000 v -0.048773 -0.245196 0.125000 v -0.095671 -0.230970 -0.125000 v -0.095671 -0.230970 0.125000 v -0.138893 -0.207867 -0.125000 v -0.138893 -0.207867 0.125000 v -0.176778 -0.176777 -0.125000 v -0.176778 -0.176777 0.125000 v -0.207868 -0.138892 -0.125000 v -0.207868 -0.138892 0.125000 v -0.230971 -0.095671 -0.125000 v -0.230971 -0.095671 0.125000 v -0.245197 -0.048772 -0.125000 v -0.245197 -0.048772 0.125000 v -0.250001 0.000000 -0.125000 v -0.250001 0.000000 0.125000 v -0.245197 0.048773 -0.125000 v -0.245197 0.048773 0.125000 v -0.230971 0.095671 -0.125000 v -0.230971 0.095671 0.125000 v -0.207868 0.138893 -0.125000 v -0.207868 0.138893 0.125000 v -0.176777 0.176777 -0.125000 v -0.176777 0.176777 0.125000 v -0.138893 0.207868 -0.125000 v -0.138893 0.207868 0.125000 v -0.095671 0.230970 -0.125000 v -0.095671 0.230970 0.125000 v -0.048772 0.245196 -0.125000 v -0.048772 0.245196 0.125000 v -0.000000 0.260013 -0.130006 v -0.020424 0.274546 -0.128018 v -0.020424 0.274546 0.128018 v -0.000000 0.260013 0.130006 v 0.050726 0.255017 0.130006 v 0.073593 0.265287 0.128018 v 0.073593 0.265287 -0.128018 v 0.050726 0.255017 -0.130006 v 0.086195 0.261464 -0.128018 v 0.099503 0.240220 -0.130006 v 0.099503 0.240220 0.130006 v 0.086195 0.261464 0.128018 v 0.144456 0.216193 0.130006 v 0.169512 0.216930 0.128018 v 0.169512 0.216930 -0.128018 v 0.144456 0.216193 -0.130006 v 0.179692 0.208576 -0.128018 v 0.183857 0.183857 -0.130006 v 0.183857 0.183857 0.130006 v 0.179692 0.208576 0.128018 v 0.216194 0.144455 0.130006 v 0.239625 0.135548 0.128018 v 0.239625 0.135548 -0.128018 v 0.216194 0.144455 -0.130006 v 0.245833 0.123934 -0.128018 v 0.240221 0.099503 -0.130006 v 0.240221 0.099503 0.130006 v 0.245833 0.123934 0.128018 v 0.255018 0.050726 0.130006 v 0.273257 0.033530 0.128018 v 0.273257 0.033530 -0.128018 v 0.255018 0.050726 -0.130006 v 0.274547 0.020424 -0.128018 v 0.260014 0.000000 -0.130006 v 0.260014 0.000000 0.130006 v 0.274547 0.020424 0.128018 v 0.255018 -0.050726 0.130006 v 0.265288 -0.073593 0.128018 v 0.265288 -0.073593 -0.128018 v 0.255018 -0.050726 -0.130006 v 0.261465 -0.086195 -0.128018 v 0.240221 -0.099503 -0.130006 v 0.240221 -0.099503 0.130006 v 0.261465 -0.086195 0.128018 v 0.216194 -0.144455 0.130006 v 0.216931 -0.169512 0.128018 v 0.216931 -0.169512 -0.128018 v 0.216194 -0.144455 -0.130006 v 0.208576 -0.179692 -0.128018 v 0.183857 -0.183857 -0.130006 v 0.183857 -0.183857 0.130006 v 0.208576 -0.179692 0.128018 v 0.144456 -0.216193 0.130006 v 0.135548 -0.239624 0.128018 v 0.135548 -0.239624 -0.128018 v 0.144456 -0.216193 -0.130006 v 0.123934 -0.245832 -0.128018 v 0.099503 -0.240220 -0.130006 v 0.099503 -0.240220 0.130006 v 0.123934 -0.245832 0.128018 v 0.050726 -0.255017 0.130006 v 0.033530 -0.273256 0.128018 v 0.033530 -0.273256 -0.128018 v 0.050726 -0.255017 -0.130006 v 0.020424 -0.274546 -0.128018 v -0.000000 -0.260013 -0.130006 v -0.000000 -0.260013 0.130006 v 0.020424 -0.274546 0.128018 v -0.050726 -0.255017 0.130006 v -0.073593 -0.265287 0.128018 v -0.073593 -0.265287 -0.128018 v -0.050726 -0.255017 -0.130006 v -0.086195 -0.261464 -0.128018 v -0.099503 -0.240220 -0.130006 v -0.099503 -0.240220 0.130006 v -0.086195 -0.261464 0.128018 v -0.144456 -0.216193 0.130006 v -0.169513 -0.216930 0.128018 v -0.169513 -0.216930 -0.128018 v -0.144456 -0.216193 -0.130006 v -0.179692 -0.208576 -0.128018 v -0.183858 -0.183857 -0.130006 v -0.183858 -0.183857 0.130006 v -0.179692 -0.208576 0.128018 v -0.216194 -0.144455 0.130006 v -0.239625 -0.135548 0.128018 v -0.239625 -0.135548 -0.128018 v -0.216194 -0.144455 -0.130006 v -0.245833 -0.123934 -0.128018 v -0.240221 -0.099502 -0.130006 v -0.240221 -0.099502 0.130006 v -0.245833 -0.123934 0.128018 v -0.255018 -0.050726 0.130006 v -0.273257 -0.033529 0.128018 v -0.273257 -0.033529 -0.128018 v -0.255018 -0.050726 -0.130006 v -0.274548 -0.020424 -0.128018 v -0.260014 0.000000 -0.130006 v -0.260014 0.000000 0.130006 v -0.274548 -0.020424 0.128018 v -0.255018 0.050726 0.130006 v -0.265288 0.073593 0.128018 v -0.265288 0.073593 -0.128018 v -0.255018 0.050726 -0.130006 v -0.261465 0.086195 -0.128018 v -0.240221 0.099503 -0.130006 v -0.240221 0.099503 0.130006 v -0.261465 0.086195 0.128018 v -0.216193 0.144456 0.130006 v -0.216931 0.169512 0.128018 v -0.216931 0.169512 -0.128018 v -0.216193 0.144456 -0.130006 v -0.208576 0.179692 -0.128018 v -0.183857 0.183857 -0.130006 v -0.183857 0.183857 0.130006 v -0.208576 0.179692 0.128018 v -0.144456 0.216193 0.130006 v -0.135548 0.239624 0.128018 v -0.135548 0.239624 -0.128018 v -0.144456 0.216193 -0.130006 v -0.123934 0.245832 -0.128018 v -0.099503 0.240221 -0.130006 v -0.099503 0.240221 0.130006 v -0.123934 0.245832 0.128018 v -0.050726 0.255017 0.130006 v -0.033529 0.273256 0.128018 v -0.033529 0.273256 -0.128018 v -0.050726 0.255017 -0.130006 v 0.038788 0.195002 0.101006 v 0.000000 0.198822 0.101006 v 0.076086 0.183688 0.101006 v 0.110460 0.165315 0.101006 v 0.140589 0.140589 0.101006 v 0.165315 0.110460 0.101006 v 0.183689 0.076086 0.101006 v 0.195003 0.038788 0.101006 v 0.198823 0.000000 0.101006 v 0.195003 -0.038788 0.101006 v 0.183689 -0.076086 0.101006 v 0.165315 -0.110460 0.101006 v 0.140589 -0.140589 0.101006 v 0.110460 -0.165315 0.101006 v 0.076086 -0.183688 0.101006 v 0.038788 -0.195002 0.101006 v -0.000000 -0.198822 0.101006 v -0.038789 -0.195002 0.101006 v -0.076086 -0.183688 0.101006 v -0.110460 -0.165315 0.101006 v -0.140589 -0.140589 0.101006 v -0.165315 -0.110460 0.101006 v -0.183689 -0.076086 0.101006 v -0.195003 -0.038788 0.101006 v -0.198823 0.000000 0.101006 v -0.195003 0.038789 0.101006 v -0.183689 0.076086 0.101006 v -0.165315 0.110460 0.101006 v -0.140589 0.140589 0.101006 v -0.110460 0.165315 0.101006 v -0.076086 0.183688 0.101006 v -0.038788 0.195002 0.101006 v 0.024488 0.123107 0.119050 v 0.000000 0.125518 0.119050 v 0.048034 0.115964 0.119050 v 0.069735 0.104365 0.119050 v 0.088755 0.088755 0.119050 v 0.104365 0.069734 0.119050 v 0.115964 0.048034 0.119050 v 0.123107 0.024487 0.119050 v 0.125519 0.000000 0.119050 v 0.123107 -0.024487 0.119050 v 0.115964 -0.048034 0.119050 v 0.104365 -0.069734 0.119050 v 0.088755 -0.088755 0.119050 v 0.069734 -0.104365 0.119050 v 0.048034 -0.115964 0.119050 v 0.024487 -0.123106 0.119050 v -0.000000 -0.125518 0.119050 v -0.024488 -0.123106 0.119050 v -0.048034 -0.115964 0.119050 v -0.069735 -0.104365 0.119050 v -0.088755 -0.088755 0.119050 v -0.104365 -0.069734 0.119050 v -0.115964 -0.048034 0.119050 v -0.123107 -0.024487 0.119050 v -0.125519 0.000000 0.119050 v -0.123107 0.024488 0.119050 v -0.115964 0.048034 0.119050 v -0.104365 0.069734 0.119050 v -0.088755 0.088755 0.119050 v -0.069734 0.104365 0.119050 v -0.048034 0.115964 0.119050 v -0.024487 0.123107 0.119050 v 0.015584 0.078347 0.098577 v 0.000000 0.079882 0.098577 v 0.030570 0.073802 0.098577 v 0.044380 0.066420 0.098577 v 0.056486 0.056485 0.098577 v 0.066420 0.044380 0.098577 v 0.073802 0.030570 0.098577 v 0.078348 0.015584 0.098577 v 0.079883 0.000000 0.098577 v 0.078348 -0.015584 0.098577 v 0.073802 -0.030570 0.098577 v 0.066420 -0.044380 0.098577 v 0.056486 -0.056485 0.098577 v 0.044380 -0.066420 0.098577 v 0.030570 -0.073802 0.098577 v 0.015584 -0.078347 0.098577 v -0.000000 -0.079882 0.098577 v -0.015584 -0.078347 0.098577 v -0.030570 -0.073802 0.098577 v -0.044380 -0.066420 0.098577 v -0.056486 -0.056485 0.098577 v -0.066420 -0.044380 0.098577 v -0.073802 -0.030570 0.098577 v -0.078348 -0.015584 0.098577 v -0.079883 0.000000 0.098577 v -0.078348 0.015584 0.098577 v -0.073802 0.030570 0.098577 v -0.066420 0.044380 0.098577 v -0.056485 0.056485 0.098577 v -0.044380 0.066420 0.098577 v -0.030570 0.073802 0.098577 v -0.015584 0.078347 0.098577 vt 0.097288 0.960814 vt 0.095523 0.932572 vt 0.097288 0.932572 vt 0.095523 0.960814 vt 0.095523 0.932572 vt 0.093758 0.960814 vt 0.091992 0.932572 vt 0.093758 0.932572 vt 0.075635 0.907179 vt 0.073581 0.908865 vt 0.073581 0.908865 vt 0.090227 0.960814 vt 0.088462 0.932572 vt 0.090227 0.932572 vt 0.049629 0.960814 vt 0.049629 0.932572 vt 0.049629 0.932572 vt 0.086697 0.960814 vt 0.084932 0.932572 vt 0.086697 0.932572 vt 0.093758 0.960814 vt 0.083167 0.960814 vt 0.081401 0.932572 vt 0.083167 0.932572 vt 0.077871 0.960814 vt 0.077871 0.932572 vt 0.077871 0.932572 vt 0.079636 0.960814 vt 0.079636 0.932572 vt 0.047864 0.932572 vt 0.047864 0.960814 vt 0.047864 0.960814 vt 0.076106 0.960814 vt 0.074341 0.932572 vt 0.076106 0.932572 vt 0.088354 0.930975 vt 0.085811 0.931746 vt 0.088354 0.930975 vt 0.072576 0.960814 vt 0.070811 0.932572 vt 0.072576 0.932572 vt 0.081401 0.960814 vt 0.081401 0.932572 vt 0.069045 0.960814 vt 0.067280 0.932572 vt 0.069045 0.932572 vt 0.071895 0.925982 vt 0.073581 0.928036 vt 0.073581 0.928036 vt 0.065515 0.960814 vt 0.063750 0.932572 vt 0.065515 0.932572 vt 0.054924 0.932007 vt 0.052280 0.931746 vt 0.052280 0.931746 vt 0.061985 0.960814 vt 0.060220 0.932572 vt 0.061985 0.932572 vt 0.079636 0.960814 vt 0.058455 0.960814 vt 0.056689 0.932572 vt 0.058455 0.932572 vt 0.912589 0.240475 vt 0.912161 0.214329 vt 0.912589 0.214329 vt 0.054924 0.960814 vt 0.053159 0.932572 vt 0.054924 0.932572 vt 0.095691 0.913263 vt 0.096463 0.915806 vt 0.095691 0.913263 vt 0.051394 0.960814 vt 0.051394 0.932572 vt 0.080522 0.905155 vt 0.077979 0.905926 vt 0.077979 0.905926 vt 0.046098 0.932572 vt 0.065515 0.960814 vt 0.198437 0.502542 vt 0.198697 0.499897 vt 0.198697 0.499897 vt 0.044333 0.960814 vt 0.042568 0.932572 vt 0.044333 0.932572 vt 0.898468 0.240475 vt 0.898039 0.214329 vt 0.898468 0.214329 vt 0.096463 0.921095 vt 0.085811 0.905155 vt 0.069871 0.915806 vt 0.060112 0.930975 vt 0.057569 0.931746 vt 0.057569 0.931746 vt 0.064510 0.928036 vt 0.062456 0.929722 vt 0.062456 0.929722 vt 0.066196 0.925982 vt 0.067449 0.923638 vt 0.066196 0.925982 vt 0.068481 0.918450 vt 0.068220 0.921095 vt 0.068220 0.921095 vt 0.067449 0.913263 vt 0.068220 0.915806 vt 0.068220 0.915806 vt 0.064510 0.908865 vt 0.066196 0.910919 vt 0.066196 0.910919 vt 0.060112 0.905926 vt 0.062456 0.907179 vt 0.062456 0.907179 vt 0.054924 0.904894 vt 0.057569 0.905155 vt 0.057569 0.905155 vt 0.052280 0.905155 vt 0.049736 0.905926 vt 0.052280 0.905155 vt 0.045338 0.908865 vt 0.047393 0.907179 vt 0.047393 0.907179 vt 0.042400 0.913263 vt 0.043653 0.910919 vt 0.043653 0.910919 vt 0.041628 0.915806 vt 0.041368 0.918451 vt 0.041628 0.915806 vt 0.041628 0.921095 vt 0.042400 0.923638 vt 0.041628 0.921095 vt 0.043653 0.925982 vt 0.045338 0.928036 vt 0.043653 0.925982 vt 0.049736 0.930975 vt 0.047393 0.929722 vt 0.047393 0.929722 vt 0.080522 0.931746 vt 0.083167 0.932007 vt 0.083167 0.932007 vt 0.046098 0.960814 vt 0.046098 0.932572 vt 0.061985 0.960814 vt 0.091992 0.960814 vt 0.091992 0.932572 vt 0.063750 0.960814 vt 0.063750 0.932572 vt 0.926710 0.240475 vt 0.926282 0.214329 vt 0.926710 0.214329 vt 0.060220 0.960814 vt 0.060220 0.932572 vt 0.076106 0.960814 vt 0.894938 0.240475 vt 0.894509 0.214329 vt 0.894938 0.214329 vt 0.094438 0.910919 vt 0.092752 0.908865 vt 0.092752 0.908865 vt 0.940832 0.240475 vt 0.940403 0.214329 vt 0.940832 0.214329 vt 0.044333 0.960814 vt 0.074341 0.960814 vt 0.074341 0.932572 vt 0.090227 0.960814 vt 0.075635 0.929722 vt 0.077979 0.930975 vt 0.077979 0.930975 vt 0.909059 0.240475 vt 0.908630 0.214329 vt 0.909059 0.214329 vt 0.092752 0.928036 vt 0.090698 0.929722 vt 0.092752 0.928036 vt 0.042568 0.960814 vt 0.042568 0.932572 vt 0.058455 0.960814 vt 0.088462 0.960814 vt 0.088462 0.932572 vt 0.071895 0.910919 vt 0.070642 0.913263 vt 0.070642 0.913263 vt 0.923180 0.240475 vt 0.922751 0.214329 vt 0.923180 0.214329 vt 0.056689 0.960814 vt 0.056689 0.932572 vt 0.072576 0.960814 vt 0.090698 0.907179 vt 0.088354 0.905926 vt 0.088354 0.905926 vt 0.937301 0.240475 vt 0.936873 0.214329 vt 0.937301 0.214329 vt 0.097288 0.960814 vt 0.070811 0.960814 vt 0.070811 0.932572 vt 0.086697 0.960814 vt 0.944362 0.240475 vt 0.943933 0.214329 vt 0.944362 0.214329 vt 0.905529 0.240475 vt 0.905100 0.214329 vt 0.905529 0.214329 vt 0.094438 0.925982 vt 0.095691 0.923638 vt 0.095691 0.923638 vt 0.054924 0.960814 vt 0.084932 0.960814 vt 0.084932 0.932572 vt 0.069610 0.918451 vt 0.069610 0.918451 vt 0.919650 0.240475 vt 0.919221 0.214329 vt 0.919650 0.214329 vt 0.053159 0.960814 vt 0.053159 0.932572 vt 0.069045 0.960814 vt 0.083167 0.904894 vt 0.083167 0.904894 vt 0.933771 0.240475 vt 0.933342 0.214329 vt 0.933771 0.214329 vt 0.067280 0.960814 vt 0.067280 0.932572 vt 0.083167 0.960814 vt 0.901998 0.240475 vt 0.901570 0.214329 vt 0.901998 0.214329 vt 0.096723 0.918450 vt 0.096723 0.918450 vt 0.051394 0.960814 vt 0.930241 0.240475 vt 0.929812 0.214329 vt 0.930241 0.214329 vt 0.069871 0.921095 vt 0.070642 0.923638 vt 0.070642 0.923638 vt 0.916120 0.240475 vt 0.915691 0.214329 vt 0.916120 0.214329 vt 0.890979 0.921218 vt 0.890311 0.947568 vt 0.890311 0.921015 vt 0.944362 0.947365 vt 0.945030 0.920170 vt 0.945030 0.948413 vt 0.944362 0.921218 vt 0.943318 0.920170 vt 0.943318 0.948413 vt 0.943933 0.947365 vt 0.940832 0.947365 vt 0.941500 0.920170 vt 0.941500 0.948413 vt 0.940403 0.921218 vt 0.941447 0.920170 vt 0.940832 0.921218 vt 0.940403 0.947365 vt 0.941447 0.948413 vt 0.939788 0.948413 vt 0.939735 0.920170 vt 0.939735 0.948413 vt 0.937301 0.921218 vt 0.937970 0.948413 vt 0.937301 0.947365 vt 0.936873 0.921218 vt 0.937917 0.920170 vt 0.936257 0.948413 vt 0.936873 0.947365 vt 0.936204 0.920170 vt 0.936204 0.948413 vt 0.933771 0.921218 vt 0.934439 0.948413 vt 0.933771 0.947365 vt 0.932727 0.920170 vt 0.934386 0.920170 vt 0.932727 0.948413 vt 0.933342 0.947365 vt 0.930241 0.921218 vt 0.930909 0.948413 vt 0.930241 0.947365 vt 0.929812 0.921218 vt 0.930856 0.920170 vt 0.929197 0.948413 vt 0.929812 0.947365 vt 0.929144 0.920170 vt 0.929144 0.948413 vt 0.926710 0.921218 vt 0.927379 0.948413 vt 0.926710 0.947365 vt 0.926282 0.921218 vt 0.927326 0.920170 vt 0.926282 0.947365 vt 0.927326 0.948413 vt 0.925666 0.948413 vt 0.925614 0.920170 vt 0.925614 0.948413 vt 0.923180 0.921218 vt 0.923848 0.948413 vt 0.923180 0.947365 vt 0.922136 0.920170 vt 0.923796 0.920170 vt 0.922136 0.948413 vt 0.922751 0.947365 vt 0.919650 0.947365 vt 0.920318 0.920170 vt 0.920318 0.948413 vt 0.919221 0.921218 vt 0.919650 0.921218 vt 0.919221 0.947365 vt 0.918606 0.948413 vt 0.918553 0.920170 vt 0.916120 0.921218 vt 0.916788 0.948413 vt 0.916120 0.947365 vt 0.915691 0.921218 vt 0.916735 0.920170 vt 0.915691 0.947365 vt 0.916735 0.948413 vt 0.915075 0.948413 vt 0.915023 0.920170 vt 0.915023 0.948413 vt 0.912589 0.947365 vt 0.913257 0.920170 vt 0.913257 0.948413 vt 0.912161 0.921218 vt 0.913205 0.920170 vt 0.912589 0.921218 vt 0.911545 0.948413 vt 0.912161 0.947365 vt 0.911492 0.920170 vt 0.909059 0.921218 vt 0.909727 0.948413 vt 0.909059 0.947365 vt 0.908630 0.921218 vt 0.909674 0.920170 vt 0.908015 0.948413 vt 0.908630 0.947365 vt 0.907962 0.920170 vt 0.905529 0.921218 vt 0.906197 0.948413 vt 0.905529 0.947365 vt 0.904485 0.920170 vt 0.906144 0.920170 vt 0.904485 0.948413 vt 0.905100 0.947365 vt 0.904432 0.920170 vt 0.904432 0.948413 vt 0.901998 0.921218 vt 0.902667 0.948413 vt 0.901998 0.947365 vt 0.901570 0.921218 vt 0.902614 0.920170 vt 0.900954 0.948413 vt 0.901570 0.947365 vt 0.900901 0.920170 vt 0.900901 0.948413 vt 0.898468 0.921218 vt 0.899136 0.948413 vt 0.898468 0.947365 vt 0.898039 0.921218 vt 0.899083 0.920170 vt 0.897424 0.948413 vt 0.898039 0.947365 vt 0.897371 0.920170 vt 0.894938 0.921218 vt 0.895606 0.948413 vt 0.894938 0.947365 vt 0.894509 0.921218 vt 0.895553 0.920170 vt 0.894509 0.947365 vt 0.893894 0.948413 vt 0.893841 0.920170 vt 0.893841 0.948413 vt 0.891407 0.921218 vt 0.892076 0.948413 vt 0.891407 0.947365 vt 0.892023 0.920170 vt 0.890979 0.947365 vt 0.892023 0.948413 vt 0.890363 0.948413 vt 0.891407 0.240475 vt 0.890979 0.214329 vt 0.891407 0.214329 vt 0.073242 0.102763 vt 0.075296 0.101077 vt 0.073242 0.102763 vt 0.199262 0.542261 vt 0.197497 0.542261 vt 0.197497 0.542261 vt 0.172616 0.505085 vt 0.171845 0.502542 vt 0.172616 0.505085 vt 0.192672 0.511169 vt 0.194727 0.509483 vt 0.194727 0.509483 vt 0.206323 0.542261 vt 0.204557 0.542261 vt 0.204557 0.542261 vt 0.173869 0.492366 vt 0.172616 0.494710 vt 0.172616 0.494710 vt 0.174550 0.542261 vt 0.172785 0.542261 vt 0.172785 0.542261 vt 0.213383 0.542261 vt 0.211618 0.542261 vt 0.211618 0.542261 vt 0.182496 0.486602 vt 0.179953 0.487373 vt 0.179953 0.487373 vt 0.181610 0.542261 vt 0.179845 0.542261 vt 0.179845 0.542261 vt 0.220444 0.542261 vt 0.218679 0.542261 vt 0.218679 0.542261 vt 0.192672 0.488626 vt 0.190329 0.487373 vt 0.190329 0.487373 vt 0.186906 0.542261 vt 0.188671 0.542261 vt 0.186906 0.542261 vt 0.227504 0.542261 vt 0.225739 0.542261 vt 0.225739 0.542261 vt 0.185141 0.513454 vt 0.182496 0.513193 vt 0.185141 0.513454 vt 0.198437 0.497253 vt 0.197665 0.494710 vt 0.197665 0.494710 vt 0.195732 0.542261 vt 0.193966 0.542261 vt 0.193966 0.542261 vt 0.175555 0.509483 vt 0.173869 0.507429 vt 0.175555 0.509483 vt 0.196412 0.507429 vt 0.197665 0.505085 vt 0.197665 0.505085 vt 0.202792 0.542261 vt 0.201027 0.542261 vt 0.201027 0.542261 vt 0.171845 0.497253 vt 0.171584 0.499898 vt 0.171584 0.499898 vt 0.190329 0.512422 vt 0.187785 0.513193 vt 0.190329 0.512422 vt 0.209853 0.542261 vt 0.208088 0.542261 vt 0.208088 0.542261 vt 0.177609 0.488626 vt 0.175555 0.490312 vt 0.175555 0.490312 vt 0.176315 0.542261 vt 0.178080 0.542261 vt 0.176315 0.542261 vt 0.216913 0.542261 vt 0.215148 0.542261 vt 0.215148 0.542261 vt 0.187785 0.486602 vt 0.185141 0.486341 vt 0.185141 0.486341 vt 0.183376 0.542261 vt 0.185141 0.542261 vt 0.183376 0.542261 vt 0.222209 0.542261 vt 0.223974 0.542261 vt 0.222209 0.542261 vt 0.196412 0.492366 vt 0.194727 0.490312 vt 0.194727 0.490312 vt 0.190436 0.542261 vt 0.192201 0.542261 vt 0.190436 0.542261 vt 0.179953 0.512422 vt 0.177609 0.511169 vt 0.179953 0.512422 vt 0.056689 0.960814 vt 0.058455 0.960814 vt 0.056689 0.960814 vt 0.114600 0.154712 vt 0.112835 0.154712 vt 0.112835 0.154712 vt 0.088123 0.154712 vt 0.089888 0.154712 vt 0.088123 0.154712 vt 0.096124 0.114993 vt 0.096384 0.112348 vt 0.096384 0.112348 vt 0.071556 0.104817 vt 0.070303 0.107161 vt 0.070303 0.107161 vt 0.111070 0.154712 vt 0.109305 0.154712 vt 0.109305 0.154712 vt 0.084593 0.154712 vt 0.086358 0.154712 vt 0.084593 0.154712 vt 0.095352 0.107161 vt 0.096124 0.109704 vt 0.095352 0.107161 vt 0.069271 0.112348 vt 0.069532 0.109704 vt 0.069271 0.112348 vt 0.107540 0.154712 vt 0.105775 0.154712 vt 0.105775 0.154712 vt 0.082828 0.154712 vt 0.081063 0.154712 vt 0.081063 0.154712 vt 0.094099 0.104817 vt 0.092414 0.102763 vt 0.092414 0.102763 vt 0.070303 0.117536 vt 0.069532 0.114993 vt 0.070303 0.117536 vt 0.104010 0.154712 vt 0.102244 0.154712 vt 0.102244 0.154712 vt 0.079297 0.154712 vt 0.077532 0.154712 vt 0.077532 0.154712 vt 0.090359 0.101077 vt 0.088016 0.099824 vt 0.088016 0.099824 vt 0.125191 0.154712 vt 0.123426 0.154712 vt 0.123426 0.154712 vt 0.073242 0.121934 vt 0.071556 0.119880 vt 0.073242 0.121934 vt 0.100479 0.154712 vt 0.098714 0.154712 vt 0.098714 0.154712 vt 0.088016 0.124873 vt 0.085472 0.125644 vt 0.088016 0.124873 vt 0.074002 0.154712 vt 0.075767 0.154712 vt 0.074002 0.154712 vt 0.085472 0.099053 vt 0.082828 0.098792 vt 0.082828 0.098792 vt 0.119896 0.154712 vt 0.121661 0.154712 vt 0.119896 0.154712 vt 0.075296 0.123620 vt 0.077640 0.124873 vt 0.077640 0.124873 vt 0.096949 0.154712 vt 0.095184 0.154712 vt 0.095184 0.154712 vt 0.090359 0.123620 vt 0.092414 0.121934 vt 0.092414 0.121934 vt 0.070472 0.154712 vt 0.072237 0.154712 vt 0.070472 0.154712 vt 0.077640 0.099824 vt 0.080183 0.099053 vt 0.077640 0.099824 vt 0.118131 0.154712 vt 0.116366 0.154712 vt 0.116366 0.154712 vt 0.082828 0.125905 vt 0.080183 0.125644 vt 0.082828 0.125905 vt 0.093419 0.154712 vt 0.091654 0.154712 vt 0.091654 0.154712 vt 0.094099 0.119880 vt 0.095352 0.117536 vt 0.095352 0.117536 vt 0.895008 0.080332 vt 0.902344 0.062619 vt 0.920057 0.069956 vt 0.068220 0.915806 vt 0.067449 0.913263 vt 0.067449 0.913263 vt 0.041368 0.918451 vt 0.041628 0.915806 vt 0.041368 0.918451 vt 0.079636 0.960814 vt 0.077871 0.960814 vt 0.077871 0.960814 vt 0.053159 0.960814 vt 0.054924 0.960814 vt 0.053159 0.960814 vt 0.066196 0.910919 vt 0.064510 0.908865 vt 0.064510 0.908865 vt 0.042400 0.923638 vt 0.041628 0.921095 vt 0.042400 0.923638 vt 0.076106 0.960814 vt 0.074341 0.960814 vt 0.074341 0.960814 vt 0.051394 0.960814 vt 0.049629 0.960814 vt 0.049629 0.960814 vt 0.062456 0.907179 vt 0.060112 0.905926 vt 0.060112 0.905926 vt 0.097288 0.960814 vt 0.095523 0.960814 vt 0.095523 0.960814 vt 0.043653 0.925982 vt 0.045338 0.928036 vt 0.045338 0.928036 vt 0.072576 0.960814 vt 0.070811 0.960814 vt 0.070811 0.960814 vt 0.057569 0.931746 vt 0.060112 0.930975 vt 0.060112 0.930975 vt 0.047864 0.960814 vt 0.046098 0.960814 vt 0.046098 0.960814 vt 0.057569 0.905155 vt 0.054924 0.904894 vt 0.054924 0.904894 vt 0.093758 0.960814 vt 0.091992 0.960814 vt 0.091992 0.960814 vt 0.047393 0.929722 vt 0.049736 0.930975 vt 0.049736 0.930975 vt 0.069045 0.960814 vt 0.067280 0.960814 vt 0.067280 0.960814 vt 0.062456 0.929722 vt 0.064510 0.928036 vt 0.064510 0.928036 vt 0.044333 0.960814 vt 0.042568 0.960814 vt 0.042568 0.960814 vt 0.049736 0.905926 vt 0.052280 0.905155 vt 0.049736 0.905926 vt 0.090227 0.960814 vt 0.088462 0.960814 vt 0.088462 0.960814 vt 0.052280 0.931746 vt 0.054924 0.932007 vt 0.054924 0.932007 vt 0.065515 0.960814 vt 0.063750 0.960814 vt 0.063750 0.960814 vt 0.066196 0.925982 vt 0.067449 0.923638 vt 0.067449 0.923638 vt 0.045338 0.908865 vt 0.047393 0.907179 vt 0.045338 0.908865 vt 0.086697 0.960814 vt 0.084932 0.960814 vt 0.084932 0.960814 vt 0.060220 0.960814 vt 0.061985 0.960814 vt 0.060220 0.960814 vt 0.068220 0.921095 vt 0.068481 0.918450 vt 0.068481 0.918450 vt 0.043653 0.910919 vt 0.042400 0.913263 vt 0.042400 0.913263 vt 0.081401 0.960814 vt 0.083167 0.960814 vt 0.081401 0.960814 vt 0.095523 0.960814 vt 0.075635 0.907179 vt 0.049629 0.960814 vt 0.093758 0.932572 vt 0.077871 0.960814 vt 0.047864 0.932572 vt 0.085811 0.931746 vt 0.081401 0.960814 vt 0.071895 0.925982 vt 0.054924 0.932007 vt 0.079636 0.932572 vt 0.912161 0.240475 vt 0.096463 0.915806 vt 0.080522 0.905155 vt 0.065515 0.932572 vt 0.198437 0.502542 vt 0.898039 0.240475 vt 0.090698 0.929722 vt 0.060112 0.930975 vt 0.064510 0.928036 vt 0.067449 0.923638 vt 0.068481 0.918450 vt 0.067449 0.913263 vt 0.064510 0.908865 vt 0.060112 0.905926 vt 0.054924 0.904894 vt 0.049736 0.905926 vt 0.045338 0.908865 vt 0.042400 0.913263 vt 0.041368 0.918451 vt 0.042400 0.923638 vt 0.045338 0.928036 vt 0.049736 0.930975 vt 0.080522 0.931746 vt 0.046098 0.960814 vt 0.061985 0.932572 vt 0.091992 0.960814 vt 0.063750 0.960814 vt 0.926282 0.240475 vt 0.060220 0.960814 vt 0.076106 0.932572 vt 0.894509 0.240475 vt 0.094438 0.910919 vt 0.940403 0.240475 vt 0.044333 0.932572 vt 0.074341 0.960814 vt 0.090227 0.932572 vt 0.075635 0.929722 vt 0.908630 0.240475 vt 0.042568 0.960814 vt 0.058455 0.932572 vt 0.088462 0.960814 vt 0.071895 0.910919 vt 0.922751 0.240475 vt 0.056689 0.960814 vt 0.072576 0.932572 vt 0.090698 0.907179 vt 0.936873 0.240475 vt 0.097288 0.932572 vt 0.070811 0.960814 vt 0.086697 0.932572 vt 0.943933 0.240475 vt 0.905100 0.240475 vt 0.094438 0.925982 vt 0.054924 0.932572 vt 0.084932 0.960814 vt 0.069871 0.915806 vt 0.919221 0.240475 vt 0.053159 0.960814 vt 0.069045 0.932572 vt 0.085811 0.905155 vt 0.933342 0.240475 vt 0.067280 0.960814 vt 0.083167 0.932572 vt 0.901570 0.240475 vt 0.096463 0.921095 vt 0.051394 0.932572 vt 0.929812 0.240475 vt 0.069871 0.921095 vt 0.915691 0.240475 vt 0.943933 0.921218 vt 0.939788 0.920170 vt 0.937970 0.920170 vt 0.936257 0.920170 vt 0.937917 0.948413 vt 0.933342 0.921218 vt 0.929197 0.920170 vt 0.927379 0.920170 vt 0.925666 0.920170 vt 0.922751 0.921218 vt 0.916788 0.920170 vt 0.915075 0.920170 vt 0.913205 0.948413 vt 0.906197 0.920170 vt 0.905100 0.921218 vt 0.906144 0.948413 vt 0.902667 0.920170 vt 0.900954 0.920170 vt 0.902614 0.948413 vt 0.893894 0.920170 vt 0.892076 0.920170 vt 0.890363 0.920170 vt 0.890979 0.240475 vt 0.075296 0.101077 vt 0.199262 0.542261 vt 0.171845 0.502542 vt 0.192672 0.511169 vt 0.206323 0.542261 vt 0.173869 0.492366 vt 0.174550 0.542261 vt 0.213383 0.542261 vt 0.182496 0.486602 vt 0.181610 0.542261 vt 0.220444 0.542261 vt 0.192672 0.488626 vt 0.188671 0.542261 vt 0.227504 0.542261 vt 0.182496 0.513193 vt 0.198437 0.497253 vt 0.195732 0.542261 vt 0.173869 0.507429 vt 0.196412 0.507429 vt 0.202792 0.542261 vt 0.171845 0.497253 vt 0.187785 0.513193 vt 0.209853 0.542261 vt 0.177609 0.488626 vt 0.178080 0.542261 vt 0.216913 0.542261 vt 0.187785 0.486602 vt 0.185141 0.542261 vt 0.223974 0.542261 vt 0.196412 0.492366 vt 0.192201 0.542261 vt 0.177609 0.511169 vt 0.058455 0.960814 vt 0.114600 0.154712 vt 0.089888 0.154712 vt 0.096124 0.114993 vt 0.071556 0.104817 vt 0.111070 0.154712 vt 0.086358 0.154712 vt 0.096124 0.109704 vt 0.069532 0.109704 vt 0.107540 0.154712 vt 0.082828 0.154712 vt 0.094099 0.104817 vt 0.069532 0.114993 vt 0.104010 0.154712 vt 0.079297 0.154712 vt 0.090359 0.101077 vt 0.125191 0.154712 vt 0.071556 0.119880 vt 0.100479 0.154712 vt 0.085472 0.125644 vt 0.075767 0.154712 vt 0.085472 0.099053 vt 0.121661 0.154712 vt 0.075296 0.123620 vt 0.096949 0.154712 vt 0.090359 0.123620 vt 0.072237 0.154712 vt 0.080183 0.099053 vt 0.118131 0.154712 vt 0.080183 0.125644 vt 0.093419 0.154712 vt 0.094099 0.119880 vt 0.912720 0.087668 vt 0.910177 0.088440 vt 0.907532 0.088700 vt 0.904887 0.088440 vt 0.902344 0.087668 vt 0.900001 0.086415 vt 0.897946 0.084730 vt 0.896260 0.082675 vt 0.894236 0.077789 vt 0.893976 0.075144 vt 0.894236 0.072499 vt 0.895008 0.069956 vt 0.896260 0.067612 vt 0.897946 0.065558 vt 0.900001 0.063872 vt 0.904887 0.061848 vt 0.907532 0.061587 vt 0.910177 0.061848 vt 0.912720 0.062619 vt 0.915064 0.063872 vt 0.917118 0.065558 vt 0.918804 0.067612 vt 0.920828 0.072499 vt 0.921089 0.075144 vt 0.920828 0.077789 vt 0.920057 0.080332 vt 0.918804 0.082675 vt 0.917118 0.084730 vt 0.915064 0.086415 vt 0.068220 0.915806 vt 0.041628 0.915806 vt 0.079636 0.960814 vt 0.054924 0.960814 vt 0.066196 0.910919 vt 0.041628 0.921095 vt 0.076106 0.960814 vt 0.051394 0.960814 vt 0.062456 0.907179 vt 0.097288 0.960814 vt 0.043653 0.925982 vt 0.072576 0.960814 vt 0.057569 0.931746 vt 0.047864 0.960814 vt 0.057569 0.905155 vt 0.093758 0.960814 vt 0.047393 0.929722 vt 0.069045 0.960814 vt 0.062456 0.929722 vt 0.044333 0.960814 vt 0.052280 0.905155 vt 0.090227 0.960814 vt 0.052280 0.931746 vt 0.065515 0.960814 vt 0.066196 0.925982 vt 0.047393 0.907179 vt 0.086697 0.960814 vt 0.061985 0.960814 vt 0.068220 0.921095 vt 0.043653 0.910919 vt 0.083167 0.960814 vn 0.0980 0.9952 0.0000 vn -0.9808 0.1951 0.0000 vn 0.4714 0.8819 0.0000 vn 0.2848 0.3470 -0.8936 vn 0.7730 0.6344 0.0000 vn -0.5556 -0.8315 0.0000 vn 0.9569 0.2903 0.0000 vn 0.9239 -0.3827 0.0000 vn 0.9952 -0.0980 0.0000 vn 0.5556 0.8315 0.0000 vn 0.8819 -0.4714 0.0000 vn 0.7071 0.7071 0.0000 vn 0.6344 -0.7730 0.0000 vn -0.1303 -0.4296 -0.8936 vn 0.2903 -0.9569 0.0000 vn 0.1951 0.9808 0.0000 vn -0.0980 -0.9952 0.0000 vn 0.3470 -0.2848 -0.8936 vn -0.4714 -0.8819 0.0000 vn 0.0440 -0.4468 0.8936 vn -0.7730 -0.6344 0.0000 vn -0.3827 -0.9239 0.0000 vn -0.9569 -0.2903 0.0000 vn -0.6344 -0.7730 0.0000 vn -0.9952 0.0980 0.0000 vn -0.4296 0.1303 -0.8936 vn -0.8819 0.4714 0.0000 vn 0.1303 0.4296 -0.8936 vn -0.6344 0.7730 0.0000 vn -0.9239 0.3827 0.0000 vn -0.4241 -0.0418 0.9046 vn -0.2903 0.9569 0.0000 vn -0.7730 0.6344 0.0000 vn 0.0000 0.0000 -1.0000 vn -0.1303 -0.4296 0.8936 vn -0.2848 -0.3470 0.8936 vn -0.3959 -0.2116 0.8936 vn -0.4468 -0.0440 0.8936 vn -0.4296 0.1303 0.8936 vn -0.3470 0.2848 0.8936 vn -0.2116 0.3959 0.8936 vn -0.0440 0.4468 0.8936 vn 0.1303 0.4296 0.8936 vn 0.2848 0.3470 0.8936 vn 0.3959 0.2116 0.8936 vn 0.4468 0.0440 0.8936 vn 0.4296 -0.1303 0.8936 vn 0.3470 -0.2848 0.8936 vn 0.2116 -0.3959 0.8936 vn 0.0440 -0.4468 -0.8936 vn -0.8315 -0.5556 0.0000 vn -0.7071 0.7071 0.0000 vn -0.8315 0.5556 0.0000 vn 0.8315 -0.5556 0.0000 vn 0.7730 -0.6344 0.0000 vn 0.5556 -0.8315 0.0000 vn -0.7071 -0.7071 0.0000 vn -0.4714 0.8819 0.0000 vn -0.3470 0.2848 -0.8936 vn 0.6344 0.7730 0.0000 vn 0.9239 0.3827 0.0000 vn 0.8315 0.5556 0.0000 vn 0.7071 -0.7071 0.0000 vn 0.2116 -0.3959 -0.8936 vn -0.8819 -0.4714 0.0000 vn -0.2848 -0.3470 -0.8936 vn -0.9808 -0.1951 0.0000 vn -0.3827 0.9239 0.0000 vn -0.5556 0.8315 0.0000 vn 0.3959 0.2116 -0.8936 vn 0.4714 -0.8819 0.0000 vn 0.1951 -0.9808 0.0000 vn -0.9239 -0.3827 0.0000 vn -0.2116 0.3959 -0.8936 vn 0.8819 0.4714 0.0000 vn 1.0000 0.0000 0.0000 vn 0.9808 0.1951 0.0000 vn 0.3827 -0.9239 0.0000 vn 0.2903 0.9569 0.0000 vn -0.9952 -0.0980 0.0000 vn -0.3959 -0.2116 -0.8936 vn 0.0000 1.0000 0.0000 vn -0.1951 0.9808 0.0000 vn 0.4468 0.0440 -0.8936 vn 0.0980 -0.9952 0.0000 vn -0.1951 -0.9808 0.0000 vn -1.0000 0.0000 0.0000 vn -0.0440 0.4468 -0.8936 vn 0.9952 0.0980 0.0000 vn 0.9808 -0.1951 0.0000 vn -0.0000 -1.0000 0.0000 vn -0.9569 0.2903 0.0000 vn -0.4468 -0.0440 -0.8936 vn 0.3827 0.9239 0.0000 vn 0.9569 -0.2903 0.0000 vn 0.4296 -0.1303 -0.8936 vn -0.2903 -0.9569 -0.0000 vn 0.5798 0.8148 0.0000 vn -0.4097 0.9122 0.0000 vn 0.0348 0.1148 -0.9928 vn 0.0348 0.1148 0.9928 vn 0.8474 0.5309 0.0000 vn -0.0294 0.9996 0.0000 vn 0.0761 0.0927 -0.9928 vn 0.0761 0.0927 0.9928 vn 0.9861 0.1662 0.0000 vn 0.3553 0.9347 0.0000 vn 0.1058 0.0565 -0.9928 vn 0.1058 0.0565 0.9928 vn 0.9746 -0.2239 0.0000 vn 0.6860 0.7276 0.0000 vn 0.1193 0.0118 -0.9928 vn 0.1193 0.0118 0.9928 vn 0.8148 -0.5798 0.0000 vn 0.9122 0.4097 0.0000 vn 0.1147 -0.0348 -0.9928 vn 0.1147 -0.0348 0.9928 vn 0.5309 -0.8475 0.0000 vn 0.9996 0.0294 0.0000 vn 0.0927 -0.0761 -0.9928 vn 0.0927 -0.0761 0.9928 vn 0.1662 -0.9861 0.0000 vn 0.9347 -0.3553 0.0000 vn 0.0565 -0.1058 -0.9928 vn 0.0565 -0.1058 0.9928 vn -0.2239 -0.9746 0.0000 vn 0.7276 -0.6860 0.0000 vn 0.0118 -0.1193 -0.9928 vn 0.0118 -0.1193 0.9928 vn -0.5798 -0.8148 0.0000 vn 0.4097 -0.9122 0.0000 vn -0.0348 -0.1147 -0.9928 vn -0.0348 -0.1148 0.9928 vn -0.8474 -0.5309 0.0000 vn 0.0294 -0.9996 0.0000 vn -0.0761 -0.0927 -0.9928 vn -0.0761 -0.0927 0.9928 vn -0.9861 -0.1662 0.0000 vn -0.3553 -0.9347 0.0000 vn -0.1058 -0.0565 -0.9928 vn -0.1058 -0.0565 0.9928 vn -0.9746 0.2239 0.0000 vn -0.6860 -0.7276 0.0000 vn -0.1193 -0.0118 -0.9928 vn -0.1193 -0.0118 0.9928 vn -0.8148 0.5798 0.0000 vn -0.9122 -0.4097 0.0000 vn -0.1148 0.0348 -0.9928 vn -0.1147 0.0348 0.9928 vn -0.5309 0.8475 0.0000 vn -0.9996 -0.0294 0.0000 vn -0.0927 0.0761 -0.9928 vn -0.0927 0.0761 0.9928 vn -0.1662 0.9861 0.0000 vn -0.9347 0.3553 -0.0000 vn -0.0565 0.1058 -0.9928 vn -0.0565 0.1058 0.9928 vn 0.2239 0.9746 0.0000 vn -0.7276 0.6860 0.0000 vn -0.0118 0.1193 -0.9928 vn -0.0118 0.1193 0.9928 vn -0.0980 0.9952 0.0000 vn -0.1523 -0.1856 0.9707 vn 0.0418 0.4241 0.9046 vn 0.4078 -0.1237 0.9046 vn -0.2704 -0.3294 0.9046 vn -0.2704 0.3294 0.9046 vn 0.3759 0.2009 0.9046 vn 0.1237 -0.4078 0.9046 vn -0.4241 0.0418 0.9046 vn 0.1237 0.4078 0.9046 vn 0.3759 -0.2009 0.9046 vn -0.3294 -0.2704 0.9046 vn -0.2009 0.3759 0.9046 vn 0.4078 0.1237 0.9046 vn -0.0418 -0.4241 0.9046 vn 0.0418 -0.4241 0.9046 vn -0.4078 0.1237 0.9046 vn 0.2009 0.3759 0.9046 vn 0.3294 -0.2704 0.9046 vn -0.3759 -0.2009 0.9046 vn -0.1237 0.4078 0.9046 vn 0.4241 0.0418 0.9046 vn -0.1237 -0.4078 0.9046 vn -0.3759 0.2009 0.9046 vn 0.2704 0.3294 0.9046 vn 0.2704 -0.3294 0.9046 vn -0.4078 -0.1237 0.9046 vn -0.0418 0.4241 0.9046 vn 0.4241 -0.0418 0.9046 vn -0.2009 -0.3759 0.9046 vn -0.3294 0.2704 0.9046 vn 0.3294 0.2704 0.9046 vn 0.2009 -0.3759 0.9046 vn 0.3933 0.1193 0.9117 vn 0.2298 0.0697 0.9707 vn -0.1856 -0.1523 0.9707 vn 0.2390 0.0235 0.9707 vn -0.2118 -0.1132 0.9707 vn 0.2390 -0.0235 0.9707 vn -0.2298 -0.0697 0.9707 vn 0.2298 -0.0697 0.9707 vn -0.2390 -0.0235 0.9707 vn 0.2118 -0.1132 0.9707 vn -0.2390 0.0235 0.9707 vn 0.1856 -0.1523 0.9707 vn -0.2298 0.0697 0.9707 vn 0.1523 -0.1856 0.9707 vn -0.2118 0.1132 0.9707 vn 0.1132 -0.2118 0.9707 vn 0.0235 0.2390 0.9707 vn -0.1856 0.1523 0.9707 vn 0.0697 -0.2298 0.9707 vn 0.0697 0.2298 0.9707 vn -0.1523 0.1856 0.9707 vn 0.0235 -0.2390 0.9707 vn 0.1132 0.2118 0.9707 vn -0.1132 0.2118 0.9707 vn -0.0235 -0.2390 0.9707 vn 0.1523 0.1856 0.9707 vn -0.0697 0.2298 0.9707 vn -0.0697 -0.2298 0.9707 vn 0.1856 0.1523 0.9707 vn -0.0235 0.2390 0.9707 vn -0.1132 -0.2118 0.9707 vn 0.2118 0.1132 0.9707 vn 0.0000 0.0000 1.0000 vn -0.3933 0.1193 0.9117 vn 0.4090 0.0403 0.9117 vn -0.3624 0.1937 0.9117 vn 0.4090 -0.0403 0.9117 vn -0.3177 0.2607 0.9117 vn 0.3933 -0.1193 0.9117 vn -0.2607 0.3177 0.9117 vn 0.3624 -0.1937 0.9117 vn -0.1937 0.3624 0.9117 vn -0.0403 -0.4090 0.9117 vn 0.3177 -0.2607 0.9117 vn -0.1193 0.3933 0.9117 vn -0.1193 -0.3933 0.9117 vn 0.2607 -0.3177 0.9117 vn -0.0403 0.4090 0.9117 vn -0.1937 -0.3624 0.9117 vn 0.1937 -0.3624 0.9117 vn 0.0403 0.4090 0.9117 vn -0.2607 -0.3177 0.9117 vn 0.1193 -0.3933 0.9117 vn 0.1193 0.3933 0.9117 vn -0.3177 -0.2607 0.9117 vn 0.0403 -0.4090 0.9117 vn 0.1937 0.3624 0.9117 vn -0.3624 -0.1937 0.9117 vn 0.2607 0.3177 0.9117 vn -0.3933 -0.1193 0.9117 vn 0.3177 0.2607 0.9117 vn -0.4090 -0.0403 0.9117 vn 0.3624 0.1937 0.9117 vn -0.4090 0.0403 0.9117 vn 0.8475 0.5309 0.0000 vn 0.1148 -0.0348 -0.9928 vn 0.1148 -0.0348 0.9928 vn -0.0348 -0.1148 -0.9928 vn -0.8475 -0.5309 0.0000 vn -0.1148 0.0348 0.9928 s off f 2/1/1 3/2/1 1/3/1 f 4/4/2 72/5/2 3/2/2 f 6/6/3 7/7/3 5/8/3 f 39/9/4 146/10/4 41/11/4 f 10/12/5 11/13/5 9/14/5 f 56/15/6 176/16/6 55/17/6 f 14/18/7 15/19/7 13/20/7 f 5/8/8 75/21/8 6/6/8 f 18/22/9 19/23/9 17/24/9 f 24/25/10 112/26/10 23/27/10 f 22/28/11 23/27/11 21/29/11 f 57/30/12 179/31/12 58/32/12 f 26/33/13 27/34/13 25/35/13 f 5/36/14 72/37/14 74/38/14 f 30/39/15 31/40/15 29/41/15 f 20/42/16 104/43/16 19/23/16 f 34/44/17 35/45/17 33/46/17 f 55/47/18 178/48/18 57/49/18 f 38/50/19 39/51/19 37/52/19 f 2/53/20 189/54/20 64/55/20 f 42/56/21 43/57/21 41/58/21 f 21/29/22 107/59/22 22/28/22 f 46/60/23 47/61/23 45/62/23 f 142/63/24 145/64/24 143/65/24 f 50/66/25 51/67/25 49/68/25 f 21/69/26 104/70/26 106/71/26 f 54/72/27 55/17/27 53/73/27 f 35/74/28 138/75/28 37/76/28 f 58/32/29 59/77/29 57/30/29 f 37/52/30 139/78/30 38/50/30 f 16/79/31 201/80/31 18/81/31 f 62/82/32 63/83/32 61/84/32 f 174/85/33 177/86/33 175/87/33 f 15/88/34 31/89/34 47/90/34 f 6/91/35 69/92/35 4/93/35 f 10/94/36 77/95/36 8/96/36 f 12/97/37 91/98/37 85/99/37 f 18/100/38 93/101/38 16/102/38 f 22/103/39 101/104/39 20/105/39 f 26/106/40 109/107/40 24/108/40 f 30/109/41 117/110/41 28/111/41 f 34/112/42 125/113/42 32/114/42 f 36/115/43 139/116/43 133/117/43 f 42/118/44 141/119/44 40/120/44 f 46/121/45 149/122/45 44/123/45 f 48/124/46 163/125/46 157/126/46 f 52/127/47 171/128/47 165/129/47 f 56/130/48 179/131/48 173/132/48 f 62/133/49 181/134/49 60/135/49 f 63/136/50 65/137/50 1/138/50 f 60/139/51 184/140/51 59/77/51 f 41/58/52 147/141/52 42/56/52 f 8/142/53 80/143/53 7/7/53 f 40/144/54 144/145/54 39/51/54 f 110/146/55 113/147/55 111/148/55 f 44/149/56 152/150/56 43/57/56 f 25/35/57 115/151/57 26/33/57 f 182/152/58 185/153/58 183/154/58 f 23/155/59 114/156/59 25/157/59 f 78/158/60 81/159/60 79/160/60 f 61/84/61 187/161/61 62/82/61 f 28/162/62 120/163/62 27/34/62 f 9/14/63 83/164/63 10/12/63 f 59/165/64 186/166/64 61/167/64 f 150/168/65 153/169/65 151/170/65 f 9/171/66 80/172/66 82/173/66 f 64/174/67 192/175/67 63/83/67 f 45/62/68 155/176/68 46/60/68 f 12/177/69 88/178/69 11/13/69 f 43/179/70 154/180/70 45/181/70 f 118/182/71 121/183/71 119/184/71 f 48/185/72 160/186/72 47/61/72 f 29/41/73 123/187/73 30/39/73 f 27/188/74 122/189/74 29/190/74 f 86/191/75 89/192/75 87/193/75 f 1/3/76 68/194/76 2/1/76 f 32/195/77 128/196/77 31/40/77 f 13/20/78 91/197/78 14/18/78 f 70/198/79 73/199/79 71/200/79 f 158/201/80 161/202/80 159/203/80 f 11/204/81 90/205/81 13/206/81 f 49/68/82 163/207/82 50/66/82 f 16/208/83 96/209/83 15/19/83 f 47/90/84 162/210/84 49/211/84 f 126/212/85 129/213/85 127/214/85 f 52/215/86 168/216/86 51/67/86 f 33/46/87 131/217/87 34/44/87 f 31/89/88 130/218/88 33/219/88 f 94/220/89 97/221/89 95/222/89 f 36/223/90 136/224/90 35/45/90 f 17/24/91 99/225/91 18/22/91 f 166/226/92 169/227/92 167/228/92 f 15/88/93 98/229/93 17/230/93 f 53/73/94 171/231/94 54/72/94 f 102/232/95 105/233/95 103/234/95 f 51/235/96 170/236/96 53/237/96 f 134/238/97 137/239/97 135/240/97 f 66/241/98 68/242/98 65/243/98 f 70/244/99 72/245/99 69/246/99 f 71/247/100 74/248/100 72/245/100 f 70/244/101 75/249/101 76/250/101 f 74/248/102 76/250/102 75/249/102 f 78/251/103 80/252/103 77/253/103 f 81/254/104 80/255/104 79/256/104 f 84/257/105 77/258/105 83/259/105 f 82/260/106 84/257/106 83/261/106 f 87/262/107 85/263/107 86/264/107 f 89/265/108 88/266/108 87/262/108 f 86/264/109 91/267/109 92/268/109 f 90/269/110 92/268/110 91/270/110 f 95/271/111 93/272/111 94/273/111 f 95/271/112 98/274/112 96/275/112 f 94/273/113 99/276/113 100/277/113 f 98/274/114 100/277/114 99/276/114 f 103/278/115 101/279/115 102/280/115 f 105/281/116 104/282/116 103/278/116 f 102/280/117 107/283/117 108/284/117 f 106/285/118 108/284/118 107/286/118 f 111/287/119 109/288/119 110/289/119 f 113/290/120 112/291/120 111/287/120 f 116/292/121 109/293/121 115/294/121 f 114/295/122 116/292/122 115/296/122 f 119/297/123 117/298/123 118/299/123 f 119/297/124 122/300/124 120/301/124 f 118/299/125 123/302/125 124/303/125 f 122/300/126 124/303/126 123/302/126 f 126/304/127 128/305/127 125/306/127 f 129/307/128 128/305/128 127/308/128 f 132/309/129 125/306/129 131/310/129 f 130/311/130 132/309/130 131/310/130 f 135/312/131 133/313/131 134/314/131 f 137/315/132 136/316/132 135/312/132 f 140/317/133 133/318/133 139/319/133 f 138/320/134 140/317/134 139/321/134 f 142/322/135 144/323/135 141/324/135 f 145/325/136 144/326/136 143/327/136 f 142/322/137 147/328/137 148/329/137 f 146/330/138 148/329/138 147/328/138 f 151/331/139 149/332/139 150/333/139 f 153/334/140 152/335/140 151/331/140 f 150/333/141 155/336/141 156/337/141 f 154/338/142 156/337/142 155/336/142 f 159/339/143 157/340/143 158/341/143 f 159/339/144 162/342/144 160/343/144 f 158/341/145 163/344/145 164/345/145 f 162/346/146 164/345/146 163/347/146 f 167/348/147 165/349/147 166/350/147 f 169/351/148 168/352/148 167/348/148 f 166/350/149 171/353/149 172/354/149 f 170/355/150 172/354/150 171/356/150 f 175/357/151 173/358/151 174/359/151 f 177/360/152 176/361/152 175/357/152 f 174/359/153 179/362/153 180/363/153 f 178/364/154 180/363/154 179/362/154 f 183/365/155 181/366/155 182/367/155 f 185/368/156 184/369/156 183/365/156 f 188/370/157 181/366/157 187/371/157 f 186/372/158 188/370/158 187/373/158 f 191/374/159 189/375/159 190/376/159 f 66/241/160 192/377/160 191/374/160 f 67/378/161 189/379/161 68/380/161 f 190/381/162 66/382/162 191/383/162 f 213/384/163 244/385/163 245/386/163 f 34/387/164 210/388/164 36/389/164 f 54/390/165 218/391/165 219/392/165 f 8/393/166 197/394/166 10/395/166 f 26/396/167 206/397/167 28/398/167 f 44/399/168 215/400/168 46/401/168 f 62/402/169 224/403/169 64/404/169 f 18/405/170 202/406/170 20/407/170 f 36/408/171 211/409/171 38/410/171 f 54/411/172 220/412/172 56/413/172 f 10/414/173 198/415/173 12/416/173 f 28/417/174 207/418/174 30/419/174 f 48/420/175 215/421/175 216/422/175 f 2/423/176 193/424/176 4/425/176 f 2/426/177 224/427/177 194/428/177 f 20/429/178 203/430/178 22/431/178 f 38/432/179 212/433/179 40/434/179 f 58/435/180 220/436/180 221/437/180 f 12/438/181 199/439/181 14/440/181 f 30/441/182 208/442/182 32/443/182 f 48/444/183 217/445/183 50/446/183 f 6/447/184 193/448/184 195/449/184 f 22/450/185 204/451/185 24/452/185 f 40/453/186 213/454/186 42/455/186 f 60/456/187 221/457/187 222/458/187 f 14/459/188 200/460/188 16/461/188 f 32/462/189 209/463/189 34/464/189 f 52/465/190 217/466/190 218/467/190 f 8/468/191 195/469/191 196/470/191 f 24/471/192 205/472/192 26/473/192 f 44/474/193 213/475/193 214/476/193 f 62/477/194 222/478/194 223/479/194 f 248/480/195 279/481/195 280/482/195 f 199/483/196 232/484/196 200/485/196 f 214/486/197 245/487/197 246/488/197 f 200/489/198 233/490/198 201/491/198 f 214/492/199 247/493/199 215/494/199 f 201/495/200 234/496/200 202/497/200 f 216/498/201 247/499/201 248/500/201 f 203/501/202 234/502/202 235/503/202 f 217/504/203 248/505/203 249/506/203 f 203/507/204 236/508/204 204/509/204 f 217/510/205 250/511/205 218/512/205 f 204/513/206 237/514/206 205/515/206 f 219/516/207 250/517/207 251/518/207 f 205/519/208 238/520/208 206/521/208 f 219/522/209 252/523/209 220/524/209 f 206/525/210 239/526/210 207/527/210 f 194/528/211 225/529/211 193/530/211 f 221/531/212 252/532/212 253/533/212 f 207/534/213 240/535/213 208/536/213 f 195/537/214 225/538/214 227/539/214 f 222/540/215 253/541/215 254/542/215 f 208/543/216 241/544/216 209/545/216 f 196/546/217 227/547/217 228/548/217 f 222/549/218 255/550/218 223/551/218 f 209/552/219 242/553/219 210/554/219 f 196/555/220 229/556/220 197/557/220 f 224/558/221 255/559/221 256/560/221 f 211/561/222 242/562/222 243/563/222 f 197/564/223 230/565/223 198/566/223 f 194/567/224 256/568/224 226/569/224 f 211/570/225 244/571/225 212/572/225 f 198/573/226 231/574/226 199/575/226 f 283/576/227 275/577/227 267/578/227 f 234/579/228 267/580/228 235/581/228 f 249/582/229 280/583/229 281/584/229 f 235/585/230 268/586/230 236/587/230 f 250/588/231 281/589/231 282/590/231 f 236/591/232 269/592/232 237/593/232 f 251/594/233 282/595/233 283/596/233 f 237/597/234 270/598/234 238/599/234 f 251/600/235 284/601/235 252/602/235 f 238/603/236 271/604/236 239/605/236 f 226/606/237 257/607/237 225/608/237 f 252/609/238 285/610/238 253/611/238 f 239/612/239 272/613/239 240/614/239 f 225/615/240 259/616/240 227/617/240 f 253/618/241 286/619/241 254/620/241 f 240/621/242 273/622/242 241/623/242 f 227/624/243 260/625/243 228/626/243 f 254/627/244 287/628/244 255/629/244 f 241/630/245 274/631/245 242/632/245 f 228/633/246 261/634/246 229/635/246 f 255/636/247 288/637/247 256/638/247 f 243/639/248 274/640/248 275/641/248 f 229/642/249 262/643/249 230/644/249 f 256/645/250 258/646/250 226/647/250 f 243/648/251 276/649/251 244/650/251 f 230/651/252 263/652/252 231/653/252 f 245/654/253 276/655/253 277/656/253 f 231/657/254 264/658/254 232/659/254 f 246/660/255 277/661/255 278/662/255 f 232/663/256 265/664/256 233/665/256 f 246/666/257 279/667/257 247/668/257 f 234/669/258 265/670/258 266/671/258 f 2/1/1 4/4/1 3/2/1 f 4/4/2 69/672/2 72/5/2 f 6/6/3 8/142/3 7/7/3 f 39/9/4 144/673/4 146/10/4 f 10/12/5 12/177/5 11/13/5 f 56/15/6 173/674/6 176/16/6 f 14/18/7 16/208/7 15/19/7 f 5/8/8 74/675/8 75/21/8 f 18/22/9 20/42/9 19/23/9 f 24/25/10 109/676/10 112/26/10 f 22/28/11 24/25/11 23/27/11 f 57/30/12 178/677/12 179/31/12 f 26/33/13 28/162/13 27/34/13 f 5/36/14 3/678/14 72/37/14 f 30/39/15 32/195/15 31/40/15 f 20/42/16 101/679/16 104/43/16 f 34/44/17 36/223/17 35/45/17 f 55/47/18 176/680/18 178/48/18 f 38/50/19 40/144/19 39/51/19 f 2/53/20 68/681/20 189/54/20 f 42/56/21 44/149/21 43/57/21 f 21/29/22 106/682/22 107/59/22 f 46/60/23 48/185/23 47/61/23 f 142/63/24 148/683/24 145/64/24 f 50/66/25 52/215/25 51/67/25 f 21/69/26 19/684/26 104/70/26 f 54/72/27 56/15/27 55/17/27 f 35/74/28 136/685/28 138/75/28 f 58/32/29 60/139/29 59/77/29 f 37/52/30 138/686/30 139/78/30 f 16/79/31 200/687/31 201/80/31 f 62/82/32 64/174/32 63/83/32 f 174/85/33 180/688/33 177/86/33 f 63/136/34 1/138/34 3/678/34 f 3/678/34 5/36/34 7/689/34 f 7/689/34 9/171/34 11/204/34 f 11/204/34 13/206/34 7/689/34 f 13/206/34 15/88/34 7/689/34 f 15/88/34 17/230/34 19/684/34 f 19/684/34 21/69/34 15/88/34 f 21/69/34 23/155/34 15/88/34 f 23/155/34 25/157/34 31/89/34 f 25/157/34 27/188/34 31/89/34 f 27/188/34 29/190/34 31/89/34 f 31/89/34 33/219/34 35/74/34 f 35/74/34 37/76/34 39/9/34 f 39/9/34 41/11/34 47/90/34 f 41/11/34 43/179/34 47/90/34 f 43/179/34 45/181/34 47/90/34 f 47/90/34 49/211/34 51/235/34 f 51/235/34 53/237/34 55/47/34 f 55/47/34 57/49/34 59/165/34 f 59/165/34 61/167/34 63/136/34 f 63/136/34 3/678/34 7/689/34 f 31/89/34 35/74/34 47/90/34 f 35/74/34 39/9/34 47/90/34 f 47/90/34 51/235/34 63/136/34 f 51/235/34 55/47/34 63/136/34 f 55/47/34 59/165/34 63/136/34 f 63/136/34 7/689/34 15/88/34 f 15/88/34 23/155/34 31/89/34 f 63/136/34 15/88/34 47/90/34 f 6/91/35 75/690/35 69/92/35 f 10/94/36 83/691/36 77/95/36 f 12/97/37 14/692/37 91/98/37 f 18/100/38 99/693/38 93/101/38 f 22/103/39 107/694/39 101/104/39 f 26/106/40 115/695/40 109/107/40 f 30/109/41 123/696/41 117/110/41 f 34/112/42 131/697/42 125/113/42 f 36/115/43 38/698/43 139/116/43 f 42/118/44 147/699/44 141/119/44 f 46/121/45 155/700/45 149/122/45 f 48/124/46 50/701/46 163/125/46 f 52/127/47 54/702/47 171/128/47 f 56/130/48 58/703/48 179/131/48 f 62/133/49 187/704/49 181/134/49 f 63/136/50 192/705/50 65/137/50 f 60/139/51 181/706/51 184/140/51 f 41/58/52 146/707/52 147/141/52 f 8/142/53 77/708/53 80/143/53 f 40/144/54 141/709/54 144/145/54 f 110/146/55 116/710/55 113/147/55 f 44/149/56 149/711/56 152/150/56 f 25/35/57 114/712/57 115/151/57 f 182/152/58 188/713/58 185/153/58 f 23/155/59 112/714/59 114/156/59 f 78/158/60 84/715/60 81/159/60 f 61/84/61 186/716/61 187/161/61 f 28/162/62 117/717/62 120/163/62 f 9/14/63 82/718/63 83/164/63 f 59/165/64 184/719/64 186/166/64 f 150/168/65 156/720/65 153/169/65 f 9/171/66 7/689/66 80/172/66 f 64/174/67 189/721/67 192/175/67 f 45/62/68 154/722/68 155/176/68 f 12/177/69 85/723/69 88/178/69 f 43/179/70 152/724/70 154/180/70 f 118/182/71 124/725/71 121/183/71 f 48/185/72 157/726/72 160/186/72 f 29/41/73 122/727/73 123/187/73 f 27/188/74 120/728/74 122/189/74 f 86/191/75 92/729/75 89/192/75 f 1/3/76 65/730/76 68/194/76 f 32/195/77 125/731/77 128/196/77 f 13/20/78 90/732/78 91/197/78 f 70/198/79 76/733/79 73/199/79 f 158/201/80 164/734/80 161/202/80 f 11/204/81 88/735/81 90/205/81 f 49/68/82 162/736/82 163/207/82 f 16/208/83 93/737/83 96/209/83 f 47/90/84 160/738/84 162/210/84 f 126/212/85 132/739/85 129/213/85 f 52/215/86 165/740/86 168/216/86 f 33/46/87 130/741/87 131/217/87 f 31/89/88 128/742/88 130/218/88 f 94/220/89 100/743/89 97/221/89 f 36/223/90 133/744/90 136/224/90 f 17/24/91 98/745/91 99/225/91 f 166/226/92 172/746/92 169/227/92 f 15/88/93 96/747/93 98/229/93 f 53/73/94 170/748/94 171/231/94 f 102/232/95 108/749/95 105/233/95 f 51/235/96 168/750/96 170/236/96 f 134/238/97 140/751/97 137/239/97 f 66/241/98 67/378/98 68/242/98 f 70/244/99 71/247/99 72/245/99 f 71/247/100 73/752/100 74/248/100 f 70/244/101 69/246/101 75/249/101 f 74/248/259 73/752/259 76/250/259 f 78/251/103 79/256/103 80/252/103 f 81/254/104 82/753/104 80/255/104 f 84/257/105 78/251/105 77/258/105 f 82/260/106 81/254/106 84/257/106 f 87/262/107 88/754/107 85/263/107 f 89/265/108 90/755/108 88/266/108 f 86/264/109 85/756/109 91/267/109 f 90/269/110 89/265/110 92/268/110 f 95/271/111 96/275/111 93/272/111 f 95/271/112 97/757/112 98/274/112 f 94/273/113 93/272/113 99/276/113 f 98/274/114 97/757/114 100/277/114 f 103/278/115 104/282/115 101/279/115 f 105/281/260 106/758/260 104/282/260 f 102/280/261 101/279/261 107/283/261 f 106/285/118 105/281/118 108/284/118 f 111/287/119 112/759/119 109/288/119 f 113/290/120 114/760/120 112/291/120 f 116/292/121 110/289/121 109/293/121 f 114/295/122 113/290/122 116/292/122 f 119/297/123 120/301/123 117/298/123 f 119/297/124 121/761/124 122/300/124 f 118/299/125 117/298/125 123/302/125 f 122/300/126 121/761/126 124/303/126 f 126/304/127 127/308/127 128/305/127 f 129/307/128 130/311/128 128/305/128 f 132/309/129 126/304/129 125/306/129 f 130/311/130 129/307/130 132/309/130 f 135/312/131 136/762/131 133/313/131 f 137/315/262 138/763/262 136/316/262 f 140/317/133 134/314/133 133/318/133 f 138/320/263 137/315/263 140/317/263 f 142/322/135 143/327/135 144/323/135 f 145/325/136 146/330/136 144/326/136 f 142/322/137 141/764/137 147/328/137 f 146/330/138 145/325/138 148/329/138 f 151/331/139 152/335/139 149/332/139 f 153/334/140 154/338/140 152/335/140 f 150/333/141 149/332/141 155/336/141 f 154/338/142 153/334/142 156/337/142 f 159/339/143 160/765/143 157/340/143 f 159/339/144 161/766/144 162/342/144 f 158/341/145 157/767/145 163/344/145 f 162/346/146 161/766/146 164/345/146 f 167/348/147 168/768/147 165/349/147 f 169/351/148 170/769/148 168/352/148 f 166/350/264 165/770/264 171/353/264 f 170/355/150 169/351/150 172/354/150 f 175/357/151 176/361/151 173/358/151 f 177/360/152 178/364/152 176/361/152 f 174/359/153 173/358/153 179/362/153 f 178/364/154 177/360/154 180/363/154 f 183/365/155 184/369/155 181/366/155 f 185/368/156 186/771/156 184/369/156 f 188/370/157 182/367/157 181/366/157 f 186/372/158 185/368/158 188/370/158 f 191/374/159 192/772/159 189/375/159 f 66/241/160 65/773/160 192/377/160 f 67/378/161 190/376/161 189/379/161 f 190/381/162 67/774/162 66/382/162 f 213/384/163 212/775/163 244/385/163 f 34/387/164 209/776/164 210/388/164 f 54/390/165 52/777/165 218/391/165 f 8/393/166 196/778/166 197/394/166 f 26/396/167 205/779/167 206/397/167 f 44/399/168 214/780/168 215/400/168 f 62/402/169 223/781/169 224/403/169 f 18/405/170 201/782/170 202/406/170 f 36/408/171 210/783/171 211/409/171 f 54/411/172 219/784/172 220/412/172 f 10/414/173 197/785/173 198/415/173 f 28/417/174 206/786/174 207/418/174 f 48/420/175 46/787/175 215/421/175 f 2/423/176 194/788/176 193/424/176 f 2/426/177 64/789/177 224/427/177 f 20/429/178 202/790/178 203/430/178 f 38/432/179 211/791/179 212/433/179 f 58/435/180 56/792/180 220/436/180 f 12/438/181 198/793/181 199/439/181 f 30/441/182 207/794/182 208/442/182 f 48/444/183 216/795/183 217/445/183 f 6/447/184 4/796/184 193/448/184 f 22/450/185 203/797/185 204/451/185 f 40/453/186 212/798/186 213/454/186 f 60/456/187 58/799/187 221/457/187 f 14/459/188 199/800/188 200/460/188 f 32/462/189 208/801/189 209/463/189 f 52/465/190 50/802/190 217/466/190 f 8/468/191 6/803/191 195/469/191 f 24/471/192 204/804/192 205/472/192 f 44/474/193 42/805/193 213/475/193 f 62/477/194 60/806/194 222/478/194 f 248/480/195 247/807/195 279/481/195 f 199/483/196 231/808/196 232/484/196 f 214/486/197 213/809/197 245/487/197 f 200/489/198 232/810/198 233/490/198 f 214/492/199 246/811/199 247/493/199 f 201/495/200 233/812/200 234/496/200 f 216/498/201 215/813/201 247/499/201 f 203/501/202 202/814/202 234/502/202 f 217/504/203 216/815/203 248/505/203 f 203/507/204 235/816/204 236/508/204 f 217/510/205 249/817/205 250/511/205 f 204/513/206 236/818/206 237/514/206 f 219/516/207 218/819/207 250/517/207 f 205/519/208 237/820/208 238/520/208 f 219/522/209 251/821/209 252/523/209 f 206/525/210 238/822/210 239/526/210 f 194/528/211 226/823/211 225/529/211 f 221/531/212 220/824/212 252/532/212 f 207/534/213 239/825/213 240/535/213 f 195/537/214 193/826/214 225/538/214 f 222/540/215 221/827/215 253/541/215 f 208/543/216 240/828/216 241/544/216 f 196/546/217 195/829/217 227/547/217 f 222/549/218 254/830/218 255/550/218 f 209/552/219 241/831/219 242/553/219 f 196/555/220 228/832/220 229/556/220 f 224/558/221 223/833/221 255/559/221 f 211/561/222 210/834/222 242/562/222 f 197/564/223 229/835/223 230/565/223 f 194/567/224 224/836/224 256/568/224 f 211/570/225 243/837/225 244/571/225 f 198/573/226 230/838/226 231/574/226 f 259/839/227 257/840/227 258/841/227 f 258/841/227 288/842/227 287/843/227 f 287/843/227 286/844/227 283/576/227 f 286/844/227 285/845/227 283/576/227 f 285/845/227 284/846/227 283/576/227 f 283/576/227 282/847/227 281/848/227 f 281/848/227 280/849/227 279/850/227 f 279/850/227 278/851/227 275/577/227 f 278/851/227 277/852/227 275/577/227 f 277/852/227 276/853/227 275/577/227 f 275/577/227 274/854/227 273/855/227 f 273/855/227 272/856/227 271/857/227 f 271/857/227 270/858/227 269/859/227 f 269/859/227 268/860/227 267/578/227 f 267/578/227 266/861/227 265/862/227 f 265/862/227 264/863/227 267/578/227 f 264/863/227 263/864/227 267/578/227 f 263/864/227 262/865/227 261/866/227 f 261/866/227 260/867/227 263/864/227 f 260/867/227 259/839/227 263/864/227 f 259/839/227 258/841/227 283/576/227 f 258/841/227 287/843/227 283/576/227 f 283/576/227 281/848/227 275/577/227 f 281/848/227 279/850/227 275/577/227 f 275/577/227 273/855/227 271/857/227 f 271/857/227 269/859/227 275/577/227 f 269/859/227 267/578/227 275/577/227 f 267/578/227 263/864/227 259/839/227 f 259/839/227 283/576/227 267/578/227 f 234/579/228 266/868/228 267/580/228 f 249/582/229 248/869/229 280/583/229 f 235/585/230 267/870/230 268/586/230 f 250/588/231 249/871/231 281/589/231 f 236/591/232 268/872/232 269/592/232 f 251/594/233 250/873/233 282/595/233 f 237/597/234 269/874/234 270/598/234 f 251/600/235 283/875/235 284/601/235 f 238/603/236 270/876/236 271/604/236 f 226/606/237 258/877/237 257/607/237 f 252/609/238 284/878/238 285/610/238 f 239/612/239 271/879/239 272/613/239 f 225/615/240 257/880/240 259/616/240 f 253/618/241 285/881/241 286/619/241 f 240/621/242 272/882/242 273/622/242 f 227/624/243 259/883/243 260/625/243 f 254/627/244 286/884/244 287/628/244 f 241/630/245 273/885/245 274/631/245 f 228/633/246 260/886/246 261/634/246 f 255/636/247 287/887/247 288/637/247 f 243/639/248 242/888/248 274/640/248 f 229/642/249 261/889/249 262/643/249 f 256/645/250 288/890/250 258/646/250 f 243/648/251 275/891/251 276/649/251 f 230/651/252 262/892/252 263/652/252 f 245/654/253 244/893/253 276/655/253 f 231/657/254 263/894/254 264/658/254 f 246/660/255 245/895/255 277/661/255 f 232/663/256 264/896/256 265/664/256 f 246/666/257 278/897/257 279/667/257 f 234/669/258 233/898/258 265/670/258 ================================================ FILE: src/asset/botdef/building_example.json ================================================ { "id": { "value": 1441884615969752781, "phantom": null }, "file_path": "./src/asset/botdef/building_example.json", "radius": 0.5, "max_life": 1000, "turn_accel": 0.0, "max_turn_rate": 0.0, "accel": 0.0, "break_accel": 0.0, "max_speed": 0.0, "build_power": 0.5, "build_dist": 10.0, "metal_cost": 100, "part_tree": { "id": { "value": 19713591288447385, "phantom": null }, "placed_mesh": null, "placed_collider": null, "parent_to_self": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], "joint": "Fix", "children": [ { "id": { "value": 6444314735306398051, "phantom": null }, "placed_mesh": { "trans": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/cube.obj", "mesh_index": 0 }, "placed_collider": null, "parent_to_self": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], "joint": "Fix", "children": [] } ] } } ================================================ FILE: src/asset/botdef/unit_example.json ================================================ { "id": { "value": 14418846159697527818, "phantom": null }, "file_path": "./src/asset/botdef/unit_example.json", "radius": 0.5, "max_life": 100, "turn_accel": 0.44440976, "max_turn_rate": 0.38327432, "accel": 0.1, "break_accel": 0.3, "max_speed": 1.0, "build_power": 0.5, "build_dist": 10.0, "metal_cost": 10, "part_tree": { "id": { "value": 197135912884473854, "phantom": null }, "placed_mesh": null, "placed_collider": null, "parent_to_self": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], "joint": "Fix", "children": [ { "id": { "value": 1756212347027302969, "phantom": null }, "placed_mesh": { "trans": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/tank/base.obj", "mesh_index": 3 }, "placed_collider": null, "parent_to_self": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.25, 1.0 ], "joint": "Fix", "children": [ { "id": { "value": 13491902060961674828, "phantom": null }, "placed_mesh": { "trans": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.315, 0.005, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/tank/canon.obj", "mesh_index": 5 }, "placed_collider": null, "parent_to_self": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.196, 0.0, 0.0, 1.0 ], "joint": "AimWeapon0", "children": [] }, { "id": { "value": 11290295935695058242, "phantom": null }, "placed_mesh": { "trans": [ -0.0011959828, -1.8058838e-10, -0.9999993, 0.0, 2.3841828e-7, 1.0, -4.657329e-10, 0.0, 0.9999993, -2.384187e-7, -0.0011959828, 0.0, 0.0, 0.0, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/tank/wheel.obj", "mesh_index": 4 }, "placed_collider": null, "parent_to_self": [ -0.99999875, 0.001592548, 0.0, 0.0, -0.001592548, -0.99999875, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.516, -0.617, 0.0, 1.0 ], "joint": "Wheel0", "children": [] }, { "id": { "value": 6250614812945715198, "phantom": null }, "placed_mesh": { "trans": [ 0.010791883, 0.0, -0.99994177, 0.0, 0.0, 1.0, 0.0, 0.0, 0.99994177, 0.0, 0.010791883, 0.0, 0.0, 0.0, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/tank/wheel.obj", "mesh_index": 4 }, "placed_collider": null, "parent_to_self": [ -0.99999875, 0.001592548, 0.0, 0.0, -0.001592548, -0.99999875, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.518, -0.619, 0.0, 1.0 ], "joint": "Wheel0", "children": [] }, { "id": { "value": 12952766970854183071, "phantom": null }, "placed_mesh": { "trans": [ 0.0042427215, 4.6193324e-7, 0.999991, 0.0, -0.00010887664, 1.0, 0.0, 0.0, -0.999991, -0.000108875654, 0.0042427215, 0.0, 0.0, 0.0, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/tank/wheel.obj", "mesh_index": 4 }, "placed_collider": null, "parent_to_self": [ -0.99999994, 0.00040736992, 0.0, 0.0, -0.00040736992, -0.99999994, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.523, 0.625, 0.0, 1.0 ], "joint": "Wheel0", "children": [] }, { "id": { "value": 833497444713801088, "phantom": null }, "placed_mesh": { "trans": [ -0.00059798185, -0.000007530322, -0.9999998, 0.0, 0.012592674, -0.9999207, -4.656272e-10, 0.0, -0.99992055, -0.012592672, 0.00059802923, 0.0, 0.0, 0.0, 0.0, 1.0 ], "mesh_path": "./src/asset/3d/tank/wheel.obj", "mesh_index": 4 }, "placed_collider": null, "parent_to_self": [ -0.999954, -0.009592537, 0.0, 0.0, 0.009592537, -0.999954, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.525, 0.609, 0.0, 1.0 ], "joint": "Wheel0", "children": [] } ] } ] } } ================================================ FILE: src/asset/map/map_example/data.json ================================================ { "metal_spots": [] } ================================================ FILE: src/botdef.rs ================================================ use crate::unit; use crate::utils; use serde::{Deserialize, Serialize}; use typename::TypeName; use utils::Id; #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub struct BotDef { pub id: Id, pub file_path: String, pub radius: f32, pub max_life: i32, //Movement ///rad/frame² pub turn_accel: f32, ///rad/frame pub max_turn_rate: f32, ///m/frame² pub accel: f32, ///m/frame² pub break_accel: f32, ///m/frame pub max_speed: f32, ///metal/frame pub build_power: f32, ///m pub build_dist: f32, ///metal pub metal_cost: i32, pub part_tree: unit::PartTree, } ================================================ FILE: src/client/camera.rs ================================================ extern crate nalgebra as na; use super::client::*; use na::{Matrix4, Point3, Vector3}; const FOVY: f32 = 3.14 / 4.0; const NEAR: f32 = 1.0; const FAR: f32 = 8000.0; pub fn create_view(pos: &Point3, dir: &Vector3) -> Matrix4 { Matrix4::look_at_rh(pos, &(pos + dir), &Vector3::new(0.0, 0.0, 1.0)) } pub fn create_normal(pos: &Point3, dir: &Vector3) -> Matrix4 { create_view(pos, dir).try_inverse().unwrap().transpose() } pub fn create_proj(aspect_ratio: f32, near: f32) -> Matrix4 { let mx_projection = Matrix4::new_perspective(aspect_ratio, FOVY, near, FAR); let mx_correction: Matrix4 = Matrix4::new( 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, ); mx_correction * mx_projection } pub fn create_view_proj( aspect_ratio: f32, near: f32, pos: &Point3, dir: &Vector3, ) -> Matrix4 { let mx_view = create_view(pos, dir); let mx_proj = create_proj(aspect_ratio, near); mx_proj * mx_view } pub fn create_camera_uniform_vec( screen_res: (u32, u32), near: f32, pos: &Point3, dir: &Vector3, ) -> Vec { let mut res = Vec::new(); //ViewProj let mx_total = create_view_proj(screen_res.0 as f32 / screen_res.1 as f32, near, pos, dir); let mx_ref: &[f32] = mx_total.as_slice(); res.extend_from_slice(mx_ref); //View let mx_total = create_view(pos, dir); let mx_ref: &[f32] = mx_total.as_slice(); res.extend_from_slice(mx_ref); //Proj let mx_total = create_proj(screen_res.0 as f32 / screen_res.1 as f32, near); let mx_ref: &[f32] = mx_total.as_slice(); res.extend_from_slice(mx_ref); //Normal let mx_total = create_normal(pos, dir); let mx_ref: &[f32] = mx_total.as_slice(); res.extend_from_slice(mx_ref); res } impl App { pub fn rts_camera(&mut self, sim_sec: f32) { use winit::event::VirtualKeyCode as Key; let key_pressed = &self.input_state.key_pressed; let on = |vkc| key_pressed.contains(&vkc); let mut offset = Vector3::new(0.0, 0.0, 0.0); let mut dir_offset = self.game_state.dir.clone(); let mut new_dir = None; let camera_ground_height = self.heightmap_gpu.phy.z( self.game_state .position .x .max(0.0) .min(self.heightmap_gpu.phy.width as f32 - 1.0), self.game_state .position .y .max(0.0) .min(self.heightmap_gpu.phy.height as f32 - 1.0), ); let screen_center_world_pos_fallback = self.game_state.position_smooth + self.game_state.dir_smooth * (40.0 - self.game_state.position_smooth.z) / self.game_state.dir_smooth.z; let height_from_ground = self.game_state.position.z - camera_ground_height; let distance_camera_middle_screen = self .game_state .screen_center_world_pos .or(Some(screen_center_world_pos_fallback.coords)) .map(|scwp| (self.game_state.position.coords - scwp).magnitude()) .unwrap_or(height_from_ground); let k = (if !on(Key::LShift) { 1.0 } else { 2.0 }) * distance_camera_middle_screen.max(10.0); //Game if on(Key::S) { offset.y -= k; } if on(Key::Z) { offset.y += k; } if on(Key::Q) { offset.x -= k; } if on(Key::D) { offset.x += k; } if on(Key::LControl) { if let Some(screen_center_world_pos) = self .game_state .screen_center_world_pos .or(Some(screen_center_world_pos_fallback.coords)) { if self.input_state.last_scroll != 0.0 { let camera_to_center = screen_center_world_pos - self.game_state.position.coords; let distance = camera_to_center.norm(); let mut new_camera_to_center = camera_to_center.normalize(); if self.input_state.last_scroll > 0.0 { new_camera_to_center.y += 1.0 * 0.30; } if self.input_state.last_scroll < 0.0 { new_camera_to_center.z -= 1.0 * 0.30; } new_camera_to_center.x = 0.0; new_camera_to_center = new_camera_to_center.normalize(); new_camera_to_center.y = new_camera_to_center.y.max(0.01); new_dir = Some(new_camera_to_center); let new_pos = screen_center_world_pos - new_camera_to_center.normalize() * distance; offset += (new_pos - self.game_state.position.coords) / sim_sec; } } else { if self.input_state.last_scroll > 0.0 { dir_offset.y += 0.010 / sim_sec; } if self.input_state.last_scroll < 0.0 { dir_offset.z -= 0.010 / sim_sec; } } } else { if let Some(mouse_world_pos) = self .game_state .mouse_world_pos .or(Some(screen_center_world_pos_fallback.coords)) { let u = (mouse_world_pos - self.game_state.position.coords).normalize(); offset += self.input_state.last_scroll * u * k * 0.75 * 0.320 / sim_sec; } else { offset.z = -self.input_state.last_scroll * k * 0.75 * 0.20 / sim_sec; } } self.game_state.position += offset * sim_sec; self.game_state.dir = (self.game_state.dir + dir_offset * 33.0 * sim_sec).normalize(); new_dir.map(|new_dir| { self.game_state.dir = new_dir; }); self.game_state.position.z = self.game_state.position.z.max(camera_ground_height + 3.0); if self.game_state.position.coords.magnitude() > 7000.0 { self.game_state.position = Point3::from(7000.0 * self.game_state.position.coords.normalize()); } self.game_state.position_smooth += (self.game_state.position.coords - self.game_state.position_smooth.coords) * sim_sec.min(0.033) * 15.0; self.game_state.dir_smooth += (self.game_state.dir - self.game_state.dir_smooth) * sim_sec.min(0.033) * 15.0; } pub fn orbit_camera(&mut self, sim_sec: f32) { let to_orbit = (self.unit_editor.orbit.coords - self.game_state.position_smooth.coords).normalize(); let right_vec = to_orbit.cross(&Vector3::new(0.0, 0.0, 1.0)).normalize(); let up_vec = -to_orbit.cross(&right_vec).normalize(); if self .input_state .mouse_pressed .contains(&winit::event::MouseButton::Middle) { let k = 3.0; let right_offset = -self.input_state.cursor_offset.0 as f32 * right_vec * sim_sec * k; let up_offset = self.input_state.cursor_offset.1 as f32 * up_vec * sim_sec * k; self.game_state.position += right_offset + up_offset; self.game_state.position_smooth += right_offset + up_offset; if self .input_state .key_pressed .contains(&winit::event::VirtualKeyCode::LShift) { self.unit_editor.orbit += right_offset + up_offset; } } let to_orbit = (self.unit_editor.orbit.coords - self.game_state.position_smooth.coords).normalize(); self.game_state.dir = to_orbit; self.game_state.dir_smooth = self.game_state.dir; self.game_state.position += self.input_state.last_scroll * to_orbit * sim_sec * 15.0; self.game_state.position_smooth = self.game_state.position; } } ================================================ FILE: src/client/game_state.rs ================================================ extern crate nalgebra as na; use super::heightmap_editor; use crate::botdef; use crate::frame::Frame; use crate::mobile; use crate::utils; use fnv::{FnvHashMap, FnvHashSet}; use na::{Matrix4, Point3, Vector2, Vector3}; use std::time::Instant; use utils::*; use super::uitool::UiTool; use crate::frame::Player; use mobile::*; #[derive(Clone, Copy, Debug)] pub struct Explosion { pub position: Point3, pub born_sec: f32, pub death_sec: f32, pub size: f32, pub seed: f32, } pub struct State { pub position: Point3, pub dir: Vector3, pub position_smooth: Point3, pub dir_smooth: Vector3, pub mouse_world_pos: Option>, pub screen_center_world_pos: Option>, pub heightmap_editor: heightmap_editor::State, //Data to interpolate pub frame_minus_one: Frame, pub frame_zero: Frame, pub frame_zero_time_received: Instant, //Interpolated from curve pub kbots: Vec<(KBot, ClientKbot)>, pub server_sec: f32, //Extrapolated from events pub explosions: Vec, pub kinematic_projectiles_cache: FnvHashMap, KinematicProjectile>, pub kinematic_projectiles: Vec>, pub selected: FnvHashSet>, pub under_mouse: Option>, pub uitool: UiTool, pub start_time: Instant, pub last_frame: Instant, pub my_player_id: Option>, pub players: FnvHashMap, Player>, pub fps: u64, //parameters pub unit_icon_distance: f32, } impl State { pub fn new() -> Self { State { position: Point3::new(1024.0, 100.0, 50.0), dir: Vector3::new(0.0, 0.3, -1.0), position_smooth: Point3::new(0.0, 0.0, 30000.0), dir_smooth: Vector3::new(0.0, 0.01, -1.0), mouse_world_pos: None, screen_center_world_pos: None, heightmap_editor: heightmap_editor::State::new(), frame_minus_one: Frame::new(), frame_zero: Frame::new(), frame_zero_time_received: Instant::now(), kbots: Vec::new(), kinematic_projectiles_cache: FnvHashMap::default(), kinematic_projectiles: Vec::new(), explosions: Vec::new(), server_sec: 0.0, selected: FnvHashSet::default(), under_mouse: None, uitool: UiTool::None, players: FnvHashMap::default(), my_player_id: None, start_time: Instant::now(), last_frame: Instant::now(), fps: 144, unit_icon_distance: 200.0, } } pub fn handle_new_frame(&mut self, frame: Frame) { let time_between = self.frame_zero_time_received.elapsed(); log::trace!("receive: NewFrame after {:?}", time_between); self.frame_zero_time_received = Instant::now(); self.frame_minus_one = std::mem::replace(&mut self.frame_zero, frame); let sec = self.frame_zero.number as f32 / 10.0; let mut seed = sec * 3.141592; for explosion in self.frame_zero.explosions.iter() { seed += 1.0; self.explosions.push(Explosion { position: explosion.position, born_sec: sec, death_sec: sec + explosion.life_time, size: explosion.size, seed, }); } for proj_b in self.frame_zero.kinematic_projectiles_birth.iter() { self.kinematic_projectiles_cache .insert(proj_b.id, proj_b.clone()); } for dead in self.frame_zero.kinematic_projectiles_dead.iter() { self.kinematic_projectiles_cache.remove(dead); } self.selected = self .selected .difference(&self.frame_zero.kbots_dead.iter().cloned().collect()) .copied() .collect(); self.kbots = self .frame_zero .kbots .values() .map(|kbot| (kbot.clone(), ClientKbot::new(kbot.position))) .collect(); } pub fn interpolate(&mut self, threadpool: &rayon::ThreadPool, view_proj: &Matrix4) { let elapsed = self.frame_zero_time_received.elapsed().as_secs_f64(); //elapsed normalize between 0 and 1 if frame arrives every 100ms (0.1s) let lambda = (elapsed / 0.1) as f32; let i0 = lambda; let im = 1.0 - lambda; self.server_sec = (self.frame_zero.number as f32 * i0 + self.frame_minus_one.number as f32 * im) / 10.0; log::trace!("server_sec {}", self.server_sec); use rayon::prelude::*; fn test_screen( id: Id, position: Point3, view_proj: &Matrix4, ) -> Option<(Id, Vector2, f32)> { let p = position.to_homogeneous(); let r = view_proj * p; let margin = 1.2; let rw_with_margin = r.w * margin; //Keeping those of the clipped space in screen (-1 1, -1 1 , 0 1) if r.z > 0.0 && r.x < rw_with_margin && r.x > -rw_with_margin && r.y < rw_with_margin && r.y > -rw_with_margin { // log::debug!("z {}", r.w); // log::debug!("d {}", (position.coords - cam_pos.coords).norm()); Some((id, Vector2::new(r.x / r.w, r.y / r.w), r.w)) } else { None } } self.explosions = self .explosions .iter() .copied() .filter(|e| e.death_sec > self.server_sec) .collect(); let mut kbots = std::mem::replace(&mut self.kbots, Vec::new()); threadpool.install(|| { kbots.par_chunks_mut(1000).for_each(|chunk| { for (kbot_0, client_kbot0) in chunk.iter_mut() { let kbot_m_opt = self.frame_minus_one.kbots.get(&kbot_0.id); if let Some(kbot_m) = kbot_m_opt { client_kbot0.position = kbot_0.position * i0 + (im * kbot_m.position).coords; } let screen = test_screen(kbot_0.id, client_kbot0.position, view_proj); match screen { Some((_, screen_pos, distance_to_camera)) => { if let Some(kbot_m) = kbot_m_opt { client_kbot0.dir = kbot_0.dir * i0 + kbot_m.dir * im; client_kbot0.up = kbot_0.up * i0 + kbot_m.up * im; client_kbot0.weapon0_dir = kbot_0.weapon0_dir * i0 + kbot_m.weapon0_dir * im; client_kbot0.wheel0_angle = kbot_0.wheel0_angle * i0 + kbot_m.wheel0_angle * im; } client_kbot0.is_in_screen = true; let mat = utils::face_towards_dir( &client_kbot0.position.coords, &(client_kbot0.dir.normalize()), &client_kbot0.up, ); client_kbot0.trans = Some(mat); client_kbot0.distance_to_camera = distance_to_camera; client_kbot0.screen_pos = screen_pos; } _ => { client_kbot0.is_in_screen = false; } } } }); }); self.kbots = kbots; self.kinematic_projectiles.clear(); for kproj in self.kinematic_projectiles_cache.values_mut() { let pos = kproj.position_at(self.frame_minus_one.number + 1) * im + kproj.position_at(self.frame_zero.number + 1).coords * i0; self.kinematic_projectiles.push(pos); } self.players = self.frame_zero.players.clone(); } pub fn my_player(&self) -> Option<&Player> { self.my_player_id .map(|id| self.players.get(&id)) .unwrap_or(None) } pub fn near(&self) -> f32 { if self.position_smooth.z > 515.0 || self.position_smooth.coords.x < -500.0 || self.position_smooth.coords.y < -500.0 { 10.0 } else { 0.3 } } } ================================================ FILE: src/client/heightmap_editor.rs ================================================ use imgui::*; use na::Vector3; use std::collections::HashSet; use crate::gpu_obj::heightmap_gpu; use noise::{NoiseFn, Seedable}; #[derive(PartialEq, Clone, Copy)] pub enum Mode { Raise, Flatten, Median, Noise, Blur, } pub struct State { pub map_path: String, pub pen_radius: u32, pub pen_strength: f32, pub mode: Mode, noise: noise::Perlin, noise_freq: f64, min_z: f32, max_z: f32, } impl State { pub fn new() -> Self { State { map_path: "src/asset/map/map_example".to_owned(), pen_radius: 30, pen_strength: 2.0, mode: Mode::Raise, noise: noise::Perlin::new().set_seed(0), noise_freq: 10.0, min_z: 0.0, max_z: heightmap_gpu::MAX_Z, } } pub fn draw_ui(&mut self, ui: &Ui, heightmap_gpu: &mut heightmap_gpu::HeightmapGpu) { let pen_radius = &mut self.pen_radius; let pen_strength = &mut self.pen_strength; let mode = &mut self.mode; let noise_freq = &mut self.noise_freq; let noise_seed: &mut i32 = &mut (self.noise.seed() as i32); let mut update_noise = false; let min_z = &mut self.min_z; let max_z = &mut self.max_z; let edit_height_window = imgui::Window::new(im_str!("Heightmap editor")); edit_height_window .size([400.0, 300.0], imgui::Condition::FirstUseEver) .position([3.0, 415.0], imgui::Condition::FirstUseEver) .collapsed(false, imgui::Condition::FirstUseEver) .build(&ui, || { ui.radio_button(im_str!("Raise/Lower"), mode, Mode::Raise); ui.radio_button(im_str!("Flatten/Unflatten"), mode, Mode::Flatten); ui.radio_button(im_str!("Median"), mode, Mode::Median); ui.radio_button(im_str!("Blur"), mode, Mode::Blur); ui.radio_button(im_str!("Noise"), mode, Mode::Noise); if mode == &mut Mode::Noise { imgui::Slider::new(im_str!("noise frequency"), 0.0_f64..=200.0) .power(3.0) .build(&ui, noise_freq); update_noise = ui .drag_int(im_str!("noise seed"), noise_seed) .min(0) .build(); ui.separator(); } else { ui.separator(); } imgui::Slider::new(im_str!("pen radius"), 1..=1000).build(&ui, pen_radius); imgui::Slider::new(im_str!("pen strength"), 0.0..=10.0).build(&ui, pen_strength); ui.separator(); imgui::Slider::new(im_str!("min height"), 0.0..=heightmap_gpu::MAX_Z) .build(&ui, min_z); imgui::Slider::new(im_str!("max height"), 0.0..=heightmap_gpu::MAX_Z) .build(&ui, max_z); if ui.small_button(im_str!("Save")) { Self::save(heightmap_gpu, "src/asset/map/map_example"); } if ui.small_button(im_str!("Clear")) { for i in 0..heightmap_gpu.phy.width * heightmap_gpu.phy.height { heightmap_gpu.phy.texels[i as usize] = 50.0; } heightmap_gpu.update_rect( 0 as u32, 0 as u32, heightmap_gpu.phy.width as u32, heightmap_gpu.phy.height as u32, ); } if ui.small_button(im_str!("Load")) { Self::load(heightmap_gpu, "src/asset/map/map_example"); } }); // let window_selector = imgui::Window::new(im_str!("Map Selector")); // window_selector // .size([400.0, 200.0], imgui::Condition::FirstUseEver) // .position([400.0, 3.0], imgui::Condition::FirstUseEver) // .collapsed(false, imgui::Condition::FirstUseEver) // .build(&ui, || { // // Self::visit_dirs_for_selection(ui); // }); self.max_z = max_z.max(*min_z); if update_noise { self.noise = self.noise.set_seed(*noise_seed as u32); } } pub fn handle_user_input( &self, mouse_pressed: &HashSet, mouse_world_pos: &Vector3, heightmap_gpu: &mut heightmap_gpu::HeightmapGpu, ) { log::trace!("heightmap_editor handle_user_input"); { let pen_strength = self.pen_strength * if mouse_pressed.contains(&winit::event::MouseButton::Left) { 1.0 } else if mouse_pressed.contains(&winit::event::MouseButton::Right) { -1.0 } else { 0.0 }; if pen_strength != 0.0 { let (x, y) = (mouse_world_pos.x, mouse_world_pos.y); let middle_i = x.floor() as i32; let middle_j = y.floor() as i32; let pen_size = self.pen_radius as i32; let pen_size2 = pen_size * pen_size; let min_i = (middle_i - pen_size).max(0); let min_j = (middle_j - pen_size).max(0); let max_i = (middle_i + pen_size).min(heightmap_gpu.phy.width as i32 - 1); let max_j = (middle_j + pen_size).min(heightmap_gpu.phy.height as i32 - 1); let size_i = max_i - min_i + 1; let size_j = max_j - min_j + 1; if size_i > 0 && size_j > 0 { //let start = std::time::Instant::now(); let mut pixels = Vec::with_capacity((size_i * size_j) as usize); for j in min_j..=max_j { for i in min_i..=max_i { let falloff = 1.0 - (i32::pow(i - middle_i, 2) + i32::pow(j - middle_j, 2)) as f32 / pen_size2 as f32; pixels.push(( i, j, (i + j * heightmap_gpu.phy.width as i32) as usize, falloff.max(0.0), )); } } match self.mode { Mode::Raise => { for (_, _, index, falloff) in pixels { let power = pen_strength * falloff; heightmap_gpu.phy.texels[index] = (heightmap_gpu.phy.texels[index] + power) .min(self.max_z) .max(self.min_z); } } Mode::Flatten => { let mut average = 0.0; for (_, _, index, _) in &pixels { let z = heightmap_gpu.phy.texels[*index]; average += z; } average /= (size_i * size_j) as f32; for (_, _, index, falloff) in pixels { let power = (pen_strength * falloff) / 50.0; let z = heightmap_gpu.phy.texels[index] * (1.0 - power) + average * power; heightmap_gpu.phy.texels[index] = z.min(self.max_z).max(self.min_z); } } Mode::Noise => { for (i, j, index, falloff) in pixels { let power = pen_strength * falloff * self.noise.get([ (0.001 * self.noise_freq) * i as f64, (0.001 * self.noise_freq) * j as f64, ]) as f32; heightmap_gpu.phy.texels[index] = (heightmap_gpu.phy.texels[index] + power) .min(self.max_z) .max(self.min_z); } } Mode::Median => { let mut new_pix = Vec::new(); for (i, j, index, _) in pixels { let power = pen_strength / 10.0; let kernel = 4; let mut acc = Vec::new(); for ti in (-kernel + i).max(0) ..=(kernel + i).min(heightmap_gpu.phy.width as i32 - 1) { for tj in (-kernel + j).max(0) ..=(kernel + j).min(heightmap_gpu.phy.height as i32 - 1) { let tindex = (ti + tj * heightmap_gpu.phy.width as i32) as usize; acc.push( (heightmap_gpu.phy.texels[tindex] * 1000.0 * 1000.0) .floor() as i128, ); } } acc.sort(); new_pix.push(( index, heightmap_gpu.phy.texels[index] * (1.0 - power) + power * (acc[acc.len() / 2] as f64 / 1000000.0) as f32, )); } for (index, z) in new_pix { heightmap_gpu.phy.texels[index] = z.min(self.max_z).max(self.min_z); } } Mode::Blur => { let mut new_pix = Vec::new(); for (i, j, index, falloff) in pixels { let power = pen_strength * falloff / 10.0; let kernel = 1; let mut acc = 0.0; let mut tap = 0; for ti in (-kernel + i).max(0) ..=(kernel + i).min(heightmap_gpu.phy.width as i32 - 1) { for tj in (-kernel + j).max(0) ..=(kernel + j).min(heightmap_gpu.phy.height as i32 - 1) { tap += 1; let tindex = (ti + tj * heightmap_gpu.phy.width as i32) as usize; acc += heightmap_gpu.phy.texels[tindex]; } } let z = heightmap_gpu.phy.texels [(i + j * heightmap_gpu.phy.width as i32) as usize] * (1.0 - power) + power * (acc / tap as f32); new_pix.push((index, z)); } for (index, z) in new_pix { heightmap_gpu.phy.texels[index] = z.min(self.max_z).max(self.min_z); } } } heightmap_gpu.update_rect( min_i as u32, min_j as u32, size_i as u32, size_j as u32, ); // println!("handle hei took {}", start.elapsed().as_micros()); } } } } pub fn save(heightmap_gpu: &heightmap_gpu::HeightmapGpu, path: &str) { use std::fs::File; use std::io::BufWriter; use std::path::Path; let height_path = format!("{}/height.png", path); let height_path = Path::new(&height_path); let file = File::create(height_path).unwrap(); let ref mut w = BufWriter::new(file); let mut encoder = png::Encoder::new( w, heightmap_gpu.phy.width as u32, heightmap_gpu.phy.height as u32, ); encoder.set_color(png::ColorType::Grayscale); encoder.set_depth(png::BitDepth::Sixteen); let mut writer = encoder.write_header().unwrap(); let data: Vec = heightmap_gpu .phy .texels .iter() .map(|e| ((e / 511.0).min(1.0).max(0.0) * 65535.0) as u16) .flat_map(|e| vec![(e >> 8) as u8, e as u8]) .collect(); // 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. writer.write_image_data(&data).unwrap(); // Save let json_path = &format!("{}/data.json", path); use std::fs::OpenOptions; use std::io::prelude::*; use std::io::BufReader; let file = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(true) .open(json_path) .unwrap(); let mut buf_w = BufWriter::new(file); serde_json::to_writer_pretty(buf_w, &heightmap_gpu.phy.data); } pub fn load(heightmap_gpu: &mut heightmap_gpu::HeightmapGpu, path: &str) { use byteorder::{BigEndian, ReadBytesExt}; use std::fs::File; use std::io::Cursor; let height_path = format!("{}/height.png", path); let mut decoder = png::Decoder::new(File::open(&height_path).unwrap()); decoder.set_transformations(png::Transformations::IDENTITY); let (info, mut reader) = decoder.read_info().unwrap(); log::debug!("info: {:?}", info.width); log::debug!("height: {:?}", info.height); log::debug!("bit depth: {:?}", info.bit_depth); log::debug!("buffer size: {:?}", info.buffer_size()); let mut buf = vec![0; info.buffer_size()]; reader.next_frame(&mut buf).unwrap(); // Transform buffer into 16 bits slice. let mut buffer_u16 = vec![0; (info.width * info.height) as usize]; let mut buffer_cursor = Cursor::new(buf); buffer_cursor .read_u16_into::(&mut buffer_u16) .unwrap(); for i in 0..heightmap_gpu.phy.width * heightmap_gpu.phy.height { heightmap_gpu.phy.texels[i as usize] = buffer_u16[i as usize] as f32 / (65535.0 / 511.0); } heightmap_gpu.update_rect( 0 as u32, 0 as u32, heightmap_gpu.phy.width as u32, heightmap_gpu.phy.height as u32, ); } } ================================================ FILE: src/client/input_state.rs ================================================ use std::collections::HashSet; #[derive(Clone, Debug)] pub enum Drag { None, Start { x0: u32, y0: u32 }, Dragging { x0: u32, y0: u32, x1: u32, y1: u32 }, End { x0: u32, y0: u32, x1: u32, y1: u32 }, } #[derive(Clone, Debug)] pub struct InputState { pub key_pressed: HashSet, pub mouse_pressed: HashSet, pub key_trigger: HashSet, pub mouse_trigger: HashSet, pub key_release: HashSet, pub mouse_release: HashSet, pub drag: Drag, pub last_scroll: f32, pub cursor_pos: (u32, u32), pub cursor_offset: (i32, i32), } impl InputState { pub fn new() -> Self { InputState { key_pressed: HashSet::new(), mouse_pressed: HashSet::new(), key_trigger: HashSet::new(), mouse_trigger: HashSet::new(), key_release: HashSet::new(), mouse_release: HashSet::new(), last_scroll: 0.0, cursor_pos: (0, 0), cursor_offset: (0, 0), drag: Drag::None, } } pub fn update(&mut self) { self.key_trigger.clear(); self.mouse_trigger.clear(); self.mouse_release.clear(); self.key_release.clear(); if let Drag::End { .. } = self.drag { self.drag = Drag::None; } self.last_scroll = 0.0; self.cursor_offset = (0, 0); } } ================================================ FILE: src/client/misc.rs ================================================ use super::client::*; use crate::*; use unit_part_gpu::*; use super::uitool::UiTool; impl App { pub fn clear_gpu_instance_and_game_state(&mut self) { self.game_state.players.clear(); self.game_state.my_player_id = None; self.game_state.kbots.clear(); self.game_state.selected.clear(); self.game_state.explosions.clear(); self.game_state.kinematic_projectiles_cache.clear(); // self.unit_editor.root.children.clear(); self.health_bar.update_instance(&[], &self.gpu.device); self.unit_icon.update_instance(&[], &self.gpu.device); self.explosion_gpu.update_instance(&[], &self.gpu.device); for (model_gpu_state) in self.unit_part_gpu.states.iter_mut() { match model_gpu_state { ModelGpuState::Ready(model_gpu) => { model_gpu.update_instance_dirty(&[], &self.gpu.device) } _ => {} } } self.kinematic_projectile_gpu .update_instance_dirty(&[], &self.gpu.device); } pub fn visit_part_tree( part_tree: &unit::PartTree, root_trans: &Matrix4, unit_part_gpu: &mut UnitPartGpu, highlight_factor: f32, team: f32, con_completed: f32, weapon0_dir: Vector3, wheel0_angle: f32, ) { for c in part_tree.children.iter() { if let Some(placed_mesh) = &c.placed_mesh { let display_model = &placed_mesh; let combined = match &c.joint { unit::Joint::Fix => root_trans * c.parent_to_self, unit::Joint::AimWeapon0 => { let comb = root_trans * c.parent_to_self; utils::face_towards_dir( &Vector3::new(comb[12], comb[13], comb[14]), &weapon0_dir, &Vector3::new(0.0, 0.0, 1.0), ) } unit::Joint::Wheel0 => { let comb = root_trans * c.parent_to_self; comb * utils::face_towards_dir( &Vector3::new(0.0, 0.0, 0.0), &Vector3::new(0.0, 1.0, 0.0), &Vector3::new(f32::cos(wheel0_angle), 0.0, f32::sin(wheel0_angle)), ) } }; let for_display = combined * display_model.trans; // log::warn!( // "root {:?}\nlocal {:?}\ncombined {:?}\n", // root_trans, // mat, // combined // ); //TODO fix performance : HALF OF TIME IS IN GET_MUT match unit_part_gpu.get_mut(placed_mesh.mesh_index) { //} get_mut(&placed_mesh.mesh_path) { ModelGpuState::Ready(generic_cpu) => { let buf = &mut generic_cpu.instance_attr_cpu_buf; let isometry: Isometry3 = unsafe { na::convert_unchecked::, Isometry3>(for_display) }; let euler = isometry.rotation.euler_angles(); buf.push(for_display[12]); buf.push(for_display[13]); buf.push(for_display[14]); buf.push(euler.0); buf.push(euler.1); buf.push(euler.2); //Bit representation in decimal order //SELECTED TEAM TEAM //ex : team 5 and selected = 1 0 5 let bitpacked: f32 = highlight_factor * 100. + team; buf.push(bitpacked); buf.push(con_completed); } _ => {} } Self::visit_part_tree( c, &combined, unit_part_gpu, highlight_factor, team, con_completed, weapon0_dir, wheel0_angle, ); } else { Self::visit_part_tree( c, root_trans, unit_part_gpu, highlight_factor, team, con_completed, weapon0_dir, wheel0_angle, ); } } } pub fn upload_to_gpu(&mut self, view_proj: &Matrix4, encoder: &mut wgpu::CommandEncoder) { //Upload to gpu let upload_to_gpu_duration = time(|| { let unit_icon_distance = self.game_state.unit_icon_distance; //generic_gpu { for model_gpu in self.unit_part_gpu.states.iter_mut() { match model_gpu { ModelGpuState::Ready(model_gpu) => { model_gpu.instance_attr_cpu_buf.clear(); } _ => {} } } let identity = utils::face_towards_dir( &Vector3::new(300.0_f32, 100.0, 0.50), &Vector3::new(1.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 1.0), ); //Matrix4::identity(); let t = self.game_state.start_time.elapsed().as_secs_f32(); if self.main_menu == MainMode::UnitEditor { Self::visit_part_tree( &self.unit_editor.botdef.part_tree, &identity, &mut self.unit_part_gpu, 0.0, 0.0, 1.0, Vector3::new(f32::cos(t), f32::sin(t), f32::sin(t / 5.0) * 0.1).normalize(), (t * 2.0), ); } //Kbot { for (mobile, client_kbot) in self.game_state.kbots.iter_mut().filter(|e| { e.1.is_in_screen && e.1.distance_to_camera < unit_icon_distance }) { let mat = client_kbot.trans.unwrap(); let highlight_factor: f32 = match ( self.game_state.selected.contains(&mobile.id), self.game_state.under_mouse == Some(mobile.id), ) { (true, false) => 1.0, (false, false) => 0.0, (false, true) => 2.0, (true, true) => 3.0, }; let team = mobile.team; if let Some(botdef) = self.game_state.frame_zero.bot_defs.get(&mobile.botdef_id) { Self::visit_part_tree( &botdef.part_tree, &mat, &mut self.unit_part_gpu, highlight_factor, team as f32, mobile.con_completed, client_kbot.weapon0_dir, client_kbot.wheel0_angle, ); } } } for model_gpu in self.unit_part_gpu.states.iter_mut() { match model_gpu { ModelGpuState::Ready(model_gpu) => { model_gpu.update_instance_dirty_own_buffer(&self.gpu.device); } _ => {} } } } // //Kbot // { // self.vertex_attr_buffer_f32.clear(); // for mobile in self // .game_state // .kbots // .iter() // .filter(|e| e.is_in_screen && e.distance_to_camera < unit_icon_distance) // { // let mat = mobile.trans.unwrap(); // let is_selected = if self.game_state.selected.contains(&mobile.id.value) { // 1.0 // } else { // 0.0 // }; // let team = mobile.team; // self.vertex_attr_buffer_f32 // .extend_from_slice(mat.as_slice()); // self.vertex_attr_buffer_f32.push(is_selected); // self.vertex_attr_buffer_f32.push(team as f32) // } // self.kbot_gpu // .update_instance_dirty(&self.vertex_attr_buffer_f32[..], &self.gpu.device); // } //Kinematic Projectile self.vertex_attr_buffer_f32.clear(); for mobile in self.game_state.kinematic_projectiles.iter() { let mat = utils::face_towards_dir( &mobile.coords, &(Vector3::new(1.0, 0.0, 0.0)), &Vector3::new(0.0, 0.0, 1.0), ); let isometry: Isometry3 = unsafe { na::convert_unchecked::, Isometry3>(mat) }; let euler = isometry.rotation.euler_angles(); self.vertex_attr_buffer_f32.push(mat[12]); self.vertex_attr_buffer_f32.push(mat[13]); self.vertex_attr_buffer_f32.push(mat[14]); self.vertex_attr_buffer_f32.push(euler.0); self.vertex_attr_buffer_f32.push(euler.1); self.vertex_attr_buffer_f32.push(euler.2); self.vertex_attr_buffer_f32.push(99.); self.vertex_attr_buffer_f32.push(1.0) } self.kinematic_projectile_gpu .update_instance_dirty(&self.vertex_attr_buffer_f32[..], &self.gpu.device); //Arrow self.vertex_attr_buffer_f32.clear(); for arrow in self.game_state.frame_zero.arrows.iter() { let mat = Matrix4::face_towards( &arrow.position, &arrow.end, &Vector3::new(0.0, 0.0, 1.0), ); self.vertex_attr_buffer_f32 .extend_from_slice(mat.as_slice()); self.vertex_attr_buffer_f32 .extend_from_slice(&arrow.color[..3]); self.vertex_attr_buffer_f32 .push((arrow.end.coords - arrow.position.coords).magnitude()); } self.arrow_gpu .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device); //Unit life self.vertex_attr_buffer_f32.clear(); for (kbot, client_kbot) in self .game_state .kbots .iter() .filter(|e| e.1.is_in_screen && e.1.distance_to_camera < unit_icon_distance) { let distance = (self.game_state.position_smooth.coords - client_kbot.position.coords) .magnitude(); let alpha_range = 10.0; let max_dist = 100.0; let alpha = (1.0 + (max_dist - distance) / alpha_range) .min(1.0) .max(0.0) .powf(2.0); let alpha_range = 50.0; let size_factor = (0.3 + (max_dist - distance) / alpha_range) .min(1.0) .max(0.3) .powf(1.0); let botdef = self .game_state .frame_zero .bot_defs .get(&kbot.botdef_id) .unwrap(); let life = kbot.life as f32 / botdef.max_life as f32; let con_completed = kbot.con_completed; let display_life = life < 1.0; let display_con_completed = con_completed < 1.0; let display_one = display_life || display_con_completed; if alpha > 0.0 && display_one { let w = self.gpu.sc_desc.width as f32; let h = self.gpu.sc_desc.height as f32; let half_size = Vector2::new(20.0 / w, 3.0 / h) * size_factor; // u is direction above kbot in camera space // right cross camera_to_unit = u let camera_to_unit = client_kbot.position.coords - self.game_state.position_smooth.coords; let right = Vector3::new(1.0, 0.0, 0.0); let u = right.cross(&camera_to_unit).normalize(); let world_pos = client_kbot.position + u * botdef.radius * 1.5; let r = view_proj * world_pos.to_homogeneous(); let r = r / r.w; let offset = Vector2::new(r.x, r.y); let min = offset - half_size; let max = offset + half_size; let life = kbot.life as f32 / botdef.max_life as f32; self.vertex_attr_buffer_f32 .extend_from_slice(min.as_slice()); self.vertex_attr_buffer_f32 .extend_from_slice(max.as_slice()); self.vertex_attr_buffer_f32.push(life); self.vertex_attr_buffer_f32.push(alpha); //health self.vertex_attr_buffer_f32.push(0.0); let mut next_bar_offset = Vector2::new(0., -3. * half_size.y); if display_con_completed { let min = min + next_bar_offset; let max = max + next_bar_offset; self.vertex_attr_buffer_f32 .extend_from_slice(min.as_slice()); self.vertex_attr_buffer_f32 .extend_from_slice(max.as_slice()); self.vertex_attr_buffer_f32.push(con_completed); self.vertex_attr_buffer_f32.push(alpha); //con_completed self.vertex_attr_buffer_f32.push(1.0); next_bar_offset += Vector2::new(0., -3. * half_size.y); } } } self.health_bar .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device); //Icon self.vertex_attr_buffer_f32.clear(); for (kbot, client_kbot) in self .game_state .kbots .iter() .filter(|e| e.1.is_in_screen && e.1.distance_to_camera >= unit_icon_distance) { self.vertex_attr_buffer_f32 .extend_from_slice(client_kbot.screen_pos.as_slice()); //TODO f(distance) instead of 20.0 let size = ((1.0 / (client_kbot.distance_to_camera / unit_icon_distance)) * 15.0).max(4.0); self.vertex_attr_buffer_f32.push(size); let is_selected = self.game_state.selected.contains(&kbot.id); let team = if is_selected { -1.0 } else { kbot.team as f32 }; self.vertex_attr_buffer_f32.push(team); } self.unit_icon .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device); //Cursor Icon self.vertex_attr_buffer_f32.clear(); { let cursor_icon_size: i32 = 48; let cursor_icon_size_half = cursor_icon_size / 2; let cursor_icon_size_third = cursor_icon_size / 3; let (x, y) = self.input_state.cursor_pos; let (x, y) = (x as i32, y as i32); let (x, y) = (x + cursor_icon_size_third, y - cursor_icon_size_third); let min_screen = Vector2::new( (x - cursor_icon_size_half) as f32 / self.gpu.sc_desc.width as f32, (y - cursor_icon_size_half) as f32 / self.gpu.sc_desc.height as f32, ); let max_screen = Vector2::new( (x + cursor_icon_size_half) as f32 / self.gpu.sc_desc.width as f32, (y + cursor_icon_size_half) as f32 / self.gpu.sc_desc.height as f32, ); let mut min_texture = Vector2::new(0.0, 0.0); let mut max_texture = Vector2::new(0.0, 0.0); let mut index_to_vector = |x, y| { min_texture = Vector2::new(x as f32 * 0.25, y as f32 * 0.25); max_texture = min_texture + Vector2::new(0.25, 0.25); }; match self.game_state.uitool { UiTool::Repair => { index_to_vector(0, 0); } UiTool::Spawn(..) => { index_to_vector(1, 0); } UiTool::Guard => { index_to_vector(2, 1); } UiTool::Attack => { index_to_vector(1, 1); } _ => {} } self.vertex_attr_buffer_f32 .extend_from_slice(min_screen.as_slice()); self.vertex_attr_buffer_f32 .extend_from_slice(max_screen.as_slice()); self.vertex_attr_buffer_f32 .extend_from_slice(min_texture.as_slice()); self.vertex_attr_buffer_f32 .extend_from_slice(max_texture.as_slice()); } self.cursor_icon .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device); //Line self.vertex_attr_buffer_f32.clear(); { let mut count = 0; let see_all_order = self .input_state .key_pressed .contains(&winit::event::VirtualKeyCode::LShift); { for (kbot, client_kbot) in self.game_state.kbots.iter() { if see_all_order || self.game_state.selected.contains(&kbot.id) { fn add_line( view_proj: &Matrix4, buffer: &mut Vec, start: &Point3, end: &Point3, type_: f32, count: &mut i32, ) { let min = view_proj * start.to_homogeneous(); let max = view_proj * end.to_homogeneous(); if (min.z > 0.0 && min.x > -min.w && min.x < min.w && min.y > -min.w && min.y < min.w) || (max.z > 0.0 && max.x > -max.w && max.x < max.w && max.y > -max.w && max.y < max.w) { *count += 1; buffer.push(min.x / min.w); buffer.push(min.y / min.w); buffer.push(max.x / max.w); buffer.push(max.y / max.w); //0.0 is move line //1.0 is build line buffer.push(type_); buffer.push(0.0); } } if let Some(target) = kbot.move_target { add_line( view_proj, &mut self.vertex_attr_buffer_f32, &client_kbot.position, &target, 0.0, &mut count, ); } match kbot.current_command { mobile::Command::Build(id_builded) => { for target_kbot in self.game_state.frame_zero.kbots.get(&id_builded) { add_line( view_proj, &mut self.vertex_attr_buffer_f32, &client_kbot.position, &target_kbot.position, 1.0, &mut count, ); } } mobile::Command::Repair(id_builded) => { for target_kbot in self.game_state.frame_zero.kbots.get(&id_builded) { add_line( view_proj, &mut self.vertex_attr_buffer_f32, &client_kbot.position, &target_kbot.position, 2.0, &mut count, ); } } _ => {} } } } for i in (0..self.vertex_attr_buffer_f32.len()).step_by(6) { self.vertex_attr_buffer_f32[i + 5] = count as f32; } } } self.line_gpu .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device); //Explosions self.vertex_attr_buffer_f32.clear(); for explosion in self.game_state.explosions.iter() { let screen_pos = view_proj * explosion.position.to_homogeneous(); if screen_pos.z > 0.0 && screen_pos.x > -screen_pos.w && screen_pos.x < screen_pos.w && screen_pos.y > -screen_pos.w && screen_pos.y < screen_pos.w { self.vertex_attr_buffer_f32.push(explosion.position.x); self.vertex_attr_buffer_f32.push(explosion.position.y); self.vertex_attr_buffer_f32.push(explosion.position.z); self.vertex_attr_buffer_f32.push( (self.game_state.server_sec - explosion.born_sec) / (explosion.death_sec - explosion.born_sec), ); self.vertex_attr_buffer_f32.push(explosion.seed); self.vertex_attr_buffer_f32.push(explosion.size); } } self.explosion_gpu .update_instance(&self.vertex_attr_buffer_f32[..], &self.gpu.device); }); self.profiler .mix("upload_to_gpu", upload_to_gpu_duration, 20); } } ================================================ FILE: src/client/mod.rs ================================================ use crate::*; use na::{Isometry3, Matrix4, Point3, Vector2, Vector3, Vector4}; use gpu_obj::imgui_wgpu::Renderer; use gpu_obj::arrow_gpu::ArrowGpu; use gpu_obj::blit_texture::BlitTextureGpu; use gpu_obj::gpu; use gpu_obj::heightmap_gpu::HeightmapGpu; use gpu_obj::model_gpu::ModelGpu; use gpu_obj::trait_gpu::TraitGpu; use gpu_obj::water::WaterGpu; use imgui::*; use imgui_winit_support; use imgui_winit_support::WinitPlatform; mod camera; mod game_state; mod unit_part_gpu; use unit_part_gpu::UnitPartGpu; mod unit_editor; mod heightmap_editor; mod input_state; mod misc; mod play; mod render; mod uitool; use crate::heightmap_phy; use log::info; use spin_sleep::LoopHelper; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use std::time::Instant; use utils::time; use wgpu::{BufferMapAsyncResult, Extent3d, SwapChain, TextureFormat}; use winit::event::WindowEvent; pub struct StartClient { pub bind: String, } pub struct StartServer { pub bind: String, } pub enum FromClient { PlayerInput(frame::FrameEventFromPlayer), StartServer(StartServer), StartClient(StartClient), DisconnectServer, DisconnectClient, } struct ImguiWrap { imgui: imgui::Context, platform: WinitPlatform, renderer: Renderer, } #[derive(Clone)] enum RenderEvent { ChangeMode { from: MainMode, to: MainMode }, } #[derive(PartialEq, Clone, Copy)] pub enum MainMode { Home, Play, UnitEditor, MapEditor, MultiplayerLobby, } #[derive(PartialEq, Clone, Copy)] pub enum NetMode { Offline, Server, Client, } pub struct App { //Wgpu gpu: gpu::WgpuState, first_color_att_view: wgpu::TextureView, secon_color_att_view: wgpu::TextureView, normal_att_view: wgpu::TextureView, forward_depth: wgpu::TextureView, position_att: wgpu::Texture, position_att_view: wgpu::TextureView, heightmap_gpu: HeightmapGpu, water_gpu: WaterGpu, unit_part_gpu: UnitPartGpu, arrow_gpu: ArrowGpu, kinematic_projectile_gpu: ModelGpu, vertex_attr_buffer_f32: Vec, bind_group: wgpu::BindGroup, bind_group_layout: wgpu::BindGroupLayout, ub_camera_mat: wgpu::Buffer, postfx: gpu_obj::post_fx::PostFx, postfxaa: gpu_obj::post_fxaa::PostFxaa, post_bicopy: gpu_obj::texture_view_bicopy::TextureViewBiCopy, health_bar: gpu_obj::health_bar::HealthBarGpu, line_gpu: gpu_obj::line::LineGpu, cursor_icon: BlitTextureGpu, unit_icon: gpu_obj::unit_icon::UnitIconGpu, explosion_gpu: gpu_obj::explosion::ExplosionGpu, game_state: game_state::State, input_state: input_state::InputState, imgui_wrap: ImguiWrap, main_menu: MainMode, net_mode: NetMode, unit_editor: unit_editor::UnitEditor, sender_to_client: crossbeam_channel::Sender, receiver_to_client: crossbeam_channel::Receiver, sender_to_event_loop: crossbeam_channel::Sender, sender_from_client_to_manager: crossbeam_channel::Sender, receiver_notify: crossbeam_channel::Receiver>, watcher: notify::RecommendedWatcher, mailbox: Vec, loop_helper: LoopHelper, profiler: frame::ProfilerMap, global_info: Option, threadpool: rayon::ThreadPool, frame_count: i32, } impl App { pub fn new( window: winit::window::Window, sender_to_client: crossbeam_channel::Sender, receiver_to_client: crossbeam_channel::Receiver, sender_to_event_loop: crossbeam_channel::Sender, sender_from_client_to_manager: crossbeam_channel::Sender, ) -> (Self) { log::trace!("App init"); let mut gpu = gpu::WgpuState::new(window); let mut init_encoder = gpu .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); let bind_group_layout = gpu.device .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::UniformBuffer { dynamic: false }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 2, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); // Create the texture let size = 256u32; let texels = procedural_texels::create_texels(size as usize); let texture_extent = wgpu::Extent3d { width: size, height: size, depth: 1, }; let texture = gpu.device.create_texture(&wgpu::TextureDescriptor { size: texture_extent, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, }); let texture_view = texture.create_default_view(); let temp_buf = gpu .device .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * size, image_height: size, }, wgpu::TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); // Create other resources let sampler = gpu.device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::MirrorRepeat, address_mode_v: wgpu::AddressMode::MirrorRepeat, address_mode_w: wgpu::AddressMode::MirrorRepeat, mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Nearest, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let mx_total = camera::create_view_proj( gpu.sc_desc.width as f32 / gpu.sc_desc.height as f32, 1.0, &Point3::new(0.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 0.0), ); let mx_ref: &[f32] = mx_total.as_slice(); let mx_view = camera::create_view(&Point3::new(0.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 0.0)); let mx_view_ref: &[f32] = mx_view.as_slice(); let mx_normal = camera::create_normal(&Point3::new(0.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 0.0)); let mx_normal_ref: &[f32] = mx_normal.as_slice(); let mut filler = Vec::new(); filler.extend_from_slice(mx_ref); filler.extend_from_slice(mx_view_ref); //TODO reuse camera.rs code filler.extend_from_slice(mx_ref); filler.extend_from_slice(mx_normal_ref); filler.extend_from_slice(&[ 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, ]); // mat4 cor_proj_view; // mat4 u_View; // mat4 u_proj; // mat4 u_Normal; // vec2 mouse_pos; // vec2 resolution; // vec2 inv_resolution; // vec2 start_drag; // float radius // float pen_strength // vec2 mapSize; let ub_camera_mat = gpu .device .create_buffer_mapped( 16 * 4 + 12, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&filler[..]); // Create bind group let bind_group = gpu.device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::Buffer { buffer: &ub_camera_mat, range: 0..(4 * 16 + 12) * 4, }, }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::TextureView(&texture_view), }, wgpu::Binding { binding: 2, resource: wgpu::BindingResource::Sampler(&sampler), }, ], }); log::trace!(" imgui_wrap init"); let imgui_wrap = { // imgui let mut imgui = imgui::Context::create(); let mut platform = imgui_winit_support::WinitPlatform::init(&mut imgui); platform.attach_window( imgui.io_mut(), &gpu.window, imgui_winit_support::HiDpiMode::Locked(1.0), ); imgui.set_ini_filename(None); let font_size = (13.0 * gpu.hidpi_factor) as f32; imgui.io_mut().font_global_scale = (1.0) as f32; imgui.io_mut().mouse_draw_cursor = true; imgui.fonts().add_font(&[FontSource::DefaultFontData { config: Some(imgui::FontConfig { oversample_h: 1, pixel_snap_h: true, size_pixels: font_size, ..Default::default() }), }]); // imgui <-> wgpu let renderer = Renderer::new( &mut imgui, &mut gpu.device, &mut gpu.queue, gpu.sc_desc.format, None, ); ImguiWrap { imgui, platform, renderer, } }; let format: TextureFormat = gpu.sc_desc.format; let heightmap_gpu = HeightmapGpu::new( &gpu.device, &mut init_encoder, format, &bind_group_layout, heightmap_phy::HeightmapPhy::new(2048, 2048), ); let kinematic_projectile_gpu = ModelGpu::new( &model::open_obj("./src/asset/3d/small_sphere.obj").unwrap(), &gpu.device, format, &bind_group_layout, ); let arrow_gpu = ArrowGpu::new( &model::open_obj("./src/asset/3d/arrow.obj").unwrap(), &gpu.device, format, &bind_group_layout, ); let mut unit_part_gpu = UnitPartGpu::new(); unit_part_gpu.append(Path::new("./src/asset/3d/cube.obj").to_owned()); unit_part_gpu.append(Path::new("./src/asset/3d/axis_debug.obj").to_owned()); unit_part_gpu.append(Path::new("./src/asset/3d/small_sphere.obj").to_owned()); let health_bar = gpu_obj::health_bar::HealthBarGpu::new(&gpu.device, format, &bind_group_layout); let line_gpu = gpu_obj::line::LineGpu::new(&gpu.device, format, &bind_group_layout); let cursor_icon = BlitTextureGpu::new( &mut init_encoder, &gpu.device, format, &bind_group_layout, crate::utils::ImageRGBA8::open("./src/asset/2d/cursor_icons.png"), ); let unit_icon = gpu_obj::unit_icon::UnitIconGpu::new(&gpu.device, format, &bind_group_layout); let depth_texture = gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: gpu.sc_desc.width, height: gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Depth32Float, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, }); let forward_depth = depth_texture.create_default_view(); let position_att = gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: gpu.sc_desc.width, height: gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba32Float, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_SRC, }); let position_att_view = position_att.create_default_view(); let game_state = game_state::State::new(); let (receiver_notify, watcher) = { use crossbeam_channel::unbounded; use notify::{watcher, RecursiveMode, Result}; use std::time::Duration; let (tx, rx) = unbounded(); use notify::Watcher; // Automatically select the best implementation for your platform. // You can also access each implementation directly e.g. INotifyWatcher. let mut watcher = watcher(tx, Duration::from_millis(500)).unwrap(); // Add a path to be watched. All files and directories at that path and // below will be monitored for changes. watcher .watch(std::env::current_dir().unwrap(), RecursiveMode::Recursive) .unwrap(); (rx, watcher) }; let first_color_att = gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: gpu.sc_desc.width, height: gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Bgra8UnormSrgb, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_SRC, }); let first_color_att_view = first_color_att.create_default_view(); let secon_color_att = gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: gpu.sc_desc.width, height: gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Bgra8UnormSrgb, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_SRC, }); let secon_color_att_view = secon_color_att.create_default_view(); let normal_att = gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: gpu.sc_desc.width, height: gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rg16Float, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, }); let normal_att_view = normal_att.create_default_view(); let explosion_gpu = gpu_obj::explosion::ExplosionGpu::new( &mut init_encoder, &gpu.device, format, &bind_group_layout, &position_att_view, &normal_att_view, ); let water_gpu = WaterGpu::new( &gpu.device, format, &bind_group_layout, &secon_color_att_view, &position_att_view, ); let postfx = gpu_obj::post_fx::PostFx::new( &gpu.device, &bind_group_layout, format, &position_att_view, ); let postfxaa = gpu_obj::post_fxaa::PostFxaa::new( &gpu.device, &bind_group_layout, format, &first_color_att_view, ); let post_bicopy = gpu_obj::texture_view_bicopy::TextureViewBiCopy::new( &gpu.device, &bind_group_layout, format, &secon_color_att_view, ); let mut unit_editor = unit_editor::UnitEditor::new(); Self::load_botdef_in_editor( "src/asset/botdef/unit_example.json", &mut unit_editor, &mut unit_part_gpu, ); gpu.queue.submit(&[init_encoder.finish()]); // Done let this = App { gpu, bind_group, bind_group_layout, ub_camera_mat, unit_part_gpu, kinematic_projectile_gpu, arrow_gpu, heightmap_gpu, water_gpu, vertex_attr_buffer_f32: Vec::new(), first_color_att_view, secon_color_att_view, normal_att_view, forward_depth, position_att_view, position_att, postfx, postfxaa, post_bicopy, health_bar, line_gpu, cursor_icon, unit_icon, explosion_gpu, game_state, input_state: input_state::InputState::new(), imgui_wrap, main_menu: MainMode::Home, net_mode: NetMode::Offline, unit_editor, sender_to_client, receiver_to_client, sender_to_event_loop, sender_from_client_to_manager, receiver_notify, watcher, mailbox: Vec::new(), loop_helper: LoopHelper::builder().build_with_target_rate(144.0), profiler: frame::ProfilerMap::new(), global_info: None, threadpool: rayon::ThreadPoolBuilder::new() // .num_threads(8) .build() .unwrap(), frame_count: 0, }; (this) } fn resize(&mut self) -> Option { log::trace!("resize"); let normal_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: self.gpu.sc_desc.width, height: self.gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rg16Float, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, }); self.normal_att_view = normal_att.create_default_view(); let first_color_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: self.gpu.sc_desc.width, height: self.gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Bgra8UnormSrgb, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_SRC, }); self.first_color_att_view = first_color_att.create_default_view(); let secon_color_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: self.gpu.sc_desc.width, height: self.gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Bgra8UnormSrgb, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_SRC, }); self.secon_color_att_view = secon_color_att.create_default_view(); self.postfxaa .update_last_pass_view(&self.gpu.device, &self.first_color_att_view); self.post_bicopy .update_last_pass_view(&self.gpu.device, &self.secon_color_att_view); let depth_texture = self.gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: self.gpu.sc_desc.width, height: self.gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Depth32Float, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, }); self.forward_depth = depth_texture.create_default_view(); let position_att = self.gpu.device.create_texture(&wgpu::TextureDescriptor { size: wgpu::Extent3d { width: self.gpu.sc_desc.width, height: self.gpu.sc_desc.height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba32Float, usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_SRC, }); self.position_att_view = position_att.create_default_view(); self.water_gpu.update_bind_group( &self.gpu.device, &self.secon_color_att_view, &self.position_att_view, ); self.postfx .update_pos_att_view(&self.gpu.device, &self.position_att_view); self.position_att = position_att; self.explosion_gpu.update_bind_group( &self.gpu.device, &self.position_att_view, &self.normal_att_view, ); None } pub fn handle_winit_event(&mut self, _event: &winit::event::Event<()>) { log::trace!("[client.rs] update {:?}", _event); use winit::event; self.imgui_wrap.platform.handle_event( self.imgui_wrap.imgui.io_mut(), &self.gpu.window, _event, ); //Low level match _event { event::Event::WindowEvent { event: WindowEvent::Resized(size), .. } => { let physical = size.to_physical(self.gpu.hidpi_factor); info!("Resizing to logical {:?} physical {:?}", size, physical); self.gpu.sc_desc.width = physical.width.round() as u32; self.gpu.sc_desc.height = physical.height.round() as u32; self.gpu.swap_chain = self .gpu .device .create_swap_chain(&self.gpu.surface, &self.gpu.sc_desc); let command_buf = self.resize(); if let Some(command_buf) = command_buf { self.gpu.queue.submit(&[command_buf]); } } event::Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => { self.sender_to_event_loop.send(EventLoopMsg::Stop).unwrap(); } WindowEvent::KeyboardInput { input: event::KeyboardInput { virtual_keycode: Some(vkc), state: event::ElementState::Pressed, .. }, .. } => { self.input_state.key_pressed.insert(vkc.clone()); self.input_state.key_trigger.insert(vkc.clone()); } WindowEvent::KeyboardInput { input: event::KeyboardInput { virtual_keycode: Some(vkc), state: event::ElementState::Released, .. }, .. } => { self.input_state.key_pressed.remove(vkc); self.input_state.key_release.insert(vkc.clone()); } WindowEvent::MouseWheel { delta: event::MouseScrollDelta::LineDelta(_, dy), .. } => { self.input_state.last_scroll = *dy; } WindowEvent::CursorMoved { position, .. } => { let position = position.to_physical(self.gpu.hidpi_factor); let (old_x, old_y) = self.input_state.cursor_pos; self.input_state.cursor_offset = ( position.x as i32 - old_x as i32, position.y as i32 - old_y as i32, ); self.input_state.cursor_pos = (position.x as u32, position.y as u32); match self.input_state.drag { input_state::Drag::Start { x0, y0 } | input_state::Drag::Dragging { x0, y0, .. } => { self.input_state.drag = input_state::Drag::Dragging { x0, y0, x1: self.input_state.cursor_pos.0 as u32, y1: self.input_state.cursor_pos.1 as u32, }; } _ => {} } } WindowEvent::MouseInput { state, button, .. } => { if !self.imgui_wrap.imgui.io().want_capture_mouse { if let &winit::event::ElementState::Pressed = state { self.input_state.mouse_pressed.insert(*button); self.input_state.mouse_trigger.insert(*button); if let event::MouseButton::Left = button { self.input_state.drag = input_state::Drag::Start { x0: self.input_state.cursor_pos.0 as u32, y0: self.input_state.cursor_pos.1 as u32, } }; } else { self.input_state.mouse_pressed.remove(button); self.input_state.mouse_release.insert(*button); if let event::MouseButton::Left = button { match self.input_state.drag { input_state::Drag::Dragging { x0, y0, .. } => { self.input_state.drag = input_state::Drag::End { x0, y0, x1: self.input_state.cursor_pos.0 as u32, y1: self.input_state.cursor_pos.1 as u32, }; } _ => { self.input_state.drag = input_state::Drag::None; } } } } } } _ => { // log::warn!("{:?}", x); } }, _ => (), } } pub fn map_read_async_msg(&mut self, vec: Vec, usage: String) { let to_update = match usage.as_ref() { "screen_center_world_pos" => &mut self.game_state.screen_center_world_pos, "mouse_world_pos" => &mut self.game_state.mouse_world_pos, _ => &mut self.game_state.mouse_world_pos, }; if vec.len() == 4 && vec[0] >= 0.0 { std::mem::replace(to_update, Some(Vector3::new(vec[0], vec[1], vec[2]))); } else { std::mem::replace(to_update, None); } } pub fn receive(&mut self) { let msg: std::result::Result< notify::Result, crossbeam_channel::TryRecvError, > = self.receiver_notify.try_recv(); match msg { Ok(Ok(event)) => { log::trace!("notify {:?}", event); if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "post_ui.frag" || name.to_os_string() == "post.vert" }) }) { log::info!("Reloading post.vert/post_ui.frag"); self.postfx.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "post_fxaa.frag" || name.to_os_string() == "post.vert" }) }) { log::info!("Reloading post.vert/post_fxaa.frag"); self.postfxaa.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "heightmap.frag" || name.to_os_string() == "heightmap.vert" }) }) { log::info!("Reloading heightmap.vert/heightmap.frag"); self.heightmap_gpu.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "cube_instanced.frag" || name.to_os_string() == "cube_instanced.vert" }) }) { log::info!("Reloading cube_instanced.vert/cube_instanced.frag"); for (model_gpu_state) in self.unit_part_gpu.states.iter_mut() { match model_gpu_state { unit_part_gpu::ModelGpuState::Ready(model_gpu) => model_gpu .reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ), _ => {} } } } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "arrow.frag" || name.to_os_string() == "arrow.vert" }) }) { log::info!("Reloading arrow.vert/arrow.frag"); self.arrow_gpu.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "health_bar.frag" || name.to_os_string() == "health_bar.vert" }) }) { log::info!("Reloading health_bar.vert/health_bar.frag"); self.health_bar.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "unit_icon.frag" || name.to_os_string() == "unit_icon.vert" }) }) { log::info!("Reloading unit_icon.vert/unit_icon.frag"); self.unit_icon.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "explosion.frag" || name.to_os_string() == "explosion.vert" }) }) { log::info!("Reloading explosion.vert/explosion.frag"); self.explosion_gpu.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "line.frag" || name.to_os_string() == "line.vert" }) }) { log::info!("Reloading line.vert/line.frag"); self.line_gpu.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "water.frag" || name.to_os_string() == "water.vert" }) }) { log::info!("Reloading water.vert/water.frag"); self.water_gpu.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } if event.paths.iter().any(|p| { p.file_name().iter().any(|name| { name.to_os_string() == "blit_texture.frag" || name.to_os_string() == "blit_texture.vert" }) }) { log::info!("Reloading blit_texture.vert/blit_texture.frag"); self.cursor_icon.reload_shader( &self.gpu.device, &self.bind_group_layout, self.gpu.sc_desc.format, ); } } _ => {} } let msgs: Vec<_> = self.receiver_to_client.try_iter().collect(); for msg in msgs { { match msg { ToClient::MapReadAsyncMessage { vec, usage } => { log::trace!("receive: MapReadAsyncMessage"); self.map_read_async_msg(vec, usage); } ToClient::NewFrame(frame) => { self.game_state.handle_new_frame(frame); } ToClient::GlobalInfo(global_info) => self.global_info = Some(global_info), } } } } } ================================================ FILE: src/client/play.rs ================================================ use super::client::*; use crate::botdef; use crate::frame::FrameEventFromPlayer; use crate::frame::Player; use crate::*; use fnv::{FnvHashMap, FnvHashSet}; use imgui::*; use mobile::*; use na::{Isometry3, Matrix4, Point3, Vector2, Vector3, Vector4}; use std::time::{Duration, Instant}; use utils::*; impl App { pub fn init_play(&mut self) { match self.net_mode { NetMode::Offline | NetMode::Server => { self.clear_gpu_instance_and_game_state(); self.game_state.position = Point3::new(300.0, 100.0, self.heightmap_gpu.phy.z(300.0, 100.0) + 50.0); self.game_state.dir = Vector3::new(0.0, 0.3, -1.0); let mut player_me = Player::new(); let mut player_ennemy = Player::new(); player_ennemy.team = 1; let mut kbots = FnvHashMap::default(); let tank_example = Self::load_botdef_on_disk("src/asset/botdef/unit_example.json").unwrap(); let building_example = Self::load_botdef_on_disk("src/asset/botdef/building_example.json").unwrap(); for i in (100..300).step_by(4) { for j in (100..500).step_by(4) { let m = mobile::KBot::new( Point3::new(i as f32, j as f32, 100.0), &tank_example, player_me.id, ); player_me.kbots.insert(m.id); kbots.insert(m.id, m); } } for i in (320..520).step_by(4) { for j in (100..500).step_by(4) { let mut m = mobile::KBot::new( Point3::new(i as f32, j as f32, 100.0), &tank_example, player_ennemy.id, ); m.team = 1; player_ennemy.kbots.insert(m.id); kbots.insert(m.id, m); } } log::info!("Starting a game with {} bots", kbots.len()); self.game_state.my_player_id = Some(player_me.id); self.game_state.players.insert(player_me.id, player_me); self.game_state .players .insert(player_ennemy.id, player_ennemy); let mut bot_defs = FnvHashMap::default(); bot_defs.insert(tank_example.id, tank_example); bot_defs.insert(building_example.id, building_example); let mut moddef = crate::moddef::ModDef { units_id: bot_defs.keys().copied().collect(), con_map: FnvHashMap::default(), }; let replacer = FrameEventFromPlayer::ReplaceFrame(frame::Frame { number: 0, players: self.game_state.players.clone(), moddef, kbots, kbots_dead: HashSet::default(), kinematic_projectiles_dead: Vec::new(), kinematic_projectiles_birth: Vec::new(), kinematic_projectiles: self.game_state.kinematic_projectiles_cache.clone(), arrows: Vec::new(), explosions: Vec::new(), heightmap_phy: Some(self.heightmap_gpu.phy.clone()), frame_profiler: frame::ProfilerMap::new(), bot_defs, }); let _ = self .sender_from_client_to_manager .try_send(client::FromClient::PlayerInput(replacer)); } NetMode::Client => { self.clear_gpu_instance_and_game_state(); self.game_state.position = Point3::new(300.0, 100.0, self.heightmap_gpu.phy.z(300.0, 100.0) + 50.0); self.game_state.dir = Vector3::new(0.0, 0.3, -1.0); self.game_state.my_player_id = self .game_state .frame_zero .players .values() .filter(|p| p.team == 1) .map(|p| p.id.clone()) .next(); } } } pub fn handle_play( &mut self, delta_sim_sec: f32, encoder: &mut wgpu::CommandEncoder, view_proj: &Matrix4, ) { //Interpolate let interp_duration = time(|| { self.game_state.interpolate(&self.threadpool, &view_proj); }); // Selection on screen let selection_screen = time(|| { //Under_cursor { let (x, y) = self.input_state.cursor_pos; let (x, y) = (x as i32, y as i32); let (x0, y0) = (x - 7, y - 7); let (x1, y1) = (x + 7, y + 7); let min_x = (x0.min(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0; let min_y = (y0.min(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0; let max_x = (x0.max(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0; let max_y = (y0.max(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0; if let Some(mpos) = self.game_state.mouse_world_pos { let mut closest = None; let mut screen_only = true; let mut distance = 999999999.0_f32; for e in self.game_state.kbots.iter() { if e.1.is_in_screen { let test3d = e.1.distance_to_camera < self.game_state.unit_icon_distance; let dist = || (e.1.position.coords - mpos).magnitude_squared(); //TODO replace 1.0 with bot radius if test3d && dist() < 1.0 && dist() < distance { closest = Some(e.0.id); distance = dist(); screen_only = false } else if screen_only && e.1.screen_pos.x > min_x && e.1.screen_pos.x < max_x && e.1.screen_pos.y < max_y && e.1.screen_pos.y > min_y { let dist = { let dx = x as f32 - e.1.screen_pos.x; let dy = y as f32 - e.1.screen_pos.y; (dx * dx + dy * dy).sqrt() * 3000.0 }; if dist < distance { closest = Some(e.0.id); distance = dist } } } } self.game_state.under_mouse = closest; } else { self.game_state.under_mouse = None; } } if let Some(me) = self.game_state.my_player() { //Selection square if let input_state::Drag::End { x0, y0, x1, y1 } = self.input_state.drag { let start_sel = std::time::Instant::now(); let min_x = (x0.min(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0; let min_y = (y0.min(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0; let max_x = (x0.max(x1) as f32 / self.gpu.sc_desc.width as f32) * 2.0 - 1.0; let max_y = (y0.max(y1) as f32 / self.gpu.sc_desc.height as f32) * 2.0 - 1.0; let selected: FnvHashSet> = self .game_state .kbots .iter() .filter(|e| { e.1.is_in_screen && me.kbots.contains(&e.0.id) && e.1.screen_pos.x > min_x && e.1.screen_pos.x < max_x && e.1.screen_pos.y < max_y && e.1.screen_pos.y > min_y }) .map(|e| e.0.id) .collect(); log::trace!("Selection took {}us", start_sel.elapsed().as_micros()); self.game_state.selected = selected; } else if self .input_state .mouse_release .contains(&winit::event::MouseButton::Left) { //Single Picking if let Some(id) = self.game_state.under_mouse { if me.kbots.contains(&id) { self.game_state.selected.clear(); self.game_state.selected.insert(id); } else { self.game_state.selected.clear(); } } else { self.game_state.selected.clear(); } } } }); self.profiler.mix("interp", interp_duration, 20); self.profiler.mix("selection_screen", selection_screen, 20); } } ================================================ FILE: src/client/render.rs ================================================ use super::client::*; use super::uitool::UiTool; use crate::frame; use crate::frame::FrameEventFromPlayer; use crate::frame::Player; use crate::*; use imgui::*; use na::{IsometryMatrix3, Matrix4, Point3, Vector2, Vector3, Vector4}; use std::time::Duration; use std::time::Instant; use unit_part_gpu; use unit_part_gpu::UnitPartGpu; use utils::time; use wgpu::{BufferMapAsyncResult, Extent3d}; impl App { pub fn render(&mut self) { if self.frame_count == 1 { self.gpu.window.set_maximized(true); } self.frame_count += 1; let frame_time = self.game_state.last_frame.elapsed(); self.profiler.mix("frame_time", frame_time, 20); log::trace!("sleep"); self.loop_helper.loop_sleep(); self.loop_helper.loop_start(); log::trace!("render"); let capped_frame_time = self.game_state.last_frame.elapsed(); self.profiler .mix("capped_frame_time", capped_frame_time, 20); self.game_state.last_frame = Instant::now(); let sim_sec = capped_frame_time.as_secs_f32(); let mailbox = self.mailbox.clone(); self.mailbox.clear(); for mail in mailbox { match mail { RenderEvent::ChangeMode { from, to: MainMode::MapEditor, } => { self.clear_gpu_instance_and_game_state(); self.game_state.position = Point3::new(1024.0, 400.0, 1100.0); self.game_state.dir = Vector3::new(0.0, 0.3, -1.0); } RenderEvent::ChangeMode { from, to: MainMode::Play, } => { self.init_play(); } RenderEvent::ChangeMode { from, to: MainMode::Home, } => { self.clear_gpu_instance_and_game_state(); self.game_state.position = Point3::new(200.0, 100.0, 50.0); self.game_state.dir = Vector3::new(0.0, 0.3, -1.0); match self.net_mode { NetMode::Offline | NetMode::Server => { let replacer = FrameEventFromPlayer::ReplaceFrame(frame::Frame::new()); let _ = self .sender_from_client_to_manager .try_send(client::FromClient::PlayerInput(replacer)); } NetMode::Client => {} } } RenderEvent::ChangeMode { from, to: MainMode::MultiplayerLobby, } => {} RenderEvent::ChangeMode { from, to: MainMode::UnitEditor, } => { self.init_unit_editor(); } _ => { log::info!("Something else"); } } } if self .input_state .key_trigger .contains(&winit::event::VirtualKeyCode::Escape) { let next_mode = MainMode::Home; if self.main_menu != next_mode { self.mailbox.push(RenderEvent::ChangeMode { from: self.main_menu, to: next_mode, }); self.main_menu = next_mode; } } let mode_with_camera = [MainMode::Play, MainMode::MapEditor]; // Camera Movements if mode_with_camera.contains(&self.main_menu) { self.rts_camera(sim_sec); } if self.main_menu == MainMode::UnitEditor { self.orbit_camera(sim_sec); } let heightmap_editor_duration = time(|| { if let MainMode::MapEditor = self.main_menu { if let Some(mouse_world_pos) = self.game_state.mouse_world_pos { self.game_state.heightmap_editor.handle_user_input( &self.input_state.mouse_pressed, &mouse_world_pos, &mut self.heightmap_gpu, ); } } }); self.profiler .mix("heightmap_editor", heightmap_editor_duration, 20); //Render let mut encoder_render = self .gpu .device .create_command_encoder(&wgpu::CommandEncoderDescriptor { todo: 0 }); //Load pending generic gpu for (index, generic_gpu_state) in self.unit_part_gpu.states.iter_mut().enumerate() { if let unit_part_gpu::ModelGpuState::ToLoad(tri_list) = generic_gpu_state { let mut generic_gpu = ModelGpu::new( &tri_list, &self.gpu.device, self.gpu.sc_desc.format, &self.bind_group_layout, ); log::debug!("Load pending generic gpu {:?} ", index); let mut generic_gpu_state_new = unit_part_gpu::ModelGpuState::Ready(generic_gpu); std::mem::replace(generic_gpu_state, generic_gpu_state_new); } } let view_proj = camera::create_view_proj( self.gpu.sc_desc.width as f32 / self.gpu.sc_desc.height as f32, self.game_state.near(), &self.game_state.position_smooth, &self.game_state.dir_smooth, ); if self.main_menu == MainMode::Play { self.handle_play(sim_sec, &mut encoder_render, &view_proj); } self.upload_to_gpu(&view_proj, &mut encoder_render); let heightmap_gpu_step_duration = time(|| { self.heightmap_gpu .step(&self.gpu.device, &mut encoder_render); }); self.profiler .mix("heightmap_gpu_step", heightmap_gpu_step_duration, 20); let mut start_drag = ( self.input_state.cursor_pos.0 as f32, self.input_state.cursor_pos.1 as f32, ); if let MainMode::Play = self.main_menu { if let input_state::Drag::Dragging { x0, y0, .. } = self.input_state.drag { start_drag = (x0 as f32, y0 as f32); } } let radius = if self.main_menu == MainMode::MapEditor { self.game_state.heightmap_editor.pen_radius as f32 } else { 0.0 }; let mut filler = camera::create_camera_uniform_vec( (self.gpu.sc_desc.width, self.gpu.sc_desc.height), self.game_state.near(), &self.game_state.position_smooth, &self.game_state.dir_smooth, ); filler.extend_from_slice(&[ self.input_state.cursor_pos.0 as f32, self.input_state.cursor_pos.1 as f32, self.gpu.sc_desc.width as f32, self.gpu.sc_desc.height as f32, 1.0 / self.gpu.sc_desc.width as f32, 1.0 / self.gpu.sc_desc.height as f32, start_drag.0, start_drag.1, radius, self.game_state.heightmap_editor.pen_strength as f32, self.heightmap_gpu.phy.width as f32, self.heightmap_gpu.phy.height as f32, ]); let ub_camera_temp = self .gpu .device .create_buffer_mapped(4 * 16 + 12, wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&filler[..]); encoder_render.copy_buffer_to_buffer( &ub_camera_temp, 0, &self.ub_camera_mat, 0, (4 * 16 + 12) * 4, ); self.heightmap_gpu.update_uniform( &self.gpu.device, &mut encoder_render, self.game_state.position_smooth.x, self.game_state.position_smooth.y, ); //Imgui let start = Instant::now(); log::trace!("imgui render"); self.imgui_wrap .platform .prepare_frame(self.imgui_wrap.imgui.io_mut(), &self.gpu.window) .expect("Failed to prepare frame"); let ui: Ui = self.imgui_wrap.imgui.frame(); { let main_menu = &mut self.main_menu; { //Stat let fps_before = self.game_state.fps.clone(); let mut_fps = &mut self.game_state.fps; let profiler_logic = &self.game_state.frame_zero.frame_profiler; let profiler_render = &self.profiler; let stats_window = imgui::Window::new(im_str!("Statistics")); stats_window .size([300.0, 400.0], imgui::Condition::FirstUseEver) .position([3.0, 3.0], imgui::Condition::FirstUseEver) .collapsed(true, imgui::Condition::FirstUseEver) .resizable(true) .movable(false) .build(&ui, || { imgui::Slider::new(im_str!("fps cap"), 1..=480).build(&ui, mut_fps); ui.text(im_str!( "render: {:?}", profiler_render.get("frame_time").unwrap() )); ProgressBar::new( profiler_render.get("frame_time").unwrap().as_secs_f32() / (1.0 / *mut_fps as f32), ) .build(&ui); let mut others = profiler_render .hm .iter() .filter(|(n, d)| *n != "frame_time") .collect::>(); others.sort_by_key(|e| e.0); for (name, dur) in others.iter() { let name: String = format!("{} ", name) .chars() .take(23) .collect::>() .into_iter() .collect(); ui.text(im_str!(" {}: {:?}", name, dur)); } ui.separator(); ui.text(im_str!("logic: {:?}", profiler_logic.get("total").unwrap())); ProgressBar::new( profiler_logic.get("total").unwrap().as_millis() as f32 / 100.0, ) .build(&ui); let mut others = profiler_logic .hm .iter() .filter(|(n, d)| *n != "total") .collect::>(); others.sort_by_key(|e| e.0); for (name, dur) in others.iter() { let name: String = format!("{} ", name) .chars() .take(23) .collect::>() .into_iter() .collect(); ui.text(im_str!(" {}: {:?}", name, dur)); } }); if fps_before != *mut_fps { self.loop_helper = LoopHelper::builder().build_with_target_rate(*mut_fps as f64) } //Global info if let Some(global_info) = self.global_info { let w = 300.0; let h = 200.0; let info_window = imgui::Window::new(im_str!("Global info")); info_window .size([w, h], imgui::Condition::FirstUseEver) .position( [self.gpu.sc_desc.width as f32 - w, 0.0], imgui::Condition::Always, ) .collapsed(true, imgui::Condition::FirstUseEver) .resizable(true) .movable(false) .build(&ui, || { ui.text(im_str!("{:#?}", global_info)); }); } //Main menu match main_menu { MainMode::Home => { let home_window = imgui::Window::new(im_str!("Home")); let mut next_mode = MainMode::Home; let mut exit = false; home_window // .size([w, h], imgui::Condition::Always) .position( [ (self.gpu.sc_desc.width as f32) / 2.0, (self.gpu.sc_desc.height as f32) / 2.0, ], imgui::Condition::Always, ) .position_pivot([0.5, 0.5]) .title_bar(false) .always_auto_resize(true) .resizable(false) .movable(false) .scroll_bar(false) .collapsible(false) .build(&ui, || { if ui.button(im_str!("Play"), [200.0_f32, 100.0]) { next_mode = MainMode::Play; } if ui.button(im_str!("Map Editor"), [200.0_f32, 100.0]) { next_mode = MainMode::MapEditor; } if ui.button(im_str!("Unit Editor"), [200.0_f32, 100.0]) { next_mode = MainMode::UnitEditor; } if ui.button(im_str!("Multiplayer"), [200.0_f32, 100.0]) { next_mode = MainMode::MultiplayerLobby; } if ui.button(im_str!("Exit"), [200.0_f32, 100.0]) { exit = true; } }); if exit { self.sender_to_event_loop.send(EventLoopMsg::Stop).unwrap(); } if self.main_menu != next_mode { self.mailbox.push(RenderEvent::ChangeMode { from: self.main_menu, to: next_mode, }); self.main_menu = next_mode; } } MainMode::Play => { if let Some(me) = self.game_state.my_player() { let resource_window = imgui::Window::new(im_str!("Resources")); resource_window .size([400.0, 120.0], imgui::Condition::FirstUseEver) .position([500.0, 3.0], imgui::Condition::FirstUseEver) .collapsed(false, imgui::Condition::FirstUseEver) .build(&ui, || { ui.text(im_str!("metal: {:.1}", me.metal)); ProgressBar::new((me.metal / 500.0) as f32).build(&ui); ui.text(im_str!("energy: {:.1}", me.energy)); ProgressBar::new((me.energy / 500.0) as f32).build(&ui); }); } let mut uitool = self.game_state.uitool; let can_be_built: Vec<_> = self .game_state .frame_zero .moddef .units_id .iter() .cloned() .collect(); let can_be_built = &self.game_state.frame_zero.bot_defs; let command_window = imgui::Window::new(im_str!("Command")); command_window .size([400.0, 300.0], imgui::Condition::FirstUseEver) .position([3.0, 415.0], imgui::Condition::FirstUseEver) .collapsed(false, imgui::Condition::FirstUseEver) .build(&ui, || { for can_be_built in can_be_built { let txt = format!("Build {:?}", can_be_built.1.file_path); if ui.small_button(&im_str!("{}", txt)) { uitool = UiTool::Spawn(can_be_built.0.clone()); } } if ui.small_button(im_str!("Repair")) { uitool = UiTool::Repair; } }); if self.game_state.uitool != uitool { log::debug!( "UiTool state from {:?} to {:?}", self.game_state.uitool, uitool ); self.game_state.uitool = uitool; } } MainMode::MapEditor => { self.game_state .heightmap_editor .draw_ui(&ui, &mut self.heightmap_gpu); } MainMode::UnitEditor => { Self::draw_unit_editor_ui( &ui, &mut self.unit_editor, &mut self.unit_part_gpu, ); } MainMode::MultiplayerLobby => { let w = 216.0; let h = 324.0; let home_window = imgui::Window::new(im_str!("Multiplayer Lobby")); let mut create_server = false; let mut create_client = false; let mut disconnect_server = false; let mut disconnect_client = false; let mut next_mode = MainMode::MultiplayerLobby; if let Some(global_info) = self.global_info { home_window .size([w, h], imgui::Condition::Always) .position( [ (self.gpu.sc_desc.width as f32 - w) / 2.0, (self.gpu.sc_desc.height as f32 - h) / 2.0, ], imgui::Condition::Always, ) .title_bar(false) .resizable(false) .movable(false) .collapsible(false) .build(&ui, || { if global_info.net_server.is_none() && global_info.net_client.is_none() { create_server = ui.button(im_str!("Start server"), [200.0_f32, 100.0]); create_client = ui.button(im_str!("Start client"), [200.0_f32, 100.0]); } else if global_info.net_server.is_some() { disconnect_server = ui.button( im_str!("Disconnect server"), [200.0_f32, 100.0], ); } else if global_info.net_client.is_some() { disconnect_client = ui.button( im_str!("Disconnect client"), [200.0_f32, 100.0], ); } if ui.button(im_str!("Back"), [200.0_f32, 100.0]) { next_mode = MainMode::Home; } }); } if create_server { self.net_mode = NetMode::Server; let e = client::FromClient::StartServer(client::StartServer { bind: "127.0.0.1:4567".to_owned(), }); let _ = self.sender_from_client_to_manager.try_send(e); } if create_client { self.net_mode = NetMode::Client; let e = client::FromClient::StartClient(client::StartClient { bind: "127.0.0.1:4567".to_owned(), }); let _ = self.sender_from_client_to_manager.try_send(e); } if disconnect_server { self.net_mode = NetMode::Offline; let e = client::FromClient::DisconnectServer; let _ = self.sender_from_client_to_manager.try_send(e); } if disconnect_client { self.net_mode = NetMode::Offline; let e = client::FromClient::DisconnectClient; let _ = self.sender_from_client_to_manager.try_send(e); } if self.main_menu != next_mode { self.mailbox.push(RenderEvent::ChangeMode { from: self.main_menu, to: next_mode, }); self.main_menu = next_mode; } } } } self.imgui_wrap .platform .prepare_render(&ui, &self.gpu.window); } self.profiler.mix("imgui_render", start.elapsed(), 20); let frame = &self.gpu.swap_chain.get_next_texture(); let now = Instant::now(); // Pass { log::trace!("begin_render_pass"); let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[ wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.first_color_att_view, resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }, }, wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.position_att_view, resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: -1.0, g: -1.0, b: -1.0, a: -1.0, }, }, wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.normal_att_view, resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: -1.0, g: -1.0, b: -1.0, a: -1.0, }, }, ], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { attachment: &self.forward_depth, depth_load_op: wgpu::LoadOp::Clear, depth_store_op: wgpu::StoreOp::Store, stencil_load_op: wgpu::LoadOp::Clear, stencil_store_op: wgpu::StoreOp::Store, clear_depth: 1.0, clear_stencil: 0, }), }); self.heightmap_gpu.render(&mut rpass, &self.bind_group); for model_gpu_state in self.unit_part_gpu.states.iter_mut() { match model_gpu_state { unit_part_gpu::ModelGpuState::Ready(model_gpu) => { model_gpu.render(&mut rpass, &self.bind_group); } _ => {} } } self.kinematic_projectile_gpu .render(&mut rpass, &self.bind_group); self.arrow_gpu.render(&mut rpass, &self.bind_group); } //Transparent pass { log::trace!("begin_render_pass transparent"); let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.first_color_att_view, resolve_target: None, load_op: wgpu::LoadOp::Load, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }, }], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { attachment: &self.forward_depth, depth_load_op: wgpu::LoadOp::Load, depth_store_op: wgpu::StoreOp::Store, stencil_load_op: wgpu::LoadOp::Clear, stencil_store_op: wgpu::StoreOp::Store, clear_depth: 1.0, clear_stencil: 0, }), }); self.water_gpu.render(&mut rpass, &self.bind_group); } // Post pass { log::trace!("begin_post_render_pass"); let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.first_color_att_view, resolve_target: None, load_op: wgpu::LoadOp::Load, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }, }], depth_stencil_attachment: None, }); self.explosion_gpu.render(&mut rpass, &self.bind_group); self.postfx .render(&mut rpass, &self.gpu.device, &self.bind_group); } //Post fxaa pass { log::trace!("begin_post_render_pass"); let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.secon_color_att_view, resolve_target: None, load_op: wgpu::LoadOp::Clear, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 0.1, g: 0.2, b: 0.3, a: 1.0, }, }], depth_stencil_attachment: None, }); self.postfxaa .render(&mut rpass, &self.gpu.device, &self.bind_group); } //Custom Ui pass { log::trace!("begin curtom ui renderpass"); let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &self.secon_color_att_view, resolve_target: None, load_op: wgpu::LoadOp::Load, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 1.0, g: 0.2, b: 0.3, a: 1.0, }, }], depth_stencil_attachment: None, }); self.health_bar.render(&mut rpass, &self.bind_group); self.unit_icon.render(&mut rpass, &self.bind_group); self.line_gpu.render(&mut rpass, &self.bind_group); self.cursor_icon.render(&mut rpass, &self.bind_group); } //Copy on frame view { log::trace!("copy on frame view render pass"); let mut rpass = encoder_render.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment: &frame.view, resolve_target: None, load_op: wgpu::LoadOp::Load, store_op: wgpu::StoreOp::Store, clear_color: wgpu::Color { r: 1.0, g: 0.2, b: 0.3, a: 1.0, }, }], depth_stencil_attachment: None, }); self.post_bicopy .render(&mut rpass, &self.gpu.device, &self.bind_group); } let render_pass_3d = now.elapsed(); self.profiler.mix("render_pass_3d", render_pass_3d, 20); self.imgui_wrap .renderer .render(ui, &self.gpu.device, &mut encoder_render, &frame.view) .expect("Rendering failed"); let cursor_sample_position = self .gpu .device .create_buffer_mapped::( 4, wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ, ) .finish(); let screen_center_sample_position = self .gpu .device .create_buffer_mapped::( 4, wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::MAP_READ, ) .finish(); encoder_render.copy_texture_to_buffer( wgpu::TextureCopyView { texture: &self.position_att, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: self .input_state .cursor_pos .0 .max(0) .min(self.gpu.sc_desc.width - 1) as f32, y: self .input_state .cursor_pos .1 .max(0) .min(self.gpu.sc_desc.height - 1) as f32, z: 0.0, }, }, wgpu::BufferCopyView { buffer: &cursor_sample_position, offset: 0, row_pitch: 4 * 4, image_height: 1, }, Extent3d { width: 1, height: 1, depth: 1, }, ); encoder_render.copy_texture_to_buffer( wgpu::TextureCopyView { texture: &self.position_att, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: self.gpu.sc_desc.width as f32 / 2.0, y: self.gpu.sc_desc.height as f32 / 2.0, z: 0.0, }, }, wgpu::BufferCopyView { buffer: &screen_center_sample_position, offset: 0, row_pitch: 4 * 4, image_height: 1, }, Extent3d { width: 1, height: 1, depth: 1, }, ); let start = Instant::now(); self.gpu.queue.submit(&[encoder_render.finish()]); self.profiler .mix("device queue submit", start.elapsed(), 20); //Handle right click if let (true, Some(id), Some(mouse_world_pos)) = ( self.input_state .mouse_trigger .contains(&winit::event::MouseButton::Right), self.game_state.my_player_id, self.game_state.mouse_world_pos, ) { let orders = match self.game_state.uitool { UiTool::Move | UiTool::None => vec![FrameEventFromPlayer::MoveOrder { id, selected: self.game_state.selected.clone(), mouse_world_pos, }], UiTool::Spawn(id_to_con) => { self.game_state.uitool = UiTool::None; vec![FrameEventFromPlayer::ConOrder { id, selected: self.game_state.selected.clone(), mouse_world_pos, botdef_id: id_to_con, }] } UiTool::Repair => { self.game_state.uitool = UiTool::None; if let Some(under) = self.game_state.under_mouse { vec![FrameEventFromPlayer::RepairOrder { id, selected: self.game_state.selected.clone(), to_repair: under, }] } else { vec![] } } _ => vec![], }; for order in orders { let order_type = &format!("{:?}", order)[..8]; log::info!("order {} from {}", order_type, id); let _ = self .sender_from_client_to_manager .try_send(client::FromClient::PlayerInput(order)); } } self.input_state.update(); let tx = self.sender_to_client.clone(); cursor_sample_position.map_read_async(0, 4 * 4, move |e: BufferMapAsyncResult<&[f32]>| { match e { Ok(e) => { log::trace!("BufferMapAsyncResult callback"); let _ = tx.try_send(ToClient::MapReadAsyncMessage { vec: e.data.to_vec(), usage: "mouse_world_pos".to_owned(), }); } Err(_) => {} } }); let tx = self.sender_to_client.clone(); screen_center_sample_position.map_read_async( 0, 4 * 4, move |e: BufferMapAsyncResult<&[f32]>| match e { Ok(e) => { log::trace!("BufferMapAsyncResult callback"); let _ = tx.try_send(ToClient::MapReadAsyncMessage { vec: e.data.to_vec(), usage: "screen_center_world_pos".to_owned(), }); } Err(_) => {} }, ); } } ================================================ FILE: src/client/uitool.rs ================================================ use crate::botdef; use crate::*; use utils::*; #[derive(Clone, Copy, Debug, PartialEq)] pub enum UiTool { None, Move, Repair, Guard, Attack, Spawn(Id), } ================================================ FILE: src/client/unit_editor.rs ================================================ use super::client::*; use crate::botdef::BotDef; use crate::model::*; use crate::utils::FileTree; use crate::*; use gpu_obj::model_gpu::ModelGpu; use na::{Matrix4, Point3, Vector2, Vector3, Vector4}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; use unit::*; use unit_part_gpu::*; pub struct UnitEditor { pub orbit: Point3, pub botdef: BotDef, pub asset_dir_cached: FileTree, } impl UnitEditor { pub fn new() -> Self { let root = PartTree { id: utils::rand_id(), children: vec![], placed_mesh: None, placed_collider: None, parent_to_self: Matrix4::identity(), joint: Joint::Fix, }; let botdef = botdef::BotDef { id: utils::rand_id(), file_path: "src/asset/test.json".to_owned(), radius: 0.5, max_life: 100, turn_accel: 1.5, max_turn_rate: 1.5, accel: 0.1, break_accel: 0.3, max_speed: 1.0, build_power: 10.0, build_dist: 5.0, metal_cost: 100, part_tree: root, }; UnitEditor { orbit: Point3::new(300.0, 100.0, 0.5), asset_dir_cached: FileTree::Unknown, botdef, } } fn add_to_parts(&mut self, parent: utils::Id, path: PathBuf, mesh_index: usize) { log::debug!("adding {:?} to {}", path, parent); match self.botdef.part_tree.find_node_mut(parent) { Some(node) => node.children.push(PartTree { placed_mesh: Some(PlacedMesh { trans: utils::face_towards_dir( &Vector3::new(0.0, 0.0, 0.0), &Vector3::new(1.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 1.0), ), mesh_path: path, mesh_index, }), placed_collider: None, parent_to_self: Matrix4::identity(), joint: Joint::Fix, id: utils::rand_id(), children: vec![], }), None => {} } } } impl App { pub fn init_unit_editor(&mut self) { self.clear_gpu_instance_and_game_state(); self.game_state.position = Point3::new(300.0, 97.0, 1.0); self.game_state.position_smooth = Point3::new(300.0, 97.0, 1.0); self.game_state.dir = Vector3::new(0.0, 1.0, -1.0); self.game_state.dir_smooth = Vector3::new(0.0, 1.0, -1.0); } pub fn draw_unit_editor_ui( ui: &Ui, unit_editor: &mut UnitEditor, unit_part_gpu: &mut UnitPartGpu, ) { let path = std::path::Path::new("./src/asset/"); if let FileTree::Unknown = unit_editor.asset_dir_cached { log::debug!("Reading all assets to build file directory cache"); unit_editor.asset_dir_cached = FileTree::new(path.to_owned()); } let window_editor = imgui::Window::new(im_str!("Unit Editor")); window_editor .size([400.0, 700.0], imgui::Condition::FirstUseEver) .position([3.0, 12.0], imgui::Condition::FirstUseEver) .collapsed(false, imgui::Condition::FirstUseEver) .build(&ui, || { let BotDef { id, file_path, radius, max_life, turn_accel, max_turn_rate, accel, break_accel, max_speed, build_power, build_dist, metal_cost, part_tree, } = &unit_editor.botdef; let file_path = file_path.clone(); let to_rev = 0.5 / std::f32::consts::PI; let to_rad = 1.0 / to_rev; let to_sec = 10.0; let to_frame = 1.0 / to_sec; let mut max_turn_rate_human = max_turn_rate * to_sec * to_rev; ui.drag_float( im_str!("maximum turn rate (rev/sec)"), &mut max_turn_rate_human, ) .speed(0.01) .min(0.01) .max(100.0) .build(); let mut turn_accel_human = turn_accel * to_sec * to_sec * to_rev; ui.drag_float( im_str!("turn acceleration (rev/sec²)"), &mut turn_accel_human, ) .speed(0.01) .min(0.01) .max(100.0) .build(); let mut max_speed_human = max_speed * to_sec; ui.drag_float(im_str!("max speed (m/sec)"), &mut max_speed_human) .speed(0.01) .min(0.01) .max(100.0) .build(); let mut accel_human = accel * to_sec * to_sec; ui.drag_float(im_str!("acceleration (m/sec²)"), &mut accel_human) .speed(0.01) .min(0.01) .max(100.0) .build(); let mut break_accel_human = break_accel * to_sec * to_sec; ui.drag_float(im_str!("break accel (m/sec²)"), &mut break_accel_human) .speed(0.01) .min(0.01) .max(100.0) .build(); let mut life = max_life.clone(); ui.drag_int(im_str!("health"), &mut life).build(); let mut build_power_human = build_power * to_sec; ui.drag_float(im_str!("build power (metal/sec)"), &mut build_power_human) .speed(0.01) .min(0.01) .max(100.0) .build(); let mut build_dist_ = build_dist.clone(); ui.drag_float(im_str!("build distance (m)"), &mut build_dist_) .speed(0.01) .min(0.01) .max(100.0) .build(); unit_editor.botdef.max_turn_rate = max_turn_rate_human * to_frame * to_rad; unit_editor.botdef.turn_accel = turn_accel_human * to_frame * to_frame * to_rad; unit_editor.botdef.max_speed = max_speed_human * to_frame; unit_editor.botdef.accel = accel_human * to_frame * to_frame; unit_editor.botdef.break_accel = break_accel_human * to_frame * to_frame; unit_editor.botdef.max_life = life.max(0); unit_editor.botdef.build_power = build_power_human * to_frame; unit_editor.botdef.build_dist = build_dist_; ui.separator(); Self::ui_part_tree( ui, &mut unit_editor.botdef.part_tree.clone(), unit_editor, true, unit_part_gpu, ); if ui.button(im_str!("load"), [0.0, 0.0]) { Self::load_botdef_in_editor(&file_path, unit_editor, unit_part_gpu); } if ui.button(im_str!("save"), [0.0, 0.0]) { Self::save_botdef_on_disk(&unit_editor.botdef, &file_path); log::info!("Saving {:?}", unit_editor.botdef.part_tree); } }); let window_selector = imgui::Window::new(im_str!("Unit Selector")); window_selector .size([400.0, 200.0], imgui::Condition::FirstUseEver) .position([400.0, 3.0], imgui::Condition::FirstUseEver) .collapsed(false, imgui::Condition::FirstUseEver) .build(&ui, || { Self::visit_dirs_for_selection( &unit_editor.asset_dir_cached.clone(), ui, unit_editor, unit_part_gpu, ); }); } pub fn save_botdef_on_disk(bot_def: &BotDef, path: &str) { use std::fs::OpenOptions; use std::io::prelude::*; use std::io::{BufReader, BufWriter}; let file = OpenOptions::new() .read(true) .write(true) .create(true) .truncate(true) .open(path) .unwrap(); let mut buf_w = BufWriter::new(file); serde_json::to_writer_pretty(buf_w, bot_def); // bincode::serialize_into(buf_w, bot_def); } pub fn load_botdef_on_disk(path: &str) -> serde_json::Result { use std::fs::OpenOptions; use std::io::prelude::*; use std::io::{BufReader, BufWriter}; let file = OpenOptions::new() .read(true) .write(true) .create(true) .open(path) .unwrap(); let mut buf_r = BufReader::new(file); serde_json::from_reader(buf_r) } pub fn load_botdef_in_editor( path: &str, unit_editor: &mut UnitEditor, unit_part_gpu: &mut unit_part_gpu::UnitPartGpu, ) { if let Ok(botdef) = Self::load_botdef_on_disk(path) { log::info!("Loaded {:?}", botdef.id); unit_editor.botdef = botdef; //Might need to clone for node in unit_editor.botdef.clone().part_tree.iter() { if let Some(mesh) = &node.placed_mesh { let index = unit_part_gpu.index_of_or_create_if_na(mesh.mesh_path.clone()); //Update the mesh index, because it depend on the load order in the unit_part_gpu for node_mut in unit_editor.botdef.part_tree.find_node_mut(node.id) { for pm in &mut node_mut.placed_mesh { pm.mesh_index = index; } } } } } } fn ui_part_tree( ui: &Ui, part_tree: &PartTree, unit_editor: &mut UnitEditor, is_root: bool, unit_part_gpu: &mut UnitPartGpu, ) { { if !is_root { if ui.button(im_str!("remove##{:?}", part_tree.id).as_ref(), [0.0, 0.0]) { let deleter = unit_editor.botdef.part_tree.remove_node(part_tree.id); } } let add_str = im_str!("Add child##{:?}", part_tree.id); if ui.button(add_str.as_ref(), [0.0, 0.0]) { ui.open_popup(add_str.as_ref()); } ui.popup_modal(add_str.as_ref()) .always_auto_resize(true) .build(|| { Self::visit_dirs_for_add_child( &unit_editor.asset_dir_cached.clone(), ui, unit_editor, unit_part_gpu, part_tree.id, ); if ui.button(im_str!("Close"), [0.0, 0.0]) { ui.close_current_popup(); } }); ui.tree_node(im_str!("children").as_ref()) .default_open(true) .build(|| { for c in part_tree.children.iter() { { ui.tree_node(im_str!("child##{:?}", c.id).as_ref()) .default_open(true) .build(|| { let name = im_str!("child"); ChildWindow::new(name) .border(true) .always_auto_resize(true) .build(ui, || { let ui_for_transform = |id: String, matrix: Matrix4| -> Matrix4 { let pos = matrix * Vector4::new(0.0, 0.0, 0.0, 1.0); let pos = pos.xyz() / pos.w; let arr_pos: &mut [f32; 3] = &mut [pos.x, pos.y, pos.z]; ui.drag_float3( im_str!("position##{:?}", id).as_ref(), arr_pos, ) .speed(0.001) .min(-3.0) .max(3.0) .build(); let isometry: Isometry3 = unsafe { na::convert_unchecked::< Matrix4, Isometry3, >( matrix ) }; let euler = isometry.rotation.euler_angles(); let arr_angle: &mut [f32; 3] = &mut [euler.0, euler.1, euler.2]; ui.drag_float3( im_str!("euler angles##{:?}", id).as_ref(), arr_angle, ) .speed(0.001) .min(-6.0) .max(6.0) .build(); let rotation_mat = Matrix4::from_euler_angles( arr_angle[0], arr_angle[1], arr_angle[2], ); let final_mat = utils::face_towards_dir( &Vector3::new( arr_pos[0], arr_pos[1], arr_pos[2], ), &Vector3::new(1.0, 0.0, 0.0), &Vector3::new(0.0, 0.0, 1.0), ) * rotation_mat; final_mat }; ui.text(im_str!("node transform:")); let new_parent_to_self = ui_for_transform( format!("{:?}", c.id), c.parent_to_self, ); //Joint ui.text(im_str!("joint {:?}", c.joint)); ui.same_line(0.0); if ui.small_button(im_str!("swap##{:?}", c.id).as_ref()) { unit_editor .botdef .part_tree .find_node_mut(c.id) .unwrap() .joint .replace_with_next(); } if let Some(model) = &c.placed_mesh { ui.text(im_str!("mesh: {:?}", model.mesh_path)); } let replace_str = im_str!("replace mesh##{:?}", c.id); if ui.button(replace_str.as_ref(), [0.0, 0.0]) { ui.open_popup(replace_str.as_ref()); } ui.popup_modal(replace_str.as_ref()) .always_auto_resize(true) .build(|| { Self::visit_dirs_for_replace_mesh( &unit_editor.asset_dir_cached.clone(), ui, unit_editor, unit_part_gpu, c.id, ); if ui.button(im_str!("Close"), [0.0, 0.0]) { ui.close_current_popup(); } }); ui.text(im_str!("mesh transform:")); let new_placed_mesh = if let Some(Some(old_placed_mesh)) = unit_editor .botdef .part_tree .find_node(c.id) .map(|e| &e.placed_mesh) { let new_placed_mesh = PlacedMesh { trans: ui_for_transform( format!("placed_mesh{:?}", c.id), old_placed_mesh.trans, ), mesh_path: old_placed_mesh .mesh_path .clone(), mesh_index: old_placed_mesh.mesh_index, }; Some(new_placed_mesh) } else { None }; if let Some(node) = unit_editor.botdef.part_tree.find_node_mut(c.id) { node.parent_to_self = new_parent_to_self; node.placed_mesh = new_placed_mesh; }; Self::ui_part_tree( ui, &c, unit_editor, false, unit_part_gpu, ); }) }); }; } }); }; } fn visit_dirs_for_add_child( dir: &FileTree, ui: &Ui, unit_editor: &mut UnitEditor, unit_part_gpu: &mut UnitPartGpu, parent: utils::Id, ) { match dir { FileTree::Unknown => { ui.text(im_str!("Error reading asset file")); } FileTree::Leaf { path } => { let file_name = path.file_name().unwrap(); let extension = path.extension().unwrap(); if extension == "obj" { ui.text(im_str!("{:?}", file_name)); ui.same_line(0.0); let (index, state) = unit_part_gpu.path_get_or_create_if_na(path.to_owned()); match state { ModelGpuState::Ready(_) | ModelGpuState::ToLoad(_) => { if ui.small_button(im_str!("add to parts##{:?}", path).as_ref()) { log::debug!("add to parts {:?}", path); unit_editor.add_to_parts(parent, path.clone(), index); } ui.same_line(0.0); if ui.small_button(im_str!("reload##{:?}", path).as_ref()) { unit_part_gpu.reload(path.clone()); } } ModelGpuState::Error(e) => { ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!("Error")); ui.same_line(0.0); if ui.small_button(im_str!("reload##{:?}", path).as_ref()) { unit_part_gpu.reload(path.clone()); } } } } } FileTree::Node { path, children } => { ui.tree_node( im_str!("{:?}", path.components().last().unwrap().as_os_str()).as_ref(), ) .build(|| { for child in children { Self::visit_dirs_for_add_child( &child, ui, unit_editor, unit_part_gpu, parent, ); } }); } } } fn visit_dirs_for_replace_mesh( dir: &FileTree, ui: &Ui, unit_editor: &mut UnitEditor, unit_part_gpu: &mut UnitPartGpu, id_to_mesh_replace: utils::Id, ) { match dir { FileTree::Unknown => { ui.text(im_str!("Error reading asset file")); } FileTree::Leaf { path } => { let file_name = path.file_name().unwrap(); let extension = path.extension().unwrap(); if extension == "obj" { ui.text(im_str!("{:?}", file_name)); ui.same_line(0.0); let mut replace_exe = |mesh_index| { if let Some(child) = unit_editor .botdef .part_tree .find_node_mut(id_to_mesh_replace) { if let Some(old) = &child.placed_mesh { child.placed_mesh = Some(PlacedMesh { mesh_index, mesh_path: path.clone(), trans: old.trans.clone(), }); } else { child.placed_mesh = Some(PlacedMesh { mesh_index, mesh_path: path.clone(), trans: Matrix4::identity(), }); } } }; let state = unit_part_gpu.path_get(path.to_owned()); match state { None => { if ui.small_button(im_str!("replace with this##{:?}", path).as_ref()) { log::debug!("replace with this {:?}", path); log::debug!("was not open {:?}", path); let index = unit_part_gpu.index_of_or_create_if_na(path.to_owned()); replace_exe(index); } } Some(ModelGpuState::Ready(_)) | Some(ModelGpuState::ToLoad(_)) => { if ui.small_button(im_str!("replace with this##{:?}", path).as_ref()) { log::debug!("replace with this {:?}", path); let index = unit_part_gpu.index_of_or_create_if_na(path.to_owned()); replace_exe(index); } ui.same_line(0.0); if ui.small_button(im_str!("reload##{:?}", path).as_ref()) { unit_part_gpu.reload(path.clone()); } } Some(ModelGpuState::Error(e)) => { ui.text_colored([1.0, 0.0, 0.0, 1.0], im_str!("Error")); ui.same_line(0.0); if ui.small_button(im_str!("reload##{:?}", path).as_ref()) { unit_part_gpu.reload(path.clone()); } } } } } FileTree::Node { path, children } => { ui.tree_node( im_str!("{:?}", path.components().last().unwrap().as_os_str()).as_ref(), ) .build(|| { for child in children { Self::visit_dirs_for_replace_mesh( &child, ui, unit_editor, unit_part_gpu, id_to_mesh_replace, ); } }); } } } fn visit_dirs_for_selection( dir: &FileTree, ui: &Ui, unit_editor: &mut UnitEditor, unit_part_gpu: &mut UnitPartGpu, ) { match dir { FileTree::Unknown => { ui.text(im_str!("Error reading asset file")); } FileTree::Leaf { path } => { let file_name = path.file_name().unwrap(); let extension = path.extension().unwrap(); if extension == "json" { ui.text(im_str!("{:?}", file_name)); ui.same_line(0.0); if ui.small_button(im_str!("load##{:?}", path).as_ref()) { log::debug!("load botdef {:?}", path); Self::load_botdef_in_editor( path.to_str().unwrap(), unit_editor, unit_part_gpu, ); } } } FileTree::Node { path, children } => { ui.tree_node( im_str!("{:?}", path.components().last().unwrap().as_os_str()).as_ref(), ) .build(|| { for child in children { Self::visit_dirs_for_selection(&child, ui, unit_editor, unit_part_gpu); } }); } } } } ================================================ FILE: src/client/unit_part_gpu.rs ================================================ use crate::gpu_obj; use crate::model; use gpu_obj::model_gpu::ModelGpu; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; pub enum ModelGpuState { ToLoad(model::TriangleList), Ready(ModelGpu), Error(String), } pub struct UnitPartGpu { pub states: Vec, path_to_index: HashMap, } impl UnitPartGpu { pub fn new() -> Self { UnitPartGpu { states: Vec::new(), path_to_index: HashMap::new(), } } pub fn index_of(&self, path: PathBuf) -> Option<&usize> { self.path_to_index.get(&path) } pub fn path_get(&self, path: PathBuf) -> Option<&ModelGpuState> { self.index_of(path).map(|index| &self.states[*index]) } pub fn path_get_mut(&mut self, path: PathBuf) -> Option<&mut ModelGpuState> { self.index_of(path) .cloned() .map(move |index| &mut self.states[index]) } pub fn get(&self, index: usize) -> &ModelGpuState { &self.states[index] } pub fn get_mut(&mut self, index: usize) -> &mut ModelGpuState { &mut self.states[index] } fn load_at(&mut self, index: usize, path: PathBuf) { self.path_to_index.insert(path.clone(), index); let to_push = match crate::model::open_obj(path.to_str().unwrap()) { Ok(triangle_list) => ModelGpuState::ToLoad(triangle_list.clone()), Err(e) => ModelGpuState::Error(e), }; self.states.push(to_push); } ///Push a new entry, regardless of if the same path is already present. ///Returns the entry index pub fn append(&mut self, path: PathBuf) -> usize { let index = self.states.len(); self.load_at(index, path); index } pub fn reload(&mut self, path: PathBuf) -> usize { match self.index_of(path.clone()).cloned() { Some(index) => { self.load_at(index, path); index } None => self.append(path), } } pub fn index_of_or_create_if_na(&mut self, path: PathBuf) -> usize { match self.index_of(path.clone()) { Some(index) => *index, None => self.append(path), } } pub fn path_get_or_create_if_na(&mut self, path: PathBuf) -> (usize, &ModelGpuState) { let index = self.index_of_or_create_if_na(path); (index, self.get(index)) } } ================================================ FILE: src/frame.rs ================================================ extern crate nalgebra as na; use crate::heightmap_phy; use crate::botdef; use crate::mobile; use crate::moddef; use crate::utils; use fnv::{FnvHashMap, FnvHashSet}; use na::{Point3, Vector3}; use std::collections::HashMap; use std::time::Duration; use crate::unit; use mobile::*; use serde::{Deserialize, Serialize}; use utils::*; #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub struct Player { pub id: Id, pub kbots: FnvHashSet>, pub team: u8, pub metal: f64, pub energy: f64, } impl Player { pub fn new() -> Self { let id = utils::rand_id(); Player { id, kbots: FnvHashSet::default(), team: 0, metal: 500.0, energy: 500.0, } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum FrameEventFromPlayer { RepairOrder { id: Id, selected: FnvHashSet>, to_repair: Id, }, ConOrder { id: Id, selected: FnvHashSet>, mouse_world_pos: Vector3, botdef_id: Id, }, MoveOrder { id: Id, selected: FnvHashSet>, mouse_world_pos: Vector3, }, ReplaceFrame(Frame), } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct ProfilerMap { pub hm: HashMap, } impl ProfilerMap { pub fn new() -> Self { ProfilerMap { hm: HashMap::new() } } pub fn mix(&mut self, s: &str, duration: Duration, last_ratio: u32) { match self.hm.get_mut(&s.to_owned()) { Some(val) => { *val = val .checked_mul(last_ratio) .unwrap() .checked_add(duration) .unwrap() .checked_div(last_ratio + 1) .unwrap(); } None => { self.hm.insert(s.to_owned(), duration); } } } pub fn add(&mut self, s: &str, duration: Duration) { self.hm.insert(s.to_owned(), duration); } pub fn get(&self, s: &str) -> Option<&Duration> { self.hm.get(s) } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct DataToComputeNextFrame { pub old_frame: Frame, pub events: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct FrameUpdate { pub kbots: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Frame { // relevant to send to client on change pub kinematic_projectiles: FnvHashMap, KinematicProjectile>, pub arrows: Vec, pub heightmap_phy: Option, pub players: FnvHashMap, Player>, pub kbots: FnvHashMap, KBot>, pub moddef: moddef::ModDef, // relevant to send to client once pub bot_defs: FnvHashMap, botdef::BotDef>, // relevant to send to client always pub number: i32, pub explosions: Vec, pub kbots_dead: FnvHashSet>, pub kinematic_projectiles_dead: Vec>, pub kinematic_projectiles_birth: Vec, pub frame_profiler: ProfilerMap, } impl Frame { pub fn new() -> Self { Frame { number: 0, players: FnvHashMap::default(), moddef: moddef::ModDef::new(), kbots: FnvHashMap::default(), kinematic_projectiles: FnvHashMap::default(), arrows: Vec::new(), explosions: Vec::new(), heightmap_phy: None, frame_profiler: ProfilerMap::new(), kbots_dead: FnvHashSet::default(), kinematic_projectiles_dead: Vec::new(), kinematic_projectiles_birth: Vec::new(), bot_defs: FnvHashMap::default(), } } } ================================================ FILE: src/frame_server/mod.rs ================================================ use crate::frame::*; use crate::botdef; use crate::heightmap_phy; use crate::mobile::*; use crate::utils::*; use crossbeam_channel::{Receiver, Sender}; use fnv::{FnvHashMap, FnvHashSet}; use na::{Matrix4, Point3, Vector2, Vector3}; use std::time::Instant; pub enum ToFrameServer { DataToComputeNextFrame(DataToComputeNextFrame), } pub enum FromFrameServer { NewFrame(Frame), } pub struct FrameServerCache { pub grid: Vec>>, pub small_grid: Vec>>, pub heightmap_phy: Option, } impl FrameServerCache { pub fn spawn( r_to_frame_server: Receiver, s_from_frame_server: Sender, ) -> () { let _ = std::thread::Builder::new() .name("frame_server".to_string()) .spawn(move || { let mut fsc = FrameServerCache::new(); for msg in r_to_frame_server.iter() { match msg { ToFrameServer::DataToComputeNextFrame(DataToComputeNextFrame { old_frame, events, }) => { let next_frame = fsc.next_frame(old_frame, events); // let dur = utils::time(|| { // use flate2::write::ZlibEncoder; // use flate2::Compression; // use std::io::prelude::*; // let mut e = ZlibEncoder::new(Vec::new(), Compression::new(1)); // e.write_all(&vec); // let compressed_bytes = e.finish().unwrap(); // log::info!("Compressed is {} bytes", compressed_bytes.len()); // }); // log::info!("compression took {:?}", dur); let _ = s_from_frame_server.send(FromFrameServer::NewFrame(next_frame)); } } } }); } pub fn new() -> Self { FrameServerCache { grid: Vec::new(), small_grid: Vec::new(), heightmap_phy: None, } } pub fn next_frame(&mut self, old_frame: Frame, events: Vec) -> Frame { let mut frame_profiler = ProfilerMap::new(); let start = std::time::Instant::now(); log::trace!("Received frame {} to compute next frame", old_frame.number); log::trace!("Event {}", events.len()); let mut replacer = None; for event in events.iter() { match event { FrameEventFromPlayer::ReplaceFrame(frame) => { self.heightmap_phy = frame.heightmap_phy.clone(); replacer = Some(frame.clone()); log::trace!("Replacing frame"); } _ => {} } } let mut frame = replacer.unwrap_or(old_frame); frame.number += 1; frame.kbots_dead.clear(); frame.heightmap_phy = None; frame.explosions.clear(); frame.kinematic_projectiles_birth.clear(); frame.kinematic_projectiles_dead.clear(); //TODO order event by player then by type before doing any effect. This step should be deterministic for event in events { match event { FrameEventFromPlayer::MoveOrder { id, selected, mouse_world_pos, } => { //TODO Validate selected are owned by id update_mobile_target(mouse_world_pos, &selected, &mut frame.kbots); } FrameEventFromPlayer::ConOrder { id, selected, mouse_world_pos, botdef_id, } => { //TODO Validate selected are owned by id && botdef_id is constructable by at least 1 selected let botdef = frame.bot_defs.get(&botdef_id).unwrap(); let mut m = KBot::new(Point3::from(mouse_world_pos), botdef, id); m.team = frame.players.get(&id).unwrap().team; m.con_completed = std::f32::MIN_POSITIVE; m.life = 1; for selected_raw_id in &selected { for kbot in frame.kbots.get_mut(selected_raw_id) { kbot.current_command = Command::Build(m.id.clone()) } } let player = frame.players.get_mut(&id).unwrap(); player.kbots.insert(m.id); frame.kbots.insert(m.id, m); } FrameEventFromPlayer::RepairOrder { id, selected, to_repair, } => { for selected_raw_id in &selected { for kbot in frame.kbots.get_mut(selected_raw_id) { kbot.current_command = Command::Repair(to_repair) } } } _ => {} } } frame_profiler.add("1 handle_events", start.elapsed()); let mut arrows = Vec::new(); let start_update_units = Instant::now(); if let Some(heightmap) = &self.heightmap_phy { update_units( &mut frame_profiler, &mut frame.kbots, &mut frame.kbots_dead, &mut frame.kinematic_projectiles_dead, &mut frame.kinematic_projectiles_birth, &mut frame.kinematic_projectiles, heightmap, &mut arrows, frame.number, &mut frame.players, &mut self.grid, &mut self.small_grid, &mut frame.explosions, &frame.bot_defs, ); } frame_profiler.add("0 update_units", start_update_units.elapsed()); frame_profiler.add("total", start.elapsed()); Frame { number: frame.number, frame_profiler, arrows, ..frame } } } pub fn update_mobile_target( mouse_world_pos: Vector3, selected: &FnvHashSet>, kbots: &mut FnvHashMap, KBot>, ) { let selected_count = selected.len(); let formation_w = (selected_count as f32).sqrt().ceil() as i32; let mut spot = Vec::>::new(); for i in 0..formation_w { for j in 0..formation_w { spot.push( mouse_world_pos + Vector3::new( i as f32 + 0.5 - formation_w as f32 / 2.0, j as f32 + 0.5 - formation_w as f32 / 2.0, 0.0, ) * 4.0, ) } } let mut center = Vector3::new(0.0, 0.0, 0.0); let mut tap = 0.0; let mut id_to_pos = Vec::new(); for &s in selected.iter() { if let Some(mobile) = kbots.get(&s) { id_to_pos.push((mobile.id, mobile.position.coords)); center += mobile.position.coords; tap += 1.0; } } center /= tap; let axis = (mouse_world_pos - center).normalize(); let mut projected_spot: Vec<_> = spot .iter() .enumerate() .map(|(index, v)| (index, v.dot(&axis))) .collect(); projected_spot.sort_by(|(_, proj), (_, proj2)| { if proj > proj2 { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Less } }); let mut id_to_proj: Vec<_> = id_to_pos .iter() .map(|(index, v)| (index, v.dot(&axis))) .collect(); id_to_proj.sort_by(|(_, proj), (_, proj2)| { if proj > proj2 { std::cmp::Ordering::Greater } else { std::cmp::Ordering::Less } }); for ((id, _), (spot_id, _)) in id_to_proj.iter().zip(&projected_spot[..]) { if let Some(mobile) = kbots.get_mut(id) { log::trace!("New order for {}", mobile.id); mobile.move_target = Some(Point3::::from(spot[*spot_id])); mobile.current_command = Command::None; } } } pub fn update_units( frame_profiler: &mut ProfilerMap, kbots: &mut FnvHashMap, KBot>, kbots_dead: &mut FnvHashSet>, kinematic_projectiles_dead: &mut Vec>, kinematic_projectiles_birth: &mut Vec, kinematic_projectiles: &mut FnvHashMap, KinematicProjectile>, heightmap_phy: &heightmap_phy::HeightmapPhy, arrows: &mut Vec, frame_count: i32, players: &mut FnvHashMap, Player>, grid: &mut Vec>>, small_grid: &mut Vec>>, explosions: &mut Vec, bot_defs: &FnvHashMap, botdef::BotDef>, ) { let start = std::time::Instant::now(); let cell_size = 4; let grid_w = (heightmap_phy.width / cell_size) as usize; let grid_h = (heightmap_phy.height / cell_size) as usize; if grid.len() != grid_w * grid_h { std::mem::replace(grid, vec![Vec::>::new(); grid_w * grid_h]); } else { for zone in grid.iter_mut() { zone.clear(); } } let grid_pos = |mobile: &KBot| -> usize { let (x, y) = (mobile.position.x, mobile.position.y); (x as usize / cell_size as usize) as usize + (y as usize / cell_size as usize) as usize * grid_w }; for (&id, mobile) in kbots.iter() { let gp = grid_pos(mobile); grid[gp].push(id); for cell in &[ -1_i32 - grid_w as i32, -(grid_w as i32), 1 - grid_w as i32, -1, 1, -1 + grid_w as i32, grid_w as i32, 1 + grid_w as i32, ] { let cell_index = cell + gp as i32; if cell_index >= 0 && (cell_index as usize) < grid_w * grid_h { grid[cell_index as usize].push(id); } } } frame_profiler.add("01 grid", start.elapsed()); //AABB for kbot and proj { let start = std::time::Instant::now(); let cell_size = 4; let grid_w = (heightmap_phy.width / cell_size) as usize; let grid_h = (heightmap_phy.height / cell_size) as usize; if small_grid.len() != grid_w * grid_h { std::mem::replace(small_grid, vec![Vec::>::new(); grid_w * grid_h]); } else { for zone in small_grid.iter_mut() { zone.clear(); } } fn index_aabb( position: Vector3, radius: f32, cell_size: usize, grid_w: usize, ) -> Vec { let mut indices = Vec::new(); let min_x = (position.x - radius * 1.0).floor() as usize; let max_x = (position.x + radius * 1.0).ceil() as usize; let min_y = (position.y - radius * 1.0).floor() as usize; let max_y = (position.y + radius * 1.0).ceil() as usize; let min_x = min_x / cell_size; let max_x = (max_x + 1) / cell_size; let min_y = min_y / cell_size; let max_y = (max_y + 1) / cell_size; for i in (min_x..=max_x).step_by(cell_size) { for j in (min_y..=max_y).step_by(cell_size) { // println!("INSERTION {} {} {}", i, j, id); indices.push(i + j * grid_w); } } indices } for (id, kbot) in kbots.iter() { let radius = bot_defs.get(&kbot.botdef_id).unwrap().radius; for index in index_aabb(kbot.position.coords, radius, cell_size, grid_w).iter() { small_grid[*index].push(*id); } } frame_profiler.add("03 small_grid", start.elapsed()); let start = std::time::Instant::now(); //Projectile move compute { for proj in kinematic_projectiles.values_mut() { let current_pos = proj.position_at(frame_count - 1); let next_pos = proj.position_at(frame_count); { //Slowly interpolate to not miss collisions let step_size = proj.radius * 1.0; let ul = next_pos.coords - current_pos.coords; let distance_to_travel = ul.magnitude(); let u = ul / distance_to_travel; let mut current_interp = current_pos.coords.clone(); let count = (distance_to_travel / step_size).floor() as usize; 'interp: for n in 0..=count { current_interp += u * step_size; if n == count { current_interp = next_pos.coords; } //Checking collision with current_interp let indices = index_aabb(current_interp, proj.radius, cell_size, grid_w); let kbots_in_proximity: FnvHashSet<_> = indices .iter() .map(|index| small_grid[*index].clone()) .flatten() .collect(); // &small_grid_kbot[index]; 'bot_test: for kbot_id in kbots_in_proximity.iter() { let kbot = kbots.get_mut(kbot_id).unwrap(); let distance_to_target = (kbot.position.coords - current_interp).magnitude(); // println!("Distance {}", distance_to_target); let kbot_radius = bot_defs.get(&kbot.botdef_id).unwrap().radius; if distance_to_target < (kbot_radius + proj.radius) { //Colission between Kbot and projectile kbot.life = (kbot.life - 10).max(0); proj.death_frame = frame_count; explosions.push(ExplosionEvent { position: Point3::from(current_interp), size: 0.5, life_time: 0.8, }); break 'interp; } } } } if proj.death_frame == frame_count { kinematic_projectiles_dead.push(proj.id); } } for r in kinematic_projectiles_dead.iter() { kinematic_projectiles.remove(&r); } } frame_profiler.add("04 proj move", start.elapsed()); } //Projectile fire compute { let teams: FnvHashSet<_> = players.values().map(|p| p.team).collect(); let start = std::time::Instant::now(); //TODO cache let mut id_to_team: FnvHashMap, u8> = FnvHashMap::with_capacity_and_hasher(kbots.len(), Default::default()); for team in teams.iter() { let team_players: Vec<_> = players.values().filter(|e| &e.team == team).collect(); for p in team_players.iter() { for kbot in p.kbots.iter() { id_to_team.insert(*kbot, *team); } } } frame_profiler.add("05 id_to_team", start.elapsed()); let start = std::time::Instant::now(); struct Shot { bot: Id, target: Vector3, }; let mut shots = Vec::new(); for (me, me_kbot) in kbots.iter() { if me_kbot.con_completed == 1.0 { let grid_pos = grid_pos(me_kbot); let my_team = id_to_team.get(me).unwrap(); // let ennemies_in_cell = &team_to_ennemy_grid.get(my_team).unwrap()[grid_pos]; let mut ennemies_in_cell: Vec> = grid[grid_pos].clone(); let to_remove = ennemies_in_cell.iter().position(|e| e == me).unwrap(); ennemies_in_cell.remove(to_remove); let can_shoot =// *my_team == 0&& frame_count - me_kbot.frame_last_shot > me_kbot.reload_frame_count; if can_shoot { //We choose the first ennemy in the cell, we could sort by distance or something else here //TODO Configurable strategy 'meloop: for potential_ennemy in ennemies_in_cell { if id_to_team.get(&potential_ennemy).unwrap() != my_team { let ennemy_kbot = kbots.get(&potential_ennemy).unwrap(); if (ennemy_kbot.position.coords - me_kbot.position.coords).magnitude() < 6.0 { shots.push(Shot { bot: *me, target: ennemy_kbot.position.coords, }); break 'meloop; } } } } } } for shot in shots.iter() { let kbot = kbots.get_mut(&shot.bot).unwrap(); let dir = (shot.target - kbot.position.coords).normalize(); kbot.weapon0_dir = dir; kbot.frame_last_shot = frame_count; let kbot_radius = bot_defs.get(&kbot.botdef_id).unwrap().radius; let proj = KinematicProjectile { id: rand_id(), birth_frame: frame_count, death_frame: frame_count + 6, position_at_birth: kbot.position + dir * (kbot_radius + 0.25 + 0.01), speed_per_frame_at_birth: dir * 2.0 + Vector3::new(0.0, 0.0, 0.2), accel_per_frame: Vector3::new(0.0, 0.0, -0.08), radius: 0.25, position_cache: Vec::new(), speed_cache: Vec::new(), }; kinematic_projectiles_birth.push(proj.clone()); kinematic_projectiles.insert(proj.id, proj); } frame_profiler.add("07 kbot_fire", start.elapsed()); } let start = std::time::Instant::now(); let mobiles2 = kbots.clone(); struct BuildPart { amount: f64, repair: bool, player: Id, from: Id, to: Id, } let mut build_throughputs = Vec::new(); //Build compute for (id, mobile) in kbots.iter_mut() { if mobile.con_completed >= 1.0 { // Look at current_command, change move_target if necessary match mobile.current_command { Command::Build(to_build) => match mobiles2.get(&to_build) { Some(to_build) => { if to_build.con_completed < 1.0 { let dist = (to_build.position.coords - mobile.position.coords).magnitude(); let botdef = bot_defs.get(&mobile.botdef_id).unwrap(); if dist <= botdef.build_dist { mobile.move_target = None; build_throughputs.push(BuildPart { amount: botdef.build_power as f64, repair: false, player: mobile.player_id, from: *id, to: to_build.id, }) } else { mobile.move_target = Some(to_build.position); } } else { mobile.current_command = Command::None; mobile.move_target = None; } } None => {} }, Command::Repair(to_build) => match mobiles2.get(&to_build) { Some(to_build) => { let botdef_of_to_build = bot_defs.get(&to_build.botdef_id).unwrap(); if to_build.life < botdef_of_to_build.max_life || to_build.con_completed < 1.0 { let dist = (to_build.position.coords - mobile.position.coords).magnitude(); let botdef = bot_defs.get(&mobile.botdef_id).unwrap(); if dist <= botdef.build_dist { mobile.move_target = None; build_throughputs.push(BuildPart { amount: botdef.build_power as f64, repair: to_build.con_completed >= 1.0, player: mobile.player_id, from: *id, to: to_build.id, }) } else { mobile.move_target = Some(to_build.position); } } else { mobile.current_command = Command::None; mobile.move_target = None; } } None => {} }, _ => {} } } } //Compute resource usage for each player struct ResourceUsage { metal: f64, energy: f64, } let mut resources_usage = FnvHashMap::, ResourceUsage>::default(); for BuildPart { amount, to, repair, from, player, } in build_throughputs.iter() { let stat = resources_usage.entry(*player).or_insert(ResourceUsage { metal: 0.0, energy: 0.0, }); *stat = ResourceUsage { metal: stat.metal + if *repair { 0.0 } else { *amount as f64 }, energy: 0.0, }; } //Compute what proportion of usage is usable without negative stock struct ResourceUsagePropMax { metal: f64, energy: f64, } let mut usage_props_max = FnvHashMap::, ResourceUsagePropMax>::default(); for (player_id, player) in players.iter_mut() { if let Some(ru) = resources_usage.get(player_id) { let current_metal_stock = player.metal; let current_energy_stock = player.energy; let metal_needed = ru.metal; let energy_needed = ru.energy; //TODO Energy count too let metal_prop_max: f64 = (current_metal_stock / metal_needed) .min(current_energy_stock / energy_needed) .min(1.0); usage_props_max.insert( *player_id, ResourceUsagePropMax { metal: metal_prop_max, energy: 1.0, }, ); player.metal = (player.metal - metal_needed * metal_prop_max).max(0.0); } } //SYNC beause we modify player directly instead of creating another step next //Compute build percent for each unit, refund player for overcost struct ResourceSurplus { metal: f64, energy: f64, } let mut resources_surplus = FnvHashMap::, ResourceSurplus>::default(); for BuildPart { amount, to, from, player, repair, } in build_throughputs { let kbot = kbots.get_mut(&to).unwrap(); let botdef = bot_defs.get(&kbot.botdef_id).unwrap(); let metal_available = amount * usage_props_max.get(&player).unwrap().metal; let metal_needed = if repair { 0.0 } else { (1.0 - kbot.con_completed as f64) * botdef.metal_cost as f64 }; let mut metal_used = metal_available; if metal_needed > metal_available { let metal_built = metal_available + kbot.con_completed as f64 * botdef.metal_cost as f64; kbot.con_completed = (metal_built / botdef.metal_cost as f64) as f32; } else { let metal_not_used = metal_available - metal_needed; metal_used = metal_available - metal_not_used; if !repair { players.get_mut(&player).unwrap().metal += metal_not_used; kbot.con_completed = 1.0; } } let lambda = if repair { amount as f32 } else { metal_used as f32 } / botdef.metal_cost as f32; kbot.life = ((kbot.life as f32 + lambda * botdef.max_life as f32).ceil() as i32) .min((botdef.max_life as f32 * kbot.con_completed).ceil() as i32); } frame_profiler.add("01b build compute", start.elapsed()); //Movement compute for (id, mobile) in kbots.iter_mut() { if mobile.con_completed >= 1.0 { if mobile.speed.magnitude_squared() > 0.001 || mobile.move_target.is_some() || !mobile.grounded { let botdef = bot_defs.get(&mobile.botdef_id).unwrap(); let grid_pos = grid_pos(mobile); let mut neighbors_id: Vec> = grid[grid_pos].clone(); let to_remove = neighbors_id.iter().position(|e| e == id).unwrap(); neighbors_id.remove(to_remove); let avoidance_force = avoid_neighbors_force(mobile, neighbors_id, &mobiles2) * 0.3; let TargetForce { target_force, stop_tracking, } = to_target_force(mobile, botdef); // arrows.push(Arrow { // position: mobile.position, // color: [target_force.norm(), 0.0, 0.0, 0.0], // end: mobile.position // + Vector3::new(target_force.x * 2.0, target_force.y * 2.0, 0.0), // }); // arrows.push(Arrow { // position: mobile.position, // color: [0.0, avoidance_force.norm(), 0.0, 0.0], // end: mobile.position // + Vector3::new(avoidance_force.x * 2.0, avoidance_force.y * 2.0, 0.0), // }); if stop_tracking { mobile.move_target = None; } let dir = avoidance_force + target_force; let dir_intensity = (avoidance_force.norm() + target_force.norm()) .max(0.0) .min(1.0); //Clamp in cone let wanted_angle: Angle = dir.into(); let current_angle = mobile.angle; fn clamp_abs(x: f32, max_abs: f32) -> f32 { let sign = x.signum(); sign * (x.abs().min(max_abs)) } let diff = (wanted_angle - (current_angle + mobile.angular_velocity.into())).rad; mobile.angular_velocity = clamp_abs( mobile.angular_velocity + clamp_abs(diff, botdef.turn_accel), botdef.max_turn_rate, ); let new_angle = current_angle + mobile.angular_velocity.into(); // current_angle.clamp_around(wanted_angle, mobile.angular_velocity.into()); mobile.angle = new_angle; let new_dir: Vector2 = new_angle.into(); mobile.dir = Vector3::new(new_dir.x, new_dir.y, 0.0); //TODO drift factor ? //drift = 1 (adherence = 0) // mobile.speed = mobile.speed + mobile.dir * botdef.accel * dir_intensity; //drift = 0 (adherence = 1) let speed_scalar = mobile.speed.xy().magnitude(); let thrust = if speed_scalar > 0.01 { dir.normalize().dot(&(mobile.speed.xy() / speed_scalar)) } else { 1.0 }; let accel = if mobile.move_target != None && thrust > 0.0 { botdef.accel * dir_intensity * thrust } else { -botdef.break_accel * thrust.abs() }; // arrows.push(Arrow { // position: mobile.position + Vector3::new(0.0, 0.0, 2.0), // color: [0.0, 0.0, accel, 0.0], // end: mobile.position // + Vector3::new(dir.x, dir.y, 0.0) * 4.0 // + Vector3::new(0.0, 0.0, 2.0), // }); // arrows.push(Arrow { // position: mobile.position + Vector3::new(0.0, 0.0, 1.0), // color: [0.0, 0.0, accel, 0.0], // end: mobile.position + mobile.dir * accel * 4.0 + Vector3::new(0.0, 0.0, 1.0), // }); mobile.speed = mobile.dir * (accel + mobile.speed.magnitude()).max(0.0); let speed = mobile.speed.magnitude(); if speed > botdef.max_speed { mobile.speed /= speed / botdef.max_speed; } mobile.position += mobile.speed; mobile.position.x = mobile .position .x .max(0.0) .min(heightmap_phy.width as f32 - 1.0); mobile.position.y = mobile .position .y .max(0.0) .min(heightmap_phy.height as f32 - 1.0); mobile.position.z = heightmap_phy.z_linear(mobile.position.x, mobile.position.y); mobile.grounded = true; mobile.up = heightmap_phy.normal(mobile.position.x, mobile.position.y); let y = -mobile.dir.cross(&mobile.up); let x = y.cross(&mobile.up); mobile.dir = x; mobile.weapon0_dir = (mobile.weapon0_dir + mobile.dir).normalize(); //w = v/r mobile.wheel0_angle += mobile.speed.norm() / 0.5; } } } frame_profiler.add("02 movement", start.elapsed()); //Remove dead kbot for (id, kbot) in kbots.iter() { if kbot.life <= 0 { kbots_dead.insert(*id); explosions.push(ExplosionEvent { position: Point3::from(kbot.position), size: 1.0, life_time: 1.2, }); } } for id in kbots_dead.iter() { kbots.remove(id); } } fn avoid_neighbors_force( me: &KBot, neighbors_id: Vec>, kbots: &FnvHashMap, KBot>, ) -> Vector2 { // could be speed/ brake // let prediction = 1.0; let pos = me.position + me.speed; let mut avoidance = Vector2::new(0.0, 0.0); for other_id in neighbors_id.iter() { let other = kbots.get(other_id).unwrap(); let o_pos = other.position + other.speed; let to_other = (o_pos.coords - pos.coords).xy(); let distance = (to_other.magnitude() - 1.1).max(0.1); let inv_distance = 1.0 / distance; let to_other_normalized = to_other * inv_distance; avoidance += -to_other_normalized * inv_distance; } avoidance } struct TargetForce { target_force: Vector2, stop_tracking: bool, } fn to_target_force(me: &KBot, botdef: &botdef::BotDef) -> TargetForce { if let Some(target) = me.move_target { let to_target = (target.coords - (me.position.coords + me.speed)).xy(); let to_target_distance = to_target.norm(); let will_to_go_target = if to_target_distance > botdef.radius { 1.0 } else { 1.0 - ((botdef.radius - to_target_distance) / botdef.radius) }; TargetForce { target_force: (to_target / to_target_distance) * will_to_go_target, stop_tracking: to_target_distance < botdef.radius / 2.0, } } else { TargetForce { target_force: Vector2::new(0.0, 0.0), stop_tracking: true, } } } ================================================ FILE: src/glsl.rs ================================================ use crate::gpu_obj::glsl_compiler; use std::fs::{self, DirEntry}; use std::io; use std::path::Path; use std::slice; pub fn compile_all_glsl() { println!("Compile all glsl"); let path = std::path::Path::new("./src/shader/"); let cb = |de: &DirEntry| { let path_to_read = de.path(); let ext = path_to_read.extension().unwrap().to_str().unwrap(); if !ext.contains("spirv") { println!("compiling {:?}", path_to_read); let spirv = glsl_compiler::load(path_to_read.to_str().unwrap()).unwrap(); let file_name = path_to_read.file_name().unwrap(); let mut path_to_write = path_to_read.parent().unwrap().to_path_buf(); path_to_write.push("compiled"); path_to_write.push(file_name); let path_to_write = path_to_write.with_extension(format!("{}.spirv", ext)); println!("write to {:?}", path_to_write); let slice_u8: Vec = spirv .iter() .map(|w| w.to_le_bytes().iter().copied().collect::>()) .flatten() .collect(); std::fs::write(path_to_write, slice_u8).unwrap(); } }; visit_dirs(path, &cb).unwrap(); } fn visit_dirs(dir: &Path, cb: &dyn Fn(&DirEntry)) -> io::Result<()> { if dir.is_dir() { for entry in fs::read_dir(dir)? { let entry = entry?; let path = entry.path(); if path.is_dir() { visit_dirs(&path, cb)?; } else { cb(&entry); } } } Ok(()) } ================================================ FILE: src/gpu_obj/arrow_gpu.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat}; pub struct ArrowGpu { vertex_buf: wgpu::Buffer, index_buf: wgpu::Buffer, index_count: usize, instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, } impl ArrowGpu { pub fn new( triangle_list: &model::TriangleList, device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, ) -> Self { log::trace!("ArrowGpu new"); // Create the vertex and index buffers let model::TriangleList { vertex_data, index_data, } = triangle_list; let vertex_buf = device .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(&vertex_data); let index_buf = device .create_buffer_mapped(index_data.len(), wgpu::BufferUsage::INDEX) .fill_from_slice(&index_data); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();; ArrowGpu { vertex_buf, index_buf, index_count: index_data.len(), instance_buf, instance_count: positions.len() as u32 / 3, pipeline, } } pub fn create_pipeline( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout], }); let vertex_size = std::mem::size_of::(); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/arrow.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/arrow.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[ wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Rgba32Float, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Rg16Float, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, ], depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_read_mask: 0, stencil_write_mask: 0, }), index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[ wgpu::VertexBufferDescriptor { stride: vertex_size as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 4, shader_location: 1, }, ], }, wgpu::VertexBufferDescriptor { stride: (4 * 20) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 0, shader_location: 2, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 4 * 4, shader_location: 3, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 4 * 8, shader_location: 4, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 4 * 12, shader_location: 5, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 4 * 16, shader_location: 6, }, ], }, ], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("ArrowGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, main_bind_group, &[]); rpass.set_index_buffer(&self.index_buf, 0); rpass.set_vertex_buffers(0, &[(&self.vertex_buf, 0), (&self.instance_buf, 0)]); rpass.draw_indexed(0..self.index_count as u32, 0, 0..self.instance_count as u32); } } pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("ArrowGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 20; } } impl super::trait_gpu::TraitGpu for ArrowGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline(device, main_bind_group_layout, format) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/blit_texture.rs ================================================ use super::glsl_compiler; use crate::model; use crate::utils::ImageRGBA8; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, Texture, TextureFormat, TextureView}; pub struct BlitTextureGpu { instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, bind_group_layout: BindGroupLayout, bind_group: BindGroup, noise_texture: Texture, } impl BlitTextureGpu { pub fn new( init_encoder: &mut wgpu::CommandEncoder, device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, img: ImageRGBA8, ) -> Self { log::trace!("BlitTextureGpu new"); let texels = img.data; let texture_extent = wgpu::Extent3d { width: img.w, height: img.h, depth: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { size: texture_extent, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, }); let temp_buf = device .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * img.w, image_height: img.h, }, wgpu::TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let noise_texture_view = texture.create_default_view(); let bind_group = Self::create_bind_group(device, &bind_group_layout, &noise_texture_view); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap(); BlitTextureGpu { instance_buf, instance_count: 0, pipeline, bind_group, bind_group_layout, noise_texture: texture, } } pub fn create_bind_group( device: &Device, bind_group_layout: &BindGroupLayout, noise_texture_view: &TextureView, ) -> BindGroup { // Create other resources let sampler_noise = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::Repeat, address_mode_v: wgpu::AddressMode::Repeat, address_mode_w: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); device.create_bind_group(&wgpu::BindGroupDescriptor { layout: bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(noise_texture_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler_noise), }, ], }) } pub fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/blit_texture.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/blit_texture.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: (4 * (8)) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 2, shader_location: 1, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 4, shader_location: 2, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 6, shader_location: 3, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("BlitTextureGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]); rpass.set_bind_group(0, main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.draw(0..4, 0..self.instance_count as u32); } } pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("BlitTextureGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 8; } } impl super::trait_gpu::TraitGpu for BlitTextureGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/explosion.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, Texture, TextureFormat, TextureView}; pub struct ExplosionGpu { instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, bind_group_layout: BindGroupLayout, bind_group: BindGroup, noise_texture: Texture, } impl ExplosionGpu { pub fn new( init_encoder: &mut wgpu::CommandEncoder, device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, current_position_att: &TextureView, normal_att: &TextureView, ) -> Self { log::trace!("ExplosionGpu new"); let size = 256u32; let texels = Self::open_noise(); let texture_extent = wgpu::Extent3d { width: size, height: size, depth: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { size: texture_extent, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, }); let temp_buf = device .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * size, image_height: size, }, wgpu::TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, wgpu::BindGroupLayoutBinding { binding: 2, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 3, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, wgpu::BindGroupLayoutBinding { binding: 4, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 5, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let noise_texture_view = texture.create_default_view(); let bind_group = Self::create_bind_group( device, &bind_group_layout, &noise_texture_view, current_position_att, normal_att, ); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap(); ExplosionGpu { instance_buf, instance_count: 0, pipeline, bind_group, bind_group_layout, noise_texture: texture, } } pub fn create_bind_group( device: &Device, bind_group_layout: &BindGroupLayout, noise_texture_view: &TextureView, current_position_att: &TextureView, normal_att: &TextureView, ) -> BindGroup { // Create other resources let sampler_noise = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::Repeat, address_mode_v: wgpu::AddressMode::Repeat, address_mode_w: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let sampler_position = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let sampler_normal = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); device.create_bind_group(&wgpu::BindGroupDescriptor { layout: bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(noise_texture_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler_noise), }, wgpu::Binding { binding: 2, resource: wgpu::BindingResource::TextureView(current_position_att), }, wgpu::Binding { binding: 3, resource: wgpu::BindingResource::Sampler(&sampler_position), }, wgpu::Binding { binding: 4, resource: wgpu::BindingResource::TextureView(normal_att), }, wgpu::Binding { binding: 5, resource: wgpu::BindingResource::Sampler(&sampler_normal), }, ], }) } pub fn update_bind_group( &mut self, device: &Device, current_position_att: &TextureView, normal_att: &TextureView, ) { let noise_texture_view = self.noise_texture.create_default_view(); self.bind_group = Self::create_bind_group( device, &self.bind_group_layout, &noise_texture_view, current_position_att, normal_att, ); } pub fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/explosion.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/explosion.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::One, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: (4 * (6)) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float3, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 3, shader_location: 1, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 4, shader_location: 2, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 5, shader_location: 3, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn open_noise() -> Vec { use byteorder::{BigEndian, ReadBytesExt}; use std::fs::File; // The decoder is a build for reader and can be used to set various decoding options // via `Transformations`. The default output transformation is `Transformations::EXPAND // | Transformations::STRIP_ALPHA`. let mut decoder = png::Decoder::new(File::open(r"src/asset/2d/noise.png").unwrap()); decoder.set_transformations(png::Transformations::IDENTITY); let (info, mut reader) = decoder.read_info().unwrap(); // Display image metadata. log::debug!("info: {:?}", info.width); log::debug!("height: {:?}", info.height); log::debug!("bit depth: {:?}", info.bit_depth); log::debug!("buffer size: {:?}", info.buffer_size()); // Allocate the output buffer. let mut buf = vec![0; info.buffer_size()]; // Read the next frame. Currently this function should only called once. // The default options reader.next_frame(&mut buf).unwrap(); buf } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("ExplosionGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]); rpass.set_bind_group(0, main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.draw(0..4, 0..self.instance_count as u32); } } pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("ExplosionGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 6; } } impl super::trait_gpu::TraitGpu for ExplosionGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/glsl_compiler.rs ================================================ #[cfg(feature = "use_shaderc")] use shaderc; #[allow(dead_code)] pub enum ShaderStage { Vertex, Fragment, Compute, } fn str_to_shader_stage(str: &str) -> ShaderStage { if str.ends_with("vert") { ShaderStage::Vertex } else if str.ends_with("frag") { ShaderStage::Fragment } else { ShaderStage::Compute } } use std::error; use std::fmt; use std::slice; pub type Result = std::result::Result; #[derive(Debug, Clone)] pub struct ShaderCompilationError { pub msg: String, } impl fmt::Display for ShaderCompilationError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "shader compilation error {}", self.msg) } } impl error::Error for ShaderCompilationError { fn source(&self) -> Option<&(dyn error::Error + 'static)> { None } } #[cfg(feature = "use_glsl_to_spirv")] pub fn load(rel_path: &str) -> Result> { let stage = str_to_shader_stage(rel_path); log::debug!("glsl_to_spirv : compiling {}", rel_path); let glsl_code = std::fs::read_to_string(std::path::Path::new(rel_path)).unwrap(); let ty = match stage { ShaderStage::Vertex => glsl_to_spirv::ShaderType::Vertex, ShaderStage::Fragment => glsl_to_spirv::ShaderType::Fragment, ShaderStage::Compute => glsl_to_spirv::ShaderType::Compute, }; Ok( wgpu::read_spirv(glsl_to_spirv::compile(&glsl_code, ty).map_err(|e| { ShaderCompilationError { msg: format!("{}", e), } })?) .map_err(|e| ShaderCompilationError { msg: format!("{}", e), })?, ) // wgpu::read_spirv(glsl_to_spirv::compile(&glsl_code, ty).unwrap()).unwrap() } #[cfg(feature = "use_shaderc")] pub fn load(rel_path: &str) -> Result> { let stage = str_to_shader_stage(rel_path); log::debug!("shaderc : compiling {}", rel_path); let glsl_code = std::fs::read_to_string(std::path::Path::new(rel_path)).unwrap(); let ty = match stage { ShaderStage::Vertex => shaderc::ShaderKind::Vertex, ShaderStage::Fragment => shaderc::ShaderKind::Fragment, ShaderStage::Compute => shaderc::ShaderKind::Compute, }; let mut compiler = shaderc::Compiler::new().unwrap(); let mut options = shaderc::CompileOptions::new().unwrap(); options.add_macro_definition("EP", Some("main")); let binary_result = compiler .compile_into_spirv(&glsl_code, ty, rel_path, "main", Some(&options)) .map_err(|e| ShaderCompilationError { msg: format!("{}", e), })?; Ok(binary_result.as_binary().to_owned()) } #[cfg(not(any(feature = "use_shaderc", feature = "use_glsl_to_spirv")))] pub fn load(rel_path: &str) -> Result> { let glsl_path = std::path::Path::new(rel_path); let file_name = glsl_path.file_name().unwrap(); let ext = glsl_path.extension().unwrap().to_str().unwrap(); let mut spirv_path = glsl_path.to_path_buf().parent().unwrap().to_path_buf(); spirv_path.push("compiled"); spirv_path.push(file_name); let spirv_path = spirv_path.with_extension(format!("{}.spirv", ext)); log::debug!("spirv : reading {:?}", spirv_path); let spirv = std::fs::read(spirv_path).unwrap(); use std::convert::TryInto; let vec_u32: Vec = spirv .chunks_exact(4) .map(|chunk| u32::from_le_bytes(chunk.try_into().unwrap())) .collect(); Ok(vec_u32) } ================================================ FILE: src/gpu_obj/gpu.rs ================================================ use wgpu::SwapChain; pub struct WgpuState { pub sc_desc: wgpu::SwapChainDescriptor, pub device: wgpu::Device, pub window: winit::window::Window, pub hidpi_factor: f64, pub swap_chain: SwapChain, pub surface: wgpu::Surface, pub queue: wgpu::Queue, } impl WgpuState { pub fn new(window: winit::window::Window) -> Self { let (hidpi_factor, size, surface) = { let hidpi_factor = window.hidpi_factor(); window.set_title("Oxidator"); log::info!("hidpi scaling: {}", hidpi_factor); let size = window.inner_size().to_physical(hidpi_factor); let surface = wgpu::Surface::create(&window); (hidpi_factor, size, surface) }; let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::HighPerformance, backends: wgpu::BackendBit::PRIMARY, }) .unwrap(); let (device, queue) = adapter.request_device(&wgpu::DeviceDescriptor { extensions: wgpu::Extensions { anisotropic_filtering: false, }, limits: wgpu::Limits::default(), }); let sc_desc = wgpu::SwapChainDescriptor { usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, format: wgpu::TextureFormat::Bgra8UnormSrgb, width: size.width.round() as u32, height: size.height.round() as u32, present_mode: wgpu::PresentMode::NoVsync, }; let swap_chain = device.create_swap_chain(&surface, &sc_desc); // let physical_wanted = winit::dpi::PhysicalSize { // width: 1281.0, // height: 720.0, // }; // let logical_wanted = physical_wanted.to_logical(hidpi_factor); // window.set_inner_size(logical_wanted); WgpuState { sc_desc, device, window, hidpi_factor, swap_chain, surface, queue, } } } ================================================ FILE: src/gpu_obj/health_bar.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat}; pub struct HealthBarGpu { instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, } impl HealthBarGpu { pub fn new( device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, ) -> Self { log::trace!("HealthBarGpu new"); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap(); HealthBarGpu { instance_buf, instance_count: 0, pipeline, } } pub fn create_pipeline( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/health_bar.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/health_bar.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: (4 * (2 + 2 + 2 + 1)) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 2, shader_location: 1, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 4, shader_location: 2, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 5, shader_location: 3, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 6, shader_location: 4, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("HealthBarGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]); rpass.set_bind_group(0, main_bind_group, &[]); rpass.draw(0..4, 0..self.instance_count as u32); } } pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("HealthBarGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 7; } } impl super::trait_gpu::TraitGpu for HealthBarGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline(device, main_bind_group_layout, format) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/heightmap_gpu.rs ================================================ use super::glsl_compiler; use super::heightmap_helper; use crate::heightmap_phy; use wgpu::{BindGroup, BindGroupLayout, RenderPass, RenderPipeline, Texture, TextureFormat}; use wgpu::{CommandEncoder, Device}; const ZONE_SIZE_MIP0: usize = 64; const UPDATE_PER_STEP: usize = 300; const MIP_COUNT: u32 = 5; pub const MAX_Z: f32 = 511.0; pub struct HeightmapGpu { pipeline: RenderPipeline, bind_group_layout: BindGroupLayout, bind_group: BindGroup, vertex_buf: wgpu::Buffer, index_buf: wgpu::Buffer, index_count: usize, pub phy: heightmap_phy::HeightmapPhy, ring_size: u32, texture: Texture, texture_lod: Texture, uniform_buf: wgpu::Buffer, zone_to_update_mip0: Vec, zone_to_update_mip1: Vec, zone_to_update_mip2: Vec, mip4_to_update: bool, } impl HeightmapGpu { pub fn new( device: &Device, init_encoder: &mut CommandEncoder, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, phy: heightmap_phy::HeightmapPhy, ) -> Self { log::trace!("HeightmapGpu new"); let texture_view_checker = { let size = 2u32; let texels = crate::procedural_texels::checker(size as usize); let texture_extent = wgpu::Extent3d { width: size, height: size, depth: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { size: texture_extent, array_layer_count: 1, mip_level_count: 2, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, }); let temp_buf = device .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * size, image_height: size, }, wgpu::TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); { let texture_extent = wgpu::Extent3d { width: 1, height: 1, depth: 1, }; let temp_buf = device .create_buffer_mapped(4, wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&[123, 123, 123, 255]); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * 1, image_height: 1, }, wgpu::TextureCopyView { texture: &texture, mip_level: 1, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); } texture.create_default_view() }; let sampler_checker = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::Repeat, address_mode_v: wgpu::AddressMode::Repeat, address_mode_w: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let (texture_view_lod, texture_lod) = { let width = phy.width as u32 / ZONE_SIZE_MIP0 as u32; let height = phy.height as u32 / ZONE_SIZE_MIP0 as u32; let size = phy.width as u32 * phy.height as u32; let texture_extent = wgpu::Extent3d { width, height, depth: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { size: texture_extent, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::R32Float, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, }); let mut texels = vec![0_f32; size as usize]; texels[0] = 4.0; let temp_buf = device .create_buffer_mapped(texels.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * width, image_height: height, }, wgpu::TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); (texture.create_default_view(), texture) }; let sampler_lod = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::Repeat, address_mode_v: wgpu::AddressMode::Repeat, address_mode_w: wgpu::AddressMode::Repeat, mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let start = std::time::Instant::now(); let texture_extent = wgpu::Extent3d { width: phy.width as u32, height: phy.height as u32, depth: 1, }; let texture = device.create_texture(&wgpu::TextureDescriptor { size: texture_extent, array_layer_count: 1, mip_level_count: MIP_COUNT, sample_count: 1, dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::R32Float, usage: wgpu::TextureUsage::SAMPLED | wgpu::TextureUsage::COPY_DST, }); let temp_buf = device .create_buffer_mapped(phy.texels.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&phy.texels); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * phy.width as u32, image_height: phy.height as u32, }, wgpu::TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); let mut mipmaper = |mip: u32| { let m = 2_u32.pow(mip); let width = phy.width as u32 / m; let height = phy.height as u32 / m; let texture_extent = wgpu::Extent3d { width, height, depth: 1, }; let mut texels2 = Vec::new(); for j in 0..height { for i in 0..width { texels2.push(phy.texels[(i * m + (j * m) * width * m) as usize]); } } let temp_buf = device .create_buffer_mapped(texels2.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels2); init_encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * width, image_height: height, }, wgpu::TextureCopyView { texture: &texture, mip_level: mip, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); }; for i in 1..MIP_COUNT { mipmaper(i); } let texture_view_height = texture.create_default_view(); let sampler_height = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); //Map size let ring_size = 128; let map_size_cam_pos = [ phy.width as f32, phy.height as f32, ring_size as f32, 0.0, 0.0, ]; let uniform_buf = device .create_buffer_mapped(5, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST) .fill_from_slice(&map_size_cam_pos); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::UniformBuffer { dynamic: false }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 2, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, wgpu::BindGroupLayoutBinding { binding: 3, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 4, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, wgpu::BindGroupLayoutBinding { binding: 5, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 6, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap(); // Create bind group let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::Buffer { buffer: &uniform_buf, range: 0..20, }, }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::TextureView(&texture_view_checker), }, wgpu::Binding { binding: 2, resource: wgpu::BindingResource::Sampler(&sampler_checker), }, wgpu::Binding { binding: 3, resource: wgpu::BindingResource::TextureView(&texture_view_height), }, wgpu::Binding { binding: 4, resource: wgpu::BindingResource::Sampler(&sampler_height), }, wgpu::Binding { binding: 5, resource: wgpu::BindingResource::TextureView(&texture_view_lod), }, wgpu::Binding { binding: 6, resource: wgpu::BindingResource::Sampler(&sampler_lod), }, ], }); let (vertex_data, height_index_data) = heightmap_helper::create_vertex_index_rings(ring_size); // heightmap::create_vertices_indices(width, height, 0.0); let vertex_buf = device .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(&vertex_data); let index_buf = device .create_buffer_mapped(height_index_data.len(), wgpu::BufferUsage::INDEX) .fill_from_slice(&height_index_data); let index_count = height_index_data.len(); let mut zone_to_update_mip0 = Vec::new(); for _ in (0..=phy.width).step_by(ZONE_SIZE_MIP0) { for _ in (0..=phy.height).step_by(ZONE_SIZE_MIP0) { zone_to_update_mip0.push(0); } } let mut zone_to_update_mip1 = Vec::new(); for _ in (0..=phy.width).step_by(ZONE_SIZE_MIP0 * 2) { for _ in (0..=phy.height).step_by(ZONE_SIZE_MIP0 * 2) { zone_to_update_mip1.push(0); } } let mut zone_to_update_mip2 = Vec::new(); for _ in (0..=phy.width).step_by(ZONE_SIZE_MIP0 * 4) { for _ in (0..=phy.height).step_by(ZONE_SIZE_MIP0 * 4) { zone_to_update_mip2.push(0); } } HeightmapGpu { pipeline, bind_group, bind_group_layout, vertex_buf, index_buf, index_count, phy, ring_size, texture, texture_lod, uniform_buf, zone_to_update_mip0, zone_to_update_mip1, zone_to_update_mip2, mip4_to_update: false, } } pub fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { // Create pipeline layout let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[main_bind_group_layout, bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/heightmap.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/heightmap.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[ wgpu::ColorStateDescriptor { format, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Rgba32Float, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Rg16Float, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, ], depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_read_mask: 0, stencil_write_mask: 0, }), index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: std::mem::size_of::() as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 2, shader_location: 1, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn mipmap_update( &self, mip: u32, device: &Device, encoder: &mut CommandEncoder, texture: &Texture, texels: &[f32], texels_width: u32, i: u32, j: u32, width: u32, height: u32, ) { let m = 2_u32.pow(mip); let width = width / m; let height = height / m; let texture_extent = wgpu::Extent3d { width, height, depth: 1, }; let min_x = i / m; let min_y = j / m; let mut texels2 = Vec::new(); for j in min_y..(min_y + height) { for i in min_x..(min_x + width) { texels2.push(texels[(i * m + (j * m) * (texels_width / m) * m) as usize]); } } let temp_buf = device .create_buffer_mapped(texels2.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&texels2); encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * width, image_height: height, }, wgpu::TextureCopyView { texture, mip_level: mip, array_layer: 0, origin: wgpu::Origin3d { x: min_x as f32, y: min_y as f32, z: 0.0, }, }, texture_extent, ); } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("HeightmapGpu render"); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.set_index_buffer(&self.index_buf, 0); rpass.set_vertex_buffers(0, &[(&self.vertex_buf, 0)]); rpass.draw_indexed(0..(self.index_count) as u32, 0, 0..1); } pub fn update_uniform( &mut self, device: &Device, encoder: &mut CommandEncoder, camera_x: f32, camera_y: f32, ) { log::trace!("HeightmapGpu update_uniform"); //Map size let map_size_cam_pos = [ self.phy.width as u32 as f32, self.phy.height as u32 as f32, self.ring_size as f32, (camera_x.max(0.0).min(self.phy.width as u32 as f32) / 1.0), (camera_y.max(0.0).min(self.phy.height as u32 as f32) / 1.0), ]; let uniform_buf = device .create_buffer_mapped( 5, wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST | wgpu::BufferUsage::COPY_SRC, ) .fill_from_slice(&map_size_cam_pos); encoder.copy_buffer_to_buffer(&uniform_buf, 0, &self.uniform_buf, 0, 20); } pub fn step(&mut self, device: &Device, encoder: &mut CommandEncoder) { let mut update_left = UPDATE_PER_STEP; if self.mip4_to_update { self.mip4_to_update = false; update_left = update_left / 2; for mip in 3..MIP_COUNT { self.mipmap_update( mip, device, encoder, &self.texture, &self.phy.texels, self.phy.width as u32, 0, 0, self.phy.width as u32, self.phy.height as u32, ); } } if update_left > 0 { let mut zone_to_update_mip2 = self .zone_to_update_mip2 .iter() .enumerate() .filter(|(_, b)| **b != 0) .collect::>(); let update_to_do = zone_to_update_mip2 .len() .min(UPDATE_PER_STEP) .min(update_left); update_left -= update_to_do; zone_to_update_mip2.sort_by_key(|(_, i)| **i); let indices: Vec = zone_to_update_mip2 .iter() .map(|(index, _)| *index) .collect(); for index in indices.iter().skip(update_to_do) { self.zone_to_update_mip2[*index] -= 1; } for index in indices.iter().take(update_to_do) { self.zone_to_update_mip2[*index] = 0; let i = *index as u32 % (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 4) as u32); let j = *index as u32 / (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 4) as u32); let min_x = i * ZONE_SIZE_MIP0 as u32 * 4; let min_y = j * ZONE_SIZE_MIP0 as u32 * 4; if min_x < self.phy.width as u32 && min_y < self.phy.height as u32 { let width = (ZONE_SIZE_MIP0 as u32 * 4).min(self.phy.width as u32 - min_x); let height = (ZONE_SIZE_MIP0 as u32 * 4).min(self.phy.height as u32 - min_y); self.mipmap_update( 2, device, encoder, &self.texture, &self.phy.texels, self.phy.width as u32, min_x, min_y, width, height, ); } } } if update_left > 0 { let mut zone_to_update_mip1 = self .zone_to_update_mip1 .iter() .enumerate() .filter(|(_, b)| **b != 0) .collect::>(); let update_to_do = zone_to_update_mip1 .len() .min(UPDATE_PER_STEP) .min(update_left); update_left -= update_to_do; zone_to_update_mip1.sort_by_key(|(_, i)| **i); let indices: Vec = zone_to_update_mip1 .iter() .map(|(index, _)| *index) .collect(); for index in indices.iter().skip(update_to_do) { self.zone_to_update_mip1[*index] -= 1; } for index in indices.iter().take(update_to_do) { self.zone_to_update_mip1[*index] = 0; let i = *index as u32 % (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 2) as u32); let j = *index as u32 / (self.phy.width as u32 / (ZONE_SIZE_MIP0 * 2) as u32); let min_x = i * ZONE_SIZE_MIP0 as u32 * 2; let min_y = j * ZONE_SIZE_MIP0 as u32 * 2; if min_x < self.phy.width as u32 && min_y < self.phy.height as u32 { let width = (ZONE_SIZE_MIP0 as u32 * 2).min(self.phy.width as u32 - min_x); let height = (ZONE_SIZE_MIP0 as u32 * 2).min(self.phy.height as u32 - min_y); self.mipmap_update( 1, device, encoder, &self.texture, &self.phy.texels, self.phy.width as u32, min_x, min_y, width, height, ); } } } if update_left > 0 { let mut zone_to_update_mip0 = self .zone_to_update_mip0 .iter() .enumerate() .filter(|(_, b)| **b != 0) .collect::>(); let update_to_do = zone_to_update_mip0 .len() .min(UPDATE_PER_STEP) .min(update_left); update_left -= update_to_do; zone_to_update_mip0.sort_by_key(|(_, i)| **i); let indices: Vec = zone_to_update_mip0 .iter() .map(|(index, _)| *index) .collect(); for index in indices.iter().skip(update_to_do) { self.zone_to_update_mip0[*index] -= 1; } for index in indices.iter().take(update_to_do) { self.zone_to_update_mip0[*index] = 0; let i = *index as u32 % (self.phy.width as u32 / ZONE_SIZE_MIP0 as u32); let j = *index as u32 / (self.phy.width as u32 / ZONE_SIZE_MIP0 as u32); let min_x = i * ZONE_SIZE_MIP0 as u32; let min_y = j * ZONE_SIZE_MIP0 as u32; if min_x < self.phy.width as u32 && min_y < self.phy.height as u32 { let width = (ZONE_SIZE_MIP0 as u32).min(self.phy.width as u32 - min_x); let height = (ZONE_SIZE_MIP0 as u32).min(self.phy.height as u32 - min_y); self.mipmap_update( 0, device, encoder, &self.texture, &self.phy.texels, self.phy.width as u32, min_x, min_y, width, height, ); } } } //Update lod texture let width = self.phy.width as u32 / ZONE_SIZE_MIP0 as u32; let height = self.phy.height as u32 / ZONE_SIZE_MIP0 as u32; let size = width * height; let texture_extent = wgpu::Extent3d { width, height, depth: 1, }; let mut lod = vec![3.0f32; size as usize]; for j in 0..height as usize { for i in 0..width as usize { if self.zone_to_update_mip0[i + j * width as usize] == 0 { lod[i + j * width as usize] = 0.0; } else if self.zone_to_update_mip1[i / 2 + (j / 2) * width as usize / 2] == 0 { lod[i + j * width as usize] = 1.0; } else if self.zone_to_update_mip2[i / 4 + (j / 4) * (width as usize / 4)] == 0 { lod[i + j * width as usize] = 2.0; } } } let temp_buf = device .create_buffer_mapped(lod.len(), wgpu::BufferUsage::COPY_SRC) .fill_from_slice(&lod); encoder.copy_buffer_to_texture( wgpu::BufferCopyView { buffer: &temp_buf, offset: 0, row_pitch: 4 * width, image_height: height, }, wgpu::TextureCopyView { texture: &self.texture_lod, mip_level: 0, array_layer: 0, origin: wgpu::Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, texture_extent, ); } pub fn update_rect(&mut self, min_x: u32, min_y: u32, width: u32, height: u32) { for i in (min_x / ZONE_SIZE_MIP0 as u32)..=(min_x + width) / ZONE_SIZE_MIP0 as u32 { for j in (min_y / ZONE_SIZE_MIP0 as u32)..=(min_y + height) / ZONE_SIZE_MIP0 as u32 { let width = self.phy.width as u32 / ZONE_SIZE_MIP0 as u32; let rank = &mut self.zone_to_update_mip2[(i / 4 + (j / 4) * (width / 4)) as usize]; if *rank == 0 { *rank = -1; } let rank = &mut self.zone_to_update_mip1[(i / 2 + (j / 2) * (width / 2)) as usize]; if *rank == 0 { *rank = -1; } let rank = &mut self.zone_to_update_mip0[(i + j * width) as usize]; if *rank == 0 { *rank = -1; } } } self.mip4_to_update = true; } } impl super::trait_gpu::TraitGpu for HeightmapGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/heightmap_helper.rs ================================================ use std::collections::HashMap; use std::hash::Hash; use std::hash::Hasher; #[derive(Clone, Copy, Debug)] pub struct Vertex { _pos: [f32; 2], _mip: f32, } impl PartialEq for Vertex { fn eq(&self, other: &Vertex) -> bool { self.canonicalize() == other.canonicalize() } } impl Eq for Vertex {} impl Vertex { fn canonicalize(&self) -> i128 { (self._pos[0] * 1024.0 * 1024.0).round() as i128 + (self._pos[1] * 1024.0 * 1024.0 * 1024.0 * 1024.0).round() as i128 } } impl Hash for Vertex { fn hash(&self, state: &mut H) where H: Hasher, { self.canonicalize().hash(state); } } pub fn z(x: f32, y: f32) -> f32 { // (49.0 // + 30.0 * f32::sin((x + y) / 95.0) // + 15.0 * (f32::sin(x / 20.0) * f32::cos(y / 45.0 + 1.554)) // + 3.0 * (f32::sin(x / 3.0 + f32::sin(x / 12.0)) * f32::cos(y / 3.3 + 1.94)) // + 0.0 * 1.0 * (f32::sin(x * 3.0) * f32::cos(y * 3.3 + 1.94))) 50.0_f32.min(511.0).max(0.0) // 100.0 // + 50.0 * f32::sin(4.0 * x * std::f32::consts::PI / 1024.0) // + 50.0 * f32::cos(4.0 * y * std::f32::consts::PI / 1024.0) // 10.0 * (0.5 + 0.5 * (f32::sin(x * 3.141592 / 2.0) * f32::cos(y * 3.141592 / 2.0))) } pub fn create_texels(width: u32, height: u32, t: f32) -> Vec { let mut texels = Vec::with_capacity((width * height) as usize); for j in 0..height { for i in 0..width { texels.push(z(i as f32 + t, j as f32 + t)); } } texels } pub fn create_vertex_index_rings(hsize: u32) -> (Vec, Vec) { let nb_square = ((hsize - 1) * (hsize - 1)) as usize; let mut vertex_data = Vec::with_capacity(nb_square * 4); let mut index_data = Vec::with_capacity(nb_square * 4); let vertex = |x: f32, y: f32| -> Vertex { Vertex { _pos: [x, y], _mip: 0.0, } }; for i in 0_u32..hsize { for j in 0_u32..hsize { let index_a: u32 = vertex_data.len() as u32; let a = vertex(i as f32, j as f32); let b = vertex(i as f32 + 1.0, j as f32); let c = vertex(i as f32 + 1.0, j as f32 + 1.0); let d = vertex(i as f32, j as f32 + 1.0); vertex_data.push(a); vertex_data.push(b); vertex_data.push(c); vertex_data.push(d); index_data.push(index_a); index_data.push(index_a + 1); index_data.push(index_a + 2); index_data.push(index_a); index_data.push(index_a + 2); index_data.push(index_a + 3); } } let vertex = |x: f32, y: f32, m: f32| -> Vertex { Vertex { _pos: [x, y], _mip: m, } }; log::trace!("{}", vertex_data.len()); enum Pass { Step(i32), Trans { from: i32, to: i32 }, } let mut passes = vec![]; passes.push(Pass::Trans { from: 1, to: 2 }); passes.extend((0..63).into_iter().map(|_| Pass::Step(2))); passes.push(Pass::Trans { from: 2, to: 4 }); passes.extend((0..63).into_iter().map(|_| Pass::Step(4))); passes.push(Pass::Trans { from: 4, to: 8 }); passes.extend((0..31).into_iter().map(|_| Pass::Step(8))); passes.push(Pass::Trans { from: 8, to: 16 }); passes.extend((0..80).into_iter().map(|_| Pass::Step(16))); // passes.push(Pass::Trans { from: 16, to: 32 }); // passes.extend((0..50).into_iter().map(|e| Pass::Step(32))); fn power_to_exp(i: i32) -> f32 { match i { 1 => 0.0, 2 => 1.0, 4 => 2.0, 8 => 3.0, 16 => 4.0, _ => 4.0, } } let mut start_min = hsize as i32; for pass in passes.iter() { match pass { Pass::Trans { from, to } => { log::trace!("Pass::Trans {} {}", from, to); let m = (power_to_exp(*from) + power_to_exp(*to)) / 2.0; log::trace!("m {}", m); let i = start_min; let j = start_min; { let index_a: u32 = vertex_data.len() as u32; let a = vertex(i as f32, j as f32, m); let b = vertex(i as f32 + *to as f32, j as f32, m); let c = vertex(i as f32 + *to as f32, j as f32 + *to as f32, m); let d = vertex(i as f32, j as f32 + *to as f32, m); vertex_data.push(a); vertex_data.push(b); vertex_data.push(c); vertex_data.push(d); index_data.push(index_a); index_data.push(index_a + 1); index_data.push(index_a + 2); index_data.push(index_a); index_data.push(index_a + 2); index_data.push(index_a + 3); }; let i = start_min; for j in (0..=start_min - *to).step_by(*to as usize) { let index_a: u32 = vertex_data.len() as u32; let a = vertex(i as f32, j as f32, m); let b = vertex(i as f32 + *to as f32, j as f32, m); let c = vertex(i as f32 + *to as f32, j as f32 + *to as f32, m); let d = vertex(i as f32, j as f32 + *to as f32, m); let e = vertex(i as f32, j as f32 + *from as f32, m); vertex_data.push(a); vertex_data.push(b); vertex_data.push(c); vertex_data.push(d); vertex_data.push(e); index_data.push(index_a + 4); index_data.push(index_a); index_data.push(index_a + 1); index_data.push(index_a + 4); index_data.push(index_a + 1); index_data.push(index_a + 2); index_data.push(index_a + 4); index_data.push(index_a + 2); index_data.push(index_a + 3); } let j = start_min; for i in (0..=start_min - *to).step_by(*to as usize) { let index_a: u32 = vertex_data.len() as u32; let a = vertex(i as f32, j as f32, m); let b = vertex(i as f32 + *to as f32, j as f32, m); let c = vertex(i as f32 + *to as f32, j as f32 + *to as f32, m); let d = vertex(i as f32, j as f32 + *to as f32, m); let e = vertex(i as f32 + *from as f32, j as f32, m); vertex_data.push(a); vertex_data.push(b); vertex_data.push(c); vertex_data.push(d); vertex_data.push(e); index_data.push(index_a + 4); index_data.push(index_a + 1); index_data.push(index_a + 2); index_data.push(index_a + 4); index_data.push(index_a + 2); index_data.push(index_a + 3); index_data.push(index_a + 4); index_data.push(index_a + 3); index_data.push(index_a); } start_min += *to; } Pass::Step(step) => { let m = power_to_exp(*step); let mut make_square = |i, j, step| { let index_a: u32 = vertex_data.len() as u32; let a = vertex(i as f32, j as f32, m); let b = vertex(i as f32 + step as f32, j as f32, m); let c = vertex(i as f32 + step as f32, j as f32 + step as f32, m); let d = vertex(i as f32, j as f32 + step as f32, m); { vertex_data.push(a); vertex_data.push(b); vertex_data.push(c); vertex_data.push(d); index_data.push(index_a); index_data.push(index_a + 1); index_data.push(index_a + 2); index_data.push(index_a); index_data.push(index_a + 2); index_data.push(index_a + 3); } }; let j = start_min; for i in (0..=start_min).step_by(*step as usize) { make_square(i, j, *step); } let i = start_min; for j in (0..start_min).step_by(*step as usize) { make_square(i, j, *step); } start_min += *step; } } } log::trace!("Passes Done"); log::trace!("index_data size {}", index_data.len()); log::trace!("vertex_data size {}", vertex_data.len()); { let mut symmetry_vertex_data_left = Vec::new(); for &vert in vertex_data.iter() { symmetry_vertex_data_left.push(Vertex { _pos: [-1.0 * vert._pos[0], 1.0 * vert._pos[1]], ..vert }); } let copie: Vec = index_data.iter().copied().collect(); let mut symmetry_index_data_left: Vec = copie .chunks(3) .into_iter() .flat_map(|e| vec![e[1], e[0], e[2]]) .map(|i| i + vertex_data.len() as u32) .collect(); for e in symmetry_index_data_left .chunks_mut(6) .take((hsize * hsize) as usize) { e[1] = e[5]; e[3] = e[0]; } let mut symmetry_vertex_data_down = Vec::new(); for &vert in vertex_data.iter() { symmetry_vertex_data_down.push(Vertex { _pos: [1.0 * vert._pos[0], -1.0 * vert._pos[1]], ..vert }); } let copie: Vec = index_data.iter().copied().collect(); let mut symmetry_index_data_down: Vec = copie .chunks(3) .into_iter() .flat_map(|e| vec![e[1], e[0], e[2]]) .map(|i| i + 2 * vertex_data.len() as u32) .collect(); for e in symmetry_index_data_down .chunks_mut(6) .take((hsize * hsize) as usize) { e[1] = e[5]; e[3] = e[0]; } let mut symmetry_vertex_data_down_and_left = Vec::new(); for &vert in vertex_data.iter() { symmetry_vertex_data_down_and_left.push(Vertex { _pos: [-1.0 * vert._pos[0], -1.0 * vert._pos[1]], ..vert }); } let copie: Vec = index_data.iter().copied().collect(); let symmetry_index_data_down_and_left: Vec = copie .into_iter() .map(|i| i + 3 * vertex_data.len() as u32) .collect(); vertex_data.extend(symmetry_vertex_data_left); index_data.extend(symmetry_index_data_left); vertex_data.extend(symmetry_vertex_data_down); index_data.extend(symmetry_index_data_down); vertex_data.extend(symmetry_vertex_data_down_and_left); index_data.extend(symmetry_index_data_down_and_left); } log::trace!("Symmetry Done"); log::trace!("index_data size {}", index_data.len()); log::trace!("vertex_data size {}", vertex_data.len()); let (vertex_data, index_data) = optimize_vertex_index(vertex_data, index_data); (vertex_data, index_data) } pub fn optimize_vertex_index( vertex_data: Vec, mut index_data: Vec, ) -> (Vec, Vec) { let start = std::time::Instant::now(); log::trace!("Before Optimisation"); log::trace!("index_data size {}", index_data.len()); log::trace!("vertex_data size {}", vertex_data.len()); let mut new_vertex_data: Vec = Vec::new(); let mut map: HashMap> = HashMap::new(); for v in &vertex_data { map.insert(v.clone(), None); } for i in index_data.iter_mut() { let v = &vertex_data[*i as usize]; if let Some(position) = map.get(v).unwrap() { *i = *position as u32; } else { new_vertex_data.push(v.clone()); let new_index = new_vertex_data.len() - 1; map.insert(v.clone(), Some(new_index)); *i = new_index as u32; } } log::trace!("Optimisation Done"); log::trace!("index_data size {}", index_data.len()); log::trace!("vertex_data size {}", new_vertex_data.len()); log::trace!("Optimisation took {}us", start.elapsed().as_micros()); (new_vertex_data, index_data) } ================================================ FILE: src/gpu_obj/imgui_wgpu.rs ================================================ use imgui::{Context, DrawCmd::Elements, DrawIdx, DrawList, DrawVert, TextureId, Textures, Ui}; use std::mem::size_of; use wgpu::*; pub type RendererResult = Result; #[derive(Clone, Debug)] pub enum RendererError { BadTexture(TextureId), } fn get_program_link() -> (&'static str, &'static str) { (("./src/shader/imgui.vert"), ("./src/shader/imgui.frag")) } /// A container for a bindable texture to be used internally. struct Texture { bind_group: BindGroup, } impl Texture { /// Creates a new imgui texture from a wgpu texture. fn new(texture: wgpu::Texture, layout: &BindGroupLayout, device: &Device) -> Self { // Extract the texture view. let view = texture.create_default_view(); // Create the texture sampler. let sampler = device.create_sampler(&SamplerDescriptor { address_mode_u: AddressMode::ClampToEdge, address_mode_v: AddressMode::ClampToEdge, address_mode_w: AddressMode::ClampToEdge, mag_filter: FilterMode::Linear, min_filter: FilterMode::Linear, mipmap_filter: FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: CompareFunction::Always, }); // Create the texture bind group from the layout. let bind_group = device.create_bind_group(&BindGroupDescriptor { layout, bindings: &[ Binding { binding: 0, resource: BindingResource::TextureView(&view), }, Binding { binding: 1, resource: BindingResource::Sampler(&sampler), }, ], }); Texture { bind_group } } } #[allow(dead_code)] pub struct Renderer { pipeline: RenderPipeline, uniform_buffer: Buffer, uniform_bind_group: BindGroup, textures: Textures, texture_layout: BindGroupLayout, clear_color: Option, } impl Renderer { /// Create an entirely new imgui wgpu renderer. pub fn new( imgui: &mut Context, device: &mut Device, queue: &mut Queue, format: TextureFormat, clear_color: Option, ) -> Renderer { let (vs_code, fs_code) = get_program_link(); let vs_raw = super::glsl_compiler::load(vs_code).unwrap(); let fs_raw = super::glsl_compiler::load(fs_code).unwrap(); Self::new_impl(imgui, device, queue, format, clear_color, vs_raw, fs_raw) } /// Create an entirely new imgui wgpu renderer. fn new_impl( imgui: &mut Context, device: &mut Device, queue: &mut Queue, format: TextureFormat, clear_color: Option, vs_raw: Vec, fs_raw: Vec, ) -> Renderer { // Load shaders. let vs_module = device.create_shader_module(&vs_raw); let fs_module = device.create_shader_module(&fs_raw); // Create the uniform matrix buffer. let size = 64; let uniform_buffer = device.create_buffer(&BufferDescriptor { size, usage: BufferUsage::UNIFORM | BufferUsage::COPY_DST, }); // Create the uniform matrix buffer bind group layout. let uniform_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { bindings: &[BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::VERTEX, ty: BindingType::UniformBuffer { dynamic: false }, }], }); // Create the uniform matrix buffer bind group. let uniform_bind_group = device.create_bind_group(&BindGroupDescriptor { layout: &uniform_layout, bindings: &[Binding { binding: 0, resource: BindingResource::Buffer { buffer: &uniform_buffer, range: 0..size, }, }], }); // Create the texture layout for further usage. let texture_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor { bindings: &[ BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: BindingType::SampledTexture { multisampled: false, dimension: TextureViewDimension::D2, }, }, BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: BindingType::Sampler, }, ], }); // Create the render pipeline layout. let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor { bind_group_layouts: &[&uniform_layout, &texture_layout], }); // Create the render pipeline. let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(RasterizationStateDescriptor { front_face: FrontFace::Cw, cull_mode: CullMode::None, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: PrimitiveTopology::TriangleList, color_states: &[ColorStateDescriptor { format, color_blend: BlendDescriptor { src_factor: BlendFactor::SrcAlpha, dst_factor: BlendFactor::OneMinusSrcAlpha, operation: BlendOperation::Add, }, alpha_blend: BlendDescriptor { src_factor: BlendFactor::OneMinusDstAlpha, dst_factor: BlendFactor::One, operation: BlendOperation::Add, }, write_mask: ColorWrite::ALL, }], depth_stencil_state: None, index_format: IndexFormat::Uint16, vertex_buffers: &[VertexBufferDescriptor { stride: size_of::() as BufferAddress, step_mode: InputStepMode::Vertex, attributes: &[ VertexAttributeDescriptor { format: VertexFormat::Float2, shader_location: 0, offset: 0, }, VertexAttributeDescriptor { format: VertexFormat::Float2, shader_location: 1, offset: 8, }, VertexAttributeDescriptor { format: VertexFormat::Uint, shader_location: 2, offset: 16, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); let mut renderer = Renderer { pipeline, uniform_buffer, uniform_bind_group, textures: Textures::new(), texture_layout, clear_color, }; // Immediately load the fon texture to the GPU. renderer.reload_font_texture(imgui, device, queue); renderer } /// Render the current imgui frame. pub fn render<'a>( &mut self, ui: Ui<'a>, device: &Device, encoder: &mut CommandEncoder, view: &TextureView, ) -> RendererResult<()> { let draw_data = ui.render(); let fb_width = draw_data.display_size[0] * draw_data.framebuffer_scale[0]; let fb_height = draw_data.display_size[1] * draw_data.framebuffer_scale[1]; // If the render area is <= 0, exit here and now. if !(fb_width > 0.0 && fb_height > 0.0) { return Ok(()); } let width = draw_data.display_size[0]; let height = draw_data.display_size[1]; // Create and update the transform matrix for the current frame. // This is required to adapt to vulkan coordinates. let matrix = [ [2.0 / width, 0.0, 0.0, 0.0], [0.0, 2.0 / height as f32, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], [-1.0, -1.0, 0.0, 1.0], ]; self.update_uniform_buffer(device, encoder, &matrix); // Start a new renderpass and prepare it properly. let mut rpass = encoder.begin_render_pass(&RenderPassDescriptor { color_attachments: &[RenderPassColorAttachmentDescriptor { attachment: &view, resolve_target: None, load_op: match self.clear_color { Some(_) => LoadOp::Clear, _ => LoadOp::Load, }, store_op: StoreOp::Store, clear_color: self.clear_color.unwrap_or(Color { r: 0.0, g: 0.0, b: 0.0, a: 1.0, }), }], depth_stencil_attachment: None, }); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, &self.uniform_bind_group, &[]); // Execute all the imgui render work. for draw_list in draw_data.draw_lists() { self.render_draw_list( device, &mut rpass, &draw_list, draw_data.display_pos, draw_data.framebuffer_scale, )?; } Ok(()) } /// Render a given `DrawList` from imgui onto a wgpu frame. fn render_draw_list<'render>( &mut self, device: &Device, rpass: &mut RenderPass<'render>, draw_list: &DrawList, clip_off: [f32; 2], clip_scale: [f32; 2], ) -> RendererResult<()> { let mut start = 0; // Make sure the current buffers are uploaded to the GPU. let vertex_buffer = self.upload_vertex_buffer(device, draw_list.vtx_buffer()); let index_buffer = self.upload_index_buffer(device, draw_list.idx_buffer()); // Make sure the current buffers are attached to the render pass. rpass.set_index_buffer(&index_buffer, 0); rpass.set_vertex_buffers(0, &[(&vertex_buffer, 0)]); for cmd in draw_list.commands() { match cmd { Elements { count, cmd_params } => { let clip_rect = [ (cmd_params.clip_rect[0] - clip_off[0]) * clip_scale[0], (cmd_params.clip_rect[1] - clip_off[1]) * clip_scale[1], (cmd_params.clip_rect[2] - clip_off[0]) * clip_scale[0], (cmd_params.clip_rect[3] - clip_off[1]) * clip_scale[1], ]; // Set the current texture bind group on the renderpass. let texture_id = cmd_params.texture_id.into(); let tex = self .textures .get(texture_id) .ok_or_else(|| RendererError::BadTexture(texture_id))?; rpass.set_bind_group(1, &tex.bind_group, &[]); // Set scissors on the renderpass. let scissors = ( clip_rect[0].max(0.0).floor() as u32, clip_rect[1].max(0.0).floor() as u32, (clip_rect[2] - clip_rect[0]).abs().ceil() as u32, (clip_rect[3] - clip_rect[1]).abs().ceil() as u32, ); rpass.set_scissor_rect(scissors.0, scissors.1, scissors.2, scissors.3); // Draw the current batch of vertices with the renderpass. let end = start + count as u32; rpass.draw_indexed(start..end, 0, 0..1); start = end; } _ => {} } } Ok(()) } /// Updates the current uniform buffer containing the transform matrix. fn update_uniform_buffer( &mut self, device: &Device, encoder: &mut CommandEncoder, matrix: &[[f32; 4]; 4], ) { // Create a new buffer. let buffer = device .create_buffer_mapped(16, BufferUsage::COPY_SRC) .fill_from_slice( matrix .iter() .flatten() .map(|f| *f) .collect::>() .as_slice(), ); // Copy the new buffer to the real buffer. encoder.copy_buffer_to_buffer(&buffer, 0, &self.uniform_buffer, 0, 64); } /// Upload the vertex buffer to the gPU. fn upload_vertex_buffer(&mut self, device: &Device, vertices: &[DrawVert]) -> Buffer { device .create_buffer_mapped(vertices.len(), BufferUsage::VERTEX) .fill_from_slice(vertices) } /// Upload the index buffer to the GPU. fn upload_index_buffer(&mut self, device: &Device, indices: &[DrawIdx]) -> Buffer { device .create_buffer_mapped(indices.len(), BufferUsage::INDEX) .fill_from_slice(indices) } /// Updates the texture on the GPU corresponding to the current imgui font atlas. /// /// This has to be called after loading a font. pub fn reload_font_texture( &mut self, imgui: &mut Context, device: &mut Device, queue: &mut Queue, ) { let mut atlas = imgui.fonts(); let handle = atlas.build_rgba32_texture(); let font_texture_id = self.upload_font_texture(device, queue, &handle.data, handle.width, handle.height); atlas.tex_id = font_texture_id; } /// Creates and uploads a new wgpu texture made from the imgui font atlas. fn upload_font_texture( &mut self, device: &mut Device, queue: &mut Queue, data: &[u8], width: u32, height: u32, ) -> TextureId { // Create the wgpu texture. let texture = device.create_texture(&TextureDescriptor { size: Extent3d { width, height, depth: 1, }, array_layer_count: 1, mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, format: TextureFormat::Rgba8Unorm, usage: TextureUsage::SAMPLED | TextureUsage::COPY_DST, }); // Upload the actual data to a wgpu buffer. let bytes = data.len(); let buffer = device .create_buffer_mapped(bytes, BufferUsage::COPY_SRC) .fill_from_slice(data); // Make sure we have an active encoder. let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { todo: 0 }); // Schedule a copy from the buffer to the texture. encoder.copy_buffer_to_texture( BufferCopyView { buffer: &buffer, offset: 0, row_pitch: bytes as u32 / height, image_height: height, }, TextureCopyView { texture: &texture, mip_level: 0, array_layer: 0, origin: Origin3d { x: 0.0, y: 0.0, z: 0.0, }, }, Extent3d { width, height, depth: 1, }, ); // Resolve the actual copy process. queue.submit(&[encoder.finish()]); let texture = Texture::new(texture, &self.texture_layout, device); self.textures.insert(texture) } } ================================================ FILE: src/gpu_obj/line.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat}; pub struct LineGpu { instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, } impl LineGpu { pub fn new( device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, ) -> Self { log::trace!("LineGpu new"); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();; LineGpu { instance_buf, instance_count: 0, pipeline, } } pub fn create_pipeline( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/line.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/line.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: (4 * (2 + 2 + 2)) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 2, shader_location: 1, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 4, shader_location: 2, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 5, shader_location: 3, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("LineGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]); rpass.set_bind_group(0, main_bind_group, &[]); rpass.draw(0..4, 0..self.instance_count as u32); } } pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("LineGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 6; } } impl super::trait_gpu::TraitGpu for LineGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline(device, main_bind_group_layout, format) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/mod.rs ================================================ pub mod arrow_gpu; pub mod blit_texture; pub mod explosion; pub mod glsl_compiler; pub mod gpu; pub mod health_bar; pub mod heightmap_gpu; mod heightmap_helper; pub mod imgui_wgpu; pub mod line; pub mod model_gpu; pub mod post_fx; pub mod post_fxaa; pub mod texture_view_bicopy; pub mod trait_gpu; pub mod unit_icon; pub mod water; ================================================ FILE: src/gpu_obj/model_gpu.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat}; pub struct ModelGpu { pub instance_attr_cpu_buf: Vec, vertex_buf: wgpu::Buffer, index_buf: wgpu::Buffer, index_count: usize, instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, } impl ModelGpu { pub fn new( triangle_list: &model::TriangleList, device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, ) -> Self { log::trace!("ModelGpu new"); // Create the vertex and index buffers let model::TriangleList { vertex_data, index_data, } = triangle_list; let vertex_buf = device .create_buffer_mapped(vertex_data.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(&vertex_data); let index_buf = device .create_buffer_mapped(index_data.len(), wgpu::BufferUsage::INDEX) .fill_from_slice(&index_data); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();; ModelGpu { instance_attr_cpu_buf: Vec::new(), vertex_buf, index_buf, index_count: index_data.len(), instance_buf, instance_count: positions.len() as u32 / 3, pipeline, } } pub fn create_pipeline( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout], }); let vertex_size = std::mem::size_of::(); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/cube_instanced.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/cube_instanced.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleList, color_states: &[ wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Rgba32Float, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Rg16Float, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }, ], depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_read_mask: 0, stencil_write_mask: 0, }), index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[ wgpu::VertexBufferDescriptor { stride: vertex_size as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Vertex, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float4, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float3, offset: 4 * 4, shader_location: 1, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 4 * 7, shader_location: 2, }, ], }, wgpu::VertexBufferDescriptor { stride: (4 * 8) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float3, offset: 0, shader_location: 3, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float3, offset: 4 * 3, shader_location: 4, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 6, shader_location: 5, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 7, shader_location: 6, }, ], }, ], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("ModelGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, main_bind_group, &[]); rpass.set_index_buffer(&self.index_buf, 0); rpass.set_vertex_buffers(0, &[(&self.vertex_buf, 0), (&self.instance_buf, 0)]); rpass.draw_indexed(0..self.index_count as u32, 0, 0..self.instance_count as u32); } } pub fn update_instance_dirty(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("ModelGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 8; } pub fn update_instance_dirty_own_buffer(&mut self, device: &wgpu::Device) { log::trace!("ModelGpu update_instance"); let temp_buf = device .create_buffer_mapped(self.instance_attr_cpu_buf.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(&self.instance_attr_cpu_buf); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = self.instance_attr_cpu_buf.len() as u32 / 8; } pub fn update_instance( &mut self, instance_attr: &[f32], device: &wgpu::Device, encoder: &mut wgpu::CommandEncoder, ) { log::trace!("ModelGpu update_instance"); for (i, chunk) in instance_attr.chunks(1800).enumerate() { let temp_buf = device .create_buffer_mapped( chunk.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_SRC, ) .fill_from_slice(chunk); encoder.copy_buffer_to_buffer( &temp_buf, 0, &self.instance_buf, i as u64 * 1800_u64, chunk.len() as u64 * 4, ); } // let temp_buf = device // .create_buffer_mapped( // instance_attr.len(), // wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_SRC, // ) // .fill_from_slice(instance_attr); // encoder.copy_buffer_to_buffer( // &temp_buf, // 0, // &self.instance_buf, // 0, // instance_attr.len() as u64 * 4, // ); self.instance_count = instance_attr.len() as u32 / 8; } } impl super::trait_gpu::TraitGpu for ModelGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline(device, main_bind_group_layout, format) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/post_fx.rs ================================================ use super::glsl_compiler; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView}; pub struct PostFx { pipeline: wgpu::RenderPipeline, bind_group: BindGroup, bind_group_layout: BindGroupLayout, sampler: wgpu::Sampler, } impl PostFx { pub fn new( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, position_att_view: &TextureView, ) -> Self { // Create pipeline layout let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Nearest, min_filter: wgpu::FilterMode::Nearest, mipmap_filter: wgpu::FilterMode::Nearest, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&position_att_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, ], }); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap(); PostFx { pipeline, bind_group_layout, bind_group, sampler, } } pub fn update_pos_att_view(&mut self, device: &Device, position_att_view: &TextureView) { self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &self.bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&position_att_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&self.sampler), }, ], }); } fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/post.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/post_ui.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, device: &Device, main_bind_group: &BindGroup) { log::trace!("PostFx render"); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, &main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.draw(0..4, 0..1); } } impl super::trait_gpu::TraitGpu for PostFx { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/post_fxaa.rs ================================================ use super::glsl_compiler; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView}; pub struct PostFxaa { pipeline: wgpu::RenderPipeline, bind_group_layout: BindGroupLayout, bind_group: BindGroup, } impl PostFxaa { pub fn new( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, last_pass_view: &TextureView, ) -> Self { // Create pipeline layout let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&last_pass_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, ], }); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap(); PostFxaa { pipeline, bind_group_layout, bind_group, } } pub fn update_last_pass_view(&mut self, device: &Device, last_pass_view: &TextureView) { let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &self.bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&last_pass_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, ], }); } fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/post.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/post_fxaa.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, device: &Device, main_bind_group: &BindGroup) { log::trace!("PostFxaa render"); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, &main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.draw(0..4, 0..1); } } impl super::trait_gpu::TraitGpu for PostFxaa { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/texture_view_bicopy.rs ================================================ use super::glsl_compiler; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView}; pub struct TextureViewBiCopy { pipeline: wgpu::RenderPipeline, bind_group_layout: BindGroupLayout, bind_group: BindGroup, sampler: wgpu::Sampler, } impl TextureViewBiCopy { pub fn new( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, last_pass_view: &TextureView, ) -> Self { // Create pipeline layout let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&last_pass_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, ], }); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap(); TextureViewBiCopy { pipeline, bind_group_layout, sampler, bind_group, } } pub fn update_last_pass_view(&mut self, device: &Device, last_pass_view: &TextureView) { self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &self.bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(&last_pass_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&self.sampler), }, ], }); } fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/post.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/post_bicopy.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, device: &Device, main_bind_group: &BindGroup) { log::trace!("TextureViewBiCopy render"); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, &main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.draw(0..4, 0..1); } } impl super::trait_gpu::TraitGpu for TextureViewBiCopy { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/trait_gpu.rs ================================================ use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat}; pub trait TraitGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ); } ================================================ FILE: src/gpu_obj/unit_icon.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat}; pub struct UnitIconGpu { instance_buf: wgpu::Buffer, instance_count: u32, pipeline: wgpu::RenderPipeline, } impl UnitIconGpu { pub fn new( device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, ) -> Self { log::trace!("UnitIconGpu new"); let positions: Vec = Vec::new(); let instance_buf = device .create_buffer_mapped( positions.len(), wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::COPY_DST, ) .fill_from_slice(&positions); let pipeline = Self::create_pipeline(device, main_bind_group_layout, format).unwrap();; UnitIconGpu { instance_buf, instance_count: 0, pipeline, } } pub fn create_pipeline( device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout], }); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/unit_icon.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/unit_icon.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Cw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format: format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: None, index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[wgpu::VertexBufferDescriptor { stride: (4 * (4)) as wgpu::BufferAddress, step_mode: wgpu::InputStepMode::Instance, attributes: &[ wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float2, offset: 0, shader_location: 0, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 2, shader_location: 1, }, wgpu::VertexAttributeDescriptor { format: wgpu::VertexFormat::Float, offset: 4 * 3, shader_location: 2, }, ], }], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("UnitIconGpu render"); if self.instance_count > 0 { rpass.set_pipeline(&self.pipeline); rpass.set_vertex_buffers(0, &[(&self.instance_buf, 0)]); rpass.set_bind_group(0, main_bind_group, &[]); rpass.draw(0..4, 0..self.instance_count as u32); } } pub fn update_instance(&mut self, instance_attr: &[f32], device: &wgpu::Device) { log::trace!("UnitIconGpu update_instance"); let temp_buf = device .create_buffer_mapped(instance_attr.len(), wgpu::BufferUsage::VERTEX) .fill_from_slice(instance_attr); std::mem::replace(&mut self.instance_buf, temp_buf); self.instance_count = instance_attr.len() as u32 / 4; } } impl super::trait_gpu::TraitGpu for UnitIconGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline(device, main_bind_group_layout, format) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/gpu_obj/water.rs ================================================ use super::glsl_compiler; use crate::model; use wgpu::Device; use wgpu::{BindGroup, BindGroupLayout, RenderPass, TextureFormat, TextureView}; pub struct WaterGpu { pipeline: wgpu::RenderPipeline, bind_group_layout: BindGroupLayout, bind_group: BindGroup, } impl WaterGpu { pub fn new( device: &Device, format: TextureFormat, main_bind_group_layout: &BindGroupLayout, last_pass_view: &TextureView, current_position_att: &TextureView, ) -> Self { log::trace!("WaterGpu new"); let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { bindings: &[ wgpu::BindGroupLayoutBinding { binding: 0, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 1, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, wgpu::BindGroupLayoutBinding { binding: 2, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::SampledTexture { multisampled: false, dimension: wgpu::TextureViewDimension::D2, }, }, wgpu::BindGroupLayoutBinding { binding: 3, visibility: wgpu::ShaderStage::FRAGMENT, ty: wgpu::BindingType::Sampler, }, ], }); let bind_group = Self::create_bind_group( device, &bind_group_layout, last_pass_view, current_position_att, ); let pipeline = Self::create_pipeline(device, &bind_group_layout, main_bind_group_layout, format) .unwrap();; WaterGpu { pipeline, bind_group, bind_group_layout, } } pub fn update_bind_group( &mut self, device: &Device, last_pass_view: &TextureView, current_position_att: &TextureView, ) { self.bind_group = Self::create_bind_group( device, &self.bind_group_layout, last_pass_view, current_position_att, ); } pub fn create_bind_group( device: &Device, bind_group_layout: &BindGroupLayout, last_pass_view: &TextureView, current_position_att: &TextureView, ) -> BindGroup { let sampler = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); let sampler_pos_att = device.create_sampler(&wgpu::SamplerDescriptor { address_mode_u: wgpu::AddressMode::ClampToEdge, address_mode_v: wgpu::AddressMode::ClampToEdge, address_mode_w: wgpu::AddressMode::ClampToEdge, mag_filter: wgpu::FilterMode::Linear, min_filter: wgpu::FilterMode::Linear, mipmap_filter: wgpu::FilterMode::Linear, lod_min_clamp: -100.0, lod_max_clamp: 100.0, compare_function: wgpu::CompareFunction::Always, }); device.create_bind_group(&wgpu::BindGroupDescriptor { layout: bind_group_layout, bindings: &[ wgpu::Binding { binding: 0, resource: wgpu::BindingResource::TextureView(last_pass_view), }, wgpu::Binding { binding: 1, resource: wgpu::BindingResource::Sampler(&sampler), }, wgpu::Binding { binding: 2, resource: wgpu::BindingResource::TextureView(current_position_att), }, wgpu::Binding { binding: 3, resource: wgpu::BindingResource::Sampler(&sampler_pos_att), }, ], }) } pub fn create_pipeline( device: &Device, bind_group_layout: &BindGroupLayout, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) -> glsl_compiler::Result { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[&main_bind_group_layout, &bind_group_layout], }); let vertex_size = std::mem::size_of::(); // Create the render pipeline let vs_bytes = glsl_compiler::load("./src/shader/water.vert")?; let fs_bytes = glsl_compiler::load("./src/shader/water.frag")?; let vs_module = device.create_shader_module(&vs_bytes); let fs_module = device.create_shader_module(&fs_bytes); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { layout: &pipeline_layout, vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::Back, depth_bias: 0, depth_bias_slope_scale: 0.0, depth_bias_clamp: 0.0, }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format, color_blend: wgpu::BlendDescriptor { src_factor: wgpu::BlendFactor::SrcAlpha, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE, stencil_read_mask: 0, stencil_write_mask: 0, }), index_format: wgpu::IndexFormat::Uint32, vertex_buffers: &[], sample_count: 1, sample_mask: !0, alpha_to_coverage_enabled: false, }); Ok(pipeline) } pub fn render(&self, rpass: &mut RenderPass, main_bind_group: &BindGroup) { log::trace!("WaterGpu render"); rpass.set_pipeline(&self.pipeline); rpass.set_bind_group(0, &main_bind_group, &[]); rpass.set_bind_group(1, &self.bind_group, &[]); rpass.draw(0..4, 0..4); //floor lwall fwall rwall } } impl super::trait_gpu::TraitGpu for WaterGpu { fn reload_shader( &mut self, device: &Device, main_bind_group_layout: &BindGroupLayout, format: TextureFormat, ) { match Self::create_pipeline( device, &self.bind_group_layout, main_bind_group_layout, format, ) { Ok(pipeline) => self.pipeline = pipeline, Err(x) => log::error!("{}", x), }; } } ================================================ FILE: src/heightmap_phy.rs ================================================ use na::Vector3; use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Data { pub metal_spots: Vec, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct HeightmapPhy { pub texels: Vec, pub width: usize, pub height: usize, pub data: Data, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct MetalSpot { metal_per_frame: f32, x: usize, y: usize, } trait HeightMapPhyUsize { fn z(&self, x: usize, y: usize) -> f32; } impl HeightMapPhyUsize for HeightmapPhy { fn z(&self, x: usize, y: usize) -> f32 { self.texels[x + y * self.width] } } impl HeightmapPhy { pub fn new(width: usize, height: usize) -> Self { let mut texels = Vec::with_capacity((width * height) as usize); for j in 0..height { for i in 0..width { texels.push(50.0); } } HeightmapPhy { texels, width, height, data: Data { metal_spots: Vec::new(), }, } } ///unsafe nearest interpolation #[inline] pub fn z(&self, x: f32, y: f32) -> f32 { let i = x as usize + (y as usize) * self.width as usize; self.texels[i] } ///safe nearest interpolation #[inline] pub fn safe_z(&self, x: f32, y: f32) -> f32 { let x = x.max(0.0).min(self.width as f32 - 1.0); let y = y.max(0.0).min(self.height as f32 - 1.0); self.z(x, y) } ///safe linear interpolation pub fn z_linear(&self, x: f32, y: f32) -> f32 { let x = x.max(0.0).min(self.width as f32 - 2.0); let y = y.max(0.0).min(self.height as f32 - 2.0); let imin = x.trunc() as usize; let imax = imin + 1; let jmin = y.trunc() as usize; let jmax = self.width as usize * (jmin + 1); let jmin = self.width as usize * jmin; let a = self.texels[imin + jmin]; let b = self.texels[imax + jmin]; let c = self.texels[imax + jmax]; let d = self.texels[imin + jmax]; let z = a * (1.0 - x.fract()) * (1.0 - y.fract()) + b * (x.fract()) * (1.0 - y.fract()) + c * (x.fract()) * (y.fract()) + d * (1.0 - x.fract()) * (y.fract()); z } ///safe normal interpolation pub fn normal(&self, x: f32, y: f32) -> Vector3 { let x = x.max(1.0).min(self.width as f32 - 2.0); let y = y.max(1.0).min(self.height as f32 - 2.0); let r = self.z_linear(x + 1.0, y); let l = self.z_linear(x - 1.0, y); let u = self.z_linear(x, y - 1.0); let d = self.z_linear(x, y + 1.0); Vector3::new(l - r, u - d, 2.0).normalize() } } ================================================ FILE: src/main.rs ================================================ mod botdef; mod client; mod frame; mod frame_server; mod glsl; mod gpu_obj; mod heightmap_phy; mod manager; mod mobile; mod model; mod moddef; mod net_client; mod net_server; mod procedural_texels; mod unit; mod utils; extern crate byteorder; extern crate crossbeam_channel; extern crate nalgebra as na; #[cfg(feature = "use_shaderc")] extern crate shaderc; #[macro_use] extern crate typename; extern crate base_62; extern crate rayon; extern crate spin_sleep; use crossbeam_channel::unbounded; use spin_sleep::LoopHelper; use winit::event::Event; use winit::event_loop::ControlFlow; #[derive(Debug)] pub enum ToClient { MapReadAsyncMessage { vec: Vec, usage: String }, NewFrame(frame::Frame), GlobalInfo(manager::GlobalInfo), } pub enum EventLoopMsg { Stop, } use std::env; fn main() { env_logger::init(); if let Some(x) = env::args().skip(1).next() { if x == "compile" { glsl::compile_all_glsl(); } } else { do_the_thing(); } } fn do_the_thing() { let (s_to_frame_server, r_to_frame_server) = unbounded::(); let (s_from_frame_server, r_from_frame_server) = unbounded::(); let frame_server = frame_server::FrameServerCache::spawn(r_to_frame_server, s_from_frame_server); let (s_from_client_to_manager, r_from_client_to_manager) = unbounded::(); let (s_to_client, r_to_client) = unbounded::(); let s_to_client_from_manager = s_to_client.clone(); let manager = manager::Manager::new( s_to_client_from_manager, s_to_frame_server, r_from_frame_server, r_from_client_to_manager, ); let (s_to_event_loop, r_to_event_loop) = unbounded::(); let event_loop = winit::event_loop::EventLoop::new(); let builder = winit::window::WindowBuilder::new(); let window = builder.build(&event_loop).unwrap(); let mut client = client::App::new( window, s_to_client, r_to_client, s_to_event_loop, s_from_client_to_manager, ); event_loop.run(move |event, _, control_flow| match event { Event::WindowEvent { .. } => { client.handle_winit_event(&event); } Event::EventsCleared => match r_to_event_loop.try_recv() { Ok(EventLoopMsg::Stop) => { *control_flow = ControlFlow::Exit; } _ => { client.receive(); client.render(); } }, _ => {} }); } ================================================ FILE: src/manager.rs ================================================ use crate::client; use crate::frame; use crate::frame_server; use crate::net_client; use crate::net_server; use crate::ToClient; use crossbeam_channel::{Receiver, Sender}; use net_client::NetClient; use net_server::NetServer; use spin_sleep::LoopHelper; pub struct Manager {} impl Manager { pub fn new( s_to_client_from_root_manager: Sender, s_to_frame_server: Sender, r_from_frame_server: Receiver, r_from_client: Receiver, ) -> () { let _ = std::thread::Builder::new() .name("manager".to_string()) .spawn(move || { let mut global_info = GlobalInfo { manager: ManagerInfo { loop_time: std::time::Duration::from_millis(0), }, net_client: None, net_server: None, }; let mut net: Net = Net::Offline; let frame0 = frame::Frame::new(); let _ = s_to_frame_server.send(frame_server::ToFrameServer::DataToComputeNextFrame( frame::DataToComputeNextFrame { old_frame: frame0.clone(), events: Vec::new(), }, )); let _ = s_to_client_from_root_manager.send(ToClient::NewFrame(frame0)); let mut loop_helper = LoopHelper::builder().build_with_target_rate(10.0_f64); loop { log::trace!("loop sleep"); loop_helper.loop_sleep(); global_info.manager.loop_time = loop_helper.loop_start(); log::trace!("receive"); //Receiving new frame let mut frame = match r_from_frame_server.recv() { Ok(frame_server::FromFrameServer::NewFrame(new_frame)) => new_frame, _ => panic!("frame_server disconnected"), }; //Receiving local player event let mut player_inputs = Vec::new(); for from_client in r_from_client.try_iter() { use client::FromClient; match from_client { FromClient::PlayerInput(event) => player_inputs.push(event), FromClient::StartClient(client::StartClient { bind }) => { net = Net::IsClient(NetClient::new(&bind)) } FromClient::StartServer(client::StartServer { bind }) => { net = Net::IsServer(NetServer::new(&bind)) } FromClient::DisconnectServer => { if let Net::IsServer(net_server) = &mut net { net_server.kill(); global_info.net_server = None; net = Net::Offline; } } FromClient::DisconnectClient => { if let Net::IsClient(net_client) = &mut net { net_client.kill(); global_info.net_client = None; net = Net::Offline; } } } } //If local is client : Send player events if let Net::IsClient(net_client) = &mut net { net_client.send_player_inputs( player_inputs .iter() .filter(|e| match e { frame::FrameEventFromPlayer::ReplaceFrame(x) => false, _ => true, }) .map(|e| e.clone()) .collect(), ); } //If local is server : Extend with remote players else if let Net::IsServer(server) = &mut net { player_inputs.extend(server.collect_remote_players_inputs()); } //Frame is now complete and ready to be sent let mut data_to_compute_next_frame = frame::DataToComputeNextFrame { old_frame: frame.clone(), events: player_inputs, }; //If local is client : Get remote frame (TEMPORARY TOTAL BYPASS OF LOCAL FRAME_SERVER) if let Net::IsClient(net_client) = &mut net { data_to_compute_next_frame = net_client.collect_data_to_compute_next_frame().unwrap(); frame = data_to_compute_next_frame.old_frame.clone(); } //If local is server : Broadcast to remotes else if let Net::IsServer(server) = &mut net { server.broadcast_data_to_compute_next_frame( data_to_compute_next_frame.clone(), ); } //Sending to local frame_server and local client let _ = s_to_frame_server.send( frame_server::ToFrameServer::DataToComputeNextFrame( data_to_compute_next_frame, ), ); let _ = s_to_client_from_root_manager.send(ToClient::NewFrame(frame)); //Gathering and sending GlobalInfo if let Net::IsClient(net_client) = &mut net { global_info.net_client = Some(net_client.get_info()); } else if let Net::IsServer(server) = &mut net { global_info.net_server = Some(server.get_info()); } let _ = s_to_client_from_root_manager.send(ToClient::GlobalInfo(global_info)); } }); } } enum Net { Offline, IsServer(NetServer), IsClient(NetClient), } #[derive(Debug, Clone, Copy)] pub struct ManagerInfo { loop_time: std::time::Duration, } ///Info about all the components of this program #[derive(Debug, Clone, Copy)] pub struct GlobalInfo { pub manager: ManagerInfo, pub net_server: Option, pub net_client: Option, } ================================================ FILE: src/mobile.rs ================================================ use super::frame::Player; use crate::botdef; use crate::unit; use crate::utils; use na::{Matrix4, Point3, Vector2, Vector3}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use typename::TypeName; use utils::Id; #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub struct ExplosionEvent { pub position: Point3, pub size: f32, pub life_time: f32, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct Angle { pub rad: f32, } impl Angle { pub fn from(x: f32, y: f32) -> Self { Angle { rad: f32::atan2(y, x), } .modulo() } pub fn new(rad: f32) -> Self { Angle { rad }.modulo() } pub fn clamp_around(self, other: Angle, cone_angle: Angle) -> Self { let max = other + cone_angle; let min = other - cone_angle; let diff = (other - self); let diff = diff.rad.max(-cone_angle.rad).min(cone_angle.rad); self + Angle::new(diff) } pub fn modulo(&self) -> Self { Angle { rad: (self.rad + std::f32::consts::PI).rem_euclid(2.0 * std::f32::consts::PI) - std::f32::consts::PI, } } } impl std::ops::Add for Angle { type Output = Angle; fn add(self, rhs: Angle) -> Angle { Angle { rad: (self.rad + rhs.rad), } .modulo() } } impl std::ops::Sub for Angle { type Output = Angle; fn sub(self, rhs: Angle) -> Angle { Angle { rad: (self.rad - rhs.rad), } .modulo() } } impl std::ops::Neg for Angle { type Output = Angle; fn neg(self) -> Angle { Angle { rad: (self.rad + std::f32::consts::PI), } .modulo() } } impl From> for Angle { fn from(dir: Vector2) -> Self { Self::from(dir.x, dir.y) } } impl Into> for Angle { fn into(self) -> Vector2 { Vector2::new(f32::cos(self.rad), f32::sin(self.rad)) } } impl From<(f32, f32)> for Angle { fn from(dir: (f32, f32)) -> Self { Self::from(dir.0, dir.1) } } impl From for Angle { fn from(rad: f32) -> Self { Self::new(rad) } } #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub enum Command { None, Build(Id), Repair(Id), } #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub struct KBot { pub id: Id, pub position: Point3, pub speed: Vector3, pub dir: Vector3, pub angle: Angle, pub angular_velocity: f32, pub up: Vector3, pub move_target: Option>, pub current_command: Command, pub life: i32, pub con_completed: f32, pub player_id: Id, pub team: u8, pub grounded: bool, pub frame_last_shot: i32, pub weapon0_dir: Vector3, pub wheel0_angle: f32, pub reload_frame_count: i32, pub botdef_id: Id, } impl KBot { pub fn new(position: Point3, botdef: &botdef::BotDef, player_id: Id) -> Self { KBot { position, speed: Vector3::new(0.0, 0.0, 0.0), player_id, team: 0, dir: Vector3::new(1.0, 0.0, 0.0), angle: Angle::new(0.0), up: Vector3::new(0.0, 0.0, 1.0), move_target: None, current_command: Command::None, id: utils::rand_id(), frame_last_shot: 0, reload_frame_count: 3, weapon0_dir: Vector3::new(1.0, 0.0, 0.0), wheel0_angle: 0.0, life: botdef.max_life, con_completed: 1.0, grounded: false, botdef_id: botdef.id, angular_velocity: 0.0, } } } pub struct ClientKbot { pub position: Point3, pub dir: Vector3, pub up: Vector3, pub weapon0_dir: Vector3, pub wheel0_angle: f32, pub trans: Option>, pub is_in_screen: bool, pub distance_to_camera: f32, pub screen_pos: Vector2, } impl ClientKbot { pub fn new(position: Point3) -> Self { ClientKbot { position, dir: Vector3::new(1.0, 0.0, 0.0), up: Vector3::new(0.0, 0.0, 1.0), weapon0_dir: Vector3::new(1.0, 0.0, 0.0), wheel0_angle: 0.0, trans: None, is_in_screen: false, distance_to_camera: 0.0, screen_pos: Vector2::new(0.0, 0.0), } } } #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub struct KinematicProjectile { pub id: Id, pub birth_frame: i32, pub death_frame: i32, pub position_at_birth: Point3, pub speed_per_frame_at_birth: Vector3, pub accel_per_frame: Vector3, pub radius: f32, pub position_cache: Vec>, pub speed_cache: Vec>, } impl KinematicProjectile { pub fn speed_at(&mut self, frame_number: i32) -> Vector3 { //End recursion if frame_number == self.birth_frame { self.speed_per_frame_at_birth } //Check cache else if self.speed_cache.len() as i32 > frame_number - self.birth_frame { self.speed_cache[(frame_number - self.birth_frame) as usize] } //Compute else { let new_speed = self.speed_at(frame_number - 1) + self.accel_per_frame; self.speed_cache.push(new_speed); *self.speed_cache.last().unwrap() } } pub fn position_at(&mut self, frame_number: i32) -> Point3 { //End recursion if frame_number == self.birth_frame { self.position_at_birth } //Check cache else if self.position_cache.len() as i32 > frame_number - self.birth_frame { self.position_cache[(frame_number - self.birth_frame) as usize] } //Compute else { let new_pos = self.position_at(frame_number - 1) + self.speed_at(frame_number); self.position_cache.push(new_pos); *self.position_cache.last().unwrap() } } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Arrow { pub position: Point3, pub end: Point3, pub color: [f32; 4], } impl Arrow { pub fn new(position: Point3, end: Point3, color: [f32; 4]) -> Self { Arrow { position, color, end, } } } ================================================ FILE: src/moddef.rs ================================================ use crate::botdef::BotDef; use crate::unit; use crate::utils; use fnv::FnvHashMap; use serde::{Deserialize, Serialize}; use typename::TypeName; use utils::Id; #[derive(Clone, TypeName, Debug, Serialize, Deserialize, PartialEq)] pub struct ModDef { pub units_id: Vec>, pub con_map: FnvHashMap, Vec>>, } impl ModDef { pub fn new() -> Self { Self { units_id: Vec::new(), con_map: FnvHashMap::default(), } } } ================================================ FILE: src/model.rs ================================================ #[derive(Clone, Copy)] pub struct Vertex { _pos: [f32; 4], _nor: [f32; 3], _tex_coord: [f32; 2], } #[derive(Clone)] pub struct TriangleList { pub vertex_data: Vec, pub index_data: Vec, } pub fn open_obj(path: &str) -> Result { use obj::{load_obj, Obj}; use std::fs::File; use std::io::BufReader; let input = BufReader::new(File::open(path).expect(&format!("Can't open {}", path))); let model: Obj = load_obj(input).map_err(|e| format!("{:?}", e))?; let vertex_data: Vec<_> = model .vertices .iter() .map(|v| Vertex { _pos: [v.position[0], v.position[1], v.position[2], 1.0], _nor: [v.normal[0], v.normal[1], v.normal[2]], _tex_coord: [v.texture[0], v.texture[1]], }) .collect(); Ok(TriangleList { vertex_data, index_data: model.indices.iter().map(|u| *u as u32).collect(), }) } ================================================ FILE: src/net_client.rs ================================================ use crate::frame::*; use crossbeam_channel::{unbounded, Receiver, Sender}; use spin_sleep::LoopHelper; use std::io::prelude::*; use std::io::BufReader; use std::net::TcpListener; use std::net::TcpStream; #[derive(Debug, Clone, Copy)] pub enum BindState { Unknown, Success, Error, Disconnected, } #[derive(Debug, Clone, Copy)] pub struct NetClientInfo { bind_state: BindState, } pub enum ToNetClientInner { PlayerInput(Vec), } pub enum FromNetClientInner { DataToComputeNextFrame(DataToComputeNextFrame), } pub struct NetClient { s: Sender, r: Receiver, info: NetClientInfo, r_info: Receiver, s_kill: Sender<()>, } impl NetClient { pub fn new(bind: &str) -> Self { let (s_to, r_to) = unbounded::(); let (s_from, r_from) = unbounded::(); let (s_info, r_info) = unbounded::(); let (s_kill, r_kill) = unbounded::<()>(); let bind_addr = bind.to_owned(); std::thread::spawn(move || { let s = s_from; let r = r_to; let s_info = s_info; match TcpStream::connect(bind_addr) { Ok(mut stream) => { s_info .try_send(NetClientInfo { bind_state: BindState::Success, }) .unwrap(); let _ = stream.set_read_timeout(Some(std::time::Duration::from_millis(2))); let _ = stream.set_nodelay(true); log::info!("Connection established!"); let mut loop_helper = LoopHelper::builder().build_with_target_rate(100.0_f64); 'streamloop: loop { loop_helper.loop_sleep(); loop_helper.loop_start(); match r.try_recv() { Ok(ToNetClientInner::PlayerInput(fe)) => { log::trace!("stream: Sending local player input to remote server"); bincode::serialize_into(&mut stream, &fe).unwrap(); } _ => { log::trace!("no player input to send"); } } log::trace!("read"); let result_bincode: bincode::Result = bincode::deserialize_from(&mut stream); match result_bincode { Ok(data) => { log::trace!(" Receive Frame from remote server"); let _ = s.try_send(FromNetClientInner::DataToComputeNextFrame(data)); } x => { log::trace!(" Error read {:?}", x); } } if let Ok(()) = r_kill.try_recv() { let _ = s_info.try_send(NetClientInfo { bind_state: BindState::Disconnected, }); break 'streamloop; } } log::info!("Killed"); } _ => { s_info .try_send(NetClientInfo { bind_state: BindState::Error, }) .unwrap(); } } }); NetClient { s: s_to, r: r_from, r_info, info: NetClientInfo { bind_state: BindState::Unknown, }, s_kill, } } pub fn kill(&mut self) { self.s_kill.try_send(()).unwrap(); } pub fn collect_data_to_compute_next_frame(&mut self) -> Option { if self.r.is_empty() { match self.r.recv() { Ok(FromNetClientInner::DataToComputeNextFrame(data)) => Some(data), _ => None, } } else { match self.r.try_iter().last().unwrap() { FromNetClientInner::DataToComputeNextFrame(data) => Some(data), } } } pub fn send_player_inputs(&mut self, player_inputs: Vec) { if player_inputs.len() > 0 { log::trace!("net_client: Sending local player input to remote server"); let _ = self .s .try_send(ToNetClientInner::PlayerInput(player_inputs)); } } pub fn get_info(&mut self) -> NetClientInfo { let last = self.r_info.try_iter().last(); if let Some(info) = last { self.info = info; } self.info } } ================================================ FILE: src/net_server.rs ================================================ use crate::frame::*; use crossbeam_channel::{unbounded, Receiver, Sender}; use spin_sleep::LoopHelper; use std::io::prelude::*; use std::net::TcpListener; use std::net::TcpStream; #[derive(Debug, Clone, Copy, PartialEq)] pub enum BindState { Unknown, Success, Error, } #[derive(Debug, Clone, Copy)] pub struct NetServerInfo { bind_state: BindState, number_of_client_connected: usize, } pub enum ToNetServerInner { DataToComputeNextFrame(DataToComputeNextFrame), } pub enum FromNetServerInner { PlayerInputs(Vec), } pub struct NetServer { s_inner: Sender, r_inner: Receiver, info: NetServerInfo, r_info: Receiver, } impl NetServer { pub fn new(bind: &str) -> Self { let (s_to, r_to) = unbounded::(); let (s_from, r_from) = unbounded::(); let (s_info, r_info) = unbounded::(); let bind_addr = bind.to_owned(); std::thread::spawn(move || { let r = r_to; let s = s_from; let s_info = s_info; let mut info = NetServerInfo { bind_state: BindState::Unknown, number_of_client_connected: 0, }; let mut net_streams = Vec::new(); //Thread that will give us the connected clients let (s_bind_state, r_bind_state) = unbounded::(); let (s_of_net_stream, r_of_net_stream) = unbounded::(); std::thread::spawn(move || match TcpListener::bind(bind_addr) { Ok(listener) => { s_bind_state.send(BindState::Success).unwrap(); for stream in listener.incoming() { let stream = stream.unwrap(); log::info!("Connection established!"); let net_stream = NetStream::new(stream); s_of_net_stream.try_send(net_stream).unwrap(); } } _ => { s_bind_state.send(BindState::Error).unwrap(); } }); let mut loop_helper = LoopHelper::builder().build_with_target_rate(100.0_f64); loop { if info.bind_state == BindState::Unknown { if let Some(bind_state) = r_bind_state.try_iter().last() { info.bind_state = bind_state; } } loop_helper.loop_sleep(); loop_helper.loop_start(); let net_streams = &mut net_streams; match r_of_net_stream.try_recv() { Ok(net_stream) => { log::info!("Connection taken care of"); net_streams.push(net_stream); } _ => {} } //Block on waiting new frames match r.try_recv() { Ok(ToNetServerInner::DataToComputeNextFrame(data)) => { let bytes = bincode::serialize(&data).unwrap(); for net_stream in net_streams.iter_mut() { net_stream.send_data_to_compute_next_frame(bytes.clone()) } } _ => {} } //Player input let mut player_inputs = Vec::new(); for net_stream in net_streams.iter_mut() { player_inputs.extend(net_stream.collect_remote_player_input()); } let _ = s.try_send(FromNetServerInner::PlayerInputs(player_inputs)); //Info update info.number_of_client_connected = net_streams.len(); s_info.try_send(info).unwrap(); } }); NetServer { s_inner: s_to, r_inner: r_from, info: NetServerInfo { bind_state: BindState::Unknown, number_of_client_connected: 0, }, r_info, } } pub fn kill(&mut self) {} pub fn collect_remote_players_inputs(&mut self) -> Vec { let mut pis = Vec::new(); for msg in self.r_inner.try_iter() { match msg { FromNetServerInner::PlayerInputs(player_inputs) => pis.extend(player_inputs), } } pis } pub fn broadcast_data_to_compute_next_frame(&mut self, data: DataToComputeNextFrame) { let _ = self .s_inner .try_send(ToNetServerInner::DataToComputeNextFrame(data)); } pub fn get_info(&mut self) -> NetServerInfo { let last = self.r_info.try_iter().last(); if let Some(info) = last { self.info = info; } self.info } } enum ToNetStream { DataToComputeNextFrame(Vec), } enum FromNetStream { PlayerInput(Vec), } struct NetStream { r: Receiver, s: Sender, } impl NetStream { fn new(stream: TcpStream) -> Self { let (s_to, r_to) = unbounded::(); let (s_from, r_from) = unbounded::(); std::thread::spawn(move || { let mut stream = stream; let _ = stream.set_read_timeout(Some(std::time::Duration::from_millis(2))); let _ = stream.set_nodelay(true); let r = r_to; let s = s_from; let mut loop_helper = LoopHelper::builder().build_with_target_rate(100.0_f64); loop { loop_helper.loop_sleep(); loop_helper.loop_start(); log::trace!("read"); let result_bincode: bincode::Result> = bincode::deserialize_from(&mut stream); match result_bincode { Ok(player_inputs) => { log::trace!( " Receive player_inputs ({}) from remote client", player_inputs.len() ); let _ = s.try_send(FromNetStream::PlayerInput(player_inputs)); } x => { log::trace!(" Error read {:?}", x); } } //Send last frame to remote player if !r.is_empty() { match r.try_iter().last().unwrap() { ToNetStream::DataToComputeNextFrame(data) => { log::debug!("Send frame to remote player ({} bytes)", data.len()); let _ = stream.write_all(&data).unwrap(); stream.flush().unwrap(); } } } } }); NetStream { s: s_to, r: r_from } } pub fn collect_remote_player_input(&mut self) -> Vec { let mut pis = Vec::new(); for msg in self.r.try_iter() { match msg { FromNetStream::PlayerInput(player_inputs) => pis.extend(player_inputs), } } pis } pub fn send_data_to_compute_next_frame(&mut self, data: Vec) { let _ = self.s.try_send(ToNetStream::DataToComputeNextFrame(data)); } } ================================================ FILE: src/procedural_texels.rs ================================================ pub fn create_texels(size: usize) -> Vec { let mut v = Vec::new(); for i in 0..size { for j in 0..size { let i = i as f32 / size as f32; let j = j as f32 / size as f32; v.push((i * 255.0) as u8); v.push(((1.0 - j) * 255.0) as u8); v.push(((1.0 - i) * j * 255.0) as u8); v.push(255); } } v } pub fn checker(size: usize) -> Vec { let mut v = Vec::new(); for i in 0..size { for j in 0..size { let pair = (i + j) % 2 == 0; if pair { v.push(0); v.push(0); v.push(0); } else { v.push(255); v.push(255); v.push(255); } v.push(255); } } v } ================================================ FILE: src/shader/arrow.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec3 world_pos; layout(location = 2) in vec4 v_color; layout(location = 0) out vec4 o_Target; layout(location = 1) out vec4 position_att; layout(set = 0, binding = 1) uniform texture2D t_Color; layout(set = 0, binding = 2) uniform sampler s_Color; void main() { vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord); position_att = vec4(world_pos, 0.0 ); vec3 color = v_color.rgb; o_Target = vec4(color,1.0); } ================================================ FILE: src/shader/arrow.vert ================================================ #version 450 layout(location = 0) in vec4 a_Pos; layout(location = 1) in vec2 a_TexCoord; layout(location = 2) in vec4 mata; layout(location = 3) in vec4 matb; layout(location = 4) in vec4 matc; layout(location = 5) in vec4 matd; layout(location = 6) in vec4 color; layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec3 world_pos; layout(location = 2) out vec4 v_color; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { v_TexCoord = a_TexCoord; v_color = color; float dist = color.w; vec3 pos = a_Pos.xyz; if (a_Pos.z>0.0){ pos.z +=dist; } else if(a_Pos.z<=0.0){ pos.z =0; } mat4 t = mat4(mata,matb,matc,matd); gl_Position = cor_proj_view *t* vec4(pos,1.0); world_pos = a_Pos.xyz; } ================================================ FILE: src/shader/blit_texture.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 0) uniform texture2D t_color; layout(set = 1, binding = 1) uniform sampler s_color; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { o_Target = texture(sampler2D(t_color, s_color), v_TexCoord); } ================================================ FILE: src/shader/blit_texture.vert ================================================ #version 450 //min_screen [0,1] layout(location = 0) in vec2 min_screen; layout(location = 1) in vec2 max_screen; //min_tex [0,1] layout(location = 2) in vec2 min_tex; layout(location = 3) in vec2 max_tex; layout(location = 0) out vec2 v_TexCoord; void main() { vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(1.0, 0.0); break; case 1: tc = vec2(1.0, 1.0); break; case 2: tc = vec2(0.0, 0.0); break; case 3: tc = vec2(0.0, 1.0); break; } v_TexCoord = min_tex +tc*(max_tex-min_tex); vec2 screen_px = min_screen +tc*(max_screen-min_screen); gl_Position = vec4(screen_px * 2.0 - 1.0, 0.5, 1.0); } ================================================ FILE: src/shader/cube_instanced.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec3 world_pos; layout(location = 2) in float v_selected; layout(location = 3) in float v_team; layout(location = 4) in float v_con_completed; layout(location = 5) in vec3 v_world_normal; layout(location = 0) out vec4 o_Target; layout(location = 1) out vec4 position_att; layout(location = 2) out vec2 o_normal; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; layout(set = 0, binding = 1) uniform texture2D t_Color; layout(set = 0, binding = 2) uniform sampler s_Color; void main() { vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord); position_att = vec4(world_pos, v_selected ); vec3 color = vec3(1); if(v_team >= 99.0){ color = vec3(1); } else if( v_team == 0.0){ color = vec3(0,0.3,1); }else if(v_team < 1.1){ color = vec3(1,0.0,0); } vec3 diffuse= mix(tex.xyz, color,0.5);; //blinn phong const vec3 ambientColor = vec3(0.05); const vec3 diffuseColor = vec3(1.0, 1.0, 1.0); const vec3 specColor = vec3(0.2); vec3 lightPos = vec3(-10000,1000,12000); vec3 vertPos = world_pos; vec3 lightDir = normalize(lightPos - vertPos); vec3 normal = v_world_normal; float lambertian = max(dot(lightDir,normal), 0.0); float specular = 0.0; if(lambertian > 0.0) { mat3 rot = mat3(u_View); vec3 camera_pos = -u_View[3].xyz*rot; vec3 viewDir = normalize( camera_pos - vertPos); vec3 halfDir = normalize(lightDir + viewDir); float specAngle = max(dot(halfDir, normal), 0.0); specular = 1.0*pow(specAngle, 32.0); } vec3 phong = vec3(ambientColor + lambertian* diffuse + specular*specColor); if(v_con_completed < 0.9999){ bool diag = int((gl_FragCoord.x +gl_FragCoord.y )/3.0) %2 == 0; bool hori = int((gl_FragCoord.y)/2.0) %2==0; bool hatch = true; if (length(phong) <0.5){ hatch = diag||hori; }else if (length(phong) <0.8){ hatch = diag; } else { hatch = false; } float m = 0.3; float f = 1+m; if ( hatch){ f= 1-m; } phong = mix(vec3(ambientColor*f + lambertian* diffuse*f + specular*specColor*f) , vec3(0,v_con_completed,0), hatch ? 1.0:0.0); } o_normal = normal.xy; o_Target = vec4(phong, 1.0); } ================================================ FILE: src/shader/cube_instanced.vert ================================================ #version 450 layout(location = 0) in vec4 a_Pos; layout(location = 1) in vec3 a_normal; layout(location = 2) in vec2 a_TexCoord; layout(location = 3) in vec3 inst_pos; layout(location = 4) in vec3 inst_euler; layout(location = 5) in float bitpack_selected_team_na_na; layout(location = 6) in float con_completed; layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec3 world_pos; layout(location = 2) out float v_selected; layout(location = 3) out float v_team; layout(location = 4) out float v_con_completed; layout(location = 5) out vec3 v_world_normal; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { v_TexCoord = a_TexCoord; v_selected= floor(bitpack_selected_team_na_na/100.0); v_team = round(bitpack_selected_team_na_na-v_selected*100.0); // v_selected = bitpack_selected_team_na_na <= 0.0 ? 1.0 : 0.0; v_con_completed = con_completed; float sr = sin(inst_euler.x); float cr = cos(inst_euler.x); float sp = sin(inst_euler.y); float cp = cos(inst_euler.y); float sy = sin(inst_euler.z); float cy = cos(inst_euler.z); // mat4 t = mat4( // cy * cp, cy * sp * sr - sy * cr, cy * sp * cr + sy * sr, inst_pos.x, // sy * cp, sy * sp * sr + cy * cr, sy * sp * cr - cy * sr, inst_pos.y, // -sp, cp * sr, cp * cr, inst_pos.z, // 0,0,0,1); mat4 t = mat4( cy * cp, sy * cp, -sp ,0, cy * sp * sr - sy * cr, sy * sp * sr + cy * cr, cp * sr ,0, cy * sp * cr + sy * sr, sy * sp * cr - cy * sr, cp * cr ,0, inst_pos.x, inst_pos.y , inst_pos.z ,1); mat3 tn = mat3( cy * cp, sy * cp, -sp , cy * sp * sr - sy * cr, sy * sp * sr + cy * cr, cp * sr , cy * sp * cr + sy * sr, sy * sp * cr - cy * sr, cp * cr ); vec4 world_pos4 = t * a_Pos; world_pos = world_pos4.xyz/world_pos4.w; gl_Position = cor_proj_view*vec4(world_pos+vec3(0.0),1.0);// cor_proj_view * t *a_Pos; v_world_normal = tn* a_normal; } ================================================ FILE: src/shader/explosion.frag ================================================ #version 450 #define EXPLOSION_SEED 1. layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec2 v_center; layout(location = 2) in float v_size; layout(location = 3) in float v_life; layout(location = 4) in float v_seed; layout(location = 5) in vec3 v_world_pos; layout(location = 6) in vec2 v_screen_pos; layout(location = 0) out vec4 o_Target; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; layout(set = 1, binding = 0) uniform texture2D t_noise; layout(set = 1, binding = 1) uniform sampler s_noise; layout(set = 1, binding = 2) uniform texture2D t_world_pos; layout(set = 1, binding = 3) uniform sampler s_world_pos; layout(set = 1, binding = 4) uniform texture2D t_world_normal; layout(set = 1, binding = 5) uniform sampler s_world_normal; float expRadius; vec3 expCenter; float iTime = 1.0; vec3 iMouse; //iq's LUT 3D noise float noise( in vec3 x ) { vec3 f = fract(x); vec3 p = x - f; // this avoids the floor() but doesnt affect performance for me. f = f*f*(3.0-2.0*f); vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy; vec2 rg = textureLod( sampler2D(t_noise,s_noise), (uv+ 0.5)/256.0, 0.0 ).yx; return mix( rg.x, rg.y, f.z ); } // assign colour to the media vec3 computeColour( float density, float radius ) { // these are almost identical to the values used by iq // colour based on density alone. gives impression of occlusion within // the media vec3 result = mix( 1.1*vec3(1.0,0.9,0.8), vec3(0.4,0.15,0.1), density ); // colour added for explosion vec3 colBottom = 3.1*vec3(1.0,0.5,0.05); vec3 colTop = 2.*vec3(0.48,0.53,0.5); result *= mix( colBottom, colTop, min( (radius+.5)/1.7, 1.0 ) ); return result; } // maps 3d position to colour and density float densityFn( in vec3 p, in float r, out float rawDens, in float rayAlpha ) { // density has dependency on mouse y coordinate (linear radial ramp) float mouseIn = iMouse.y; float mouseY = 1.0 - mouseIn; float den = -0.1 - 1.5*r*(4.*mouseY+.5); // offset noise based on seed float t = v_seed; vec3 dir = vec3(0.,1.,0.); // participating media float f; vec3 q = p - dir* t; f = 0.50000*noise( q ); q = q*2.02 - dir* t; f += 0.25000*noise( q ); q = q*2.03 - dir* t; f += 0.12500*noise( q ); q = q*2.01 - dir* t; f += 0.06250*noise( q ); q = q*2.02 - dir* t; f += 0.03125*noise( q ); // add in noise with scale factor rawDens = den + 4.0*f; den = clamp( rawDens, 0.0, 1.0 ); // thin out the volume at the far extends of the bounding sphere to avoid // clipping with the bounding sphere den *= 1.-smoothstep(0.8,1.,r/expRadius); #ifdef CROSS_SECTION den *= smoothstep(.0,.1,-p.x); #endif return den; } vec4 raymarch( in vec3 rayo, in vec3 rayd, in float expInter, in vec2 fragCoord ) { vec4 sum = vec4( 0.0 ); float step = 0.075; // dither start pos to break up aliasing vec3 pos = rayo + rayd * (expInter + step*texture( sampler2D(t_noise,s_noise), fragCoord.xy/256.0 ).x); for( int i=0; i<25; i++ ) { if( sum.a > 0.99 ) continue; float radiusFromExpCenter = length(pos - expCenter); if( radiusFromExpCenter > expRadius+0.01 ) continue; float dens, rawDens; dens = densityFn( pos, radiusFromExpCenter, rawDens, sum.a ); vec4 col = vec4( computeColour(dens,radiusFromExpCenter), dens ); // uniform scale density col.a *= 0.6; // colour by alpha col.rgb *= col.a; // alpha blend in contribution sum = sum + col*(1.0 - sum.a); // take larger steps through negative densities. // something like using the density function as a SDF. float stepMult = 1. + 2.5*(1.-clamp(rawDens+1.,0.,1.)); // step along ray pos += rayd * step * stepMult; } return clamp( sum, 0.0, 1.0 ); } // iq's sphere intersection float iSphere(in vec3 ro, in vec3 rd, in vec4 sph) { //sphere at origin has equation |xyz| = r //sp |xyz|^2 = r^2. //Since |xyz| = ro + t*rd (where t is the parameter to move along the ray), //we have ro^2 + 2*ro*rd*t + t^2 - r2. This is a quadratic equation, so: vec3 oc = ro - sph.xyz; //distance ray origin - sphere center float b = dot(oc, rd); float c = dot(oc, oc) - sph.w * sph.w; //sph.w is radius float h = b*b - c; // delta if(h < 0.0) return -1.0; float t = (-b - sqrt(h)); //Again a = 1. return t; } vec3 computePixelRay( in vec2 p, out vec3 cameraPos ) { // camera orbits around explosion float camRadius = 3.8; // use mouse x coord float a = iTime*20.; if( iMouse.z > 0. ) a = iMouse.x; float theta = -(a-resolution.x)/80.; float xoff = camRadius * cos(theta); float zoff = camRadius * sin(theta); cameraPos = vec3(xoff,expCenter.y,zoff); // camera target vec3 target = vec3(0.,expCenter.y,0.); // camera frame vec3 fo = normalize(target-cameraPos); vec3 ri = normalize(vec3(fo.z, 0., -fo.x )); vec3 up = normalize(cross(fo,ri)); // multiplier to emulate a fov control float fov = .5; // ray direction vec3 rayDir = normalize(fo + fov*p.x*ri + fov*p.y*up); return rayDir; } void main(){ float life = pow(v_life,0.1); float alpha_max = pow(1-v_life,1)*0.5; iMouse = vec3(resolution.x/2.0,life,0.0); vec2 fragCoord = v_TexCoord* resolution; // get aspect corrected normalized pixel coordinate vec2 q = v_TexCoord; vec2 p = -1.0 + 2.0*q; p *= 1.0; // p *= 10.0; expRadius = 1.75; expCenter = vec3(0.,expRadius,0.); vec3 rayDir, cameraPos; rayDir = computePixelRay( p, cameraPos ); vec4 col = vec4(0.); o_Target.w= 0.0; // does pixel ray intersect with exp bounding sphere? float boundingSphereInter = iSphere( cameraPos, rayDir, vec4(expCenter,expRadius) ); if( boundingSphereInter > 0. ) { // yes, cast ray col = raymarch( cameraPos, rayDir, boundingSphereInter,fragCoord ); o_Target.w = pow(length(col),4)*alpha_max; } // vec3 nor_world = texture(sampler2D(t_world_normal,s_world_normal), v_screen_pos ).xyz; // nor_world.z = sqrt(1- (nor_world.x*nor_world.x + nor_world.y*nor_world.y)); // vec3 mesh_pos_world = texture(sampler2D(t_world_pos,s_world_pos), v_screen_pos ).xyz; // vec3 lightPos= v_world_pos; // float dist_to_light = length(mesh_pos_world -lightPos); // float attenuation = 1*min( 0.05 * pow(v_size/ dist_to_light,2) ,1); // vec3 lightDir = normalize(lightPos - mesh_pos_world); // float lambertian = max(dot(lightDir,nor_world), 0.0); // float specular = 0.0; // if(lambertian > 0.0) { // mat3 rot = mat3(u_View); // vec3 camera_pos = -u_View[3].xyz*rot; // vec3 viewDir = normalize( camera_pos - mesh_pos_world); // vec3 halfDir = normalize(lightDir + viewDir); // float specAngle = max(dot(halfDir, nor_world), 0.0); // specular = 1.0*pow(specAngle, 32.0); // } // vec3 diffuse= vec3(1,pow(alpha_max,1),pow(alpha_max,3)); // vec3 phong = attenuation*vec3( // lambertian* diffuse + // specular*mix(diffuse,vec3(1),0.5)); // smoothstep final color to add contrast o_Target.xyz = col.xyz*col.xyz*(3.0-2.0*col.xyz); // o_Target.xyz = phong*alpha_max; // o_Target.a = 1.0; } ================================================ FILE: src/shader/explosion.vert ================================================ #version 450 layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec2 v_center; layout(location = 2) out float v_size; layout(location = 3) out float v_life; layout(location = 4) out float v_seed; layout(location = 5) out vec3 v_world_pos; layout(location = 6) out vec2 v_screen_pos; layout(location = 0) in vec3 pos_world; layout(location = 1) in float life; layout(location = 2) in float seed; layout(location = 3) in float size_world; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { vec4 hposition = vec4(pos_world, 1.0); v_world_pos = pos_world + vec3(0,0,0.5); vec4 sposition = cor_proj_view * hposition; v_size = size_world*7.0; // float size_screen= 2600*v_size / sposition.w; float size_screen= 260*v_size / sposition.w; sposition/=sposition.w; vec2 center = sposition.xy; v_center = center; v_life = life; v_seed = seed*50.0; vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(1.0, 0.0); break; case 1: tc = vec2(1.0, 1.0); break; case 2: tc = vec2(0.0, 0.0); break; case 3: tc = vec2(0.0, 1.0); break; } v_TexCoord = tc; vec2 min = -inv_resolution*size_screen; vec2 max = inv_resolution*size_screen; vec2 pos = center ; switch(gl_VertexIndex) { case 0: pos += vec2(max.x, min.y); break; case 1: pos += vec2(max.x, max.y); break; case 2: pos += vec2(min.x, min.y); break; case 3: pos += vec2(min.x, max.y); break; } v_screen_pos = pos.xy*0.5 +0.5; gl_Position = vec4(pos , 0.5, 1.0); } ================================================ FILE: src/shader/health_bar.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec2 v_min; layout(location = 2) in vec2 v_max; layout(location = 3) in float v_life; layout(location = 4) in float v_alpha; layout(location = 5) in float v_type; layout(location = 0) out vec4 o_Target; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { vec3 color = vec3(pow(1.0- v_life,0.3),pow(v_life,1.0),0.0); if(v_type <= 0.0){ }else if (v_type <= 1.0){ color = vec3(0.5 + 0.13*sin(v_life*6.28*5)); } if (v_TexCoord.x > v_life){ color= vec3(0); } o_Target = vec4(color,v_alpha); } ================================================ FILE: src/shader/health_bar.vert ================================================ #version 450 layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec2 v_min; layout(location = 2) out vec2 v_max; layout(location = 3) out float v_life; layout(location = 4) out float v_alpha; layout(location = 5) out float v_type; layout(location = 0) in vec2 min; layout(location = 1) in vec2 max; layout(location = 2) in float life; layout(location = 3) in float alpha; layout(location = 4) in float type; void main() { v_min = min; v_max = max; v_life = life; v_alpha = alpha; v_type = type; vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(1.0, 0.0); break; case 1: tc = vec2(1.0, 1.0); break; case 2: tc = vec2(0.0, 0.0); break; case 3: tc = vec2(0.0, 1.0); break; } v_TexCoord = tc; vec2 pos = vec2(0.0) ; switch(gl_VertexIndex) { case 0: pos = vec2(max.x, min.y); break; case 1: pos = vec2(max.x, max.y); break; case 2: pos = vec2(min.x, min.y); break; case 3: pos = vec2(min.x, max.y); break; } gl_Position = vec4(pos , 0.5, 1.0); } ================================================ FILE: src/shader/heightmap.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec3 color; layout(location = 2) in float min_lod; layout(location = 3) in float max_mip; layout(location = 0) out vec4 o_Target; layout(location = 1) out vec4 o_position_att; layout(location = 2) out vec2 o_normal; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; layout(set = 0, binding = 1) uniform texture2D t_Color; layout(set = 0, binding = 2) uniform sampler s_Color; layout(set = 1, binding = 0) uniform MapCfg { float width; float height; float cam_x; float cam_y; }; layout(set = 1, binding = 1) uniform texture2D t_Color_checker; layout(set = 1, binding = 2) uniform sampler s_Color_checker; layout(set = 1, binding = 3) uniform texture2D height_tex; layout(set = 1, binding = 4) uniform sampler height_sampler; const vec3 ambientColor = vec3(0.05); const vec3 diffuseColor = vec3(1.0, 1.0, 1.0); const vec3 specColor = vec3(0.2); float linlerp(float v, float min, float max){ return (v-min)/(max-min); } vec3 normal_at(vec2 uv,float lod){ float r= textureLod(sampler2D(height_tex, height_sampler),uv +vec2(1,0)/ vec2(width,height),lod ).r; float l= textureLod(sampler2D(height_tex, height_sampler),uv+ vec2(-1,0)/ vec2(width,height),lod ).r; float u= textureLod(sampler2D(height_tex, height_sampler),uv+vec2(0,1)/ vec2(width,height),lod ).r; float d= textureLod(sampler2D(height_tex, height_sampler),uv +vec2(0,-1)/ vec2(width,height),lod ).r; return normalize(vec3(-(r-l), (d-u), 2)); } float slope_of(vec3 normal){ return 1-asin(normal.z)/(3.141592/2.0); } void main() { vec4 tex = texture(sampler2D(t_Color, s_Color), v_TexCoord); vec4 tex_checker = texture(sampler2D(t_Color_checker, s_Color_checker), (v_TexCoord) * vec2(width/2.0,height/2.0) + vec2(0.5/2.0)); vec2 pos_xy = v_TexCoord* vec2(width,height); vec2 tex_coord_floor =vec2(floor(pos_xy.x),floor(pos_xy.y)) / vec2(width,height); float lod = max(textureQueryLod(sampler2D(height_tex, height_sampler),v_TexCoord).x,min_lod); vec3 pos = vec3(pos_xy, textureLod(sampler2D(height_tex, height_sampler),v_TexCoord,lod ).r ); vec3 normal = normal_at(v_TexCoord,lod); float slope = slope_of(normal); vec3 diffuse= mix(vec3(0.5,0.4,0.3),tex_checker.xyz,0.041); float ground_end= 1/90.0; float grass_start= 2/90.0; vec3 grass_color = vec3(0.45,0.7,0.2); vec3 sand_color = vec3(1.0,0.8,0.7); grass_color = mix(grass_color,sand_color, min(1,max(46.0- pos.z,0)*2 )); if (slope > ground_end){ diffuse = mix(diffuse,grass_color, linlerp(slope,ground_end,grass_start) ); } if (slope > grass_start){ diffuse= grass_color; } if (slope > 45/90.0){ diffuse = vec3(0.5); } //blinn phong vec3 lightPos = vec3(-10000,1000,12000); vec3 vertPos = pos; vec3 lightDir = normalize(lightPos - vertPos); float lambertian = max(dot(lightDir,normal), 0.0); float specular = 0.0; if(lambertian > 0.0) { mat3 rot = mat3(u_View); vec3 camera_pos = -u_View[3].xyz*rot; vec3 viewDir = normalize( camera_pos - vertPos); vec3 halfDir = normalize(lightDir + viewDir); float specAngle = max(dot(halfDir, normal), 0.0); specular = 1.0*pow(specAngle, 32.0); } vec3 phong = vec3(ambientColor + lambertian* diffuse + specular*specColor); // phong =mix(color, phong,0.1); if( pos.y <=0.5 || pos.x <=0.5 || pos.x >= width-0.5 || pos.y >= height ){ phong= vec3(0.1); } o_normal = normal.xy; o_position_att = vec4(pos, 0.0); o_Target = vec4(phong,1.0); } ================================================ FILE: src/shader/heightmap.vert ================================================ #version 450 layout(location = 0) in vec2 a_Pos; layout(location = 1) in float mip; layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec3 color; layout(location = 2) out float min_lod; layout(location = 3) out float max_mip; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; layout(set = 1, binding = 0) uniform MapCfg { float width; float height; float ring_size; float cam_x; float cam_y; }; layout(set = 1, binding = 3) uniform texture2D height_tex; layout(set = 1, binding = 4) uniform sampler height_sampler; layout(set = 1, binding = 5) uniform texture2D height_lod_tex; layout(set = 1, binding = 6) uniform sampler height_lod_sampler; float floor_res(int res, float val ){ return floor(int(val)/res)*res; } void main() { int res = int(round(pow(2.0,(mip)))); vec2 cam_pos = vec2(floor_res(res,cam_x), floor_res(res,cam_y)); //vec2(266.987); // vec2 dim = hmap_size ; vec2 pos_xy = a_Pos.xy+cam_pos + vec2(0.5); v_TexCoord =pos_xy/dim; min_lod = texture(sampler2D(height_lod_tex, height_lod_sampler),v_TexCoord).r; max_mip = max(mip, min_lod); float z = textureLod(sampler2D(height_tex, height_sampler),v_TexCoord, max_mip).r; vec3 pos = vec3(pos_xy,z); color= vec3(max_mip/4); float rock_bottom = -40.0; float f = fract(max_mip); float stride = pow(2,ceil(max_mip)); if(pos_xy.x<-stride){ pos.x= -1.0; pos.z = rock_bottom; color = vec3(0,0,1); } else if(pos_xy.x<=0.0){ pos.x= -1.0; color = vec3(1,0,0); } if(pos_xy.x> width+stride){ pos.x= width+1; pos.z = rock_bottom; color = vec3(0,1,1); } else if(pos_xy.x> width){ pos.x= width+1; color = vec3(1,0,0); } if(pos_xy.y> height + stride){ pos.y= height+1; pos.z = rock_bottom; color = vec3(0,0,1); }else if(pos_xy.y>height){ pos.y= height+1; color = vec3(1,0,0); } if(pos_xy.y<-stride){ pos.y= -1.0; pos.z = rock_bottom; color = vec3(0,0,1); }else if(pos_xy.y<-0.0){ pos.y= -1.0; color = vec3(1,0,0); } v_TexCoord =pos_xy/dim; gl_Position = cor_proj_view * ( vec4(pos,1.0) ); } ================================================ FILE: src/shader/imgui.frag ================================================ #version 450 layout(set = 1, binding = 0) uniform texture2D u_Texture; layout(set = 1, binding = 1) uniform sampler u_Sampler; layout(location = 0) in vec2 v_UV; layout(location = 1) in vec4 v_Color; layout(location = 0) out vec4 o_Target; void main() { o_Target = v_Color * texture(sampler2D(u_Texture, u_Sampler), v_UV); } ================================================ FILE: src/shader/imgui.vert ================================================ #version 450 layout(set = 0, binding = 0) uniform View { mat4 u_Matrix; }; layout(location = 0) in vec2 a_Pos; layout(location = 1) in vec2 a_UV; layout(location = 2) in uint a_Color; layout(location = 0) out vec2 v_UV; layout(location = 1) out vec4 v_Color; // Built-in: // vec4 gl_Position void main() { v_UV = a_UV; v_Color = vec4(a_Color & 0xFF, (a_Color >> 8) & 0xFF, (a_Color >> 16) & 0xFF, (a_Color >> 24) & 0xFF) / 255.0; gl_Position = u_Matrix * vec4(a_Pos.xy, 0.0, 1.0); } ================================================ FILE: src/shader/line.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec2 v_min; layout(location = 2) in vec2 v_max; layout(location = 3) in float v_type; layout(location = 4) in float v_count; layout(location = 5) in float v_l; layout(location = 6) in float v_w; layout(location = 0) out vec4 o_Target; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { float alpha = 1-abs(v_TexCoord.y-0.5)/0.5; alpha = pow(alpha,2); alpha = min(alpha, pow(sin(v_TexCoord.x*v_l*0.2)*0.5+0.5,2) ) ; vec3 color = vec3(0); if (v_type ==0 ){ color = vec3(0,0.5+ 0.5*pow(alpha,0.7),pow(alpha,0.7)*0.5); } else if (v_type ==1 ){ color = vec3(0,pow(alpha,0.7)*0.5,0.5+ 0.5*pow(alpha,0.7)); } else if (v_type ==2 ){ color = vec3(0,0.5+pow(alpha,0.7)*0.5,0.5+ 0.5*pow(alpha,0.7)); } float calpha = pow(1.0/max(v_count-50.0,1.0),0.25); alpha*=calpha; o_Target = vec4(color,alpha); } ================================================ FILE: src/shader/line.vert ================================================ #version 450 layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec2 v_min; layout(location = 2) out vec2 v_max; layout(location = 3) out float v_type; layout(location = 4) out float v_count; layout(location = 5) out float v_l; layout(location = 6) out float v_w; layout(location = 0) in vec2 min; layout(location = 1) in vec2 max; layout(location = 2) in float type; layout(location = 3) in float count; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { v_min = min; v_max = max; v_type = type; v_count = count; v_l = length(max*resolution-min*resolution); v_w = 8; vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(1.0, 0.0); break; case 1: tc = vec2(1.0, 1.0); break; case 2: tc = vec2(0.0, 0.0); break; case 3: tc = vec2(0.0, 1.0); break; } v_TexCoord = tc; vec2 u = normalize(max-min); vec2 ortho = vec2(u.y,-u.x)*inv_resolution*v_w/2.0; vec2 a = min -ortho; vec2 b = min +ortho; vec2 c = max -ortho; vec2 d = max +ortho; vec2 pos = vec2(0.0) ; switch(gl_VertexIndex) { case 0: pos = a; break; case 1: pos = b; break; case 2: pos = c; break; case 3: pos = d; break; } gl_Position = vec4(pos , 0.5, 1.0); } ================================================ FILE: src/shader/post.vert ================================================ #version 450 layout(location = 0) out vec2 v_TexCoord; void main() { vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(1.0, 0.0); break; case 1: tc = vec2(1.0, 1.0); break; case 2: tc = vec2(0.0, 0.0); break; case 3: tc = vec2(0.0, 1.0); break; } v_TexCoord = tc; gl_Position = vec4(tc * 2.0 - 1.0, 0.5, 1.0); } ================================================ FILE: src/shader/post_bicopy.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 0) uniform texture2D t_color; layout(set = 1, binding = 1) uniform sampler s_color; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { vec3 color = texture(sampler2D(t_color, s_color), v_TexCoord).rgb; o_Target = vec4(color,1.0); } ================================================ FILE: src/shader/post_fxaa.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 0) uniform texture2D t_color; layout(set = 1, binding = 1) uniform sampler s_color; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; float rgb2luma(vec3 rgb){ return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114))); } float luma(vec2 offset){ return rgb2luma(texture(sampler2D(t_color, s_color), v_TexCoord + offset*inv_resolution).rgb); } void main() { vec3 color = texture(sampler2D(t_color, s_color), v_TexCoord).rgb; float l= luma(vec2(0)); float l_n = luma(vec2(0,1)); float l_s = luma(vec2(0,-1)); float l_e = luma(vec2(1,0)); float l_w = luma(vec2(-1,0)); float lumaMin = min(l,min(min(l_s,l_n),min(l_e,l_w))); float lumaMax = max(l,max(max(l_s,l_n),max(l_e,l_w))); float lumaRange = lumaMax - lumaMin; if ( lumaRange> min(0.0312 , lumaMax*0.125)){ float l_ne = luma(vec2(1,1)); float l_se = luma(vec2(1,-1)); float l_nw = luma(vec2(-1,1)); float l_sw = luma(vec2(-1,-1)); float l_ns = l_n + l_s; float l_we = l_e + l_w; float l_nc = l_nw + l_ne; float l_sc = l_se + l_sw; float l_ec = l_se + l_ne; float l_wc = l_nw + l_sw; float edgeHorizontal = abs(-2.0 * l_w + l_wc) + abs(-2.0 * l + l_ns ) * 2.0 + abs(-2.0 * l_e + l_ec); float edgeVertical = abs(-2.0 * l_n + l_nc) + abs(-2.0 * l + l_we) * 2.0 + abs(-2.0 * l_s + l_sc); bool isHorizontal = (edgeHorizontal >= edgeVertical); // color = vec3(0.5+ (edgeVertical- edgeHorizontal)/2.0); // Select the two neighboring texels lumas in the opposite direction to the local edge. float luma1 = isHorizontal ? l_s : l_w; float luma2 = isHorizontal ? l_n : l_e; // Compute gradients in this direction. float gradient1 = luma1 - l; float gradient2 = luma2 - l; // Which direction is the steepest ? bool is1Steepest = abs(gradient1) >= abs(gradient2); // Gradient in the corresponding direction, normalized. float gradientScaled = 0.25*max(abs(gradient1),abs(gradient2)); // color = vec3(gradientScaled); // Choose the step size (one pixel) according to the edge direction. float stepLength = isHorizontal ? inv_resolution.y : inv_resolution.x; // Average luma in the correct direction. float lumaLocalAverage = 0.0; if(is1Steepest){ // Switch the direction stepLength = - stepLength; lumaLocalAverage = 0.5*(luma1 + l); } else { lumaLocalAverage = 0.5*(luma2 + l); } // Shift UV in the correct direction by half a pixel. vec2 currentUv = v_TexCoord; if(isHorizontal){ currentUv.y += stepLength * 0.5; } else { currentUv.x += stepLength * 0.5; } ///First iteration exploration // Compute offset (for each iteration step) in the right direction. vec2 offset = isHorizontal ? vec2(inv_resolution.x,0.0) : vec2(0.0,inv_resolution.y); // Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster. vec2 uv1 = currentUv - offset; vec2 uv2 = currentUv + offset; // Read the lumas at both current extremities of the exploration segment, and compute the delta wrt to the local average luma. float lumaEnd1 = rgb2luma(texture(sampler2D(t_color, s_color), uv1).rgb); float lumaEnd2 = rgb2luma(texture(sampler2D(t_color, s_color), uv2).rgb); lumaEnd1 -= lumaLocalAverage; lumaEnd2 -= lumaLocalAverage; // If the luma deltas at the current extremities are larger than the local gradient, we have reached the side of the edge. bool reached1 = abs(lumaEnd1) >= gradientScaled; bool reached2 = abs(lumaEnd2) >= gradientScaled; bool reachedBoth = reached1 && reached2; // If the side is not reached, we continue to explore in this direction. if(!reached1){ uv1 -= offset; } if(!reached2){ uv2 += offset; } // If both sides have not been reached, continue to explore. if(!reachedBoth){ 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); for(int i = 2; i < 12; i++){ // If needed, read luma in 1st direction, compute delta. if(!reached1){ lumaEnd1 = rgb2luma(texture(sampler2D(t_color, s_color), uv1).rgb); lumaEnd1 = lumaEnd1 - lumaLocalAverage; } // If needed, read luma in opposite direction, compute delta. if(!reached2){ lumaEnd2 = rgb2luma(texture(sampler2D(t_color, s_color), uv2).rgb); lumaEnd2 = lumaEnd2 - lumaLocalAverage; } // If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge. reached1 = abs(lumaEnd1) >= gradientScaled; reached2 = abs(lumaEnd2) >= gradientScaled; reachedBoth = reached1 && reached2; // If the side is not reached, we continue to explore in this direction, with a variable quality. if(!reached1){ uv1 -= offset * QUALITY[i]; } if(!reached2){ uv2 += offset * QUALITY[i]; } // If both sides have been reached, stop the exploration. if(reachedBoth){ break;} } } // Compute the distances to each extremity of the edge. float distance1 = isHorizontal ? (v_TexCoord.x - uv1.x) : (v_TexCoord.y - uv1.y); float distance2 = isHorizontal ? (uv2.x - v_TexCoord.x) : (uv2.y - v_TexCoord.y); // In which direction is the extremity of the edge closer ? bool isDirection1 = distance1 < distance2; float distanceFinal = min(distance1, distance2); // Length of the edge. float edgeThickness = (distance1 + distance2); // UV offset: read in the direction of the closest side of the edge. float pixelOffset = - distanceFinal / edgeThickness + 0.5; // Is the luma at center smaller than the local average ? bool isLumaCenterSmaller = l < lumaLocalAverage; // If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation). // (in the direction of the closer side of the edge.) bool correctVariation = ((isDirection1 ? lumaEnd1 : lumaEnd2) < 0.0) != isLumaCenterSmaller; // If the luma variation is incorrect, do not offset. float finalOffset = correctVariation ? pixelOffset : 0.0; // Sub-pixel shifting // Full weighted average of the luma over the 3x3 neighborhood. float lumaAverage = (1.0/12.0) * (2.0 * (l_ns + l_we) + l_ec + l_wc); // Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood. float subPixelOffset1 = clamp(abs(lumaAverage - l)/lumaRange,0.0,1.0); float subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1; // Compute a sub-pixel offset based on this delta. float subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * 0.75; // Pick the biggest of the two offsets. finalOffset = max(finalOffset,subPixelOffsetFinal); // color = vec3(finalOffset); // Compute the final UV coordinates. vec2 finalUv = v_TexCoord; if(isHorizontal){ finalUv.y += finalOffset * stepLength; } else { finalUv.x += finalOffset * stepLength; } // Read the color at the new UV coordinates, and use it. color = texture(sampler2D(t_color, s_color), finalUv).rgb; } o_Target = vec4(color,1.0); } ================================================ FILE: src/shader/post_ui.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 0) uniform texture2D t_pos; layout(set = 1, binding = 1) uniform sampler s_pos; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { vec4 pos_attachment = texture(sampler2D(t_pos, s_pos), v_TexCoord); float alpha= 0; vec3 color = vec3(1.0); if (pen_radius >0){ vec3 frag_pos = pos_attachment.xyz; color = vec3(1.0); //Cursor circle vec3 mouse_pos_world = texture(sampler2D(t_pos, s_pos), mouse_pos/resolution).xyz; float dist = length(frag_pos.xy - mouse_pos_world.xy); float distance_to_sphere = length(pen_radius - dist); float base = clamp(1-distance_to_sphere,0.0,1.0); alpha= pow(min(base,0.01)/0.01,2.0); if (dist < pen_radius){ alpha= max(alpha, 0.5*pen_strength/10.0 ); } if (mouse_pos_world.x <0.0 || frag_pos.x < 0.0 || frag_pos.y <0.0){ alpha = 0; } } // //Unit selection bool is_selected_area = pos_attachment.a >= 0.99; float highlight_index= 0.0; bool is_edge_area = false; for(int i = -1; i<= 1; i++){ for(int j = -1; j<= 1; j++){ if (!is_edge_area && (i!= 0 || j!= 0)){ highlight_index = round(texture( sampler2D(t_pos, s_pos), v_TexCoord + vec2(i,j)/resolution).a); is_edge_area = highlight_index> 0.0; } } } bool is_edge2_area = false; if (!is_edge_area){ for(int i = -2; i<= 2; i++){ for(int j = -2; j<= 2; j++){ if ((max(i,j)==2 || min(i,j)==-2 ) && !is_edge2_area){ is_edge2_area = is_edge2_area|| texture( sampler2D(t_pos, s_pos), v_TexCoord + vec2(i,j)/resolution).a >=0.99; } } } } vec3 highlight_color = vec3(0,1,0); if (highlight_index ==2){ highlight_color = vec3(0.0,0.5,1); } else if (highlight_index ==3){ highlight_color = vec3(0.4,1,0.4); } if (!is_selected_area && is_edge_area ){ alpha = 1.0; color = highlight_color; } else if(!is_selected_area && is_edge2_area){ alpha = 0.5; color = mix(highlight_color,vec3(0),0.5); } if (start_drag != mouse_pos){ uvec2 coord_screen = uvec2(v_TexCoord*resolution); vec2 min_ = vec2(min(start_drag.x,mouse_pos.x), min(start_drag.y,mouse_pos.y)); vec2 max_ = vec2(max(start_drag.x,mouse_pos.x), max(start_drag.y,mouse_pos.y)); if (coord_screen.x > min_.x && coord_screen.y > min_.y&& coord_screen.x < max_.x && coord_screen.y < max_.y){ color = mix(vec3(0.0,1.0,0.3), color, 0.1); alpha = max(alpha, 0.1); } else if (coord_screen.x >= min_.x && coord_screen.y >= min_.y&& coord_screen.x <= max_.x && coord_screen.y <= max_.y){ color = mix(vec3(0.0,0.7,0.1), color, 0.8); alpha = max(alpha, 0.8); } } o_Target = vec4(color,alpha); // o_Target= vec4(pos_attachment.xyz,1.0); } ================================================ FILE: src/shader/unit_icon.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) in vec2 v_center; layout(location = 2) in float v_size; layout(location = 3) in float v_team; layout(location = 0) out vec4 o_Target; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { vec3 color = vec3(1.0); if (v_team< 0){ //Unit is selected color = vec3(1.0); } else if( v_team == 0.0){ color = vec3(0,0.3,1); }else if(v_team < 1.1){ color = vec3(1,0.0,0); } float alpha=1; float distance_from_center = length(v_TexCoord-vec2(0.5))*sqrt(2); float circle_radius = 0.4; float distance_from_circle = abs(circle_radius - distance_from_center); float proximity = 1-distance_from_circle; float thickness = 0.90; float proximity_thick = min(proximity,thickness)/thickness; alpha = pow(proximity_thick,6); color*= pow(alpha,2); o_Target = vec4(color,alpha); } ================================================ FILE: src/shader/unit_icon.vert ================================================ #version 450 layout(location = 0) out vec2 v_TexCoord; layout(location = 1) out vec2 v_center; layout(location = 2) out float v_size; layout(location = 3) out float v_team; layout(location = 0) in vec2 center; layout(location = 1) in float size; layout(location = 2) in float team; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { v_center = center; v_size = size; v_team = team; vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(1.0, 0.0); break; case 1: tc = vec2(1.0, 1.0); break; case 2: tc = vec2(0.0, 0.0); break; case 3: tc = vec2(0.0, 1.0); break; } v_TexCoord = tc; vec2 min = -inv_resolution*size; vec2 max = inv_resolution*size; vec2 pos = center ; switch(gl_VertexIndex) { case 0: pos += vec2(max.x, min.y); break; case 1: pos += vec2(max.x, max.y); break; case 2: pos += vec2(min.x, min.y); break; case 3: pos += vec2(min.x, max.y); break; } gl_Position = vec4(pos , 0.5, 1.0); } ================================================ FILE: src/shader/water.frag ================================================ #version 450 layout(location = 0) in vec2 v_TexCoord; layout(location = 1) flat in int v_floor_lwall_fwall_rwall; layout(location = 0) out vec4 o_Target; layout(set = 1, binding = 0) uniform texture2D t_color; layout(set = 1, binding = 1) uniform sampler s_color; layout(set = 1, binding = 2) uniform texture2D t_pos; layout(set = 1, binding = 3) uniform sampler s_pos; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; int max_step = 40; void main() { float water_level = 40; vec3 world_pos = vec3(v_TexCoord*hmap_size,water_level); vec4 view_pos4 = u_View* vec4(world_pos,1.0); vec3 view_pos = view_pos4.xyz/ view_pos4.w; vec3 normal = vec3(0,0,1); vec3 view_normal = mat3(u_Normal)*normal; vec3 reflected = normalize(reflect(normalize(view_pos), normalize(view_normal))); vec3 current = view_pos; vec3 current_dir = reflected*15; bool found = false; int j = 0; if(v_floor_lwall_fwall_rwall==0) for(int i =0; i1 || projected.y>1 ){ j=max_step; break; } vec4 world = texture(sampler2D(t_pos, s_pos), projected.xy); if ( !(world.w >-0.01) ){ j=max_step; break; } vec4 view= u_View * vec4(world.xyz,1.0); float depth = view.z/view.w; if(depth>current.z ){ //We overshot, go back with binary search found = true; current_dir*=0.5; current -= current_dir; for(int k=0; k< 5; k++){ current_dir*=0.5; vec4 projected= u_proj* vec4(current, 1); projected.xy /= projected.w; projected.xy =projected.xy*0.5 +0.5; vec4 world = texture(sampler2D(t_pos, s_pos), projected.xy); vec4 view= u_View * vec4(world.xyz,1.0); float depth = view.z/view.w; if(depth > current.z){ current -= current_dir; } else{ current += current_dir; } } break; } } vec3 sky_color= vec3(0.1,0.2,0.3); vec3 ref_color= sky_color; if(found){ vec4 projected= u_proj* vec4(current, 1); projected.xy /= projected.w; projected.xy =projected.xy*0.5 +0.5; float alpha = pow(max(abs(0.5-projected.x), abs(0.5-projected.y))/0.5,4); ref_color = mix( texture(sampler2D(t_color, s_color), projected.xy).xyz, ref_color, alpha); } //if wall if(v_floor_lwall_fwall_rwall!=0){ ref_color*=ref_color; } o_Target = vec4(ref_color,1.0); o_Target = vec4(vec3(float(j)/float(max_step)),1.0); o_Target = vec4(vec3(float(found)/1.0),1.0); vec3 water_color = vec3(0.3,0.5,1.0); o_Target = vec4(mix(water_color,ref_color ,0.8),0.9); } ================================================ FILE: src/shader/water.vert ================================================ #version 450 layout(location = 0) out vec2 v_TexCoord; layout(location = 1) flat out int v_floor_lwall_fwall_rwall; layout(set = 0, binding = 0) uniform Locals { mat4 cor_proj_view; mat4 u_View; mat4 u_proj; mat4 u_Normal; vec2 mouse_pos; vec2 resolution; vec2 inv_resolution; vec2 start_drag; float pen_radius; float pen_strength; vec2 hmap_size; }; void main() { float min = -0.00000; float max = 1.0-min; vec2 tc = vec2(0.0); switch(gl_VertexIndex) { case 0: tc = vec2(max, min); break; case 1: tc = vec2(max, max); break; case 2: tc = vec2(min, min); break; case 3: tc = vec2(min, max); break; } v_TexCoord = tc; v_floor_lwall_fwall_rwall = gl_InstanceIndex; float water_level = 40; vec3 pos = vec3(0); switch(v_floor_lwall_fwall_rwall){ case 0: pos = vec3(tc*hmap_size,water_level); break; case 1: if(tc== vec2(max,max)){ tc= vec2(min,min); }else if(tc== vec2(min,min)){ tc= vec2(max,max); } pos = vec3(min*hmap_size.x, tc* vec2( hmap_size.x ,water_level*1.001)); break; case 2: pos = vec3(tc.x* hmap_size.x,min*hmap_size.y, tc.y* water_level*1.001); break; case 3: pos = vec3(max*hmap_size.x, tc* vec2( hmap_size.x ,water_level*1.001)); break; } gl_Position = cor_proj_view *vec4(pos,1.0); } ================================================ FILE: src/unit.rs ================================================ use super::client::*; use crate::model::*; use crate::utils::FileTree; use crate::*; use gpu_obj::model_gpu::ModelGpu; use na::{Matrix4, Point3, Vector2, Vector3}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::path::{Path, PathBuf}; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct PlacedMesh { pub trans: Matrix4, pub mesh_path: PathBuf, pub mesh_index: usize, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum PlacedCollider { Sphere { position: Point3, radius: f32 }, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub enum Joint { Fix, AimWeapon0, Wheel0, } impl Joint { pub fn next(&self) -> Self { match self { Joint::Fix => Joint::AimWeapon0, Joint::AimWeapon0 => Joint::Wheel0, Joint::Wheel0 => Joint::Fix, } } pub fn replace_with_next(&mut self) { let next = self.next(); std::mem::replace(self, next); } } #[derive(Debug, Clone, typename::TypeName, PartialEq, Serialize, Deserialize)] pub struct PartTree { pub id: utils::Id, pub placed_mesh: Option, pub placed_collider: Option, pub parent_to_self: Matrix4, pub joint: Joint, pub children: Vec, } pub struct PartTreeIter<'a> { stack: Vec<&'a PartTree>, } impl<'a> Iterator for PartTreeIter<'a> { type Item = &'a PartTree; fn next(&mut self) -> Option<&'a PartTree> { let item = self.stack.pop()?; for c in item.children.iter() { self.stack.push(c) } Some(item) } } impl PartTree { pub fn iter(&self) -> PartTreeIter { PartTreeIter { stack: vec![self] } } pub fn find_node_mut(&mut self, id: utils::Id) -> Option<&mut PartTree> { if self.id == id { Some(self) } else { for c in self.children.iter_mut() { match c.find_node_mut(id) { Some(node) => return Some(node), None => {} } } None } } pub fn find_node(&self, id: utils::Id) -> Option<&PartTree> { if self.id == id { Some(self) } else { for c in self.children.iter() { match c.find_node(id) { Some(node) => return Some(node), None => {} } } None } } ///Remove a node and return the parent if successful pub fn remove_node(&mut self, id: utils::Id) -> Option> { let pos = self.children.iter().position(|e| e.id == id); match pos { Some(index) => { self.children.remove(index); Some(self.id) } None => { let mut res = None; for c in self.children.iter_mut() { res = res.or(c.remove_node(id)); } res } } } } ================================================ FILE: src/utils.rs ================================================ use base_62::base62; use na::{IsometryMatrix3, Matrix4, Point3, Vector2, Vector3, Vector4}; use rand::seq::SliceRandom; use rand::thread_rng; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, HashSet}; use std::fmt; use std::hash::Hash; use std::hash::Hasher; pub fn face_towards_dir( pos: &Vector3, normalized_dir: &Vector3, normalized_up: &Vector3, ) -> Matrix4 { let x_axis = normalized_dir; let y_axis = normalized_up.cross(&x_axis); let z_axis = x_axis.cross(&y_axis); Matrix4::new( x_axis.x, y_axis.x, z_axis.x, pos.x, // x_axis.y, y_axis.y, z_axis.y, pos.y, // x_axis.z, y_axis.z, z_axis.z, pos.z, // 0.0, 0.0, 0.0, 1.0, ) } const ID_CHARS: [char; 62] = [ 'A', 'Z', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'Q', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'W', 'X', 'C', 'V', 'B', 'N', 'a', 'z', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'q', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'w', 'x', 'c', 'v', 'b', 'n', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ]; const ID_SIZE: usize = 5; trait IdBase { type Type; } pub type IdValue = u64; #[derive(Serialize, Deserialize)] pub struct Id { pub value: IdValue, phantom: std::marker::PhantomData, } impl Id { pub fn new(value: IdValue) -> Self { Id { value, phantom: std::marker::PhantomData, } } } impl fmt::Display for Id { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } } impl fmt::Debug for Id { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let x: [u8; 8] = unsafe { std::mem::transmute(self.value.to_le()) }; let name = format!("{:?}", T::type_name()); let simple_name = name.split("::").last().unwrap(); let simple_name = &simple_name[..simple_name.len() - 1]; write!( f, "{}#{}", simple_name, base62::encode(&x) // format!("{:X}", self.value) ) } } impl Clone for Id { fn clone(&self) -> Self { Self::new(self.value) } } impl Copy for Id {} impl PartialEq for Id { fn eq(&self, other: &Self) -> bool { self.value == other.value } } impl Hash for Id { fn hash(&self, state: &mut H) where H: Hasher, { self.value.hash(state); } } impl Eq for Id {} impl IdBase for Id { type Type = T; } pub fn rand_id() -> Id { Id::new(rand::prelude::random()) } pub fn rand_id_unsafe() -> String { let mut rng = thread_rng(); let mut s = String::with_capacity(ID_SIZE); for _ in 0..ID_SIZE { s.push(*ID_CHARS.choose(&mut rng).unwrap()); } s } pub fn pop_set(set: &mut HashSet) -> T { let elt = set.iter().next().cloned().unwrap(); set.take(&elt).unwrap() } pub fn time(f: F) -> std::time::Duration where F: FnOnce() -> K, { let start = std::time::Instant::now(); f(); start.elapsed() } use std::fs::{self, DirEntry}; use std::io; use std::path::{Path, PathBuf}; #[derive(Clone, Debug)] pub enum FileTree { Unknown, Node { path: PathBuf, children: Vec, }, Leaf { path: PathBuf, }, } impl FileTree { pub fn new(path: PathBuf) -> Self { if path.is_dir() { let mut nodes = Vec::new(); for entry in fs::read_dir(path.clone()).unwrap() { let entry = entry.unwrap(); let path = entry.path(); nodes.push(Self::new(path)); } FileTree::Node { path: path.to_owned(), children: nodes, } } else { FileTree::Leaf { path } } } } pub struct ImageRGBA8 { pub w: u32, pub h: u32, pub data: Vec, } impl ImageRGBA8 { pub fn open(path: &str) -> ImageRGBA8 { use byteorder::{BigEndian, ReadBytesExt}; use std::fs::File; // The decoder is a build for reader and can be used to set various decoding options // via `Transformations`. The default output transformation is `Transformations::EXPAND // | Transformations::STRIP_ALPHA`. let mut decoder = png::Decoder::new(File::open(path).unwrap()); decoder.set_transformations(png::Transformations::IDENTITY); let (info, mut reader) = decoder.read_info().unwrap(); // Display image metadata. log::debug!("info: {:?}", info.width); log::debug!("height: {:?}", info.height); log::debug!("bit depth: {:?}", info.bit_depth); log::debug!("buffer size: {:?}", info.buffer_size()); // Allocate the output buffer. let mut buf = vec![0; info.buffer_size()]; // Read the next frame. Currently this function should only called once. // The default options reader.next_frame(&mut buf).unwrap(); ImageRGBA8 { w: info.width, h: info.height, data: buf, } } }