diff --git a/Cargo.lock b/Cargo.lock index c525bf2..611fe98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -734,6 +734,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + [[package]] name = "is-terminal" version = "0.4.13" @@ -1047,11 +1056,13 @@ version = "0.1.0" dependencies = [ "anyhow", "bytemuck", + "cfg-if", "cgmath", "env_logger", "fs_extra", "glob", "image", + "instant", "log", "pollster", "tobj", diff --git a/Cargo.toml b/Cargo.toml index cc79611..f4c2d62 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,22 +4,22 @@ version = "0.1.0" edition = "2021" [dependencies] -cgmath = "0.18" -winit = { version = "0.29", features = ["rwh_05"] } -env_logger = "0.10" -log = "0.4" -pollster = "0.3" -wgpu = "22.0" -bytemuck = { version = "1.16", features = [ "derive" ] } +cfg-if = "1" anyhow = "1.0" +bytemuck = { version = "1.16", features = [ "derive" ] } +cgmath = "0.18" +env_logger = "0.10" +pollster = "0.3" +log = "0.4" tobj = { version = "3.2", default-features = false, features = ["async"]} -fs_extra = "1.2" -glob = "0.3" +wgpu = { version = "22.0"} +winit = { version = "0.29", features = ["rwh_05"] } +instant = "0.1" [dependencies.image] version = "0.24" default-features = false -features = ["png", "jpeg"] +features = ["png", "jpeg", "hdr"] [build-dependencies] anyhow = "1.0" diff --git a/res/cobble-diffuse.png b/res/cobble-diffuse.png new file mode 100644 index 0000000..93eda7e Binary files /dev/null and b/res/cobble-diffuse.png differ diff --git a/res/cobble-normal.png b/res/cobble-normal.png new file mode 100644 index 0000000..61de39c Binary files /dev/null and b/res/cobble-normal.png differ diff --git a/res/cobble_sphere.mtl b/res/cobble_sphere.mtl new file mode 100644 index 0000000..39f2319 --- /dev/null +++ b/res/cobble_sphere.mtl @@ -0,0 +1,14 @@ +# Blender MTL File: 'cobble_sphere.blend' +# Material Count: 1 + +newmtl Material +Ns 250.000000 +Ka 1.000000 1.000000 1.000000 +Kd 0.800000 0.800000 0.800000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.450000 +d 1.000000 +illum 2 +map_Bump cobble-normal.png +map_Kd cobble-diffuse.png diff --git a/res/cobble_sphere.obj b/res/cobble_sphere.obj new file mode 100644 index 0000000..32eaa7f --- /dev/null +++ b/res/cobble_sphere.obj @@ -0,0 +1,2041 @@ +# Blender v3.6.1 OBJ File: 'cobble_sphere.blend' +# www.blender.org +mtllib cobble_sphere.mtl +o Sphere +v 0.000000 0.831470 -0.555570 +v 0.000000 0.555570 -0.831470 +v 0.000000 0.195090 -0.980785 +v 0.000000 0.000000 -1.000000 +v 0.000000 -0.195090 -0.980785 +v 0.000000 -0.555570 -0.831470 +v 0.038060 0.980785 -0.191342 +v 0.074658 0.923880 -0.375330 +v 0.108386 0.831470 -0.544895 +v 0.137950 0.707107 -0.693520 +v 0.162212 0.555570 -0.815493 +v 0.180240 0.382683 -0.906127 +v 0.191342 0.195090 -0.961940 +v 0.195090 0.000000 -0.980785 +v 0.191342 -0.195090 -0.961940 +v 0.180240 -0.382683 -0.906127 +v 0.162212 -0.555570 -0.815493 +v 0.137950 -0.707107 -0.693520 +v 0.108386 -0.831470 -0.544895 +v 0.074658 -0.923880 -0.375330 +v 0.038060 -0.980785 -0.191342 +v 0.074658 0.980785 -0.180240 +v 0.146447 0.923880 -0.353553 +v 0.212608 0.831470 -0.513280 +v 0.270598 0.707107 -0.653281 +v 0.318190 0.555570 -0.768178 +v 0.353553 0.382683 -0.853553 +v 0.375330 0.195090 -0.906127 +v 0.382683 0.000000 -0.923879 +v 0.375330 -0.195090 -0.906127 +v 0.353553 -0.382683 -0.853553 +v 0.318190 -0.555570 -0.768178 +v 0.270598 -0.707107 -0.653281 +v 0.212608 -0.831470 -0.513280 +v 0.146447 -0.923880 -0.353553 +v 0.074658 -0.980785 -0.180240 +v 0.108386 0.980785 -0.162212 +v 0.212608 0.923880 -0.318190 +v 0.308658 0.831470 -0.461940 +v 0.392847 0.707107 -0.587938 +v 0.461940 0.555570 -0.691342 +v 0.513280 0.382683 -0.768178 +v 0.544895 0.195090 -0.815493 +v 0.555570 0.000000 -0.831469 +v 0.544895 -0.195090 -0.815493 +v 0.513280 -0.382683 -0.768178 +v 0.461940 -0.555570 -0.691342 +v 0.392847 -0.707107 -0.587938 +v 0.308658 -0.831470 -0.461940 +v 0.212608 -0.923880 -0.318190 +v 0.108386 -0.980785 -0.162212 +v 0.137950 0.980785 -0.137950 +v 0.270598 0.923880 -0.270598 +v 0.392847 0.831470 -0.392847 +v 0.500000 0.707107 -0.500000 +v 0.587938 0.555570 -0.587938 +v 0.653281 0.382683 -0.653281 +v 0.693520 0.195090 -0.693520 +v 0.707107 0.000000 -0.707107 +v 0.693520 -0.195090 -0.693520 +v 0.653281 -0.382683 -0.653281 +v 0.587938 -0.555570 -0.587938 +v 0.500000 -0.707107 -0.500000 +v 0.392847 -0.831470 -0.392847 +v 0.270598 -0.923880 -0.270598 +v 0.137950 -0.980785 -0.137950 +v 0.162212 0.980785 -0.108386 +v 0.318190 0.923880 -0.212608 +v 0.461940 0.831470 -0.308658 +v 0.587938 0.707107 -0.392847 +v 0.691342 0.555570 -0.461940 +v 0.768178 0.382683 -0.513280 +v 0.815493 0.195090 -0.544895 +v 0.831470 0.000000 -0.555570 +v 0.815493 -0.195090 -0.544895 +v 0.768178 -0.382683 -0.513280 +v 0.691342 -0.555570 -0.461940 +v 0.587938 -0.707107 -0.392847 +v 0.461940 -0.831470 -0.308658 +v 0.318190 -0.923880 -0.212608 +v 0.162212 -0.980785 -0.108386 +v 0.000000 1.000000 0.000000 +v 0.180240 0.980785 -0.074658 +v 0.353553 0.923880 -0.146447 +v 0.513280 0.831470 -0.212607 +v 0.653281 0.707107 -0.270598 +v 0.768178 0.555570 -0.318190 +v 0.853553 0.382683 -0.353553 +v 0.906127 0.195090 -0.375330 +v 0.923879 0.000000 -0.382683 +v 0.906127 -0.195090 -0.375330 +v 0.853553 -0.382683 -0.353553 +v 0.768178 -0.555570 -0.318190 +v 0.653281 -0.707107 -0.270598 +v 0.513280 -0.831470 -0.212607 +v 0.353553 -0.923880 -0.146447 +v 0.180240 -0.980785 -0.074658 +v 0.191342 0.980785 -0.038060 +v 0.375330 0.923880 -0.074658 +v 0.544895 0.831470 -0.108386 +v 0.693520 0.707107 -0.137950 +v 0.815493 0.555570 -0.162212 +v 0.906127 0.382683 -0.180240 +v 0.961940 0.195090 -0.191342 +v 0.980785 0.000000 -0.195090 +v 0.961940 -0.195090 -0.191342 +v 0.906127 -0.382683 -0.180240 +v 0.815493 -0.555570 -0.162212 +v 0.693520 -0.707107 -0.137950 +v 0.544895 -0.831470 -0.108386 +v 0.375330 -0.923880 -0.074658 +v 0.191342 -0.980785 -0.038060 +v 0.195090 0.980785 0.000000 +v 0.382683 0.923880 0.000000 +v 0.555570 0.831470 0.000000 +v 0.707107 0.707107 -0.000000 +v 0.831469 0.555570 0.000000 +v 0.923879 0.382683 -0.000000 +v 0.980785 0.195090 0.000000 +v 1.000000 0.000000 0.000000 +v 0.980785 -0.195090 0.000000 +v 0.923879 -0.382683 -0.000000 +v 0.831469 -0.555570 0.000000 +v 0.707107 -0.707107 -0.000000 +v 0.555570 -0.831470 0.000000 +v 0.382683 -0.923880 0.000000 +v 0.195090 -0.980785 0.000000 +v 0.191342 0.980785 0.038060 +v 0.375330 0.923880 0.074658 +v 0.544895 0.831470 0.108386 +v 0.693520 0.707107 0.137950 +v 0.815493 0.555570 0.162212 +v 0.906127 0.382683 0.180240 +v 0.961940 0.195090 0.191342 +v 0.980785 0.000000 0.195090 +v 0.961940 -0.195090 0.191342 +v 0.906127 -0.382683 0.180240 +v 0.815493 -0.555570 0.162212 +v 0.693520 -0.707107 0.137950 +v 0.544895 -0.831470 0.108386 +v 0.375330 -0.923880 0.074658 +v 0.191342 -0.980785 0.038060 +v 0.180240 0.980785 0.074658 +v 0.353553 0.923880 0.146447 +v 0.513280 0.831470 0.212608 +v 0.653281 0.707107 0.270598 +v 0.768178 0.555570 0.318190 +v 0.853553 0.382683 0.353553 +v 0.906127 0.195090 0.375330 +v 0.923879 0.000000 0.382683 +v 0.906127 -0.195090 0.375330 +v 0.853553 -0.382683 0.353553 +v 0.768178 -0.555570 0.318190 +v 0.653281 -0.707107 0.270598 +v 0.513280 -0.831470 0.212608 +v 0.353553 -0.923880 0.146447 +v 0.180240 -0.980785 0.074658 +v 0.162212 0.980785 0.108386 +v 0.318190 0.923880 0.212608 +v 0.461940 0.831470 0.308658 +v 0.587938 0.707107 0.392847 +v 0.691341 0.555570 0.461940 +v 0.768178 0.382683 0.513280 +v 0.815493 0.195090 0.544895 +v 0.831469 0.000000 0.555570 +v 0.815493 -0.195090 0.544895 +v 0.768178 -0.382683 0.513280 +v 0.691341 -0.555570 0.461940 +v 0.587938 -0.707107 0.392847 +v 0.461940 -0.831470 0.308658 +v 0.318190 -0.923880 0.212608 +v 0.162212 -0.980785 0.108386 +v 0.137950 0.980785 0.137950 +v 0.270598 0.923880 0.270598 +v 0.392847 0.831470 0.392847 +v 0.500000 0.707107 0.500000 +v 0.587938 0.555570 0.587938 +v 0.653281 0.382683 0.653281 +v 0.693520 0.195090 0.693520 +v 0.707106 0.000000 0.707107 +v 0.693520 -0.195090 0.693520 +v 0.653281 -0.382683 0.653281 +v 0.587938 -0.555570 0.587938 +v 0.500000 -0.707107 0.500000 +v 0.392847 -0.831470 0.392847 +v 0.270598 -0.923880 0.270598 +v 0.137950 -0.980785 0.137950 +v 0.108386 0.980785 0.162212 +v 0.212607 0.923880 0.318190 +v 0.308658 0.831470 0.461940 +v 0.392847 0.707107 0.587938 +v 0.461940 0.555570 0.691342 +v 0.513280 0.382683 0.768178 +v 0.544895 0.195090 0.815493 +v 0.555570 0.000000 0.831469 +v 0.544895 -0.195090 0.815493 +v 0.513280 -0.382683 0.768178 +v 0.461940 -0.555570 0.691342 +v 0.392847 -0.707107 0.587938 +v 0.308658 -0.831470 0.461940 +v 0.212607 -0.923880 0.318190 +v 0.108386 -0.980785 0.162212 +v 0.074658 0.980785 0.180240 +v 0.146447 0.923880 0.353553 +v 0.212607 0.831470 0.513280 +v 0.270598 0.707107 0.653281 +v 0.318189 0.555570 0.768178 +v 0.353553 0.382683 0.853553 +v 0.375330 0.195090 0.906127 +v 0.382683 0.000000 0.923879 +v 0.375330 -0.195090 0.906127 +v 0.353553 -0.382683 0.853553 +v 0.318189 -0.555570 0.768178 +v 0.270598 -0.707107 0.653281 +v 0.212607 -0.831470 0.513280 +v 0.146447 -0.923880 0.353553 +v 0.074658 -0.980785 0.180240 +v 0.038060 0.980785 0.191342 +v 0.074658 0.923880 0.375330 +v 0.108386 0.831470 0.544895 +v 0.137950 0.707107 0.693520 +v 0.162212 0.555570 0.815493 +v 0.180240 0.382683 0.906127 +v 0.191342 0.195090 0.961939 +v 0.195090 0.000000 0.980785 +v 0.191342 -0.195090 0.961939 +v 0.180240 -0.382683 0.906127 +v 0.162212 -0.555570 0.815493 +v 0.137950 -0.707107 0.693520 +v 0.108386 -0.831470 0.544895 +v 0.074658 -0.923880 0.375330 +v 0.038060 -0.980785 0.191342 +v -0.000000 0.980785 0.195090 +v -0.000000 0.923880 0.382683 +v -0.000000 0.831470 0.555570 +v -0.000000 0.707107 0.707107 +v -0.000000 0.555570 0.831469 +v 0.000000 0.382683 0.923879 +v -0.000000 0.195090 0.980785 +v -0.000000 0.000000 0.999999 +v -0.000000 -0.195090 0.980785 +v 0.000000 -0.382683 0.923879 +v -0.000000 -0.555570 0.831469 +v -0.000000 -0.707107 0.707107 +v -0.000000 -0.831470 0.555570 +v -0.000000 -0.923880 0.382683 +v -0.000000 -0.980785 0.195090 +v -0.038060 0.980785 0.191342 +v -0.074658 0.923880 0.375330 +v -0.108386 0.831470 0.544895 +v -0.137950 0.707107 0.693520 +v -0.162212 0.555570 0.815493 +v -0.180240 0.382683 0.906127 +v -0.191342 0.195090 0.961939 +v -0.195091 0.000000 0.980785 +v -0.191342 -0.195090 0.961939 +v -0.180240 -0.382683 0.906127 +v -0.162212 -0.555570 0.815493 +v -0.137950 -0.707107 0.693520 +v -0.108386 -0.831470 0.544895 +v -0.074658 -0.923880 0.375330 +v -0.038060 -0.980785 0.191342 +v -0.074658 0.980785 0.180240 +v -0.146447 0.923880 0.353553 +v -0.212608 0.831470 0.513280 +v -0.270598 0.707107 0.653281 +v -0.318190 0.555570 0.768177 +v -0.353553 0.382683 0.853553 +v -0.375330 0.195090 0.906127 +v -0.382683 0.000000 0.923879 +v -0.375330 -0.195090 0.906127 +v -0.353553 -0.382683 0.853553 +v -0.318190 -0.555570 0.768177 +v -0.270598 -0.707107 0.653281 +v -0.212608 -0.831470 0.513280 +v -0.146447 -0.923880 0.353553 +v -0.074658 -0.980785 0.180240 +v -0.108386 0.980785 0.162212 +v -0.212608 0.923880 0.318190 +v -0.308658 0.831470 0.461939 +v -0.392847 0.707107 0.587938 +v -0.461940 0.555570 0.691341 +v -0.513280 0.382683 0.768178 +v -0.544895 0.195090 0.815493 +v -0.555570 0.000000 0.831469 +v -0.544895 -0.195090 0.815493 +v -0.513280 -0.382683 0.768178 +v -0.461940 -0.555570 0.691341 +v -0.392847 -0.707107 0.587938 +v -0.308658 -0.831470 0.461939 +v -0.212608 -0.923880 0.318190 +v -0.108386 -0.980785 0.162212 +v -0.137950 0.980785 0.137950 +v -0.270598 0.923880 0.270598 +v -0.392847 0.831470 0.392847 +v -0.500000 0.707107 0.500000 +v -0.587938 0.555570 0.587937 +v -0.653281 0.382683 0.653281 +v -0.693520 0.195090 0.693520 +v -0.707106 0.000000 0.707106 +v -0.693520 -0.195090 0.693520 +v -0.653281 -0.382683 0.653281 +v -0.587938 -0.555570 0.587937 +v -0.500000 -0.707107 0.500000 +v -0.392847 -0.831470 0.392847 +v -0.270598 -0.923880 0.270598 +v -0.137950 -0.980785 0.137950 +v 0.000000 -1.000000 0.000000 +v -0.162212 0.980785 0.108386 +v -0.318190 0.923880 0.212607 +v -0.461940 0.831470 0.308658 +v -0.587938 0.707107 0.392847 +v -0.691341 0.555570 0.461939 +v -0.768177 0.382683 0.513280 +v -0.815493 0.195090 0.544895 +v -0.831469 0.000000 0.555569 +v -0.815493 -0.195090 0.544895 +v -0.768177 -0.382683 0.513280 +v -0.691341 -0.555570 0.461939 +v -0.587938 -0.707107 0.392847 +v -0.461940 -0.831470 0.308658 +v -0.318190 -0.923880 0.212607 +v -0.162212 -0.980785 0.108386 +v -0.180240 0.980785 0.074658 +v -0.353553 0.923880 0.146447 +v -0.513280 0.831470 0.212607 +v -0.653281 0.707107 0.270598 +v -0.768177 0.555570 0.318189 +v -0.853553 0.382683 0.353553 +v -0.906127 0.195090 0.375330 +v -0.923879 0.000000 0.382683 +v -0.906127 -0.195090 0.375330 +v -0.853553 -0.382683 0.353553 +v -0.768177 -0.555570 0.318189 +v -0.653281 -0.707107 0.270598 +v -0.513280 -0.831470 0.212607 +v -0.353553 -0.923880 0.146447 +v -0.180240 -0.980785 0.074658 +v -0.191342 0.980785 0.038060 +v -0.375330 0.923880 0.074658 +v -0.544895 0.831470 0.108386 +v -0.693520 0.707107 0.137950 +v -0.815493 0.555570 0.162211 +v -0.906127 0.382683 0.180240 +v -0.961939 0.195090 0.191341 +v -0.980784 0.000000 0.195090 +v -0.961939 -0.195090 0.191341 +v -0.906127 -0.382683 0.180240 +v -0.815493 -0.555570 0.162211 +v -0.693520 -0.707107 0.137950 +v -0.544895 -0.831470 0.108386 +v -0.375330 -0.923880 0.074658 +v -0.191342 -0.980785 0.038060 +v -0.195090 0.980785 -0.000000 +v -0.382683 0.923880 -0.000000 +v -0.555570 0.831470 -0.000000 +v -0.707107 0.707107 -0.000000 +v -0.831469 0.555570 -0.000000 +v -0.923879 0.382683 -0.000000 +v -0.980785 0.195090 -0.000000 +v -0.999999 0.000000 -0.000000 +v -0.980785 -0.195090 -0.000000 +v -0.923879 -0.382683 -0.000000 +v -0.831469 -0.555570 -0.000000 +v -0.707107 -0.707107 -0.000000 +v -0.555570 -0.831470 -0.000000 +v -0.382683 -0.923880 -0.000000 +v -0.195090 -0.980785 -0.000000 +v -0.191342 0.980785 -0.038060 +v -0.375330 0.923880 -0.074658 +v -0.544895 0.831470 -0.108386 +v -0.693520 0.707107 -0.137950 +v -0.815493 0.555570 -0.162212 +v -0.906127 0.382683 -0.180240 +v -0.961939 0.195090 -0.191342 +v -0.980784 0.000000 -0.195091 +v -0.961939 -0.195090 -0.191342 +v -0.906127 -0.382683 -0.180240 +v -0.815493 -0.555570 -0.162212 +v -0.693520 -0.707107 -0.137950 +v -0.544895 -0.831470 -0.108386 +v -0.375330 -0.923880 -0.074658 +v -0.191342 -0.980785 -0.038060 +v -0.180240 0.980785 -0.074658 +v -0.353553 0.923880 -0.146447 +v -0.513279 0.831470 -0.212607 +v -0.653281 0.707107 -0.270598 +v -0.768177 0.555570 -0.318190 +v -0.853553 0.382683 -0.353553 +v -0.906127 0.195090 -0.375330 +v -0.923878 0.000000 -0.382683 +v -0.906127 -0.195090 -0.375330 +v -0.853553 -0.382683 -0.353553 +v -0.768177 -0.555570 -0.318190 +v -0.653281 -0.707107 -0.270598 +v -0.513279 -0.831470 -0.212607 +v -0.353553 -0.923880 -0.146447 +v -0.180240 -0.980785 -0.074658 +v -0.162212 0.980785 -0.108386 +v -0.318189 0.923880 -0.212607 +v -0.461939 0.831470 -0.308658 +v -0.587938 0.707107 -0.392847 +v -0.691341 0.555570 -0.461940 +v -0.768177 0.382683 -0.513280 +v -0.815493 0.195090 -0.544895 +v -0.831468 0.000000 -0.555570 +v -0.815493 -0.195090 -0.544895 +v -0.768177 -0.382683 -0.513280 +v -0.691341 -0.555570 -0.461940 +v -0.587938 -0.707107 -0.392847 +v -0.461939 -0.831470 -0.308658 +v -0.318189 -0.923880 -0.212607 +v -0.162212 -0.980785 -0.108386 +v -0.137950 0.980785 -0.137950 +v -0.270598 0.923880 -0.270598 +v -0.392847 0.831470 -0.392847 +v -0.500000 0.707107 -0.500000 +v -0.587937 0.555570 -0.587938 +v -0.653281 0.382683 -0.653281 +v -0.693519 0.195090 -0.693520 +v -0.707106 0.000000 -0.707106 +v -0.693519 -0.195090 -0.693520 +v -0.653281 -0.382683 -0.653281 +v -0.587937 -0.555570 -0.587938 +v -0.500000 -0.707107 -0.500000 +v -0.392847 -0.831470 -0.392847 +v -0.270598 -0.923880 -0.270598 +v -0.137950 -0.980785 -0.137950 +v -0.108386 0.980785 -0.162212 +v -0.212607 0.923880 -0.318190 +v -0.308658 0.831470 -0.461939 +v -0.392847 0.707107 -0.587938 +v -0.461939 0.555570 -0.691341 +v -0.513280 0.382683 -0.768177 +v -0.544895 0.195090 -0.815493 +v -0.555569 0.000000 -0.831469 +v -0.544895 -0.195090 -0.815493 +v -0.513280 -0.382683 -0.768177 +v -0.461939 -0.555570 -0.691341 +v -0.392847 -0.707107 -0.587938 +v -0.308658 -0.831470 -0.461939 +v -0.212607 -0.923880 -0.318190 +v -0.108386 -0.980785 -0.162212 +v -0.074658 0.980785 -0.180240 +v -0.146446 0.923880 -0.353553 +v -0.212607 0.831470 -0.513279 +v -0.270598 0.707107 -0.653281 +v -0.318189 0.555570 -0.768177 +v -0.353553 0.382683 -0.853553 +v -0.375330 0.195090 -0.906127 +v -0.382683 0.000000 -0.923879 +v -0.375330 -0.195090 -0.906127 +v -0.353553 -0.382683 -0.853553 +v -0.318189 -0.555570 -0.768177 +v -0.270598 -0.707107 -0.653281 +v -0.212607 -0.831470 -0.513279 +v -0.146446 -0.923880 -0.353553 +v -0.074658 -0.980785 -0.180240 +v -0.038060 0.980785 -0.191342 +v -0.074658 0.923880 -0.375330 +v -0.108386 0.831470 -0.544895 +v -0.137950 0.707107 -0.693520 +v -0.162211 0.555570 -0.815493 +v -0.180240 0.382683 -0.906127 +v -0.191341 0.195090 -0.961939 +v -0.195090 0.000000 -0.980784 +v -0.191341 -0.195090 -0.961939 +v -0.180240 -0.382683 -0.906127 +v -0.162211 -0.555570 -0.815493 +v -0.137950 -0.707107 -0.693520 +v -0.108386 -0.831470 -0.544895 +v -0.074658 -0.923880 -0.375330 +v -0.038060 -0.980785 -0.191342 +v 0.000000 0.980785 -0.195090 +v 0.000000 0.923880 -0.382683 +v 0.000000 0.707107 -0.707107 +v 0.000000 0.382683 -0.923879 +v 0.000000 -0.382683 -0.923879 +v 0.000000 -0.707107 -0.707107 +v 0.000000 -0.831470 -0.555570 +v 0.000000 -0.923880 -0.382683 +v 0.000000 -0.980785 -0.195090 +vt 0.750000 0.312500 +vt 0.750000 0.375000 +vt 0.718750 0.375000 +vt 0.718750 0.312500 +vt 0.750000 0.750000 +vt 0.750000 0.812500 +vt 0.718750 0.812500 +vt 0.718750 0.750000 +vt 0.750000 0.250000 +vt 0.718750 0.250000 +vt 0.750000 0.687500 +vt 0.718750 0.687500 +vt 0.750000 0.187500 +vt 0.718750 0.187500 +vt 0.750000 0.625000 +vt 0.718750 0.625000 +vt 0.750000 0.125000 +vt 0.718750 0.125000 +vt 0.750000 0.562500 +vt 0.718750 0.562500 +vt 0.750000 0.062500 +vt 0.718750 0.062500 +vt 0.750000 0.500000 +vt 0.718750 0.500000 +vt 0.750000 0.937500 +vt 0.734375 1.000000 +vt 0.718750 0.937500 +vt 0.734375 0.000000 +vt 0.750000 0.437500 +vt 0.718750 0.437500 +vt 0.750000 0.875000 +vt 0.718750 0.875000 +vt 0.687500 0.437500 +vt 0.687500 0.375000 +vt 0.687500 0.875000 +vt 0.687500 0.812500 +vt 0.687500 0.312500 +vt 0.687500 0.750000 +vt 0.687500 0.250000 +vt 0.687500 0.687500 +vt 0.687500 0.187500 +vt 0.687500 0.625000 +vt 0.687500 0.125000 +vt 0.687500 0.562500 +vt 0.687500 0.062500 +vt 0.687500 0.500000 +vt 0.703125 1.000000 +vt 0.687500 0.937500 +vt 0.703125 0.000000 +vt 0.656250 0.187500 +vt 0.656250 0.125000 +vt 0.656250 0.625000 +vt 0.656250 0.562500 +vt 0.656250 0.062500 +vt 0.656250 0.500000 +vt 0.671875 1.000000 +vt 0.656250 0.937500 +vt 0.671875 0.000000 +vt 0.656250 0.437500 +vt 0.656250 0.875000 +vt 0.656250 0.375000 +vt 0.656250 0.812500 +vt 0.656250 0.312500 +vt 0.656250 0.750000 +vt 0.656250 0.250000 +vt 0.656250 0.687500 +vt 0.625000 0.875000 +vt 0.625000 0.812500 +vt 0.625000 0.375000 +vt 0.625000 0.312500 +vt 0.625000 0.750000 +vt 0.625000 0.250000 +vt 0.625000 0.687500 +vt 0.625000 0.187500 +vt 0.625000 0.625000 +vt 0.625000 0.125000 +vt 0.625000 0.562500 +vt 0.625000 0.062500 +vt 0.625000 0.500000 +vt 0.640625 1.000000 +vt 0.625000 0.937500 +vt 0.640625 0.000000 +vt 0.625000 0.437500 +vt 0.593750 0.625000 +vt 0.593750 0.562500 +vt 0.593750 0.125000 +vt 0.593750 0.062500 +vt 0.593750 0.500000 +vt 0.609375 1.000000 +vt 0.593750 0.937500 +vt 0.609375 0.000000 +vt 0.593750 0.437500 +vt 0.593750 0.875000 +vt 0.593750 0.375000 +vt 0.593750 0.812500 +vt 0.593750 0.312500 +vt 0.593750 0.750000 +vt 0.593750 0.250000 +vt 0.593750 0.687500 +vt 0.593750 0.187500 +vt 0.562500 0.375000 +vt 0.562500 0.312500 +vt 0.562500 0.812500 +vt 0.562500 0.750000 +vt 0.562500 0.250000 +vt 0.562500 0.687500 +vt 0.562500 0.187500 +vt 0.562500 0.625000 +vt 0.562500 0.125000 +vt 0.562500 0.562500 +vt 0.562500 0.062500 +vt 0.562500 0.500000 +vt 0.578125 1.000000 +vt 0.562500 0.937500 +vt 0.578125 0.000000 +vt 0.562500 0.437500 +vt 0.562500 0.875000 +vt 0.531250 0.125000 +vt 0.531250 0.062500 +vt 0.531250 0.562500 +vt 0.531250 0.500000 +vt 0.546875 1.000000 +vt 0.531250 0.937500 +vt 0.546875 0.000000 +vt 0.531250 0.437500 +vt 0.531250 0.875000 +vt 0.531250 0.375000 +vt 0.531250 0.812500 +vt 0.531250 0.312500 +vt 0.531250 0.750000 +vt 0.531250 0.250000 +vt 0.531250 0.687500 +vt 0.531250 0.187500 +vt 0.531250 0.625000 +vt 0.500000 0.312500 +vt 0.500000 0.250000 +vt 0.500000 0.750000 +vt 0.500000 0.687500 +vt 0.500000 0.187500 +vt 0.500000 0.625000 +vt 0.500000 0.125000 +vt 0.500000 0.562500 +vt 0.500000 0.062500 +vt 0.500000 0.500000 +vt 0.515625 1.000000 +vt 0.500000 0.937500 +vt 0.515625 0.000000 +vt 0.500000 0.437500 +vt 0.500000 0.875000 +vt 0.500000 0.375000 +vt 0.500000 0.812500 +vt 0.484375 1.000000 +vt 0.468750 0.937500 +vt 0.484375 0.000000 +vt 0.468750 0.062500 +vt 0.468750 0.500000 +vt 0.468750 0.437500 +vt 0.468750 0.875000 +vt 0.468750 0.375000 +vt 0.468750 0.812500 +vt 0.468750 0.312500 +vt 0.468750 0.750000 +vt 0.468750 0.250000 +vt 0.468750 0.687500 +vt 0.468750 0.187500 +vt 0.468750 0.625000 +vt 0.468750 0.125000 +vt 0.468750 0.562500 +vt 0.437500 0.750000 +vt 0.437500 0.687500 +vt 0.437500 0.250000 +vt 0.437500 0.187500 +vt 0.437500 0.625000 +vt 0.437500 0.125000 +vt 0.437500 0.562500 +vt 0.437500 0.062500 +vt 0.437500 0.500000 +vt 0.453125 1.000000 +vt 0.437500 0.937500 +vt 0.453125 0.000000 +vt 0.437500 0.437500 +vt 0.437500 0.875000 +vt 0.437500 0.375000 +vt 0.437500 0.812500 +vt 0.437500 0.312500 +vt 0.406250 0.500000 +vt 0.406250 0.437500 +vt 0.406250 0.937500 +vt 0.406250 0.875000 +vt 0.406250 0.375000 +vt 0.406250 0.812500 +vt 0.406250 0.312500 +vt 0.406250 0.750000 +vt 0.406250 0.250000 +vt 0.406250 0.687500 +vt 0.406250 0.187500 +vt 0.406250 0.625000 +vt 0.406250 0.125000 +vt 0.406250 0.562500 +vt 0.406250 0.062500 +vt 0.421875 1.000000 +vt 0.421875 0.000000 +vt 0.375000 0.250000 +vt 0.375000 0.187500 +vt 0.375000 0.687500 +vt 0.375000 0.625000 +vt 0.375000 0.125000 +vt 0.375000 0.562500 +vt 0.375000 0.062500 +vt 0.375000 0.500000 +vt 0.390625 1.000000 +vt 0.375000 0.937500 +vt 0.390625 0.000000 +vt 0.375000 0.437500 +vt 0.375000 0.875000 +vt 0.375000 0.375000 +vt 0.375000 0.812500 +vt 0.375000 0.312500 +vt 0.375000 0.750000 +vt 0.343750 0.937500 +vt 0.343750 0.875000 +vt 0.343750 0.437500 +vt 0.343750 0.375000 +vt 0.343750 0.812500 +vt 0.343750 0.312500 +vt 0.343750 0.750000 +vt 0.343750 0.250000 +vt 0.343750 0.687500 +vt 0.343750 0.187500 +vt 0.343750 0.625000 +vt 0.343750 0.125000 +vt 0.343750 0.562500 +vt 0.343750 0.062500 +vt 0.343750 0.500000 +vt 0.359375 1.000000 +vt 0.359375 0.000000 +vt 0.312500 0.687500 +vt 0.312500 0.625000 +vt 0.312500 0.187500 +vt 0.312500 0.125000 +vt 0.312500 0.562500 +vt 0.312500 0.062500 +vt 0.312500 0.500000 +vt 0.328125 1.000000 +vt 0.312500 0.937500 +vt 0.328125 0.000000 +vt 0.312500 0.437500 +vt 0.312500 0.875000 +vt 0.312500 0.375000 +vt 0.312500 0.812500 +vt 0.312500 0.312500 +vt 0.312500 0.750000 +vt 0.312500 0.250000 +vt 0.281250 0.437500 +vt 0.281250 0.375000 +vt 0.281250 0.875000 +vt 0.281250 0.812500 +vt 0.281250 0.312500 +vt 0.281250 0.750000 +vt 0.281250 0.250000 +vt 0.281250 0.687500 +vt 0.281250 0.187500 +vt 0.281250 0.625000 +vt 0.281250 0.125000 +vt 0.281250 0.562500 +vt 0.281250 0.062500 +vt 0.281250 0.500000 +vt 0.296875 1.000000 +vt 0.281250 0.937500 +vt 0.296875 0.000000 +vt 0.250000 0.187500 +vt 0.250000 0.125000 +vt 0.250000 0.625000 +vt 0.250000 0.562500 +vt 0.250000 0.062500 +vt 0.250000 0.500000 +vt 0.265625 1.000000 +vt 0.250000 0.937500 +vt 0.265625 0.000000 +vt 0.250000 0.437500 +vt 0.250000 0.875000 +vt 0.250000 0.375000 +vt 0.250000 0.812500 +vt 0.250000 0.312500 +vt 0.250000 0.750000 +vt 0.250000 0.250000 +vt 0.250000 0.687500 +vt 0.218750 0.375000 +vt 0.218750 0.312500 +vt 0.218750 0.812500 +vt 0.218750 0.750000 +vt 0.218750 0.250000 +vt 0.218750 0.687500 +vt 0.218750 0.187500 +vt 0.218750 0.625000 +vt 0.218750 0.125000 +vt 0.218750 0.562500 +vt 0.218750 0.062500 +vt 0.218750 0.500000 +vt 0.234375 1.000000 +vt 0.218750 0.937500 +vt 0.234375 0.000000 +vt 0.218750 0.437500 +vt 0.218750 0.875000 +vt 0.187500 0.125000 +vt 0.187500 0.062500 +vt 0.187500 0.562500 +vt 0.187500 0.500000 +vt 0.203125 1.000000 +vt 0.187500 0.937500 +vt 0.203125 0.000000 +vt 0.187500 0.437500 +vt 0.187500 0.875000 +vt 0.187500 0.375000 +vt 0.187500 0.812500 +vt 0.187500 0.312500 +vt 0.187500 0.750000 +vt 0.187500 0.250000 +vt 0.187500 0.687500 +vt 0.187500 0.187500 +vt 0.187500 0.625000 +vt 0.156250 0.812500 +vt 0.156250 0.750000 +vt 0.156250 0.312500 +vt 0.156250 0.250000 +vt 0.156250 0.687500 +vt 0.156250 0.187500 +vt 0.156250 0.625000 +vt 0.156250 0.125000 +vt 0.156250 0.562500 +vt 0.156250 0.062500 +vt 0.156250 0.500000 +vt 0.171875 1.000000 +vt 0.156250 0.937500 +vt 0.171875 0.000000 +vt 0.156250 0.437500 +vt 0.156250 0.875000 +vt 0.156250 0.375000 +vt 0.125000 0.562500 +vt 0.125000 0.500000 +vt 0.140625 1.000000 +vt 0.125000 0.937500 +vt 0.140625 0.000000 +vt 0.125000 0.062500 +vt 0.125000 0.437500 +vt 0.125000 0.875000 +vt 0.125000 0.375000 +vt 0.125000 0.812500 +vt 0.125000 0.312500 +vt 0.125000 0.750000 +vt 0.125000 0.250000 +vt 0.125000 0.687500 +vt 0.125000 0.187500 +vt 0.125000 0.625000 +vt 0.125000 0.125000 +vt 0.093750 0.312500 +vt 0.093750 0.250000 +vt 0.093750 0.750000 +vt 0.093750 0.687500 +vt 0.093750 0.187500 +vt 0.093750 0.625000 +vt 0.093750 0.125000 +vt 0.093750 0.562500 +vt 0.093750 0.062500 +vt 0.093750 0.500000 +vt 0.109375 1.000000 +vt 0.093750 0.937500 +vt 0.109375 0.000000 +vt 0.093750 0.437500 +vt 0.093750 0.875000 +vt 0.093750 0.375000 +vt 0.093750 0.812500 +vt 0.078125 1.000000 +vt 0.062500 0.937500 +vt 0.078125 0.000000 +vt 0.062500 0.062500 +vt 0.062500 0.500000 +vt 0.062500 0.437500 +vt 0.062500 0.875000 +vt 0.062500 0.375000 +vt 0.062500 0.812500 +vt 0.062500 0.312500 +vt 0.062500 0.750000 +vt 0.062500 0.250000 +vt 0.062500 0.687500 +vt 0.062500 0.187500 +vt 0.062500 0.625000 +vt 0.062500 0.125000 +vt 0.062500 0.562500 +vt 0.031250 0.750000 +vt 0.031250 0.687500 +vt 0.031250 0.250000 +vt 0.031250 0.187500 +vt 0.031250 0.625000 +vt 0.031250 0.125000 +vt 0.031250 0.562500 +vt 0.031250 0.062500 +vt 0.031250 0.500000 +vt 0.046875 1.000000 +vt 0.031250 0.937500 +vt 0.046875 0.000000 +vt 0.031250 0.437500 +vt 0.031250 0.875000 +vt 0.031250 0.375000 +vt 0.031250 0.812500 +vt 0.031250 0.312500 +vt 0.000000 0.500000 +vt 0.000000 0.437500 +vt 0.000000 0.937500 +vt 0.000000 0.875000 +vt 0.000000 0.375000 +vt 0.000000 0.812500 +vt 0.000000 0.312500 +vt 0.000000 0.750000 +vt 0.000000 0.250000 +vt 0.000000 0.687500 +vt 0.000000 0.187500 +vt 0.000000 0.625000 +vt 0.000000 0.125000 +vt 0.000000 0.562500 +vt 0.000000 0.062500 +vt 0.015625 1.000000 +vt 0.015625 0.000000 +vt 1.000000 0.187500 +vt 1.000000 0.250000 +vt 0.968750 0.250000 +vt 0.968750 0.187500 +vt 1.000000 0.625000 +vt 1.000000 0.687500 +vt 0.968750 0.687500 +vt 0.968750 0.625000 +vt 1.000000 0.125000 +vt 0.968750 0.125000 +vt 1.000000 0.562500 +vt 0.968750 0.562500 +vt 1.000000 0.062500 +vt 0.968750 0.062500 +vt 1.000000 0.500000 +vt 0.968750 0.500000 +vt 1.000000 0.937500 +vt 0.984375 1.000000 +vt 0.968750 0.937500 +vt 0.984375 0.000000 +vt 1.000000 0.437500 +vt 0.968750 0.437500 +vt 1.000000 0.875000 +vt 0.968750 0.875000 +vt 1.000000 0.375000 +vt 0.968750 0.375000 +vt 1.000000 0.812500 +vt 0.968750 0.812500 +vt 1.000000 0.312500 +vt 0.968750 0.312500 +vt 1.000000 0.750000 +vt 0.968750 0.750000 +vt 0.937500 0.437500 +vt 0.937500 0.375000 +vt 0.937500 0.875000 +vt 0.937500 0.812500 +vt 0.937500 0.312500 +vt 0.937500 0.750000 +vt 0.937500 0.250000 +vt 0.937500 0.687500 +vt 0.937500 0.187500 +vt 0.937500 0.625000 +vt 0.937500 0.125000 +vt 0.937500 0.562500 +vt 0.937500 0.062500 +vt 0.937500 0.500000 +vt 0.953125 1.000000 +vt 0.937500 0.937500 +vt 0.953125 0.000000 +vt 0.906250 0.187500 +vt 0.906250 0.125000 +vt 0.906250 0.625000 +vt 0.906250 0.562500 +vt 0.906250 0.062500 +vt 0.906250 0.500000 +vt 0.921875 1.000000 +vt 0.906250 0.937500 +vt 0.921875 0.000000 +vt 0.906250 0.437500 +vt 0.906250 0.875000 +vt 0.906250 0.375000 +vt 0.906250 0.812500 +vt 0.906250 0.312500 +vt 0.906250 0.750000 +vt 0.906250 0.250000 +vt 0.906250 0.687500 +vt 0.875000 0.875000 +vt 0.875000 0.812500 +vt 0.875000 0.375000 +vt 0.875000 0.312500 +vt 0.875000 0.750000 +vt 0.875000 0.250000 +vt 0.875000 0.687500 +vt 0.875000 0.187500 +vt 0.875000 0.625000 +vt 0.875000 0.125000 +vt 0.875000 0.562500 +vt 0.875000 0.062500 +vt 0.875000 0.500000 +vt 0.890625 1.000000 +vt 0.875000 0.937500 +vt 0.890625 0.000000 +vt 0.875000 0.437500 +vt 0.843750 0.625000 +vt 0.843750 0.562500 +vt 0.843750 0.125000 +vt 0.843750 0.062500 +vt 0.843750 0.500000 +vt 0.859375 1.000000 +vt 0.843750 0.937500 +vt 0.859375 0.000000 +vt 0.843750 0.437500 +vt 0.843750 0.875000 +vt 0.843750 0.375000 +vt 0.843750 0.812500 +vt 0.843750 0.312500 +vt 0.843750 0.750000 +vt 0.843750 0.250000 +vt 0.843750 0.687500 +vt 0.843750 0.187500 +vt 0.812500 0.375000 +vt 0.812500 0.312500 +vt 0.812500 0.812500 +vt 0.812500 0.750000 +vt 0.812500 0.250000 +vt 0.812500 0.687500 +vt 0.812500 0.187500 +vt 0.812500 0.625000 +vt 0.812500 0.125000 +vt 0.812500 0.562500 +vt 0.812500 0.062500 +vt 0.812500 0.500000 +vt 0.828125 1.000000 +vt 0.812500 0.937500 +vt 0.828125 0.000000 +vt 0.812500 0.437500 +vt 0.812500 0.875000 +vt 0.781250 0.125000 +vt 0.781250 0.062500 +vt 0.781250 0.562500 +vt 0.781250 0.500000 +vt 0.796875 1.000000 +vt 0.781250 0.937500 +vt 0.796875 0.000000 +vt 0.781250 0.437500 +vt 0.781250 0.875000 +vt 0.781250 0.375000 +vt 0.781250 0.812500 +vt 0.781250 0.312500 +vt 0.781250 0.750000 +vt 0.781250 0.250000 +vt 0.781250 0.687500 +vt 0.781250 0.187500 +vt 0.781250 0.625000 +vt 0.765625 1.000000 +vt 0.765625 0.000000 +vn -0.0000 -0.5528 -0.8333 +vn -0.0000 -0.3805 -0.9248 +vn 0.1804 -0.3805 -0.9070 +vn 0.1626 -0.5528 -0.8173 +vn -0.0000 0.7041 -0.7101 +vn -0.0000 0.8286 -0.5598 +vn 0.1092 0.8286 -0.5490 +vn 0.1385 0.7041 -0.6965 +vn -0.0000 -0.7041 -0.7101 +vn 0.1385 -0.7041 -0.6965 +vn -0.0000 0.5528 -0.8333 +vn 0.1626 0.5528 -0.8173 +vn -0.0000 -0.8286 -0.5598 +vn 0.1092 -0.8286 -0.5490 +vn -0.0000 0.3805 -0.9248 +vn 0.1804 0.3805 -0.9070 +vn -0.0000 -0.9217 -0.3879 +vn 0.0757 -0.9217 -0.3804 +vn -0.0000 0.1939 -0.9810 +vn 0.1914 0.1939 -0.9622 +vn -0.0000 -0.9796 -0.2010 +vn 0.0392 -0.9796 -0.1971 +vn -0.0000 0.0000 -1.0000 +vn 0.1951 0.0000 -0.9808 +vn -0.0000 0.9796 -0.2010 +vn 0.0000 1.0000 0.0000 +vn 0.0392 0.9796 -0.1971 +vn 0.0000 -1.0000 0.0000 +vn -0.0000 -0.1939 -0.9810 +vn 0.1914 -0.1939 -0.9622 +vn -0.0000 0.9217 -0.3879 +vn 0.0757 0.9217 -0.3804 +vn 0.3754 -0.1939 -0.9063 +vn 0.3539 -0.3805 -0.8544 +vn 0.1484 0.9217 -0.3584 +vn 0.2142 0.8286 -0.5172 +vn 0.3189 -0.5528 -0.7699 +vn 0.2718 0.7041 -0.6561 +vn 0.2718 -0.7041 -0.6561 +vn 0.3189 0.5528 -0.7699 +vn 0.2142 -0.8286 -0.5172 +vn 0.3539 0.3805 -0.8544 +vn 0.1484 -0.9217 -0.3584 +vn 0.3754 0.1939 -0.9063 +vn 0.0769 -0.9796 -0.1857 +vn 0.3827 0.0000 -0.9239 +vn 0.0769 0.9796 -0.1857 +vn 0.3110 -0.8286 -0.4654 +vn 0.2155 -0.9217 -0.3225 +vn 0.5138 0.3805 -0.7689 +vn 0.5450 0.1939 -0.8157 +vn 0.1117 -0.9796 -0.1671 +vn 0.5556 0.0000 -0.8315 +vn 0.1117 0.9796 -0.1671 +vn 0.5450 -0.1939 -0.8157 +vn 0.2155 0.9217 -0.3225 +vn 0.5138 -0.3805 -0.7689 +vn 0.3110 0.8286 -0.4654 +vn 0.4630 -0.5528 -0.6929 +vn 0.3945 0.7041 -0.5905 +vn 0.3945 -0.7041 -0.5905 +vn 0.4630 0.5528 -0.6929 +vn 0.2743 0.9217 -0.2743 +vn 0.3958 0.8286 -0.3958 +vn 0.6539 -0.3805 -0.6539 +vn 0.5893 -0.5528 -0.5893 +vn 0.5021 0.7041 -0.5021 +vn 0.5021 -0.7041 -0.5021 +vn 0.5893 0.5528 -0.5893 +vn 0.3958 -0.8286 -0.3958 +vn 0.6539 0.3805 -0.6539 +vn 0.2743 -0.9217 -0.2743 +vn 0.6937 0.1939 -0.6937 +vn 0.1421 -0.9796 -0.1421 +vn 0.7071 0.0000 -0.7071 +vn 0.1421 0.9796 -0.1421 +vn 0.6937 -0.1939 -0.6937 +vn 0.7689 0.3805 -0.5138 +vn 0.8157 0.1939 -0.5450 +vn 0.3225 -0.9217 -0.2155 +vn 0.1671 -0.9796 -0.1117 +vn 0.8315 0.0000 -0.5556 +vn 0.1671 0.9796 -0.1117 +vn 0.8157 -0.1939 -0.5450 +vn 0.3225 0.9217 -0.2155 +vn 0.7689 -0.3805 -0.5138 +vn 0.4654 0.8286 -0.3110 +vn 0.6929 -0.5528 -0.4630 +vn 0.5905 0.7041 -0.3945 +vn 0.5905 -0.7041 -0.3945 +vn 0.6929 0.5528 -0.4630 +vn 0.4654 -0.8286 -0.3110 +vn 0.8544 -0.3805 -0.3539 +vn 0.7699 -0.5528 -0.3189 +vn 0.5172 0.8286 -0.2142 +vn 0.6561 0.7041 -0.2718 +vn 0.6561 -0.7041 -0.2718 +vn 0.7699 0.5528 -0.3189 +vn 0.5172 -0.8286 -0.2142 +vn 0.8544 0.3805 -0.3539 +vn 0.3584 -0.9217 -0.1484 +vn 0.9063 0.1939 -0.3754 +vn 0.1857 -0.9796 -0.0769 +vn 0.9239 0.0000 -0.3827 +vn 0.1857 0.9796 -0.0769 +vn 0.9063 -0.1939 -0.3754 +vn 0.3584 0.9217 -0.1484 +vn 0.3804 -0.9217 -0.0757 +vn 0.1971 -0.9796 -0.0392 +vn 0.9622 0.1939 -0.1914 +vn 0.9808 0.0000 -0.1951 +vn 0.1971 0.9796 -0.0392 +vn 0.9622 -0.1939 -0.1914 +vn 0.3804 0.9217 -0.0757 +vn 0.9070 -0.3805 -0.1804 +vn 0.5490 0.8286 -0.1092 +vn 0.8173 -0.5528 -0.1626 +vn 0.6965 0.7041 -0.1385 +vn 0.6965 -0.7041 -0.1385 +vn 0.8173 0.5528 -0.1626 +vn 0.5490 -0.8286 -0.1092 +vn 0.9070 0.3805 -0.1804 +vn 0.8333 -0.5528 0.0000 +vn 0.7101 -0.7041 0.0000 +vn 0.7101 0.7041 0.0000 +vn 0.8333 0.5528 0.0000 +vn 0.5598 -0.8286 0.0000 +vn 0.9248 0.3805 0.0000 +vn 0.3879 -0.9217 0.0000 +vn 0.9810 0.1939 0.0000 +vn 0.2010 -0.9796 0.0000 +vn 1.0000 0.0000 0.0000 +vn 0.2010 0.9796 0.0000 +vn 0.9810 -0.1939 0.0000 +vn 0.3879 0.9217 -0.0000 +vn 0.9248 -0.3805 0.0000 +vn 0.5598 0.8286 -0.0000 +vn 0.1971 0.9796 0.0392 +vn 0.1971 -0.9796 0.0392 +vn 0.9808 0.0000 0.1951 +vn 0.9622 -0.1939 0.1914 +vn 0.3804 0.9217 0.0757 +vn 0.9070 -0.3805 0.1804 +vn 0.5490 0.8286 0.1092 +vn 0.8173 -0.5528 0.1626 +vn 0.6965 0.7041 0.1385 +vn 0.6965 -0.7041 0.1385 +vn 0.8173 0.5528 0.1626 +vn 0.5490 -0.8286 0.1092 +vn 0.9070 0.3805 0.1804 +vn 0.3804 -0.9217 0.0757 +vn 0.9622 0.1939 0.1914 +vn 0.6561 0.7041 0.2718 +vn 0.7699 0.5528 0.3189 +vn 0.6561 -0.7041 0.2718 +vn 0.5172 -0.8286 0.2142 +vn 0.8544 0.3805 0.3539 +vn 0.3584 -0.9217 0.1484 +vn 0.9063 0.1939 0.3754 +vn 0.1857 -0.9796 0.0769 +vn 0.9239 0.0000 0.3827 +vn 0.1857 0.9796 0.0769 +vn 0.9063 -0.1939 0.3754 +vn 0.3584 0.9217 0.1484 +vn 0.8544 -0.3805 0.3539 +vn 0.5172 0.8286 0.2142 +vn 0.7699 -0.5528 0.3189 +vn 0.8315 0.0000 0.5556 +vn 0.8157 -0.1939 0.5450 +vn 0.1671 0.9796 0.1117 +vn 0.3225 0.9217 0.2155 +vn 0.7689 -0.3805 0.5138 +vn 0.4654 0.8286 0.3110 +vn 0.6929 -0.5528 0.4630 +vn 0.5905 0.7041 0.3945 +vn 0.5905 -0.7041 0.3945 +vn 0.6929 0.5528 0.4630 +vn 0.4654 -0.8286 0.3110 +vn 0.7689 0.3805 0.5138 +vn 0.3225 -0.9217 0.2155 +vn 0.8157 0.1939 0.5450 +vn 0.1671 -0.9796 0.1117 +vn 0.5021 -0.7041 0.5021 +vn 0.3958 -0.8286 0.3958 +vn 0.5893 0.5528 0.5893 +vn 0.6539 0.3805 0.6539 +vn 0.2743 -0.9217 0.2743 +vn 0.6937 0.1939 0.6937 +vn 0.1421 -0.9796 0.1421 +vn 0.7071 0.0000 0.7071 +vn 0.1421 0.9796 0.1421 +vn 0.6937 -0.1939 0.6937 +vn 0.2743 0.9217 0.2743 +vn 0.6539 -0.3805 0.6539 +vn 0.3958 0.8286 0.3958 +vn 0.5893 -0.5528 0.5893 +vn 0.5021 0.7041 0.5021 +vn 0.1117 0.9796 0.1671 +vn 0.2155 0.9217 0.3225 +vn 0.5450 -0.1939 0.8157 +vn 0.5138 -0.3805 0.7689 +vn 0.3110 0.8286 0.4654 +vn 0.4630 -0.5528 0.6929 +vn 0.3945 0.7041 0.5905 +vn 0.3945 -0.7041 0.5905 +vn 0.4630 0.5528 0.6929 +vn 0.3110 -0.8286 0.4654 +vn 0.5138 0.3805 0.7689 +vn 0.2155 -0.9217 0.3225 +vn 0.5450 0.1939 0.8157 +vn 0.1117 -0.9796 0.1671 +vn 0.5556 0.0000 0.8315 +vn 0.3189 0.5528 0.7699 +vn 0.3539 0.3805 0.8544 +vn 0.2142 -0.8286 0.5172 +vn 0.1484 -0.9217 0.3584 +vn 0.3754 0.1939 0.9063 +vn 0.0769 -0.9796 0.1857 +vn 0.3827 0.0000 0.9239 +vn 0.0769 0.9796 0.1857 +vn 0.3754 -0.1939 0.9063 +vn 0.1484 0.9217 0.3584 +vn 0.3539 -0.3805 0.8544 +vn 0.2142 0.8286 0.5172 +vn 0.3189 -0.5528 0.7699 +vn 0.2718 0.7041 0.6561 +vn 0.2718 -0.7041 0.6561 +vn 0.1914 -0.1939 0.9622 +vn 0.1804 -0.3805 0.9070 +vn 0.0757 0.9217 0.3804 +vn 0.1092 0.8286 0.5490 +vn 0.1626 -0.5528 0.8173 +vn 0.1385 0.7041 0.6965 +vn 0.1385 -0.7041 0.6965 +vn 0.1626 0.5528 0.8173 +vn 0.1092 -0.8286 0.5490 +vn 0.1804 0.3805 0.9070 +vn 0.0757 -0.9217 0.3804 +vn 0.1914 0.1939 0.9622 +vn 0.0392 -0.9796 0.1971 +vn 0.1951 0.0000 0.9808 +vn 0.0392 0.9796 0.1971 +vn -0.0000 -0.8286 0.5598 +vn -0.0000 -0.9217 0.3879 +vn -0.0000 0.3805 0.9248 +vn -0.0000 0.1939 0.9810 +vn -0.0000 -0.9796 0.2010 +vn -0.0000 0.0000 1.0000 +vn -0.0000 0.9796 0.2010 +vn -0.0000 -0.1939 0.9810 +vn -0.0000 0.9217 0.3879 +vn -0.0000 -0.3805 0.9248 +vn -0.0000 0.8286 0.5598 +vn -0.0000 -0.5528 0.8333 +vn -0.0000 0.7041 0.7101 +vn -0.0000 -0.7041 0.7101 +vn -0.0000 0.5528 0.8333 +vn -0.1804 -0.3805 0.9070 +vn -0.1626 -0.5528 0.8173 +vn -0.1092 0.8286 0.5490 +vn -0.1385 0.7041 0.6965 +vn -0.1385 -0.7041 0.6965 +vn -0.1626 0.5528 0.8173 +vn -0.1092 -0.8286 0.5490 +vn -0.1804 0.3805 0.9070 +vn -0.0757 -0.9217 0.3804 +vn -0.1914 0.1939 0.9622 +vn -0.0392 -0.9796 0.1971 +vn -0.1951 0.0000 0.9808 +vn -0.0392 0.9796 0.1971 +vn -0.1914 -0.1939 0.9622 +vn -0.0757 0.9217 0.3804 +vn -0.1484 -0.9217 0.3584 +vn -0.0769 -0.9796 0.1857 +vn -0.3754 0.1939 0.9063 +vn -0.3827 -0.0000 0.9239 +vn -0.0769 0.9796 0.1857 +vn -0.3754 -0.1939 0.9063 +vn -0.1484 0.9217 0.3584 +vn -0.3539 -0.3805 0.8544 +vn -0.2142 0.8286 0.5172 +vn -0.3189 -0.5528 0.7699 +vn -0.2718 0.7041 0.6561 +vn -0.2718 -0.7041 0.6561 +vn -0.3189 0.5528 0.7699 +vn -0.2142 -0.8286 0.5172 +vn -0.3539 0.3805 0.8544 +vn -0.3110 0.8286 0.4654 +vn -0.3945 0.7041 0.5905 +vn -0.4630 -0.5528 0.6929 +vn -0.3945 -0.7041 0.5905 +vn -0.4630 0.5528 0.6929 +vn -0.3110 -0.8286 0.4654 +vn -0.5138 0.3805 0.7689 +vn -0.2155 -0.9217 0.3225 +vn -0.5450 0.1939 0.8157 +vn -0.1117 -0.9796 0.1671 +vn -0.5556 -0.0000 0.8315 +vn -0.1117 0.9796 0.1671 +vn -0.5450 -0.1939 0.8157 +vn -0.2155 0.9217 0.3225 +vn -0.5138 -0.3805 0.7689 +vn -0.6937 0.1939 0.6937 +vn -0.7071 0.0000 0.7071 +vn -0.1421 0.9796 0.1421 +vn -0.1421 -0.9796 0.1421 +vn -0.6937 -0.1939 0.6937 +vn -0.2743 0.9217 0.2743 +vn -0.6539 -0.3805 0.6539 +vn -0.3958 0.8286 0.3958 +vn -0.5893 -0.5528 0.5893 +vn -0.5021 0.7041 0.5021 +vn -0.5021 -0.7041 0.5021 +vn -0.5893 0.5528 0.5893 +vn -0.3958 -0.8286 0.3958 +vn -0.6539 0.3805 0.6539 +vn -0.2743 -0.9217 0.2743 +vn -0.6929 -0.5528 0.4630 +vn -0.5905 -0.7041 0.3945 +vn -0.5905 0.7041 0.3945 +vn -0.6929 0.5528 0.4630 +vn -0.4654 -0.8286 0.3110 +vn -0.7689 0.3805 0.5138 +vn -0.3225 -0.9217 0.2155 +vn -0.8157 0.1939 0.5450 +vn -0.1671 -0.9796 0.1117 +vn -0.8315 0.0000 0.5556 +vn -0.1671 0.9796 0.1117 +vn -0.8157 -0.1939 0.5450 +vn -0.3225 0.9217 0.2155 +vn -0.7689 -0.3805 0.5138 +vn -0.4654 0.8286 0.3110 +vn -0.1857 0.9796 0.0769 +vn -0.1857 -0.9796 0.0769 +vn -0.9239 0.0000 0.3827 +vn -0.9063 -0.1939 0.3754 +vn -0.3584 0.9217 0.1484 +vn -0.8544 -0.3805 0.3539 +vn -0.5172 0.8286 0.2142 +vn -0.7699 -0.5528 0.3189 +vn -0.6561 0.7041 0.2718 +vn -0.6561 -0.7041 0.2718 +vn -0.7699 0.5528 0.3189 +vn -0.5172 -0.8286 0.2142 +vn -0.8544 0.3805 0.3539 +vn -0.3584 -0.9217 0.1484 +vn -0.9063 0.1939 0.3754 +vn -0.6965 0.7041 0.1385 +vn -0.8173 0.5528 0.1626 +vn -0.6965 -0.7041 0.1385 +vn -0.5490 -0.8286 0.1092 +vn -0.9070 0.3805 0.1804 +vn -0.3804 -0.9217 0.0757 +vn -0.9622 0.1939 0.1914 +vn -0.1971 -0.9796 0.0392 +vn -0.9808 0.0000 0.1951 +vn -0.1971 0.9796 0.0392 +vn -0.9622 -0.1939 0.1914 +vn -0.3804 0.9217 0.0757 +vn -0.9070 -0.3805 0.1804 +vn -0.5490 0.8286 0.1092 +vn -0.8173 -0.5528 0.1626 +vn -1.0000 0.0000 -0.0000 +vn -0.9810 -0.1939 -0.0000 +vn -0.2010 0.9796 -0.0000 +vn -0.3879 0.9217 -0.0000 +vn -0.9248 -0.3805 -0.0000 +vn -0.5598 0.8286 0.0000 +vn -0.8333 -0.5528 -0.0000 +vn -0.7101 0.7041 -0.0000 +vn -0.7101 -0.7041 -0.0000 +vn -0.8333 0.5528 -0.0000 +vn -0.5598 -0.8286 -0.0000 +vn -0.9248 0.3805 -0.0000 +vn -0.3879 -0.9217 -0.0000 +vn -0.9810 0.1939 -0.0000 +vn -0.2010 -0.9796 -0.0000 +vn -0.6965 -0.7041 -0.1385 +vn -0.5490 -0.8286 -0.1092 +vn -0.8173 0.5528 -0.1626 +vn -0.9070 0.3805 -0.1804 +vn -0.3804 -0.9217 -0.0757 +vn -0.9622 0.1939 -0.1914 +vn -0.1971 -0.9796 -0.0392 +vn -0.9808 0.0000 -0.1951 +vn -0.1971 0.9796 -0.0392 +vn -0.9622 -0.1939 -0.1914 +vn -0.3804 0.9217 -0.0757 +vn -0.9070 -0.3805 -0.1804 +vn -0.5490 0.8286 -0.1092 +vn -0.8173 -0.5528 -0.1626 +vn -0.6965 0.7041 -0.1385 +vn -0.9063 -0.1939 -0.3754 +vn -0.8544 -0.3805 -0.3539 +vn -0.3584 0.9217 -0.1484 +vn -0.5172 0.8286 -0.2142 +vn -0.7699 -0.5528 -0.3189 +vn -0.6561 0.7041 -0.2718 +vn -0.6561 -0.7041 -0.2718 +vn -0.7699 0.5528 -0.3189 +vn -0.5172 -0.8286 -0.2142 +vn -0.8544 0.3805 -0.3539 +vn -0.3584 -0.9217 -0.1484 +vn -0.9063 0.1939 -0.3754 +vn -0.1857 -0.9796 -0.0769 +vn -0.9239 0.0000 -0.3827 +vn -0.1857 0.9796 -0.0769 +vn -0.4654 -0.8286 -0.3110 +vn -0.3225 -0.9217 -0.2155 +vn -0.7689 0.3805 -0.5138 +vn -0.8157 0.1939 -0.5450 +vn -0.1671 -0.9796 -0.1117 +vn -0.8315 0.0000 -0.5556 +vn -0.1671 0.9796 -0.1117 +vn -0.8157 -0.1939 -0.5450 +vn -0.3225 0.9217 -0.2155 +vn -0.7689 -0.3805 -0.5138 +vn -0.4654 0.8286 -0.3110 +vn -0.6929 -0.5528 -0.4630 +vn -0.5905 0.7041 -0.3945 +vn -0.5905 -0.7041 -0.3945 +vn -0.6929 0.5528 -0.4630 +vn -0.2743 0.9217 -0.2743 +vn -0.3958 0.8286 -0.3958 +vn -0.6539 -0.3805 -0.6539 +vn -0.5893 -0.5528 -0.5893 +vn -0.5021 0.7041 -0.5021 +vn -0.5021 -0.7041 -0.5021 +vn -0.5893 0.5528 -0.5893 +vn -0.3958 -0.8286 -0.3958 +vn -0.6539 0.3805 -0.6539 +vn -0.2743 -0.9217 -0.2743 +vn -0.6937 0.1939 -0.6937 +vn -0.1421 -0.9796 -0.1421 +vn -0.7071 0.0000 -0.7071 +vn -0.1421 0.9796 -0.1421 +vn -0.6937 -0.1939 -0.6937 +vn -0.5138 0.3805 -0.7689 +vn -0.5450 0.1939 -0.8157 +vn -0.2155 -0.9217 -0.3225 +vn -0.1117 -0.9796 -0.1671 +vn -0.5556 -0.0000 -0.8315 +vn -0.1117 0.9796 -0.1671 +vn -0.5450 -0.1939 -0.8157 +vn -0.2155 0.9217 -0.3225 +vn -0.5138 -0.3805 -0.7689 +vn -0.3110 0.8286 -0.4654 +vn -0.4630 -0.5528 -0.6929 +vn -0.3945 0.7041 -0.5905 +vn -0.3945 -0.7041 -0.5905 +vn -0.4630 0.5528 -0.6929 +vn -0.3110 -0.8286 -0.4654 +vn -0.3539 -0.3805 -0.8544 +vn -0.3189 -0.5528 -0.7699 +vn -0.2142 0.8286 -0.5172 +vn -0.2718 0.7041 -0.6561 +vn -0.2718 -0.7041 -0.6561 +vn -0.3189 0.5528 -0.7699 +vn -0.2142 -0.8286 -0.5172 +vn -0.3539 0.3805 -0.8544 +vn -0.1484 -0.9217 -0.3584 +vn -0.3754 0.1939 -0.9063 +vn -0.0769 -0.9796 -0.1857 +vn -0.3827 0.0000 -0.9239 +vn -0.0769 0.9796 -0.1857 +vn -0.3754 -0.1939 -0.9063 +vn -0.1484 0.9217 -0.3584 +vn -0.0757 -0.9217 -0.3804 +vn -0.0392 -0.9796 -0.1971 +vn -0.1914 0.1939 -0.9622 +vn -0.1951 0.0000 -0.9808 +vn -0.0392 0.9796 -0.1971 +vn -0.1914 -0.1939 -0.9622 +vn -0.0757 0.9217 -0.3804 +vn -0.1804 -0.3805 -0.9070 +vn -0.1092 0.8286 -0.5490 +vn -0.1626 -0.5528 -0.8173 +vn -0.1385 0.7041 -0.6965 +vn -0.1385 -0.7041 -0.6965 +vn -0.1626 0.5528 -0.8173 +vn -0.1092 -0.8286 -0.5490 +vn -0.1804 0.3805 -0.9070 +usemtl Material +s 1 +f 6/1/1 478/2/2 16/3/3 17/4/4 +f 476/5/5 1/6/6 9/7/7 10/8/8 +f 479/9/9 6/1/1 17/4/4 18/10/10 +f 2/11/11 476/5/5 10/8/8 11/12/12 +f 480/13/13 479/9/9 18/10/10 19/14/14 +f 477/15/15 2/11/11 11/12/12 12/16/16 +f 481/17/17 480/13/13 19/14/14 20/18/18 +f 3/19/19 477/15/15 12/16/16 13/20/20 +f 482/21/21 481/17/17 20/18/18 21/22/22 +f 4/23/23 3/19/19 13/20/20 14/24/24 +f 474/25/25 82/26/26 7/27/27 +f 308/28/28 482/21/21 21/22/22 +f 5/29/29 4/23/23 14/24/24 15/30/30 +f 475/31/31 474/25/25 7/27/27 8/32/32 +f 478/2/2 5/29/29 15/30/30 16/3/3 +f 1/6/6 475/31/31 8/32/32 9/7/7 +f 16/3/3 15/30/30 30/33/33 31/34/34 +f 9/7/7 8/32/32 23/35/35 24/36/36 +f 17/4/4 16/3/3 31/34/34 32/37/37 +f 10/8/8 9/7/7 24/36/36 25/38/38 +f 18/10/10 17/4/4 32/37/37 33/39/39 +f 11/12/12 10/8/8 25/38/38 26/40/40 +f 19/14/14 18/10/10 33/39/39 34/41/41 +f 12/16/16 11/12/12 26/40/40 27/42/42 +f 20/18/18 19/14/14 34/41/41 35/43/43 +f 13/20/20 12/16/16 27/42/42 28/44/44 +f 21/22/22 20/18/18 35/43/43 36/45/45 +f 14/24/24 13/20/20 28/44/44 29/46/46 +f 7/27/27 82/47/26 22/48/47 +f 308/49/28 21/22/22 36/45/45 +f 15/30/30 14/24/24 29/46/46 30/33/33 +f 8/32/32 7/27/27 22/48/47 23/35/35 +f 35/43/43 34/41/41 49/50/48 50/51/49 +f 28/44/44 27/42/42 42/52/50 43/53/51 +f 36/45/45 35/43/43 50/51/49 51/54/52 +f 29/46/46 28/44/44 43/53/51 44/55/53 +f 22/48/47 82/56/26 37/57/54 +f 308/58/28 36/45/45 51/54/52 +f 30/33/33 29/46/46 44/55/53 45/59/55 +f 23/35/35 22/48/47 37/57/54 38/60/56 +f 31/34/34 30/33/33 45/59/55 46/61/57 +f 24/36/36 23/35/35 38/60/56 39/62/58 +f 32/37/37 31/34/34 46/61/57 47/63/59 +f 25/38/38 24/36/36 39/62/58 40/64/60 +f 33/39/39 32/37/37 47/63/59 48/65/61 +f 26/40/40 25/38/38 40/64/60 41/66/62 +f 34/41/41 33/39/39 48/65/61 49/50/48 +f 27/42/42 26/40/40 41/66/62 42/52/50 +f 39/62/58 38/60/56 53/67/63 54/68/64 +f 47/63/59 46/61/57 61/69/65 62/70/66 +f 40/64/60 39/62/58 54/68/64 55/71/67 +f 48/65/61 47/63/59 62/70/66 63/72/68 +f 41/66/62 40/64/60 55/71/67 56/73/69 +f 49/50/48 48/65/61 63/72/68 64/74/70 +f 42/52/50 41/66/62 56/73/69 57/75/71 +f 50/51/49 49/50/48 64/74/70 65/76/72 +f 43/53/51 42/52/50 57/75/71 58/77/73 +f 51/54/52 50/51/49 65/76/72 66/78/74 +f 44/55/53 43/53/51 58/77/73 59/79/75 +f 37/57/54 82/80/26 52/81/76 +f 308/82/28 51/54/52 66/78/74 +f 45/59/55 44/55/53 59/79/75 60/83/77 +f 38/60/56 37/57/54 52/81/76 53/67/63 +f 46/61/57 45/59/55 60/83/77 61/69/65 +f 58/77/73 57/75/71 72/84/78 73/85/79 +f 66/78/74 65/76/72 80/86/80 81/87/81 +f 59/79/75 58/77/73 73/85/79 74/88/82 +f 52/81/76 82/89/26 67/90/83 +f 308/91/28 66/78/74 81/87/81 +f 60/83/77 59/79/75 74/88/82 75/92/84 +f 53/67/63 52/81/76 67/90/83 68/93/85 +f 61/69/65 60/83/77 75/92/84 76/94/86 +f 54/68/64 53/67/63 68/93/85 69/95/87 +f 62/70/66 61/69/65 76/94/86 77/96/88 +f 55/71/67 54/68/64 69/95/87 70/97/89 +f 63/72/68 62/70/66 77/96/88 78/98/90 +f 56/73/69 55/71/67 70/97/89 71/99/91 +f 64/74/70 63/72/68 78/98/90 79/100/92 +f 57/75/71 56/73/69 71/99/91 72/84/78 +f 65/76/72 64/74/70 79/100/92 80/86/80 +f 77/96/88 76/94/86 92/101/93 93/102/94 +f 70/97/89 69/95/87 85/103/95 86/104/96 +f 78/98/90 77/96/88 93/102/94 94/105/97 +f 71/99/91 70/97/89 86/104/96 87/106/98 +f 79/100/92 78/98/90 94/105/97 95/107/99 +f 72/84/78 71/99/91 87/106/98 88/108/100 +f 80/86/80 79/100/92 95/107/99 96/109/101 +f 73/85/79 72/84/78 88/108/100 89/110/102 +f 81/87/81 80/86/80 96/109/101 97/111/103 +f 74/88/82 73/85/79 89/110/102 90/112/104 +f 67/90/83 82/113/26 83/114/105 +f 308/115/28 81/87/81 97/111/103 +f 75/92/84 74/88/82 90/112/104 91/116/106 +f 68/93/85 67/90/83 83/114/105 84/117/107 +f 76/94/86 75/92/84 91/116/106 92/101/93 +f 69/95/87 68/93/85 84/117/107 85/103/95 +f 97/111/103 96/109/101 111/118/108 112/119/109 +f 90/112/104 89/110/102 104/120/110 105/121/111 +f 83/114/105 82/122/26 98/123/112 +f 308/124/28 97/111/103 112/119/109 +f 91/116/106 90/112/104 105/121/111 106/125/113 +f 84/117/107 83/114/105 98/123/112 99/126/114 +f 92/101/93 91/116/106 106/125/113 107/127/115 +f 85/103/95 84/117/107 99/126/114 100/128/116 +f 93/102/94 92/101/93 107/127/115 108/129/117 +f 86/104/96 85/103/95 100/128/116 101/130/118 +f 94/105/97 93/102/94 108/129/117 109/131/119 +f 87/106/98 86/104/96 101/130/118 102/132/120 +f 95/107/99 94/105/97 109/131/119 110/133/121 +f 88/108/100 87/106/98 102/132/120 103/134/122 +f 96/109/101 95/107/99 110/133/121 111/118/108 +f 89/110/102 88/108/100 103/134/122 104/120/110 +f 109/131/119 108/129/117 123/135/123 124/136/124 +f 102/132/120 101/130/118 116/137/125 117/138/126 +f 110/133/121 109/131/119 124/136/124 125/139/127 +f 103/134/122 102/132/120 117/138/126 118/140/128 +f 111/118/108 110/133/121 125/139/127 126/141/129 +f 104/120/110 103/134/122 118/140/128 119/142/130 +f 112/119/109 111/118/108 126/141/129 127/143/131 +f 105/121/111 104/120/110 119/142/130 120/144/132 +f 98/123/112 82/145/26 113/146/133 +f 308/147/28 112/119/109 127/143/131 +f 106/125/113 105/121/111 120/144/132 121/148/134 +f 99/126/114 98/123/112 113/146/133 114/149/135 +f 107/127/115 106/125/113 121/148/134 122/150/136 +f 100/128/116 99/126/114 114/149/135 115/151/137 +f 108/129/117 107/127/115 122/150/136 123/135/123 +f 101/130/118 100/128/116 115/151/137 116/137/125 +f 113/146/133 82/152/26 128/153/138 +f 308/154/28 127/143/131 142/155/139 +f 121/148/134 120/144/132 135/156/140 136/157/141 +f 114/149/135 113/146/133 128/153/138 129/158/142 +f 122/150/136 121/148/134 136/157/141 137/159/143 +f 115/151/137 114/149/135 129/158/142 130/160/144 +f 123/135/123 122/150/136 137/159/143 138/161/145 +f 116/137/125 115/151/137 130/160/144 131/162/146 +f 124/136/124 123/135/123 138/161/145 139/163/147 +f 117/138/126 116/137/125 131/162/146 132/164/148 +f 125/139/127 124/136/124 139/163/147 140/165/149 +f 118/140/128 117/138/126 132/164/148 133/166/150 +f 126/141/129 125/139/127 140/165/149 141/167/151 +f 119/142/130 118/140/128 133/166/150 134/168/152 +f 127/143/131 126/141/129 141/167/151 142/155/139 +f 120/144/132 119/142/130 134/168/152 135/156/140 +f 132/164/148 131/162/146 146/169/153 147/170/154 +f 140/165/149 139/163/147 154/171/155 155/172/156 +f 133/166/150 132/164/148 147/170/154 148/173/157 +f 141/167/151 140/165/149 155/172/156 156/174/158 +f 134/168/152 133/166/150 148/173/157 149/175/159 +f 142/155/139 141/167/151 156/174/158 157/176/160 +f 135/156/140 134/168/152 149/175/159 150/177/161 +f 128/153/138 82/178/26 143/179/162 +f 308/180/28 142/155/139 157/176/160 +f 136/157/141 135/156/140 150/177/161 151/181/163 +f 129/158/142 128/153/138 143/179/162 144/182/164 +f 137/159/143 136/157/141 151/181/163 152/183/165 +f 130/160/144 129/158/142 144/182/164 145/184/166 +f 138/161/145 137/159/143 152/183/165 153/185/167 +f 131/162/146 130/160/144 145/184/166 146/169/153 +f 139/163/147 138/161/145 153/185/167 154/171/155 +f 151/181/163 150/177/161 165/186/168 166/187/169 +f 144/182/164 143/179/162 158/188/170 159/189/171 +f 152/183/165 151/181/163 166/187/169 167/190/172 +f 145/184/166 144/182/164 159/189/171 160/191/173 +f 153/185/167 152/183/165 167/190/172 168/192/174 +f 146/169/153 145/184/166 160/191/173 161/193/175 +f 154/171/155 153/185/167 168/192/174 169/194/176 +f 147/170/154 146/169/153 161/193/175 162/195/177 +f 155/172/156 154/171/155 169/194/176 170/196/178 +f 148/173/157 147/170/154 162/195/177 163/197/179 +f 156/174/158 155/172/156 170/196/178 171/198/180 +f 149/175/159 148/173/157 163/197/179 164/199/181 +f 157/176/160 156/174/158 171/198/180 172/200/182 +f 150/177/161 149/175/159 164/199/181 165/186/168 +f 143/179/162 82/201/26 158/188/170 +f 308/202/28 157/176/160 172/200/182 +f 170/196/178 169/194/176 184/203/183 185/204/184 +f 163/197/179 162/195/177 177/205/185 178/206/186 +f 171/198/180 170/196/178 185/204/184 186/207/187 +f 164/199/181 163/197/179 178/206/186 179/208/188 +f 172/200/182 171/198/180 186/207/187 187/209/189 +f 165/186/168 164/199/181 179/208/188 180/210/190 +f 158/188/170 82/211/26 173/212/191 +f 308/213/28 172/200/182 187/209/189 +f 166/187/169 165/186/168 180/210/190 181/214/192 +f 159/189/171 158/188/170 173/212/191 174/215/193 +f 167/190/172 166/187/169 181/214/192 182/216/194 +f 160/191/173 159/189/171 174/215/193 175/217/195 +f 168/192/174 167/190/172 182/216/194 183/218/196 +f 161/193/175 160/191/173 175/217/195 176/219/197 +f 169/194/176 168/192/174 183/218/196 184/203/183 +f 162/195/177 161/193/175 176/219/197 177/205/185 +f 174/215/193 173/212/191 188/220/198 189/221/199 +f 182/216/194 181/214/192 196/222/200 197/223/201 +f 175/217/195 174/215/193 189/221/199 190/224/202 +f 183/218/196 182/216/194 197/223/201 198/225/203 +f 176/219/197 175/217/195 190/224/202 191/226/204 +f 184/203/183 183/218/196 198/225/203 199/227/205 +f 177/205/185 176/219/197 191/226/204 192/228/206 +f 185/204/184 184/203/183 199/227/205 200/229/207 +f 178/206/186 177/205/185 192/228/206 193/230/208 +f 186/207/187 185/204/184 200/229/207 201/231/209 +f 179/208/188 178/206/186 193/230/208 194/232/210 +f 187/209/189 186/207/187 201/231/209 202/233/211 +f 180/210/190 179/208/188 194/232/210 195/234/212 +f 173/212/191 82/235/26 188/220/198 +f 308/236/28 187/209/189 202/233/211 +f 181/214/192 180/210/190 195/234/212 196/222/200 +f 193/230/208 192/228/206 207/237/213 208/238/214 +f 201/231/209 200/229/207 215/239/215 216/240/216 +f 194/232/210 193/230/208 208/238/214 209/241/217 +f 202/233/211 201/231/209 216/240/216 217/242/218 +f 195/234/212 194/232/210 209/241/217 210/243/219 +f 188/220/198 82/244/26 203/245/220 +f 308/246/28 202/233/211 217/242/218 +f 196/222/200 195/234/212 210/243/219 211/247/221 +f 189/221/199 188/220/198 203/245/220 204/248/222 +f 197/223/201 196/222/200 211/247/221 212/249/223 +f 190/224/202 189/221/199 204/248/222 205/250/224 +f 198/225/203 197/223/201 212/249/223 213/251/225 +f 191/226/204 190/224/202 205/250/224 206/252/226 +f 199/227/205 198/225/203 213/251/225 214/253/227 +f 192/228/206 191/226/204 206/252/226 207/237/213 +f 200/229/207 199/227/205 214/253/227 215/239/215 +f 212/249/223 211/247/221 226/254/228 227/255/229 +f 205/250/224 204/248/222 219/256/230 220/257/231 +f 213/251/225 212/249/223 227/255/229 228/258/232 +f 206/252/226 205/250/224 220/257/231 221/259/233 +f 214/253/227 213/251/225 228/258/232 229/260/234 +f 207/237/213 206/252/226 221/259/233 222/261/235 +f 215/239/215 214/253/227 229/260/234 230/262/236 +f 208/238/214 207/237/213 222/261/235 223/263/237 +f 216/240/216 215/239/215 230/262/236 231/264/238 +f 209/241/217 208/238/214 223/263/237 224/265/239 +f 217/242/218 216/240/216 231/264/238 232/266/240 +f 210/243/219 209/241/217 224/265/239 225/267/241 +f 203/245/220 82/268/26 218/269/242 +f 308/270/28 217/242/218 232/266/240 +f 211/247/221 210/243/219 225/267/241 226/254/228 +f 204/248/222 203/245/220 218/269/242 219/256/230 +f 231/264/238 230/262/236 245/271/243 246/272/244 +f 224/265/239 223/263/237 238/273/245 239/274/246 +f 232/266/240 231/264/238 246/272/244 247/275/247 +f 225/267/241 224/265/239 239/274/246 240/276/248 +f 218/269/242 82/277/26 233/278/249 +f 308/279/28 232/266/240 247/275/247 +f 226/254/228 225/267/241 240/276/248 241/280/250 +f 219/256/230 218/269/242 233/278/249 234/281/251 +f 227/255/229 226/254/228 241/280/250 242/282/252 +f 220/257/231 219/256/230 234/281/251 235/283/253 +f 228/258/232 227/255/229 242/282/252 243/284/254 +f 221/259/233 220/257/231 235/283/253 236/285/255 +f 229/260/234 228/258/232 243/284/254 244/286/256 +f 222/261/235 221/259/233 236/285/255 237/287/257 +f 230/262/236 229/260/234 244/286/256 245/271/243 +f 223/263/237 222/261/235 237/287/257 238/273/245 +f 243/284/254 242/282/252 257/288/258 258/289/259 +f 236/285/255 235/283/253 250/290/260 251/291/261 +f 244/286/256 243/284/254 258/289/259 259/292/262 +f 237/287/257 236/285/255 251/291/261 252/293/263 +f 245/271/243 244/286/256 259/292/262 260/294/264 +f 238/273/245 237/287/257 252/293/263 253/295/265 +f 246/272/244 245/271/243 260/294/264 261/296/266 +f 239/274/246 238/273/245 253/295/265 254/297/267 +f 247/275/247 246/272/244 261/296/266 262/298/268 +f 240/276/248 239/274/246 254/297/267 255/299/269 +f 233/278/249 82/300/26 248/301/270 +f 308/302/28 247/275/247 262/298/268 +f 241/280/250 240/276/248 255/299/269 256/303/271 +f 234/281/251 233/278/249 248/301/270 249/304/272 +f 242/282/252 241/280/250 256/303/271 257/288/258 +f 235/283/253 234/281/251 249/304/272 250/290/260 +f 262/298/268 261/296/266 276/305/273 277/306/274 +f 255/299/269 254/297/267 269/307/275 270/308/276 +f 248/301/270 82/309/26 263/310/277 +f 308/311/28 262/298/268 277/306/274 +f 256/303/271 255/299/269 270/308/276 271/312/278 +f 249/304/272 248/301/270 263/310/277 264/313/279 +f 257/288/258 256/303/271 271/312/278 272/314/280 +f 250/290/260 249/304/272 264/313/279 265/315/281 +f 258/289/259 257/288/258 272/314/280 273/316/282 +f 251/291/261 250/290/260 265/315/281 266/317/283 +f 259/292/262 258/289/259 273/316/282 274/318/284 +f 252/293/263 251/291/261 266/317/283 267/319/285 +f 260/294/264 259/292/262 274/318/284 275/320/286 +f 253/295/265 252/293/263 267/319/285 268/321/287 +f 261/296/266 260/294/264 275/320/286 276/305/273 +f 254/297/267 253/295/265 268/321/287 269/307/275 +f 266/317/283 265/315/281 280/322/288 281/323/289 +f 274/318/284 273/316/282 288/324/290 289/325/291 +f 267/319/285 266/317/283 281/323/289 282/326/292 +f 275/320/286 274/318/284 289/325/291 290/327/293 +f 268/321/287 267/319/285 282/326/292 283/328/294 +f 276/305/273 275/320/286 290/327/293 291/329/295 +f 269/307/275 268/321/287 283/328/294 284/330/296 +f 277/306/274 276/305/273 291/329/295 292/331/297 +f 270/308/276 269/307/275 284/330/296 285/332/298 +f 263/310/277 82/333/26 278/334/299 +f 308/335/28 277/306/274 292/331/297 +f 271/312/278 270/308/276 285/332/298 286/336/300 +f 264/313/279 263/310/277 278/334/299 279/337/301 +f 272/314/280 271/312/278 286/336/300 287/338/302 +f 265/315/281 264/313/279 279/337/301 280/322/288 +f 273/316/282 272/314/280 287/338/302 288/324/290 +f 285/332/298 284/330/296 299/339/303 300/340/304 +f 278/334/299 82/341/26 293/342/305 +f 308/343/28 292/331/297 307/344/306 +f 286/336/300 285/332/298 300/340/304 301/345/307 +f 279/337/301 278/334/299 293/342/305 294/346/308 +f 287/338/302 286/336/300 301/345/307 302/347/309 +f 280/322/288 279/337/301 294/346/308 295/348/310 +f 288/324/290 287/338/302 302/347/309 303/349/311 +f 281/323/289 280/322/288 295/348/310 296/350/312 +f 289/325/291 288/324/290 303/349/311 304/351/313 +f 282/326/292 281/323/289 296/350/312 297/352/314 +f 290/327/293 289/325/291 304/351/313 305/353/315 +f 283/328/294 282/326/292 297/352/314 298/354/316 +f 291/329/295 290/327/293 305/353/315 306/355/317 +f 284/330/296 283/328/294 298/354/316 299/339/303 +f 292/331/297 291/329/295 306/355/317 307/344/306 +f 304/351/313 303/349/311 319/356/318 320/357/319 +f 297/352/314 296/350/312 312/358/320 313/359/321 +f 305/353/315 304/351/313 320/357/319 321/360/322 +f 298/354/316 297/352/314 313/359/321 314/361/323 +f 306/355/317 305/353/315 321/360/322 322/362/324 +f 299/339/303 298/354/316 314/361/323 315/363/325 +f 307/344/306 306/355/317 322/362/324 323/364/326 +f 300/340/304 299/339/303 315/363/325 316/365/327 +f 293/342/305 82/366/26 309/367/328 +f 308/368/28 307/344/306 323/364/326 +f 301/345/307 300/340/304 316/365/327 317/369/329 +f 294/346/308 293/342/305 309/367/328 310/370/330 +f 302/347/309 301/345/307 317/369/329 318/371/331 +f 295/348/310 294/346/308 310/370/330 311/372/332 +f 303/349/311 302/347/309 318/371/331 319/356/318 +f 296/350/312 295/348/310 311/372/332 312/358/320 +f 309/367/328 82/373/26 324/374/333 +f 308/375/28 323/364/326 338/376/334 +f 317/369/329 316/365/327 331/377/335 332/378/336 +f 310/370/330 309/367/328 324/374/333 325/379/337 +f 318/371/331 317/369/329 332/378/336 333/380/338 +f 311/372/332 310/370/330 325/379/337 326/381/339 +f 319/356/318 318/371/331 333/380/338 334/382/340 +f 312/358/320 311/372/332 326/381/339 327/383/341 +f 320/357/319 319/356/318 334/382/340 335/384/342 +f 313/359/321 312/358/320 327/383/341 328/385/343 +f 321/360/322 320/357/319 335/384/342 336/386/344 +f 314/361/323 313/359/321 328/385/343 329/387/345 +f 322/362/324 321/360/322 336/386/344 337/388/346 +f 315/363/325 314/361/323 329/387/345 330/389/347 +f 323/364/326 322/362/324 337/388/346 338/376/334 +f 316/365/327 315/363/325 330/389/347 331/377/335 +f 328/385/343 327/383/341 342/390/348 343/391/349 +f 336/386/344 335/384/342 350/392/350 351/393/351 +f 329/387/345 328/385/343 343/391/349 344/394/352 +f 337/388/346 336/386/344 351/393/351 352/395/353 +f 330/389/347 329/387/345 344/394/352 345/396/354 +f 338/376/334 337/388/346 352/395/353 353/397/355 +f 331/377/335 330/389/347 345/396/354 346/398/356 +f 324/374/333 82/399/26 339/400/357 +f 308/401/28 338/376/334 353/397/355 +f 332/378/336 331/377/335 346/398/356 347/402/358 +f 325/379/337 324/374/333 339/400/357 340/403/359 +f 333/380/338 332/378/336 347/402/358 348/404/360 +f 326/381/339 325/379/337 340/403/359 341/405/361 +f 334/382/340 333/380/338 348/404/360 349/406/362 +f 327/383/341 326/381/339 341/405/361 342/390/348 +f 335/384/342 334/382/340 349/406/362 350/392/350 +f 347/402/358 346/398/356 361/407/363 362/408/364 +f 340/403/359 339/400/357 354/409/365 355/410/366 +f 348/404/360 347/402/358 362/408/364 363/411/367 +f 341/405/361 340/403/359 355/410/366 356/412/368 +f 349/406/362 348/404/360 363/411/367 364/413/369 +f 342/390/348 341/405/361 356/412/368 357/414/370 +f 350/392/350 349/406/362 364/413/369 365/415/371 +f 343/391/349 342/390/348 357/414/370 358/416/372 +f 351/393/351 350/392/350 365/415/371 366/417/373 +f 344/394/352 343/391/349 358/416/372 359/418/374 +f 352/395/353 351/393/351 366/417/373 367/419/375 +f 345/396/354 344/394/352 359/418/374 360/420/376 +f 353/397/355 352/395/353 367/419/375 368/421/377 +f 346/398/356 345/396/354 360/420/376 361/407/363 +f 339/400/357 82/422/26 354/409/365 +f 308/423/28 353/397/355 368/421/377 +f 366/424/373 365/425/371 380/426/378 381/427/379 +f 359/428/374 358/429/372 373/430/380 374/431/381 +f 367/432/375 366/424/373 381/427/379 382/433/382 +f 360/434/376 359/428/374 374/431/381 375/435/383 +f 368/436/377 367/432/375 382/433/382 383/437/384 +f 361/438/363 360/434/376 375/435/383 376/439/385 +f 354/440/365 82/441/26 369/442/386 +f 308/443/28 368/436/377 383/437/384 +f 362/444/364 361/438/363 376/439/385 377/445/387 +f 355/446/366 354/440/365 369/442/386 370/447/388 +f 363/448/367 362/444/364 377/445/387 378/449/389 +f 356/450/368 355/446/366 370/447/388 371/451/390 +f 364/452/369 363/448/367 378/449/389 379/453/391 +f 357/454/370 356/450/368 371/451/390 372/455/392 +f 365/425/371 364/452/369 379/453/391 380/426/378 +f 358/429/372 357/454/370 372/455/392 373/430/380 +f 378/449/389 377/445/387 392/456/393 393/457/394 +f 371/451/390 370/447/388 385/458/395 386/459/396 +f 379/453/391 378/449/389 393/457/394 394/460/397 +f 372/455/392 371/451/390 386/459/396 387/461/398 +f 380/426/378 379/453/391 394/460/397 395/462/399 +f 373/430/380 372/455/392 387/461/398 388/463/400 +f 381/427/379 380/426/378 395/462/399 396/464/401 +f 374/431/381 373/430/380 388/463/400 389/465/402 +f 382/433/382 381/427/379 396/464/401 397/466/403 +f 375/435/383 374/431/381 389/465/402 390/467/404 +f 383/437/384 382/433/382 397/466/403 398/468/405 +f 376/439/385 375/435/383 390/467/404 391/469/406 +f 369/442/386 82/470/26 384/471/407 +f 308/472/28 383/437/384 398/468/405 +f 377/445/387 376/439/385 391/469/406 392/456/393 +f 370/447/388 369/442/386 384/471/407 385/458/395 +f 397/466/403 396/464/401 411/473/408 412/474/409 +f 390/467/404 389/465/402 404/475/410 405/476/411 +f 398/468/405 397/466/403 412/474/409 413/477/412 +f 391/469/406 390/467/404 405/476/411 406/478/413 +f 384/471/407 82/479/26 399/480/414 +f 308/481/28 398/468/405 413/477/412 +f 392/456/393 391/469/406 406/478/413 407/482/415 +f 385/458/395 384/471/407 399/480/414 400/483/416 +f 393/457/394 392/456/393 407/482/415 408/484/417 +f 386/459/396 385/458/395 400/483/416 401/485/418 +f 394/460/397 393/457/394 408/484/417 409/486/419 +f 387/461/398 386/459/396 401/485/418 402/487/420 +f 395/462/399 394/460/397 409/486/419 410/488/421 +f 388/463/400 387/461/398 402/487/420 403/489/422 +f 396/464/401 395/462/399 410/488/421 411/473/408 +f 389/465/402 388/463/400 403/489/422 404/475/410 +f 401/485/418 400/483/416 415/490/423 416/491/424 +f 409/486/419 408/484/417 423/492/425 424/493/426 +f 402/487/420 401/485/418 416/491/424 417/494/427 +f 410/488/421 409/486/419 424/493/426 425/495/428 +f 403/489/422 402/487/420 417/494/427 418/496/429 +f 411/473/408 410/488/421 425/495/428 426/497/430 +f 404/475/410 403/489/422 418/496/429 419/498/431 +f 412/474/409 411/473/408 426/497/430 427/499/432 +f 405/476/411 404/475/410 419/498/431 420/500/433 +f 413/477/412 412/474/409 427/499/432 428/501/434 +f 406/478/413 405/476/411 420/500/433 421/502/435 +f 399/480/414 82/503/26 414/504/436 +f 308/505/28 413/477/412 428/501/434 +f 407/482/415 406/478/413 421/502/435 422/506/437 +f 400/483/416 399/480/414 414/504/436 415/490/423 +f 408/484/417 407/482/415 422/506/437 423/492/425 +f 420/500/433 419/498/431 434/507/438 435/508/439 +f 428/501/434 427/499/432 442/509/440 443/510/441 +f 421/502/435 420/500/433 435/508/439 436/511/442 +f 414/504/436 82/512/26 429/513/443 +f 308/514/28 428/501/434 443/510/441 +f 422/506/437 421/502/435 436/511/442 437/515/444 +f 415/490/423 414/504/436 429/513/443 430/516/445 +f 423/492/425 422/506/437 437/515/444 438/517/446 +f 416/491/424 415/490/423 430/516/445 431/518/447 +f 424/493/426 423/492/425 438/517/446 439/519/448 +f 417/494/427 416/491/424 431/518/447 432/520/449 +f 425/495/428 424/493/426 439/519/448 440/521/450 +f 418/496/429 417/494/427 432/520/449 433/522/451 +f 426/497/430 425/495/428 440/521/450 441/523/452 +f 419/498/431 418/496/429 433/522/451 434/507/438 +f 427/499/432 426/497/430 441/523/452 442/509/440 +f 439/519/448 438/517/446 453/524/453 454/525/454 +f 432/520/449 431/518/447 446/526/455 447/527/456 +f 440/521/450 439/519/448 454/525/454 455/528/457 +f 433/522/451 432/520/449 447/527/456 448/529/458 +f 441/523/452 440/521/450 455/528/457 456/530/459 +f 434/507/438 433/522/451 448/529/458 449/531/460 +f 442/509/440 441/523/452 456/530/459 457/532/461 +f 435/508/439 434/507/438 449/531/460 450/533/462 +f 443/510/441 442/509/440 457/532/461 458/534/463 +f 436/511/442 435/508/439 450/533/462 451/535/464 +f 429/513/443 82/536/26 444/537/465 +f 308/538/28 443/510/441 458/534/463 +f 437/515/444 436/511/442 451/535/464 452/539/466 +f 430/516/445 429/513/443 444/537/465 445/540/467 +f 438/517/446 437/515/444 452/539/466 453/524/453 +f 431/518/447 430/516/445 445/540/467 446/526/455 +f 458/534/463 457/532/461 472/541/468 473/542/469 +f 451/535/464 450/533/462 465/543/470 466/544/471 +f 444/537/465 82/545/26 459/546/472 +f 308/547/28 458/534/463 473/542/469 +f 452/539/466 451/535/464 466/544/471 467/548/473 +f 445/540/467 444/537/465 459/546/472 460/549/474 +f 453/524/453 452/539/466 467/548/473 468/550/475 +f 446/526/455 445/540/467 460/549/474 461/551/476 +f 454/525/454 453/524/453 468/550/475 469/552/477 +f 447/527/456 446/526/455 461/551/476 462/553/478 +f 455/528/457 454/525/454 469/552/477 470/554/479 +f 448/529/458 447/527/456 462/553/478 463/555/480 +f 456/530/459 455/528/457 470/554/479 471/556/481 +f 449/531/460 448/529/458 463/555/480 464/557/482 +f 457/532/461 456/530/459 471/556/481 472/541/468 +f 450/533/462 449/531/460 464/557/482 465/543/470 +f 462/553/478 461/551/476 1/6/6 476/5/5 +f 470/554/479 469/552/477 6/1/1 479/9/9 +f 463/555/480 462/553/478 476/5/5 2/11/11 +f 471/556/481 470/554/479 479/9/9 480/13/13 +f 464/557/482 463/555/480 2/11/11 477/15/15 +f 472/541/468 471/556/481 480/13/13 481/17/17 +f 465/543/470 464/557/482 477/15/15 3/19/19 +f 473/542/469 472/541/468 481/17/17 482/21/21 +f 466/544/471 465/543/470 3/19/19 4/23/23 +f 459/546/472 82/558/26 474/25/25 +f 308/559/28 473/542/469 482/21/21 +f 467/548/473 466/544/471 4/23/23 5/29/29 +f 460/549/474 459/546/472 474/25/25 475/31/31 +f 468/550/475 467/548/473 5/29/29 478/2/2 +f 461/551/476 460/549/474 475/31/31 1/6/6 +f 469/552/477 468/550/475 478/2/2 6/1/1 diff --git a/res/pure-sky.hdr b/res/pure-sky.hdr new file mode 100644 index 0000000..c5c0112 Binary files /dev/null and b/res/pure-sky.hdr differ diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..b5bfe79 --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,190 @@ +use cgmath::*; +use std::f32::consts::FRAC_PI_2; +use std::time::Duration; +use winit::dpi::PhysicalPosition; +use winit::event::*; +use winit::keyboard::KeyCode; + +const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001; + +#[derive(Debug)] +pub struct Camera { + pub position: Point3, + yaw: Rad, + pitch: Rad, +} + +impl Camera { + pub fn new>, Y: Into>, P: Into>>( + position: V, + yaw: Y, + pitch: P, + ) -> Self { + Self { + position: position.into(), + yaw: yaw.into(), + pitch: pitch.into(), + } + } + + pub fn calc_matrix(&self) -> Matrix4 { + let (sin_pitch, cos_pitch) = self.pitch.0.sin_cos(); + let (sin_yaw, cos_yaw) = self.yaw.0.sin_cos(); + + Matrix4::look_to_rh( + self.position, + Vector3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(), + Vector3::unit_y(), + ) + } +} + +pub struct Projection { + aspect: f32, + fovy: Rad, + znear: f32, + zfar: f32, +} + +impl Projection { + pub fn new>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self { + Self { + aspect: width as f32 / height as f32, + fovy: fovy.into(), + znear, + zfar, + } + } + + pub fn resize(&mut self, width: u32, height: u32) { + self.aspect = width as f32 / height as f32; + } + + pub fn calc_matrix(&self) -> Matrix4 { + // UDPATE + perspective(self.fovy, self.aspect, self.znear, self.zfar) + } +} + +#[derive(Debug)] +pub struct CameraController { + amount_left: f32, + amount_right: f32, + amount_forward: f32, + amount_backward: f32, + amount_up: f32, + amount_down: f32, + rotate_horizontal: f32, + rotate_vertical: f32, + scroll: f32, + speed: f32, + sensitivity: f32, +} + +impl CameraController { + pub fn new(speed: f32, sensitivity: f32) -> Self { + Self { + amount_left: 0.0, + amount_right: 0.0, + amount_forward: 0.0, + amount_backward: 0.0, + amount_up: 0.0, + amount_down: 0.0, + rotate_horizontal: 0.0, + rotate_vertical: 0.0, + scroll: 0.0, + speed, + sensitivity, + } + } + + pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool { + let amount = if state == ElementState::Pressed { + 1.0 + } else { + 0.0 + }; + match key { + KeyCode::KeyW | KeyCode::ArrowUp => { + self.amount_forward = amount; + true + } + KeyCode::KeyS | KeyCode::ArrowDown => { + self.amount_backward = amount; + true + } + KeyCode::KeyA | KeyCode::ArrowLeft => { + self.amount_left = amount; + true + } + KeyCode::KeyD | KeyCode::ArrowRight => { + self.amount_right = amount; + true + } + KeyCode::Space => { + self.amount_up = amount; + true + } + KeyCode::ShiftLeft => { + self.amount_down = amount; + true + } + _ => false, + } + } + + pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) { + self.rotate_horizontal = mouse_dx as f32; + self.rotate_vertical = mouse_dy as f32; + } + + pub fn process_scroll(&mut self, delta: &MouseScrollDelta) { + self.scroll = match delta { + // I'm assuming a line is about 100 pixels + MouseScrollDelta::LineDelta(_, scroll) => -scroll * 0.5, + MouseScrollDelta::PixelDelta(PhysicalPosition { y: scroll, .. }) => -*scroll as f32, + }; + } + + pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) { + let dt = dt.as_secs_f32(); + + // Move forward/backward and left/right + let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos(); + let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize(); + let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize(); + camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt; + camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt; + + // Move in/out (aka. "zoom") + // Note: this isn't an actual zoom. The camera's position + // changes when zooming. I've added this to make it easier + // to get closer to an object you want to focus on. + let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos(); + let scrollward = + Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize(); + camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt; + self.scroll = 0.0; + + // Move up/down. Since we don't use roll, we can just + // modify the y coordinate directly. + camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt; + + // Rotate + camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt; + camera.pitch += Rad(-self.rotate_vertical) * self.sensitivity * dt; + + // If process_mouse isn't called every frame, these values + // will not get set to zero, and the camera will rotate + // when moving in a non cardinal direction. + self.rotate_horizontal = 0.0; + self.rotate_vertical = 0.0; + + // Keep the camera's angle from going too high/low. + if camera.pitch < -Rad(SAFE_FRAC_PI_2) { + camera.pitch = -Rad(SAFE_FRAC_PI_2); + } else if camera.pitch > Rad(SAFE_FRAC_PI_2) { + camera.pitch = Rad(SAFE_FRAC_PI_2); + } + } +} \ No newline at end of file diff --git a/src/debug.rs b/src/debug.rs new file mode 100644 index 0000000..ddba327 --- /dev/null +++ b/src/debug.rs @@ -0,0 +1,99 @@ +use std::mem::size_of; + +use wgpu::util::{BufferInitDescriptor, DeviceExt}; + +use crate::create_render_pipeline; + +#[repr(C)] +#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +pub struct PositionColor { + position: [f32; 3], + color: [f32; 3], +} + +const AXIS_COLORS: &'static [PositionColor] = &[ + // X + PositionColor { + position: [0.0, 0.0, 0.0], + color: [0.5, 0.0, 0.0], + }, + PositionColor { + position: [1.0, 0.0, 0.0], + color: [1.0, 0.0, 0.0], + }, + // Y + PositionColor { + position: [0.0, 0.0, 0.0], + color: [0.0, 0.5, 0.0], + }, + PositionColor { + position: [0.0, 1.0, 0.0], + color: [0.0, 1.0, 0.0], + }, + // Z + PositionColor { + position: [0.0, 0.0, 0.0], + color: [0.0, 0.0, 0.5], + }, + PositionColor { + position: [0.0, 0.0, 1.0], + color: [0.0, 0.0, 1.0], + }, +]; + +const POSITION_COLOR_LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { + array_stride: size_of::() as _, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array![ + 0 => Float32x3, + 1 => Float32x3, + ], +}; + +pub struct Debug { + color_lines: wgpu::RenderPipeline, + axis: wgpu::Buffer, +} + +impl Debug { + pub fn new( + device: &wgpu::Device, + camera_layout: &wgpu::BindGroupLayout, + color_format: wgpu::TextureFormat, + ) -> Self { + let axis = device.create_buffer_init(&BufferInitDescriptor { + label: Some("Debug::axis"), + contents: bytemuck::cast_slice(AXIS_COLORS), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX, + }); + + let shader = wgpu::include_wgsl!("debug.wgsl"); + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[camera_layout], + push_constant_ranges: &[], + }); + let color_lines = create_render_pipeline( + device, + &layout, + color_format, + None, + &[POSITION_COLOR_LAYOUT], + wgpu::PrimitiveTopology::LineList, + shader, + ); + + Self { color_lines, axis } + } + + pub fn draw_axis<'a: 'b, 'b>( + &'a self, + pass: &'b mut wgpu::RenderPass<'a>, + camera: &'a wgpu::BindGroup, + ) { + pass.set_pipeline(&self.color_lines); + pass.set_bind_group(0, camera, &[]); + pass.set_vertex_buffer(0, self.axis.slice(..)); + pass.draw(0..AXIS_COLORS.len() as u32, 0..1); + } +} \ No newline at end of file diff --git a/src/equirectangular.wgsl b/src/equirectangular.wgsl new file mode 100644 index 0000000..846ae59 --- /dev/null +++ b/src/equirectangular.wgsl @@ -0,0 +1,87 @@ +const PI: f32 = 3.1415926535897932384626433832795; + +struct Face { + forward: vec3, + up: vec3, + right: vec3, +} + +@group(0) +@binding(0) +var src: texture_2d; + +@group(0) +@binding(1) +var dst: texture_storage_2d_array; + + +@compute +@workgroup_size(16, 16, 1) +fn compute_equirect_to_cubemap( + @builtin(global_invocation_id) + gid: vec3, +) { + // If texture size is not divisible by 32 we + // need to make sure we don't try to write to + // pixels that don't exist. + if gid.x >= u32(textureDimensions(dst).x) { + return; + } + + var FACES: array = array( + // FACES +X + Face( + vec3(1.0, 0.0, 0.0), // forward + vec3(0.0, 1.0, 0.0), // up + vec3(0.0, 0.0, -1.0), // right + ), + // FACES -X + Face ( + vec3(-1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0), + ), + // FACES +Y + Face ( + vec3(0.0, -1.0, 0.0), + vec3(0.0, 0.0, 1.0), + vec3(1.0, 0.0, 0.0), + ), + // FACES -Y + Face ( + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, -1.0), + vec3(1.0, 0.0, 0.0), + ), + // FACES +Z + Face ( + vec3(0.0, 0.0, 1.0), + vec3(0.0, 1.0, 0.0), + vec3(1.0, 0.0, 0.0), + ), + // FACES -Z + Face ( + vec3(0.0, 0.0, -1.0), + vec3(0.0, 1.0, 0.0), + vec3(-1.0, 0.0, 0.0), + ), + ); + + // Get texture coords relative to cubemap face + let dst_dimensions = vec2(textureDimensions(dst)); + let cube_uv = vec2(gid.xy) / dst_dimensions * 2.0 - 1.0; + + // Get spherical coordinate from cube_uv + let face = FACES[gid.z]; + let spherical = normalize(face.forward + face.right * cube_uv.x + face.up * cube_uv.y); + + // Get coordinate on the equirectangular texture + let inv_atan = vec2(0.1591, 0.3183); + let eq_uv = vec2(atan2(spherical.z, spherical.x), asin(spherical.y)) * inv_atan + 0.5; + let eq_pixel = vec2(eq_uv * vec2(textureDimensions(src))); + + // We use textureLoad() as textureSample() is not allowed in compute shaders + var sample = textureLoad(src, eq_pixel, 0); + + textureStore(dst, gid.xy, gid.z, sample); +} \ No newline at end of file diff --git a/src/hdr.rs b/src/hdr.rs new file mode 100644 index 0000000..e74157a --- /dev/null +++ b/src/hdr.rs @@ -0,0 +1,160 @@ +use wgpu::Operations; + +use crate::{create_render_pipeline, texture}; + +/// Owns the render texture and controls tonemapping +pub struct HdrPipeline { + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, + texture: texture::Texture, + width: u32, + height: u32, + format: wgpu::TextureFormat, + layout: wgpu::BindGroupLayout, +} + +impl HdrPipeline { + pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self { + let width = config.width; + let height = config.height; + + // We could use `Rgba32Float`, but that requires some extra + // features to be enabled. + let format = wgpu::TextureFormat::Rgba16Float; + + let texture = texture::Texture::create_2d_texture( + device, + width, + height, + format, + wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, + wgpu::FilterMode::Nearest, + Some("Hdr::texture"), + ); + + let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("Hdr::layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + // The Rgba16Float format cannot be filtered + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, + ], + }); + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("Hdr::bind_group"), + layout: &layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&texture.sampler), + }, + ], + }); + + let shader = wgpu::include_wgsl!("hdr.wgsl"); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&layout], + push_constant_ranges: &[], + }); + + let pipeline = create_render_pipeline( + device, + &pipeline_layout, + config.format.add_srgb_suffix(), + None, + &[], + wgpu::PrimitiveTopology::TriangleList, + shader, + ); + + Self { + pipeline, + bind_group, + layout, + texture, + width, + height, + format, + } + } + + /// Resize the HDR texture + pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) { + self.texture = texture::Texture::create_2d_texture( + device, + width, + height, + wgpu::TextureFormat::Rgba16Float, + wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, + wgpu::FilterMode::Nearest, + Some("Hdr::texture"), + ); + self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("Hdr::bind_group"), + layout: &self.layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&self.texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&self.texture.sampler), + }, + ], + }); + self.width = width; + self.height = height; + } + + /// Exposes the HDR texture + pub fn view(&self) -> &wgpu::TextureView { + &self.texture.view + } + + /// The format of the HDR texture + pub fn format(&self) -> wgpu::TextureFormat { + self.format + } + + /// This renders the internal HDR texture to the [TextureView] + /// supplied as parameter. + pub fn process(&self, encoder: &mut wgpu::CommandEncoder, output: &wgpu::TextureView) { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Hdr::process"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &output, + resolve_target: None, + ops: Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + pass.set_pipeline(&self.pipeline); + pass.set_bind_group(0, &self.bind_group, &[]); + pass.draw(0..3, 0..1); + } +} \ No newline at end of file diff --git a/src/hdr.wgsl b/src/hdr.wgsl new file mode 100644 index 0000000..237e472 --- /dev/null +++ b/src/hdr.wgsl @@ -0,0 +1,55 @@ +// Maps HDR values to linear values +// Based on http://www.oscars.org/science-technology/sci-tech-projects/aces +fn aces_tone_map(hdr: vec3) -> vec3 { + let m1 = mat3x3( + 0.59719, 0.07600, 0.02840, + 0.35458, 0.90834, 0.13383, + 0.04823, 0.01566, 0.83777, + ); + let m2 = mat3x3( + 1.60475, -0.10208, -0.00327, + -0.53108, 1.10813, -0.07276, + -0.07367, -0.00605, 1.07602, + ); + let v = m1 * hdr; + let a = v * (v + 0.0245786) - 0.000090537; + let b = v * (0.983729 * v + 0.4329510) + 0.238081; + return clamp(m2 * (a / b), vec3(0.0), vec3(1.0)); +} + +struct VertexOutput { + @location(0) uv: vec2, + @builtin(position) clip_position: vec4, +}; + +@vertex +fn vs_main( + @builtin(vertex_index) vi: u32, +) -> VertexOutput { + var out: VertexOutput; + // Generate a triangle that covers the whole screen + out.uv = vec2( + f32((vi << 1u) & 2u), + f32(vi & 2u), + ); + out.clip_position = vec4(out.uv * 2.0 - 1.0, 0.0, 1.0); + // We need to invert the y coordinate so the image + // is not upside down + out.uv.y = 1.0 - out.uv.y; + return out; +} + +@group(0) +@binding(0) +var hdr_image: texture_2d; + +@group(0) +@binding(1) +var hdr_sampler: sampler; + +@fragment +fn fs_main(vs: VertexOutput) -> @location(0) vec4 { + let hdr = textureSample(hdr_image, hdr_sampler, vs.uv); + let sdr = aces_tone_map(hdr.rgb); + return vec4(sdr, hdr.a); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index f3c9d18..a87a62c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -use std::iter; +use std::{f32::consts::PI, iter}; use cgmath::prelude::*; use wgpu::util::DeviceExt; @@ -9,170 +9,50 @@ use winit::{ window::Window, }; +mod camera; +mod hdr; mod model; mod resources; mod texture; -use model::{DrawModel, Vertex}; +#[cfg(feature = "debug")] +mod debug; -#[rustfmt::skip] -pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::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, -); +use model::{DrawLight, DrawModel, Vertex}; const NUM_INSTANCES_PER_ROW: u32 = 10; -#[repr(C)] -#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] -struct LightUniform { - position: [f32; 3], - // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here - _padding: u32, - color: [f32; 3], - // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here - _padding2: u32, -} - -#[derive(Debug)] -struct Camera { - eye: cgmath::Point3, - target: cgmath::Point3, - up: cgmath::Vector3, - aspect: f32, - fovy: f32, - znear: f32, - zfar: f32, -} - -impl Camera { - fn build_view_projection_matrix(&self) -> cgmath::Matrix4 { - let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up); - let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); - proj * view - } -} - #[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct CameraUniform { view_position: [f32; 4], + view: [[f32; 4]; 4], // NEW! view_proj: [[f32; 4]; 4], + inv_proj: [[f32; 4]; 4], // NEW! + inv_view: [[f32; 4]; 4], // NEW! } impl CameraUniform { fn new() -> Self { Self { view_position: [0.0; 4], + view: cgmath::Matrix4::identity().into(), // NEW! view_proj: cgmath::Matrix4::identity().into(), + inv_proj: cgmath::Matrix4::identity().into(), // NEW! + inv_view: cgmath::Matrix4::identity().into(), // NEW! } } - fn update_view_proj(&mut self, camera: &Camera) { - // We're using Vector4 because of the uniforms 16 byte spacing requirement - self.view_position = camera.eye.to_homogeneous().into(); - self.view_proj = (OPENGL_TO_WGPU_MATRIX * camera.build_view_projection_matrix()).into(); - } -} - -struct CameraController { - speed: f32, - is_up_pressed: bool, - is_down_pressed: bool, - is_forward_pressed: bool, - is_backward_pressed: bool, - is_left_pressed: bool, - is_right_pressed: bool, -} - -impl CameraController { - fn new(speed: f32) -> Self { - Self { - speed, - is_up_pressed: false, - is_down_pressed: false, - is_forward_pressed: false, - is_backward_pressed: false, - is_left_pressed: false, - is_right_pressed: false, - } - } - - fn process_events(&mut self, event: &WindowEvent) -> bool { - match event { - WindowEvent::KeyboardInput { - event: - KeyEvent { - state, - physical_key: PhysicalKey::Code(keycode), - .. - }, - .. - } => { - let is_pressed = *state == ElementState::Pressed; - match keycode { - KeyCode::Space => { - self.is_up_pressed = is_pressed; - true - } - KeyCode::ShiftLeft => { - self.is_down_pressed = is_pressed; - true - } - KeyCode::KeyW | KeyCode::ArrowUp => { - self.is_forward_pressed = is_pressed; - true - } - KeyCode::KeyA | KeyCode::ArrowLeft => { - self.is_left_pressed = is_pressed; - true - } - KeyCode::KeyS | KeyCode::ArrowDown => { - self.is_backward_pressed = is_pressed; - true - } - KeyCode::KeyD | KeyCode::ArrowRight => { - self.is_right_pressed = is_pressed; - true - } - _ => false, - } - } - _ => false, - } - } - - fn update_camera(&self, camera: &mut Camera) { - let forward = camera.target - camera.eye; - let forward_norm = forward.normalize(); - let forward_mag = forward.magnitude(); - - // Prevents glitching when camera gets too close to the - // center of the scene. - if self.is_forward_pressed && forward_mag > self.speed { - camera.eye += forward_norm * self.speed; - } - if self.is_backward_pressed { - camera.eye -= forward_norm * self.speed; - } - - let right = forward_norm.cross(camera.up); - - // Redo radius calc in case the up/ down is pressed. - let forward = camera.target - camera.eye; - let forward_mag = forward.magnitude(); - - if self.is_right_pressed { - // Rescale the distance between the target and eye so - // that it doesn't change. The eye therefore still - // lies on the circle made by the target and eye. - camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag; - } - if self.is_left_pressed { - camera.eye = camera.target - (forward - right * self.speed).normalize() * forward_mag; - } + // UPDATED! + fn update_view_proj(&mut self, camera: &camera::Camera, projection: &camera::Projection) { + self.view_position = camera.position.to_homogeneous().into(); + let proj = projection.calc_matrix(); + let view = camera.calc_matrix(); + let view_proj = proj * view; + self.view = view.into(); + self.view_proj = view_proj.into(); + self.inv_proj = proj.invert().unwrap().into(); + self.inv_view = view.transpose().into(); } } @@ -183,10 +63,10 @@ struct Instance { impl Instance { fn to_raw(&self) -> InstanceRaw { - let model = - cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation); InstanceRaw { - model: model.into(), + model: (cgmath::Matrix4::from_translation(self.position) + * cgmath::Matrix4::from(self.rotation)) + .into(), normal: cgmath::Matrix3::from(self.rotation).into(), } } @@ -212,13 +92,13 @@ impl model::Vertex for InstanceRaw { attributes: &[ wgpu::VertexAttribute { offset: 0, - // While our vertex shader only uses locations 0, and 1 now, in later tutorials, we'll - // be using 2, 3, and 4 for Vertex. We'll start at slot 5 to not conflict with them later + // While our vertex shader only uses locations 0, and 1 now, in later tutorials we'll + // be using 2, 3, and 4, for Vertex. We'll start at slot 5 not conflict with them later shader_location: 5, format: wgpu::VertexFormat::Float32x4, }, // A mat4 takes up 4 vertex slots as it is technically 4 vec4s. We need to define a slot - // for each vec4. We don't have to do this in code, though. + // for each vec4. We don't have to do this in code though. wgpu::VertexAttribute { offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress, shader_location: 6, @@ -254,16 +134,27 @@ impl model::Vertex for InstanceRaw { } } +#[repr(C)] +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct LightUniform { + position: [f32; 3], + // Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here + _padding: u32, + color: [f32; 3], + _padding2: u32, +} + struct State<'a> { + window: &'a Window, surface: wgpu::Surface<'a>, device: wgpu::Device, queue: wgpu::Queue, config: wgpu::SurfaceConfiguration, - size: winit::dpi::PhysicalSize, render_pipeline: wgpu::RenderPipeline, obj_model: model::Model, - camera: Camera, - camera_controller: CameraController, + camera: camera::Camera, + projection: camera::Projection, + camera_controller: camera::CameraController, camera_uniform: CameraUniform, camera_buffer: wgpu::Buffer, camera_bind_group: wgpu::BindGroup, @@ -271,12 +162,20 @@ struct State<'a> { #[allow(dead_code)] instance_buffer: wgpu::Buffer, depth_texture: texture::Texture, - window: &'a Window, + size: winit::dpi::PhysicalSize, light_uniform: LightUniform, light_buffer: wgpu::Buffer, - light_bind_group_layout: wgpu::BindGroupLayout, light_bind_group: wgpu::BindGroup, light_render_pipeline: wgpu::RenderPipeline, + #[allow(dead_code)] + debug_material: model::Material, + mouse_pressed: bool, + // NEW! + hdr: hdr::HdrPipeline, + environment_bind_group: wgpu::BindGroup, + sky_pipeline: wgpu::RenderPipeline, + #[cfg(feature = "debug")] + debug: debug::Debug, } fn create_render_pipeline( @@ -285,13 +184,13 @@ fn create_render_pipeline( color_format: wgpu::TextureFormat, depth_format: Option, vertex_layouts: &[wgpu::VertexBufferLayout], + topology: wgpu::PrimitiveTopology, // NEW! shader: wgpu::ShaderModuleDescriptor, ) -> wgpu::RenderPipeline { let shader = device.create_shader_module(shader); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - cache: None, + label: Some(&format!("{:?}", shader)), layout: Some(layout), vertex: wgpu::VertexState { module: &shader, @@ -304,16 +203,13 @@ fn create_render_pipeline( entry_point: "fs_main", targets: &[Some(wgpu::ColorTargetState { format: color_format, - blend: Some(wgpu::BlendState { - alpha: wgpu::BlendComponent::REPLACE, - color: wgpu::BlendComponent::REPLACE, - }), + blend: None, write_mask: wgpu::ColorWrites::ALL, })], compilation_options: Default::default(), }), primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, + topology, // NEW! strip_index_format: None, front_face: wgpu::FrontFace::Ccw, cull_mode: Some(wgpu::Face::Back), @@ -327,7 +223,7 @@ fn create_render_pipeline( depth_stencil: depth_format.map(|format| wgpu::DepthStencilState { format, depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, + depth_compare: wgpu::CompareFunction::LessEqual, // UDPATED! stencil: wgpu::StencilState::default(), bias: wgpu::DepthBiasState::default(), }), @@ -336,17 +232,19 @@ fn create_render_pipeline( mask: !0, alpha_to_coverage_enabled: false, }, + // If the pipeline will be used with a multiview render pass, this + // indicates how many array layers the attachments will have. multiview: None, + cache: None, }) } impl<'a> State<'a> { - async fn new(window: &'a Window) -> State<'a> { + async fn new(window: &'a Window) -> anyhow::Result> { let size = window.inner_size(); // The instance is a handle to our GPU // BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU - log::warn!("WGPU setup"); let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { backends: wgpu::Backends::PRIMARY, ..Default::default() @@ -362,26 +260,23 @@ impl<'a> State<'a> { }) .await .unwrap(); - log::warn!("device and queue"); let (device, queue) = adapter .request_device( &wgpu::DeviceDescriptor { label: None, + // UPDATED! required_features: wgpu::Features::empty(), - // WebGL doesn't support all of wgpu's features, so if - // we're building for the web we'll have to disable some. - required_limits: wgpu::Limits::default(), + // UPDATED! + required_limits: wgpu::Limits::downlevel_defaults(), memory_hints: Default::default(), }, - // Some(&std::path::Path::new("trace")), // Trace path None, // Trace path ) .await .unwrap(); - log::warn!("Surface"); let surface_caps = surface.get_capabilities(&adapter); - // Shader code assumes an Srgb surface texture. Using a different + // Shader code in this tutorial assumes an Srgb surface texture. Using a different // one will result all the colors comming out darker. If you want to support non // Srgb surfaces, you'll need to account for that when drawing to the frame. let surface_format = surface_caps @@ -397,7 +292,8 @@ impl<'a> State<'a> { height: size.height, present_mode: surface_caps.present_modes[0], alpha_mode: surface_caps.alpha_modes[0], - view_formats: vec![], + // NEW! + view_formats: vec![surface_format.add_srgb_suffix()], desired_maximum_frame_latency: 2, }; @@ -409,8 +305,8 @@ impl<'a> State<'a> { visibility: wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Texture { multisampled: false, - view_dimension: wgpu::TextureViewDimension::D2, sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, }, count: None, }, @@ -420,23 +316,34 @@ impl<'a> State<'a> { ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), count: None, }, + // normal map + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + multisampled: false, + sample_type: wgpu::TextureSampleType::Float { filterable: true }, + view_dimension: wgpu::TextureViewDimension::D2, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering), + count: None, + }, ], label: Some("texture_bind_group_layout"), }); - let camera = Camera { - eye: (0.0, 5.0, -10.0).into(), - target: (0.0, 0.0, 0.0).into(), - up: cgmath::Vector3::unit_y(), - aspect: config.width as f32 / config.height as f32, - fovy: 45.0, - znear: 0.1, - zfar: 100.0, - }; - let camera_controller = CameraController::new(0.2); + let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0)); + let projection = + camera::Projection::new(config.width, config.height, cgmath::Deg(45.0), 0.1, 100.0); + let camera_controller = camera::CameraController::new(4.0, 0.4); let mut camera_uniform = CameraUniform::new(); - camera_uniform.update_view_proj(&camera); + camera_uniform.update_view_proj(&camera, &projection); let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Camera Buffer"), @@ -453,7 +360,14 @@ impl<'a> State<'a> { let position = cgmath::Vector3 { x, y: 0.0, z }; - let rotation = cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(180.0)); + let rotation = if position.is_zero() { + cgmath::Quaternion::from_axis_angle( + cgmath::Vector3::unit_z(), + cgmath::Deg(0.0), + ) + } else { + cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0)) + }; Instance { position, rotation } }) @@ -491,20 +405,11 @@ impl<'a> State<'a> { label: Some("camera_bind_group"), }); - log::warn!("Load model"); let obj_model = resources::load_model("cube.obj", &device, &queue, &texture_bind_group_layout) .await .unwrap(); - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("shader.wgsl"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), - }); - - let depth_texture = - texture::Texture::create_depth_texture(&device, &config, "depth_texture"); - let light_uniform = LightUniform { position: [2.0, 2.0, 2.0], _padding: 0, @@ -512,14 +417,11 @@ impl<'a> State<'a> { _padding2: 0, }; - // We'll want to update our lights position, so we use COPY_DST - let light_buffer = device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Light VB"), - contents: bytemuck::cast_slice(&[light_uniform]), - usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - } - ); + let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Light VB"), + contents: bytemuck::cast_slice(&[light_uniform]), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); let light_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -545,16 +447,71 @@ impl<'a> State<'a> { label: None, }); - let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - bind_group_layouts: &[ - &texture_bind_group_layout, - &camera_bind_group_layout, - &light_bind_group_layout, + let depth_texture = + texture::Texture::create_depth_texture(&device, &config, "depth_texture"); + + let hdr = hdr::HdrPipeline::new(&device, &config); + + let hdr_loader = resources::HdrLoader::new(&device); + let sky_bytes = resources::load_binary("pure-sky.hdr").await?; + let sky_texture = hdr_loader.from_equirectangular_bytes( + &device, + &queue, + &sky_bytes, + 1080, + Some("Sky Texture"), + )?; + + let environment_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("environment_layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::Cube, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::FRAGMENT, + ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering), + count: None, + }, + ], + }); + + let environment_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("environment_bind_group"), + layout: &environment_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&sky_texture.view()), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(sky_texture.sampler()), + }, ], - label: Some("Render Pipeline Layout"), - push_constant_ranges: &[], }); + let render_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[ + &texture_bind_group_layout, + &camera_bind_group_layout, + &light_bind_group_layout, + &environment_layout, // UPDATED! + ], + push_constant_ranges: &[], + }); + let render_pipeline = { let shader = wgpu::ShaderModuleDescriptor { label: Some("Normal Shader"), @@ -563,9 +520,10 @@ impl<'a> State<'a> { create_render_pipeline( &device, &render_pipeline_layout, - config.format, + hdr.format(), Some(texture::Texture::DEPTH_FORMAT), &[model::ModelVertex::desc(), InstanceRaw::desc()], + wgpu::PrimitiveTopology::TriangleList, shader, ) }; @@ -583,22 +541,76 @@ impl<'a> State<'a> { create_render_pipeline( &device, &layout, - config.format, + hdr.format(), Some(texture::Texture::DEPTH_FORMAT), &[model::ModelVertex::desc()], + wgpu::PrimitiveTopology::TriangleList, shader, ) }; - Self { + // NEW! + let sky_pipeline = { + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Sky Pipeline Layout"), + bind_group_layouts: &[&camera_bind_group_layout, &environment_layout], + push_constant_ranges: &[], + }); + let shader = wgpu::include_wgsl!("sky.wgsl"); + create_render_pipeline( + &device, + &layout, + hdr.format(), + Some(texture::Texture::DEPTH_FORMAT), + &[], + wgpu::PrimitiveTopology::TriangleList, + shader, + ) + }; + + let debug_material = { + let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png"); + let normal_bytes = include_bytes!("../res/cobble-normal.png"); + + let diffuse_texture = texture::Texture::from_bytes( + &device, + &queue, + diffuse_bytes, + "res/alt-diffuse.png", + false, + ) + .unwrap(); + let normal_texture = texture::Texture::from_bytes( + &device, + &queue, + normal_bytes, + "res/alt-normal.png", + true, + ) + .unwrap(); + + model::Material::new( + &device, + "alt-material", + diffuse_texture, + normal_texture, + &texture_bind_group_layout, + ) + }; + + #[cfg(feature = "debug")] + let debug = debug::Debug::new(&device, &camera_bind_group_layout, surface_format); + + Ok(Self { + window, surface, device, queue, config, - size, render_pipeline, obj_model, camera, + projection, camera_controller, camera_buffer, camera_bind_group, @@ -606,13 +618,22 @@ impl<'a> State<'a> { instances, instance_buffer, depth_texture, - window, + size, light_uniform, light_buffer, - light_bind_group_layout, light_bind_group, light_render_pipeline, - } + #[allow(dead_code)] + debug_material, + mouse_pressed: false, + // NEW! + hdr, + environment_bind_group, + sky_pipeline, + + #[cfg(feature = "debug")] + debug, + }) } pub fn window(&self) -> &Window { @@ -621,42 +642,76 @@ impl<'a> State<'a> { fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { if new_size.width > 0 && new_size.height > 0 { + println!("Resizing to {:?}", new_size); + self.projection.resize(new_size.width, new_size.height); + self.hdr + .resize(&self.device, new_size.width, new_size.height); + self.size = new_size; self.config.width = new_size.width; self.config.height = new_size.height; - self.size = new_size; - self.camera.aspect = self.config.width as f32 / self.config.height as f32; self.surface.configure(&self.device, &self.config); self.depth_texture = texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture"); } } + fn input(&mut self, event: &WindowEvent) -> bool { - self.camera_controller.process_events(event) + match event { + WindowEvent::KeyboardInput { + event: + KeyEvent { + physical_key: PhysicalKey::Code(key), + state, + .. + }, + .. + } => self.camera_controller.process_keyboard(*key, *state), + WindowEvent::MouseWheel { delta, .. } => { + self.camera_controller.process_scroll(delta); + true + } + WindowEvent::MouseInput { + button: MouseButton::Left, + state, + .. + } => { + self.mouse_pressed = *state == ElementState::Pressed; + true + } + _ => false, + } } - fn update(&mut self) { - self.camera_controller.update_camera(&mut self.camera); - log::info!("{:?}", self.camera); - self.camera_uniform.update_view_proj(&self.camera); + fn update(&mut self, dt: std::time::Duration) { + self.camera_controller.update_camera(&mut self.camera, dt); + self.camera_uniform + .update_view_proj(&self.camera, &self.projection); self.queue.write_buffer( &self.camera_buffer, 0, bytemuck::cast_slice(&[self.camera_uniform]), ); + // Update the light let old_position: cgmath::Vector3<_> = self.light_uniform.position.into(); - self.light_uniform.position = - (cgmath::Quaternion::from_axis_angle((0.0, 1.0, 0.0).into(), cgmath::Deg(1.0)) - * old_position) - .into(); - self.queue.write_buffer(&self.light_buffer, 0, bytemuck::cast_slice(&[self.light_uniform])); + self.light_uniform.position = (cgmath::Quaternion::from_axis_angle( + (0.0, 1.0, 0.0).into(), + cgmath::Deg(PI * dt.as_secs_f32()), + ) * old_position) + .into(); + self.queue.write_buffer( + &self.light_buffer, + 0, + bytemuck::cast_slice(&[self.light_uniform]), + ); } fn render(&mut self) -> Result<(), wgpu::SurfaceError> { let output = self.surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); + let view = output.texture.create_view(&wgpu::TextureViewDescriptor { + format: Some(self.config.format.add_srgb_suffix()), + ..Default::default() + }); let mut encoder = self .device @@ -668,7 +723,7 @@ impl<'a> State<'a> { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, + view: self.hdr.view(), // UPDATED! resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { @@ -693,8 +748,6 @@ impl<'a> State<'a> { }); render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); - - use crate::model::DrawLight; render_pass.set_pipeline(&self.light_render_pipeline); render_pass.draw_light_model( &self.obj_model, @@ -708,7 +761,36 @@ impl<'a> State<'a> { 0..self.instances.len() as u32, &self.camera_bind_group, &self.light_bind_group, + &self.environment_bind_group, ); + + render_pass.set_pipeline(&self.sky_pipeline); + render_pass.set_bind_group(0, &self.camera_bind_group, &[]); + render_pass.set_bind_group(1, &self.environment_bind_group, &[]); + render_pass.draw(0..3, 0..1); + } + + // NEW! + // Apply tonemapping + self.hdr.process(&mut encoder, &view); + + #[cfg(feature = "debug")] + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Debug"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + occlusion_query_set: None, + timestamp_writes: None, + }); + self.debug.draw_axis(&mut pass, &self.camera_bind_group); } self.queue.submit(iter::once(encoder.finish())); @@ -728,66 +810,55 @@ pub async fn run() { .build(&event_loop) .unwrap(); - // State::new uses async code, so we're going to wait for it to finish - let mut state = State::new(&window).await; - let mut surface_configured = false; - - event_loop - .run(move |event, control_flow| { - match event { - Event::WindowEvent { - ref event, - window_id, - } if window_id == state.window().id() => { - if !state.input(event) { - match event { - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - event: - KeyEvent { - state: ElementState::Pressed, - physical_key: PhysicalKey::Code(KeyCode::Escape), - .. - }, - .. - } => control_flow.exit(), - WindowEvent::Resized(physical_size) => { - surface_configured = true; - state.resize(*physical_size); - } - WindowEvent::RedrawRequested => { - // This tells winit that we want another frame after this one - state.window().request_redraw(); - - if !surface_configured { - return; - } - - state.update(); - match state.render() { - Ok(_) => {} - // Reconfigure the surface if it's lost or outdated - Err( - wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated, - ) => state.resize(state.size), - // The system is out of memory, we should probably quit - Err(wgpu::SurfaceError::OutOfMemory) => { - log::error!("OutOfMemory"); - control_flow.exit(); - } - - // This happens when the a frame takes too long to present - Err(wgpu::SurfaceError::Timeout) => { - log::warn!("Surface timeout") - } - } - } - _ => {} + let mut state = State::new(&window).await.unwrap(); + let mut last_render_time = instant::Instant::now(); + event_loop.run(move |event, control_flow| { + match event { + Event::DeviceEvent { + event: DeviceEvent::MouseMotion{ delta, }, + .. // We're not using device_id currently + } => if state.mouse_pressed { + state.camera_controller.process_mouse(delta.0, delta.1) + } + // UPDATED! + Event::WindowEvent { + ref event, + window_id, + } if window_id == state.window().id() && !state.input(event) => { + match event { + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + event: + KeyEvent { + state: ElementState::Pressed, + physical_key: PhysicalKey::Code(KeyCode::Escape), + .. + }, + .. + } => control_flow.exit(), + WindowEvent::Resized(physical_size) => { + state.resize(*physical_size); + } + WindowEvent::RedrawRequested => { + state.window().request_redraw(); + let now = instant::Instant::now(); + let dt = now - last_render_time; + last_render_time = now; + state.update(dt); + match state.render() { + Ok(_) => {} + // Reconfigure the surface if it's lost or outdated + Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size), + // The system is out of memory, we should probably quit + Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(), + // We're ignoring timeouts + Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"), } } + _ => {} } - _ => {} } - }) - .unwrap(); + _ => {} + } + }).unwrap(); } \ No newline at end of file diff --git a/src/light.wgsl b/src/light.wgsl index 5ecede8..d320f20 100644 --- a/src/light.wgsl +++ b/src/light.wgsl @@ -1,8 +1,11 @@ -// light.wgsl // Vertex shader struct Camera { + view_pos: vec4, + view: mat4x4, view_proj: mat4x4, + inv_proj: mat4x4, + inv_view: mat4x4, } @group(0) @binding(0) var camera: Camera; diff --git a/src/model.rs b/src/model.rs index 442fc6a..20734d9 100644 --- a/src/model.rs +++ b/src/model.rs @@ -12,6 +12,8 @@ pub struct ModelVertex { pub position: [f32; 3], pub tex_coords: [f32; 2], pub normal: [f32; 3], + pub tangent: [f32; 3], + pub bitangent: [f32; 3], } impl Vertex for ModelVertex { @@ -36,6 +38,17 @@ impl Vertex for ModelVertex { shader_location: 2, format: wgpu::VertexFormat::Float32x3, }, + // Tangent and bitangent + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress, + shader_location: 3, + format: wgpu::VertexFormat::Float32x3, + }, + wgpu::VertexAttribute { + offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress, + shader_location: 4, + format: wgpu::VertexFormat::Float32x3, + }, ], } } @@ -46,9 +59,51 @@ pub struct Material { pub name: String, #[allow(unused)] pub diffuse_texture: texture::Texture, + #[allow(unused)] + pub normal_texture: texture::Texture, pub bind_group: wgpu::BindGroup, } +impl Material { + pub fn new( + device: &wgpu::Device, + name: &str, + diffuse_texture: texture::Texture, + normal_texture: texture::Texture, + layout: &wgpu::BindGroupLayout, + ) -> Self { + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&normal_texture.view), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::Sampler(&normal_texture.sampler), + }, + ], + label: Some(name), + }); + + Self { + name: String::from(name), + diffuse_texture, + normal_texture, + bind_group, + } + } +} + pub struct Mesh { #[allow(unused)] pub name: String, @@ -63,14 +118,15 @@ pub struct Model { pub materials: Vec, } -// model.rs pub trait DrawModel<'a> { + #[allow(unused)] fn draw_mesh( &mut self, mesh: &'a Mesh, material: &'a Material, camera_bind_group: &'a wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + environment_bind_group: &'a wgpu::BindGroup, ); fn draw_mesh_instanced( &mut self, @@ -79,13 +135,16 @@ pub trait DrawModel<'a> { instances: Range, camera_bind_group: &'a wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + environment_bind_group: &'a wgpu::BindGroup, ); + #[allow(unused)] fn draw_model( &mut self, model: &'a Model, camera_bind_group: &'a wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + environment_bind_group: &'a wgpu::BindGroup, ); fn draw_model_instanced( &mut self, @@ -93,6 +152,17 @@ pub trait DrawModel<'a> { instances: Range, camera_bind_group: &'a wgpu::BindGroup, light_bind_group: &'a wgpu::BindGroup, + environment_bind_group: &'a wgpu::BindGroup, + ); + #[allow(unused)] + fn draw_model_instanced_with_material( + &mut self, + model: &'a Model, + material: &'a Material, + instances: Range, + camera_bind_group: &'a wgpu::BindGroup, + light_bind_group: &'a wgpu::BindGroup, + environment_bind_group: &'a wgpu::BindGroup, ); } @@ -106,8 +176,16 @@ where material: &'b Material, camera_bind_group: &'b wgpu::BindGroup, light_bind_group: &'b wgpu::BindGroup, + environment_bind_group: &'b wgpu::BindGroup, ) { - self.draw_mesh_instanced(mesh, material, 0..1, camera_bind_group, light_bind_group); + self.draw_mesh_instanced( + mesh, + material, + 0..1, + camera_bind_group, + light_bind_group, + environment_bind_group, + ); } fn draw_mesh_instanced( @@ -117,12 +195,14 @@ where instances: Range, camera_bind_group: &'b wgpu::BindGroup, light_bind_group: &'b wgpu::BindGroup, + environment_bind_group: &'b wgpu::BindGroup, ) { self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); self.set_bind_group(0, &material.bind_group, &[]); self.set_bind_group(1, camera_bind_group, &[]); self.set_bind_group(2, light_bind_group, &[]); + self.set_bind_group(3, environment_bind_group, &[]); self.draw_indexed(0..mesh.num_elements, 0, instances); } @@ -131,8 +211,15 @@ where model: &'b Model, camera_bind_group: &'b wgpu::BindGroup, light_bind_group: &'b wgpu::BindGroup, + environment_bind_group: &'b wgpu::BindGroup, ) { - self.draw_model_instanced(model, 0..1, camera_bind_group, light_bind_group); + self.draw_model_instanced( + model, + 0..1, + camera_bind_group, + light_bind_group, + environment_bind_group, + ); } fn draw_model_instanced( @@ -141,16 +228,45 @@ where instances: Range, camera_bind_group: &'b wgpu::BindGroup, light_bind_group: &'b wgpu::BindGroup, + environment_bind_group: &'b wgpu::BindGroup, // NEW! ) { for mesh in &model.meshes { let material = &model.materials[mesh.material]; - self.draw_mesh_instanced(mesh, material, instances.clone(), camera_bind_group, light_bind_group); + self.draw_mesh_instanced( + mesh, + material, + instances.clone(), + camera_bind_group, + light_bind_group, + environment_bind_group, + ); + } + } + + fn draw_model_instanced_with_material( + &mut self, + model: &'b Model, + material: &'b Material, + instances: Range, + camera_bind_group: &'b wgpu::BindGroup, + light_bind_group: &'b wgpu::BindGroup, + environment_bind_group: &'b wgpu::BindGroup, + ) { + for mesh in &model.meshes { + self.draw_mesh_instanced( + mesh, + material, + instances.clone(), + camera_bind_group, + light_bind_group, + environment_bind_group, + ); } } } -// model.rs pub trait DrawLight<'a> { + #[allow(unused)] fn draw_light_mesh( &mut self, mesh: &'a Mesh, @@ -223,7 +339,12 @@ where light_bind_group: &'b wgpu::BindGroup, ) { for mesh in &model.meshes { - self.draw_light_mesh_instanced(mesh, instances.clone(), camera_bind_group, light_bind_group); + self.draw_light_mesh_instanced( + mesh, + instances.clone(), + camera_bind_group, + light_bind_group, + ); } } -} +} \ No newline at end of file diff --git a/src/resources.rs b/src/resources.rs index f5f358a..53034bb 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -1,5 +1,7 @@ use std::io::{BufReader, Cursor}; +use cfg_if::cfg_if; +use image::codecs::hdr::HdrDecoder; use wgpu::util::DeviceExt; use crate::{model, texture}; @@ -24,11 +26,12 @@ pub async fn load_binary(file_name: &str) -> anyhow::Result> { pub async fn load_texture( file_name: &str, + is_normal_map: bool, device: &wgpu::Device, queue: &wgpu::Queue, ) -> anyhow::Result { let data = load_binary(file_name).await?; - texture::Texture::from_bytes(device, queue, &data, file_name) + texture::Texture::from_bytes(device, queue, &data, file_name, is_normal_map) } pub async fn load_model( @@ -53,66 +56,112 @@ pub async fn load_model( tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text))) }, ) - .await?; + .await?; let mut materials = Vec::new(); for m in obj_materials? { - let diffuse_texture = load_texture(&m.diffuse_texture, device, queue).await?; - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::TextureView(&diffuse_texture.view), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler), - }, - ], - label: None, - }); + let diffuse_texture = load_texture(&m.diffuse_texture, false, device, queue).await?; + let normal_texture = load_texture(&m.normal_texture, true, device, queue).await?; - materials.push(model::Material { - name: m.name, + materials.push(model::Material::new( + device, + &m.name, diffuse_texture, - bind_group, - }) + normal_texture, + layout, + )); } let meshes = models .into_iter() .map(|m| { - let vertices = (0..m.mesh.positions.len() / 3) - .map(|i| { - if m.mesh.normals.is_empty(){ - model::ModelVertex { - position: [ - m.mesh.positions[i * 3], - m.mesh.positions[i * 3 + 1], - m.mesh.positions[i * 3 + 2], - ], - tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], - normal: [0.0, 0.0, 0.0], - } - }else{ - model::ModelVertex { - position: [ - m.mesh.positions[i * 3], - m.mesh.positions[i * 3 + 1], - m.mesh.positions[i * 3 + 2], - ], - tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], - normal: [ - m.mesh.normals[i * 3], - m.mesh.normals[i * 3 + 1], - m.mesh.normals[i * 3 + 2], - ], - } - } + let mut vertices = (0..m.mesh.positions.len() / 3) + .map(|i| model::ModelVertex { + position: [ + m.mesh.positions[i * 3], + m.mesh.positions[i * 3 + 1], + m.mesh.positions[i * 3 + 2], + ], + tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], + normal: [ + m.mesh.normals[i * 3], + m.mesh.normals[i * 3 + 1], + m.mesh.normals[i * 3 + 2], + ], + // We'll calculate these later + tangent: [0.0; 3], + bitangent: [0.0; 3], }) .collect::>(); + let indices = &m.mesh.indices; + let mut triangles_included = vec![0; vertices.len()]; + + // Calculate tangents and bitangets. We're going to + // use the triangles, so we need to loop through the + // indices in chunks of 3 + for c in indices.chunks(3) { + let v0 = vertices[c[0] as usize]; + let v1 = vertices[c[1] as usize]; + let v2 = vertices[c[2] as usize]; + + let pos0: cgmath::Vector3<_> = v0.position.into(); + let pos1: cgmath::Vector3<_> = v1.position.into(); + let pos2: cgmath::Vector3<_> = v2.position.into(); + + let uv0: cgmath::Vector2<_> = v0.tex_coords.into(); + let uv1: cgmath::Vector2<_> = v1.tex_coords.into(); + let uv2: cgmath::Vector2<_> = v2.tex_coords.into(); + + // Calculate the edges of the triangle + let delta_pos1 = pos1 - pos0; + let delta_pos2 = pos2 - pos0; + + // This will give us a direction to calculate the + // tangent and bitangent + let delta_uv1 = uv1 - uv0; + let delta_uv2 = uv2 - uv0; + + // Solving the following system of equations will + // give us the tangent and bitangent. + // delta_pos1 = delta_uv1.x * T + delta_u.y * B + // delta_pos2 = delta_uv2.x * T + delta_uv2.y * B + // Luckily, the place I found this equation provided + // the solution! + let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x); + let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r; + // We flip the bitangent to enable right-handed normal + // maps with wgpu texture coordinate system + let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r; + + // We'll use the same tangent/bitangent for each vertex in the triangle + vertices[c[0] as usize].tangent = + (tangent + cgmath::Vector3::from(vertices[c[0] as usize].tangent)).into(); + vertices[c[1] as usize].tangent = + (tangent + cgmath::Vector3::from(vertices[c[1] as usize].tangent)).into(); + vertices[c[2] as usize].tangent = + (tangent + cgmath::Vector3::from(vertices[c[2] as usize].tangent)).into(); + vertices[c[0] as usize].bitangent = + (bitangent + cgmath::Vector3::from(vertices[c[0] as usize].bitangent)).into(); + vertices[c[1] as usize].bitangent = + (bitangent + cgmath::Vector3::from(vertices[c[1] as usize].bitangent)).into(); + vertices[c[2] as usize].bitangent = + (bitangent + cgmath::Vector3::from(vertices[c[2] as usize].bitangent)).into(); + + // Used to average the tangents/bitangents + triangles_included[c[0] as usize] += 1; + triangles_included[c[1] as usize] += 1; + triangles_included[c[2] as usize] += 1; + } + + // Average the tangents/bitangents + for (i, n) in triangles_included.into_iter().enumerate() { + let denom = 1.0 / n as f32; + let v = &mut vertices[i]; + v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into(); + v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into(); + } + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some(&format!("{:?} Vertex Buffer", file_name)), contents: bytemuck::cast_slice(&vertices), @@ -124,7 +173,6 @@ pub async fn load_model( usage: wgpu::BufferUsages::INDEX, }); - log::info!("Mesh: {}", m.name); model::Mesh { name: file_name.to_string(), vertex_buffer, @@ -136,4 +184,164 @@ pub async fn load_model( .collect::>(); Ok(model::Model { meshes, materials }) -} \ No newline at end of file +} + +pub struct HdrLoader { + texture_format: wgpu::TextureFormat, + equirect_layout: wgpu::BindGroupLayout, + equirect_to_cubemap: wgpu::ComputePipeline, +} + +impl HdrLoader { + pub fn new(device: &wgpu::Device) -> Self { + let module = device.create_shader_module(wgpu::include_wgsl!("equirectangular.wgsl")); + let texture_format = wgpu::TextureFormat::Rgba32Float; + let equirect_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("HdrLoader::equirect_layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::StorageTexture { + access: wgpu::StorageTextureAccess::WriteOnly, + format: texture_format, + view_dimension: wgpu::TextureViewDimension::D2Array, + }, + count: None, + }, + ], + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&equirect_layout], + push_constant_ranges: &[], + }); + + let equirect_to_cubemap = + device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("equirect_to_cubemap"), + layout: Some(&pipeline_layout), + module: &module, + entry_point: "compute_equirect_to_cubemap", + compilation_options: Default::default(), + cache: None, + }); + + Self { + equirect_to_cubemap, + texture_format, + equirect_layout, + } + } + + pub fn from_equirectangular_bytes( + &self, + device: &wgpu::Device, + queue: &wgpu::Queue, + data: &[u8], + dst_size: u32, + label: Option<&str>, + ) -> anyhow::Result { + let hdr_decoder = HdrDecoder::new(Cursor::new(data))?; + let meta = hdr_decoder.metadata(); + + let pixels = { + let mut pixels = vec![[0.0, 0.0, 0.0, 0.0]; meta.width as usize * meta.height as usize]; + hdr_decoder.read_image_transform( + |pix| { + let rgb = pix.to_hdr(); + [rgb.0[0], rgb.0[1], rgb.0[2], 1.0f32] + }, + &mut pixels[..], + )?; + pixels + }; + + let src = texture::Texture::create_2d_texture( + device, + meta.width, + meta.height, + self.texture_format, + wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + wgpu::FilterMode::Linear, + None, + ); + + queue.write_texture( + wgpu::ImageCopyTexture { + texture: &src.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &bytemuck::cast_slice(&pixels), + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(src.size.width * std::mem::size_of::<[f32; 4]>() as u32), + rows_per_image: Some(src.size.height), + }, + src.size, + ); + + let dst = texture::CubeTexture::create_2d( + device, + dst_size, + dst_size, + self.texture_format, + 1, + wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING, + wgpu::FilterMode::Nearest, + label, + ); + + let dst_view = dst.texture().create_view(&wgpu::TextureViewDescriptor { + label, + dimension: Some(wgpu::TextureViewDimension::D2Array), + // array_layer_count: Some(6), + ..Default::default() + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label, + layout: &self.equirect_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(&src.view), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(&dst_view), + }, + ], + }); + + let mut encoder = device.create_command_encoder(&Default::default()); + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { + label, + timestamp_writes: None, + }); + + let num_workgroups = (dst_size + 15) / 16; + pass.set_pipeline(&self.equirect_to_cubemap); + pass.set_bind_group(0, &bind_group, &[]); + pass.dispatch_workgroups(num_workgroups, num_workgroups, 6); + + drop(pass); + + queue.submit([encoder.finish()]); + + Ok(dst) + } +} diff --git a/src/shader.wgsl b/src/shader.wgsl index 39ba767..ec9d8c5 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -1,5 +1,15 @@ // Vertex shader +struct Camera { + view_pos: vec4, + view: mat4x4, + view_proj: mat4x4, + inv_proj: mat4x4, + inv_view: mat4x4, +} +@group(1) @binding(0) +var camera: Camera; + struct Light { position: vec3, color: vec3, @@ -7,34 +17,34 @@ struct Light { @group(2) @binding(0) var light: Light; -struct Camera { - view_pos: vec4, - view_proj: mat4x4, -} -@group(1) @binding(0) -var camera: Camera; - struct VertexInput { @location(0) position: vec3, @location(1) tex_coords: vec2, @location(2) normal: vec3, -}; + @location(3) tangent: vec3, + @location(4) bitangent: vec3, +} struct InstanceInput { @location(5) model_matrix_0: vec4, @location(6) model_matrix_1: vec4, @location(7) model_matrix_2: vec4, @location(8) model_matrix_3: vec4, @location(9) normal_matrix_0: vec3, - @location(10) normal_matrix_1: vec3, - @location(11) normal_matrix_2: vec3, + @location(10) normal_matrix_1: vec3, + @location(11) normal_matrix_2: vec3, } struct VertexOutput { @builtin(position) clip_position: vec4, @location(0) tex_coords: vec2, - @location(1) world_normal: vec3, - @location(2) world_position: vec3, -}; + // Updated! + @location(1) world_position: vec3, + @location(2) world_view_position: vec3, + @location(3) world_light_position: vec3, + @location(4) world_normal: vec3, + @location(5) world_tangent: vec3, + @location(6) world_bitangent: vec3, +} @vertex fn vs_main( @@ -47,18 +57,23 @@ fn vs_main( instance.model_matrix_2, instance.model_matrix_3, ); - let normal_matrix = mat3x3( instance.normal_matrix_0, instance.normal_matrix_1, instance.normal_matrix_2, ); + + // UPDATED! + let world_position = model_matrix * vec4(model.position, 1.0); + var out: VertexOutput; - out.tex_coords = model.tex_coords; - out.world_normal = normal_matrix * model.normal; - var world_position: vec4 = model_matrix * vec4(model.position, 1.0); - out.world_position = world_position.xyz; out.clip_position = camera.view_proj * world_position; + out.tex_coords = model.tex_coords; + out.world_normal = normalize(normal_matrix * model.normal); + out.world_tangent = normalize(normal_matrix * model.tangent); + out.world_bitangent = normalize(normal_matrix * model.bitangent); + out.world_position = world_position.xyz; + out.world_view_position = camera.view_pos.xyz; return out; } @@ -68,27 +83,57 @@ fn vs_main( var t_diffuse: texture_2d; @group(0)@binding(1) var s_diffuse: sampler; +@group(0)@binding(2) +var t_normal: texture_2d; +@group(0) @binding(3) +var s_normal: sampler; + +@group(3) +@binding(0) +var env_map: texture_cube; +@group(3) +@binding(1) +var env_sampler: sampler; @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4 { let object_color: vec4 = textureSample(t_diffuse, s_diffuse, in.tex_coords); + let object_normal: vec4 = textureSample(t_normal, s_normal, in.tex_coords); + // NEW! + // Adjust the tangent and bitangent using the Gramm-Schmidt process + // This makes sure that they are perpedicular to each other and the + // normal of the surface. + let world_tangent = normalize(in.world_tangent - dot(in.world_tangent, in.world_normal) * in.world_normal); + let world_bitangent = cross(world_tangent, in.world_normal); + + // Convert the normal sample to world space + let TBN = mat3x3( + world_tangent, + world_bitangent, + in.world_normal, + ); + let tangent_normal = object_normal.xyz * 2.0 - 1.0; + let world_normal = TBN * tangent_normal; + + // Create the lighting vectors let light_dir = normalize(light.position - in.world_position); - - let view_dir = normalize(camera.view_pos.xyz - in.world_position); + let view_dir = normalize(in.world_view_position - in.world_position); let half_dir = normalize(view_dir + light_dir); - let specular_strength = pow(max(dot(in.world_normal, half_dir), 0.0), 32.0); - let specular_color = specular_strength * light.color; - - let diffuse_strength = max(dot(in.world_normal, light_dir), 0.0); + let diffuse_strength = max(dot(world_normal, light_dir), 0.0); let diffuse_color = light.color * diffuse_strength; - // We don't need (or want) much ambient light, so 0.1 is fine - let ambient_strength = 0.03; - let ambient_color = light.color * ambient_strength; + let specular_strength = pow(max(dot(world_normal, half_dir), 0.0), 32.0); + let specular_color = specular_strength * light.color; - let result = (ambient_color + diffuse_color + specular_color) * object_color.xyz; + // NEW! + // Calculate reflections + let world_reflect = reflect(-view_dir, world_normal); + let reflection = textureSample(env_map, env_sampler, world_reflect).rgb; + let shininess = 0.1; + + let result = (diffuse_color + specular_color) * object_color.xyz + reflection * shininess; return vec4(result, object_color.a); } \ No newline at end of file diff --git a/src/sky.wgsl b/src/sky.wgsl new file mode 100644 index 0000000..613227e --- /dev/null +++ b/src/sky.wgsl @@ -0,0 +1,45 @@ +struct Camera { + view_pos: vec4, + view: mat4x4, + view_proj: mat4x4, + inv_proj: mat4x4, + inv_view: mat4x4, +} +@group(0) @binding(0) +var camera: Camera; + +@group(1) +@binding(0) +var env_map: texture_cube; +@group(1) +@binding(1) +var env_sampler: sampler; + +struct VertexOutput { + @builtin(position) frag_position: vec4, + @location(0) clip_position: vec4, +} + +@vertex +fn vs_main( + @builtin(vertex_index) id: u32, +) -> VertexOutput { + let uv = vec2(vec2( + id & 1u, + (id >> 1u) & 1u, + )); + var out: VertexOutput; + out.clip_position = vec4(uv * 4.0 - 1.0, 1.0, 1.0); + out.frag_position = out.clip_position; + return out; +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + let view_pos_homogeneous = camera.inv_proj * in.clip_position; + let view_ray_direction = view_pos_homogeneous.xyz / view_pos_homogeneous.w; + var ray_direction = normalize((camera.inv_view * vec4(view_ray_direction, 0.0)).xyz); + + let sample = textureSample(env_map, env_sampler, ray_direction); + return sample; +} \ No newline at end of file diff --git a/src/texture.rs b/src/texture.rs index 5b80725..d514f2f 100644 --- a/src/texture.rs +++ b/src/texture.rs @@ -1,88 +1,22 @@ -use image::GenericImageView; use anyhow::*; +use image::GenericImageView; pub struct Texture { #[allow(unused)] pub texture: wgpu::Texture, pub view: wgpu::TextureView, pub sampler: wgpu::Sampler, -} - -impl Texture { - pub fn from_bytes( - device: &wgpu::Device, - queue: &wgpu::Queue, - bytes: &[u8], - label: &str - ) -> Result { - let img = image::load_from_memory(bytes)?; - Self::from_image(device, queue, &img, Some(label)) - } - - pub fn from_image( - device: &wgpu::Device, - queue: &wgpu::Queue, - img: &image::DynamicImage, - label: Option<&str> - ) -> Result { - let rgba = img.to_rgba8(); - let dimensions = img.dimensions(); - - let size = wgpu::Extent3d { - width: dimensions.0, - height: dimensions.1, - depth_or_array_layers: 1, - }; - let texture = device.create_texture( - &wgpu::TextureDescriptor { - label, - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, - view_formats: &[], - } - ); - - queue.write_texture( - wgpu::ImageCopyTexture { - aspect: wgpu::TextureAspect::All, - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, - &rgba, - wgpu::ImageDataLayout { - offset: 0, - bytes_per_row: Some(4 * dimensions.0), - rows_per_image: Some(dimensions.1), - }, - size, - ); - - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - 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::Nearest, - mipmap_filter: wgpu::FilterMode::Nearest, - ..Default::default() - } - ); - - Ok(Self { texture, view, sampler }) - } + pub size: wgpu::Extent3d, } impl Texture { pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - pub fn create_depth_texture(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration, label: &str) -> Self { + pub fn create_depth_texture( + device: &wgpu::Device, + config: &wgpu::SurfaceConfiguration, + label: &str, + ) -> Self { let size = wgpu::Extent3d { width: config.width.max(1), height: config.height.max(1), @@ -95,28 +29,227 @@ impl Texture { sample_count: 1, dimension: wgpu::TextureDimension::D2, format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[Self::DEPTH_FORMAT], }; let texture = device.create_texture(&desc); - let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); - 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::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), - lod_min_clamp: 0.0, - lod_max_clamp: 100.0, - ..Default::default() - } + 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::Nearest, + compare: Some(wgpu::CompareFunction::LessEqual), + lod_min_clamp: 0.0, + lod_max_clamp: 100.0, + ..Default::default() + }); + + Self { + texture, + view, + sampler, + size, // NEW! + } + } + + #[allow(dead_code)] + pub fn from_bytes( + device: &wgpu::Device, + queue: &wgpu::Queue, + bytes: &[u8], + label: &str, + is_normal_map: bool, + ) -> Result { + let img = image::load_from_memory(bytes)?; + Self::from_image(device, queue, &img, Some(label), is_normal_map) + } + + pub fn from_image( + device: &wgpu::Device, + queue: &wgpu::Queue, + img: &image::DynamicImage, + label: Option<&str>, + is_normal_map: bool, + ) -> Result { + let dimensions = img.dimensions(); + let rgba = img.to_rgba8(); + + let format = if is_normal_map { + wgpu::TextureFormat::Rgba8Unorm + } else { + wgpu::TextureFormat::Rgba8UnormSrgb + }; + let usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST; + let size = wgpu::Extent3d { + width: img.width(), + height: img.height(), + depth_or_array_layers: 1, + }; + let texture = Self::create_2d_texture( + device, + size.width, + size.height, + format, + usage, + wgpu::FilterMode::Linear, + label, ); - Self { texture, view, sampler } + queue.write_texture( + wgpu::ImageCopyTexture { + aspect: wgpu::TextureAspect::All, + texture: &texture.texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + }, + &rgba, + wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(4 * dimensions.0), + rows_per_image: Some(dimensions.1), + }, + size, + ); + + Ok(texture) + } + + pub(crate) fn create_2d_texture( + device: &wgpu::Device, + width: u32, + height: u32, + format: wgpu::TextureFormat, + usage: wgpu::TextureUsages, + mag_filter: wgpu::FilterMode, + label: Option<&str>, + ) -> Self { + let size = wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }; + + Self::create_texture( + device, + label, + size, + format, + usage, + wgpu::TextureDimension::D2, + mag_filter, + ) + } + + pub fn create_texture( + device: &wgpu::Device, + label: Option<&str>, + size: wgpu::Extent3d, + format: wgpu::TextureFormat, + usage: wgpu::TextureUsages, + dimension: wgpu::TextureDimension, + mag_filter: wgpu::FilterMode, + ) -> Self { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label, + size, + mip_level_count: 1, + sample_count: 1, + dimension, + format, + usage, + view_formats: &[], + }); + + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + 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, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + Self { + texture, + view, + sampler, + size, + } + } +} + +pub struct CubeTexture { + texture: wgpu::Texture, + sampler: wgpu::Sampler, + view: wgpu::TextureView, +} + +impl CubeTexture { + pub fn create_2d( + device: &wgpu::Device, + width: u32, + height: u32, + format: wgpu::TextureFormat, + mip_level_count: u32, + usage: wgpu::TextureUsages, + mag_filter: wgpu::FilterMode, + label: Option<&str>, + ) -> Self { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label, + size: wgpu::Extent3d { + width, + height, + // A cube has 6 sides, so we need 6 layers + depth_or_array_layers: 6, + }, + mip_level_count, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage, + view_formats: &[], + }); + + let view = texture.create_view(&wgpu::TextureViewDescriptor { + label, + dimension: Some(wgpu::TextureViewDimension::Cube), + array_layer_count: Some(6), + ..Default::default() + }); + + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label, + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter, + min_filter: wgpu::FilterMode::Nearest, + mipmap_filter: wgpu::FilterMode::Nearest, + ..Default::default() + }); + + Self { + texture, + sampler, + view, + } + } + + pub fn texture(&self) -> &wgpu::Texture { + &self.texture + } + + pub fn view(&self) -> &wgpu::TextureView { + &self.view + } + + pub fn sampler(&self) -> &wgpu::Sampler { + &self.sampler } } \ No newline at end of file