├── web ├── README.md ├── examples │ ├── README.md │ ├── cube │ │ ├── README.md │ │ ├── wgpu_rust_renderer_example_cube_bg.wasm │ │ ├── build.sh │ │ ├── Cargo.toml │ │ ├── index.html │ │ └── src │ │ │ └── lib.rs │ ├── gltf │ │ ├── README.md │ │ ├── assets │ │ │ ├── Default_AO.jpg │ │ │ ├── DamagedHelmet.bin │ │ │ ├── Default_albedo.jpg │ │ │ ├── Default_normal.jpg │ │ │ ├── Default_emissive.jpg │ │ │ ├── Default_metalRoughness.jpg │ │ │ └── DamagedHelmet.gltf │ │ ├── wgpu_rust_renderer_example_gltf_bg.wasm │ │ ├── build.sh │ │ ├── Cargo.toml │ │ ├── index.html │ │ └── src │ │ │ └── lib.rs │ ├── rotation │ │ ├── README.md │ │ ├── wgpu_rust_renderer_example_rotation_bg.wasm │ │ ├── build.sh │ │ ├── Cargo.toml │ │ ├── index.html │ │ └── src │ │ │ └── lib.rs │ ├── texture │ │ ├── README.md │ │ ├── assets │ │ │ └── texture.png │ │ ├── wgpu_rust_renderer_example_texture_bg.wasm │ │ ├── build.sh │ │ ├── Cargo.toml │ │ ├── index.html │ │ └── src │ │ │ └── lib.rs │ ├── triangle │ │ ├── README.md │ │ ├── wgpu_rust_renderer_example_triangle_bg.wasm │ │ ├── build.sh │ │ ├── Cargo.toml │ │ ├── index.html │ │ └── src │ │ │ └── lib.rs │ ├── face_culling │ │ ├── README.md │ │ ├── wgpu_rust_renderer_example_face_culling_bg.wasm │ │ ├── build.sh │ │ ├── Cargo.toml │ │ ├── index.html │ │ └── src │ │ │ └── lib.rs │ ├── build.sh │ ├── icons │ │ ├── ic_arrow_drop_down_black_24dp.svg │ │ ├── ic_menu_black_24dp.svg │ │ ├── ic_close_black_24dp.svg │ │ └── ic_search_black_24dp.svg │ ├── utils │ │ └── window.rs │ └── index.html └── build_examples.sh ├── .gitignore ├── src ├── resource │ ├── mod.rs │ └── resource.rs ├── material │ ├── mod.rs │ └── node │ │ ├── mod.rs │ │ ├── node.rs │ │ ├── normal.rs │ │ ├── normal_matrix.rs │ │ ├── const_float.rs │ │ ├── float.rs │ │ ├── const_vector3.rs │ │ ├── vector3.rs │ │ ├── x.rs │ │ ├── y.rs │ │ ├── z.rs │ │ ├── xyz.rs │ │ ├── linear_to_srgb.rs │ │ ├── srgb_to_linear.rs │ │ ├── add.rs │ │ ├── sub.rs │ │ ├── multiply.rs │ │ ├── texture.rs │ │ ├── tangent_to_object_normal.rs │ │ └── brdf.rs ├── texture │ ├── mod.rs │ ├── texture.rs │ └── sampler.rs ├── geometry │ ├── mod.rs │ ├── index.rs │ ├── attribute.rs │ └── geometry.rs ├── web │ ├── mod.rs │ └── wgpu_web_renderer.rs ├── scene │ ├── mod.rs │ ├── mesh.rs │ ├── camera.rs │ ├── scene.rs │ └── node.rs ├── math │ ├── mod.rs │ ├── color.rs │ ├── euler.rs │ ├── matrix3gpu.rs │ ├── vector3.rs │ ├── quaternion.rs │ └── matrix3.rs ├── utils │ ├── mod.rs │ ├── log.rs │ ├── file_loader.rs │ ├── material_helper.rs │ ├── texture_loader.rs │ └── geometry_helper.rs ├── lib.rs └── renderer │ ├── mod.rs │ ├── wgpu_attributes.rs │ ├── wgpu_indices.rs │ ├── wgpu_samplers.rs │ ├── wgpu_textures.rs │ └── wgpu_render_pipeline.rs ├── .travis.yml ├── screenshots ├── web.png └── desktop.png ├── examples ├── texture │ ├── texture.png │ └── main.rs ├── gltf │ ├── assets │ │ ├── Default_AO.jpg │ │ ├── DamagedHelmet.bin │ │ ├── Default_albedo.jpg │ │ ├── Default_normal.jpg │ │ ├── Default_emissive.jpg │ │ ├── Default_metalRoughness.jpg │ │ └── DamagedHelmet.gltf │ └── main.rs ├── triangle │ └── main.rs ├── rotation │ └── main.rs ├── cube │ └── main.rs ├── pbr │ └── main.rs └── face_culling │ └── main.rs ├── LICENSE ├── Cargo.toml └── README.md /web/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/examples/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/examples/cube/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/examples/gltf/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/examples/rotation/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/examples/texture/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/examples/triangle/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target/ 3 | -------------------------------------------------------------------------------- /src/resource/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod resource; -------------------------------------------------------------------------------- /web/examples/face_culling/README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | 5 | -------------------------------------------------------------------------------- /src/material/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod material; 2 | pub mod node; 3 | -------------------------------------------------------------------------------- /src/texture/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod sampler; 2 | pub mod texture; 3 | -------------------------------------------------------------------------------- /web/build_examples.sh: -------------------------------------------------------------------------------- 1 | cd examples 2 | bash build.sh 3 | cd - 4 | 5 | -------------------------------------------------------------------------------- /src/geometry/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod attribute; 2 | pub mod geometry; 3 | pub mod index; 4 | -------------------------------------------------------------------------------- /src/web/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_arch = "wasm32")] 2 | pub mod wgpu_web_renderer; 3 | -------------------------------------------------------------------------------- /src/scene/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod camera; 2 | pub mod mesh; 3 | pub mod node; 4 | pub mod scene; 5 | -------------------------------------------------------------------------------- /screenshots/web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/screenshots/web.png -------------------------------------------------------------------------------- /screenshots/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/screenshots/desktop.png -------------------------------------------------------------------------------- /examples/texture/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/texture/texture.png -------------------------------------------------------------------------------- /examples/gltf/assets/Default_AO.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/gltf/assets/Default_AO.jpg -------------------------------------------------------------------------------- /examples/gltf/assets/DamagedHelmet.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/gltf/assets/DamagedHelmet.bin -------------------------------------------------------------------------------- /examples/gltf/assets/Default_albedo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/gltf/assets/Default_albedo.jpg -------------------------------------------------------------------------------- /examples/gltf/assets/Default_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/gltf/assets/Default_normal.jpg -------------------------------------------------------------------------------- /web/examples/build.sh: -------------------------------------------------------------------------------- 1 | for dir in $(ls -d ./*/ | grep -v icons | grep -v utils) 2 | do 3 | cd $dir 4 | bash build.sh 5 | cd - 6 | done 7 | -------------------------------------------------------------------------------- /web/examples/gltf/assets/Default_AO.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/assets/Default_AO.jpg -------------------------------------------------------------------------------- /web/examples/texture/assets/texture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/texture/assets/texture.png -------------------------------------------------------------------------------- /examples/gltf/assets/Default_emissive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/gltf/assets/Default_emissive.jpg -------------------------------------------------------------------------------- /web/examples/gltf/assets/DamagedHelmet.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/assets/DamagedHelmet.bin -------------------------------------------------------------------------------- /web/examples/gltf/assets/Default_albedo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/assets/Default_albedo.jpg -------------------------------------------------------------------------------- /web/examples/gltf/assets/Default_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/assets/Default_normal.jpg -------------------------------------------------------------------------------- /examples/gltf/assets/Default_metalRoughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/examples/gltf/assets/Default_metalRoughness.jpg -------------------------------------------------------------------------------- /web/examples/gltf/assets/Default_emissive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/assets/Default_emissive.jpg -------------------------------------------------------------------------------- /src/math/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod color; 2 | pub mod euler; 3 | pub mod matrix3; 4 | pub mod matrix3gpu; 5 | pub mod matrix4; 6 | pub mod quaternion; 7 | pub mod vector3; -------------------------------------------------------------------------------- /web/examples/gltf/assets/Default_metalRoughness.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/assets/Default_metalRoughness.jpg -------------------------------------------------------------------------------- /src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod file_loader; 2 | pub mod log; 3 | pub mod geometry_helper; 4 | pub mod gltf_loader; 5 | pub mod material_helper; 6 | pub mod texture_loader; 7 | -------------------------------------------------------------------------------- /web/examples/cube/wgpu_rust_renderer_example_cube_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/cube/wgpu_rust_renderer_example_cube_bg.wasm -------------------------------------------------------------------------------- /web/examples/gltf/wgpu_rust_renderer_example_gltf_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/gltf/wgpu_rust_renderer_example_gltf_bg.wasm -------------------------------------------------------------------------------- /web/examples/texture/wgpu_rust_renderer_example_texture_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/texture/wgpu_rust_renderer_example_texture_bg.wasm -------------------------------------------------------------------------------- /web/examples/rotation/wgpu_rust_renderer_example_rotation_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/rotation/wgpu_rust_renderer_example_rotation_bg.wasm -------------------------------------------------------------------------------- /web/examples/triangle/wgpu_rust_renderer_example_triangle_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/triangle/wgpu_rust_renderer_example_triangle_bg.wasm -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod geometry; 2 | pub mod material; 3 | pub mod math; 4 | pub mod renderer; 5 | pub mod resource; 6 | pub mod scene; 7 | pub mod texture; 8 | pub mod utils; 9 | pub mod web; 10 | -------------------------------------------------------------------------------- /web/examples/face_culling/wgpu_rust_renderer_example_face_culling_bg.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takahirox/wgpu-rust-renderer/HEAD/web/examples/face_culling/wgpu_rust_renderer_example_face_culling_bg.wasm -------------------------------------------------------------------------------- /src/renderer/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod wgpu_attributes; 2 | pub mod wgpu_bindings; 3 | pub mod wgpu_indices; 4 | pub mod wgpu_render_pipeline; 5 | pub mod wgpu_renderer; 6 | pub mod wgpu_samplers; 7 | pub mod wgpu_textures; 8 | -------------------------------------------------------------------------------- /web/examples/icons/ic_arrow_drop_down_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/examples/icons/ic_menu_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /web/examples/cube/build.sh: -------------------------------------------------------------------------------- 1 | RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --no-default-features --release --lib --target wasm32-unknown-unknown 2 | wasm-bindgen ./target/wasm32-unknown-unknown/release/wgpu_rust_renderer_example_cube.wasm --out-dir ./ --target web --no-typescript 3 | -------------------------------------------------------------------------------- /web/examples/gltf/build.sh: -------------------------------------------------------------------------------- 1 | RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --no-default-features --release --lib --target wasm32-unknown-unknown 2 | wasm-bindgen ./target/wasm32-unknown-unknown/release/wgpu_rust_renderer_example_gltf.wasm --out-dir ./ --target web --no-typescript 3 | -------------------------------------------------------------------------------- /web/examples/texture/build.sh: -------------------------------------------------------------------------------- 1 | RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --no-default-features --release --lib --target wasm32-unknown-unknown 2 | wasm-bindgen ./target/wasm32-unknown-unknown/release/wgpu_rust_renderer_example_texture.wasm --out-dir ./ --target web --no-typescript 3 | -------------------------------------------------------------------------------- /web/examples/rotation/build.sh: -------------------------------------------------------------------------------- 1 | RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --no-default-features --release --lib --target wasm32-unknown-unknown 2 | wasm-bindgen ./target/wasm32-unknown-unknown/release/wgpu_rust_renderer_example_rotation.wasm --out-dir ./ --target web --no-typescript 3 | -------------------------------------------------------------------------------- /web/examples/triangle/build.sh: -------------------------------------------------------------------------------- 1 | RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --no-default-features --release --lib --target wasm32-unknown-unknown 2 | wasm-bindgen ./target/wasm32-unknown-unknown/release/wgpu_rust_renderer_example_triangle.wasm --out-dir ./ --target web --no-typescript 3 | -------------------------------------------------------------------------------- /web/examples/face_culling/build.sh: -------------------------------------------------------------------------------- 1 | RUSTFLAGS=--cfg=web_sys_unstable_apis cargo build --no-default-features --release --lib --target wasm32-unknown-unknown 2 | wasm-bindgen ./target/wasm32-unknown-unknown/release/wgpu_rust_renderer_example_face_culling.wasm --out-dir ./ --target web --no-typescript 3 | -------------------------------------------------------------------------------- /web/examples/icons/ic_close_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/utils/log.rs: -------------------------------------------------------------------------------- 1 | // Non-Wasm 2 | 3 | #[cfg(not(target_arch = "wasm32"))] 4 | pub fn log(s: &str) { 5 | println!("{}", s); 6 | } 7 | 8 | // Wasm 9 | 10 | #[cfg(target_arch = "wasm32")] 11 | use wasm_bindgen::prelude::*; 12 | 13 | #[cfg(target_arch = "wasm32")] 14 | #[wasm_bindgen] 15 | extern "C" { 16 | #[wasm_bindgen(js_namespace = console)] 17 | pub fn log(s: &str); 18 | } 19 | -------------------------------------------------------------------------------- /src/geometry/index.rs: -------------------------------------------------------------------------------- 1 | // @TODO: Should we reuse Attribute? 2 | 3 | pub struct Index { 4 | count: u32, 5 | data: Vec, 6 | } 7 | 8 | impl Index { 9 | pub fn new(data: Vec) -> Self { 10 | Index { 11 | count: data.len() as u32, 12 | data: data, 13 | } 14 | } 15 | 16 | pub fn get_count(&self) -> u32 { 17 | self.count 18 | } 19 | 20 | pub fn borrow_data(&self) -> &Vec { 21 | &self.data 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/material/node/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod add; 2 | pub mod brdf; 3 | pub mod const_float; 4 | pub mod const_vector3; 5 | pub mod float; 6 | pub mod linear_to_srgb; 7 | pub mod multiply; 8 | pub mod node; 9 | pub mod normal; 10 | pub mod normal_matrix; 11 | pub mod srgb_to_linear; 12 | pub mod sub; 13 | pub mod tangent_to_object_normal; 14 | pub mod texture; 15 | pub mod vector3; 16 | pub mod xyz; 17 | pub mod x; 18 | pub mod y; 19 | pub mod z; 20 | -------------------------------------------------------------------------------- /web/examples/icons/ic_search_black_24dp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /src/geometry/attribute.rs: -------------------------------------------------------------------------------- 1 | pub struct Attribute { 2 | count: u32, 3 | data: Vec, 4 | _item_size: u32, 5 | } 6 | 7 | impl Attribute { 8 | pub fn new(data: Vec, item_size: u32) -> Self { 9 | Attribute { 10 | count: data.len() as u32 / item_size, 11 | data: data, 12 | _item_size: item_size, 13 | } 14 | } 15 | 16 | pub fn get_count(&self) -> u32 { 17 | self.count 18 | } 19 | 20 | pub fn borrow_data(&self) -> &Vec { 21 | &self.data 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/math/color.rs: -------------------------------------------------------------------------------- 1 | const ELEMENT_NUM: usize = 3; 2 | type Elements = [f32; ELEMENT_NUM]; 3 | 4 | pub struct Color { 5 | } 6 | 7 | impl Color { 8 | pub fn create() -> Elements { 9 | [0.0; ELEMENT_NUM] 10 | } 11 | 12 | pub fn set(c: &mut Elements, r: f32, g: f32, b: f32) -> &mut Elements { 13 | c[0] = r; 14 | c[1] = g; 15 | c[2] = b; 16 | c 17 | } 18 | 19 | pub fn copy<'a>(c: &'a mut Elements, c2: &'a Elements) -> &'a mut Elements { 20 | for i in 0..ELEMENT_NUM { 21 | c[i] = c2[i]; 22 | } 23 | c 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /web/examples/cube/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer_example_cube" 3 | version = "0.1.0" 4 | authors = ["Takahiro "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.6" 14 | console_log = "0.1.2" 15 | wasm-bindgen = "0.2.78" 16 | wasm-bindgen-futures = "0.4.28" 17 | web-sys = "0.3.55" 18 | wgpu_rust_renderer = {path = "../../../"} 19 | 20 | [dependencies.winit] 21 | version = "0.25" 22 | features = ["web-sys"] 23 | -------------------------------------------------------------------------------- /web/examples/gltf/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer_example_gltf" 3 | version = "0.1.0" 4 | authors = ["Takahiro "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.6" 14 | console_log = "0.1.2" 15 | wasm-bindgen = "0.2.78" 16 | wasm-bindgen-futures = "0.4.28" 17 | web-sys = "0.3.55" 18 | wgpu_rust_renderer = {path = "../../../"} 19 | 20 | [dependencies.winit] 21 | version = "0.25" 22 | features = ["web-sys"] 23 | -------------------------------------------------------------------------------- /web/examples/rotation/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer_example_rotation" 3 | version = "0.1.0" 4 | authors = ["Takahiro "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.6" 14 | console_log = "0.1.2" 15 | wasm-bindgen = "0.2.78" 16 | wasm-bindgen-futures = "0.4.28" 17 | web-sys = "0.3.55" 18 | wgpu_rust_renderer = {path = "../../../"} 19 | 20 | [dependencies.winit] 21 | version = "0.25" 22 | features = ["web-sys"] 23 | -------------------------------------------------------------------------------- /web/examples/texture/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer_example_texture" 3 | version = "0.1.0" 4 | authors = ["Takahiro "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.6" 14 | console_log = "0.1.2" 15 | wasm-bindgen = "0.2.78" 16 | wasm-bindgen-futures = "0.4.28" 17 | web-sys = "0.3.55" 18 | wgpu_rust_renderer = {path = "../../../"} 19 | 20 | [dependencies.winit] 21 | version = "0.25" 22 | features = ["web-sys"] 23 | -------------------------------------------------------------------------------- /web/examples/triangle/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer_example_triangle" 3 | version = "0.1.0" 4 | authors = ["Takahiro "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.6" 14 | console_log = "0.1.2" 15 | wasm-bindgen = "0.2.78" 16 | wasm-bindgen-futures = "0.4.28" 17 | web-sys = "0.3.55" 18 | wgpu_rust_renderer = {path = "../../../"} 19 | 20 | [dependencies.winit] 21 | version = "0.25" 22 | features = ["web-sys"] 23 | -------------------------------------------------------------------------------- /web/examples/face_culling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer_example_face_culling" 3 | version = "0.1.0" 4 | authors = ["Takahiro "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [lib] 9 | path = "src/lib.rs" 10 | crate-type = ["cdylib"] 11 | 12 | [dependencies] 13 | console_error_panic_hook = "0.1.6" 14 | console_log = "0.1.2" 15 | wasm-bindgen = "0.2.78" 16 | wasm-bindgen-futures = "0.4.28" 17 | web-sys = "0.3.55" 18 | wgpu_rust_renderer = {path = "../../../"} 19 | 20 | [dependencies.winit] 21 | version = "0.25" 22 | features = ["web-sys"] 23 | -------------------------------------------------------------------------------- /src/scene/mesh.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | geometry::geometry::Geometry, 3 | material::material::Material, 4 | resource::resource::ResourceId, 5 | }; 6 | 7 | // @TODO: Support shared geometry and material 8 | pub struct Mesh { 9 | geometry: ResourceId, 10 | material: ResourceId, 11 | } 12 | 13 | impl Mesh { 14 | pub fn new( 15 | geometry: ResourceId, 16 | material: ResourceId, 17 | ) -> Self { 18 | Mesh { 19 | geometry: geometry, 20 | material: material, 21 | } 22 | } 23 | 24 | pub fn borrow_geometry(&self) -> &ResourceId { 25 | &self.geometry 26 | } 27 | 28 | pub fn borrow_material(&self) -> &ResourceId { 29 | &self.material 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/math/euler.rs: -------------------------------------------------------------------------------- 1 | const ELEMENT_NUM: usize = 3; 2 | type Elements = [f32; ELEMENT_NUM]; 3 | 4 | pub struct Euler { 5 | } 6 | 7 | impl Euler { 8 | pub fn create() -> Elements { 9 | [0.0; ELEMENT_NUM] 10 | } 11 | 12 | pub fn set_from_quaternion<'a>( 13 | e: &'a mut Elements, 14 | q: &'a [f32; 4], 15 | ) -> &'a mut Elements { 16 | // Assume XYZ order 17 | let q0 = q[0]; 18 | let q1 = q[1]; 19 | let q2 = q[2]; 20 | let q3 = q[3]; 21 | 22 | let q0q0 = q0 * q0; 23 | let q0q1 = q0 * q1; 24 | let q0q2 = q0 * q2; 25 | let q0q3 = q0 * q3; 26 | let q1q1 = q1 * q1; 27 | let q1q2 = q1 * q2; 28 | let q1q3 = q1 * q3; 29 | let q2q2 = q2 * q2; 30 | let q2q3 = q2 * q3; 31 | let q3q3 = q3 * q3; 32 | 33 | let roll = (2.0 * (q2q3 + q0q1)).atan2(q0q0 - q1q1 - q2q2 + q3q3); 34 | let pitch = (2.0 * (q0q2 - q1q3)).asin(); 35 | let yaw = (2.0 * (q1q2 + q0q3)).atan2(q0q0 + q1q1 - q2q2 - q3q3); 36 | 37 | e[0] = roll; 38 | e[1] = pitch; 39 | e[2] = yaw; 40 | 41 | e 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/texture/texture.rs: -------------------------------------------------------------------------------- 1 | // @TODO: Support more format 2 | pub enum TextureFormat { 3 | Float, 4 | Uint8, 5 | Uint8Srgb, 6 | } 7 | 8 | impl Default for TextureFormat { 9 | fn default() -> Self { 10 | TextureFormat::Uint8 11 | } 12 | } 13 | 14 | // @TODO: Support 3D texture 15 | pub struct Texture { 16 | format: TextureFormat, 17 | height: u32, 18 | texels: Vec, // @TODO: Support shared texels? 19 | width: u32, 20 | } 21 | 22 | impl Texture { 23 | pub fn new( 24 | width: u32, 25 | height: u32, 26 | format: TextureFormat, 27 | texels: Vec, 28 | ) -> Self { 29 | Texture { 30 | format: format, 31 | height: height, 32 | texels: texels, 33 | width: width, 34 | } 35 | } 36 | 37 | pub fn get_width(&self) -> u32 { 38 | self.width 39 | } 40 | 41 | pub fn get_height(&self) -> u32 { 42 | self.height 43 | } 44 | 45 | pub fn borrow_format(&self) -> &TextureFormat { 46 | &self.format 47 | } 48 | 49 | pub fn borrow_texels(&self) -> &Vec { 50 | &self.texels 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /web/examples/cube/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGPU Rust Renderer example - cube 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /web/examples/gltf/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGPU Rust Renderer example - glTF 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /web/examples/texture/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGPU Rust Renderer example - cube 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /web/examples/rotation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGPU Rust Renderer example - rotation 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /web/examples/triangle/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGPU Rust Renderer example - triangle 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /web/examples/face_culling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGPU Rust Renderer example - Face culling 4 | 5 | 6 | 7 | 13 | 14 | 15 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Takahiro 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/geometry/geometry.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | geometry::{ 5 | attribute::Attribute, 6 | index::Index, 7 | }, 8 | resource::resource::ResourceId, 9 | }; 10 | 11 | // @TODO: Support shared attribute 12 | pub struct Geometry { 13 | attributes: HashMap<&'static str, ResourceId>, 14 | index: Option>, 15 | } 16 | 17 | impl Geometry { 18 | pub fn new() -> Self { 19 | Geometry { 20 | attributes: HashMap::new(), 21 | index: None, 22 | } 23 | } 24 | 25 | pub fn set_attribute(&mut self, key: &'static str, attribute: ResourceId) -> &mut Self { 26 | self.attributes.insert(key, attribute); 27 | self 28 | } 29 | 30 | pub fn borrow_attribute(&self, key: &'static str) -> Option<&ResourceId> { 31 | self.attributes.get(key) 32 | } 33 | 34 | pub fn set_index(&mut self, index: ResourceId) -> &mut Self { 35 | self.index = Some(index); 36 | self 37 | } 38 | 39 | pub fn remove_index(&mut self) -> &mut Self { 40 | self.index = None; 41 | self 42 | } 43 | 44 | pub fn borrow_index(&self) -> Option<&ResourceId> { 45 | self.index.as_ref() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/math/matrix3gpu.rs: -------------------------------------------------------------------------------- 1 | const ELEMENT_NUM: usize = 12; 2 | type Elements = [f32; ELEMENT_NUM]; 3 | 4 | pub struct Matrix3GPU { 5 | } 6 | 7 | impl Matrix3GPU { 8 | pub fn create() -> Elements { 9 | let mut elements = [0.0; ELEMENT_NUM]; 10 | Self::identity(&mut elements); 11 | elements 12 | } 13 | 14 | pub fn identity(m: &mut Elements) -> &mut Elements { 15 | m[0] = 1.0; 16 | m[1] = 0.0; 17 | m[2] = 0.0; 18 | m[3] = 0.0; 19 | m[4] = 0.0; 20 | m[5] = 1.0; 21 | m[6] = 0.0; 22 | m[7] = 0.0; 23 | m[8] = 0.0; 24 | m[9] = 0.0; 25 | m[10] = 1.0; 26 | m[11] = 0.0; 27 | m 28 | } 29 | 30 | pub fn copy<'a>(m: &'a mut Elements, src: &'a Elements) -> &'a mut Elements { 31 | for i in 0..ELEMENT_NUM { 32 | m[i] = src[i]; 33 | } 34 | m 35 | } 36 | 37 | pub fn copy_from_matrix3<'a>(m: &'a mut Elements, src: &'a [f32; 9]) -> &'a mut Elements { 38 | // @TODO: Use loop? 39 | m[0] = src[0]; 40 | m[1] = src[1]; 41 | m[2] = src[2]; 42 | m[3] = 0.0; 43 | m[4] = src[3]; 44 | m[5] = src[4]; 45 | m[6] = src[5]; 46 | m[7] = 0.0; 47 | m[8] = src[6]; 48 | m[9] = src[7]; 49 | m[10] = src[8]; 50 | m[11] = 0.0; 51 | m 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/material/node/node.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | resource::resource::{ 4 | ResourceId, 5 | ResourcePool, 6 | }, 7 | texture::{ 8 | sampler::Sampler, 9 | texture::Texture, 10 | }, 11 | }; 12 | 13 | pub enum UniformContents { 14 | Float {value: [f32; 1]}, 15 | Matrix4 {value: [f32; 16]}, 16 | Vector3 {value: [f32; 3]}, 17 | Texture { 18 | texture: ResourceId, 19 | sampler: ResourceId, 20 | }, 21 | } 22 | 23 | pub trait MaterialNode { 24 | fn collect_nodes ( 25 | &self, 26 | pool: &ResourcePool>, 27 | nodes: &mut Vec>>, 28 | visited: &mut HashMap>, bool>, 29 | self_rid: ResourceId>, 30 | ); 31 | fn borrow_contents(&self) -> Option<&UniformContents>; 32 | fn build_declaration(&self, self_id: usize) -> String; 33 | fn build_functions(&self, self_id: usize) -> String; 34 | fn build_fragment_shader( 35 | &self, 36 | pool: &ResourcePool>, 37 | visited: &mut HashMap, 38 | self_id: usize, 39 | ) -> String; 40 | fn get_fragment_output(&self, self_id: usize) -> String; 41 | } 42 | 43 | // @TODO: Ensure unique variable names 44 | -------------------------------------------------------------------------------- /src/math/vector3.rs: -------------------------------------------------------------------------------- 1 | const ELEMENT_NUM: usize = 3; 2 | type Elements = [f32; ELEMENT_NUM]; 3 | 4 | pub struct Vector3 { 5 | } 6 | 7 | impl Vector3 { 8 | pub fn create() -> Elements { 9 | [0.0; ELEMENT_NUM] 10 | } 11 | 12 | pub fn set(v: &mut Elements, x: f32, y: f32, z: f32) -> &mut Elements { 13 | v[0] = x; 14 | v[1] = y; 15 | v[2] = z; 16 | v 17 | } 18 | 19 | pub fn copy<'a>(v: &'a mut Elements, v2: &'a Elements) -> &'a mut Elements { 20 | for i in 0..ELEMENT_NUM { 21 | v[i] = v2[i]; 22 | } 23 | v 24 | } 25 | 26 | pub fn add<'a>(v: &'a mut Elements, v2: &'a Elements) -> &'a mut Elements { 27 | for i in 0..ELEMENT_NUM { 28 | v[i] += v2[i]; 29 | } 30 | v 31 | } 32 | 33 | pub fn sub<'a>(v: &'a mut Elements, v2: &'a Elements) -> &'a mut Elements { 34 | for i in 0..ELEMENT_NUM { 35 | v[i] -= v2[i]; 36 | } 37 | v 38 | } 39 | 40 | pub fn length(v: &Elements) -> f32 { 41 | (v[0].powf(2.0) + v[1].powf(2.0) + v[2].powf(2.0)).sqrt() 42 | } 43 | 44 | pub fn normalize(v: &mut Elements) -> &mut Elements { 45 | let length = Self::length(v); 46 | 47 | // @TODO: Error handling? 48 | if length != 0.0 { 49 | for i in 0..ELEMENT_NUM { 50 | v[i] /= length; 51 | } 52 | } 53 | 54 | v 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/scene/camera.rs: -------------------------------------------------------------------------------- 1 | use crate::math::matrix4::Matrix4; 2 | 3 | pub struct PerspectiveCamera { 4 | aspect: f32, 5 | far: f32, 6 | fovy: f32, 7 | near: f32, 8 | projection_matrix: [f32; 16], 9 | projection_matrix_inverse: [f32; 16], 10 | } 11 | 12 | impl PerspectiveCamera { 13 | pub fn new(fovy: f32, aspect: f32, near: f32, far: f32) -> Self { 14 | let mut camera = PerspectiveCamera { 15 | aspect: aspect, 16 | far: far, 17 | fovy: fovy, 18 | near: near, 19 | projection_matrix: Matrix4::create(), 20 | projection_matrix_inverse: Matrix4::create(), 21 | }; 22 | camera.update_projection_matrix(); 23 | camera 24 | } 25 | 26 | pub fn set_aspect(&mut self, aspect: f32) -> &mut Self { 27 | self.aspect = aspect; 28 | self.update_projection_matrix(); 29 | self 30 | } 31 | 32 | pub fn update_projection_matrix(&mut self) { 33 | Matrix4::make_perspective( 34 | &mut self.projection_matrix, 35 | self.fovy, 36 | self.aspect, 37 | self.near, 38 | self.far, 39 | ); 40 | Matrix4::invert( 41 | Matrix4::copy(&mut self.projection_matrix_inverse, &self.projection_matrix) 42 | ); 43 | } 44 | 45 | pub fn borrow_projection_matrix(&self) -> &[f32; 16] { 46 | &self.projection_matrix 47 | } 48 | 49 | pub fn borrow_projection_matrix_inverse(&self) -> &[f32; 16] { 50 | &self.projection_matrix_inverse 51 | } 52 | } -------------------------------------------------------------------------------- /web/examples/utils/window.rs: -------------------------------------------------------------------------------- 1 | use wasm_bindgen::{ 2 | JsCast, 3 | prelude::*, 4 | }; 5 | use winit::{ 6 | event_loop::EventLoop, 7 | }; 8 | 9 | // Window and DOM element helpers 10 | 11 | pub fn get_window_inner_size() -> (f64, f64) { 12 | let window = web_sys::window().unwrap(); 13 | ( 14 | window.inner_width().unwrap().as_f64().unwrap(), 15 | window.inner_height().unwrap().as_f64().unwrap() 16 | ) 17 | } 18 | 19 | pub fn get_window_device_pixel_ratio() -> f64 { 20 | let window = web_sys::window().unwrap(); 21 | window.device_pixel_ratio() 22 | } 23 | 24 | pub fn create_window(event_loop: &EventLoop<()>) -> std::rc::Rc { 25 | let window = winit::window::Window::new(&event_loop).unwrap(); 26 | let window = std::rc::Rc::new(window); 27 | 28 | // winit::window::Window doesn't seem to detect browser's onresize event so we emulate it. 29 | { 30 | let window = window.clone(); 31 | let closure = Closure::wrap(Box::new(move |_e: web_sys::Event| { 32 | let size = get_window_inner_size(); 33 | window.set_inner_size(winit::dpi::PhysicalSize::new( 34 | size.0, size.1, 35 | )); 36 | }) as Box); 37 | web_sys::window() 38 | .unwrap() 39 | .add_event_listener_with_callback("resize", closure.as_ref().unchecked_ref()) 40 | .unwrap(); 41 | closure.forget(); 42 | } 43 | 44 | window 45 | } 46 | -------------------------------------------------------------------------------- /src/material/node/normal.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct NormalNode { 14 | } 15 | 16 | impl NormalNode { 17 | pub fn new() -> Self { 18 | NormalNode { 19 | } 20 | } 21 | } 22 | 23 | impl MaterialNode for NormalNode { 24 | fn collect_nodes ( 25 | &self, 26 | _pool: &ResourcePool>, 27 | nodes: &mut Vec>>, 28 | visited: &mut HashMap>, bool>, 29 | self_rid: ResourceId>, 30 | ) { 31 | if !visited.contains_key(&self_rid) { 32 | visited.insert(self_rid, true); 33 | nodes.push(self_rid); 34 | } 35 | } 36 | 37 | fn borrow_contents(&self) -> Option<&UniformContents> { 38 | None 39 | } 40 | 41 | fn build_declaration(&self, _self_id: usize) -> String { 42 | format!("") 43 | } 44 | 45 | fn build_functions(&self, _self_id: usize) -> String { 46 | format!("") 47 | } 48 | 49 | fn build_fragment_shader( 50 | &self, 51 | _pool: &ResourcePool>, 52 | _visited: &mut HashMap, 53 | _self_id: usize, 54 | ) -> String { 55 | format!("") 56 | } 57 | 58 | fn get_fragment_output(&self, _self_id: usize) -> String { 59 | format!("in.normal") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/renderer/wgpu_attributes.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{ 4 | geometry::attribute::Attribute, 5 | resource::resource::{ 6 | ResourceId, 7 | ResourcePools, 8 | }, 9 | }; 10 | 11 | pub struct WGPUAttributes { 12 | attributes: HashMap, wgpu::Buffer>, 13 | } 14 | 15 | impl WGPUAttributes { 16 | pub fn new() -> Self { 17 | WGPUAttributes { 18 | attributes: HashMap::new() 19 | } 20 | } 21 | 22 | pub fn borrow(&self, attribute: &ResourceId) -> Option<&wgpu::Buffer> { 23 | self.attributes.get(attribute) 24 | } 25 | 26 | // @TODO: Implement correctly 27 | pub fn update( 28 | &mut self, 29 | device: &wgpu::Device, 30 | pools: &ResourcePools, 31 | attribute_rid: &ResourceId, 32 | ) { 33 | if !self.attributes.contains_key(attribute_rid) { 34 | if let Some(attribute) = pools.borrow::().borrow(attribute_rid) { 35 | self.attributes.insert(*attribute_rid, create_buffer( 36 | device, 37 | bytemuck::cast_slice(attribute.borrow_data()), 38 | wgpu::BufferUsages::VERTEX, 39 | )); 40 | } 41 | } 42 | } 43 | } 44 | 45 | fn create_buffer(device: &wgpu::Device, contents: &[u8], usage: wgpu::BufferUsages) -> wgpu::Buffer { 46 | use wgpu::util::DeviceExt; 47 | device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 48 | label: None, 49 | contents: contents, 50 | usage: usage, 51 | }) 52 | } 53 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "wgpu_rust_renderer" 3 | version = "0.0.4" 4 | description = "Tiny WebGPU Renderer in Rust" 5 | authors = ["Takahiro "] 6 | edition = "2018" 7 | license = "MIT" 8 | resolver = "2" 9 | homepage = "https://github.com/takahirox/wgpu-rust-renderer" 10 | repository = "https://github.com/takahirox/wgpu-rust-renderer" 11 | exclude = [ 12 | "screenshots/*", 13 | "web/*" 14 | ] 15 | 16 | [dependencies] 17 | bytemuck = {version = "1.7.2", features = ["derive"]} 18 | futures = "0.3.17" 19 | gltf = "0.16.0" 20 | png = "0.17.1" 21 | wgpu = "0.11.0" 22 | 23 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies] 24 | jpeg-decoder = "0.1.22" 25 | winit = "0.25.0" 26 | 27 | [target.'cfg(target_arch = "wasm32")'.dependencies] 28 | jpeg-decoder = {version = "0.1.22", default-features = false} 29 | js-sys = "0.3.55" 30 | wasm-bindgen = "0.2.78" 31 | wasm-bindgen-futures = "0.4.28" 32 | winit = {version = "0.25.0", features = ["web-sys"]} 33 | 34 | [target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] 35 | version = "0.3.55" 36 | features = [ 37 | "CssStyleDeclaration", 38 | "HtmlCanvasElement", 39 | "Request", 40 | "RequestInit", 41 | "RequestMode", 42 | "Response", 43 | ] 44 | 45 | [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] 46 | tokio = {version = "1.12.0", features = ["full"]} 47 | 48 | [badges] 49 | travis-ci = {repository = "takahirox/wgpu-rust-renderer"} 50 | -------------------------------------------------------------------------------- /src/renderer/wgpu_indices.rs: -------------------------------------------------------------------------------- 1 | // @TODO: Should we reuse wgpu_attributes? 2 | 3 | use std::collections::HashMap; 4 | 5 | use crate::{ 6 | geometry::index::Index, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePools, 10 | }, 11 | }; 12 | 13 | pub struct WGPUIndices { 14 | indices: HashMap, wgpu::Buffer>, 15 | } 16 | 17 | impl WGPUIndices { 18 | pub fn new() -> Self { 19 | WGPUIndices { 20 | indices: HashMap::new() 21 | } 22 | } 23 | 24 | pub fn borrow(&self, index: &ResourceId) -> Option<&wgpu::Buffer> { 25 | self.indices.get(index) 26 | } 27 | 28 | // @TODO: Implement correctly 29 | pub fn update( 30 | &mut self, 31 | device: &wgpu::Device, 32 | pools: &ResourcePools, 33 | index_rid: &ResourceId, 34 | ) { 35 | if !self.indices.contains_key(index_rid) { 36 | if let Some(index) = pools.borrow::().borrow(index_rid) { 37 | self.indices.insert(*index_rid, create_buffer( 38 | device, 39 | bytemuck::cast_slice(index.borrow_data()), 40 | wgpu::BufferUsages::INDEX, 41 | )); 42 | } 43 | } 44 | } 45 | } 46 | 47 | // @TODO: Remove duplication with wgpu_attributes.rs 48 | fn create_buffer(device: &wgpu::Device, contents: &[u8], usage: wgpu::BufferUsages) -> wgpu::Buffer { 49 | use wgpu::util::DeviceExt; 50 | device.create_buffer_init(&wgpu::util::BufferInitDescriptor { 51 | label: None, 52 | contents: contents, 53 | usage: usage, 54 | }) 55 | } 56 | -------------------------------------------------------------------------------- /src/material/node/normal_matrix.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct NormalMatrixNode { 14 | } 15 | 16 | impl NormalMatrixNode { 17 | pub fn new() -> Self { 18 | NormalMatrixNode { 19 | } 20 | } 21 | } 22 | 23 | impl MaterialNode for NormalMatrixNode { 24 | fn collect_nodes ( 25 | &self, 26 | _pool: &ResourcePool>, 27 | nodes: &mut Vec>>, 28 | visited: &mut HashMap>, bool>, 29 | self_rid: ResourceId>, 30 | ) { 31 | if !visited.contains_key(&self_rid) { 32 | visited.insert(self_rid, true); 33 | nodes.push(self_rid); 34 | } 35 | } 36 | 37 | fn borrow_contents(&self) -> Option<&UniformContents> { 38 | None 39 | } 40 | 41 | fn build_declaration(&self, _self_id: usize) -> String { 42 | format!("") 43 | } 44 | 45 | fn build_functions(&self, _self_id: usize) -> String { 46 | format!("") 47 | } 48 | 49 | fn build_fragment_shader( 50 | &self, 51 | _pool: &ResourcePool>, 52 | _visited: &mut HashMap, 53 | _self_id: usize, 54 | ) -> String { 55 | format!("") 56 | } 57 | 58 | fn get_fragment_output(&self, _self_id: usize) -> String { 59 | format!("object.normal_matrix") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/material/node/const_float.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct ConstFloatNode { 14 | value: f32, 15 | } 16 | 17 | impl ConstFloatNode { 18 | pub fn new(value: f32) -> Self { 19 | ConstFloatNode { 20 | value: value, 21 | } 22 | } 23 | } 24 | 25 | impl MaterialNode for ConstFloatNode { 26 | fn collect_nodes ( 27 | &self, 28 | _pool: &ResourcePool>, 29 | nodes: &mut Vec>>, 30 | visited: &mut HashMap>, bool>, 31 | self_rid: ResourceId>, 32 | ) { 33 | if !visited.contains_key(&self_rid) { 34 | visited.insert(self_rid, true); 35 | nodes.push(self_rid); 36 | } 37 | } 38 | 39 | fn borrow_contents(&self) -> Option<&UniformContents> { 40 | None 41 | } 42 | 43 | fn build_declaration(&self, _self_id: usize) -> String { 44 | format!("") 45 | } 46 | 47 | fn build_functions(&self, _self_id: usize) -> String { 48 | format!("") 49 | } 50 | 51 | fn build_fragment_shader( 52 | &self, 53 | _pool: &ResourcePool>, 54 | _visited: &mut HashMap, 55 | _self_id: usize, 56 | ) -> String { 57 | format!("") 58 | } 59 | 60 | fn get_fragment_output(&self, _self_id: usize) -> String { 61 | format!("{:.16}", self.value) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/material/node/float.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct FloatNode { 14 | contents: UniformContents, 15 | } 16 | 17 | impl FloatNode { 18 | pub fn new(value: f32) -> Self { 19 | FloatNode { 20 | contents: UniformContents::Float { 21 | value: [value], 22 | }, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for FloatNode { 28 | fn collect_nodes ( 29 | &self, 30 | _pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | if !visited.contains_key(&self_rid) { 36 | visited.insert(self_rid, true); 37 | nodes.push(self_rid); 38 | } 39 | } 40 | 41 | fn borrow_contents(&self) -> Option<&UniformContents> { 42 | Some(&self.contents) 43 | } 44 | 45 | fn build_declaration(&self, self_id: usize) -> String { 46 | format!("f32_{}: f32;\n", self_id) 47 | } 48 | 49 | fn build_functions(&self, _self_id: usize) -> String { 50 | format!("") 51 | } 52 | 53 | fn build_fragment_shader( 54 | &self, 55 | _pool: &ResourcePool>, 56 | _visited: &mut HashMap, 57 | _self_id: usize, 58 | ) -> String { 59 | format!("") 60 | } 61 | 62 | fn get_fragment_output(&self, self_id: usize) -> String { 63 | format!("unif.f32_{}", self_id) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/material/node/const_vector3.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct ConstVector3Node { 14 | value: [f32; 3], 15 | } 16 | 17 | impl ConstVector3Node { 18 | pub fn new(value: [f32; 3]) -> Self { 19 | ConstVector3Node { 20 | value: value, 21 | } 22 | } 23 | } 24 | 25 | impl MaterialNode for ConstVector3Node { 26 | fn collect_nodes ( 27 | &self, 28 | _pool: &ResourcePool>, 29 | nodes: &mut Vec>>, 30 | visited: &mut HashMap>, bool>, 31 | self_rid: ResourceId>, 32 | ) { 33 | if !visited.contains_key(&self_rid) { 34 | visited.insert(self_rid, true); 35 | nodes.push(self_rid); 36 | } 37 | } 38 | 39 | fn borrow_contents(&self) -> Option<&UniformContents> { 40 | None 41 | } 42 | 43 | fn build_declaration(&self, _self_id: usize) -> String { 44 | format!("") 45 | } 46 | 47 | fn build_functions(&self, _self_id: usize) -> String { 48 | format!("") 49 | } 50 | 51 | fn build_fragment_shader( 52 | &self, 53 | _pool: &ResourcePool>, 54 | _visited: &mut HashMap, 55 | _self_id: usize, 56 | ) -> String { 57 | format!("") 58 | } 59 | 60 | fn get_fragment_output(&self, _self_id: usize) -> String { 61 | format!("vec3({}, {}, {})", 62 | self.value[0], 63 | self.value[1], 64 | self.value[2], 65 | ) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/material/node/vector3.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct Vector3Node { 14 | contents: UniformContents, 15 | } 16 | 17 | impl Vector3Node { 18 | pub fn new(value: [f32; 3]) -> Self { 19 | Vector3Node { 20 | contents: UniformContents::Vector3 { 21 | value: value, 22 | }, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for Vector3Node { 28 | fn collect_nodes ( 29 | &self, 30 | _pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | if !visited.contains_key(&self_rid) { 36 | visited.insert(self_rid, true); 37 | nodes.push(self_rid); 38 | } 39 | } 40 | 41 | fn borrow_contents(&self) -> Option<&UniformContents> { 42 | Some(&self.contents) 43 | } 44 | 45 | fn build_declaration(&self, self_id: usize) -> String { 46 | format!("vector3_{}: vec3;\n", self_id) 47 | } 48 | 49 | fn build_functions(&self, _self_id: usize) -> String { 50 | format!("") 51 | } 52 | 53 | fn build_fragment_shader( 54 | &self, 55 | _pool: &ResourcePool>, 56 | _visited: &mut HashMap, 57 | _self_id: usize, 58 | ) -> String { 59 | format!("") 60 | } 61 | 62 | fn get_fragment_output(&self, self_id: usize) -> String { 63 | format!("unif.vector3_{}", self_id) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/texture/sampler.rs: -------------------------------------------------------------------------------- 1 | pub enum WrapMode { 2 | ClampToBorder, 3 | ClampToEdge, 4 | MirrorRepeat, 5 | Repeat, 6 | } 7 | 8 | pub enum FilterMode { 9 | Linear, 10 | Nearest, 11 | } 12 | 13 | pub struct Sampler { 14 | mag_filter: FilterMode, 15 | min_filter: FilterMode, 16 | mipmap_filter: FilterMode, 17 | wrap_u: WrapMode, 18 | wrap_v: WrapMode, 19 | wrap_w: WrapMode, 20 | } 21 | 22 | pub struct SamplerDescriptor { 23 | pub mag_filter: FilterMode, 24 | pub min_filter: FilterMode, 25 | pub mipmap_filter: FilterMode, 26 | pub wrap_u: WrapMode, 27 | pub wrap_v: WrapMode, 28 | pub wrap_w: WrapMode, 29 | } 30 | 31 | impl Default for SamplerDescriptor { 32 | fn default() -> Self { 33 | SamplerDescriptor { 34 | mag_filter: FilterMode::Linear, 35 | min_filter: FilterMode::Linear, 36 | mipmap_filter: FilterMode::Linear, 37 | wrap_u: WrapMode::ClampToEdge, 38 | wrap_v: WrapMode::ClampToEdge, 39 | wrap_w: WrapMode::ClampToEdge, 40 | } 41 | } 42 | } 43 | 44 | impl Sampler { 45 | pub fn new(desc: SamplerDescriptor) -> Self { 46 | // @TODO: Fix default parameters 47 | Sampler { 48 | mag_filter: desc.mag_filter, 49 | min_filter: desc.min_filter, 50 | mipmap_filter: desc.mipmap_filter, 51 | wrap_u: desc.wrap_u, 52 | wrap_v: desc.wrap_v, 53 | wrap_w: desc.wrap_w, 54 | } 55 | } 56 | 57 | pub fn mag_filter(&self) -> &FilterMode { 58 | &self.mag_filter 59 | } 60 | 61 | pub fn min_filter(&self) -> &FilterMode { 62 | &self.min_filter 63 | } 64 | 65 | pub fn mipmap_filter(&self) -> &FilterMode { 66 | &self.mipmap_filter 67 | } 68 | 69 | pub fn wrap_u(&self) -> &WrapMode { 70 | &self.wrap_u 71 | } 72 | 73 | pub fn wrap_v(&self) -> &WrapMode { 74 | &self.wrap_v 75 | } 76 | 77 | pub fn wrap_w(&self) -> &WrapMode { 78 | &self.wrap_w 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/material/node/x.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct XNode { 14 | node: ResourceId>, 15 | } 16 | 17 | impl XNode { 18 | pub fn new( 19 | node: ResourceId>, 20 | ) -> Self { 21 | XNode { 22 | node: node, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for XNode { 28 | fn collect_nodes ( 29 | &self, 30 | pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | pool.borrow(&self.node).unwrap().collect_nodes( 36 | pool, nodes, visited, self.node, 37 | ); 38 | if !visited.contains_key(&self_rid) { 39 | visited.insert(self_rid, true); 40 | nodes.push(self_rid); 41 | } 42 | } 43 | 44 | fn borrow_contents(&self) -> Option<&UniformContents> { 45 | None 46 | } 47 | 48 | fn build_declaration(&self, _self_id: usize) -> String { 49 | format!("") 50 | } 51 | 52 | fn build_functions(&self, _self_id: usize) -> String { 53 | format!("") 54 | } 55 | 56 | fn build_fragment_shader( 57 | &self, 58 | pool: &ResourcePool>, 59 | visited: &mut HashMap, 60 | self_id: usize, 61 | ) -> String { 62 | if visited.contains_key(&self_id) { 63 | return "".to_string(); 64 | } 65 | visited.insert(self_id, true); 66 | 67 | let node = pool.borrow(&self.node).unwrap(); 68 | 69 | node.build_fragment_shader(pool, visited, self.node.id) + 70 | &format!("let {} = {}.x;\n", 71 | self.get_fragment_output(self_id), 72 | node.get_fragment_output(self.node.id), 73 | ) 74 | } 75 | 76 | fn get_fragment_output(&self, self_id: usize) -> String { 77 | format!("x_output_{}", self_id) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/material/node/y.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct YNode { 14 | node: ResourceId>, 15 | } 16 | 17 | impl YNode { 18 | pub fn new( 19 | node: ResourceId>, 20 | ) -> Self { 21 | YNode { 22 | node: node, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for YNode { 28 | fn collect_nodes ( 29 | &self, 30 | pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | pool.borrow(&self.node).unwrap().collect_nodes( 36 | pool, nodes, visited, self.node, 37 | ); 38 | if !visited.contains_key(&self_rid) { 39 | visited.insert(self_rid, true); 40 | nodes.push(self_rid); 41 | } 42 | } 43 | 44 | fn borrow_contents(&self) -> Option<&UniformContents> { 45 | None 46 | } 47 | 48 | fn build_declaration(&self, _self_id: usize) -> String { 49 | format!("") 50 | } 51 | 52 | fn build_functions(&self, _self_id: usize) -> String { 53 | format!("") 54 | } 55 | 56 | fn build_fragment_shader( 57 | &self, 58 | pool: &ResourcePool>, 59 | visited: &mut HashMap, 60 | self_id: usize, 61 | ) -> String { 62 | if visited.contains_key(&self_id) { 63 | return "".to_string(); 64 | } 65 | visited.insert(self_id, true); 66 | 67 | let node = pool.borrow(&self.node).unwrap(); 68 | 69 | node.build_fragment_shader(pool, visited, self.node.id) + 70 | &format!("let {} = {}.y;\n", 71 | self.get_fragment_output(self_id), 72 | node.get_fragment_output(self.node.id), 73 | ) 74 | } 75 | 76 | fn get_fragment_output(&self, self_id: usize) -> String { 77 | format!("y_output_{}", self_id) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/material/node/z.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct ZNode { 14 | node: ResourceId>, 15 | } 16 | 17 | impl ZNode { 18 | pub fn new( 19 | node: ResourceId>, 20 | ) -> Self { 21 | ZNode { 22 | node: node, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for ZNode { 28 | fn collect_nodes<'a> ( 29 | &self, 30 | pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | pool.borrow(&self.node).unwrap().collect_nodes( 36 | pool, nodes, visited, self.node, 37 | ); 38 | if !visited.contains_key(&self_rid) { 39 | visited.insert(self_rid, true); 40 | nodes.push(self_rid); 41 | } 42 | } 43 | 44 | fn borrow_contents(&self) -> Option<&UniformContents> { 45 | None 46 | } 47 | 48 | fn build_declaration(&self, _self_id: usize) -> String { 49 | format!("") 50 | } 51 | 52 | fn build_functions(&self, _self_id: usize) -> String { 53 | format!("") 54 | } 55 | 56 | fn build_fragment_shader( 57 | &self, 58 | pool: &ResourcePool>, 59 | visited: &mut HashMap, 60 | self_id: usize, 61 | ) -> String { 62 | if visited.contains_key(&self_id) { 63 | return "".to_string(); 64 | } 65 | visited.insert(self_id, true); 66 | 67 | let node = pool.borrow(&self.node).unwrap(); 68 | 69 | node.build_fragment_shader(pool, visited, self.node.id) + 70 | &format!("let {} = {}.z;\n", 71 | self.get_fragment_output(self_id), 72 | node.get_fragment_output(self.node.id), 73 | ) 74 | } 75 | 76 | fn get_fragment_output(&self, self_id: usize) -> String { 77 | format!("z_output_{}", self_id) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/material/node/xyz.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct XYZNode { 14 | node: ResourceId>, 15 | } 16 | 17 | impl XYZNode { 18 | pub fn new( 19 | node: ResourceId>, 20 | ) -> Self { 21 | XYZNode { 22 | node: node, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for XYZNode { 28 | fn collect_nodes ( 29 | &self, 30 | pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | pool.borrow(&self.node).unwrap().collect_nodes( 36 | pool, nodes, visited, self.node, 37 | ); 38 | if !visited.contains_key(&self_rid) { 39 | visited.insert(self_rid, true); 40 | nodes.push(self_rid); 41 | } 42 | } 43 | 44 | fn borrow_contents(&self) -> Option<&UniformContents> { 45 | None 46 | } 47 | 48 | fn build_declaration(&self, _self_id: usize) -> String { 49 | format!("") 50 | } 51 | 52 | fn build_functions(&self, _self_id: usize) -> String { 53 | format!("") 54 | } 55 | 56 | fn build_fragment_shader( 57 | &self, 58 | pool: &ResourcePool>, 59 | visited: &mut HashMap, 60 | self_id: usize, 61 | ) -> String { 62 | if visited.contains_key(&self_id) { 63 | return "".to_string(); 64 | } 65 | visited.insert(self_id, true); 66 | 67 | let node = pool.borrow(&self.node).unwrap(); 68 | 69 | node.build_fragment_shader(pool, visited, self.node.id) + 70 | &format!("let {} = {}.xyz;\n", 71 | self.get_fragment_output(self_id), 72 | node.get_fragment_output(self.node.id), 73 | ) 74 | } 75 | 76 | fn get_fragment_output(&self, self_id: usize) -> String { 77 | format!("xyz_output_{}", self_id) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/math/quaternion.rs: -------------------------------------------------------------------------------- 1 | const ELEMENT_NUM: usize = 4; 2 | type Elements = [f32; ELEMENT_NUM]; 3 | 4 | pub struct Quaternion { 5 | } 6 | 7 | impl Quaternion { 8 | pub fn create() -> Elements { 9 | [0.0, 0.0, 0.0, 1.0] 10 | } 11 | 12 | pub fn set_from_euler<'a>(q: &'a mut Elements, e: &'a [f32; 3]) -> &'a mut Elements { 13 | // Assume XYZ order 14 | let x = e[0]; 15 | let y = e[1]; 16 | let z = e[2]; 17 | 18 | let c1 = (x / 2.0).cos(); 19 | let c2 = (y / 2.0).cos(); 20 | let c3 = (z / 2.0).cos(); 21 | 22 | let s1 = (x / 2.0).sin(); 23 | let s2 = (y / 2.0).sin(); 24 | let s3 = (z / 2.0).sin(); 25 | 26 | q[0] = s1 * c2 * c3 + c1 * s2 * s3; 27 | q[1] = c1 * s2 * c3 - s1 * c2 * s3; 28 | q[2] = c1 * c2 * s3 + s1 * s2 * c3; 29 | q[3] = c1 * c2 * c3 - s1 * s2 * s3; 30 | 31 | q 32 | } 33 | 34 | pub fn set_from_rotation_matrix<'a>( 35 | q: &'a mut Elements, 36 | m: &'a [f32; 16], 37 | ) -> &'a mut Elements { 38 | let m11 = m[0]; 39 | let m12 = m[4]; 40 | let m13 = m[8]; 41 | let m21 = m[1]; 42 | let m22 = m[5]; 43 | let m23 = m[9]; 44 | let m31 = m[2]; 45 | let m32 = m[6]; 46 | let m33 = m[10]; 47 | 48 | let trace = m11 + m22 + m33; 49 | 50 | if trace > 0.0 { 51 | let s = 0.5 / (trace + 1.0).sqrt(); 52 | q[0] = (m32 - m23) * s; 53 | q[1] = (m13 - m31) * s; 54 | q[2] = (m21 - m12) * s; 55 | q[3] = 0.25 / s; 56 | } else if m11 > m22 && m11 > m33 { 57 | let s = 2.0 * (1.0 + m11 - m22 - m33).sqrt(); 58 | q[0] = 0.25 * s; 59 | q[1] = (m12 + m21) / s; 60 | q[2] = (m13 + m31) / s; 61 | q[3] = (m32 - m23) / s; 62 | } else if m22 > m33 { 63 | let s = 2.0 * (1.0 + m22 - m11 - m33).sqrt(); 64 | q[0] = (m12 + m21) / s; 65 | q[1] = 0.25 * s; 66 | q[2] = (m23 + m32) / s; 67 | q[3] = (m13 - m31) / s; 68 | } else { 69 | let s = 2.0 * (1.0 + m33 - m11 - m22).sqrt(); 70 | q[0] = (m13 + m31) / s; 71 | q[1] = (m23 + m32) / s; 72 | q[2] = 0.25 * s; 73 | q[3] = (m21 - m12) / s; 74 | } 75 | 76 | q 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/material/node/linear_to_srgb.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct LinearToSRGBNode { 14 | node: ResourceId>, 15 | } 16 | 17 | impl LinearToSRGBNode { 18 | pub fn new( 19 | node: ResourceId>, 20 | ) -> Self { 21 | LinearToSRGBNode { 22 | node: node, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for LinearToSRGBNode { 28 | fn collect_nodes ( 29 | &self, 30 | pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | pool.borrow(&self.node).unwrap().collect_nodes( 36 | pool, nodes, visited, self.node, 37 | ); 38 | if !visited.contains_key(&self_rid) { 39 | visited.insert(self_rid, true); 40 | nodes.push(self_rid); 41 | } 42 | } 43 | 44 | fn borrow_contents(&self) -> Option<&UniformContents> { 45 | None 46 | } 47 | 48 | fn build_declaration(&self, _self_id: usize) -> String { 49 | format!("") 50 | } 51 | 52 | fn build_functions(&self, _self_id: usize) -> String { 53 | format!("") 54 | } 55 | 56 | fn build_fragment_shader( 57 | &self, 58 | pool: &ResourcePool>, 59 | visited: &mut HashMap, 60 | self_id: usize, 61 | ) -> String { 62 | if visited.contains_key(&self_id) { 63 | return "".to_string(); 64 | } 65 | visited.insert(self_id, true); 66 | 67 | let node = pool.borrow(&self.node).unwrap(); 68 | 69 | node.build_fragment_shader(pool, visited, self.node.id) + 70 | &format!("let {} = linear_to_srgb({});\n", 71 | self.get_fragment_output(self_id), 72 | node.get_fragment_output(self.node.id), 73 | ) 74 | } 75 | 76 | fn get_fragment_output(&self, self_id: usize) -> String { 77 | format!("linear_to_srgb_output_{}", self_id) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/material/node/srgb_to_linear.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct SRGBToLinearNode { 14 | node: ResourceId>, 15 | } 16 | 17 | impl SRGBToLinearNode { 18 | pub fn new( 19 | node: ResourceId>, 20 | ) -> Self { 21 | SRGBToLinearNode { 22 | node: node, 23 | } 24 | } 25 | } 26 | 27 | impl MaterialNode for SRGBToLinearNode { 28 | fn collect_nodes ( 29 | &self, 30 | pool: &ResourcePool>, 31 | nodes: &mut Vec>>, 32 | visited: &mut HashMap>, bool>, 33 | self_rid: ResourceId>, 34 | ) { 35 | pool.borrow(&self.node).unwrap().collect_nodes( 36 | pool, nodes, visited, self.node, 37 | ); 38 | if !visited.contains_key(&self_rid) { 39 | visited.insert(self_rid, true); 40 | nodes.push(self_rid); 41 | } 42 | } 43 | 44 | fn borrow_contents(&self) -> Option<&UniformContents> { 45 | None 46 | } 47 | 48 | fn build_declaration(&self, _self_id: usize) -> String { 49 | format!("") 50 | } 51 | 52 | fn build_functions(&self, _self_id: usize) -> String { 53 | format!("") 54 | } 55 | 56 | fn build_fragment_shader( 57 | &self, 58 | pool: &ResourcePool>, 59 | visited: &mut HashMap, 60 | self_id: usize, 61 | ) -> String { 62 | if visited.contains_key(&self_id) { 63 | return "".to_string(); 64 | } 65 | visited.insert(self_id, true); 66 | 67 | let node = pool.borrow(&self.node).unwrap(); 68 | 69 | node.build_fragment_shader(pool, visited, self.node.id) + 70 | &format!("let {} = srgb_to_linear({});\n", 71 | self.get_fragment_output(self_id), 72 | node.get_fragment_output(self.node.id), 73 | ) 74 | } 75 | 76 | fn get_fragment_output(&self, self_id: usize) -> String { 77 | format!("srgb_to_linear_output_{}", self_id) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/utils/file_loader.rs: -------------------------------------------------------------------------------- 1 | pub struct FileLoader { 2 | } 3 | 4 | // Non-Wasm 5 | 6 | #[cfg(not(target_arch = "wasm32"))] 7 | use std::fs::File; 8 | 9 | #[cfg(not(target_arch = "wasm32"))] 10 | impl FileLoader { 11 | pub async fn open(file_path: &str) -> File { 12 | File::open(file_path).unwrap() 13 | } 14 | } 15 | 16 | // Wasm 17 | 18 | #[cfg(target_arch = "wasm32")] 19 | use { 20 | std::io::Cursor, 21 | wasm_bindgen::JsCast, 22 | wasm_bindgen_futures::JsFuture, 23 | web_sys::{ 24 | Request, 25 | RequestInit, 26 | RequestMode, 27 | Response, 28 | }, 29 | }; 30 | 31 | // @TODO: Proper error handling 32 | #[cfg(target_arch = "wasm32")] 33 | impl FileLoader { 34 | pub async fn open(file_path: &str) -> Cursor> { 35 | let result = fetch_as_binary(file_path).await.unwrap(); 36 | Cursor::new(result) 37 | } 38 | } 39 | 40 | // @TODO: Proper error handling 41 | #[cfg(target_arch = "wasm32")] 42 | pub async fn fetch_as_binary(url: &str) -> Result, String> { 43 | let mut opts = RequestInit::new(); 44 | opts.method("GET"); 45 | opts.mode(RequestMode::Cors); // @TODO: Should be able to opt-out 46 | 47 | let request = match Request::new_with_str_and_init(&url, &opts) { 48 | Ok(request) => request, 49 | Err(_e) => return Err("Failed to create request".to_string()), 50 | }; 51 | 52 | let window = web_sys::window().unwrap(); 53 | let response = match JsFuture::from(window.fetch_with_request(&request)).await { 54 | Ok(response) => response, 55 | Err(_e) => return Err("Failed to fetch".to_string()), 56 | }; 57 | 58 | let response: Response = match response.dyn_into() { 59 | Ok(response) => response, 60 | Err(_e) => return Err("Failed to dyn_into Response".to_string()), 61 | }; 62 | 63 | let buffer = match response.array_buffer() { 64 | Ok(buffer) => buffer, 65 | Err(_e) => return Err("Failed to get as array buffer".to_string()), 66 | }; 67 | 68 | let buffer = match JsFuture::from(buffer).await { 69 | Ok(buffer) => buffer, 70 | Err(_e) => return Err("Failed to ...?".to_string()), 71 | }; 72 | 73 | Ok(js_sys::Uint8Array::new(&buffer).to_vec()) 74 | } 75 | -------------------------------------------------------------------------------- /src/web/wgpu_web_renderer.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | use winit::window::Window; 3 | use crate::renderer::wgpu_renderer::{ 4 | WGPURenderer, 5 | WGPURendererOptions, 6 | }; 7 | 8 | pub struct WGPUWebRenderer { 9 | canvas: web_sys::HtmlCanvasElement, 10 | renderer: WGPURenderer, 11 | } 12 | 13 | // Using Deref/DerefMut for inheritance may be a bad design 14 | 15 | impl Deref for WGPUWebRenderer { 16 | type Target = WGPURenderer; 17 | fn deref(&self) -> &WGPURenderer { 18 | &self.renderer 19 | } 20 | } 21 | 22 | impl DerefMut for WGPUWebRenderer { 23 | fn deref_mut(&mut self) -> &mut WGPURenderer { 24 | &mut self.renderer 25 | } 26 | } 27 | 28 | impl WGPUWebRenderer { 29 | pub async fn new( 30 | window: &Window, 31 | canvas: web_sys::HtmlCanvasElement, 32 | options: WGPURendererOptions, 33 | ) -> WGPUWebRenderer { 34 | WGPUWebRenderer { 35 | canvas: canvas, 36 | renderer: WGPURenderer::new(window, options).await, 37 | } 38 | } 39 | 40 | pub fn borrow_canvas(&self) -> &web_sys::HtmlCanvasElement { 41 | &self.canvas 42 | } 43 | 44 | pub fn set_size( 45 | &mut self, 46 | width: f64, 47 | height: f64, 48 | ) { 49 | WGPURenderer::set_size(&mut self.renderer, width, height); 50 | update_canvas_size( 51 | &self.canvas, 52 | self.renderer.get_size(), 53 | self.renderer.get_pixel_ratio(), 54 | ); 55 | } 56 | 57 | pub fn set_pixel_ratio( 58 | &mut self, 59 | _pixel_ratio: f64, 60 | ) { 61 | // I don't know thy but pixel_ratio parameter needs to be 1.0 for Web. 62 | // Otherwise, crashes with 63 | // Attachment size mismatch 64 | // - While encoding BeginRenderPass([RenderPassDescriptor]). 65 | // error. 66 | // @TODO: Fix the root issue 67 | let pixel_ratio = 1.0; 68 | 69 | WGPURenderer::set_pixel_ratio(&mut self.renderer, pixel_ratio); 70 | update_canvas_size( 71 | &self.canvas, 72 | self.renderer.get_size(), 73 | self.renderer.get_pixel_ratio(), 74 | ); 75 | } 76 | } 77 | 78 | fn update_canvas_size( 79 | canvas: &web_sys::HtmlCanvasElement, 80 | (width, height): (f64, f64), 81 | pixel_ratio: f64 82 | ) { 83 | canvas.set_width((width * pixel_ratio) as u32); 84 | canvas.set_height((height * pixel_ratio) as u32); 85 | canvas.style().set_property("width", &((width as u32).to_string() + "px")).unwrap(); 86 | canvas.style().set_property("height", &((height as u32).to_string() + "px")).unwrap(); 87 | } 88 | -------------------------------------------------------------------------------- /src/material/node/add.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct AddNode { 14 | value1: ResourceId>, 15 | value2: ResourceId>, 16 | } 17 | 18 | impl AddNode { 19 | pub fn new( 20 | value1: ResourceId>, 21 | value2: ResourceId>, 22 | ) -> Self { 23 | AddNode { 24 | value1: value1, 25 | value2: value2, 26 | } 27 | } 28 | } 29 | 30 | impl MaterialNode for AddNode { 31 | fn collect_nodes ( 32 | &self, 33 | pool: &ResourcePool>, 34 | nodes: &mut Vec>>, 35 | visited: &mut HashMap>, bool>, 36 | self_rid: ResourceId>, 37 | ) { 38 | pool.borrow(&self.value1).unwrap().collect_nodes( 39 | pool, nodes, visited, self.value1, 40 | ); 41 | pool.borrow(&self.value2).unwrap().collect_nodes( 42 | pool, nodes, visited, self.value2, 43 | ); 44 | if !visited.contains_key(&self_rid) { 45 | visited.insert(self_rid, true); 46 | nodes.push(self_rid); 47 | } 48 | } 49 | 50 | fn borrow_contents(&self) -> Option<&UniformContents> { 51 | None 52 | } 53 | 54 | fn build_declaration(&self, _self_id: usize) -> String { 55 | format!("") 56 | } 57 | 58 | fn build_functions(&self, _self_id: usize) -> String { 59 | format!("") 60 | } 61 | 62 | fn build_fragment_shader( 63 | &self, 64 | pool: &ResourcePool>, 65 | visited: &mut HashMap, 66 | self_id: usize, 67 | ) -> String { 68 | if visited.contains_key(&self_id) { 69 | return "".to_string(); 70 | } 71 | visited.insert(self_id, true); 72 | 73 | let value1 = pool.borrow(&self.value1).unwrap(); 74 | let value2 = pool.borrow(&self.value2).unwrap(); 75 | 76 | value1.build_fragment_shader(pool, visited, self.value1.id) + 77 | &value2.build_fragment_shader(pool, visited, self.value2.id) + 78 | &format!("let {} = {} + {};\n", 79 | self.get_fragment_output(self_id), 80 | value1.get_fragment_output(self.value1.id), 81 | value2.get_fragment_output(self.value2.id), 82 | ) 83 | } 84 | 85 | fn get_fragment_output(&self, self_id: usize) -> String { 86 | format!("add_output_{}", self_id) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/material/node/sub.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct SubNode { 14 | value1: ResourceId>, 15 | value2: ResourceId>, 16 | } 17 | 18 | impl SubNode { 19 | pub fn new( 20 | value1: ResourceId>, 21 | value2: ResourceId>, 22 | ) -> Self { 23 | SubNode { 24 | value1: value1, 25 | value2: value2, 26 | } 27 | } 28 | } 29 | 30 | impl MaterialNode for SubNode { 31 | fn collect_nodes ( 32 | &self, 33 | pool: &ResourcePool>, 34 | nodes: &mut Vec>>, 35 | visited: &mut HashMap>, bool>, 36 | self_rid: ResourceId>, 37 | ) { 38 | pool.borrow(&self.value1).unwrap().collect_nodes( 39 | pool, nodes, visited, self.value1, 40 | ); 41 | pool.borrow(&self.value2).unwrap().collect_nodes( 42 | pool, nodes, visited, self.value2, 43 | ); 44 | if !visited.contains_key(&self_rid) { 45 | visited.insert(self_rid, true); 46 | nodes.push(self_rid); 47 | } 48 | } 49 | 50 | fn borrow_contents(&self) -> Option<&UniformContents> { 51 | None 52 | } 53 | 54 | fn build_declaration(&self, _self_id: usize) -> String { 55 | format!("") 56 | } 57 | 58 | fn build_functions(&self, _self_id: usize) -> String { 59 | format!("") 60 | } 61 | 62 | fn build_fragment_shader( 63 | &self, 64 | pool: &ResourcePool>, 65 | visited: &mut HashMap, 66 | self_id: usize, 67 | ) -> String { 68 | if visited.contains_key(&self_id) { 69 | return "".to_string(); 70 | } 71 | visited.insert(self_id, true); 72 | 73 | let value1 = pool.borrow(&self.value1).unwrap(); 74 | let value2 = pool.borrow(&self.value2).unwrap(); 75 | 76 | value1.build_fragment_shader(pool, visited, self.value1.id) + 77 | &value2.build_fragment_shader(pool, visited, self.value2.id) + 78 | &format!("let {} = {} - {};\n", 79 | self.get_fragment_output(self_id), 80 | value1.get_fragment_output(self.value1.id), 81 | value2.get_fragment_output(self.value2.id), 82 | ) 83 | } 84 | 85 | fn get_fragment_output(&self, self_id: usize) -> String { 86 | format!("sub_output_{}", self_id) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/math/matrix3.rs: -------------------------------------------------------------------------------- 1 | const ELEMENT_NUM: usize = 9; 2 | type Elements = [f32; ELEMENT_NUM]; 3 | 4 | pub struct Matrix3 { 5 | } 6 | 7 | impl Matrix3 { 8 | pub fn create() -> Elements { 9 | let mut elements = [0.0; ELEMENT_NUM]; 10 | Self::identity(&mut elements); 11 | elements 12 | } 13 | 14 | pub fn identity(m: &mut Elements) -> &mut Elements { 15 | m[0] = 1.0; 16 | m[1] = 0.0; 17 | m[2] = 0.0; 18 | m[3] = 0.0; 19 | m[4] = 1.0; 20 | m[5] = 0.0; 21 | m[6] = 0.0; 22 | m[7] = 0.0; 23 | m[8] = 1.0; 24 | m 25 | } 26 | 27 | pub fn copy<'a>(m: &'a mut Elements, src: &'a Elements) -> &'a mut Elements { 28 | for i in 0..ELEMENT_NUM { 29 | m[i] = src[i]; 30 | } 31 | m 32 | } 33 | 34 | pub fn make_normal_from_matrix4<'a>( 35 | m: &'a mut Elements, 36 | src: &'a [f32; 16], 37 | ) -> &'a mut Elements { 38 | let a00 = src[0]; 39 | let a01 = src[1]; 40 | let a02 = src[2]; 41 | let a03 = src[3]; 42 | let a10 = src[4]; 43 | let a11 = src[5]; 44 | let a12 = src[6]; 45 | let a13 = src[7]; 46 | let a20 = src[8]; 47 | let a21 = src[9]; 48 | let a22 = src[10]; 49 | let a23 = src[11]; 50 | let a30 = src[12]; 51 | let a31 = src[13]; 52 | let a32 = src[14]; 53 | let a33 = src[15]; 54 | 55 | let b00 = a00 * a11 - a01 * a10; 56 | let b01 = a00 * a12 - a02 * a10; 57 | let b02 = a00 * a13 - a03 * a10; 58 | let b03 = a01 * a12 - a02 * a11; 59 | let b04 = a01 * a13 - a03 * a11; 60 | let b05 = a02 * a13 - a03 * a12; 61 | let b06 = a20 * a31 - a21 * a30; 62 | let b07 = a20 * a32 - a22 * a30; 63 | let b08 = a20 * a33 - a23 * a30; 64 | let b09 = a21 * a32 - a22 * a31; 65 | let b10 = a21 * a33 - a23 * a31; 66 | let b11 = a22 * a33 - a23 * a32; 67 | 68 | let det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; 69 | 70 | if det == 0.0 { 71 | // @TODO: Error handling? 72 | return m; 73 | } 74 | 75 | let det = 1.0 / det; 76 | m[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det; 77 | m[1] = (a12 * b08 - a10 * b11 - a13 * b07) * det; 78 | m[2] = (a10 * b10 - a11 * b08 + a13 * b06) * det; 79 | m[3] = (a02 * b10 - a01 * b11 - a03 * b09) * det; 80 | m[4] = (a00 * b11 - a02 * b08 + a03 * b07) * det; 81 | m[5] = (a01 * b08 - a00 * b10 - a03 * b06) * det; 82 | m[6] = (a31 * b05 - a32 * b04 + a33 * b03) * det; 83 | m[7] = (a32 * b02 - a30 * b05 - a33 * b01) * det; 84 | m[8] = (a30 * b04 - a31 * b02 + a33 * b00) * det; 85 | 86 | m 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/material/node/multiply.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | pub struct MultiplyNode { 14 | value1: ResourceId>, 15 | value2: ResourceId>, 16 | } 17 | 18 | impl MultiplyNode { 19 | pub fn new( 20 | value1: ResourceId>, 21 | value2: ResourceId>, 22 | ) -> Self { 23 | MultiplyNode { 24 | value1: value1, 25 | value2: value2, 26 | } 27 | } 28 | } 29 | 30 | impl MaterialNode for MultiplyNode { 31 | fn collect_nodes ( 32 | &self, 33 | pool: &ResourcePool>, 34 | nodes: &mut Vec>>, 35 | visited: &mut HashMap>, bool>, 36 | self_rid: ResourceId>, 37 | ) { 38 | pool.borrow(&self.value1).unwrap().collect_nodes( 39 | pool, nodes, visited, self.value1, 40 | ); 41 | pool.borrow(&self.value2).unwrap().collect_nodes( 42 | pool, nodes, visited, self.value2, 43 | ); 44 | if !visited.contains_key(&self_rid) { 45 | visited.insert(self_rid, true); 46 | nodes.push(self_rid); 47 | } 48 | } 49 | 50 | fn borrow_contents(&self) -> Option<&UniformContents> { 51 | None 52 | } 53 | 54 | fn build_declaration(&self, _self_id: usize) -> String { 55 | format!("") 56 | } 57 | 58 | fn build_functions(&self, _self_id: usize) -> String { 59 | format!("") 60 | } 61 | 62 | fn build_fragment_shader( 63 | &self, 64 | pool: &ResourcePool>, 65 | visited: &mut HashMap, 66 | self_id: usize, 67 | ) -> String { 68 | if visited.contains_key(&self_id) { 69 | return "".to_string(); 70 | } 71 | visited.insert(self_id, true); 72 | 73 | let value1 = pool.borrow(&self.value1).unwrap(); 74 | let value2 = pool.borrow(&self.value2).unwrap(); 75 | 76 | value1.build_fragment_shader(pool, visited, self.value1.id) + 77 | &value2.build_fragment_shader(pool, visited, self.value2.id) + 78 | &format!("let {} = {} * {};\n", 79 | self.get_fragment_output(self_id), 80 | value1.get_fragment_output(self.value1.id), 81 | value2.get_fragment_output(self.value2.id), 82 | ) 83 | } 84 | 85 | fn get_fragment_output(&self, self_id: usize) -> String { 86 | format!("multiply_output_{}", self_id) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/material/node/texture.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | texture::{ 12 | sampler::Sampler, 13 | texture::Texture, 14 | } 15 | }; 16 | 17 | pub struct TextureNode { 18 | contents: UniformContents, 19 | } 20 | 21 | impl TextureNode { 22 | pub fn new( 23 | texture: ResourceId, 24 | sampler: ResourceId, 25 | ) -> Self { 26 | TextureNode { 27 | contents: UniformContents::Texture { 28 | sampler: sampler, 29 | texture: texture, 30 | }, 31 | } 32 | } 33 | 34 | fn get_texture_name(&self) -> String { 35 | match self.contents { 36 | UniformContents::Texture{texture, ..} => { 37 | format!("texture_{}", texture.id) 38 | }, 39 | _ => panic!(), 40 | } 41 | } 42 | 43 | fn get_sampler_name(&self) -> String { 44 | match self.contents { 45 | UniformContents::Texture{sampler, ..} => { 46 | format!("sampler_{}", sampler.id) 47 | }, 48 | _ => panic!(), 49 | } 50 | } 51 | } 52 | 53 | impl MaterialNode for TextureNode { 54 | fn collect_nodes ( 55 | &self, 56 | _pool: &ResourcePool>, 57 | nodes: &mut Vec>>, 58 | visited: &mut HashMap>, bool>, 59 | self_rid: ResourceId>, 60 | ) { 61 | if !visited.contains_key(&self_rid) { 62 | visited.insert(self_rid, true); 63 | nodes.push(self_rid); 64 | } 65 | } 66 | 67 | fn borrow_contents(&self) -> Option<&UniformContents> { 68 | Some(&self.contents) 69 | } 70 | 71 | fn build_declaration(&self, _self_id: usize) -> String { 72 | format!("") 73 | } 74 | 75 | fn build_functions(&self, _self_id: usize) -> String { 76 | format!("") 77 | } 78 | 79 | fn build_fragment_shader( 80 | &self, 81 | _pool: &ResourcePool>, 82 | visited: &mut HashMap, 83 | self_id: usize, 84 | ) -> String { 85 | if visited.contains_key(&self_id) { 86 | return "".to_string(); 87 | } 88 | visited.insert(self_id, true); 89 | 90 | format!("let {} = textureSample({}, {}, in.uv);\n", 91 | self.get_fragment_output(self_id), 92 | self.get_texture_name(), 93 | self.get_sampler_name(), 94 | ) 95 | } 96 | 97 | fn get_fragment_output(&self, self_id: usize) -> String { 98 | format!("texture_output_{}", self_id) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/renderer/wgpu_samplers.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::{ 4 | material::Material, 5 | node::node::MaterialNode, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePools, 10 | }, 11 | texture::sampler::{ 12 | FilterMode, 13 | Sampler, 14 | WrapMode, 15 | }, 16 | }; 17 | 18 | pub struct WGPUSamplers { 19 | samplers: HashMap, wgpu::Sampler>, 20 | } 21 | 22 | impl WGPUSamplers { 23 | pub fn new() -> Self { 24 | WGPUSamplers { 25 | samplers: HashMap::new(), 26 | } 27 | } 28 | 29 | pub fn borrow(&self, sampler: &ResourceId) -> Option<&wgpu::Sampler> { 30 | self.samplers.get(sampler) 31 | } 32 | 33 | // @TODO: Implement correctly 34 | fn update( 35 | &mut self, 36 | device: &wgpu::Device, 37 | pools: &ResourcePools, 38 | sampler_rid: &ResourceId, 39 | ) { 40 | if !self.samplers.contains_key(sampler_rid) { 41 | if let Some(sampler) = pools.borrow::().borrow(sampler_rid) { 42 | let sampler_gpu = create_sampler( 43 | device, 44 | sampler, 45 | ); 46 | self.samplers.insert(*sampler_rid, sampler_gpu); 47 | } 48 | } 49 | } 50 | 51 | pub fn update_from_material( 52 | &mut self, 53 | device: &wgpu::Device, 54 | pools: &ResourcePools, 55 | material: &Material, 56 | ) { 57 | let samplers = material.borrow_samplers( 58 | pools.borrow::>(), 59 | ); 60 | for sampler in samplers.iter() { 61 | self.update(device, pools, sampler); 62 | } 63 | } 64 | } 65 | 66 | fn create_sampler( 67 | device: &wgpu::Device, 68 | sampler: &Sampler, 69 | ) -> wgpu::Sampler { 70 | // @TODO: Fix me 71 | device.create_sampler(&wgpu::SamplerDescriptor { 72 | address_mode_u: get_address_mode(sampler.wrap_u()), 73 | address_mode_v: get_address_mode(sampler.wrap_v()), 74 | address_mode_w: get_address_mode(sampler.wrap_w()), 75 | anisotropy_clamp: None, 76 | border_color: None, 77 | compare: None, 78 | mag_filter: get_filter_mode(sampler.mag_filter()), 79 | min_filter: get_filter_mode(sampler.min_filter()), 80 | mipmap_filter: get_filter_mode(sampler.mipmap_filter()), 81 | label: None, 82 | lod_max_clamp: 0.0, 83 | lod_min_clamp: 0.0, 84 | }) 85 | } 86 | 87 | fn get_address_mode(mode: &WrapMode) -> wgpu::AddressMode { 88 | match mode { 89 | WrapMode::ClampToBorder => wgpu::AddressMode::ClampToBorder, 90 | WrapMode::ClampToEdge => wgpu::AddressMode::ClampToEdge, 91 | WrapMode::MirrorRepeat => wgpu::AddressMode::MirrorRepeat, 92 | WrapMode::Repeat => wgpu::AddressMode::Repeat, 93 | } 94 | } 95 | 96 | fn get_filter_mode(mode: &FilterMode) -> wgpu::FilterMode { 97 | match mode { 98 | FilterMode::Nearest => wgpu::FilterMode::Nearest, 99 | FilterMode::Linear => wgpu::FilterMode::Linear, 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/material/node/tangent_to_object_normal.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | const FUNCTION_CHUNK: &str = " 14 | fn perturb_normal_to_arb( 15 | eye_pos: vec3, 16 | surf_norm: vec3, 17 | map_n: vec3, 18 | uv: vec2 19 | ) -> vec3 { 20 | let q0: vec3 = vec3(dpdx(eye_pos.x), dpdx(eye_pos.y), dpdx(eye_pos.z)); 21 | let q1: vec3 = vec3(dpdy(eye_pos.x), dpdy(eye_pos.y), dpdy(eye_pos.z)); 22 | let st0: vec2 = dpdx(uv); 23 | let st1: vec2 = dpdy(uv); 24 | let n: vec3 = surf_norm; // normalized 25 | let q1perp: vec3 = cross(q1, n); 26 | let q0perp: vec3 = cross(n, q0); 27 | let t = q1perp * st0.x + q0perp * st1.x; 28 | let b = q1perp * st0.y + q0perp * st1.y; 29 | let det: f32 = max(dot(t, t), dot(b, b)); 30 | var scale: f32; 31 | if (det == 0.0) { 32 | scale = 0.0; 33 | } else { 34 | scale = inverseSqrt(det); 35 | } 36 | return normalize(t * (map_n.x * scale) + b * (map_n.y * scale) + n * map_n.z); 37 | } 38 | "; 39 | 40 | pub struct TangentToObjectNormalNode { 41 | node: ResourceId>, 42 | } 43 | 44 | impl TangentToObjectNormalNode { 45 | pub fn new( 46 | node: ResourceId>, 47 | ) -> Self { 48 | TangentToObjectNormalNode { 49 | node: node, 50 | } 51 | } 52 | } 53 | 54 | impl MaterialNode for TangentToObjectNormalNode { 55 | fn collect_nodes ( 56 | &self, 57 | pool: &ResourcePool>, 58 | nodes: &mut Vec>>, 59 | visited: &mut HashMap>, bool>, 60 | self_rid: ResourceId>, 61 | ) { 62 | pool.borrow(&self.node).unwrap().collect_nodes( 63 | pool, nodes, visited, self.node, 64 | ); 65 | if !visited.contains_key(&self_rid) { 66 | visited.insert(self_rid, true); 67 | nodes.push(self_rid); 68 | } 69 | } 70 | 71 | fn borrow_contents(&self) -> Option<&UniformContents> { 72 | None 73 | } 74 | 75 | fn build_declaration(&self, _self_id: usize) -> String { 76 | format!("") 77 | } 78 | 79 | fn build_functions(&self, _self_id: usize) -> String { 80 | // @TODO: Add self_id suffix for unique function name 81 | FUNCTION_CHUNK.to_string() 82 | } 83 | 84 | fn build_fragment_shader( 85 | &self, 86 | pool: &ResourcePool>, 87 | visited: &mut HashMap, 88 | self_id: usize, 89 | ) -> String { 90 | if visited.contains_key(&self_id) { 91 | return "".to_string(); 92 | } 93 | visited.insert(self_id, true); 94 | 95 | let node = pool.borrow(&self.node).unwrap(); 96 | 97 | node.build_fragment_shader(pool, visited, self.node.id) + 98 | &format!("let {} = perturb_normal_to_arb(-in.view_position, in.normal, {}, in.uv);\n", 99 | self.get_fragment_output(self_id), 100 | node.get_fragment_output(self.node.id), 101 | ) 102 | } 103 | 104 | fn get_fragment_output(&self, self_id: usize) -> String { 105 | format!("tangent_to_object_normal_output_{}", self_id) 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/utils/material_helper.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | material::{ 3 | material::{ 4 | Material, 5 | Side, 6 | }, 7 | node::{ 8 | brdf::{ 9 | BRDFNode, 10 | BRDFNodeDescriptor, 11 | }, 12 | float::FloatNode, 13 | multiply::MultiplyNode, 14 | node::MaterialNode, 15 | normal::NormalNode, 16 | texture::TextureNode, 17 | vector3::Vector3Node, 18 | xyz::XYZNode, 19 | }, 20 | }, 21 | math::color::Color, 22 | resource::resource::{ 23 | ResourceId, 24 | ResourcePools, 25 | }, 26 | texture::{ 27 | sampler::{ 28 | Sampler, 29 | SamplerDescriptor, 30 | }, 31 | texture::Texture, 32 | }, 33 | }; 34 | 35 | pub struct MaterialHelper { 36 | } 37 | 38 | impl MaterialHelper { 39 | pub fn create_basic_material( 40 | pools: &mut ResourcePools, 41 | color: &[f32; 3], 42 | ) -> ResourceId { 43 | let color_node = pools.borrow_mut::>().add( 44 | Box::new(Vector3Node::new( 45 | *Color::copy(&mut Color::create(), color), 46 | )), 47 | ); 48 | 49 | pools.borrow_mut::().add(Material::new(color_node, Side::default())) 50 | } 51 | 52 | pub fn create_basic_material_with_texture( 53 | pools: &mut ResourcePools, 54 | color: &[f32; 3], 55 | texture: ResourceId, 56 | ) -> ResourceId { 57 | let color = pools.borrow_mut::>().add( 58 | Box::new(Vector3Node::new( 59 | *Color::copy(&mut Color::create(), color), 60 | ), 61 | )); 62 | 63 | let sampler = pools.borrow_mut::().add( 64 | Sampler::new(SamplerDescriptor::default()), 65 | ); 66 | 67 | let texture = pools.borrow_mut::>().add( 68 | Box::new(TextureNode::new(texture, sampler)), 69 | ); 70 | 71 | let texture_rgb = pools.borrow_mut::>().add( 72 | Box::new(XYZNode::new(texture)), 73 | ); 74 | 75 | let color_node = pools.borrow_mut::>().add( 76 | Box::new(MultiplyNode::new(color, texture_rgb)), 77 | ); 78 | 79 | pools.borrow_mut::().add(Material::new(color_node, Side::default())) 80 | } 81 | 82 | pub fn create_brdf_material( 83 | pools: &mut ResourcePools, 84 | color: &[f32; 3], 85 | metallic: f32, 86 | roughness: f32, 87 | ) -> ResourceId { 88 | let base_color = pools.borrow_mut::>().add( 89 | Box::new(Vector3Node::new( 90 | *Color::copy(&mut Color::create(), color), 91 | )), 92 | ); 93 | 94 | let metallic = pools.borrow_mut::>().add( 95 | Box::new(FloatNode::new(metallic)), 96 | ); 97 | 98 | let roughness = pools.borrow_mut::>().add( 99 | Box::new(FloatNode::new(roughness)), 100 | ); 101 | 102 | let normal = pools.borrow_mut::>().add( 103 | Box::new(NormalNode::new()), 104 | ); 105 | 106 | let desc = BRDFNodeDescriptor { 107 | base_color: base_color, 108 | metallic: metallic, 109 | normal: normal, 110 | roughness: roughness, 111 | }; 112 | 113 | let brdf_node = pools.borrow_mut::>().add( 114 | Box::new(BRDFNode::new(desc)), 115 | ); 116 | 117 | pools.borrow_mut::().add(Material::new(brdf_node, Side::default())) 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/utils/texture_loader.rs: -------------------------------------------------------------------------------- 1 | use crate::utils::file_loader::FileLoader; 2 | 3 | use crate::{ 4 | resource::resource::{ 5 | ResourceId, 6 | ResourcePools, 7 | }, 8 | texture::{ 9 | texture::{ 10 | Texture, 11 | TextureFormat, 12 | }, 13 | }, 14 | }; 15 | 16 | pub struct TextureLoader { 17 | } 18 | 19 | impl TextureLoader { 20 | pub fn load_png( 21 | pools: &mut ResourcePools, 22 | reader: R, 23 | // @TODO: Should use default rather than Option? 24 | format: TextureFormat, 25 | ) -> ResourceId { 26 | let decoder = png::Decoder::new(reader); 27 | let mut reader = decoder.read_info().unwrap(); 28 | let (width, height) = { 29 | let info = reader.info(); 30 | (info.width, info.height) 31 | }; 32 | let mut buf = vec![0; reader.output_buffer_size()]; 33 | reader.next_frame(&mut buf).unwrap(); 34 | 35 | pools.borrow_mut::().add( 36 | Texture::new( 37 | width, 38 | height, 39 | format, 40 | buf, 41 | ) 42 | ) 43 | } 44 | 45 | pub async fn load_png_with_filepath( 46 | pools: &mut ResourcePools, 47 | file_path: &str, 48 | format: TextureFormat, 49 | ) -> ResourceId { 50 | Self::load_png(pools, FileLoader::open(file_path).await, format) 51 | } 52 | 53 | pub fn load_jpg( 54 | pools: &mut ResourcePools, 55 | reader: R, 56 | format: TextureFormat, 57 | ) -> ResourceId { 58 | let mut decoder = jpeg_decoder::Decoder::new(reader); 59 | let pixels = decoder.decode().expect("failed to decode image"); 60 | let (width, height) = { 61 | let metadata = decoder.info().unwrap(); 62 | (metadata.width as u32, metadata.height as u32) 63 | }; 64 | 65 | // @TODO: Fix me 66 | let mut data = Vec::new(); 67 | for y in 0..height as usize { 68 | for x in 0..width as usize { 69 | data.push(pixels[(y * width as usize + x) * 3 + 0]); 70 | data.push(pixels[(y * width as usize + x) * 3 + 1]); 71 | data.push(pixels[(y * width as usize + x) * 3 + 2]); 72 | data.push(255); 73 | } 74 | } 75 | 76 | pools.borrow_mut::().add( 77 | Texture::new( 78 | width, 79 | height, 80 | format, 81 | data, 82 | ) 83 | ) 84 | } 85 | 86 | pub async fn load_jpg_with_filepath( 87 | pools: &mut ResourcePools, 88 | file_path: &str, 89 | format: TextureFormat, 90 | ) -> ResourceId { 91 | Self::load_jpg(pools, FileLoader::open(file_path).await, format) 92 | } 93 | 94 | pub async fn load_with_filepath( 95 | pools: &mut ResourcePools, 96 | file_path: &str, 97 | format: TextureFormat, 98 | ) -> ResourceId { 99 | let path = std::path::Path::new(file_path); 100 | // @TODO: proper error handling 101 | match path.extension() { 102 | Some(extension) => match extension.to_str() { 103 | Some(str) => match str.to_lowercase().as_str() { 104 | "png" => Self::load_png_with_filepath(pools, file_path, format).await, 105 | "jpg" | "jpeg" => Self::load_jpg_with_filepath(pools, file_path, format).await, 106 | _ => panic!("Unknown texture image format, {:?}", extension), 107 | }, 108 | None => panic!("Can not detect image file format from the file path, {}", file_path), 109 | }, 110 | None => panic!("Can not detect image file format from the file path, {}", file_path), 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/renderer/wgpu_textures.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::{ 4 | material::Material, 5 | node::node::MaterialNode, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePools, 10 | }, 11 | texture::texture::{ 12 | Texture, 13 | TextureFormat, 14 | }, 15 | }; 16 | 17 | pub struct WGPUTextures { 18 | textures: HashMap, wgpu::Texture>, 19 | } 20 | 21 | impl WGPUTextures { 22 | pub fn new() -> WGPUTextures { 23 | WGPUTextures { 24 | textures: HashMap::new(), 25 | } 26 | } 27 | 28 | pub fn borrow(&self, texture: &ResourceId) -> Option<&wgpu::Texture> { 29 | self.textures.get(texture) 30 | } 31 | 32 | // @TODO: Implement correctly 33 | fn update( 34 | &mut self, 35 | device: &wgpu::Device, 36 | queue: &wgpu::Queue, 37 | pools: &ResourcePools, 38 | texture_rid: &ResourceId, 39 | ) { 40 | if !self.textures.contains_key(texture_rid) { 41 | if let Some(texture) = pools.borrow::().borrow(texture_rid) { 42 | let texture_gpu = create_texture( 43 | device, 44 | texture.get_width(), 45 | texture.get_height(), 46 | get_wgpu_format(texture.borrow_format()), 47 | ); 48 | upload_texture( 49 | queue, 50 | &texture_gpu, 51 | texture.get_width(), 52 | texture.get_height(), 53 | bytemuck::cast_slice(texture.borrow_texels()), 54 | ); 55 | self.textures.insert(*texture_rid, texture_gpu); 56 | } 57 | } 58 | } 59 | 60 | pub fn update_from_material( 61 | &mut self, 62 | device: &wgpu::Device, 63 | queue: &wgpu::Queue, 64 | pools: &ResourcePools, 65 | material: &Material, 66 | ) { 67 | let textures = material.borrow_textures( 68 | pools.borrow::>(), 69 | ); 70 | for texture in textures.iter() { 71 | self.update(device, queue, pools, texture); 72 | } 73 | } 74 | } 75 | 76 | fn create_texture( 77 | device: &wgpu::Device, 78 | width: u32, 79 | height: u32, 80 | format: wgpu::TextureFormat 81 | ) -> wgpu::Texture { 82 | device.create_texture(&wgpu::TextureDescriptor { 83 | label: None, 84 | size: wgpu::Extent3d { 85 | width: width, 86 | height: height, 87 | depth_or_array_layers: 1, 88 | }, 89 | mip_level_count: 1, 90 | sample_count: 1, 91 | dimension: wgpu::TextureDimension::D2, 92 | format: format, 93 | usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, 94 | }) 95 | } 96 | 97 | fn upload_texture( 98 | queue: &wgpu::Queue, 99 | texture: &wgpu::Texture, 100 | width: u32, 101 | height: u32, 102 | texels: &[u8], 103 | ) { 104 | queue.write_texture( 105 | texture.as_image_copy(), 106 | &texels, 107 | wgpu::ImageDataLayout { 108 | offset: 0, 109 | // @TODO: Fix me 110 | bytes_per_row: Some(std::num::NonZeroU32::new(width * 4).unwrap()), 111 | rows_per_image: None, 112 | }, 113 | wgpu::Extent3d { 114 | width: width, 115 | height: height, 116 | depth_or_array_layers: 1, 117 | }, 118 | ); 119 | } 120 | 121 | fn get_wgpu_format(format: &TextureFormat) -> wgpu::TextureFormat { 122 | match format { 123 | TextureFormat::Float => wgpu::TextureFormat::Rgba32Float, 124 | TextureFormat::Uint8 => wgpu::TextureFormat::Rgba8Unorm, 125 | TextureFormat::Uint8Srgb => wgpu::TextureFormat::Rgba8UnormSrgb, 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /examples/triangle/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::color::Color, 8 | renderer::wgpu_renderer::{ 9 | WGPURenderer, 10 | WGPURendererOptions, 11 | }, 12 | resource::resource::{ 13 | ResourceId, 14 | ResourcePools, 15 | }, 16 | scene::{ 17 | camera::PerspectiveCamera, 18 | mesh::Mesh, 19 | node::Node, 20 | scene::Scene, 21 | }, 22 | utils::{ 23 | geometry_helper::GeometryHelper, 24 | material_helper::MaterialHelper, 25 | } 26 | }; 27 | 28 | fn create_scene( 29 | window: &Window, 30 | pools: &mut ResourcePools 31 | ) -> (ResourceId, ResourceId) { 32 | let mut scene = Scene::new(); 33 | 34 | let geometry = GeometryHelper::create_triangle( 35 | pools, 36 | 1.0, 37 | 1.0, 38 | ); 39 | 40 | let material = MaterialHelper::create_basic_material( 41 | pools, 42 | Color::set(&mut Color::create(), 1.0, 0.0, 0.0), 43 | ); 44 | 45 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 46 | let node = pools.borrow_mut::().add(Node::new()); 47 | scene.add_node(&node); 48 | scene.assign(&node, &mesh); 49 | 50 | let window_size = window.inner_size(); 51 | let camera = pools.borrow_mut::().add( 52 | PerspectiveCamera::new( 53 | 60.0_f32.to_radians(), 54 | window_size.width as f32 / window_size.height as f32, 55 | 0.1, 56 | 1000.0, 57 | ), 58 | ); 59 | 60 | let mut node = Node::new(); 61 | node.borrow_position_mut()[2] = 1.0; 62 | 63 | let node = pools.borrow_mut::().add(node); 64 | scene.add_node(&node); 65 | scene.assign(&node, &camera); 66 | 67 | (pools.borrow_mut::().add(scene), camera) 68 | } 69 | 70 | fn resize( 71 | renderer: &mut WGPURenderer, 72 | pools: &mut ResourcePools, 73 | camera: &ResourceId, 74 | width: u32, 75 | height: u32, 76 | ) { 77 | pools 78 | .borrow_mut::() 79 | .borrow_mut(camera) 80 | .unwrap() 81 | .set_aspect(width as f32 / height as f32); 82 | renderer.set_size(width as f64, height as f64); 83 | } 84 | 85 | fn update( 86 | pools: &mut ResourcePools, 87 | scene: &ResourceId, 88 | ) { 89 | pools.borrow::() 90 | .borrow(scene) 91 | .unwrap() 92 | .update_matrices(pools); 93 | } 94 | 95 | fn render( 96 | renderer: &mut WGPURenderer, 97 | pools: &ResourcePools, 98 | scene: &ResourceId, 99 | camera: &ResourceId, 100 | ) { 101 | renderer.render(pools, scene, camera); 102 | } 103 | 104 | #[tokio::main] 105 | async fn main() { 106 | let event_loop = EventLoop::new(); 107 | let window = Window::new(&event_loop).unwrap(); 108 | 109 | let window_size = window.inner_size(); 110 | let pixel_ratio = window.scale_factor(); 111 | 112 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 113 | renderer.set_size(window_size.width as f64, window_size.height as f64); 114 | renderer.set_pixel_ratio(pixel_ratio); 115 | 116 | let mut pools = ResourcePools::new(); 117 | let (scene, camera) = create_scene(&window, &mut pools); 118 | 119 | event_loop.run(move |event, _, control_flow| { 120 | *control_flow = ControlFlow::Wait; 121 | match event { 122 | Event::WindowEvent { 123 | event: WindowEvent::Resized(size), 124 | .. 125 | } => { 126 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 127 | update(&mut pools, &scene); 128 | render(&mut renderer, &mut pools, &scene, &camera); 129 | }, 130 | Event::RedrawRequested(_) => { 131 | update(&mut pools, &scene); 132 | render(&mut renderer, &mut pools, &scene, &camera); 133 | }, 134 | Event::WindowEvent { 135 | event: WindowEvent::CloseRequested, 136 | .. 137 | } => { 138 | *control_flow = ControlFlow::Exit; 139 | }, 140 | _ => {} 141 | } 142 | }); 143 | } 144 | -------------------------------------------------------------------------------- /web/examples/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | wgpu-rust-renderer web examples 8 | 9 | 10 | 11 | 12 |
13 | 17 |
18 |
19 |
20 |
21 |
22 | 23 | 24 | 25 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /examples/rotation/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::color::Color, 8 | renderer::wgpu_renderer::{ 9 | WGPURenderer, 10 | WGPURendererOptions, 11 | }, 12 | resource::resource::{ 13 | ResourceId, 14 | ResourcePools, 15 | }, 16 | scene::{ 17 | camera::PerspectiveCamera, 18 | mesh::Mesh, 19 | node::Node, 20 | scene::Scene, 21 | }, 22 | utils::{ 23 | geometry_helper::GeometryHelper, 24 | material_helper::MaterialHelper, 25 | }, 26 | }; 27 | 28 | fn create_scene( 29 | window: &Window, 30 | pools: &mut ResourcePools 31 | ) -> (ResourceId, ResourceId, Vec>) { 32 | let mut objects = Vec::new(); 33 | let mut scene = Scene::new(); 34 | 35 | let geometry = GeometryHelper::create_plane( 36 | pools, 37 | 1.0, 38 | 1.0, 39 | ); 40 | 41 | let material = MaterialHelper::create_basic_material( 42 | pools, 43 | Color::set(&mut Color::create(), 0.0, 1.0, 0.0), 44 | ); 45 | 46 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 47 | let node = pools.borrow_mut::().add(Node::new()); 48 | scene.add_node(&node); 49 | scene.assign(&node, &mesh); 50 | objects.push(node); 51 | 52 | let window_size = window.inner_size(); 53 | let camera = pools.borrow_mut::().add( 54 | PerspectiveCamera::new( 55 | 60.0_f32.to_radians(), 56 | window_size.width as f32 / window_size.height as f32, 57 | 0.1, 58 | 1000.0, 59 | ), 60 | ); 61 | 62 | let mut node = Node::new(); 63 | node.borrow_position_mut()[2] = 2.0; 64 | 65 | let node = pools.borrow_mut::().add(node); 66 | scene.add_node(&node); 67 | scene.assign(&node, &camera); 68 | 69 | (pools.borrow_mut::().add(scene), camera, objects) 70 | } 71 | 72 | fn resize( 73 | renderer: &mut WGPURenderer, 74 | pools: &mut ResourcePools, 75 | camera: &ResourceId, 76 | width: u32, 77 | height: u32, 78 | ) { 79 | pools 80 | .borrow_mut::() 81 | .borrow_mut(camera) 82 | .unwrap() 83 | .set_aspect(width as f32 / height as f32); 84 | renderer.set_size(width as f64, height as f64); 85 | } 86 | 87 | fn update( 88 | pools: &mut ResourcePools, 89 | scene: &ResourceId, 90 | objects: &Vec>, 91 | ) { 92 | { 93 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 94 | node.borrow_rotation_mut()[2] += 0.01; 95 | } 96 | 97 | pools.borrow::() 98 | .borrow(scene) 99 | .unwrap() 100 | .update_matrices(pools); 101 | } 102 | 103 | fn render( 104 | renderer: &mut WGPURenderer, 105 | pools: &ResourcePools, 106 | scene: &ResourceId, 107 | camera: &ResourceId, 108 | ) { 109 | renderer.render(pools, scene, camera); 110 | } 111 | 112 | #[tokio::main] 113 | async fn main() { 114 | let event_loop = EventLoop::new(); 115 | let window = Window::new(&event_loop).unwrap(); 116 | 117 | let window_size = window.inner_size(); 118 | let pixel_ratio = window.scale_factor(); 119 | 120 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 121 | renderer.set_size(window_size.width as f64, window_size.height as f64); 122 | renderer.set_pixel_ratio(pixel_ratio); 123 | 124 | let mut pools = ResourcePools::new(); 125 | let (scene, camera, objects) = create_scene(&window, &mut pools); 126 | 127 | event_loop.run(move |event, _, control_flow| { 128 | *control_flow = ControlFlow::Poll; 129 | match event { 130 | Event::WindowEvent { 131 | event: WindowEvent::Resized(size), 132 | .. 133 | } => { 134 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 135 | update(&mut pools, &scene, &objects); 136 | render(&mut renderer, &mut pools, &scene, &camera); 137 | }, 138 | Event::RedrawEventsCleared => { 139 | window.request_redraw(); 140 | }, 141 | Event::RedrawRequested(_) => { 142 | update(&mut pools, &scene, &objects); 143 | render(&mut renderer, &mut pools, &scene, &camera); 144 | }, 145 | Event::WindowEvent { 146 | event: WindowEvent::CloseRequested, 147 | .. 148 | } => { 149 | *control_flow = ControlFlow::Exit; 150 | }, 151 | _ => {} 152 | } 153 | }); 154 | } 155 | -------------------------------------------------------------------------------- /examples/cube/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::{ 8 | color::Color, 9 | vector3::Vector3, 10 | }, 11 | renderer::wgpu_renderer::{ 12 | WGPURenderer, 13 | WGPURendererOptions, 14 | }, 15 | resource::resource::{ 16 | ResourceId, 17 | ResourcePools, 18 | }, 19 | scene::{ 20 | camera::PerspectiveCamera, 21 | mesh::Mesh, 22 | node::Node, 23 | scene::Scene, 24 | }, 25 | utils::{ 26 | geometry_helper::GeometryHelper, 27 | material_helper::MaterialHelper, 28 | }, 29 | }; 30 | 31 | fn create_scene( 32 | window: &Window, 33 | pools: &mut ResourcePools 34 | ) -> (ResourceId, ResourceId, Vec>) { 35 | let mut objects = Vec::new(); 36 | let mut scene = Scene::new(); 37 | 38 | let geometry = GeometryHelper::create_box( 39 | pools, 40 | 1.0, 41 | 1.0, 42 | 1.0, 43 | ); 44 | 45 | let material = MaterialHelper::create_basic_material( 46 | pools, 47 | Color::set(&mut Color::create(), 0.5, 0.5, 1.0), 48 | ); 49 | 50 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 51 | let node = pools.borrow_mut::().add(Node::new()); 52 | scene.add_node(&node); 53 | scene.assign(&node, &mesh); 54 | objects.push(node); 55 | 56 | let window_size = window.inner_size(); 57 | let camera = pools.borrow_mut::().add( 58 | PerspectiveCamera::new( 59 | 60.0_f32.to_radians(), 60 | window_size.width as f32 / window_size.height as f32, 61 | 0.1, 62 | 1000.0, 63 | ), 64 | ); 65 | 66 | let mut node = Node::new(); 67 | Vector3::set( 68 | node.borrow_position_mut(), 69 | 0.0, 0.0, 3.0, 70 | ); 71 | 72 | let node = pools.borrow_mut::().add(node); 73 | scene.add_node(&node); 74 | scene.assign(&node, &camera); 75 | 76 | (pools.borrow_mut::().add(scene), camera, objects) 77 | } 78 | 79 | fn resize( 80 | renderer: &mut WGPURenderer, 81 | pools: &mut ResourcePools, 82 | camera: &ResourceId, 83 | width: u32, 84 | height: u32, 85 | ) { 86 | pools 87 | .borrow_mut::() 88 | .borrow_mut(camera) 89 | .unwrap() 90 | .set_aspect(width as f32 / height as f32); 91 | renderer.set_size(width as f64, height as f64); 92 | } 93 | 94 | fn update( 95 | pools: &mut ResourcePools, 96 | scene: &ResourceId, 97 | objects: &Vec>, 98 | ) { 99 | { 100 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 101 | Vector3::add( 102 | node.borrow_rotation_mut(), 103 | &[0.001, 0.01, 0.003], 104 | ); 105 | } 106 | 107 | pools.borrow::() 108 | .borrow(scene) 109 | .unwrap() 110 | .update_matrices(pools); 111 | } 112 | 113 | fn render( 114 | renderer: &mut WGPURenderer, 115 | pools: &ResourcePools, 116 | scene: &ResourceId, 117 | camera: &ResourceId, 118 | ) { 119 | renderer.render(pools, scene, camera); 120 | } 121 | 122 | #[tokio::main] 123 | async fn main() { 124 | let event_loop = EventLoop::new(); 125 | let window = Window::new(&event_loop).unwrap(); 126 | 127 | let window_size = window.inner_size(); 128 | let pixel_ratio = window.scale_factor(); 129 | 130 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 131 | renderer.set_size(window_size.width as f64, window_size.height as f64); 132 | renderer.set_pixel_ratio(pixel_ratio); 133 | 134 | let mut pools = ResourcePools::new(); 135 | let (scene, camera, objects) = create_scene(&window, &mut pools); 136 | 137 | event_loop.run(move |event, _, control_flow| { 138 | *control_flow = ControlFlow::Poll; 139 | match event { 140 | Event::WindowEvent { 141 | event: WindowEvent::Resized(size), 142 | .. 143 | } => { 144 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 145 | update(&mut pools, &scene, &objects); 146 | render(&mut renderer, &mut pools, &scene, &camera); 147 | }, 148 | Event::RedrawEventsCleared => { 149 | window.request_redraw(); 150 | }, 151 | Event::RedrawRequested(_) => { 152 | update(&mut pools, &scene, &objects); 153 | render(&mut renderer, &mut pools, &scene, &camera); 154 | }, 155 | Event::WindowEvent { 156 | event: WindowEvent::CloseRequested, 157 | .. 158 | } => { 159 | *control_flow = ControlFlow::Exit; 160 | }, 161 | _ => {} 162 | } 163 | }); 164 | } 165 | -------------------------------------------------------------------------------- /examples/pbr/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::{ 8 | color::Color, 9 | vector3::Vector3, 10 | }, 11 | renderer::wgpu_renderer::{ 12 | WGPURenderer, 13 | WGPURendererOptions, 14 | }, 15 | resource::resource::{ 16 | ResourceId, 17 | ResourcePools, 18 | }, 19 | scene::{ 20 | camera::PerspectiveCamera, 21 | mesh::Mesh, 22 | node::Node, 23 | scene::Scene, 24 | }, 25 | utils::{ 26 | geometry_helper::GeometryHelper, 27 | material_helper::MaterialHelper, 28 | }, 29 | }; 30 | 31 | fn create_scene( 32 | window: &Window, 33 | pools: &mut ResourcePools 34 | ) -> (ResourceId, ResourceId, Vec>) { 35 | let mut objects = Vec::new(); 36 | let mut scene = Scene::new(); 37 | 38 | let geometry = GeometryHelper::create_box( 39 | pools, 40 | 1.0, 41 | 1.0, 42 | 1.0, 43 | ); 44 | 45 | let material = MaterialHelper::create_brdf_material( 46 | pools, 47 | Color::set(&mut Color::create(), 1.0, 1.0, 1.0), 48 | 0.5, 49 | 0.5, 50 | ); 51 | 52 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 53 | let node = pools.borrow_mut::().add(Node::new()); 54 | scene.add_node(&node); 55 | scene.assign(&node, &mesh); 56 | objects.push(node); 57 | 58 | let window_size = window.inner_size(); 59 | let camera = pools.borrow_mut::().add( 60 | PerspectiveCamera::new( 61 | 60.0_f32.to_radians(), 62 | window_size.width as f32 / window_size.height as f32, 63 | 0.1, 64 | 1000.0, 65 | ), 66 | ); 67 | 68 | let mut node = Node::new(); 69 | Vector3::set( 70 | node.borrow_position_mut(), 71 | 0.0, 0.0, 3.0, 72 | ); 73 | 74 | let node = pools.borrow_mut::().add(node); 75 | scene.add_node(&node); 76 | scene.assign(&node, &camera); 77 | 78 | (pools.borrow_mut::().add(scene), camera, objects) 79 | } 80 | 81 | fn resize( 82 | renderer: &mut WGPURenderer, 83 | pools: &mut ResourcePools, 84 | camera: &ResourceId, 85 | width: u32, 86 | height: u32, 87 | ) { 88 | pools 89 | .borrow_mut::() 90 | .borrow_mut(camera) 91 | .unwrap() 92 | .set_aspect(width as f32 / height as f32); 93 | renderer.set_size(width as f64, height as f64); 94 | } 95 | 96 | fn update( 97 | pools: &mut ResourcePools, 98 | scene: &ResourceId, 99 | objects: &Vec>, 100 | ) { 101 | { 102 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 103 | Vector3::add( 104 | node.borrow_rotation_mut(), 105 | &[0.001, 0.01, 0.003], 106 | ); 107 | } 108 | 109 | pools.borrow::() 110 | .borrow(scene) 111 | .unwrap() 112 | .update_matrices(pools); 113 | } 114 | 115 | fn render( 116 | renderer: &mut WGPURenderer, 117 | pools: &ResourcePools, 118 | scene: &ResourceId, 119 | camera: &ResourceId, 120 | ) { 121 | renderer.render(pools, scene, camera); 122 | } 123 | 124 | #[tokio::main] 125 | async fn main() { 126 | let event_loop = EventLoop::new(); 127 | let window = Window::new(&event_loop).unwrap(); 128 | 129 | let window_size = window.inner_size(); 130 | let pixel_ratio = window.scale_factor(); 131 | 132 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 133 | renderer.set_size(window_size.width as f64, window_size.height as f64); 134 | renderer.set_pixel_ratio(pixel_ratio); 135 | 136 | let mut pools = ResourcePools::new(); 137 | let (scene, camera, objects) = create_scene(&window, &mut pools); 138 | 139 | event_loop.run(move |event, _, control_flow| { 140 | *control_flow = ControlFlow::Poll; 141 | match event { 142 | Event::WindowEvent { 143 | event: WindowEvent::Resized(size), 144 | .. 145 | } => { 146 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 147 | update(&mut pools, &scene, &objects); 148 | render(&mut renderer, &mut pools, &scene, &camera); 149 | }, 150 | Event::RedrawEventsCleared => { 151 | window.request_redraw(); 152 | }, 153 | Event::RedrawRequested(_) => { 154 | update(&mut pools, &scene, &objects); 155 | render(&mut renderer, &mut pools, &scene, &camera); 156 | }, 157 | Event::WindowEvent { 158 | event: WindowEvent::CloseRequested, 159 | .. 160 | } => { 161 | *control_flow = ControlFlow::Exit; 162 | }, 163 | _ => {} 164 | } 165 | }); 166 | } 167 | -------------------------------------------------------------------------------- /examples/gltf/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::vector3::Vector3, 8 | renderer::wgpu_renderer::{ 9 | WGPURenderer, 10 | WGPURendererOptions, 11 | }, 12 | resource::resource::{ 13 | ResourceId, 14 | ResourcePools, 15 | }, 16 | scene::{ 17 | camera::PerspectiveCamera, 18 | node::Node, 19 | scene::Scene, 20 | }, 21 | utils::gltf_loader::GltfLoader, 22 | }; 23 | 24 | async fn create_scene( 25 | window: &Window, 26 | pools: &mut ResourcePools 27 | ) -> (ResourceId, ResourceId, Vec>) { 28 | let mut objects = Vec::new(); 29 | let scene_rid = pools.borrow_mut::().add(Scene::new()); 30 | 31 | let nodes = GltfLoader::load_gltf( 32 | pools, 33 | &scene_rid, 34 | concat!( 35 | env!("CARGO_MANIFEST_DIR"), 36 | "/examples/gltf/assets/", 37 | ), 38 | "DamagedHelmet.gltf", 39 | ).await; 40 | 41 | for node in nodes.iter() { 42 | pools.borrow_mut::() 43 | .borrow_mut(&scene_rid) 44 | .unwrap() 45 | .add_node(node); 46 | objects.push(*node); 47 | pools.borrow_mut::() 48 | .borrow_mut(node) 49 | .unwrap() 50 | .borrow_rotation_mut()[0] = 90.0_f32.to_radians(); 51 | } 52 | 53 | let window_size = window.inner_size(); 54 | let camera = pools.borrow_mut::().add( 55 | PerspectiveCamera::new( 56 | 60.0_f32.to_radians(), 57 | window_size.width as f32 / window_size.height as f32, 58 | 0.1, 59 | 1000.0, 60 | ), 61 | ); 62 | 63 | let mut node = Node::new(); 64 | Vector3::set( 65 | node.borrow_position_mut(), 66 | 0.0, 0.0, 3.0, 67 | ); 68 | 69 | let node = pools.borrow_mut::().add(node); 70 | 71 | { 72 | let scene = pools.borrow_mut::().borrow_mut(&scene_rid).unwrap(); 73 | scene.add_node(&node); 74 | scene.assign(&node, &camera); 75 | } 76 | 77 | (scene_rid, camera, objects) 78 | } 79 | 80 | fn resize( 81 | renderer: &mut WGPURenderer, 82 | pools: &mut ResourcePools, 83 | camera: &ResourceId, 84 | width: u32, 85 | height: u32, 86 | ) { 87 | pools 88 | .borrow_mut::() 89 | .borrow_mut(camera) 90 | .unwrap() 91 | .set_aspect(width as f32 / height as f32); 92 | renderer.set_size(width as f64, height as f64); 93 | } 94 | 95 | fn update( 96 | pools: &mut ResourcePools, 97 | scene: &ResourceId, 98 | objects: &Vec>, 99 | ) { 100 | { 101 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 102 | Vector3::add( 103 | node.borrow_rotation_mut(), 104 | &[0.0, 0.0, -0.01], 105 | ); 106 | } 107 | 108 | pools.borrow::() 109 | .borrow(scene) 110 | .unwrap() 111 | .update_matrices(pools); 112 | } 113 | 114 | fn render( 115 | renderer: &mut WGPURenderer, 116 | pools: &ResourcePools, 117 | scene: &ResourceId, 118 | camera: &ResourceId, 119 | ) { 120 | renderer.render(pools, scene, camera); 121 | } 122 | 123 | #[tokio::main] 124 | async fn main() { 125 | let event_loop = EventLoop::new(); 126 | let window = Window::new(&event_loop).unwrap(); 127 | 128 | let window_size = window.inner_size(); 129 | let pixel_ratio = window.scale_factor(); 130 | 131 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 132 | renderer.set_size(window_size.width as f64, window_size.height as f64); 133 | renderer.set_pixel_ratio(pixel_ratio); 134 | 135 | let mut pools = ResourcePools::new(); 136 | let (scene, camera, objects) = create_scene(&window, &mut pools).await; 137 | 138 | event_loop.run(move |event, _, control_flow| { 139 | *control_flow = ControlFlow::Poll; 140 | match event { 141 | Event::WindowEvent { 142 | event: WindowEvent::Resized(size), 143 | .. 144 | } => { 145 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 146 | update(&mut pools, &scene, &objects); 147 | render(&mut renderer, &mut pools, &scene, &camera); 148 | }, 149 | Event::RedrawEventsCleared => { 150 | window.request_redraw(); 151 | }, 152 | Event::RedrawRequested(_) => { 153 | update(&mut pools, &scene, &objects); 154 | render(&mut renderer, &mut pools, &scene, &camera); 155 | }, 156 | Event::WindowEvent { 157 | event: WindowEvent::CloseRequested, 158 | .. 159 | } => { 160 | *control_flow = ControlFlow::Exit; 161 | }, 162 | _ => {} 163 | } 164 | }); 165 | } 166 | -------------------------------------------------------------------------------- /web/examples/triangle/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../utils/window.rs"] 2 | mod window; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use winit::{ 6 | event::{Event, WindowEvent}, 7 | event_loop::{ControlFlow, EventLoop}, 8 | }; 9 | 10 | use window::{ 11 | create_window, 12 | get_window_device_pixel_ratio, 13 | get_window_inner_size, 14 | }; 15 | 16 | use wgpu_rust_renderer::{ 17 | math::color::Color, 18 | renderer::wgpu_renderer::WGPURendererOptions, 19 | resource::resource::{ 20 | ResourceId, 21 | ResourcePools, 22 | }, 23 | scene::{ 24 | camera::PerspectiveCamera, 25 | mesh::Mesh, 26 | node::Node, 27 | scene::Scene, 28 | }, 29 | utils::{ 30 | geometry_helper::GeometryHelper, 31 | material_helper::MaterialHelper, 32 | }, 33 | web::wgpu_web_renderer::WGPUWebRenderer, 34 | }; 35 | 36 | fn create_scene( 37 | pools: &mut ResourcePools 38 | ) -> (ResourceId, ResourceId) { 39 | let mut scene = Scene::new(); 40 | 41 | let geometry = GeometryHelper::create_triangle( 42 | pools, 43 | 1.0, 44 | 1.0, 45 | ); 46 | 47 | let material = MaterialHelper::create_basic_material( 48 | pools, 49 | Color::set(&mut Color::create(), 1.0, 0.0, 0.0), 50 | ); 51 | 52 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 53 | let node = pools.borrow_mut::().add(Node::new()); 54 | scene.add_node(&node); 55 | scene.assign(&node, &mesh); 56 | 57 | let window_size = get_window_inner_size(); 58 | let camera = pools.borrow_mut::().add( 59 | PerspectiveCamera::new( 60 | 60.0_f32.to_radians(), 61 | (window_size.0 / window_size.1) as f32, 62 | 0.1, 63 | 1000.0, 64 | ), 65 | ); 66 | 67 | let mut node = Node::new(); 68 | node.borrow_position_mut()[2] = 1.0; 69 | 70 | let node = pools.borrow_mut::().add(node); 71 | scene.add_node(&node); 72 | scene.assign(&node, &camera); 73 | 74 | (pools.borrow_mut::().add(scene), camera) 75 | } 76 | 77 | fn resize( 78 | renderer: &mut WGPUWebRenderer, 79 | pools: &mut ResourcePools, 80 | camera: &ResourceId, 81 | width: u32, 82 | height: u32, 83 | ) { 84 | pools 85 | .borrow_mut::() 86 | .borrow_mut(camera) 87 | .unwrap() 88 | .set_aspect(width as f32 / height as f32); 89 | renderer.set_size(width as f64, height as f64); 90 | } 91 | 92 | fn update( 93 | pools: &mut ResourcePools, 94 | scene: &ResourceId, 95 | ) { 96 | pools.borrow::() 97 | .borrow(scene) 98 | .unwrap() 99 | .update_matrices(pools); 100 | } 101 | 102 | fn render( 103 | renderer: &mut WGPUWebRenderer, 104 | pools: &ResourcePools, 105 | scene: &ResourceId, 106 | camera: &ResourceId, 107 | ) { 108 | renderer.render(pools, scene, camera); 109 | } 110 | 111 | #[wasm_bindgen(start)] 112 | pub async fn start() { 113 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 114 | console_log::init().expect("could not initialize logger"); 115 | 116 | let event_loop = EventLoop::new(); 117 | let window = create_window(&event_loop); 118 | 119 | use winit::platform::web::WindowExtWebSys; 120 | 121 | web_sys::window() 122 | .and_then(|win| win.document()) 123 | .and_then(|doc| doc.body()) 124 | .and_then(|body| { 125 | body.append_child(&web_sys::Element::from(window.canvas())) 126 | .ok() 127 | }) 128 | .expect("couldn't append canvas to document body"); 129 | 130 | let inner_size = get_window_inner_size(); 131 | let pixel_ratio = get_window_device_pixel_ratio(); 132 | 133 | let mut renderer = WGPUWebRenderer::new(&window, window.canvas(), WGPURendererOptions::default()).await; 134 | renderer.set_size(inner_size.0 as f64, inner_size.1 as f64); 135 | renderer.set_pixel_ratio(pixel_ratio as f64); 136 | 137 | let mut pools = ResourcePools::new(); 138 | let (scene, camera) = create_scene(&mut pools); 139 | 140 | event_loop.run(move |event, _, control_flow| { 141 | *control_flow = ControlFlow::Wait; 142 | match event { 143 | Event::WindowEvent { 144 | event: WindowEvent::Resized(size), 145 | .. 146 | } => { 147 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 148 | update(&mut pools, &scene); 149 | render(&mut renderer, &mut pools, &scene, &camera); 150 | }, 151 | Event::RedrawRequested(_) => { 152 | update(&mut pools, &scene); 153 | render(&mut renderer, &mut pools, &scene, &camera); 154 | }, 155 | Event::WindowEvent { 156 | event: WindowEvent::CloseRequested, 157 | .. 158 | } => { 159 | *control_flow = ControlFlow::Exit; 160 | }, 161 | _ => {} 162 | } 163 | }); 164 | } 165 | -------------------------------------------------------------------------------- /src/renderer/wgpu_render_pipeline.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::collections::HashMap; 3 | 4 | use crate::{ 5 | material::{ 6 | material::{ 7 | Material, 8 | Side, 9 | }, 10 | node::node::MaterialNode, 11 | }, 12 | resource::resource::{ 13 | ResourceId, 14 | ResourcePools, 15 | }, 16 | scene::node::Node, 17 | }; 18 | 19 | pub struct WGPURenderPipeline { 20 | pipeline: wgpu::RenderPipeline 21 | } 22 | 23 | impl WGPURenderPipeline { 24 | fn new( 25 | device: &wgpu::Device, 26 | bind_group_layout: &wgpu::BindGroupLayout, 27 | shader_code: &str, 28 | sample_count: u32, 29 | side: &Side, 30 | ) -> Self { 31 | // For debug 32 | //println!("{}", shader_code); 33 | 34 | let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { 35 | label: None, 36 | source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(shader_code)), 37 | }); 38 | 39 | let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 40 | label: None, 41 | bind_group_layouts: &[bind_group_layout], 42 | push_constant_ranges: &[], 43 | }); 44 | 45 | // @TODO: Programmable 46 | let vertex_buffers = [ 47 | // position 48 | wgpu::VertexBufferLayout { 49 | array_stride: 3 * 4, 50 | step_mode: wgpu::VertexStepMode::Vertex, 51 | attributes: &[ 52 | wgpu::VertexAttribute { 53 | format: wgpu::VertexFormat::Float32x3, 54 | offset: 0, 55 | shader_location: 0, 56 | } 57 | ], 58 | }, 59 | // normal 60 | wgpu::VertexBufferLayout { 61 | array_stride: 3 * 4, 62 | step_mode: wgpu::VertexStepMode::Vertex, 63 | attributes: &[ 64 | wgpu::VertexAttribute { 65 | format: wgpu::VertexFormat::Float32x3, 66 | offset: 0, 67 | shader_location: 1, 68 | }, 69 | ], 70 | }, 71 | // uv 72 | wgpu::VertexBufferLayout { 73 | array_stride: 2 * 4, 74 | step_mode: wgpu::VertexStepMode::Vertex, 75 | attributes: &[ 76 | wgpu::VertexAttribute { 77 | format: wgpu::VertexFormat::Float32x2, 78 | offset: 0, 79 | shader_location: 2, 80 | }, 81 | ], 82 | }, 83 | ]; 84 | 85 | let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 86 | label: None, 87 | layout: Some(&layout), 88 | vertex: wgpu::VertexState { 89 | module: &shader, 90 | entry_point: "vs_main", 91 | buffers: &vertex_buffers, 92 | }, 93 | fragment: Some(wgpu::FragmentState { 94 | module: &shader, 95 | entry_point: "fs_main", 96 | // @TODO: Color management 97 | targets: &[wgpu::TextureFormat::Bgra8Unorm.into()], 98 | }), 99 | // Backface culling 100 | // @TODO: Should be configurable 101 | primitive: wgpu::PrimitiveState { 102 | cull_mode: match side { 103 | Side::BackSide | 104 | Side::FrontSide => Some(wgpu::Face::Back), 105 | Side::DoubleSide => None, 106 | }, 107 | front_face: match side { 108 | Side::BackSide => wgpu::FrontFace::Cw, 109 | Side::DoubleSide | 110 | Side::FrontSide => wgpu::FrontFace::Ccw, 111 | }, 112 | ..Default::default() 113 | }, 114 | depth_stencil: Some(wgpu::DepthStencilState { 115 | bias: wgpu::DepthBiasState::default(), 116 | depth_compare: wgpu::CompareFunction::LessEqual, 117 | depth_write_enabled: true, 118 | format: wgpu::TextureFormat::Depth24PlusStencil8, 119 | stencil: wgpu::StencilState::default(), 120 | }), 121 | multisample: wgpu::MultisampleState { 122 | count: sample_count, 123 | ..Default::default() 124 | }, 125 | }); 126 | 127 | WGPURenderPipeline { 128 | pipeline: pipeline 129 | } 130 | } 131 | } 132 | 133 | pub struct WGPURenderPipelines { 134 | pipelines: HashMap::, WGPURenderPipeline> 135 | } 136 | 137 | impl WGPURenderPipelines { 138 | pub fn new() -> Self { 139 | WGPURenderPipelines { 140 | pipelines: HashMap::new() 141 | } 142 | } 143 | 144 | pub fn borrow(&self, node: &ResourceId) -> Option<&wgpu::RenderPipeline> { 145 | if let Some(pipeline) = &self.pipelines.get(node) { 146 | Some(&pipeline.pipeline) 147 | } else { 148 | None 149 | } 150 | } 151 | 152 | pub fn update( 153 | &mut self, 154 | device: &wgpu::Device, 155 | pools: &ResourcePools, 156 | node: &ResourceId, 157 | material: &Material, 158 | bind_group_layout: &wgpu::BindGroupLayout, 159 | sample_count: u32, 160 | ) { 161 | if !self.pipelines.contains_key(&node) { 162 | self.pipelines.insert( 163 | *node, 164 | WGPURenderPipeline::new( 165 | device, 166 | bind_group_layout, 167 | &material.build_shader_code( 168 | pools.borrow::>(), 169 | ), 170 | sample_count, 171 | material.borrow_side(), 172 | ) 173 | ); 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/scene/scene.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{ 3 | Any, 4 | TypeId, 5 | }, 6 | collections::HashMap, 7 | }; 8 | 9 | use crate::{ 10 | math::color::Color, 11 | resource::resource::{ 12 | ResourceId, 13 | ResourcePools, 14 | }, 15 | scene::{ 16 | camera::PerspectiveCamera, 17 | mesh::Mesh, 18 | node::{ 19 | Node, 20 | NodeExecutor, 21 | }, 22 | }, 23 | }; 24 | 25 | 26 | trait ResourceLinksTrait { 27 | fn as_any(&self) -> &dyn Any; 28 | fn as_any_mut(&mut self) -> &mut dyn Any; 29 | } 30 | 31 | pub struct ResourceLinks { 32 | links: HashMap, ResourceId>, 33 | } 34 | 35 | impl ResourceLinksTrait for ResourceLinks { 36 | fn as_any(&self) -> &dyn Any { 37 | self 38 | } 39 | 40 | fn as_any_mut(&mut self) -> &mut dyn Any { 41 | self 42 | } 43 | } 44 | 45 | impl ResourceLinks { 46 | pub fn new() -> Self { 47 | ResourceLinks { 48 | links: HashMap::new(), 49 | } 50 | } 51 | 52 | pub fn has(&self, rid_from: &ResourceId) -> bool { 53 | self.links.contains_key(rid_from) 54 | } 55 | 56 | pub fn add(&mut self, rid_from: &ResourceId, rid_to: &ResourceId) { 57 | self.links.insert(*rid_from, *rid_to); 58 | } 59 | 60 | pub fn borrow(&self, rid_from: &ResourceId) -> Option<&ResourceId> { 61 | self.links.get(rid_from) 62 | } 63 | } 64 | 65 | fn cast_links(links: &dyn ResourceLinksTrait) -> &ResourceLinks { 66 | links 67 | .as_any() 68 | .downcast_ref::>() 69 | .unwrap() 70 | } 71 | 72 | fn cast_links_mut(links: &mut dyn ResourceLinksTrait) -> &mut ResourceLinks { 73 | links 74 | .as_any_mut() 75 | .downcast_mut::>() 76 | .unwrap() 77 | } 78 | 79 | pub struct Scene { 80 | background_color: [f32; 3], 81 | links: HashMap>, 82 | nodes: Vec>, 83 | } 84 | 85 | impl Scene { 86 | pub fn new() -> Self { 87 | let mut links = HashMap::new(); 88 | Self::add_links::(&mut links); 89 | Self::add_links::(&mut links); 90 | Self::add_links::(&mut links); 91 | Self::add_links::(&mut links); 92 | 93 | Scene { 94 | background_color: *Color::set(&mut Color::create(), 1.0, 1.0, 1.0), 95 | links: links, 96 | nodes: Vec::new(), 97 | } 98 | } 99 | 100 | fn add_links(links: &mut HashMap>) { 101 | links.insert(TypeId::of::<(T1, T2)>(), Box::new(ResourceLinks::::new())); 102 | } 103 | 104 | fn borrow_links(&self) -> &ResourceLinks { 105 | if let Some(links) = self.links.get(&TypeId::of::<(T1, T2)>()) { 106 | cast_links(links.as_ref()) 107 | } else { 108 | // @TODO: Proper error handling 109 | panic!("Unknown Type"); 110 | } 111 | } 112 | 113 | fn borrow_links_mut(&mut self) -> &mut ResourceLinks { 114 | if let Some(links) = self.links.get_mut(&TypeId::of::<(T1, T2)>()) { 115 | cast_links_mut(links.as_mut()) 116 | } else { 117 | // @TODO: Proper error handling 118 | panic!("Unknown Type"); 119 | } 120 | } 121 | 122 | // Where should be this method placed? 123 | pub fn assign( 124 | &mut self, 125 | rid1: &ResourceId, 126 | rid2: &ResourceId, 127 | ) { 128 | self.borrow_links_mut::().add(rid1, rid2); 129 | self.borrow_links_mut::().add(rid2, rid1); 130 | } 131 | 132 | // @TODO: Rename? 133 | pub fn borrow_assigned_from(&self, rid: &ResourceId) -> Option<&ResourceId> { 134 | self.borrow_links::().borrow(rid) 135 | } 136 | 137 | pub fn borrow_assigned_to(&self, rid: &ResourceId) -> Option<&ResourceId> { 138 | self.borrow_links::().borrow(rid) 139 | } 140 | 141 | pub fn add_node(&mut self, rid: &ResourceId) { 142 | self.nodes.push(*rid); 143 | } 144 | 145 | pub fn collect_nodes(&self, pools: &ResourcePools) -> Vec> { 146 | let mut nodes = Vec::new(); 147 | let pool = pools.borrow::(); 148 | for node in self.nodes.iter() { 149 | NodeExecutor::collect_nodes(pool, node, &mut nodes); 150 | } 151 | nodes 152 | } 153 | 154 | pub fn borrow_background_color(&self) -> &[f32; 3] { 155 | &self.background_color 156 | } 157 | 158 | pub fn borrow_background_color_mut(&mut self) -> &[f32; 3] { 159 | &mut self.background_color 160 | } 161 | 162 | pub fn update_matrices(&self, pools: &ResourcePools) { 163 | // @TODO: Write comment about why unsafe 164 | // @TODO: Remove unsafe 165 | let pool = pools.borrow_mut_unsafe::(); 166 | for node in self.nodes.iter() { 167 | NodeExecutor::update_matrices(pool, node); 168 | } 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /examples/texture/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::{ 8 | color::Color, 9 | vector3::Vector3, 10 | }, 11 | renderer::wgpu_renderer::{ 12 | WGPURenderer, 13 | WGPURendererOptions, 14 | }, 15 | resource::resource::{ 16 | ResourceId, 17 | ResourcePools, 18 | }, 19 | scene::{ 20 | camera::PerspectiveCamera, 21 | mesh::Mesh, 22 | node::Node, 23 | scene::Scene, 24 | }, 25 | texture::texture::TextureFormat, 26 | utils::{ 27 | geometry_helper::GeometryHelper, 28 | material_helper::MaterialHelper, 29 | texture_loader::TextureLoader, 30 | }, 31 | }; 32 | 33 | async fn create_scene( 34 | window: &Window, 35 | pools: &mut ResourcePools 36 | ) -> (ResourceId, ResourceId, Vec>) { 37 | let mut objects = Vec::new(); 38 | let mut scene = Scene::new(); 39 | 40 | let geometry = GeometryHelper::create_box( 41 | pools, 42 | 1.0, 43 | 1.0, 44 | 1.0, 45 | ); 46 | 47 | let texture = TextureLoader::load_png_with_filepath( 48 | pools, 49 | concat!( 50 | env!("CARGO_MANIFEST_DIR"), 51 | "/examples/texture/texture.png", 52 | ), 53 | TextureFormat::default(), 54 | ).await; 55 | 56 | let material = MaterialHelper::create_basic_material_with_texture( 57 | pools, 58 | Color::set(&mut Color::create(), 0.5, 0.5, 1.0), 59 | texture, 60 | ); 61 | 62 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 63 | let mut node = Node::new(); 64 | node.borrow_rotation_mut()[0] = 35.0_f32.to_radians(); 65 | let node = pools.borrow_mut::().add(node); 66 | scene.add_node(&node); 67 | scene.assign(&node, &mesh); 68 | objects.push(node); 69 | 70 | let window_size = window.inner_size(); 71 | let camera = pools.borrow_mut::().add( 72 | PerspectiveCamera::new( 73 | 60.0_f32.to_radians(), 74 | window_size.width as f32 / window_size.height as f32, 75 | 0.1, 76 | 1000.0, 77 | ), 78 | ); 79 | 80 | let mut node = Node::new(); 81 | Vector3::set( 82 | node.borrow_position_mut(), 83 | 0.0, 0.0, 3.0, 84 | ); 85 | 86 | let node = pools.borrow_mut::().add(node); 87 | scene.add_node(&node); 88 | scene.assign(&node, &camera); 89 | 90 | (pools.borrow_mut::().add(scene), camera, objects) 91 | } 92 | 93 | fn resize( 94 | renderer: &mut WGPURenderer, 95 | pools: &mut ResourcePools, 96 | camera: &ResourceId, 97 | width: u32, 98 | height: u32, 99 | ) { 100 | pools 101 | .borrow_mut::() 102 | .borrow_mut(camera) 103 | .unwrap() 104 | .set_aspect(width as f32 / height as f32); 105 | renderer.set_size(width as f64, height as f64); 106 | } 107 | 108 | fn update( 109 | pools: &mut ResourcePools, 110 | scene: &ResourceId, 111 | objects: &Vec>, 112 | ) { 113 | { 114 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 115 | Vector3::add( 116 | node.borrow_rotation_mut(), 117 | &[0.0, 0.01, 0.0], 118 | ); 119 | } 120 | 121 | pools.borrow::() 122 | .borrow(scene) 123 | .unwrap() 124 | .update_matrices(pools); 125 | } 126 | 127 | fn render( 128 | renderer: &mut WGPURenderer, 129 | pools: &ResourcePools, 130 | scene: &ResourceId, 131 | camera: &ResourceId, 132 | ) { 133 | renderer.render(pools, scene, camera); 134 | } 135 | 136 | #[tokio::main] 137 | async fn main() { 138 | let event_loop = EventLoop::new(); 139 | let window = Window::new(&event_loop).unwrap(); 140 | 141 | let window_size = window.inner_size(); 142 | let pixel_ratio = window.scale_factor(); 143 | 144 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 145 | renderer.set_size(window_size.width as f64, window_size.height as f64); 146 | renderer.set_pixel_ratio(pixel_ratio); 147 | 148 | let mut pools = ResourcePools::new(); 149 | let (scene, camera, objects) = create_scene(&window, &mut pools).await; 150 | 151 | event_loop.run(move |event, _, control_flow| { 152 | *control_flow = ControlFlow::Poll; 153 | match event { 154 | Event::WindowEvent { 155 | event: WindowEvent::Resized(size), 156 | .. 157 | } => { 158 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 159 | update(&mut pools, &scene, &objects); 160 | render(&mut renderer, &mut pools, &scene, &camera); 161 | }, 162 | Event::RedrawEventsCleared => { 163 | window.request_redraw(); 164 | }, 165 | Event::RedrawRequested(_) => { 166 | update(&mut pools, &scene, &objects); 167 | render(&mut renderer, &mut pools, &scene, &camera); 168 | }, 169 | Event::WindowEvent { 170 | event: WindowEvent::CloseRequested, 171 | .. 172 | } => { 173 | *control_flow = ControlFlow::Exit; 174 | }, 175 | _ => {} 176 | } 177 | }); 178 | } 179 | -------------------------------------------------------------------------------- /src/resource/resource.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{ 3 | Any, 4 | TypeId, 5 | }, 6 | collections::HashMap, 7 | hash::{ 8 | Hash, 9 | Hasher, 10 | }, 11 | marker::PhantomData, 12 | mem::transmute, 13 | }; 14 | 15 | use crate::{ 16 | geometry::{ 17 | attribute::Attribute, 18 | geometry::Geometry, 19 | index::Index, 20 | }, 21 | material::{ 22 | material::Material, 23 | node::node::MaterialNode, 24 | }, 25 | scene::{ 26 | camera::PerspectiveCamera, 27 | mesh::Mesh, 28 | node::Node, 29 | scene::Scene, 30 | }, 31 | texture::{ 32 | sampler::Sampler, 33 | texture::Texture, 34 | }, 35 | }; 36 | 37 | trait ResourcePoolTrait { 38 | fn as_any(&self) -> &dyn Any; 39 | fn as_any_mut(&mut self) -> &mut dyn Any; 40 | } 41 | 42 | pub struct ResourcePool { 43 | resources: Vec, 44 | } 45 | 46 | impl ResourcePoolTrait for ResourcePool { 47 | fn as_any(&self) -> &dyn Any { 48 | self 49 | } 50 | 51 | fn as_any_mut(&mut self) -> &mut dyn Any { 52 | self 53 | } 54 | } 55 | 56 | fn cast_pool(pool: &dyn ResourcePoolTrait) -> &ResourcePool { 57 | pool 58 | .as_any() 59 | .downcast_ref::>() 60 | .unwrap() 61 | } 62 | 63 | fn cast_pool_mut(pool: &mut dyn ResourcePoolTrait) -> &mut ResourcePool { 64 | pool 65 | .as_any_mut() 66 | .downcast_mut::>() 67 | .unwrap() 68 | } 69 | 70 | // @TODO: Write comment 71 | fn cast_pool_mut_unsafe(pool: &Box) -> &mut ResourcePool { 72 | let ptr = cast_pool(pool.as_ref()) 73 | as *const ResourcePool as *mut ResourcePool; 74 | unsafe { transmute(ptr) } 75 | } 76 | 77 | impl ResourcePool { 78 | pub fn new() -> Self { 79 | ResourcePool { 80 | resources: Vec::new(), 81 | } 82 | } 83 | 84 | pub fn add(&mut self, resource: T) -> ResourceId { 85 | let id = self.resources.len(); 86 | self.resources.push(resource); 87 | ResourceId::new(id) 88 | } 89 | 90 | pub fn borrow(&self, r_id: &ResourceId) -> Option<&T> { 91 | if r_id.id < self.resources.len() { 92 | Some(&self.resources[r_id.id]) 93 | } else { 94 | None 95 | } 96 | } 97 | 98 | pub fn borrow_mut(&mut self, r_id: &ResourceId) -> Option<&mut T> { 99 | if r_id.id < self.resources.len() { 100 | Some(&mut self.resources[r_id.id]) 101 | } else { 102 | None 103 | } 104 | } 105 | } 106 | 107 | pub struct ResourcePools { 108 | pools: HashMap>, 109 | } 110 | 111 | impl ResourcePools { 112 | pub fn new() -> Self { 113 | let mut pools = HashMap::new(); 114 | Self::add::(&mut pools); 115 | Self::add::(&mut pools); 116 | Self::add::(&mut pools); 117 | Self::add::(&mut pools); 118 | Self::add::>(&mut pools); 119 | Self::add::(&mut pools); 120 | Self::add::(&mut pools); 121 | Self::add::(&mut pools); 122 | Self::add::(&mut pools); 123 | Self::add::(&mut pools); 124 | Self::add::(&mut pools); 125 | 126 | ResourcePools { 127 | pools: pools, 128 | } 129 | } 130 | 131 | fn add(pools: &mut HashMap>) { 132 | pools.insert(TypeId::of::(), Box::new(ResourcePool::::new())); 133 | } 134 | 135 | pub fn borrow(&self) -> &ResourcePool { 136 | if let Some(pool) = self.pools.get(&TypeId::of::()) { 137 | cast_pool(pool.as_ref()) 138 | } else { 139 | // @TODO: Proper error handling 140 | // @TODO: Trait bound 141 | panic!("Unknown Type"); 142 | } 143 | } 144 | 145 | pub fn borrow_mut(&mut self) -> &mut ResourcePool { 146 | if let Some(pool) = self.pools.get_mut(&TypeId::of::()) { 147 | cast_pool_mut(pool.as_mut()) 148 | } else { 149 | // @TODO: Proper error handling 150 | panic!("Unknown Type"); 151 | } 152 | } 153 | 154 | // @TODO: Write comment 155 | pub fn borrow_mut_unsafe(&self) -> &mut ResourcePool { 156 | if let Some(pool) = self.pools.get(&TypeId::of::()) { 157 | cast_pool_mut_unsafe(pool) 158 | } else { 159 | // @TODO: Proper error handling 160 | // @TODO: Trait bound 161 | panic!("Unknown Type"); 162 | } 163 | } 164 | } 165 | 166 | pub struct ResourceId { 167 | pub id: usize, 168 | _phantom: PhantomData, 169 | } 170 | 171 | impl ResourceId { 172 | fn new(id: usize) -> Self { 173 | ResourceId { 174 | id: id, 175 | _phantom: PhantomData 176 | } 177 | } 178 | } 179 | 180 | impl Copy for ResourceId { 181 | } 182 | 183 | impl Clone for ResourceId { 184 | fn clone(&self) -> Self { 185 | *self 186 | } 187 | } 188 | 189 | impl Hash for ResourceId { 190 | fn hash(&self, state: &mut H) { 191 | self.id.hash(state); 192 | } 193 | } 194 | 195 | impl PartialEq for ResourceId { 196 | fn eq(&self, other: &Self) -> bool { 197 | self.id == other.id 198 | } 199 | } 200 | 201 | impl Eq for ResourceId { 202 | } 203 | -------------------------------------------------------------------------------- /web/examples/rotation/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../utils/window.rs"] 2 | mod window; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use winit::{ 6 | event::{Event, WindowEvent}, 7 | event_loop::{ControlFlow, EventLoop}, 8 | }; 9 | 10 | use window::{ 11 | create_window, 12 | get_window_device_pixel_ratio, 13 | get_window_inner_size, 14 | }; 15 | 16 | use wgpu_rust_renderer::{ 17 | math::color::Color, 18 | renderer::wgpu_renderer::WGPURendererOptions, 19 | resource::resource::{ 20 | ResourceId, 21 | ResourcePools, 22 | }, 23 | scene::{ 24 | camera::PerspectiveCamera, 25 | mesh::Mesh, 26 | node::Node, 27 | scene::Scene, 28 | }, 29 | utils::{ 30 | geometry_helper::GeometryHelper, 31 | material_helper::MaterialHelper, 32 | }, 33 | web::wgpu_web_renderer::WGPUWebRenderer, 34 | }; 35 | 36 | fn create_scene( 37 | pools: &mut ResourcePools 38 | ) -> (ResourceId, ResourceId, Vec>) { 39 | let mut objects = Vec::new(); 40 | let mut scene = Scene::new(); 41 | 42 | let geometry = GeometryHelper::create_plane( 43 | pools, 44 | 1.0, 45 | 1.0, 46 | ); 47 | 48 | let material = MaterialHelper::create_basic_material( 49 | pools, 50 | Color::set(&mut Color::create(), 0.0, 1.0, 0.0), 51 | ); 52 | 53 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 54 | let node = pools.borrow_mut::().add(Node::new()); 55 | scene.add_node(&node); 56 | scene.assign(&node, &mesh); 57 | objects.push(node); 58 | 59 | let window_size = get_window_inner_size(); 60 | let camera = pools.borrow_mut::().add( 61 | PerspectiveCamera::new( 62 | 60.0_f32.to_radians(), 63 | (window_size.0 / window_size.1) as f32, 64 | 0.1, 65 | 1000.0, 66 | ), 67 | ); 68 | 69 | let mut node = Node::new(); 70 | node.borrow_position_mut()[2] = 2.0; 71 | 72 | let node = pools.borrow_mut::().add(node); 73 | scene.add_node(&node); 74 | scene.assign(&node, &camera); 75 | 76 | (pools.borrow_mut::().add(scene), camera, objects) 77 | } 78 | 79 | fn resize( 80 | renderer: &mut WGPUWebRenderer, 81 | pools: &mut ResourcePools, 82 | camera: &ResourceId, 83 | width: u32, 84 | height: u32, 85 | ) { 86 | pools 87 | .borrow_mut::() 88 | .borrow_mut(camera) 89 | .unwrap() 90 | .set_aspect(width as f32 / height as f32); 91 | renderer.set_size(width as f64, height as f64); 92 | } 93 | 94 | fn update( 95 | pools: &mut ResourcePools, 96 | scene: &ResourceId, 97 | objects: &Vec>, 98 | ) { 99 | { 100 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 101 | node.borrow_rotation_mut()[2] += 0.01; 102 | } 103 | 104 | pools.borrow::() 105 | .borrow(scene) 106 | .unwrap() 107 | .update_matrices(pools); 108 | } 109 | 110 | fn render( 111 | renderer: &mut WGPUWebRenderer, 112 | pools: &ResourcePools, 113 | scene: &ResourceId, 114 | camera: &ResourceId, 115 | ) { 116 | renderer.render(pools, scene, camera); 117 | } 118 | 119 | #[wasm_bindgen(start)] 120 | pub async fn start() { 121 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 122 | console_log::init().expect("could not initialize logger"); 123 | 124 | let event_loop = EventLoop::new(); 125 | let window = create_window(&event_loop); 126 | 127 | use winit::platform::web::WindowExtWebSys; 128 | 129 | web_sys::window() 130 | .and_then(|win| win.document()) 131 | .and_then(|doc| doc.body()) 132 | .and_then(|body| { 133 | body.append_child(&web_sys::Element::from(window.canvas())) 134 | .ok() 135 | }) 136 | .expect("couldn't append canvas to document body"); 137 | 138 | let inner_size = get_window_inner_size(); 139 | let pixel_ratio = get_window_device_pixel_ratio(); 140 | 141 | let mut renderer = WGPUWebRenderer::new(&window, window.canvas(), WGPURendererOptions::default()).await; 142 | renderer.set_size(inner_size.0 as f64, inner_size.1 as f64); 143 | renderer.set_pixel_ratio(pixel_ratio as f64); 144 | 145 | let mut pools = ResourcePools::new(); 146 | let (scene, camera, objects) = create_scene(&mut pools); 147 | 148 | event_loop.run(move |event, _, control_flow| { 149 | *control_flow = ControlFlow::Poll; 150 | match event { 151 | Event::WindowEvent { 152 | event: WindowEvent::Resized(size), 153 | .. 154 | } => { 155 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 156 | update(&mut pools, &scene, &objects); 157 | render(&mut renderer, &mut pools, &scene, &camera); 158 | }, 159 | Event::RedrawEventsCleared => { 160 | window.request_redraw(); 161 | }, 162 | Event::RedrawRequested(_) => { 163 | update(&mut pools, &scene, &objects); 164 | render(&mut renderer, &mut pools, &scene, &camera); 165 | }, 166 | Event::WindowEvent { 167 | event: WindowEvent::CloseRequested, 168 | .. 169 | } => { 170 | *control_flow = ControlFlow::Exit; 171 | }, 172 | _ => {} 173 | } 174 | }); 175 | } 176 | -------------------------------------------------------------------------------- /examples/face_culling/main.rs: -------------------------------------------------------------------------------- 1 | use winit::{ 2 | event::{Event, WindowEvent}, 3 | event_loop::{ControlFlow, EventLoop}, 4 | window::Window, 5 | }; 6 | use wgpu_rust_renderer::{ 7 | math::color::Color, 8 | renderer::wgpu_renderer::{ 9 | WGPURenderer, 10 | WGPURendererOptions, 11 | }, 12 | resource::resource::{ 13 | ResourceId, 14 | ResourcePools, 15 | }, 16 | scene::{ 17 | camera::PerspectiveCamera, 18 | mesh::Mesh, 19 | node::Node, 20 | scene::Scene, 21 | }, 22 | utils::{ 23 | geometry_helper::GeometryHelper, 24 | material_helper::MaterialHelper, 25 | }, 26 | }; 27 | 28 | fn create_scene( 29 | window: &Window, 30 | pools: &mut ResourcePools 31 | ) -> (ResourceId, ResourceId, Vec>) { 32 | let mut objects = Vec::new(); 33 | let mut scene = Scene::new(); 34 | 35 | // 36 | 37 | let geometry = GeometryHelper::create_plane( 38 | pools, 39 | 1.0, 40 | 1.0, 41 | ); 42 | 43 | let material = MaterialHelper::create_basic_material( 44 | pools, 45 | Color::set(&mut Color::create(), 0.0, 1.0, 0.0), 46 | ); 47 | 48 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 49 | let mut node = Node::new(); 50 | node.borrow_position_mut()[0] = -0.5; 51 | let node = pools.borrow_mut::().add(node); 52 | scene.add_node(&node); 53 | scene.assign(&node, &mesh); 54 | objects.push(node); 55 | 56 | let material = MaterialHelper::create_basic_material( 57 | pools, 58 | Color::set(&mut Color::create(), 0.0, 0.0, 1.0), 59 | ); 60 | 61 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 62 | let mut node = Node::new(); 63 | node.borrow_position_mut()[0] = 0.5; 64 | node.borrow_rotation_mut()[1] = 180.0_f32.to_radians(); 65 | let node = pools.borrow_mut::().add(node); 66 | scene.add_node(&node); 67 | scene.assign(&node, &mesh); 68 | objects.push(node); 69 | 70 | let window_size = window.inner_size(); 71 | let camera = pools.borrow_mut::().add( 72 | PerspectiveCamera::new( 73 | 60.0_f32.to_radians(), 74 | window_size.width as f32 / window_size.height as f32, 75 | 0.1, 76 | 1000.0, 77 | ), 78 | ); 79 | 80 | let mut node = Node::new(); 81 | node.borrow_position_mut()[2] = 2.0; 82 | 83 | let node = pools.borrow_mut::().add(node); 84 | scene.add_node(&node); 85 | scene.assign(&node, &camera); 86 | 87 | (pools.borrow_mut::().add(scene), camera, objects) 88 | } 89 | 90 | fn resize( 91 | renderer: &mut WGPURenderer, 92 | pools: &mut ResourcePools, 93 | camera: &ResourceId, 94 | width: u32, 95 | height: u32, 96 | ) { 97 | pools 98 | .borrow_mut::() 99 | .borrow_mut(camera) 100 | .unwrap() 101 | .set_aspect(width as f32 / height as f32); 102 | renderer.set_size(width as f64, height as f64); 103 | } 104 | 105 | fn update( 106 | pools: &mut ResourcePools, 107 | scene: &ResourceId, 108 | objects: &Vec> 109 | ) { 110 | { 111 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 112 | node.borrow_rotation_mut()[1] += 0.01; 113 | let node = pools.borrow_mut::().borrow_mut(&objects[1]).unwrap(); 114 | node.borrow_rotation_mut()[1] += 0.01; 115 | } 116 | 117 | pools.borrow::() 118 | .borrow(scene) 119 | .unwrap() 120 | .update_matrices(pools); 121 | } 122 | 123 | fn render( 124 | renderer: &mut WGPURenderer, 125 | pools: &ResourcePools, 126 | scene: &ResourceId, 127 | camera: &ResourceId, 128 | ) { 129 | renderer.render(pools, scene, camera); 130 | } 131 | 132 | #[tokio::main] 133 | async fn main() { 134 | let event_loop = EventLoop::new(); 135 | let window = Window::new(&event_loop).unwrap(); 136 | 137 | let window_size = window.inner_size(); 138 | let pixel_ratio = window.scale_factor(); 139 | 140 | let mut renderer = WGPURenderer::new(&window, WGPURendererOptions::default()).await; 141 | renderer.set_size(window_size.width as f64, window_size.height as f64); 142 | renderer.set_pixel_ratio(pixel_ratio); 143 | 144 | let mut pools = ResourcePools::new(); 145 | let (scene, camera, objects) = create_scene(&window, &mut pools); 146 | 147 | event_loop.run(move |event, _, control_flow| { 148 | *control_flow = ControlFlow::Poll; 149 | match event { 150 | Event::WindowEvent { 151 | event: WindowEvent::Resized(size), 152 | .. 153 | } => { 154 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 155 | update(&mut pools, &scene, &objects); 156 | render(&mut renderer, &mut pools, &scene, &camera); 157 | }, 158 | Event::RedrawEventsCleared => { 159 | window.request_redraw(); 160 | }, 161 | Event::RedrawRequested(_) => { 162 | update(&mut pools, &scene, &objects); 163 | render(&mut renderer, &mut pools, &scene, &camera); 164 | }, 165 | Event::WindowEvent { 166 | event: WindowEvent::CloseRequested, 167 | .. 168 | } => { 169 | *control_flow = ControlFlow::Exit; 170 | }, 171 | _ => {} 172 | } 173 | }); 174 | } 175 | -------------------------------------------------------------------------------- /web/examples/gltf/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../utils/window.rs"] 2 | mod window; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use winit::{ 6 | event::{Event, WindowEvent}, 7 | event_loop::{ControlFlow, EventLoop}, 8 | }; 9 | 10 | use window::{ 11 | create_window, 12 | get_window_device_pixel_ratio, 13 | get_window_inner_size, 14 | }; 15 | 16 | use wgpu_rust_renderer::{ 17 | math::vector3::Vector3, 18 | renderer::wgpu_renderer::WGPURendererOptions, 19 | resource::resource::{ 20 | ResourceId, 21 | ResourcePools, 22 | }, 23 | scene::{ 24 | camera::PerspectiveCamera, 25 | node::Node, 26 | scene::Scene, 27 | }, 28 | utils::gltf_loader::GltfLoader, 29 | web::wgpu_web_renderer::WGPUWebRenderer, 30 | }; 31 | 32 | async fn create_scene( 33 | pools: &mut ResourcePools 34 | ) -> (ResourceId, ResourceId, Vec>) { 35 | let mut objects = Vec::new(); 36 | let scene_rid = pools.borrow_mut::().add(Scene::new()); 37 | 38 | let nodes = GltfLoader::load_gltf( 39 | pools, 40 | &scene_rid, 41 | // Path from index.html 42 | "./assets/", 43 | "DamagedHelmet.gltf", 44 | ).await; 45 | 46 | for node in nodes.iter() { 47 | pools.borrow_mut::() 48 | .borrow_mut(&scene_rid) 49 | .unwrap() 50 | .add_node(node); 51 | objects.push(*node); 52 | pools.borrow_mut::() 53 | .borrow_mut(node) 54 | .unwrap() 55 | .borrow_rotation_mut()[0] = 90.0_f32.to_radians(); 56 | } 57 | 58 | let window_size = get_window_inner_size(); 59 | let camera = pools.borrow_mut::().add( 60 | PerspectiveCamera::new( 61 | 60.0_f32.to_radians(), 62 | (window_size.0 / window_size.1) as f32, 63 | 0.1, 64 | 1000.0, 65 | ), 66 | ); 67 | 68 | let mut node = Node::new(); 69 | Vector3::set( 70 | node.borrow_position_mut(), 71 | 0.0, 0.0, 3.0, 72 | ); 73 | 74 | let node = pools.borrow_mut::().add(node); 75 | 76 | { 77 | let scene = pools.borrow_mut::().borrow_mut(&scene_rid).unwrap(); 78 | scene.add_node(&node); 79 | scene.assign(&node, &camera); 80 | } 81 | 82 | (scene_rid, camera, objects) 83 | } 84 | 85 | fn resize( 86 | renderer: &mut WGPUWebRenderer, 87 | pools: &mut ResourcePools, 88 | camera: &ResourceId, 89 | width: u32, 90 | height: u32, 91 | ) { 92 | pools 93 | .borrow_mut::() 94 | .borrow_mut(camera) 95 | .unwrap() 96 | .set_aspect(width as f32 / height as f32); 97 | renderer.set_size(width as f64, height as f64); 98 | } 99 | 100 | fn update( 101 | pools: &mut ResourcePools, 102 | scene: &ResourceId, 103 | objects: &Vec>, 104 | ) { 105 | { 106 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 107 | Vector3::add( 108 | node.borrow_rotation_mut(), 109 | &[0.0, 0.0, 0.01], 110 | ); 111 | } 112 | 113 | pools.borrow::() 114 | .borrow(scene) 115 | .unwrap() 116 | .update_matrices(pools); 117 | } 118 | 119 | fn render( 120 | renderer: &mut WGPUWebRenderer, 121 | pools: &ResourcePools, 122 | scene: &ResourceId, 123 | camera: &ResourceId, 124 | ) { 125 | renderer.render(pools, scene, camera); 126 | } 127 | 128 | #[wasm_bindgen(start)] 129 | pub async fn start() { 130 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 131 | console_log::init().expect("could not initialize logger"); 132 | 133 | let event_loop = EventLoop::new(); 134 | let window = create_window(&event_loop); 135 | 136 | use winit::platform::web::WindowExtWebSys; 137 | 138 | web_sys::window() 139 | .and_then(|win| win.document()) 140 | .and_then(|doc| doc.body()) 141 | .and_then(|body| { 142 | body.append_child(&web_sys::Element::from(window.canvas())) 143 | .ok() 144 | }) 145 | .expect("couldn't append canvas to document body"); 146 | 147 | let inner_size = get_window_inner_size(); 148 | let pixel_ratio = get_window_device_pixel_ratio(); 149 | 150 | let mut renderer = WGPUWebRenderer::new(&window, window.canvas(), WGPURendererOptions::default()).await; 151 | renderer.set_size(inner_size.0 as f64, inner_size.1 as f64); 152 | renderer.set_pixel_ratio(pixel_ratio as f64); 153 | 154 | let mut pools = ResourcePools::new(); 155 | let (scene, camera, objects) = create_scene(&mut pools).await; 156 | 157 | event_loop.run(move |event, _, control_flow| { 158 | *control_flow = ControlFlow::Poll; 159 | match event { 160 | Event::WindowEvent { 161 | event: WindowEvent::Resized(size), 162 | .. 163 | } => { 164 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 165 | update(&mut pools, &scene, &objects); 166 | render(&mut renderer, &mut pools, &scene, &camera); 167 | }, 168 | Event::RedrawEventsCleared => { 169 | window.request_redraw(); 170 | }, 171 | Event::RedrawRequested(_) => { 172 | update(&mut pools, &scene, &objects); 173 | render(&mut renderer, &mut pools, &scene, &camera); 174 | }, 175 | Event::WindowEvent { 176 | event: WindowEvent::CloseRequested, 177 | .. 178 | } => { 179 | *control_flow = ControlFlow::Exit; 180 | }, 181 | _ => {} 182 | } 183 | }); 184 | } 185 | -------------------------------------------------------------------------------- /web/examples/cube/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../utils/window.rs"] 2 | mod window; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use winit::{ 6 | event::{Event, WindowEvent}, 7 | event_loop::{ControlFlow, EventLoop}, 8 | }; 9 | 10 | use window::{ 11 | create_window, 12 | get_window_device_pixel_ratio, 13 | get_window_inner_size, 14 | }; 15 | 16 | use wgpu_rust_renderer::{ 17 | math::{ 18 | color::Color, 19 | vector3::Vector3, 20 | }, 21 | renderer::wgpu_renderer::WGPURendererOptions, 22 | resource::resource::{ 23 | ResourceId, 24 | ResourcePools, 25 | }, 26 | scene::{ 27 | camera::PerspectiveCamera, 28 | mesh::Mesh, 29 | node::Node, 30 | scene::Scene, 31 | }, 32 | utils::{ 33 | geometry_helper::GeometryHelper, 34 | material_helper::MaterialHelper, 35 | }, 36 | web::wgpu_web_renderer::WGPUWebRenderer, 37 | }; 38 | 39 | fn create_scene( 40 | pools: &mut ResourcePools 41 | ) -> (ResourceId, ResourceId, Vec>) { 42 | let mut objects = Vec::new(); 43 | let mut scene = Scene::new(); 44 | 45 | let geometry = GeometryHelper::create_box( 46 | pools, 47 | 1.0, 48 | 1.0, 49 | 1.0, 50 | ); 51 | 52 | let material = MaterialHelper::create_basic_material( 53 | pools, 54 | Color::set(&mut Color::create(), 0.5, 0.5, 1.0), 55 | ); 56 | 57 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 58 | let node = pools.borrow_mut::().add(Node::new()); 59 | scene.add_node(&node); 60 | scene.assign(&node, &mesh); 61 | objects.push(node); 62 | 63 | let window_size = get_window_inner_size(); 64 | let camera = pools.borrow_mut::().add( 65 | PerspectiveCamera::new( 66 | 60.0_f32.to_radians(), 67 | (window_size.0 / window_size.1) as f32, 68 | 0.1, 69 | 1000.0, 70 | ), 71 | ); 72 | 73 | let mut node = Node::new(); 74 | Vector3::set( 75 | node.borrow_position_mut(), 76 | 0.0, 0.0, 3.0, 77 | ); 78 | 79 | let node = pools.borrow_mut::().add(node); 80 | scene.add_node(&node); 81 | scene.assign(&node, &camera); 82 | 83 | (pools.borrow_mut::().add(scene), camera, objects) 84 | } 85 | 86 | fn resize( 87 | renderer: &mut WGPUWebRenderer, 88 | pools: &mut ResourcePools, 89 | camera: &ResourceId, 90 | width: u32, 91 | height: u32, 92 | ) { 93 | pools 94 | .borrow_mut::() 95 | .borrow_mut(camera) 96 | .unwrap() 97 | .set_aspect(width as f32 / height as f32); 98 | renderer.set_size(width as f64, height as f64); 99 | } 100 | 101 | fn update( 102 | pools: &mut ResourcePools, 103 | scene: &ResourceId, 104 | objects: &Vec>, 105 | ) { 106 | { 107 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 108 | Vector3::add( 109 | node.borrow_rotation_mut(), 110 | &[0.001, 0.01, 0.003], 111 | ); 112 | } 113 | 114 | pools.borrow::() 115 | .borrow(scene) 116 | .unwrap() 117 | .update_matrices(pools); 118 | } 119 | 120 | fn render( 121 | renderer: &mut WGPUWebRenderer, 122 | pools: &ResourcePools, 123 | scene: &ResourceId, 124 | camera: &ResourceId, 125 | ) { 126 | renderer.render(pools, scene, camera); 127 | } 128 | 129 | #[wasm_bindgen(start)] 130 | pub async fn start() { 131 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 132 | console_log::init().expect("could not initialize logger"); 133 | 134 | let event_loop = EventLoop::new(); 135 | let window = create_window(&event_loop); 136 | 137 | use winit::platform::web::WindowExtWebSys; 138 | 139 | web_sys::window() 140 | .and_then(|win| win.document()) 141 | .and_then(|doc| doc.body()) 142 | .and_then(|body| { 143 | body.append_child(&web_sys::Element::from(window.canvas())) 144 | .ok() 145 | }) 146 | .expect("couldn't append canvas to document body"); 147 | 148 | let inner_size = get_window_inner_size(); 149 | let pixel_ratio = get_window_device_pixel_ratio(); 150 | 151 | let mut renderer = WGPUWebRenderer::new(&window, window.canvas(), WGPURendererOptions::default()).await; 152 | renderer.set_size(inner_size.0 as f64, inner_size.1 as f64); 153 | renderer.set_pixel_ratio(pixel_ratio as f64); 154 | 155 | let mut pools = ResourcePools::new(); 156 | let (scene, camera, objects) = create_scene(&mut pools); 157 | 158 | event_loop.run(move |event, _, control_flow| { 159 | *control_flow = ControlFlow::Poll; 160 | match event { 161 | Event::WindowEvent { 162 | event: WindowEvent::Resized(size), 163 | .. 164 | } => { 165 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 166 | update(&mut pools, &scene, &objects); 167 | render(&mut renderer, &mut pools, &scene, &camera); 168 | }, 169 | Event::RedrawEventsCleared => { 170 | window.request_redraw(); 171 | }, 172 | Event::RedrawRequested(_) => { 173 | update(&mut pools, &scene, &objects); 174 | render(&mut renderer, &mut pools, &scene, &camera); 175 | }, 176 | Event::WindowEvent { 177 | event: WindowEvent::CloseRequested, 178 | .. 179 | } => { 180 | *control_flow = ControlFlow::Exit; 181 | }, 182 | _ => {} 183 | } 184 | }); 185 | } 186 | -------------------------------------------------------------------------------- /src/scene/node.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | math::{ 3 | euler::Euler, 4 | matrix4::Matrix4, 5 | quaternion::Quaternion, 6 | vector3::Vector3, 7 | }, 8 | resource::resource::{ 9 | ResourceId, 10 | ResourcePool, 11 | }, 12 | }; 13 | 14 | pub struct Node { 15 | children: Vec>, 16 | matrix: [f32; 16], 17 | parent: Option>, 18 | position: [f32; 3], 19 | quaternion: [f32; 4], 20 | rotation: [f32; 3], 21 | scale: [f32; 3], 22 | world_matrix: [f32; 16], 23 | } 24 | 25 | impl Node { 26 | pub fn new() -> Self { 27 | Node { 28 | children: Vec::new(), 29 | matrix: Matrix4::create(), 30 | parent: None, 31 | position: Vector3::create(), 32 | quaternion: Quaternion::create(), 33 | rotation: Euler::create(), 34 | scale: *Vector3::set(&mut Vector3::create(), 1.0, 1.0, 1.0), 35 | world_matrix: Matrix4::create(), 36 | } 37 | } 38 | 39 | pub fn borrow_parent(&self) -> Option<&ResourceId> { 40 | self.parent.as_ref() 41 | } 42 | 43 | pub fn borrow_children(&self) -> &Vec> { 44 | &self.children 45 | } 46 | 47 | pub fn borrow_position(&self) -> &[f32; 3] { 48 | &self.position 49 | } 50 | 51 | pub fn borrow_position_mut(&mut self) -> &mut [f32; 3] { 52 | &mut self.position 53 | } 54 | 55 | pub fn borrow_rotation(&self) -> &[f32; 3] { 56 | &self.rotation 57 | } 58 | 59 | pub fn borrow_rotation_mut(&mut self) -> &mut [f32; 3] { 60 | &mut self.rotation 61 | } 62 | 63 | pub fn borrow_scale(&self) -> &[f32; 3] { 64 | &self.scale 65 | } 66 | 67 | pub fn borrow_scale_mut(&mut self) -> &mut [f32; 3] { 68 | &mut self.scale 69 | } 70 | 71 | pub fn borrow_matrix(&self) -> &[f32; 16] { 72 | &self.matrix 73 | } 74 | 75 | pub fn borrow_world_matrix(&self) -> &[f32; 16] { 76 | &self.world_matrix 77 | } 78 | 79 | pub fn set_matrix(&mut self, matrix: &[f32; 16]) -> &mut Self { 80 | Matrix4::copy(&mut self.matrix, matrix); 81 | Matrix4::decompose(&mut self.position, &mut self.quaternion, &mut self.scale, &self.matrix); 82 | Euler::set_from_quaternion(&mut self.rotation, &self.quaternion); 83 | self 84 | } 85 | 86 | pub fn set_world_matrix(&mut self, matrix: &[f32; 16]) -> &mut Self { 87 | Matrix4::copy(&mut self.world_matrix, matrix); 88 | self 89 | } 90 | 91 | pub fn update_matrix(&mut self) -> &mut Self { 92 | Quaternion::set_from_euler(&mut self.quaternion, &self.rotation); 93 | Matrix4::compose(&mut self.matrix, &self.position, &self.quaternion, &self.scale); 94 | self 95 | } 96 | 97 | // @TODO: Optimize 98 | pub fn update_matrices( 99 | &mut self, 100 | pool: &mut ResourcePool, 101 | ) { 102 | self.update_matrix(); 103 | 104 | if let Some(parent) = self.borrow_parent() { 105 | let parent_matrix = pool.borrow(parent).unwrap().borrow_world_matrix(); 106 | Matrix4::multiply(&mut self.world_matrix, parent_matrix, &self.matrix); 107 | } else { 108 | Matrix4::copy(&mut self.world_matrix, &self.matrix); 109 | } 110 | 111 | let mut stack = Vec::new(); 112 | 113 | for child in self.children.iter() { 114 | stack.push(*child); 115 | } 116 | 117 | while let Some(rid) = stack.pop() { 118 | let parent_matrix = { 119 | let mut matrix = Matrix4::create(); 120 | let node = pool.borrow_mut(&rid).unwrap(); 121 | let parent = node.borrow_parent().cloned().unwrap(); 122 | Matrix4::copy(&mut matrix, pool.borrow(&parent).unwrap().borrow_world_matrix()); 123 | matrix 124 | }; 125 | 126 | let node = pool.borrow_mut(&rid).unwrap(); 127 | let mut matrix = Matrix4::create(); 128 | Matrix4::multiply(&mut matrix, &parent_matrix, &node.borrow_matrix()); 129 | node.set_world_matrix(&matrix); 130 | 131 | for child in node.children.iter() { 132 | stack.push(*child); 133 | } 134 | } 135 | } 136 | } 137 | 138 | pub struct NodeExecutor { 139 | } 140 | 141 | impl NodeExecutor { 142 | pub fn update_matrices( 143 | pool: &mut ResourcePool, 144 | root: &ResourceId, 145 | ) { 146 | let mut stack = Vec::new(); 147 | stack.push(*root); 148 | 149 | while let Some(rid) = stack.pop() { 150 | let node = pool.borrow_mut(&rid).unwrap(); 151 | node.update_matrix(); 152 | 153 | let parent_matrix = { 154 | let mut matrix = Matrix4::create(); 155 | let node = pool.borrow_mut(&rid).unwrap(); 156 | if let Some(parent) = node.borrow_parent().cloned() { 157 | Matrix4::copy(&mut matrix, pool.borrow(&parent).unwrap().borrow_world_matrix()); 158 | } 159 | matrix 160 | }; 161 | 162 | let node = pool.borrow_mut(&rid).unwrap(); 163 | let mut matrix = Matrix4::create(); 164 | Matrix4::multiply(&mut matrix, &parent_matrix, &node.borrow_matrix()); 165 | node.set_world_matrix(&matrix); 166 | 167 | for child in node.children.iter() { 168 | stack.push(*child); 169 | } 170 | } 171 | } 172 | 173 | pub fn collect_nodes( 174 | pool: &ResourcePool, 175 | root: &ResourceId, 176 | nodes: &mut Vec>, 177 | ) { 178 | let mut stack = Vec::new(); 179 | stack.push(*root); 180 | nodes.push(*root); 181 | 182 | while let Some(rid) = stack.pop() { 183 | let node = pool.borrow(&rid).unwrap(); 184 | for child in node.children.iter() { 185 | stack.push(*child); 186 | nodes.push(*child); 187 | } 188 | } 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/material/node/brdf.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use crate::{ 3 | material::node::node::{ 4 | MaterialNode, 5 | UniformContents, 6 | }, 7 | resource::resource::{ 8 | ResourceId, 9 | ResourcePool, 10 | }, 11 | }; 12 | 13 | const FUNCTION_CHUNK: &str = " 14 | fn d_ggx(n_dot_h: f32, roughness: f32) -> f32 { 15 | let a: f32 = n_dot_h * roughness; 16 | let k: f32 = roughness / (1.0 - pow(n_dot_h, 2.0) + pow(a, 2.0)); 17 | return pow(k, 2.0) * (1.0 / PI); 18 | } 19 | 20 | fn v_smith_ggx_correlated_fast(n_dot_v: f32, n_dot_l: f32, roughness: f32) -> f32 { 21 | let a: f32 = roughness; 22 | let ggxv: f32 = n_dot_l * (n_dot_v * (1.0 - a) + a); 23 | let ggxl: f32 = n_dot_v * (n_dot_l * (1.0 - a) + a); 24 | return 0.5 / (ggxv + ggxl); 25 | } 26 | 27 | fn brdf( 28 | v: vec3, 29 | n: vec3, 30 | h: vec3, 31 | l: vec3, 32 | base_color: vec3, 33 | metallic: f32, 34 | roughness: f32 35 | ) -> vec3 { 36 | let black = vec3(0.0); 37 | let v_dot_h = dot(v, h); 38 | let n_dot_v = dot(v, n); 39 | let n_dot_l = dot(l, n); 40 | let n_dot_h = dot(n, h); 41 | 42 | let c_diff = mix(base_color, black, metallic); 43 | let f0 = mix(vec3(0.04), base_color, metallic); 44 | let alpha = pow(roughness, 2.0); 45 | 46 | let f = f0 + (1.0 - f0) * pow(1.0 - abs(v_dot_h), 5.0); 47 | 48 | let f_diffuse = (1.0 - f) * (1.0 / PI) * c_diff; 49 | let f_specular = f * d_ggx(n_dot_h, alpha) 50 | * v_smith_ggx_correlated_fast(n_dot_v, n_dot_l, alpha) 51 | / (4.0 * abs(n_dot_v) * abs(n_dot_l)); 52 | 53 | return f_diffuse + f_specular; 54 | } 55 | "; 56 | 57 | pub struct BRDFNodeDescriptor { 58 | pub base_color: ResourceId>, 59 | pub metallic: ResourceId>, 60 | pub normal: ResourceId>, 61 | pub roughness: ResourceId>, 62 | } 63 | 64 | pub struct BRDFNode { 65 | desc: BRDFNodeDescriptor, 66 | } 67 | 68 | impl BRDFNode { 69 | pub fn new( 70 | desc: BRDFNodeDescriptor, 71 | ) -> Self { 72 | BRDFNode { 73 | desc: desc, 74 | } 75 | } 76 | } 77 | 78 | impl MaterialNode for BRDFNode { 79 | fn collect_nodes ( 80 | &self, 81 | pool: &ResourcePool>, 82 | nodes: &mut Vec>>, 83 | visited: &mut HashMap>, bool>, 84 | self_rid: ResourceId>, 85 | ) { 86 | pool.borrow(&self.desc.base_color).unwrap().collect_nodes( 87 | pool, nodes, visited, self.desc.base_color, 88 | ); 89 | pool.borrow(&self.desc.metallic).unwrap().collect_nodes( 90 | pool, nodes, visited, self.desc.metallic, 91 | ); 92 | pool.borrow(&self.desc.normal).unwrap().collect_nodes( 93 | pool, nodes, visited, self.desc.normal, 94 | ); 95 | pool.borrow(&self.desc.roughness).unwrap().collect_nodes( 96 | pool, nodes, visited, self.desc.roughness, 97 | ); 98 | if !visited.contains_key(&self_rid) { 99 | visited.insert(self_rid, true); 100 | nodes.push(self_rid); 101 | } 102 | } 103 | 104 | fn borrow_contents(&self) -> Option<&UniformContents> { 105 | None 106 | } 107 | 108 | fn build_declaration(&self, _self_id: usize) -> String { 109 | format!("") 110 | } 111 | 112 | fn build_functions(&self, _self_id: usize) -> String { 113 | // @TODO: Add self_id suffix for unique function name 114 | FUNCTION_CHUNK.to_string() 115 | } 116 | 117 | fn build_fragment_shader( 118 | &self, 119 | pool: &ResourcePool>, 120 | visited: &mut HashMap, 121 | self_id: usize, 122 | ) -> String { 123 | if visited.contains_key(&self_id) { 124 | return "".to_string(); 125 | } 126 | visited.insert(self_id, true); 127 | 128 | let base_color = pool.borrow(&self.desc.base_color).unwrap(); 129 | let metallic = pool.borrow(&self.desc.metallic).unwrap(); 130 | let normal = pool.borrow(&self.desc.normal).unwrap(); 131 | let roughness = pool.borrow(&self.desc.roughness).unwrap(); 132 | 133 | base_color.build_fragment_shader(pool, visited, self.desc.base_color.id) + 134 | &metallic.build_fragment_shader(pool, visited, self.desc.metallic.id) + 135 | &normal.build_fragment_shader(pool, visited, self.desc.normal.id) + 136 | &roughness.build_fragment_shader(pool, visited, self.desc.roughness.id) + 137 | &format!("let brdf_v_{} = normalize(in.view_position);\n", self_id) + 138 | &format!("let brdf_l_{} = normalize(light_dir);\n", self_id) + 139 | &format!("let brdf_n_{} = normalize({});\n", 140 | self_id, 141 | normal.get_fragment_output(self.desc.normal.id), 142 | ) + 143 | &format!("let brdf_h_{} = normalize(brdf_l_{} + brdf_v_{});\n", self_id, self_id, self_id) + 144 | &format!("let {} = brdf(brdf_v_{}, brdf_n_{}, brdf_h_{}, brdf_l_{}, {}, {}, {});\n", 145 | self.get_fragment_output(self_id), 146 | self_id, 147 | self_id, 148 | self_id, 149 | self_id, 150 | base_color.get_fragment_output(self.desc.base_color.id), 151 | metallic.get_fragment_output(self.desc.metallic.id), 152 | roughness.get_fragment_output(self.desc.roughness.id) 153 | ) + 154 | // @TODO: Fix me 155 | &format!("use_directional_light = false;\n") 156 | } 157 | 158 | fn get_fragment_output(&self, self_id: usize) -> String { 159 | format!("brdf_output_{}", self_id) 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /examples/gltf/assets/DamagedHelmet.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "componentType" : 5123, 6 | "count" : 46356, 7 | "max" : [ 8 | 14555 9 | ], 10 | "min" : [ 11 | 0 12 | ], 13 | "type" : "SCALAR" 14 | }, 15 | { 16 | "bufferView" : 1, 17 | "componentType" : 5126, 18 | "count" : 14556, 19 | "max" : [ 20 | 0.9424954056739807, 21 | 0.8128451108932495, 22 | 0.900973916053772 23 | ], 24 | "min" : [ 25 | -0.9474585652351379, 26 | -1.18715500831604, 27 | -0.9009949564933777 28 | ], 29 | "type" : "VEC3" 30 | }, 31 | { 32 | "bufferView" : 2, 33 | "componentType" : 5126, 34 | "count" : 14556, 35 | "max" : [ 36 | 1.0, 37 | 1.0, 38 | 1.0 39 | ], 40 | "min" : [ 41 | -1.0, 42 | -1.0, 43 | -1.0 44 | ], 45 | "type" : "VEC3" 46 | }, 47 | { 48 | "bufferView" : 3, 49 | "componentType" : 5126, 50 | "count" : 14556, 51 | "max" : [ 52 | 0.9999759793281555, 53 | 1.998665988445282 54 | ], 55 | "min" : [ 56 | 0.002448640065267682, 57 | 1.0005531199858524 58 | ], 59 | "type" : "VEC2" 60 | } 61 | ], 62 | "asset" : { 63 | "generator" : "Khronos Blender glTF 2.0 exporter", 64 | "version" : "2.0" 65 | }, 66 | "bufferViews" : [ 67 | { 68 | "buffer" : 0, 69 | "byteLength" : 92712, 70 | "byteOffset" : 0, 71 | "target" : 34963 72 | }, 73 | { 74 | "buffer" : 0, 75 | "byteLength" : 174672, 76 | "byteOffset" : 92712, 77 | "target" : 34962 78 | }, 79 | { 80 | "buffer" : 0, 81 | "byteLength" : 174672, 82 | "byteOffset" : 267384, 83 | "target" : 34962 84 | }, 85 | { 86 | "buffer" : 0, 87 | "byteLength" : 116448, 88 | "byteOffset" : 442056, 89 | "target" : 34962 90 | } 91 | ], 92 | "buffers" : [ 93 | { 94 | "byteLength" : 558504, 95 | "uri" : "DamagedHelmet.bin" 96 | } 97 | ], 98 | "images" : [ 99 | { 100 | "uri" : "Default_albedo.jpg" 101 | }, 102 | { 103 | "uri" : "Default_metalRoughness.jpg" 104 | }, 105 | { 106 | "uri" : "Default_emissive.jpg" 107 | }, 108 | { 109 | "uri" : "Default_AO.jpg" 110 | }, 111 | { 112 | "uri" : "Default_normal.jpg" 113 | } 114 | ], 115 | "materials" : [ 116 | { 117 | "emissiveFactor" : [ 118 | 1.0, 119 | 1.0, 120 | 1.0 121 | ], 122 | "emissiveTexture" : { 123 | "index" : 2 124 | }, 125 | "name" : "Material_MR", 126 | "normalTexture" : { 127 | "index" : 4 128 | }, 129 | "occlusionTexture" : { 130 | "index" : 3 131 | }, 132 | "pbrMetallicRoughness" : { 133 | "baseColorTexture" : { 134 | "index" : 0 135 | }, 136 | "metallicRoughnessTexture" : { 137 | "index" : 1 138 | } 139 | } 140 | } 141 | ], 142 | "meshes" : [ 143 | { 144 | "name" : "mesh_helmet_LP_13930damagedHelmet", 145 | "primitives" : [ 146 | { 147 | "attributes" : { 148 | "NORMAL" : 2, 149 | "POSITION" : 1, 150 | "TEXCOORD_0" : 3 151 | }, 152 | "indices" : 0, 153 | "material" : 0 154 | } 155 | ] 156 | } 157 | ], 158 | "nodes" : [ 159 | { 160 | "mesh" : 0, 161 | "name" : "node_damagedHelmet_-6514", 162 | "rotation" : [ 163 | 0.7071068286895752, 164 | 0.0, 165 | -0.0, 166 | 0.7071068286895752 167 | ] 168 | } 169 | ], 170 | "samplers" : [ 171 | {} 172 | ], 173 | "scene" : 0, 174 | "scenes" : [ 175 | { 176 | "name" : "Scene", 177 | "nodes" : [ 178 | 0 179 | ] 180 | } 181 | ], 182 | "textures" : [ 183 | { 184 | "sampler" : 0, 185 | "source" : 0 186 | }, 187 | { 188 | "sampler" : 0, 189 | "source" : 1 190 | }, 191 | { 192 | "sampler" : 0, 193 | "source" : 2 194 | }, 195 | { 196 | "sampler" : 0, 197 | "source" : 3 198 | }, 199 | { 200 | "sampler" : 0, 201 | "source" : 4 202 | } 203 | ] 204 | } 205 | -------------------------------------------------------------------------------- /web/examples/gltf/assets/DamagedHelmet.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "accessors" : [ 3 | { 4 | "bufferView" : 0, 5 | "componentType" : 5123, 6 | "count" : 46356, 7 | "max" : [ 8 | 14555 9 | ], 10 | "min" : [ 11 | 0 12 | ], 13 | "type" : "SCALAR" 14 | }, 15 | { 16 | "bufferView" : 1, 17 | "componentType" : 5126, 18 | "count" : 14556, 19 | "max" : [ 20 | 0.9424954056739807, 21 | 0.8128451108932495, 22 | 0.900973916053772 23 | ], 24 | "min" : [ 25 | -0.9474585652351379, 26 | -1.18715500831604, 27 | -0.9009949564933777 28 | ], 29 | "type" : "VEC3" 30 | }, 31 | { 32 | "bufferView" : 2, 33 | "componentType" : 5126, 34 | "count" : 14556, 35 | "max" : [ 36 | 1.0, 37 | 1.0, 38 | 1.0 39 | ], 40 | "min" : [ 41 | -1.0, 42 | -1.0, 43 | -1.0 44 | ], 45 | "type" : "VEC3" 46 | }, 47 | { 48 | "bufferView" : 3, 49 | "componentType" : 5126, 50 | "count" : 14556, 51 | "max" : [ 52 | 0.9999759793281555, 53 | 1.998665988445282 54 | ], 55 | "min" : [ 56 | 0.002448640065267682, 57 | 1.0005531199858524 58 | ], 59 | "type" : "VEC2" 60 | } 61 | ], 62 | "asset" : { 63 | "generator" : "Khronos Blender glTF 2.0 exporter", 64 | "version" : "2.0" 65 | }, 66 | "bufferViews" : [ 67 | { 68 | "buffer" : 0, 69 | "byteLength" : 92712, 70 | "byteOffset" : 0, 71 | "target" : 34963 72 | }, 73 | { 74 | "buffer" : 0, 75 | "byteLength" : 174672, 76 | "byteOffset" : 92712, 77 | "target" : 34962 78 | }, 79 | { 80 | "buffer" : 0, 81 | "byteLength" : 174672, 82 | "byteOffset" : 267384, 83 | "target" : 34962 84 | }, 85 | { 86 | "buffer" : 0, 87 | "byteLength" : 116448, 88 | "byteOffset" : 442056, 89 | "target" : 34962 90 | } 91 | ], 92 | "buffers" : [ 93 | { 94 | "byteLength" : 558504, 95 | "uri" : "DamagedHelmet.bin" 96 | } 97 | ], 98 | "images" : [ 99 | { 100 | "uri" : "Default_albedo.jpg" 101 | }, 102 | { 103 | "uri" : "Default_metalRoughness.jpg" 104 | }, 105 | { 106 | "uri" : "Default_emissive.jpg" 107 | }, 108 | { 109 | "uri" : "Default_AO.jpg" 110 | }, 111 | { 112 | "uri" : "Default_normal.jpg" 113 | } 114 | ], 115 | "materials" : [ 116 | { 117 | "emissiveFactor" : [ 118 | 1.0, 119 | 1.0, 120 | 1.0 121 | ], 122 | "emissiveTexture" : { 123 | "index" : 2 124 | }, 125 | "name" : "Material_MR", 126 | "normalTexture" : { 127 | "index" : 4 128 | }, 129 | "occlusionTexture" : { 130 | "index" : 3 131 | }, 132 | "pbrMetallicRoughness" : { 133 | "baseColorTexture" : { 134 | "index" : 0 135 | }, 136 | "metallicRoughnessTexture" : { 137 | "index" : 1 138 | } 139 | } 140 | } 141 | ], 142 | "meshes" : [ 143 | { 144 | "name" : "mesh_helmet_LP_13930damagedHelmet", 145 | "primitives" : [ 146 | { 147 | "attributes" : { 148 | "NORMAL" : 2, 149 | "POSITION" : 1, 150 | "TEXCOORD_0" : 3 151 | }, 152 | "indices" : 0, 153 | "material" : 0 154 | } 155 | ] 156 | } 157 | ], 158 | "nodes" : [ 159 | { 160 | "mesh" : 0, 161 | "name" : "node_damagedHelmet_-6514", 162 | "rotation" : [ 163 | 0.7071068286895752, 164 | 0.0, 165 | -0.0, 166 | 0.7071068286895752 167 | ] 168 | } 169 | ], 170 | "samplers" : [ 171 | {} 172 | ], 173 | "scene" : 0, 174 | "scenes" : [ 175 | { 176 | "name" : "Scene", 177 | "nodes" : [ 178 | 0 179 | ] 180 | } 181 | ], 182 | "textures" : [ 183 | { 184 | "sampler" : 0, 185 | "source" : 0 186 | }, 187 | { 188 | "sampler" : 0, 189 | "source" : 1 190 | }, 191 | { 192 | "sampler" : 0, 193 | "source" : 2 194 | }, 195 | { 196 | "sampler" : 0, 197 | "source" : 3 198 | }, 199 | { 200 | "sampler" : 0, 201 | "source" : 4 202 | } 203 | ] 204 | } 205 | -------------------------------------------------------------------------------- /web/examples/texture/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../utils/window.rs"] 2 | mod window; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use winit::{ 6 | event::{Event, WindowEvent}, 7 | event_loop::{ControlFlow, EventLoop}, 8 | }; 9 | 10 | use window::{ 11 | create_window, 12 | get_window_device_pixel_ratio, 13 | get_window_inner_size, 14 | }; 15 | 16 | use wgpu_rust_renderer::{ 17 | math::{ 18 | color::Color, 19 | vector3::Vector3, 20 | }, 21 | renderer::wgpu_renderer::WGPURendererOptions, 22 | resource::resource::{ 23 | ResourceId, 24 | ResourcePools, 25 | }, 26 | scene::{ 27 | camera::PerspectiveCamera, 28 | mesh::Mesh, 29 | node::Node, 30 | scene::Scene, 31 | }, 32 | texture::texture::TextureFormat, 33 | utils::{ 34 | geometry_helper::GeometryHelper, 35 | material_helper::MaterialHelper, 36 | texture_loader::TextureLoader, 37 | }, 38 | web::wgpu_web_renderer::WGPUWebRenderer, 39 | }; 40 | 41 | async fn create_scene( 42 | pools: &mut ResourcePools 43 | ) -> (ResourceId, ResourceId, Vec>) { 44 | let mut objects = Vec::new(); 45 | let mut scene = Scene::new(); 46 | 47 | let geometry = GeometryHelper::create_box( 48 | pools, 49 | 1.0, 50 | 1.0, 51 | 1.0, 52 | ); 53 | 54 | let texture = TextureLoader::load_png_with_filepath( 55 | pools, 56 | // Path from index.html 57 | "./assets/texture.png", 58 | TextureFormat::default(), 59 | ).await; 60 | 61 | let material = MaterialHelper::create_basic_material_with_texture( 62 | pools, 63 | Color::set(&mut Color::create(), 1.0, 1.0, 1.0), 64 | texture, 65 | ); 66 | 67 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 68 | let mut node = Node::new(); 69 | node.borrow_rotation_mut()[0] = 35.0_f32.to_radians(); 70 | let node = pools.borrow_mut::().add(node); 71 | scene.add_node(&node); 72 | scene.assign(&node, &mesh); 73 | objects.push(node); 74 | 75 | let window_size = get_window_inner_size(); 76 | let camera = pools.borrow_mut::().add( 77 | PerspectiveCamera::new( 78 | 60.0_f32.to_radians(), 79 | (window_size.0 / window_size.1) as f32, 80 | 0.1, 81 | 1000.0, 82 | ), 83 | ); 84 | 85 | let mut node = Node::new(); 86 | Vector3::set( 87 | node.borrow_position_mut(), 88 | 0.0, 0.0, 3.0, 89 | ); 90 | 91 | let node = pools.borrow_mut::().add(node); 92 | scene.add_node(&node); 93 | scene.assign(&node, &camera); 94 | 95 | (pools.borrow_mut::().add(scene), camera, objects) 96 | } 97 | 98 | fn resize( 99 | renderer: &mut WGPUWebRenderer, 100 | pools: &mut ResourcePools, 101 | camera: &ResourceId, 102 | width: u32, 103 | height: u32, 104 | ) { 105 | pools 106 | .borrow_mut::() 107 | .borrow_mut(camera) 108 | .unwrap() 109 | .set_aspect(width as f32 / height as f32); 110 | renderer.set_size(width as f64, height as f64); 111 | } 112 | 113 | fn update( 114 | pools: &mut ResourcePools, 115 | scene: &ResourceId, 116 | objects: &Vec>, 117 | ) { 118 | { 119 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 120 | Vector3::add( 121 | node.borrow_rotation_mut(), 122 | &[0.0, 0.01, 0.0], 123 | ); 124 | } 125 | 126 | pools.borrow::() 127 | .borrow(scene) 128 | .unwrap() 129 | .update_matrices(pools); 130 | } 131 | 132 | fn render( 133 | renderer: &mut WGPUWebRenderer, 134 | pools: &ResourcePools, 135 | scene: &ResourceId, 136 | camera: &ResourceId, 137 | ) { 138 | renderer.render(pools, scene, camera); 139 | } 140 | 141 | #[wasm_bindgen(start)] 142 | pub async fn start() { 143 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 144 | console_log::init().expect("could not initialize logger"); 145 | 146 | let event_loop = EventLoop::new(); 147 | let window = create_window(&event_loop); 148 | 149 | use winit::platform::web::WindowExtWebSys; 150 | 151 | web_sys::window() 152 | .and_then(|win| win.document()) 153 | .and_then(|doc| doc.body()) 154 | .and_then(|body| { 155 | body.append_child(&web_sys::Element::from(window.canvas())) 156 | .ok() 157 | }) 158 | .expect("couldn't append canvas to document body"); 159 | 160 | let inner_size = get_window_inner_size(); 161 | let pixel_ratio = get_window_device_pixel_ratio(); 162 | 163 | let mut renderer = WGPUWebRenderer::new(&window, window.canvas(), WGPURendererOptions::default()).await; 164 | renderer.set_size(inner_size.0 as f64, inner_size.1 as f64); 165 | renderer.set_pixel_ratio(pixel_ratio as f64); 166 | 167 | let mut pools = ResourcePools::new(); 168 | let (scene, camera, objects) = create_scene(&mut pools).await; 169 | 170 | event_loop.run(move |event, _, control_flow| { 171 | *control_flow = ControlFlow::Poll; 172 | match event { 173 | Event::WindowEvent { 174 | event: WindowEvent::Resized(size), 175 | .. 176 | } => { 177 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 178 | update(&mut pools, &scene, &objects); 179 | render(&mut renderer, &mut pools, &scene, &camera); 180 | }, 181 | Event::RedrawEventsCleared => { 182 | window.request_redraw(); 183 | }, 184 | Event::RedrawRequested(_) => { 185 | update(&mut pools, &scene, &objects); 186 | render(&mut renderer, &mut pools, &scene, &camera); 187 | }, 188 | Event::WindowEvent { 189 | event: WindowEvent::CloseRequested, 190 | .. 191 | } => { 192 | *control_flow = ControlFlow::Exit; 193 | }, 194 | _ => {} 195 | } 196 | }); 197 | } 198 | -------------------------------------------------------------------------------- /web/examples/face_culling/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[path = "../../utils/window.rs"] 2 | mod window; 3 | 4 | use wasm_bindgen::prelude::*; 5 | use winit::{ 6 | event::{Event, WindowEvent}, 7 | event_loop::{ControlFlow, EventLoop}, 8 | }; 9 | 10 | use window::{ 11 | create_window, 12 | get_window_device_pixel_ratio, 13 | get_window_inner_size, 14 | }; 15 | 16 | use wgpu_rust_renderer::{ 17 | math::color::Color, 18 | renderer::wgpu_renderer::WGPURendererOptions, 19 | resource::resource::{ 20 | ResourceId, 21 | ResourcePools, 22 | }, 23 | scene::{ 24 | camera::PerspectiveCamera, 25 | mesh::Mesh, 26 | node::Node, 27 | scene::Scene, 28 | }, 29 | utils::{ 30 | geometry_helper::GeometryHelper, 31 | material_helper::MaterialHelper, 32 | }, 33 | web::wgpu_web_renderer::WGPUWebRenderer, 34 | }; 35 | 36 | fn create_scene( 37 | pools: &mut ResourcePools 38 | ) -> (ResourceId, ResourceId, Vec>) { 39 | let mut objects = Vec::new(); 40 | let mut scene = Scene::new(); 41 | 42 | let geometry = GeometryHelper::create_plane( 43 | pools, 44 | 1.0, 45 | 1.0, 46 | ); 47 | 48 | let material = MaterialHelper::create_basic_material( 49 | pools, 50 | Color::set(&mut Color::create(), 0.0, 1.0, 0.0), 51 | ); 52 | 53 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 54 | let mut node = Node::new(); 55 | node.borrow_position_mut()[0] = -0.5; 56 | let node = pools.borrow_mut::().add(node); 57 | scene.add_node(&node); 58 | scene.assign(&node, &mesh); 59 | objects.push(node); 60 | 61 | let material = MaterialHelper::create_basic_material( 62 | pools, 63 | Color::set(&mut Color::create(), 0.0, 0.0, 1.0), 64 | ); 65 | 66 | let mesh = pools.borrow_mut::().add(Mesh::new(geometry, material)); 67 | let mut node = Node::new(); 68 | node.borrow_position_mut()[0] = 0.5; 69 | node.borrow_rotation_mut()[1] = 180.0_f32.to_radians(); 70 | let node = pools.borrow_mut::().add(node); 71 | scene.add_node(&node); 72 | scene.assign(&node, &mesh); 73 | objects.push(node); 74 | 75 | let window_size = get_window_inner_size(); 76 | let camera = pools.borrow_mut::().add( 77 | PerspectiveCamera::new( 78 | 60.0_f32.to_radians(), 79 | (window_size.0 / window_size.1) as f32, 80 | 0.1, 81 | 1000.0, 82 | ), 83 | ); 84 | 85 | let mut node = Node::new(); 86 | node.borrow_position_mut()[2] = 2.0; 87 | 88 | let node = pools.borrow_mut::().add(node); 89 | scene.add_node(&node); 90 | scene.assign(&node, &camera); 91 | 92 | (pools.borrow_mut::().add(scene), camera, objects) 93 | } 94 | 95 | fn resize( 96 | renderer: &mut WGPUWebRenderer, 97 | pools: &mut ResourcePools, 98 | camera: &ResourceId, 99 | width: u32, 100 | height: u32, 101 | ) { 102 | pools 103 | .borrow_mut::() 104 | .borrow_mut(camera) 105 | .unwrap() 106 | .set_aspect(width as f32 / height as f32); 107 | renderer.set_size(width as f64, height as f64); 108 | } 109 | 110 | fn update( 111 | pools: &mut ResourcePools, 112 | scene: &ResourceId, 113 | objects: &Vec>, 114 | ) { 115 | { 116 | let node = pools.borrow_mut::().borrow_mut(&objects[0]).unwrap(); 117 | node.borrow_rotation_mut()[1] += 0.01; 118 | let node = pools.borrow_mut::().borrow_mut(&objects[1]).unwrap(); 119 | node.borrow_rotation_mut()[1] += 0.01; 120 | } 121 | 122 | pools.borrow::() 123 | .borrow(scene) 124 | .unwrap() 125 | .update_matrices(pools); 126 | } 127 | 128 | fn render( 129 | renderer: &mut WGPUWebRenderer, 130 | pools: &ResourcePools, 131 | scene: &ResourceId, 132 | camera: &ResourceId, 133 | ) { 134 | renderer.render(pools, scene, camera); 135 | } 136 | 137 | #[wasm_bindgen(start)] 138 | pub async fn start() { 139 | std::panic::set_hook(Box::new(console_error_panic_hook::hook)); 140 | console_log::init().expect("could not initialize logger"); 141 | 142 | let event_loop = EventLoop::new(); 143 | let window = create_window(&event_loop); 144 | 145 | use winit::platform::web::WindowExtWebSys; 146 | 147 | web_sys::window() 148 | .and_then(|win| win.document()) 149 | .and_then(|doc| doc.body()) 150 | .and_then(|body| { 151 | body.append_child(&web_sys::Element::from(window.canvas())) 152 | .ok() 153 | }) 154 | .expect("couldn't append canvas to document body"); 155 | 156 | let inner_size = get_window_inner_size(); 157 | let pixel_ratio = get_window_device_pixel_ratio(); 158 | 159 | let mut renderer = WGPUWebRenderer::new(&window, window.canvas(), WGPURendererOptions::default()).await; 160 | renderer.set_size(inner_size.0 as f64, inner_size.1 as f64); 161 | renderer.set_pixel_ratio(pixel_ratio as f64); 162 | 163 | let mut pools = ResourcePools::new(); 164 | let (scene, camera, objects) = create_scene(&mut pools); 165 | 166 | event_loop.run(move |event, _, control_flow| { 167 | *control_flow = ControlFlow::Poll; 168 | match event { 169 | Event::WindowEvent { 170 | event: WindowEvent::Resized(size), 171 | .. 172 | } => { 173 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 174 | update(&mut pools, &scene, &objects); 175 | render(&mut renderer, &mut pools, &scene, &camera); 176 | }, 177 | Event::RedrawEventsCleared => { 178 | window.request_redraw(); 179 | }, 180 | Event::RedrawRequested(_) => { 181 | update(&mut pools, &scene, &objects); 182 | render(&mut renderer, &mut pools, &scene, &camera); 183 | }, 184 | Event::WindowEvent { 185 | event: WindowEvent::CloseRequested, 186 | .. 187 | } => { 188 | *control_flow = ControlFlow::Exit; 189 | }, 190 | _ => {} 191 | } 192 | }); 193 | } 194 | -------------------------------------------------------------------------------- /src/utils/geometry_helper.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | math::vector3::Vector3, 3 | geometry::{ 4 | attribute::Attribute, 5 | geometry::Geometry, 6 | index::Index, 7 | }, 8 | resource::resource::{ 9 | ResourceId, 10 | ResourcePools, 11 | }, 12 | }; 13 | 14 | pub struct GeometryHelper { 15 | } 16 | 17 | impl GeometryHelper { 18 | pub fn create_triangle( 19 | pools: &mut ResourcePools, 20 | width: f32, 21 | height: f32, 22 | ) -> ResourceId { 23 | let dy = 0.75_f32.sqrt() / 2.0; 24 | let positions = [ 25 | 0.0, (0.75_f32.sqrt() - dy) * height, 0.0, 26 | -0.5 * width, -dy * height, 0.0, 27 | 0.5 * width, -dy * height, 0.0, 28 | ].to_vec(); 29 | 30 | let normals = [ 31 | 0.0, 0.0, 1.0, 32 | 0.0, 0.0, 1.0, 33 | 0.0, 0.0, 1.0, 34 | ].to_vec(); 35 | 36 | let uvs = [ 37 | 0.5, 0.0, 38 | 1.0, 1.0, 39 | 0.0, 1.0, 40 | ].to_vec(); 41 | 42 | let indices = [ 43 | 0, 1, 2, 44 | ].to_vec(); 45 | 46 | let mut geometry = Geometry::new(); 47 | geometry.set_attribute("position", pools.borrow_mut::().add(Attribute::new(positions, 3))); 48 | geometry.set_attribute("normal", pools.borrow_mut::().add(Attribute::new(normals, 3))); 49 | geometry.set_attribute("uv", pools.borrow_mut::().add(Attribute::new(uvs, 2))); 50 | geometry.set_index(pools.borrow_mut::().add(Index::new(indices))); 51 | pools.borrow_mut::().add(geometry) 52 | } 53 | 54 | pub fn create_plane( 55 | pools: &mut ResourcePools, 56 | width: f32, 57 | height: f32, 58 | ) -> ResourceId { 59 | let positions = [ 60 | // top-left 61 | -0.5 * width, 0.5 * height, 0.0, 62 | // top-right 63 | 0.5 * width, 0.5 * height, 0.0, 64 | // bottom-left 65 | -0.5 * width, -0.5 * height, 0.0, 66 | // bottom-right 67 | 0.5 * width, -0.5 * height, 0.0, 68 | ].to_vec(); 69 | 70 | let normals = [ 71 | // top-left 72 | 0.0, 0.0, 1.0, 73 | // top-right 74 | 0.0, 0.0, 1.0, 75 | // bottom-left 76 | 0.0, 0.0, 1.0, 77 | // bottom-right 78 | 0.0, 0.0, 1.0, 79 | ].to_vec(); 80 | 81 | let uvs = [ 82 | // top-left 83 | 0.0, 0.0, 84 | // top-right 85 | 1.0, 0.0, 86 | // bottom-left 87 | 0.0, 1.0, 88 | // bottom-right 89 | 1.0, 1.0, 90 | ].to_vec(); 91 | 92 | let indices = [ 93 | 0, 2, 1, 94 | 1, 2, 3, 95 | ].to_vec(); 96 | 97 | let mut geometry = Geometry::new(); 98 | geometry.set_attribute("position", pools.borrow_mut::().add(Attribute::new(positions, 3))); 99 | geometry.set_attribute("normal", pools.borrow_mut::().add(Attribute::new(normals, 3))); 100 | geometry.set_attribute("uv", pools.borrow_mut::().add(Attribute::new(uvs, 2))); 101 | geometry.set_index(pools.borrow_mut::().add(Index::new(indices))); 102 | pools.borrow_mut::().add(geometry) 103 | } 104 | 105 | pub fn create_box( 106 | pools: &mut ResourcePools, 107 | width: f32, 108 | height: f32, 109 | depth: f32, 110 | ) -> ResourceId { 111 | let mut positions = Vec::new(); 112 | let mut normals = Vec::new(); 113 | let mut uvs = Vec::new(); 114 | let mut indices = Vec::new(); 115 | 116 | let mut position_vec = Vector3::create(); 117 | 118 | // @TODO: Clean up 119 | for face in 0..6 { 120 | let (x, y, z, nx, ny, nz, dx, dy, dz) = match face { 121 | // front 122 | 0 => ( 123 | -0.5, 0.5, 0.5, 124 | 0.0, 0.0, 1.0, 125 | [0.0, 1.0, 0.0, 1.0], 126 | [0.0, 0.0, -1.0, -1.0], 127 | [0.0, 0.0, 0.0, 0.0], 128 | ), 129 | // right 130 | 1 => ( 131 | 0.5, 0.5, 0.5, 132 | 1.0, 0.0, 0.0, 133 | [0.0, 0.0, 0.0, 0.0], 134 | [0.0, 0.0, -1.0, -1.0], 135 | [0.0, -1.0, 0.0, -1.0], 136 | ), 137 | // back 138 | 2 => ( 139 | 0.5, 0.5, -0.5, 140 | 0.0, 0.0, -1.0, 141 | [0.0, -1.0, 0.0, -1.0], 142 | [0.0, 0.0, -1.0, -1.0], 143 | [0.0, 0.0, 0.0, 0.0], 144 | ), 145 | // left 146 | 3 => ( 147 | -0.5, 0.5, -0.5, 148 | -1.0, 0.0, 0.0, 149 | [0.0, 0.0, 0.0, 0.0], 150 | [0.0, 0.0, -1.0, -1.0], 151 | [0.0, 1.0, 0.0, 1.0], 152 | ), 153 | // top 154 | 4 => ( 155 | -0.5, 0.5, -0.5, 156 | 0.0, 1.0, 0.0, 157 | [0.0, 1.0, 0.0, 1.0], 158 | [0.0, 0.0, 0.0, 0.0], 159 | [0.0, 0.0, 1.0, 1.0], 160 | ), 161 | // bottom 162 | _ => ( 163 | -0.5, -0.5, 0.5, 164 | 0.0, -1.0, 0.0, 165 | [0.0, 1.0, 0.0, 1.0], 166 | [0.0, 0.0, 0.0, 0.0], 167 | [0.0, 0.0, -1.0, -1.0], 168 | ), 169 | }; 170 | 171 | for i in 0..4 { 172 | position_vec[0] = (x + dx[i]) * width; 173 | position_vec[1] = (y + dy[i]) * height; 174 | position_vec[2] = (z + dz[i]) * depth; 175 | 176 | for j in 0..3 { 177 | positions.push(position_vec[j]); 178 | } 179 | 180 | normals.push(nx); 181 | normals.push(ny); 182 | normals.push(nz); 183 | } 184 | 185 | uvs.push(0.0); 186 | uvs.push(0.0); 187 | 188 | uvs.push(1.0); 189 | uvs.push(0.0); 190 | 191 | uvs.push(0.0); 192 | uvs.push(1.0); 193 | 194 | uvs.push(1.0); 195 | uvs.push(1.0); 196 | 197 | indices.push(face * 4 + 0); 198 | indices.push(face * 4 + 2); 199 | indices.push(face * 4 + 1); 200 | 201 | indices.push(face * 4 + 1); 202 | indices.push(face * 4 + 2); 203 | indices.push(face * 4 + 3); 204 | } 205 | 206 | let mut geometry = Geometry::new(); 207 | geometry.set_attribute("position", pools.borrow_mut::().add(Attribute::new(positions, 3))); 208 | geometry.set_attribute("normal", pools.borrow_mut::().add(Attribute::new(normals, 3))); 209 | geometry.set_attribute("uv", pools.borrow_mut::().add(Attribute::new(uvs, 2))); 210 | geometry.set_index(pools.borrow_mut::().add(Index::new(indices))); 211 | pools.borrow_mut::().add(geometry) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/takahirox/wgpu-rust-renderer.svg?branch=main)](https://travis-ci.org/takahirox/wgpu-rust-renderer) 2 | [![Crate](https://img.shields.io/crates/v/wgpu_rust_renderer.svg)](https://crates.io/crates/wgpu_rust_renderer) 3 | 4 | # wgpu-rust-renderer 5 | 6 | `wgpu-rust-renderer` is a tiny [WebGPU](https://www.w3.org/TR/webgpu/) Renderer written in Rust. 7 | 8 | ## Demo 9 | 10 | [Online WebAssembly Demo](https://takahirox.github.io/wgpu-rust-renderer/web/examples/#triangle) 11 | 12 | Rust code is compiled to WebAssembly with [wasm-bindgen](https://rustwasm.github.io/wasm-bindgen/) and it runs even in web browsers. 13 | 14 | ## Screenshots 15 | 16 | Desktop application. 17 | 18 | Desktop app 19 | 20 | Web application. 21 | 22 | Web app 23 | 24 | ## Features 25 | 26 | * Tiny WebGPU Renderling library 27 | * Easy to use 28 | * Memory safe with Rust 29 | * [glTF](https://www.khronos.org/gltf/) support 30 | * Good flexibility and extensibility of materials with node-based material system 31 | * Web application compatible by compiling to WebAssembly 32 | 33 | ## Documentation 34 | 35 | T.B.D. 36 | 37 | ## Sample Code 38 | 39 | ```rust 40 | use winit::{ 41 | event::{Event, WindowEvent}, 42 | event_loop::{ControlFlow, EventLoop}, 43 | window::Window, 44 | }; 45 | use wgpu_rust_renderer::{ 46 | math::color::Color, 47 | renderer::wgpu_renderer::{ 48 | WGPURenderer, 49 | WGPURendererOptions, 50 | }, 51 | resource::resource::{ 52 | ResourceId, 53 | ResourcePools, 54 | }, 55 | scene::{ 56 | camera::PerspectiveCamera, 57 | mesh::Mesh, 58 | node::Node, 59 | scene::Scene, 60 | }, 61 | utils::{ 62 | geometry_helper::GeometryHelper, 63 | material_helper::MaterialHelper, 64 | }, 65 | }; 66 | 67 | fn create_scene( 68 | window: &Window, 69 | pools: &mut ResourcePools, 70 | ) -> (ResourceId, ResourceId) { 71 | let mut scene = Scene::new(); 72 | 73 | let geometry = GeometryHelper::create_triangle( 74 | pools, 75 | 1.0, 76 | 1.0, 77 | ); 78 | 79 | let material = MaterialHelper::create_basic_material( 80 | pools, 81 | Color::set(&mut Color::create(), 1.0, 0.0, 0.0), 82 | ); 83 | 84 | let mesh = pools.borrow_mut::() 85 | .add(Mesh::new(geometry, material)); 86 | let node = pools.borrow_mut::() 87 | .add(Node::new()); 88 | scene.add_node(&node); 89 | scene.assign(&node, &mesh); 90 | 91 | let window_size = window.inner_size(); 92 | let camera = pools.borrow_mut::().add( 93 | PerspectiveCamera::new( 94 | 60.0_f32.to_radians(), 95 | window_size.width as f32 / window_size.height as f32, 96 | 0.1, 97 | 1000.0, 98 | ), 99 | ); 100 | 101 | let mut node = Node::new(); 102 | node.borrow_position_mut()[2] = 1.0; 103 | 104 | let node = pools.borrow_mut::().add(node); 105 | scene.add_node(&node); 106 | scene.assign(&node, &camera); 107 | 108 | (pools.borrow_mut::().add(scene), camera) 109 | } 110 | 111 | fn resize( 112 | renderer: &mut WGPURenderer, 113 | pools: &mut ResourcePools, 114 | camera: &ResourceId, 115 | width: u32, 116 | height: u32, 117 | ) { 118 | pools 119 | .borrow_mut::() 120 | .borrow_mut(camera) 121 | .unwrap() 122 | .set_aspect(width as f32 / height as f32); 123 | renderer.set_size(width as f64, height as f64); 124 | } 125 | 126 | fn update( 127 | pools: &mut ResourcePools, 128 | scene: &ResourceId, 129 | ) { 130 | pools.borrow::() 131 | .borrow(scene) 132 | .unwrap() 133 | .update_matrices(pools); 134 | } 135 | 136 | fn render( 137 | renderer: &mut WGPURenderer, 138 | pools: &ResourcePools, 139 | scene: &ResourceId, 140 | camera: &ResourceId, 141 | ) { 142 | renderer.render(pools, scene, camera); 143 | } 144 | 145 | #[tokio::main] 146 | async fn main() { 147 | let event_loop = EventLoop::new(); 148 | let window = Window::new(&event_loop).unwrap(); 149 | 150 | let window_size = window.inner_size(); 151 | let pixel_ratio = window.scale_factor(); 152 | 153 | let mut renderer = WGPURenderer::new( 154 | &window, 155 | WGPURendererOptions::default(), 156 | ).await; 157 | renderer.set_size(window_size.width as f64, window_size.height as f64); 158 | renderer.set_pixel_ratio(pixel_ratio); 159 | 160 | let mut pools = ResourcePools::new(); 161 | let (scene, camera) = create_scene(&window, &mut pools); 162 | 163 | event_loop.run(move |event, _, control_flow| { 164 | *control_flow = ControlFlow::Wait; 165 | match event { 166 | Event::WindowEvent { 167 | event: WindowEvent::Resized(size), 168 | .. 169 | } => { 170 | resize(&mut renderer, &mut pools, &camera, size.width, size.height); 171 | update(&mut pools, &scene); 172 | render(&mut renderer, &mut pools, &scene, &camera); 173 | }, 174 | Event::RedrawRequested(_) => { 175 | update(&mut pools, &scene); 176 | render(&mut renderer, &mut pools, &scene, &camera); 177 | }, 178 | Event::WindowEvent { 179 | event: WindowEvent::CloseRequested, 180 | .. 181 | } => { 182 | *control_flow = ControlFlow::Exit; 183 | }, 184 | _ => {} 185 | } 186 | }); 187 | } 188 | ``` 189 | 190 | ## How to import 191 | 192 | The library is released at [crates.io](https://crates.io/crates/wgpu_rust_renderer). Add the following line into Cargo.toml of your Rust project. 193 | 194 | ``` 195 | [dependencies] 196 | wgpu_rust_renderer = "0.0.4" 197 | ``` 198 | 199 | And add the following lines in your Rust code to import the library. 200 | 201 | ```Rust 202 | use wgpu_rust_renderer::{ 203 | geometry::{ 204 | attribute::Attribute, 205 | geometry::Geometry, 206 | index::Index, 207 | }, 208 | material::material::Material, 209 | resource::resource::{ 210 | ResourceId, 211 | ResourcePools, 212 | }, 213 | scene::{ 214 | camera::PerspectiveCamera, 215 | mesh::Mesh, 216 | node::Node, 217 | scene::Scene, 218 | }, 219 | web::wgpu_web_renderer::WGPUWebRenderer, // for web 220 | renderer::wgpu_renderer::{ 221 | WGPURenderer, // for others 222 | WGPURendererOptions, 223 | }, 224 | }; 225 | ``` 226 | 227 | ## How to build the library locally 228 | 229 | ```sh 230 | $ git clone https://github.com/takahirox/wgpu-rust-renderer.git 231 | $ cd wgpu-rust-renderer 232 | $ cargo build 233 | ``` 234 | 235 | ## How to run desktop examples locally 236 | 237 | ```sh 238 | $ cd wgpu-rust-renderer 239 | $ cargo run --example example_name 240 | ``` 241 | 242 | ## How to run web examples locally 243 | 244 | Prerequirements 245 | - Install [wasm-bindgen client](https://rustwasm.github.io/docs/wasm-bindgen/) 246 | - Install Rust wasm32-unknown-unknown target with `$ rustup target add wasm32-unknown-unknown` 247 | - Install `http-server` with `$ npm install -g http-server`, or other local servers 248 | 249 | ```sh 250 | $ cd wgpu-rust-renderer/web 251 | $ bash build_examples.sh 252 | $ http-server . -p 8080 -c-1 253 | # Access http://localhost:8080/examples/index.html on your web browser 254 | ``` 255 | 256 | ## How to run tests 257 | 258 | T.B.D. 259 | --------------------------------------------------------------------------------