Compare commits
No commits in common. "12d61b25c45c95c800162fc9d650e37459698662" and "31d4d5331615d1d20db0036af52cffddf33db818" have entirely different histories.
12d61b25c4
...
31d4d53316
11
.gitignore
vendored
11
.gitignore
vendored
@ -1 +1,12 @@
|
|||||||
/target
|
/target
|
||||||
|
**/target
|
||||||
|
/Cargo.lock
|
||||||
|
|
||||||
|
.vscode/
|
||||||
|
*.code-workspace
|
||||||
|
.idea/
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
8
.idea/.gitignore
generated
vendored
8
.idea/.gitignore
generated
vendored
@ -1,8 +0,0 @@
|
|||||||
# Default ignored files
|
|
||||||
/shelf/
|
|
||||||
/workspace.xml
|
|
||||||
# Editor-based HTTP Client requests
|
|
||||||
/httpRequests/
|
|
||||||
# Datasource local storage ignored files
|
|
||||||
/dataSources/
|
|
||||||
/dataSources.local.xml
|
|
||||||
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/orbital_simulation.iml" filepath="$PROJECT_DIR$/.idea/orbital_simulation.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
11
.idea/orbital_simulation.iml
generated
11
.idea/orbital_simulation.iml
generated
@ -1,11 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="EMPTY_MODULE" version="4">
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$">
|
|
||||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
|
||||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
|
||||||
</content>
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
2530
Cargo.lock
generated
2530
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
32
Cargo.toml
32
Cargo.toml
@ -1,27 +1,5 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "orbital_simulation"
|
members = [
|
||||||
version = "0.1.0"
|
"solar_engine",
|
||||||
edition = "2021"
|
"simulator"
|
||||||
|
]
|
||||||
[dependencies]
|
|
||||||
cfg-if = "1"
|
|
||||||
anyhow = "1.0"
|
|
||||||
bytemuck = { version = "1.16", features = [ "derive" ] }
|
|
||||||
cgmath = "0.18"
|
|
||||||
env_logger = "0.10"
|
|
||||||
pollster = "0.3"
|
|
||||||
log = "0.4"
|
|
||||||
tobj = { version = "3.2", default-features = false, features = ["async"]}
|
|
||||||
wgpu = { version = "22.0"}
|
|
||||||
winit = { version = "0.30.8", features = ["rwh_05"] }
|
|
||||||
instant = "0.1"
|
|
||||||
|
|
||||||
[dependencies.image]
|
|
||||||
version = "0.24"
|
|
||||||
default-features = false
|
|
||||||
features = ["png", "jpeg", "hdr"]
|
|
||||||
|
|
||||||
[build-dependencies]
|
|
||||||
anyhow = "1.0"
|
|
||||||
fs_extra = "1.2"
|
|
||||||
glob = "0.3"
|
|
||||||
|
|||||||
18
build.rs
18
build.rs
@ -1,18 +0,0 @@
|
|||||||
use anyhow::*;
|
|
||||||
use fs_extra::copy_items;
|
|
||||||
use fs_extra::dir::CopyOptions;
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
// This tells Cargo to rerun this script if something in /res/ changes.
|
|
||||||
println!("cargo:rerun-if-changed=res/*");
|
|
||||||
|
|
||||||
let out_dir = env::var("OUT_DIR")?;
|
|
||||||
let mut copy_options = CopyOptions::new();
|
|
||||||
copy_options.overwrite = true;
|
|
||||||
let mut paths_to_copy = Vec::new();
|
|
||||||
paths_to_copy.push("res/");
|
|
||||||
copy_items(&paths_to_copy, out_dir, ©_options)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
@ -1,14 +0,0 @@
|
|||||||
# Blender MTL File: 'cobble_sphere.blend'
|
|
||||||
# Material Count: 1
|
|
||||||
|
|
||||||
newmtl Material
|
|
||||||
Ns 250.000000
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.800000 0.800000 0.800000
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.450000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
map_Bump cobble-normal.png
|
|
||||||
map_Kd cobble-diffuse.png
|
|
||||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB |
14
res/cube.mtl
14
res/cube.mtl
@ -1,14 +0,0 @@
|
|||||||
# Blender MTL File: 'cube.blend'
|
|
||||||
# Material Count: 1
|
|
||||||
|
|
||||||
newmtl Material.001
|
|
||||||
Ns 323.999994
|
|
||||||
Ka 1.000000 1.000000 1.000000
|
|
||||||
Kd 0.800000 0.800000 0.800000
|
|
||||||
Ks 0.500000 0.500000 0.500000
|
|
||||||
Ke 0.000000 0.000000 0.000000
|
|
||||||
Ni 1.450000
|
|
||||||
d 1.000000
|
|
||||||
illum 2
|
|
||||||
map_Bump cube-normal.png
|
|
||||||
map_Kd cube-diffuse.jpg
|
|
||||||
933
res/cube.obj
933
res/cube.obj
@ -1,933 +0,0 @@
|
|||||||
# Blender v2.82 (sub 7) OBJ File: 'cube.blend'
|
|
||||||
# www.blender.org
|
|
||||||
mtllib cube.mtl
|
|
||||||
o Cube_Finished_Cube.001
|
|
||||||
v 0.900000 0.900000 -1.000000
|
|
||||||
v 0.900000 1.000000 -0.900000
|
|
||||||
v 1.000000 0.900000 -0.900000
|
|
||||||
v 0.900000 0.930907 -0.995104
|
|
||||||
v 0.900000 0.958769 -0.980909
|
|
||||||
v 0.930907 0.900000 -0.995104
|
|
||||||
v 0.931727 0.931906 -0.989305
|
|
||||||
v 0.930693 0.957414 -0.975905
|
|
||||||
v 0.958769 0.900000 -0.980909
|
|
||||||
v 0.957466 0.930772 -0.975834
|
|
||||||
v 0.952912 0.952912 -0.966338
|
|
||||||
v 0.930907 0.995104 -0.900000
|
|
||||||
v 0.958769 0.980909 -0.900000
|
|
||||||
v 0.900000 0.995104 -0.930907
|
|
||||||
v 0.931906 0.989305 -0.931727
|
|
||||||
v 0.957414 0.975905 -0.930693
|
|
||||||
v 0.900000 0.980909 -0.958769
|
|
||||||
v 0.930772 0.975834 -0.957466
|
|
||||||
v 0.952912 0.966338 -0.952912
|
|
||||||
v 0.995104 0.900000 -0.930907
|
|
||||||
v 0.980909 0.900000 -0.958769
|
|
||||||
v 0.995104 0.930907 -0.900000
|
|
||||||
v 0.989305 0.931727 -0.931906
|
|
||||||
v 0.975905 0.930693 -0.957414
|
|
||||||
v 0.980909 0.958769 -0.900000
|
|
||||||
v 0.975834 0.957466 -0.930772
|
|
||||||
v 0.966338 0.952912 -0.952912
|
|
||||||
v 0.900000 -1.000000 -0.900000
|
|
||||||
v 0.900000 -0.900000 -1.000000
|
|
||||||
v 1.000000 -0.900000 -0.900000
|
|
||||||
v 0.900000 -0.995104 -0.930907
|
|
||||||
v 0.900000 -0.980909 -0.958769
|
|
||||||
v 0.930907 -0.995104 -0.900000
|
|
||||||
v 0.931727 -0.989305 -0.931906
|
|
||||||
v 0.930693 -0.975905 -0.957414
|
|
||||||
v 0.958769 -0.980909 -0.900000
|
|
||||||
v 0.957466 -0.975834 -0.930772
|
|
||||||
v 0.952912 -0.966338 -0.952912
|
|
||||||
v 0.930907 -0.900000 -0.995104
|
|
||||||
v 0.958769 -0.900000 -0.980909
|
|
||||||
v 0.900000 -0.930907 -0.995104
|
|
||||||
v 0.931906 -0.931727 -0.989305
|
|
||||||
v 0.957414 -0.930693 -0.975905
|
|
||||||
v 0.900000 -0.958769 -0.980909
|
|
||||||
v 0.930772 -0.957466 -0.975834
|
|
||||||
v 0.952912 -0.952912 -0.966338
|
|
||||||
v 0.995104 -0.930907 -0.900000
|
|
||||||
v 0.980909 -0.958769 -0.900000
|
|
||||||
v 0.995104 -0.900000 -0.930907
|
|
||||||
v 0.989305 -0.931906 -0.931727
|
|
||||||
v 0.975905 -0.957414 -0.930693
|
|
||||||
v 0.980909 -0.900000 -0.958769
|
|
||||||
v 0.975834 -0.930772 -0.957466
|
|
||||||
v 0.966338 -0.952912 -0.952912
|
|
||||||
v 1.000000 0.900000 0.900000
|
|
||||||
v 0.900000 1.000000 0.900000
|
|
||||||
v 0.900000 0.900000 1.000000
|
|
||||||
v 0.995104 0.930907 0.900000
|
|
||||||
v 0.980909 0.958769 0.900000
|
|
||||||
v 0.995104 0.900000 0.930907
|
|
||||||
v 0.989305 0.931906 0.931727
|
|
||||||
v 0.975905 0.957414 0.930693
|
|
||||||
v 0.980909 0.900000 0.958769
|
|
||||||
v 0.975834 0.930772 0.957466
|
|
||||||
v 0.966338 0.952912 0.952912
|
|
||||||
v 0.900000 0.995104 0.930907
|
|
||||||
v 0.900000 0.980909 0.958769
|
|
||||||
v 0.930907 0.995104 0.900000
|
|
||||||
v 0.931727 0.989305 0.931906
|
|
||||||
v 0.930693 0.975905 0.957414
|
|
||||||
v 0.958769 0.980909 0.900000
|
|
||||||
v 0.957466 0.975834 0.930772
|
|
||||||
v 0.952912 0.966338 0.952912
|
|
||||||
v 0.930907 0.900000 0.995104
|
|
||||||
v 0.958769 0.900000 0.980909
|
|
||||||
v 0.900000 0.930907 0.995104
|
|
||||||
v 0.931906 0.931727 0.989305
|
|
||||||
v 0.957414 0.930693 0.975905
|
|
||||||
v 0.900000 0.958769 0.980909
|
|
||||||
v 0.930772 0.957466 0.975834
|
|
||||||
v 0.952912 0.952912 0.966338
|
|
||||||
v 1.000000 -0.900000 0.900000
|
|
||||||
v 0.900000 -0.900000 1.000000
|
|
||||||
v 0.900000 -1.000000 0.900000
|
|
||||||
v 0.995104 -0.900000 0.930907
|
|
||||||
v 0.980909 -0.900000 0.958769
|
|
||||||
v 0.995104 -0.930907 0.900000
|
|
||||||
v 0.989305 -0.931727 0.931906
|
|
||||||
v 0.975905 -0.930693 0.957414
|
|
||||||
v 0.980909 -0.958769 0.900000
|
|
||||||
v 0.975834 -0.957466 0.930772
|
|
||||||
v 0.966338 -0.952912 0.952912
|
|
||||||
v 0.900000 -0.930907 0.995104
|
|
||||||
v 0.900000 -0.958769 0.980909
|
|
||||||
v 0.930907 -0.900000 0.995104
|
|
||||||
v 0.931727 -0.931906 0.989305
|
|
||||||
v 0.930693 -0.957414 0.975905
|
|
||||||
v 0.958769 -0.900000 0.980909
|
|
||||||
v 0.957466 -0.930772 0.975834
|
|
||||||
v 0.952912 -0.952912 0.966338
|
|
||||||
v 0.930907 -0.995104 0.900000
|
|
||||||
v 0.958769 -0.980909 0.900000
|
|
||||||
v 0.900000 -0.995104 0.930907
|
|
||||||
v 0.931906 -0.989305 0.931727
|
|
||||||
v 0.957414 -0.975905 0.930693
|
|
||||||
v 0.900000 -0.980909 0.958769
|
|
||||||
v 0.930772 -0.975834 0.957466
|
|
||||||
v 0.952912 -0.966338 0.952912
|
|
||||||
v -0.900000 0.900000 -1.000000
|
|
||||||
v -1.000000 0.900000 -0.900000
|
|
||||||
v -0.900000 1.000000 -0.900000
|
|
||||||
v -0.930907 0.900000 -0.995104
|
|
||||||
v -0.958769 0.900000 -0.980909
|
|
||||||
v -0.900000 0.930907 -0.995104
|
|
||||||
v -0.931906 0.931727 -0.989305
|
|
||||||
v -0.957414 0.930693 -0.975905
|
|
||||||
v -0.900000 0.958769 -0.980909
|
|
||||||
v -0.930772 0.957466 -0.975834
|
|
||||||
v -0.952912 0.952912 -0.966338
|
|
||||||
v -0.995104 0.930907 -0.900000
|
|
||||||
v -0.980909 0.958769 -0.900000
|
|
||||||
v -0.995104 0.900000 -0.930907
|
|
||||||
v -0.989305 0.931906 -0.931727
|
|
||||||
v -0.975905 0.957414 -0.930693
|
|
||||||
v -0.980909 0.900000 -0.958769
|
|
||||||
v -0.975834 0.930772 -0.957466
|
|
||||||
v -0.966338 0.952912 -0.952912
|
|
||||||
v -0.900000 0.995104 -0.930907
|
|
||||||
v -0.900000 0.980909 -0.958769
|
|
||||||
v -0.930907 0.995104 -0.900000
|
|
||||||
v -0.931727 0.989305 -0.931906
|
|
||||||
v -0.930693 0.975905 -0.957414
|
|
||||||
v -0.958769 0.980909 -0.900000
|
|
||||||
v -0.957466 0.975834 -0.930772
|
|
||||||
v -0.952912 0.966338 -0.952912
|
|
||||||
v -1.000000 -0.900000 -0.900000
|
|
||||||
v -0.900000 -0.900000 -1.000000
|
|
||||||
v -0.900000 -1.000000 -0.900000
|
|
||||||
v -0.995104 -0.900000 -0.930907
|
|
||||||
v -0.980909 -0.900000 -0.958769
|
|
||||||
v -0.995104 -0.930907 -0.900000
|
|
||||||
v -0.989305 -0.931727 -0.931906
|
|
||||||
v -0.975905 -0.930693 -0.957414
|
|
||||||
v -0.980909 -0.958769 -0.900000
|
|
||||||
v -0.975834 -0.957466 -0.930772
|
|
||||||
v -0.966338 -0.952912 -0.952912
|
|
||||||
v -0.900000 -0.930907 -0.995104
|
|
||||||
v -0.900000 -0.958769 -0.980909
|
|
||||||
v -0.930907 -0.900000 -0.995104
|
|
||||||
v -0.931727 -0.931906 -0.989305
|
|
||||||
v -0.930693 -0.957414 -0.975905
|
|
||||||
v -0.958769 -0.900000 -0.980909
|
|
||||||
v -0.957466 -0.930772 -0.975834
|
|
||||||
v -0.952912 -0.952912 -0.966338
|
|
||||||
v -0.930907 -0.995104 -0.900000
|
|
||||||
v -0.958769 -0.980909 -0.900000
|
|
||||||
v -0.900000 -0.995104 -0.930907
|
|
||||||
v -0.931906 -0.989305 -0.931727
|
|
||||||
v -0.957414 -0.975905 -0.930693
|
|
||||||
v -0.900000 -0.980909 -0.958769
|
|
||||||
v -0.930772 -0.975834 -0.957466
|
|
||||||
v -0.952912 -0.966338 -0.952912
|
|
||||||
v -1.000000 0.900000 0.900000
|
|
||||||
v -0.900000 0.900000 1.000000
|
|
||||||
v -0.900000 1.000000 0.900000
|
|
||||||
v -0.995104 0.900000 0.930907
|
|
||||||
v -0.980909 0.900000 0.958769
|
|
||||||
v -0.995104 0.930907 0.900000
|
|
||||||
v -0.989305 0.931727 0.931906
|
|
||||||
v -0.975905 0.930693 0.957414
|
|
||||||
v -0.980909 0.958769 0.900000
|
|
||||||
v -0.975834 0.957466 0.930772
|
|
||||||
v -0.966338 0.952912 0.952912
|
|
||||||
v -0.900000 0.930907 0.995104
|
|
||||||
v -0.900000 0.958769 0.980909
|
|
||||||
v -0.930907 0.900000 0.995104
|
|
||||||
v -0.931727 0.931906 0.989305
|
|
||||||
v -0.930693 0.957414 0.975905
|
|
||||||
v -0.958769 0.900000 0.980909
|
|
||||||
v -0.957466 0.930772 0.975834
|
|
||||||
v -0.952912 0.952912 0.966338
|
|
||||||
v -0.930907 0.995104 0.900000
|
|
||||||
v -0.958769 0.980909 0.900000
|
|
||||||
v -0.900000 0.995104 0.930907
|
|
||||||
v -0.931906 0.989305 0.931727
|
|
||||||
v -0.957414 0.975905 0.930693
|
|
||||||
v -0.900000 0.980909 0.958769
|
|
||||||
v -0.930772 0.975834 0.957466
|
|
||||||
v -0.952912 0.966338 0.952912
|
|
||||||
v -0.900000 -1.000000 0.900000
|
|
||||||
v -0.900000 -0.900000 1.000000
|
|
||||||
v -1.000000 -0.900000 0.900000
|
|
||||||
v -0.900000 -0.995104 0.930907
|
|
||||||
v -0.900000 -0.980909 0.958769
|
|
||||||
v -0.930907 -0.995104 0.900000
|
|
||||||
v -0.931727 -0.989305 0.931906
|
|
||||||
v -0.930693 -0.975905 0.957414
|
|
||||||
v -0.958769 -0.980909 0.900000
|
|
||||||
v -0.957466 -0.975834 0.930772
|
|
||||||
v -0.952912 -0.966338 0.952912
|
|
||||||
v -0.930907 -0.900000 0.995104
|
|
||||||
v -0.958769 -0.900000 0.980909
|
|
||||||
v -0.900000 -0.930907 0.995104
|
|
||||||
v -0.931906 -0.931727 0.989305
|
|
||||||
v -0.957414 -0.930693 0.975905
|
|
||||||
v -0.900000 -0.958769 0.980909
|
|
||||||
v -0.930772 -0.957466 0.975834
|
|
||||||
v -0.952912 -0.952912 0.966338
|
|
||||||
v -0.995104 -0.930907 0.900000
|
|
||||||
v -0.980909 -0.958769 0.900000
|
|
||||||
v -0.995104 -0.900000 0.930907
|
|
||||||
v -0.989305 -0.931906 0.931727
|
|
||||||
v -0.975905 -0.957414 0.930693
|
|
||||||
v -0.980909 -0.900000 0.958769
|
|
||||||
v -0.975834 -0.930772 0.957466
|
|
||||||
v -0.966338 -0.952912 0.952912
|
|
||||||
vt 0.137500 0.512500
|
|
||||||
vt 0.362500 0.512500
|
|
||||||
vt 0.362500 0.737500
|
|
||||||
vt 0.137500 0.737500
|
|
||||||
vt 0.387500 0.012500
|
|
||||||
vt 0.612500 0.012500
|
|
||||||
vt 0.612500 0.237500
|
|
||||||
vt 0.387500 0.237500
|
|
||||||
vt 0.387500 0.762500
|
|
||||||
vt 0.612500 0.762500
|
|
||||||
vt 0.612500 0.987500
|
|
||||||
vt 0.387500 0.987500
|
|
||||||
vt 0.637500 0.512500
|
|
||||||
vt 0.862500 0.512500
|
|
||||||
vt 0.862500 0.737500
|
|
||||||
vt 0.637500 0.737500
|
|
||||||
vt 0.387500 0.512500
|
|
||||||
vt 0.612500 0.512500
|
|
||||||
vt 0.612500 0.737500
|
|
||||||
vt 0.387500 0.737500
|
|
||||||
vt 0.612500 0.487500
|
|
||||||
vt 0.616363 0.487500
|
|
||||||
vt 0.616488 0.491466
|
|
||||||
vt 0.612500 0.491363
|
|
||||||
vt 0.619846 0.487500
|
|
||||||
vt 0.619677 0.491337
|
|
||||||
vt 0.625000 0.487500
|
|
||||||
vt 0.625000 0.491347
|
|
||||||
vt 0.616346 0.494683
|
|
||||||
vt 0.612500 0.494846
|
|
||||||
vt 0.619114 0.494114
|
|
||||||
vt 0.625000 0.494114
|
|
||||||
vt 0.633637 0.512500
|
|
||||||
vt 0.633512 0.508534
|
|
||||||
vt 0.637500 0.508637
|
|
||||||
vt 0.630154 0.512500
|
|
||||||
vt 0.630323 0.508663
|
|
||||||
vt 0.619846 0.512500
|
|
||||||
vt 0.619683 0.508653
|
|
||||||
vt 0.633654 0.505317
|
|
||||||
vt 0.637500 0.505154
|
|
||||||
vt 0.630886 0.505886
|
|
||||||
vt 0.619114 0.505886
|
|
||||||
vt 0.612500 0.508637
|
|
||||||
vt 0.616466 0.508512
|
|
||||||
vt 0.616363 0.512500
|
|
||||||
vt 0.612500 0.505154
|
|
||||||
vt 0.616337 0.505323
|
|
||||||
vt 0.619114 0.500000
|
|
||||||
vt 0.362500 0.508637
|
|
||||||
vt 0.366466 0.508512
|
|
||||||
vt 0.366363 0.512500
|
|
||||||
vt 0.362500 0.505154
|
|
||||||
vt 0.366337 0.505323
|
|
||||||
vt 0.362500 0.500000
|
|
||||||
vt 0.366347 0.500000
|
|
||||||
vt 0.369683 0.508653
|
|
||||||
vt 0.369846 0.512500
|
|
||||||
vt 0.369114 0.505886
|
|
||||||
vt 0.369114 0.500000
|
|
||||||
vt 0.387500 0.487500
|
|
||||||
vt 0.387500 0.491363
|
|
||||||
vt 0.383534 0.491488
|
|
||||||
vt 0.383637 0.487500
|
|
||||||
vt 0.387500 0.494846
|
|
||||||
vt 0.383663 0.494677
|
|
||||||
vt 0.387500 0.505154
|
|
||||||
vt 0.383653 0.505317
|
|
||||||
vt 0.380317 0.491346
|
|
||||||
vt 0.380154 0.487500
|
|
||||||
vt 0.380886 0.494114
|
|
||||||
vt 0.380886 0.505886
|
|
||||||
vt 0.383637 0.512500
|
|
||||||
vt 0.383512 0.508534
|
|
||||||
vt 0.387500 0.508637
|
|
||||||
vt 0.380154 0.512500
|
|
||||||
vt 0.380323 0.508663
|
|
||||||
vt 0.375000 0.505886
|
|
||||||
vt 0.616363 0.737500
|
|
||||||
vt 0.616488 0.741466
|
|
||||||
vt 0.612500 0.741363
|
|
||||||
vt 0.619846 0.737500
|
|
||||||
vt 0.619677 0.741337
|
|
||||||
vt 0.630154 0.737500
|
|
||||||
vt 0.630317 0.741346
|
|
||||||
vt 0.616346 0.744683
|
|
||||||
vt 0.612500 0.744846
|
|
||||||
vt 0.619114 0.744114
|
|
||||||
vt 0.630886 0.744114
|
|
||||||
vt 0.637500 0.741363
|
|
||||||
vt 0.633534 0.741488
|
|
||||||
vt 0.633637 0.737500
|
|
||||||
vt 0.637500 0.744846
|
|
||||||
vt 0.633664 0.744677
|
|
||||||
vt 0.637500 0.750000
|
|
||||||
vt 0.633653 0.750000
|
|
||||||
vt 0.630886 0.750000
|
|
||||||
vt 0.612500 0.758637
|
|
||||||
vt 0.616466 0.758512
|
|
||||||
vt 0.616363 0.762500
|
|
||||||
vt 0.612500 0.755154
|
|
||||||
vt 0.616337 0.755323
|
|
||||||
vt 0.619683 0.758653
|
|
||||||
vt 0.619846 0.762500
|
|
||||||
vt 0.619114 0.755886
|
|
||||||
vt 0.625000 0.744114
|
|
||||||
vt 0.619114 0.750000
|
|
||||||
vt 0.387500 0.741363
|
|
||||||
vt 0.383534 0.741488
|
|
||||||
vt 0.383637 0.737500
|
|
||||||
vt 0.387500 0.744846
|
|
||||||
vt 0.383663 0.744677
|
|
||||||
vt 0.387500 0.755154
|
|
||||||
vt 0.383653 0.755317
|
|
||||||
vt 0.380317 0.741346
|
|
||||||
vt 0.380154 0.737500
|
|
||||||
vt 0.380886 0.744114
|
|
||||||
vt 0.380886 0.755886
|
|
||||||
vt 0.383637 0.762500
|
|
||||||
vt 0.383512 0.758534
|
|
||||||
vt 0.387500 0.758637
|
|
||||||
vt 0.380154 0.762500
|
|
||||||
vt 0.380323 0.758663
|
|
||||||
vt 0.375000 0.762500
|
|
||||||
vt 0.375000 0.758654
|
|
||||||
vt 0.375000 0.755886
|
|
||||||
vt 0.366363 0.737500
|
|
||||||
vt 0.366488 0.741466
|
|
||||||
vt 0.362500 0.741363
|
|
||||||
vt 0.369846 0.737500
|
|
||||||
vt 0.369677 0.741337
|
|
||||||
vt 0.366347 0.744683
|
|
||||||
vt 0.362500 0.744846
|
|
||||||
vt 0.369114 0.744114
|
|
||||||
vt 0.380886 0.750000
|
|
||||||
vt 0.375000 0.744114
|
|
||||||
vt 0.612500 0.262500
|
|
||||||
vt 0.612500 0.258637
|
|
||||||
vt 0.616466 0.258512
|
|
||||||
vt 0.616363 0.262500
|
|
||||||
vt 0.612500 0.255154
|
|
||||||
vt 0.616337 0.255323
|
|
||||||
vt 0.612500 0.244846
|
|
||||||
vt 0.616346 0.244683
|
|
||||||
vt 0.619683 0.258653
|
|
||||||
vt 0.619846 0.262500
|
|
||||||
vt 0.619114 0.255886
|
|
||||||
vt 0.619114 0.244114
|
|
||||||
vt 0.616363 0.237500
|
|
||||||
vt 0.616488 0.241466
|
|
||||||
vt 0.612500 0.241363
|
|
||||||
vt 0.619846 0.237500
|
|
||||||
vt 0.619677 0.241337
|
|
||||||
vt 0.625000 0.237500
|
|
||||||
vt 0.625000 0.241347
|
|
||||||
vt 0.625000 0.244114
|
|
||||||
vt 0.862500 0.508637
|
|
||||||
vt 0.866466 0.508512
|
|
||||||
vt 0.866363 0.512500
|
|
||||||
vt 0.862500 0.505154
|
|
||||||
vt 0.866337 0.505323
|
|
||||||
vt 0.862500 0.500000
|
|
||||||
vt 0.866347 0.500000
|
|
||||||
vt 0.869683 0.508653
|
|
||||||
vt 0.869846 0.512500
|
|
||||||
vt 0.869114 0.505886
|
|
||||||
vt 0.869114 0.500000
|
|
||||||
vt 0.619114 0.250000
|
|
||||||
vt 0.625000 0.255886
|
|
||||||
vt 0.387500 0.241363
|
|
||||||
vt 0.383534 0.241488
|
|
||||||
vt 0.383637 0.237500
|
|
||||||
vt 0.387500 0.244846
|
|
||||||
vt 0.383663 0.244677
|
|
||||||
vt 0.387500 0.255154
|
|
||||||
vt 0.383653 0.255317
|
|
||||||
vt 0.380317 0.241346
|
|
||||||
vt 0.380154 0.237500
|
|
||||||
vt 0.380886 0.244114
|
|
||||||
vt 0.380886 0.255886
|
|
||||||
vt 0.387500 0.262500
|
|
||||||
vt 0.383637 0.262500
|
|
||||||
vt 0.383512 0.258534
|
|
||||||
vt 0.387500 0.258637
|
|
||||||
vt 0.380154 0.262500
|
|
||||||
vt 0.380323 0.258663
|
|
||||||
vt 0.375000 0.262500
|
|
||||||
vt 0.375000 0.258653
|
|
||||||
vt 0.375000 0.255886
|
|
||||||
vt 0.133637 0.512500
|
|
||||||
vt 0.133512 0.508534
|
|
||||||
vt 0.137500 0.508637
|
|
||||||
vt 0.130154 0.512500
|
|
||||||
vt 0.130323 0.508663
|
|
||||||
vt 0.125000 0.512500
|
|
||||||
vt 0.125000 0.508654
|
|
||||||
vt 0.133653 0.505317
|
|
||||||
vt 0.137500 0.505154
|
|
||||||
vt 0.130886 0.505886
|
|
||||||
vt 0.125000 0.505886
|
|
||||||
vt 0.380886 0.250000
|
|
||||||
vt 0.375000 0.244114
|
|
||||||
vt 0.612500 0.008637
|
|
||||||
vt 0.616466 0.008512
|
|
||||||
vt 0.616363 0.012500
|
|
||||||
vt 0.612500 0.005154
|
|
||||||
vt 0.616337 0.005323
|
|
||||||
vt 0.612500 0.000000
|
|
||||||
vt 0.616346 0.000000
|
|
||||||
vt 0.619683 0.008654
|
|
||||||
vt 0.619846 0.012500
|
|
||||||
vt 0.619114 0.005886
|
|
||||||
vt 0.619114 0.000000
|
|
||||||
vt 0.616363 0.987500
|
|
||||||
vt 0.616488 0.991466
|
|
||||||
vt 0.612500 0.991363
|
|
||||||
vt 0.619846 0.987500
|
|
||||||
vt 0.619677 0.991337
|
|
||||||
vt 0.625000 0.987500
|
|
||||||
vt 0.625000 0.991346
|
|
||||||
vt 0.616346 0.994683
|
|
||||||
vt 0.612500 0.994846
|
|
||||||
vt 0.619114 0.994114
|
|
||||||
vt 0.625000 0.994114
|
|
||||||
vt 0.866363 0.737500
|
|
||||||
vt 0.866488 0.741466
|
|
||||||
vt 0.862500 0.741363
|
|
||||||
vt 0.869846 0.737500
|
|
||||||
vt 0.869677 0.741337
|
|
||||||
vt 0.875000 0.737500
|
|
||||||
vt 0.875000 0.741347
|
|
||||||
vt 0.866346 0.744683
|
|
||||||
vt 0.862500 0.744846
|
|
||||||
vt 0.869114 0.744114
|
|
||||||
vt 0.875000 0.744114
|
|
||||||
vt 0.625000 0.005886
|
|
||||||
vt 0.137500 0.741363
|
|
||||||
vt 0.133534 0.741488
|
|
||||||
vt 0.133637 0.737500
|
|
||||||
vt 0.137500 0.744846
|
|
||||||
vt 0.133663 0.744677
|
|
||||||
vt 0.137500 0.750000
|
|
||||||
vt 0.133653 0.750000
|
|
||||||
vt 0.130317 0.741346
|
|
||||||
vt 0.130154 0.737500
|
|
||||||
vt 0.130886 0.744114
|
|
||||||
vt 0.130886 0.750000
|
|
||||||
vt 0.387500 0.991363
|
|
||||||
vt 0.383534 0.991488
|
|
||||||
vt 0.383637 0.987500
|
|
||||||
vt 0.387500 0.994846
|
|
||||||
vt 0.383663 0.994677
|
|
||||||
vt 0.387500 1.000000
|
|
||||||
vt 0.383654 1.000000
|
|
||||||
vt 0.380317 0.991346
|
|
||||||
vt 0.380154 0.987500
|
|
||||||
vt 0.380886 0.994114
|
|
||||||
vt 0.380886 1.000000
|
|
||||||
vt 0.383637 0.012500
|
|
||||||
vt 0.383512 0.008534
|
|
||||||
vt 0.387500 0.008637
|
|
||||||
vt 0.380154 0.012500
|
|
||||||
vt 0.380323 0.008663
|
|
||||||
vt 0.375000 0.012500
|
|
||||||
vt 0.375000 0.008653
|
|
||||||
vt 0.383653 0.005317
|
|
||||||
vt 0.387500 0.005154
|
|
||||||
vt 0.380886 0.005886
|
|
||||||
vt 0.375000 0.005886
|
|
||||||
vt 0.125000 0.744114
|
|
||||||
vt 0.125000 0.737500
|
|
||||||
vt 0.137500 0.500000
|
|
||||||
vt 0.612500 1.000000
|
|
||||||
vt 0.862500 0.750000
|
|
||||||
vt 0.362500 0.750000
|
|
||||||
vt 0.875000 0.512500
|
|
||||||
vt 0.637500 0.500000
|
|
||||||
vn -0.0802 -0.9935 -0.0802
|
|
||||||
vn 0.0802 -0.9935 -0.0802
|
|
||||||
vn 0.0802 -0.9935 0.0802
|
|
||||||
vn -0.0802 -0.9935 0.0802
|
|
||||||
vn -0.9935 -0.0802 0.0802
|
|
||||||
vn -0.9935 0.0802 0.0802
|
|
||||||
vn -0.9935 0.0802 -0.0802
|
|
||||||
vn -0.9935 -0.0802 -0.0802
|
|
||||||
vn 0.0802 -0.0802 0.9935
|
|
||||||
vn 0.0802 0.0802 0.9935
|
|
||||||
vn -0.0802 0.0802 0.9935
|
|
||||||
vn -0.0802 -0.0802 0.9935
|
|
||||||
vn 0.0802 0.9935 -0.0802
|
|
||||||
vn -0.0802 0.9935 -0.0802
|
|
||||||
vn -0.0802 0.9935 0.0802
|
|
||||||
vn 0.0802 0.9935 0.0802
|
|
||||||
vn 0.9935 -0.0802 -0.0802
|
|
||||||
vn 0.9935 0.0802 -0.0802
|
|
||||||
vn 0.9935 0.0802 0.0802
|
|
||||||
vn 0.9935 -0.0802 0.0802
|
|
||||||
vn 0.0802 0.0802 -0.9935
|
|
||||||
vn 0.0801 0.3083 -0.9479
|
|
||||||
vn 0.3068 0.3077 -0.9006
|
|
||||||
vn 0.3084 0.0804 -0.9478
|
|
||||||
vn 0.0754 0.5855 -0.8071
|
|
||||||
vn 0.2854 0.5696 -0.7707
|
|
||||||
vn 0.0757 0.8072 -0.5853
|
|
||||||
vn 0.2858 0.7704 -0.5698
|
|
||||||
vn 0.5698 0.2858 -0.7704
|
|
||||||
vn 0.5853 0.0757 -0.8072
|
|
||||||
vn 0.5155 0.5155 -0.6844
|
|
||||||
vn 0.5155 0.6844 -0.5155
|
|
||||||
vn 0.3083 0.9479 -0.0801
|
|
||||||
vn 0.3077 0.9006 -0.3068
|
|
||||||
vn 0.0804 0.9478 -0.3084
|
|
||||||
vn 0.5855 0.8071 -0.0754
|
|
||||||
vn 0.5696 0.7707 -0.2854
|
|
||||||
vn 0.8072 0.5853 -0.0757
|
|
||||||
vn 0.7704 0.5698 -0.2858
|
|
||||||
vn 0.6844 0.5155 -0.5155
|
|
||||||
vn 0.9479 0.0801 -0.3083
|
|
||||||
vn 0.9006 0.3068 -0.3077
|
|
||||||
vn 0.9478 0.3084 -0.0804
|
|
||||||
vn 0.8071 0.0754 -0.5855
|
|
||||||
vn 0.7707 0.2854 -0.5696
|
|
||||||
vn 0.0801 -0.9479 -0.3083
|
|
||||||
vn 0.3068 -0.9006 -0.3077
|
|
||||||
vn 0.3084 -0.9478 -0.0804
|
|
||||||
vn 0.0754 -0.8071 -0.5855
|
|
||||||
vn 0.2854 -0.7707 -0.5696
|
|
||||||
vn 0.0757 -0.5853 -0.8072
|
|
||||||
vn 0.2858 -0.5698 -0.7704
|
|
||||||
vn 0.5698 -0.7704 -0.2858
|
|
||||||
vn 0.5853 -0.8072 -0.0757
|
|
||||||
vn 0.5155 -0.6844 -0.5155
|
|
||||||
vn 0.5155 -0.5155 -0.6844
|
|
||||||
vn 0.0802 -0.0802 -0.9935
|
|
||||||
vn 0.3083 -0.0801 -0.9479
|
|
||||||
vn 0.3077 -0.3068 -0.9006
|
|
||||||
vn 0.0804 -0.3084 -0.9478
|
|
||||||
vn 0.5855 -0.0754 -0.8071
|
|
||||||
vn 0.5696 -0.2854 -0.7707
|
|
||||||
vn 0.8072 -0.0757 -0.5853
|
|
||||||
vn 0.7704 -0.2858 -0.5698
|
|
||||||
vn 0.6844 -0.5155 -0.5155
|
|
||||||
vn 0.9479 -0.3083 -0.0801
|
|
||||||
vn 0.9006 -0.3077 -0.3068
|
|
||||||
vn 0.9478 -0.0804 -0.3084
|
|
||||||
vn 0.8071 -0.5855 -0.0754
|
|
||||||
vn 0.7707 -0.5696 -0.2854
|
|
||||||
vn 0.9479 0.3083 0.0801
|
|
||||||
vn 0.9006 0.3077 0.3068
|
|
||||||
vn 0.9478 0.0804 0.3084
|
|
||||||
vn 0.8071 0.5855 0.0754
|
|
||||||
vn 0.7707 0.5696 0.2854
|
|
||||||
vn 0.5853 0.8072 0.0757
|
|
||||||
vn 0.5698 0.7704 0.2858
|
|
||||||
vn 0.7704 0.2858 0.5698
|
|
||||||
vn 0.8072 0.0757 0.5853
|
|
||||||
vn 0.6844 0.5155 0.5155
|
|
||||||
vn 0.5155 0.6844 0.5155
|
|
||||||
vn 0.0801 0.9479 0.3083
|
|
||||||
vn 0.3068 0.9006 0.3077
|
|
||||||
vn 0.3084 0.9478 0.0804
|
|
||||||
vn 0.0754 0.8071 0.5855
|
|
||||||
vn 0.2854 0.7707 0.5696
|
|
||||||
vn 0.0757 0.5853 0.8072
|
|
||||||
vn 0.2858 0.5698 0.7704
|
|
||||||
vn 0.5155 0.5155 0.6844
|
|
||||||
vn 0.3083 0.0801 0.9479
|
|
||||||
vn 0.3077 0.3068 0.9006
|
|
||||||
vn 0.0804 0.3084 0.9478
|
|
||||||
vn 0.5855 0.0754 0.8071
|
|
||||||
vn 0.5696 0.2854 0.7707
|
|
||||||
vn 0.9479 -0.0801 0.3083
|
|
||||||
vn 0.9006 -0.3068 0.3077
|
|
||||||
vn 0.9478 -0.3084 0.0804
|
|
||||||
vn 0.8071 -0.0754 0.5855
|
|
||||||
vn 0.7707 -0.2854 0.5696
|
|
||||||
vn 0.5853 -0.0757 0.8072
|
|
||||||
vn 0.5698 -0.2858 0.7704
|
|
||||||
vn 0.7704 -0.5698 0.2858
|
|
||||||
vn 0.8072 -0.5853 0.0757
|
|
||||||
vn 0.6844 -0.5155 0.5155
|
|
||||||
vn 0.5155 -0.5155 0.6844
|
|
||||||
vn 0.0801 -0.3083 0.9479
|
|
||||||
vn 0.3068 -0.3077 0.9006
|
|
||||||
vn 0.3084 -0.0804 0.9478
|
|
||||||
vn 0.0754 -0.5855 0.8071
|
|
||||||
vn 0.2854 -0.5696 0.7707
|
|
||||||
vn 0.0757 -0.8072 0.5853
|
|
||||||
vn 0.2858 -0.7704 0.5698
|
|
||||||
vn 0.5155 -0.6844 0.5155
|
|
||||||
vn 0.3083 -0.9479 0.0801
|
|
||||||
vn 0.3077 -0.9006 0.3068
|
|
||||||
vn 0.0804 -0.9478 0.3084
|
|
||||||
vn 0.5855 -0.8071 0.0754
|
|
||||||
vn 0.5696 -0.7707 0.2854
|
|
||||||
vn -0.0802 0.0802 -0.9935
|
|
||||||
vn -0.3083 0.0801 -0.9479
|
|
||||||
vn -0.3077 0.3068 -0.9006
|
|
||||||
vn -0.0804 0.3084 -0.9478
|
|
||||||
vn -0.5855 0.0754 -0.8071
|
|
||||||
vn -0.5696 0.2854 -0.7707
|
|
||||||
vn -0.8072 0.0757 -0.5853
|
|
||||||
vn -0.7704 0.2858 -0.5698
|
|
||||||
vn -0.2858 0.5698 -0.7704
|
|
||||||
vn -0.0757 0.5853 -0.8072
|
|
||||||
vn -0.5155 0.5155 -0.6844
|
|
||||||
vn -0.6844 0.5155 -0.5155
|
|
||||||
vn -0.9479 0.3083 -0.0801
|
|
||||||
vn -0.9006 0.3077 -0.3068
|
|
||||||
vn -0.9478 0.0804 -0.3084
|
|
||||||
vn -0.8071 0.5855 -0.0754
|
|
||||||
vn -0.7707 0.5696 -0.2854
|
|
||||||
vn -0.5853 0.8072 -0.0757
|
|
||||||
vn -0.5698 0.7704 -0.2858
|
|
||||||
vn -0.5155 0.6844 -0.5155
|
|
||||||
vn -0.0801 0.9479 -0.3083
|
|
||||||
vn -0.3068 0.9006 -0.3077
|
|
||||||
vn -0.3084 0.9478 -0.0804
|
|
||||||
vn -0.0754 0.8071 -0.5855
|
|
||||||
vn -0.2854 0.7707 -0.5696
|
|
||||||
vn -0.9479 -0.0801 -0.3083
|
|
||||||
vn -0.9006 -0.3068 -0.3077
|
|
||||||
vn -0.9478 -0.3084 -0.0804
|
|
||||||
vn -0.8071 -0.0754 -0.5855
|
|
||||||
vn -0.7707 -0.2854 -0.5696
|
|
||||||
vn -0.5853 -0.0757 -0.8072
|
|
||||||
vn -0.5698 -0.2858 -0.7704
|
|
||||||
vn -0.7704 -0.5698 -0.2858
|
|
||||||
vn -0.8072 -0.5853 -0.0757
|
|
||||||
vn -0.6844 -0.5155 -0.5155
|
|
||||||
vn -0.5155 -0.5155 -0.6844
|
|
||||||
vn -0.0802 -0.0802 -0.9935
|
|
||||||
vn -0.0801 -0.3083 -0.9479
|
|
||||||
vn -0.3068 -0.3077 -0.9006
|
|
||||||
vn -0.3084 -0.0804 -0.9478
|
|
||||||
vn -0.0754 -0.5855 -0.8071
|
|
||||||
vn -0.2854 -0.5696 -0.7707
|
|
||||||
vn -0.0757 -0.8072 -0.5853
|
|
||||||
vn -0.2858 -0.7704 -0.5698
|
|
||||||
vn -0.5155 -0.6844 -0.5155
|
|
||||||
vn -0.3083 -0.9479 -0.0801
|
|
||||||
vn -0.3077 -0.9006 -0.3068
|
|
||||||
vn -0.0804 -0.9478 -0.3084
|
|
||||||
vn -0.5855 -0.8071 -0.0754
|
|
||||||
vn -0.5696 -0.7707 -0.2854
|
|
||||||
vn -0.9479 0.0801 0.3083
|
|
||||||
vn -0.9006 0.3068 0.3077
|
|
||||||
vn -0.9478 0.3084 0.0804
|
|
||||||
vn -0.8071 0.0754 0.5855
|
|
||||||
vn -0.7707 0.2854 0.5696
|
|
||||||
vn -0.5853 0.0757 0.8072
|
|
||||||
vn -0.5698 0.2858 0.7704
|
|
||||||
vn -0.7704 0.5698 0.2858
|
|
||||||
vn -0.8072 0.5853 0.0757
|
|
||||||
vn -0.6844 0.5155 0.5155
|
|
||||||
vn -0.5155 0.5155 0.6844
|
|
||||||
vn -0.0801 0.3083 0.9479
|
|
||||||
vn -0.3068 0.3077 0.9006
|
|
||||||
vn -0.3084 0.0804 0.9478
|
|
||||||
vn -0.0754 0.5855 0.8071
|
|
||||||
vn -0.2854 0.5696 0.7707
|
|
||||||
vn -0.0757 0.8072 0.5853
|
|
||||||
vn -0.2858 0.7704 0.5698
|
|
||||||
vn -0.5155 0.6844 0.5155
|
|
||||||
vn -0.3083 0.9479 0.0801
|
|
||||||
vn -0.3077 0.9006 0.3068
|
|
||||||
vn -0.0804 0.9478 0.3084
|
|
||||||
vn -0.5855 0.8071 0.0754
|
|
||||||
vn -0.5696 0.7707 0.2854
|
|
||||||
vn -0.0801 -0.9479 0.3083
|
|
||||||
vn -0.3068 -0.9006 0.3077
|
|
||||||
vn -0.3084 -0.9478 0.0804
|
|
||||||
vn -0.0754 -0.8071 0.5855
|
|
||||||
vn -0.2854 -0.7707 0.5696
|
|
||||||
vn -0.0757 -0.5853 0.8072
|
|
||||||
vn -0.2858 -0.5698 0.7704
|
|
||||||
vn -0.5698 -0.7704 0.2858
|
|
||||||
vn -0.5853 -0.8072 0.0757
|
|
||||||
vn -0.5155 -0.6844 0.5155
|
|
||||||
vn -0.5155 -0.5155 0.6844
|
|
||||||
vn -0.3083 -0.0801 0.9479
|
|
||||||
vn -0.3077 -0.3068 0.9006
|
|
||||||
vn -0.0804 -0.3084 0.9478
|
|
||||||
vn -0.5855 -0.0754 0.8071
|
|
||||||
vn -0.5696 -0.2854 0.7707
|
|
||||||
vn -0.8072 -0.0757 0.5853
|
|
||||||
vn -0.7704 -0.2858 0.5698
|
|
||||||
vn -0.6844 -0.5155 0.5155
|
|
||||||
vn -0.9479 -0.3083 0.0801
|
|
||||||
vn -0.9006 -0.3077 0.3068
|
|
||||||
vn -0.9478 -0.0804 0.3084
|
|
||||||
vn -0.8071 -0.5855 0.0754
|
|
||||||
vn -0.7707 -0.5696 0.2854
|
|
||||||
usemtl Material.001
|
|
||||||
s 1
|
|
||||||
f 138/1/1 28/2/2 84/3/3 190/4/4
|
|
||||||
f 192/5/5 163/6/6 110/7/7 136/8/8
|
|
||||||
f 83/9/9 57/10/10 164/11/11 191/12/12
|
|
||||||
f 2/13/13 111/14/14 165/15/15 56/16/16
|
|
||||||
f 30/17/17 3/18/18 55/19/19 82/20/20
|
|
||||||
f 1/21/21 4/22/22 7/23/23 6/24/24
|
|
||||||
f 4/22/22 5/25/25 8/26/26 7/23/23
|
|
||||||
f 5/25/25 17/27/27 18/28/28 8/26/26
|
|
||||||
f 6/24/24 7/23/23 10/29/29 9/30/30
|
|
||||||
f 7/23/23 8/26/26 11/31/31 10/29/29
|
|
||||||
f 8/26/26 18/28/28 19/32/32 11/31/31
|
|
||||||
f 2/13/13 12/33/33 15/34/34 14/35/35
|
|
||||||
f 12/33/33 13/36/36 16/37/37 15/34/34
|
|
||||||
f 13/36/36 25/38/38 26/39/39 16/37/37
|
|
||||||
f 14/35/35 15/34/34 18/40/28 17/41/27
|
|
||||||
f 15/34/34 16/37/37 19/42/32 18/40/28
|
|
||||||
f 16/37/37 26/39/39 27/43/40 19/42/32
|
|
||||||
f 3/18/18 20/44/41 23/45/42 22/46/43
|
|
||||||
f 20/44/41 21/47/44 24/48/45 23/45/42
|
|
||||||
f 21/47/44 9/30/30 10/29/29 24/48/45
|
|
||||||
f 22/46/43 23/45/42 26/39/39 25/38/38
|
|
||||||
f 23/45/42 24/48/45 27/43/40 26/39/39
|
|
||||||
f 24/48/45 10/29/29 11/31/31 27/43/40
|
|
||||||
f 11/31/31 19/32/32 27/49/40
|
|
||||||
f 28/2/2 31/50/46 34/51/47 33/52/48
|
|
||||||
f 31/50/46 32/53/49 35/54/50 34/51/47
|
|
||||||
f 32/53/49 44/55/51 45/56/52 35/54/50
|
|
||||||
f 33/52/48 34/51/47 37/57/53 36/58/54
|
|
||||||
f 34/51/47 35/54/50 38/59/55 37/57/53
|
|
||||||
f 35/54/50 45/56/52 46/60/56 38/59/55
|
|
||||||
f 29/61/57 39/62/58 42/63/59 41/64/60
|
|
||||||
f 39/62/58 40/65/61 43/66/62 42/63/59
|
|
||||||
f 40/65/61 52/67/63 53/68/64 43/66/62
|
|
||||||
f 41/64/60 42/63/59 45/69/52 44/70/51
|
|
||||||
f 42/63/59 43/66/62 46/71/56 45/69/52
|
|
||||||
f 43/66/62 53/68/64 54/72/65 46/71/56
|
|
||||||
f 30/17/17 47/73/66 50/74/67 49/75/68
|
|
||||||
f 47/73/66 48/76/69 51/77/70 50/74/67
|
|
||||||
f 48/76/69 36/58/54 37/57/53 51/77/70
|
|
||||||
f 49/75/68 50/74/67 53/68/64 52/67/63
|
|
||||||
f 50/74/67 51/77/70 54/72/65 53/68/64
|
|
||||||
f 51/77/70 37/57/53 38/59/55 54/72/65
|
|
||||||
f 38/59/55 46/60/56 54/78/65
|
|
||||||
f 55/19/19 58/79/71 61/80/72 60/81/73
|
|
||||||
f 58/79/71 59/82/74 62/83/75 61/80/72
|
|
||||||
f 59/82/74 71/84/76 72/85/77 62/83/75
|
|
||||||
f 60/81/73 61/80/72 64/86/78 63/87/79
|
|
||||||
f 61/80/72 62/83/75 65/88/80 64/86/78
|
|
||||||
f 62/83/75 72/85/77 73/89/81 65/88/80
|
|
||||||
f 56/16/16 66/90/82 69/91/83 68/92/84
|
|
||||||
f 66/90/82 67/93/85 70/94/86 69/91/83
|
|
||||||
f 67/93/85 79/95/87 80/96/88 70/94/86
|
|
||||||
f 68/92/84 69/91/83 72/85/77 71/84/76
|
|
||||||
f 69/91/83 70/94/86 73/89/81 72/85/77
|
|
||||||
f 70/94/86 80/96/88 81/97/89 73/89/81
|
|
||||||
f 57/10/10 74/98/90 77/99/91 76/100/92
|
|
||||||
f 74/98/90 75/101/93 78/102/94 77/99/91
|
|
||||||
f 75/101/93 63/87/79 64/86/78 78/102/94
|
|
||||||
f 76/100/92 77/99/91 80/103/88 79/104/87
|
|
||||||
f 77/99/91 78/102/94 81/105/89 80/103/88
|
|
||||||
f 78/102/94 64/86/78 65/88/80 81/105/89
|
|
||||||
f 65/88/80 73/106/81 81/107/89
|
|
||||||
f 82/20/20 85/108/95 88/109/96 87/110/97
|
|
||||||
f 85/108/95 86/111/98 89/112/99 88/109/96
|
|
||||||
f 86/111/98 98/113/100 99/114/101 89/112/99
|
|
||||||
f 87/110/97 88/109/96 91/115/102 90/116/103
|
|
||||||
f 88/109/96 89/112/99 92/117/104 91/115/102
|
|
||||||
f 89/112/99 99/114/101 100/118/105 92/117/104
|
|
||||||
f 83/9/9 93/119/106 96/120/107 95/121/108
|
|
||||||
f 93/119/106 94/122/109 97/123/110 96/120/107
|
|
||||||
f 94/122/109 106/124/111 107/125/112 97/123/110
|
|
||||||
f 95/121/108 96/120/107 99/114/101 98/113/100
|
|
||||||
f 96/120/107 97/123/110 100/118/105 99/114/101
|
|
||||||
f 97/123/110 107/125/112 108/126/113 100/118/105
|
|
||||||
f 84/3/3 101/127/114 104/128/115 103/129/116
|
|
||||||
f 101/127/114 102/130/117 105/131/118 104/128/115
|
|
||||||
f 102/130/117 90/116/103 91/115/102 105/131/118
|
|
||||||
f 103/129/116 104/128/115 107/132/112 106/133/111
|
|
||||||
f 104/128/115 105/131/118 108/134/113 107/132/112
|
|
||||||
f 105/131/118 91/115/102 92/117/104 108/134/113
|
|
||||||
f 92/117/104 100/135/105 108/136/113
|
|
||||||
f 109/137/119 112/138/120 115/139/121 114/140/122
|
|
||||||
f 112/138/120 113/141/123 116/142/124 115/139/121
|
|
||||||
f 113/141/123 125/143/125 126/144/126 116/142/124
|
|
||||||
f 114/140/122 115/139/121 118/145/127 117/146/128
|
|
||||||
f 115/139/121 116/142/124 119/147/129 118/145/127
|
|
||||||
f 116/142/124 126/144/126 127/148/130 119/147/129
|
|
||||||
f 110/7/7 120/149/131 123/150/132 122/151/133
|
|
||||||
f 120/149/131 121/152/134 124/153/135 123/150/132
|
|
||||||
f 121/152/134 133/154/136 134/155/137 124/153/135
|
|
||||||
f 122/151/133 123/150/132 126/144/126 125/143/125
|
|
||||||
f 123/150/132 124/153/135 127/148/130 126/144/126
|
|
||||||
f 124/153/135 134/155/137 135/156/138 127/148/130
|
|
||||||
f 111/14/14 128/157/139 131/158/140 130/159/141
|
|
||||||
f 128/157/139 129/160/142 132/161/143 131/158/140
|
|
||||||
f 129/160/142 117/162/128 118/163/127 132/161/143
|
|
||||||
f 130/159/141 131/158/140 134/164/137 133/165/136
|
|
||||||
f 131/158/140 132/161/143 135/166/138 134/164/137
|
|
||||||
f 132/161/143 118/163/127 119/167/129 135/166/138
|
|
||||||
f 119/147/129 127/168/130 135/169/138
|
|
||||||
f 136/8/8 139/170/144 142/171/145 141/172/146
|
|
||||||
f 139/170/144 140/173/147 143/174/148 142/171/145
|
|
||||||
f 140/173/147 152/175/149 153/176/150 143/174/148
|
|
||||||
f 141/172/146 142/171/145 145/177/151 144/178/152
|
|
||||||
f 142/171/145 143/174/148 146/179/153 145/177/151
|
|
||||||
f 143/174/148 153/176/150 154/180/154 146/179/153
|
|
||||||
f 137/181/155 147/182/156 150/183/157 149/184/158
|
|
||||||
f 147/182/156 148/185/159 151/186/160 150/183/157
|
|
||||||
f 148/185/159 160/187/161 161/188/162 151/186/160
|
|
||||||
f 149/184/158 150/183/157 153/176/150 152/175/149
|
|
||||||
f 150/183/157 151/186/160 154/180/154 153/176/150
|
|
||||||
f 151/186/160 161/188/162 162/189/163 154/180/154
|
|
||||||
f 138/1/1 155/190/164 158/191/165 157/192/166
|
|
||||||
f 155/190/164 156/193/167 159/194/168 158/191/165
|
|
||||||
f 156/193/167 144/195/152 145/196/151 159/194/168
|
|
||||||
f 157/192/166 158/191/165 161/197/162 160/198/161
|
|
||||||
f 158/191/165 159/194/168 162/199/163 161/197/162
|
|
||||||
f 159/194/168 145/196/151 146/200/153 162/199/163
|
|
||||||
f 146/179/153 154/201/154 162/202/163
|
|
||||||
f 163/6/6 166/203/169 169/204/170 168/205/171
|
|
||||||
f 166/203/169 167/206/172 170/207/173 169/204/170
|
|
||||||
f 167/206/172 179/208/174 180/209/175 170/207/173
|
|
||||||
f 168/205/171 169/204/170 172/210/176 171/211/177
|
|
||||||
f 169/204/170 170/207/173 173/212/178 172/210/176
|
|
||||||
f 170/207/173 180/209/175 181/213/179 173/212/178
|
|
||||||
f 164/11/11 174/214/180 177/215/181 176/216/182
|
|
||||||
f 174/214/180 175/217/183 178/218/184 177/215/181
|
|
||||||
f 175/217/183 187/219/185 188/220/186 178/218/184
|
|
||||||
f 176/216/182 177/215/181 180/221/175 179/222/174
|
|
||||||
f 177/215/181 178/218/184 181/223/179 180/221/175
|
|
||||||
f 178/218/184 188/220/186 189/224/187 181/223/179
|
|
||||||
f 165/15/15 182/225/188 185/226/189 184/227/190
|
|
||||||
f 182/225/188 183/228/191 186/229/192 185/226/189
|
|
||||||
f 183/228/191 171/230/177 172/231/176 186/229/192
|
|
||||||
f 184/227/190 185/226/189 188/232/186 187/233/185
|
|
||||||
f 185/226/189 186/229/192 189/234/187 188/232/186
|
|
||||||
f 186/229/192 172/231/176 173/235/178 189/234/187
|
|
||||||
f 173/212/178 181/213/179 189/236/187
|
|
||||||
f 190/4/4 193/237/193 196/238/194 195/239/195
|
|
||||||
f 193/237/193 194/240/196 197/241/197 196/238/194
|
|
||||||
f 194/240/196 206/242/198 207/243/199 197/241/197
|
|
||||||
f 195/239/195 196/238/194 199/244/200 198/245/201
|
|
||||||
f 196/238/194 197/241/197 200/246/202 199/244/200
|
|
||||||
f 197/241/197 207/243/199 208/247/203 200/246/202
|
|
||||||
f 191/12/12 201/248/204 204/249/205 203/250/206
|
|
||||||
f 201/248/204 202/251/207 205/252/208 204/249/205
|
|
||||||
f 202/251/207 214/253/209 215/254/210 205/252/208
|
|
||||||
f 203/250/206 204/249/205 207/255/199 206/256/198
|
|
||||||
f 204/249/205 205/252/208 208/257/203 207/255/199
|
|
||||||
f 205/252/208 215/254/210 216/258/211 208/257/203
|
|
||||||
f 192/5/5 209/259/212 212/260/213 211/261/214
|
|
||||||
f 209/259/212 210/262/215 213/263/216 212/260/213
|
|
||||||
f 210/262/215 198/264/201 199/265/200 213/263/216
|
|
||||||
f 211/261/214 212/260/213 215/266/210 214/267/209
|
|
||||||
f 212/260/213 213/263/216 216/268/211 215/266/210
|
|
||||||
f 213/263/216 199/265/200 200/269/202 216/268/211
|
|
||||||
f 200/246/202 208/247/203 216/270/211
|
|
||||||
f 138/1/1 190/4/4 195/239/195 155/190/164
|
|
||||||
f 155/190/164 195/239/195 198/245/201 156/193/167
|
|
||||||
f 156/193/167 198/245/201 210/271/215 144/195/152
|
|
||||||
f 144/178/152 210/262/215 209/259/212 141/172/146
|
|
||||||
f 141/172/146 209/259/212 192/5/5 136/8/8
|
|
||||||
f 28/2/2 138/1/1 157/192/166 31/50/46
|
|
||||||
f 31/50/46 157/192/166 160/198/161 32/53/49
|
|
||||||
f 32/53/49 160/198/161 148/272/159 44/55/51
|
|
||||||
f 44/70/51 148/185/159 147/182/156 41/64/60
|
|
||||||
f 41/64/60 147/182/156 137/181/155 29/61/57
|
|
||||||
f 3/18/18 30/17/17 49/75/68 20/44/41
|
|
||||||
f 20/44/41 49/75/68 52/67/63 21/47/44
|
|
||||||
f 21/47/44 52/67/63 40/65/61 9/30/30
|
|
||||||
f 9/30/30 40/65/61 39/62/58 6/24/24
|
|
||||||
f 6/24/24 39/62/58 29/61/57 1/21/21
|
|
||||||
f 191/12/12 164/11/11 176/216/182 201/248/204
|
|
||||||
f 201/248/204 176/216/182 179/222/174 202/251/207
|
|
||||||
f 202/251/207 179/222/174 167/273/172 214/253/209
|
|
||||||
f 214/267/209 167/206/172 166/203/169 211/261/214
|
|
||||||
f 211/261/214 166/203/169 163/6/6 192/5/5
|
|
||||||
f 57/10/10 83/9/9 95/121/108 74/98/90
|
|
||||||
f 74/98/90 95/121/108 98/113/100 75/101/93
|
|
||||||
f 75/101/93 98/113/100 86/111/98 63/87/79
|
|
||||||
f 63/87/79 86/111/98 85/108/95 60/81/73
|
|
||||||
f 60/81/73 85/108/95 82/20/20 55/19/19
|
|
||||||
f 109/137/119 137/181/155 149/184/158 112/138/120
|
|
||||||
f 112/138/120 149/184/158 152/175/149 113/141/123
|
|
||||||
f 113/141/123 152/175/149 140/173/147 125/143/125
|
|
||||||
f 125/143/125 140/173/147 139/170/144 122/151/133
|
|
||||||
f 122/151/133 139/170/144 136/8/8 110/7/7
|
|
||||||
f 56/16/16 165/15/15 184/227/190 66/90/82
|
|
||||||
f 66/90/82 184/227/190 187/233/185 67/93/85
|
|
||||||
f 67/93/85 187/233/185 175/274/183 79/95/87
|
|
||||||
f 79/104/87 175/217/183 174/214/180 76/100/92
|
|
||||||
f 76/100/92 174/214/180 164/11/11 57/10/10
|
|
||||||
f 2/13/13 56/16/16 68/92/84 12/33/33
|
|
||||||
f 12/33/33 68/92/84 71/84/76 13/36/36
|
|
||||||
f 13/36/36 71/84/76 59/82/74 25/38/38
|
|
||||||
f 25/38/38 59/82/74 58/79/71 22/46/43
|
|
||||||
f 22/46/43 58/79/71 55/19/19 3/18/18
|
|
||||||
f 190/4/4 84/3/3 103/129/116 193/237/193
|
|
||||||
f 193/237/193 103/129/116 106/133/111 194/240/196
|
|
||||||
f 194/240/196 106/133/111 94/275/109 206/242/198
|
|
||||||
f 206/256/198 94/122/109 93/119/106 203/250/206
|
|
||||||
f 203/250/206 93/119/106 83/9/9 191/12/12
|
|
||||||
f 165/15/15 111/14/14 130/159/141 182/225/188
|
|
||||||
f 182/225/188 130/159/141 133/165/136 183/228/191
|
|
||||||
f 183/228/191 133/165/136 121/276/134 171/230/177
|
|
||||||
f 171/211/177 121/152/134 120/149/131 168/205/171
|
|
||||||
f 168/205/171 120/149/131 110/7/7 163/6/6
|
|
||||||
f 111/14/14 2/13/13 14/35/35 128/157/139
|
|
||||||
f 128/157/139 14/35/35 17/41/27 129/160/142
|
|
||||||
f 129/160/142 17/41/27 5/277/25 117/162/128
|
|
||||||
f 117/146/128 5/25/25 4/22/22 114/140/122
|
|
||||||
f 114/140/122 4/22/22 1/21/21 109/137/119
|
|
||||||
f 84/3/3 28/2/2 33/52/48 101/127/114
|
|
||||||
f 101/127/114 33/52/48 36/58/54 102/130/117
|
|
||||||
f 102/130/117 36/58/54 48/76/69 90/116/103
|
|
||||||
f 90/116/103 48/76/69 47/73/66 87/110/97
|
|
||||||
f 87/110/97 47/73/66 30/17/17 82/20/20
|
|
||||||
f 137/181/155 109/137/119 1/21/21 29/61/57
|
|
||||||
BIN
res/pure-sky.hdr
BIN
res/pure-sky.hdr
Binary file not shown.
BIN
res/skybox.glb
BIN
res/skybox.glb
Binary file not shown.
BIN
res/skybox.hdr
BIN
res/skybox.hdr
Binary file not shown.
@ -1,22 +0,0 @@
|
|||||||
# File generated by ImageToStl.com - Free Image and 3D model conversion tools
|
|
||||||
|
|
||||||
newmtl mat0
|
|
||||||
Ns 0
|
|
||||||
Ka 1.0 1.0 1.0
|
|
||||||
Kd 0 0 0
|
|
||||||
Ks 0.5 0.5 0.5
|
|
||||||
Ke 1 1 1
|
|
||||||
Ni 1.0
|
|
||||||
d 1
|
|
||||||
illum 2
|
|
||||||
|
|
||||||
newmtl mat1
|
|
||||||
Ns 250
|
|
||||||
Ka 1.0 1.0 1.0
|
|
||||||
Kd 0.85 0.85 0.85
|
|
||||||
Ks 0 0 0
|
|
||||||
Ke 0.0 0.0 0.0
|
|
||||||
Ni 1.0
|
|
||||||
d 1
|
|
||||||
illum 1
|
|
||||||
|
|
||||||
2568
res/skybox.obj
2568
res/skybox.obj
File diff suppressed because it is too large
Load Diff
BIN
res/skybox.png
BIN
res/skybox.png
Binary file not shown.
|
Before Width: | Height: | Size: 48 MiB |
11
simulator/Cargo.toml
Normal file
11
simulator/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "simulator"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
solar_engine = { path = "../solar_engine" }
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11.8"
|
||||||
|
pollster = "0.4.0"
|
||||||
|
cgmath = "0.18.0"
|
||||||
146
simulator/src/main.rs
Normal file
146
simulator/src/main.rs
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
use cgmath::{Rotation3, Vector3};
|
||||||
|
use solar_engine::{Application, Body, InputEvent, Key, Light, MouseButton, RenderInstance, Shape, Simulator};
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
pub async fn run() {
|
||||||
|
let simulator = Arc::new(RwLock::new(Simulator::new()));
|
||||||
|
{
|
||||||
|
let mut sim = simulator.write().unwrap();
|
||||||
|
sim.add_body(Body {
|
||||||
|
name: "Sun".into(),
|
||||||
|
position: Vector3::new(0.0, 0.0, 0.0),
|
||||||
|
velocity: Vector3::new(0.0, 0.0, 0.0),
|
||||||
|
mass: 1.989e30,
|
||||||
|
radius: 6.963e8,
|
||||||
|
});
|
||||||
|
sim.add_body(Body {
|
||||||
|
name: "Earth".into(),
|
||||||
|
position: Vector3::new(1.496e11, 0.0, 0.0),
|
||||||
|
velocity: Vector3::new(0.0, 29780.0, 0.0),
|
||||||
|
mass: 5.972e24,
|
||||||
|
radius: 6.371e6,
|
||||||
|
});
|
||||||
|
|
||||||
|
let earth_position = sim.bodies[1].position;
|
||||||
|
let earth_velocity = sim.bodies[1].velocity;
|
||||||
|
|
||||||
|
sim.add_body(Body {
|
||||||
|
name: "Moon".into(),
|
||||||
|
position: earth_position + Vector3::new(384.4e6, 0.0, 0.0),
|
||||||
|
velocity: earth_velocity + Vector3::new(0.0, 1022.0, 0.0),
|
||||||
|
mass: 7.342e22,
|
||||||
|
radius: 1.737e6,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let sim_clone = simulator.clone();
|
||||||
|
thread::spawn(move || {
|
||||||
|
use std::time::{Duration, Instant};
|
||||||
|
let mut last = Instant::now();
|
||||||
|
loop {
|
||||||
|
let now = Instant::now();
|
||||||
|
let dt = now.duration_since(last).as_secs_f64();
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut sim = sim_clone.write().unwrap();
|
||||||
|
let timewarp = sim.get_timewarp();
|
||||||
|
sim.step(dt * timewarp as f64);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread::sleep(Duration::from_nanos(1));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let simulator_clone = simulator.clone();
|
||||||
|
|
||||||
|
Application::new()
|
||||||
|
.on_update(move |state| {
|
||||||
|
let sim = simulator_clone.read().unwrap();
|
||||||
|
let bodies = &sim.bodies;
|
||||||
|
|
||||||
|
let sun_pos = bodies[0].position / 1.496e11;
|
||||||
|
|
||||||
|
state.light_manager.clear();
|
||||||
|
state.light_manager.add_light(Light::new_point(
|
||||||
|
sun_pos.cast::<f32>().unwrap(),
|
||||||
|
Vector3::from([1.0, 1.0, 0.8]),
|
||||||
|
5.0,
|
||||||
|
1.0 / 1000.0,
|
||||||
|
));
|
||||||
|
|
||||||
|
let instances = bodies
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, b)| {
|
||||||
|
RenderInstance {
|
||||||
|
position: ((b.position / 1.496e11) - sun_pos).cast::<f32>().unwrap(),
|
||||||
|
rotation: cgmath::Quaternion::from_angle_z(cgmath::Deg(0.0)),
|
||||||
|
color: match i {
|
||||||
|
0 => [1.0, 1.0, 0.0], // Sun
|
||||||
|
1 => [0.0, 0.0, 1.0], // Earth
|
||||||
|
_ => [0.5, 0.5, 0.5],
|
||||||
|
},
|
||||||
|
scale: 0.05,
|
||||||
|
shape: Shape::Sphere,
|
||||||
|
always_lit: i == 0, // Sun
|
||||||
|
is_transparent: false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
state.set_instances(instances);
|
||||||
|
})
|
||||||
|
.on_input({
|
||||||
|
let simulator = simulator.clone();
|
||||||
|
move |state, event| {
|
||||||
|
match event {
|
||||||
|
InputEvent::MouseDragged { delta, button: MouseButton::Left } => {
|
||||||
|
state.camera_mut().rotate_yaw_pitch(-delta.x as f32 * 0.1, delta.y as f32 * 0.1);
|
||||||
|
}
|
||||||
|
InputEvent::MouseWheel { delta } => {
|
||||||
|
state.camera_mut().zoom(delta * 0.05);
|
||||||
|
}
|
||||||
|
InputEvent::KeyPressed { key, .. } => {
|
||||||
|
match key {
|
||||||
|
Key::ArrowLeft => {
|
||||||
|
state.camera_mut().translate(Vector3::new(1.0, 0.0, 0.0));
|
||||||
|
}
|
||||||
|
Key::ArrowRight => {
|
||||||
|
state.camera_mut().translate(Vector3::new(-1.0, 0.0, 0.0));
|
||||||
|
}
|
||||||
|
Key::ArrowUp => {
|
||||||
|
state.camera_mut().translate(Vector3::new(0.0, -1.0, 0.0));
|
||||||
|
}
|
||||||
|
Key::ArrowDown => {
|
||||||
|
state.camera_mut().translate(Vector3::new(0.0, 1.0, 0.0));
|
||||||
|
}
|
||||||
|
Key::Period => {
|
||||||
|
let mut sim = simulator.write().unwrap();
|
||||||
|
sim.increase_timewarp();
|
||||||
|
println!("Timewarp: {}", sim.get_timewarp());
|
||||||
|
}
|
||||||
|
Key::Comma => {
|
||||||
|
let mut sim = simulator.write().unwrap();
|
||||||
|
sim.decrease_timewarp();
|
||||||
|
println!("Timewarp: {}", sim.get_timewarp());
|
||||||
|
}
|
||||||
|
Key::Minus => {
|
||||||
|
let mut sim = simulator.write().unwrap();
|
||||||
|
sim.reset_timewarp();
|
||||||
|
println!("Timewarp: {}", sim.get_timewarp());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
pollster::block_on(run());
|
||||||
|
}
|
||||||
7
solar_engine/Cargo.lock
generated
Normal file
7
solar_engine/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "solar_engine"
|
||||||
|
version = "0.1.0"
|
||||||
14
solar_engine/Cargo.toml
Normal file
14
solar_engine/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "solar_engine"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
rayon = "1.8"
|
||||||
|
winit = "0.30.10"
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11.8"
|
||||||
|
bytemuck = "1.23.0"
|
||||||
|
wgpu = "25.0"
|
||||||
|
pollster = "0.4.0"
|
||||||
|
cgmath = "0.18.0"
|
||||||
88
solar_engine/src/application.rs
Normal file
88
solar_engine/src/application.rs
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
use winit::application::ApplicationHandler;
|
||||||
|
use winit::event::{ElementState, Modifiers, MouseScrollDelta, WindowEvent};
|
||||||
|
use winit::event_loop::ActiveEventLoop;
|
||||||
|
use winit::window::{Window, WindowId};
|
||||||
|
use crate::input::{InputEvent, InputTracker};
|
||||||
|
|
||||||
|
pub struct StateApplication<'a> {
|
||||||
|
state: Option<crate::state::State<'a>>,
|
||||||
|
modifiers: Modifiers,
|
||||||
|
update_fn: Option<Box<dyn FnMut(&mut crate::state::State<'a>) + 'a>>,
|
||||||
|
input_fn: Option<Box<dyn FnMut(&mut crate::state::State<'a>, &InputEvent) + 'a>>,
|
||||||
|
input_tracker: InputTracker
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> StateApplication<'a> {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { state: None, update_fn: None, input_fn: None, modifiers: Modifiers::default(), input_tracker: InputTracker::default() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_update<F: FnMut(&mut crate::state::State<'a>) + 'a>(mut self, func: F) -> Self {
|
||||||
|
self.update_fn = Some(Box::new(func));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_input<F: FnMut(&mut crate::state::State<'a>, &InputEvent) + 'a>(mut self, func: F) -> Self {
|
||||||
|
self.input_fn = Some(Box::new(func));
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(mut self) {
|
||||||
|
let event_loop = winit::event_loop::EventLoop::new().unwrap();
|
||||||
|
let _ = event_loop.run_app(&mut self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ApplicationHandler for StateApplication<'a> {
|
||||||
|
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||||
|
let window = event_loop
|
||||||
|
.create_window(Window::default_attributes().with_title("Solar Engine"))
|
||||||
|
.unwrap();
|
||||||
|
self.state = Some(crate::state::State::new(window));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_event(
|
||||||
|
&mut self,
|
||||||
|
event_loop: &ActiveEventLoop,
|
||||||
|
window_id: WindowId,
|
||||||
|
event: WindowEvent,
|
||||||
|
) {
|
||||||
|
let window = self.state.as_ref().unwrap().window();
|
||||||
|
|
||||||
|
if window.id() == window_id {
|
||||||
|
match event {
|
||||||
|
WindowEvent::CloseRequested => {
|
||||||
|
event_loop.exit();
|
||||||
|
}
|
||||||
|
WindowEvent::Resized(physical_size) => {
|
||||||
|
self.state.as_mut().unwrap().resize(physical_size);
|
||||||
|
}
|
||||||
|
WindowEvent::RedrawRequested => {
|
||||||
|
if let (Some(state), Some(update_fn)) = (self.state.as_mut(), self.update_fn.as_mut()) {
|
||||||
|
update_fn(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state.as_mut().unwrap().render().unwrap();
|
||||||
|
}
|
||||||
|
WindowEvent::ModifiersChanged(modifiers) => {
|
||||||
|
self.modifiers = modifiers;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(state) = self.state.as_mut() {
|
||||||
|
if let Some(event) = self.input_tracker.handle_window_event(&event, self.modifiers) {
|
||||||
|
if let Some(input_fn) = self.input_fn.as_mut() {
|
||||||
|
input_fn(state, &event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||||
|
if let Some(state) = self.state.as_ref() {
|
||||||
|
state.window().request_redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
22
solar_engine/src/body.rs
Normal file
22
solar_engine/src/body.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
use cgmath::Vector3;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Body {
|
||||||
|
pub name: String,
|
||||||
|
pub mass: f64,
|
||||||
|
pub position: Vector3<f64>,
|
||||||
|
pub velocity: Vector3<f64>,
|
||||||
|
pub radius: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub fn new(name: &str, mass: f64, position: Vector3<f64>, velocity: Vector3<f64>, radius: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
mass,
|
||||||
|
position,
|
||||||
|
velocity,
|
||||||
|
radius,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
68
solar_engine/src/camera.rs
Normal file
68
solar_engine/src/camera.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
use cgmath::{Matrix4, Point3, Vector3, Deg, perspective, InnerSpace, Rotation, Quaternion, Rotation3, Rad};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Camera {
|
||||||
|
pub eye: Point3<f32>,
|
||||||
|
pub target: Point3<f32>,
|
||||||
|
pub up: Vector3<f32>,
|
||||||
|
pub fov_y: Deg<f32>,
|
||||||
|
pub aspect: f32,
|
||||||
|
pub znear: f32,
|
||||||
|
pub zfar: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Camera {
|
||||||
|
pub fn new(aspect: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
eye: Point3::new(0.0, 0.0, 5.0),
|
||||||
|
target: Point3::new(0.0, 0.0, 0.0),
|
||||||
|
up: Vector3::unit_y(),
|
||||||
|
fov_y: Deg(45.0),
|
||||||
|
aspect,
|
||||||
|
znear: 0.1,
|
||||||
|
zfar: 100.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_view_projection_matrix(&self) -> Matrix4<f32> {
|
||||||
|
let view = Matrix4::look_at_rh(self.eye, self.target, self.up);
|
||||||
|
let proj = perspective(self.fov_y, self.aspect, self.znear, self.zfar);
|
||||||
|
proj * view
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_view_matrix(&self) -> Matrix4<f32> {
|
||||||
|
Matrix4::look_at_rh(self.eye, self.target, self.up)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rotate_yaw_pitch(&mut self, yaw: f32, pitch: f32) {
|
||||||
|
let offset = self.eye - self.target;
|
||||||
|
|
||||||
|
let yaw_q = Quaternion::from_axis_angle(Vector3::unit_y(), Rad(yaw.to_radians()));
|
||||||
|
let right = offset.cross(self.up).normalize();
|
||||||
|
let pitch_q = Quaternion::from_axis_angle(right, Rad(pitch.to_radians()));
|
||||||
|
|
||||||
|
let rotation = yaw_q * pitch_q;
|
||||||
|
let new_offset = rotation.rotate_vector(offset);
|
||||||
|
|
||||||
|
self.eye = self.target + new_offset;
|
||||||
|
self.up = rotation.rotate_vector(self.up);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn translate(&mut self, translation: Vector3<f32>) {
|
||||||
|
let dir = (self.target - self.eye).normalize();
|
||||||
|
let horizontal = Vector3::unit_y().cross(dir).normalize();
|
||||||
|
let vertical = horizontal.cross(dir).normalize();
|
||||||
|
|
||||||
|
self.eye += horizontal * translation.x + vertical * translation.y;
|
||||||
|
self.target += horizontal * translation.x + vertical * translation.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn zoom(&mut self, amount: f32) {
|
||||||
|
let dir = (self.target - self.eye).normalize();
|
||||||
|
self.eye += dir * amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_aspect(&mut self, aspect: f32) {
|
||||||
|
self.aspect = aspect;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
solar_engine/src/device_manager.rs
Normal file
10
solar_engine/src/device_manager.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
use wgpu::{Device, Queue, Surface, SurfaceConfiguration};
|
||||||
|
use crate::state::SampleCount;
|
||||||
|
|
||||||
|
pub struct DeviceManager<'a> {
|
||||||
|
pub surface: Surface<'a>,
|
||||||
|
pub device: Device,
|
||||||
|
pub queue: Queue,
|
||||||
|
pub config: SurfaceConfiguration,
|
||||||
|
pub sample_count: SampleCount,
|
||||||
|
}
|
||||||
142
solar_engine/src/geometry_manager.rs
Normal file
142
solar_engine/src/geometry_manager.rs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use wgpu::{Device, Buffer};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use crate::renderer::Vertex;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Shape {
|
||||||
|
Circle,
|
||||||
|
Sphere,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Geometry {
|
||||||
|
pub(crate) vertex_buffer: Buffer,
|
||||||
|
pub(crate) index_buffer: Buffer,
|
||||||
|
pub(crate) index_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GeometryManager {
|
||||||
|
pub geometries: HashMap<Shape, Geometry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GeometryManager {
|
||||||
|
pub fn new(device: &Device) -> Self {
|
||||||
|
let mut geometries = HashMap::new();
|
||||||
|
|
||||||
|
// Circle
|
||||||
|
let (circle_vertices, circle_indices) = create_circle_vertices(512, 0.5, [0.5, 0.5, 0.5]);
|
||||||
|
geometries.insert(
|
||||||
|
Shape::Circle,
|
||||||
|
Self::create_geometry(device, &circle_vertices, &circle_indices),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Sphere
|
||||||
|
let (sphere_vertices, sphere_indices) = create_sphere_vertices(32, 32, 0.5, [0.5, 0.5, 0.5]);
|
||||||
|
geometries.insert(
|
||||||
|
Shape::Sphere,
|
||||||
|
Self::create_geometry(device, &sphere_vertices, &sphere_indices),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Füge hier beliebige weitere Shapes hinzu
|
||||||
|
|
||||||
|
Self { geometries }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, shape: &Shape) -> Option<&Geometry> {
|
||||||
|
self.geometries.get(shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shapes(&self) -> impl Iterator<Item = Shape> + '_ {
|
||||||
|
self.geometries.keys().copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_geometry(device: &Device, vertices: &[Vertex], indices: &[u16]) -> Geometry {
|
||||||
|
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Vertex Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(vertices),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Index Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(indices),
|
||||||
|
usage: wgpu::BufferUsages::INDEX,
|
||||||
|
});
|
||||||
|
|
||||||
|
Geometry {
|
||||||
|
vertex_buffer,
|
||||||
|
index_buffer,
|
||||||
|
index_count: indices.len() as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_circle_vertices(segment_count: usize, radius: f32, color: [f32; 3]) -> (Vec<Vertex>, Vec<u16>) {
|
||||||
|
let mut vertices = vec![Vertex {
|
||||||
|
position: [0.0, 0.0, 0.0],
|
||||||
|
color,
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
}];
|
||||||
|
let mut indices = vec![];
|
||||||
|
|
||||||
|
for i in 0..=segment_count {
|
||||||
|
let theta = (i as f32) / (segment_count as f32) * std::f32::consts::TAU;
|
||||||
|
let x = radius * theta.cos();
|
||||||
|
let y = radius * theta.sin();
|
||||||
|
vertices.push(Vertex {
|
||||||
|
position: [x, y, 0.0],
|
||||||
|
color,
|
||||||
|
normal: [0.0, 0.0, 1.0],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 1..=segment_count {
|
||||||
|
indices.push(0);
|
||||||
|
indices.push(i as u16);
|
||||||
|
indices.push((i % segment_count + 1) as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
(vertices, indices)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_sphere_vertices(stacks: usize, slices: usize, radius: f32, color: [f32; 3]) -> (Vec<Vertex>, Vec<u16>) {
|
||||||
|
let mut vertices = Vec::new();
|
||||||
|
let mut indices = Vec::new();
|
||||||
|
|
||||||
|
for i in 0..=stacks {
|
||||||
|
let phi = std::f32::consts::PI * (i as f32) / (stacks as f32);
|
||||||
|
let y = phi.cos();
|
||||||
|
let r = phi.sin();
|
||||||
|
|
||||||
|
for j in 0..=slices {
|
||||||
|
let theta = 2.0 * std::f32::consts::PI * (j as f32) / (slices as f32);
|
||||||
|
let x = r * theta.cos();
|
||||||
|
let z = r * theta.sin();
|
||||||
|
|
||||||
|
let normal = [x, y, z];
|
||||||
|
|
||||||
|
vertices.push(Vertex {
|
||||||
|
position: [x * radius, y * radius, z * radius],
|
||||||
|
color,
|
||||||
|
normal,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..stacks {
|
||||||
|
for j in 0..slices {
|
||||||
|
let first = i * (slices + 1) + j;
|
||||||
|
let second = first + slices + 1;
|
||||||
|
|
||||||
|
indices.push(first as u16);
|
||||||
|
indices.push(second as u16);
|
||||||
|
indices.push((first + 1) as u16);
|
||||||
|
|
||||||
|
indices.push(second as u16);
|
||||||
|
indices.push((second + 1) as u16);
|
||||||
|
indices.push((first + 1) as u16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(vertices, indices)
|
||||||
|
}
|
||||||
94
solar_engine/src/globals.rs
Normal file
94
solar_engine/src/globals.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use crate::camera::Camera;
|
||||||
|
use wgpu::{BindGroup, BindGroupLayout, Buffer, Device, Queue};
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
|
pub struct GlobalsUniform {
|
||||||
|
pub view_proj: [[f32; 4]; 4],
|
||||||
|
pub resolution: [f32; 2],
|
||||||
|
_padding: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct GlobalsManager {
|
||||||
|
buffer: Buffer,
|
||||||
|
pub(crate) bind_group: BindGroup,
|
||||||
|
layout: BindGroupLayout,
|
||||||
|
resolution: [f32; 2],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GlobalsManager {
|
||||||
|
pub fn new(device: &Device, width: u32, height: u32, camera: &Camera) -> Self {
|
||||||
|
let resolution = [width as f32, height as f32];
|
||||||
|
let view_proj = camera.build_view_projection_matrix();
|
||||||
|
|
||||||
|
let data = GlobalsUniform {
|
||||||
|
view_proj: view_proj.into(),
|
||||||
|
resolution,
|
||||||
|
_padding: [0.0; 2],
|
||||||
|
};
|
||||||
|
|
||||||
|
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Globals Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&[data]),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("Globals Bind Group Layout"),
|
||||||
|
entries: &[wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("Globals Bind Group"),
|
||||||
|
layout: &layout,
|
||||||
|
entries: &[wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
bind_group,
|
||||||
|
layout,
|
||||||
|
resolution,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, queue: &Queue, camera: &Camera) {
|
||||||
|
let view_proj = camera.build_view_projection_matrix();
|
||||||
|
let data = GlobalsUniform {
|
||||||
|
view_proj: view_proj.into(),
|
||||||
|
resolution: self.resolution,
|
||||||
|
_padding: [0.0; 2],
|
||||||
|
};
|
||||||
|
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[data]));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, width: u32, height: u32) {
|
||||||
|
self.resolution = [width as f32, height as f32];
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layout(&self) -> &BindGroupLayout {
|
||||||
|
&self.layout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_group(&self) -> &BindGroup {
|
||||||
|
&self.bind_group
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resolution(&self) -> [f32; 2] {
|
||||||
|
self.resolution
|
||||||
|
}
|
||||||
|
}
|
||||||
257
solar_engine/src/input.rs
Normal file
257
solar_engine/src/input.rs
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
use cgmath::Vector2;
|
||||||
|
use winit::event::{ElementState, KeyEvent, MouseScrollDelta, Modifiers, WindowEvent};
|
||||||
|
use winit::keyboard::{Key as WinitKey, ModifiersKeyState};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Key {
|
||||||
|
// Symbols
|
||||||
|
Comma, Period, Minus, Plus, Slash, Backslash, Semicolon, Apostrophe,
|
||||||
|
LeftBracket, RightBracket, Grave, Equal,
|
||||||
|
|
||||||
|
// Digits
|
||||||
|
Num0, Num1, Num2, Num3, Num4, Num5, Num6, Num7, Num8, Num9,
|
||||||
|
|
||||||
|
// Letters
|
||||||
|
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||||
|
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||||
|
|
||||||
|
// Arrows
|
||||||
|
ArrowUp, ArrowDown, ArrowLeft, ArrowRight,
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
Escape, Enter, Space, Tab, Backspace,
|
||||||
|
Insert, Delete, Home, End, PageUp, PageDown,
|
||||||
|
|
||||||
|
// Modifier keys
|
||||||
|
Shift, Control, Alt, Super,
|
||||||
|
|
||||||
|
// Function keys
|
||||||
|
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
||||||
|
|
||||||
|
// Numpad
|
||||||
|
Numpad0, Numpad1, Numpad2, Numpad3, Numpad4,
|
||||||
|
Numpad5, Numpad6, Numpad7, Numpad8, Numpad9,
|
||||||
|
NumpadAdd, NumpadSubtract, NumpadMultiply, NumpadDivide, NumpadEnter,
|
||||||
|
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct KeyModifiers {
|
||||||
|
pub lshift: bool,
|
||||||
|
pub rshift: bool,
|
||||||
|
pub lcontrol: bool,
|
||||||
|
pub rcontrol: bool,
|
||||||
|
pub lalt: bool,
|
||||||
|
pub ralt: bool,
|
||||||
|
pub rsuper_key: bool,
|
||||||
|
pub lsuper_key: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum MouseButton {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Middle,
|
||||||
|
Other(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum InputEvent {
|
||||||
|
KeyPressed {
|
||||||
|
key: Key,
|
||||||
|
modifiers: KeyModifiers,
|
||||||
|
text: String,
|
||||||
|
},
|
||||||
|
KeyReleased {
|
||||||
|
key: Key,
|
||||||
|
modifiers: KeyModifiers,
|
||||||
|
},
|
||||||
|
MouseMoved {
|
||||||
|
position: (f64, f64),
|
||||||
|
},
|
||||||
|
MouseDragged {
|
||||||
|
delta: Vector2<f64>,
|
||||||
|
button: MouseButton,
|
||||||
|
},
|
||||||
|
MouseWheel {
|
||||||
|
delta: f32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InputTracker {
|
||||||
|
pub last_cursor_pos: Option<(f64, f64)>,
|
||||||
|
pub dragging_button: Option<MouseButton>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputTracker {
|
||||||
|
pub fn handle_window_event(&mut self, event: &WindowEvent, modifiers: Modifiers) -> Option<InputEvent> {
|
||||||
|
match event {
|
||||||
|
WindowEvent::KeyboardInput { event, .. } => {
|
||||||
|
Some(handle_keyboard_input(event, modifiers))
|
||||||
|
}
|
||||||
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
|
if let (Some(last), Some(button)) = (self.last_cursor_pos, self.dragging_button) {
|
||||||
|
let delta = Vector2::new(position.x - last.0, position.y - last.1);
|
||||||
|
self.last_cursor_pos = Some((position.x, position.y));
|
||||||
|
Some(InputEvent::MouseDragged { delta, button })
|
||||||
|
} else {
|
||||||
|
self.last_cursor_pos = Some((position.x, position.y));
|
||||||
|
Some(InputEvent::MouseMoved {
|
||||||
|
position: (position.x, position.y),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WindowEvent::MouseInput { state, button, .. } => {
|
||||||
|
let mapped = map_button(*button);
|
||||||
|
match state {
|
||||||
|
ElementState::Pressed => {
|
||||||
|
self.dragging_button = Some(mapped);
|
||||||
|
}
|
||||||
|
ElementState::Released => {
|
||||||
|
self.dragging_button = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
WindowEvent::MouseWheel { delta, .. } => {
|
||||||
|
let scroll = match delta {
|
||||||
|
MouseScrollDelta::LineDelta(_, y) => *y,
|
||||||
|
MouseScrollDelta::PixelDelta(p) => p.y as f32,
|
||||||
|
};
|
||||||
|
Some(InputEvent::MouseWheel { delta: scroll })
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_keyboard_input(event: &KeyEvent, modifiers: Modifiers) -> InputEvent {
|
||||||
|
let key = map_winit_key(&event.logical_key);
|
||||||
|
let mods = KeyModifiers {
|
||||||
|
lshift: modifiers.lshift_state() == ModifiersKeyState::Pressed,
|
||||||
|
rshift: modifiers.rshift_state() == ModifiersKeyState::Pressed,
|
||||||
|
lcontrol: modifiers.lcontrol_state() == ModifiersKeyState::Pressed,
|
||||||
|
rcontrol: modifiers.rcontrol_state() == ModifiersKeyState::Pressed,
|
||||||
|
lalt: modifiers.lalt_state() == ModifiersKeyState::Pressed,
|
||||||
|
ralt: modifiers.ralt_state() == ModifiersKeyState::Pressed,
|
||||||
|
rsuper_key: modifiers.rsuper_state() == ModifiersKeyState::Pressed,
|
||||||
|
lsuper_key: modifiers.lsuper_state() == ModifiersKeyState::Pressed,
|
||||||
|
};
|
||||||
|
|
||||||
|
match event.state {
|
||||||
|
ElementState::Pressed => InputEvent::KeyPressed {
|
||||||
|
key,
|
||||||
|
modifiers: mods,
|
||||||
|
text: event.text.clone().unwrap_or_default().into(),
|
||||||
|
},
|
||||||
|
ElementState::Released => InputEvent::KeyReleased {
|
||||||
|
key,
|
||||||
|
modifiers: mods,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_button(button: winit::event::MouseButton) -> MouseButton {
|
||||||
|
match button {
|
||||||
|
winit::event::MouseButton::Left => MouseButton::Left,
|
||||||
|
winit::event::MouseButton::Right => MouseButton::Right,
|
||||||
|
winit::event::MouseButton::Middle => MouseButton::Middle,
|
||||||
|
winit::event::MouseButton::Other(n) => MouseButton::Other(n),
|
||||||
|
_ => MouseButton::Other(0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn map_winit_key(key: &WinitKey) -> Key {
|
||||||
|
use Key::*;
|
||||||
|
use winit::keyboard::NamedKey;
|
||||||
|
match key {
|
||||||
|
WinitKey::Character(s) => match s.as_str() {
|
||||||
|
"a" | "A" => A,
|
||||||
|
"b" | "B" => B,
|
||||||
|
"c" | "C" => C,
|
||||||
|
"d" | "D" => D,
|
||||||
|
"e" | "E" => E,
|
||||||
|
"f" | "F" => F,
|
||||||
|
"g" | "G" => G,
|
||||||
|
"h" | "H" => H,
|
||||||
|
"i" | "I" => I,
|
||||||
|
"j" | "J" => J,
|
||||||
|
"k" | "K" => K,
|
||||||
|
"l" | "L" => L,
|
||||||
|
"m" | "M" => M,
|
||||||
|
"n" | "N" => N,
|
||||||
|
"o" | "O" => O,
|
||||||
|
"p" | "P" => P,
|
||||||
|
"q" | "Q" => Q,
|
||||||
|
"r" | "R" => R,
|
||||||
|
"s" | "S" => S,
|
||||||
|
"t" | "T" => T,
|
||||||
|
"u" | "U" => U,
|
||||||
|
"v" | "V" => V,
|
||||||
|
"w" | "W" => W,
|
||||||
|
"x" | "X" => X,
|
||||||
|
"y" | "Y" => Y,
|
||||||
|
"z" | "Z" => Z,
|
||||||
|
"0" => Num0,
|
||||||
|
"1" => Num1,
|
||||||
|
"2" => Num2,
|
||||||
|
"3" => Num3,
|
||||||
|
"4" => Num4,
|
||||||
|
"5" => Num5,
|
||||||
|
"6" => Num6,
|
||||||
|
"7" => Num7,
|
||||||
|
"8" => Num8,
|
||||||
|
"9" => Num9,
|
||||||
|
"," => Comma,
|
||||||
|
"." => Period,
|
||||||
|
"-" => Minus,
|
||||||
|
"+" => Plus,
|
||||||
|
"/" => Slash,
|
||||||
|
"\\" => Backslash,
|
||||||
|
";" => Semicolon,
|
||||||
|
"'" => Apostrophe,
|
||||||
|
"[" => LeftBracket,
|
||||||
|
"]" => RightBracket,
|
||||||
|
"`" => Grave,
|
||||||
|
"=" => Equal,
|
||||||
|
_ => Unknown,
|
||||||
|
},
|
||||||
|
WinitKey::Named(named_key) => match named_key {
|
||||||
|
NamedKey::ArrowUp => ArrowUp,
|
||||||
|
NamedKey::ArrowDown => ArrowDown,
|
||||||
|
NamedKey::ArrowLeft => ArrowLeft,
|
||||||
|
NamedKey::ArrowRight => ArrowRight,
|
||||||
|
NamedKey::Escape => Escape,
|
||||||
|
NamedKey::Enter => Enter,
|
||||||
|
NamedKey::Tab => Tab,
|
||||||
|
NamedKey::Space => Space,
|
||||||
|
NamedKey::Backspace => Backspace,
|
||||||
|
NamedKey::Insert => Insert,
|
||||||
|
NamedKey::Delete => Delete,
|
||||||
|
NamedKey::Home => Home,
|
||||||
|
NamedKey::End => End,
|
||||||
|
NamedKey::PageUp => PageUp,
|
||||||
|
NamedKey::PageDown => PageDown,
|
||||||
|
NamedKey::Shift => Shift,
|
||||||
|
NamedKey::Control => Control,
|
||||||
|
NamedKey::Alt => Alt,
|
||||||
|
NamedKey::Super => Super,
|
||||||
|
NamedKey::F1 => F1,
|
||||||
|
NamedKey::F2 => F2,
|
||||||
|
NamedKey::F3 => F3,
|
||||||
|
NamedKey::F4 => F4,
|
||||||
|
NamedKey::F5 => F5,
|
||||||
|
NamedKey::F6 => F6,
|
||||||
|
NamedKey::F7 => F7,
|
||||||
|
NamedKey::F8 => F8,
|
||||||
|
NamedKey::F9 => F9,
|
||||||
|
NamedKey::F10 => F10,
|
||||||
|
NamedKey::F11 => F11,
|
||||||
|
NamedKey::F12 => F12,
|
||||||
|
_ => Unknown,
|
||||||
|
},
|
||||||
|
_ => Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
66
solar_engine/src/instance_manager.rs
Normal file
66
solar_engine/src/instance_manager.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use wgpu::{Buffer, Device, Queue};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use std::mem::size_of;
|
||||||
|
use crate::geometry_manager::Shape;
|
||||||
|
use crate::renderer::{InstanceRaw, RenderInstance};
|
||||||
|
|
||||||
|
pub struct InstanceManager {
|
||||||
|
instances: Vec<RenderInstance>,
|
||||||
|
raw: Vec<InstanceRaw>,
|
||||||
|
buffer: Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceManager {
|
||||||
|
pub fn new(device: &Device) -> Self {
|
||||||
|
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Instance Buffer (empty)"),
|
||||||
|
size: 1,
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
instances: Vec::new(),
|
||||||
|
raw: Vec::new(),
|
||||||
|
buffer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_instances(&mut self, device: &Device, queue: &Queue, instances: Vec<RenderInstance>) {
|
||||||
|
self.raw = instances.iter().map(RenderInstance::to_raw).collect();
|
||||||
|
let byte_len = (self.raw.len() * size_of::<InstanceRaw>()) as wgpu::BufferAddress;
|
||||||
|
|
||||||
|
if byte_len > self.buffer.size() {
|
||||||
|
self.buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Instance Buffer (resized)"),
|
||||||
|
contents: bytemuck::cast_slice(&self.raw),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&self.raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.instances = instances;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn raw_instances_for_shape(&self, shape: Shape) -> Vec<&InstanceRaw> {
|
||||||
|
self.instances
|
||||||
|
.iter()
|
||||||
|
.zip(self.raw.iter())
|
||||||
|
.filter(|(inst, _)| inst.shape == shape)
|
||||||
|
.map(|(_, raw)| raw)
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn buffer(&self) -> &Buffer {
|
||||||
|
&self.buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.instances.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.instances.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
33
solar_engine/src/lib.rs
Normal file
33
solar_engine/src/lib.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
mod body;
|
||||||
|
mod simulator;
|
||||||
|
mod state;
|
||||||
|
mod application;
|
||||||
|
mod input;
|
||||||
|
mod camera;
|
||||||
|
mod light;
|
||||||
|
mod renderer;
|
||||||
|
mod device_manager;
|
||||||
|
mod instance_manager;
|
||||||
|
mod globals;
|
||||||
|
mod geometry_manager;
|
||||||
|
|
||||||
|
pub use body::Body;
|
||||||
|
|
||||||
|
pub use simulator::Simulator;
|
||||||
|
pub use simulator::distance_squared;
|
||||||
|
|
||||||
|
pub use application::StateApplication as Application;
|
||||||
|
|
||||||
|
pub use state::State;
|
||||||
|
|
||||||
|
pub use renderer::RenderInstance;
|
||||||
|
|
||||||
|
pub use light::Light;
|
||||||
|
pub use light::LightType;
|
||||||
|
|
||||||
|
pub use geometry_manager::Shape;
|
||||||
|
|
||||||
|
pub use input::Key;
|
||||||
|
pub use input::map_winit_key;
|
||||||
|
pub use input::InputEvent;
|
||||||
|
pub use input::MouseButton;
|
||||||
360
solar_engine/src/light.rs
Normal file
360
solar_engine/src/light.rs
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use cgmath::{EuclideanSpace, Matrix4, Point3, Transform, Vector3, Zero};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
#[repr(u32)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum LightType {
|
||||||
|
Directional = 0,
|
||||||
|
Point = 1,
|
||||||
|
Spot = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CLUSTER_COUNT_X: usize = 16;
|
||||||
|
pub const CLUSTER_COUNT_Y: usize = 9;
|
||||||
|
pub const CLUSTER_COUNT_Z: usize = 24;
|
||||||
|
pub const MAX_LIGHTS_PER_CLUSTER: usize = 32;
|
||||||
|
pub const TOTAL_CLUSTERS: usize = CLUSTER_COUNT_X * CLUSTER_COUNT_Y * CLUSTER_COUNT_Z;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Pod, Zeroable, Debug)]
|
||||||
|
pub struct GpuLight {
|
||||||
|
pub position: [f32; 3],
|
||||||
|
pub light_type: u32,
|
||||||
|
pub color: [f32; 3],
|
||||||
|
pub intensity: f32,
|
||||||
|
pub direction: [f32; 3],
|
||||||
|
pub range: f32,
|
||||||
|
pub inner_cutoff: f32,
|
||||||
|
pub outer_cutoff: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Light {
|
||||||
|
pub light_type: LightType,
|
||||||
|
pub position: Vector3<f32>,
|
||||||
|
pub direction: Vector3<f32>,
|
||||||
|
pub color: Vector3<f32>,
|
||||||
|
pub intensity: f32,
|
||||||
|
pub range: f32,
|
||||||
|
pub inner_cutoff: f32,
|
||||||
|
pub outer_cutoff: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Light {
|
||||||
|
pub fn to_gpu(&self) -> GpuLight {
|
||||||
|
GpuLight {
|
||||||
|
position: self.position.into(),
|
||||||
|
light_type: self.light_type as u32,
|
||||||
|
color: self.color.into(),
|
||||||
|
intensity: self.intensity,
|
||||||
|
direction: self.direction.into(),
|
||||||
|
range: self.range,
|
||||||
|
inner_cutoff: self.inner_cutoff,
|
||||||
|
outer_cutoff: self.outer_cutoff,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_point(position: Vector3<f32>, color: Vector3<f32>, intensity: f32, min_attenuation: f32) -> Self {
|
||||||
|
let range = (intensity / min_attenuation).sqrt().max(1.0);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
light_type: LightType::Point,
|
||||||
|
position,
|
||||||
|
direction: Vector3::zero(),
|
||||||
|
color,
|
||||||
|
intensity,
|
||||||
|
range,
|
||||||
|
inner_cutoff: 0.0,
|
||||||
|
outer_cutoff: 0.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LightManager {
|
||||||
|
pub lights: Vec<Light>,
|
||||||
|
pub buffer: wgpu::Buffer,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
pub count_buffer: wgpu::Buffer,
|
||||||
|
pub layout: wgpu::BindGroupLayout,
|
||||||
|
pub cluster_buffers: Option<ClusterBuffers>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Pod, Zeroable, Debug)]
|
||||||
|
pub struct LightCount {
|
||||||
|
pub count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ClusterBuffers {
|
||||||
|
pub light_indices: wgpu::Buffer,
|
||||||
|
pub offsets: wgpu::Buffer,
|
||||||
|
pub bind_group: wgpu::BindGroup,
|
||||||
|
pub layout: wgpu::BindGroupLayout,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ClusterAssignment {
|
||||||
|
pub cluster_light_indices: Vec<u32>,
|
||||||
|
pub cluster_offsets: Vec<(u32, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightManager {
|
||||||
|
pub fn new(device: &wgpu::Device, max_lights: usize) -> Self {
|
||||||
|
let buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Light Buffer"),
|
||||||
|
size: (max_lights * size_of::<GpuLight>()) as wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let count_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Light Count Buffer"),
|
||||||
|
size: size_of::<LightCount>() as wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("Light Bind Group Layout"),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX_FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("Light Bind Group"),
|
||||||
|
layout: &layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: count_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
lights: Vec::new(),
|
||||||
|
buffer,
|
||||||
|
count_buffer,
|
||||||
|
bind_group,
|
||||||
|
layout,
|
||||||
|
cluster_buffers: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_light(&mut self, light: Light) {
|
||||||
|
self.lights.push(light);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.lights.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_gpu(&self, queue: &wgpu::Queue) {
|
||||||
|
let gpu_lights: Vec<GpuLight> = self.lights.iter().map(|l| l.to_gpu()).collect();
|
||||||
|
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&gpu_lights));
|
||||||
|
|
||||||
|
let count = LightCount {
|
||||||
|
count: self.lights.len() as u32,
|
||||||
|
};
|
||||||
|
queue.write_buffer(&self.count_buffer, 0, bytemuck::bytes_of(&count));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn bind_group(&self) -> &wgpu::BindGroup {
|
||||||
|
&self.bind_group
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn layout(&self) -> &wgpu::BindGroupLayout {
|
||||||
|
&self.layout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_cluster_buffers(
|
||||||
|
&self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
assignment: &ClusterAssignment,
|
||||||
|
) -> ClusterBuffers {
|
||||||
|
let cluster_light_indices = if assignment.cluster_light_indices.is_empty() {
|
||||||
|
vec![0u32]
|
||||||
|
} else {
|
||||||
|
assignment.cluster_light_indices.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let offset_pairs: Vec<[u32; 2]> = if assignment.cluster_offsets.is_empty() {
|
||||||
|
vec![[0, 0]]
|
||||||
|
} else {
|
||||||
|
assignment.cluster_offsets
|
||||||
|
.iter()
|
||||||
|
.map(|&(o, c)| [o, c])
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let light_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Cluster Light Indices"),
|
||||||
|
contents: bytemuck::cast_slice(&cluster_light_indices),
|
||||||
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let offset_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Cluster Offsets"),
|
||||||
|
contents: bytemuck::cast_slice(&offset_pairs),
|
||||||
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("Cluster Bind Group Layout"),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Storage { read_only: true },
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some("Cluster Bind Group"),
|
||||||
|
layout: &layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: light_index_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: offset_buffer.as_entire_binding(),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
ClusterBuffers {
|
||||||
|
light_indices: light_index_buffer,
|
||||||
|
offsets: offset_buffer,
|
||||||
|
layout,
|
||||||
|
bind_group,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_cluster_buffers(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
assignment: &ClusterAssignment,
|
||||||
|
) {
|
||||||
|
let new_index_bytes = assignment.cluster_light_indices.len() * std::mem::size_of::<u32>();
|
||||||
|
let new_offset_bytes = assignment.cluster_offsets.len() * std::mem::size_of::<[u32; 2]>();
|
||||||
|
|
||||||
|
let needs_resize = self.cluster_buffers.as_ref().map(|buffers| {
|
||||||
|
let index_size_ok = new_index_bytes <= buffers.light_indices.size() as usize;
|
||||||
|
let offset_size_ok = new_offset_bytes <= buffers.offsets.size() as usize;
|
||||||
|
!(index_size_ok && offset_size_ok)
|
||||||
|
}).unwrap_or(true);
|
||||||
|
|
||||||
|
if needs_resize {
|
||||||
|
let buffers = self.create_cluster_buffers(device, assignment);
|
||||||
|
self.cluster_buffers = Some(buffers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(buffers) = &self.cluster_buffers {
|
||||||
|
queue.write_buffer(&buffers.light_indices, 0, bytemuck::cast_slice(&assignment.cluster_light_indices));
|
||||||
|
|
||||||
|
let offset_pairs: Vec<[u32; 2]> = assignment
|
||||||
|
.cluster_offsets
|
||||||
|
.iter()
|
||||||
|
.map(|&(o, c)| [o, c])
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
queue.write_buffer(&buffers.offsets, 0, bytemuck::cast_slice(&offset_pairs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn compute_cluster_assignments(
|
||||||
|
&self,
|
||||||
|
view_matrix: Matrix4<f32>,
|
||||||
|
_projection_matrix: Matrix4<f32>,
|
||||||
|
_screen_width: f32,
|
||||||
|
_screen_height: f32,
|
||||||
|
) -> ClusterAssignment {
|
||||||
|
let mut cluster_light_lists = vec![Vec::new(); TOTAL_CLUSTERS];
|
||||||
|
|
||||||
|
let log_near = 0.1f32.log2();
|
||||||
|
let log_far = 1000.0f32.log2();
|
||||||
|
let log_range = log_far - log_near;
|
||||||
|
|
||||||
|
for (i, light) in self.lights.iter().enumerate() {
|
||||||
|
let pos_view = view_matrix.transform_point(Point3::from_vec(light.position));
|
||||||
|
let radius = light.range;
|
||||||
|
|
||||||
|
let z_bounds = [
|
||||||
|
(-pos_view.z - radius).max(0.1).log2(),
|
||||||
|
(-pos_view.z + radius).max(0.1).log2(),
|
||||||
|
];
|
||||||
|
let z_start = ((z_bounds[0].min(z_bounds[1]) - log_near) / log_range * CLUSTER_COUNT_Z as f32).floor() as usize;
|
||||||
|
let z_end = ((z_bounds[0].max(z_bounds[1]) - log_near) / log_range * CLUSTER_COUNT_Z as f32).ceil() as usize;
|
||||||
|
|
||||||
|
for z in z_start.min(CLUSTER_COUNT_Z)..z_end.min(CLUSTER_COUNT_Z) {
|
||||||
|
for y in 0..CLUSTER_COUNT_Y {
|
||||||
|
for x in 0..CLUSTER_COUNT_X {
|
||||||
|
let cluster = x + y * CLUSTER_COUNT_X + z * CLUSTER_COUNT_X * CLUSTER_COUNT_Y;
|
||||||
|
|
||||||
|
if cluster_light_lists[cluster].len() < MAX_LIGHTS_PER_CLUSTER {
|
||||||
|
cluster_light_lists[cluster].push(i as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut cluster_light_indices = Vec::new();
|
||||||
|
let mut cluster_offsets = Vec::with_capacity(TOTAL_CLUSTERS);
|
||||||
|
|
||||||
|
for lights in cluster_light_lists {
|
||||||
|
let offset = cluster_light_indices.len() as u32;
|
||||||
|
let count = lights.len() as u32;
|
||||||
|
cluster_light_indices.extend(lights);
|
||||||
|
cluster_offsets.push((offset, count));
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusterAssignment {
|
||||||
|
cluster_light_indices,
|
||||||
|
cluster_offsets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
298
solar_engine/src/renderer.rs
Normal file
298
solar_engine/src/renderer.rs
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
use crate::camera::Camera;
|
||||||
|
use crate::geometry_manager::{GeometryManager, Shape};
|
||||||
|
use crate::globals::GlobalsManager;
|
||||||
|
use crate::instance_manager::InstanceManager;
|
||||||
|
use crate::light::LightManager;
|
||||||
|
use wgpu::{Device, Queue, SurfaceTexture, TextureView};
|
||||||
|
|
||||||
|
pub struct RenderInstance {
|
||||||
|
pub position: cgmath::Vector3<f32>,
|
||||||
|
pub rotation: cgmath::Quaternion<f32>,
|
||||||
|
pub color: [f32; 3],
|
||||||
|
pub scale: f32,
|
||||||
|
pub shape: Shape,
|
||||||
|
pub always_lit: bool,
|
||||||
|
pub is_transparent: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderInstance {
|
||||||
|
pub fn to_raw(&self) -> InstanceRaw {
|
||||||
|
let model = cgmath::Matrix4::from_translation(self.position)
|
||||||
|
* cgmath::Matrix4::from(self.rotation)
|
||||||
|
* cgmath::Matrix4::from_scale(self.scale);
|
||||||
|
InstanceRaw {
|
||||||
|
model: model.into(),
|
||||||
|
color: self.color,
|
||||||
|
flags: (self.always_lit as u32) | ((self.is_transparent as u32) << 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct InstanceRaw {
|
||||||
|
model: [[f32; 4]; 4],
|
||||||
|
color: [f32; 3],
|
||||||
|
flags: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InstanceRaw {
|
||||||
|
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: size_of::<InstanceRaw>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Instance,
|
||||||
|
attributes: &[
|
||||||
|
wgpu::VertexAttribute { offset: 0, shader_location: 5, format: wgpu::VertexFormat::Float32x4 },
|
||||||
|
wgpu::VertexAttribute { offset: 16, shader_location: 6, format: wgpu::VertexFormat::Float32x4 },
|
||||||
|
wgpu::VertexAttribute { offset: 32, shader_location: 7, format: wgpu::VertexFormat::Float32x4 },
|
||||||
|
wgpu::VertexAttribute { offset: 48, shader_location: 8, format: wgpu::VertexFormat::Float32x4 },
|
||||||
|
wgpu::VertexAttribute { offset: 64, shader_location: 9, format: wgpu::VertexFormat::Float32x3 },
|
||||||
|
wgpu::VertexAttribute { offset: 76, shader_location: 10, format: wgpu::VertexFormat::Uint32 },
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct Vertex {
|
||||||
|
pub(crate) position: [f32; 3],
|
||||||
|
pub(crate) color: [f32; 3],
|
||||||
|
pub(crate) normal: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vertex {
|
||||||
|
const ATTRIBS: [wgpu::VertexAttribute; 3] =
|
||||||
|
wgpu::vertex_attr_array![0 => Float32x3, 1 => Float32x3, 2 => Float32x3];
|
||||||
|
|
||||||
|
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: size_of::<Self>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &Self::ATTRIBS,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Renderer {
|
||||||
|
pipeline: wgpu::RenderPipeline,
|
||||||
|
depth_texture: TextureView,
|
||||||
|
sample_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Renderer {
|
||||||
|
pub fn new(
|
||||||
|
device: &Device,
|
||||||
|
config: &wgpu::SurfaceConfiguration,
|
||||||
|
global_layout: &wgpu::BindGroupLayout,
|
||||||
|
light_manager: &mut LightManager,
|
||||||
|
camera: &Camera,
|
||||||
|
sample_count: u32,
|
||||||
|
) -> Self {
|
||||||
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
|
label: Some("Shader"),
|
||||||
|
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
|
||||||
|
});
|
||||||
|
|
||||||
|
let cluster_assignment = light_manager.compute_cluster_assignments(
|
||||||
|
camera.build_view_matrix(),
|
||||||
|
camera.build_view_projection_matrix(),
|
||||||
|
config.width as f32,
|
||||||
|
config.height as f32,
|
||||||
|
);
|
||||||
|
|
||||||
|
let cluster_buffers = light_manager.create_cluster_buffers(device, &cluster_assignment);
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("Render Pipeline Layout"),
|
||||||
|
bind_group_layouts: &[
|
||||||
|
global_layout,
|
||||||
|
&light_manager.layout,
|
||||||
|
&cluster_buffers.layout,
|
||||||
|
],
|
||||||
|
push_constant_ranges: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("Render Pipeline"),
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: Some("vs_main"),
|
||||||
|
buffers: &[Vertex::desc(), InstanceRaw::desc()],
|
||||||
|
compilation_options: Default::default(),
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: Some("fs_main"),
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
format: config.format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
compilation_options: Default::default(),
|
||||||
|
}),
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
unclipped_depth: false,
|
||||||
|
conservative: false,
|
||||||
|
},
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
depth_write_enabled: true,
|
||||||
|
depth_compare: wgpu::CompareFunction::Less,
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
count: sample_count,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
multiview: None,
|
||||||
|
cache: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let depth_texture =
|
||||||
|
Self::create_depth_texture(device, config.width, config.height, sample_count);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
pipeline,
|
||||||
|
depth_texture,
|
||||||
|
sample_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, device: &Device, width: u32, height: u32) {
|
||||||
|
self.depth_texture = Self::create_depth_texture(device, width, height, self.sample_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_depth_texture(
|
||||||
|
device: &Device,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
sample_count: u32,
|
||||||
|
) -> TextureView {
|
||||||
|
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: Some("Depth Texture"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
texture.create_view(&Default::default())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_frame(
|
||||||
|
&mut self,
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
output: SurfaceTexture,
|
||||||
|
view: &TextureView,
|
||||||
|
surface_format: wgpu::TextureFormat,
|
||||||
|
globals: &mut GlobalsManager,
|
||||||
|
camera: &Camera,
|
||||||
|
light_manager: &mut LightManager,
|
||||||
|
geometry: &GeometryManager,
|
||||||
|
instances: &InstanceManager,
|
||||||
|
) -> Result<(), wgpu::SurfaceError> {
|
||||||
|
// Update uniform buffer
|
||||||
|
globals.update(queue, camera);
|
||||||
|
|
||||||
|
// Update cluster buffers
|
||||||
|
let assignment = light_manager.compute_cluster_assignments(
|
||||||
|
camera.build_view_matrix(),
|
||||||
|
camera.build_view_projection_matrix(),
|
||||||
|
globals.resolution()[0],
|
||||||
|
globals.resolution()[1],
|
||||||
|
);
|
||||||
|
light_manager.update_cluster_buffers(device, queue, &assignment);
|
||||||
|
light_manager.update_gpu(queue);
|
||||||
|
|
||||||
|
let multisampled_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: Some("Multisample Target"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: globals.resolution()[0] as u32,
|
||||||
|
height: globals.resolution()[1] as u32,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: self.sample_count,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: surface_format,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let multisampled_view = multisampled_texture.create_view(&Default::default());
|
||||||
|
|
||||||
|
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||||
|
label: Some("Render Encoder"),
|
||||||
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("Main Render Pass"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view: &multisampled_view,
|
||||||
|
resolve_target: Some(view),
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.1,
|
||||||
|
g: 0.1,
|
||||||
|
b: 0.2,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
||||||
|
view: &self.depth_texture,
|
||||||
|
depth_ops: Some(wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(1.0),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
}),
|
||||||
|
stencil_ops: None,
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
pass.set_pipeline(&self.pipeline);
|
||||||
|
pass.set_bind_group(0, &globals.bind_group, &[]);
|
||||||
|
pass.set_bind_group(1, &light_manager.bind_group, &[]);
|
||||||
|
|
||||||
|
if let Some(clusters) = &light_manager.cluster_buffers {
|
||||||
|
pass.set_bind_group(2, &clusters.bind_group, &[]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for shape in geometry.shapes() {
|
||||||
|
if let Some(mesh) = geometry.get(&shape) {
|
||||||
|
let relevant = instances.raw_instances_for_shape(shape);
|
||||||
|
if relevant.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
||||||
|
pass.set_vertex_buffer(1, instances.buffer().slice(..));
|
||||||
|
pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
|
||||||
|
pass.draw_indexed(0..mesh.index_count, 0, 0..relevant.len() as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue.submit(Some(encoder.finish()));
|
||||||
|
output.present();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
168
solar_engine/src/shader.wgsl
Normal file
168
solar_engine/src/shader.wgsl
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
struct VertexInput {
|
||||||
|
@location(0) position: vec3<f32>,
|
||||||
|
@location(1) color: vec3<f32>,
|
||||||
|
@location(2) normal: vec3<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
|
const CLUSTER_COUNT_X: u32 = 16u;
|
||||||
|
const CLUSTER_COUNT_Y: u32 = 9u;
|
||||||
|
const CLUSTER_COUNT_Z: u32 = 24u;
|
||||||
|
|
||||||
|
const NEAR_PLANE: f32 = 0.1;
|
||||||
|
const FAR_PLANE: f32 = 1000.0;
|
||||||
|
|
||||||
|
struct InstanceInput {
|
||||||
|
@location(5) model_row0: vec4<f32>,
|
||||||
|
@location(6) model_row1: vec4<f32>,
|
||||||
|
@location(7) model_row2: vec4<f32>,
|
||||||
|
@location(8) model_row3: vec4<f32>,
|
||||||
|
@location(9) color: vec3<f32>,
|
||||||
|
@location(10) flags: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct VSOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) frag_color: vec3<f32>,
|
||||||
|
@location(1) world_pos: vec3<f32>,
|
||||||
|
@location(2) normal: vec3<f32>,
|
||||||
|
@location(3) flags: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LightCount {
|
||||||
|
count: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(1) @binding(1)
|
||||||
|
var<uniform> light_count: LightCount;
|
||||||
|
|
||||||
|
struct Globals {
|
||||||
|
view_proj: mat4x4<f32>,
|
||||||
|
resolution: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var<uniform> globals: Globals;
|
||||||
|
|
||||||
|
struct GpuLight {
|
||||||
|
position: vec3<f32>,
|
||||||
|
light_type: u32,
|
||||||
|
color: vec3<f32>,
|
||||||
|
intensity: f32,
|
||||||
|
direction: vec3<f32>,
|
||||||
|
range: f32,
|
||||||
|
inner_cutoff: f32,
|
||||||
|
outer_cutoff: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<storage, read> all_lights: array<GpuLight>;
|
||||||
|
|
||||||
|
@group(2) @binding(0)
|
||||||
|
var<storage, read> cluster_light_indices: array<u32>;
|
||||||
|
@group(2) @binding(1)
|
||||||
|
var<storage, read> cluster_offsets: array<vec2<u32>>;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(vertex: VertexInput, instance: InstanceInput) -> VSOutput {
|
||||||
|
var out: VSOutput;
|
||||||
|
|
||||||
|
let model = mat4x4<f32>(
|
||||||
|
instance.model_row0,
|
||||||
|
instance.model_row1,
|
||||||
|
instance.model_row2,
|
||||||
|
instance.model_row3
|
||||||
|
);
|
||||||
|
|
||||||
|
let world_position = (model * vec4<f32>(vertex.position, 1.0)).xyz;
|
||||||
|
let normal_matrix = mat3x3<f32>(
|
||||||
|
instance.model_row0.xyz,
|
||||||
|
instance.model_row1.xyz,
|
||||||
|
instance.model_row2.xyz
|
||||||
|
);
|
||||||
|
|
||||||
|
out.position = globals.view_proj * vec4<f32>(world_position, 1.0);
|
||||||
|
out.frag_color = instance.color * vertex.color;
|
||||||
|
out.world_pos = world_position;
|
||||||
|
out.normal = normalize(normal_matrix * vertex.normal);
|
||||||
|
out.flags = instance.flags;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_cluster_id(frag_coord: vec4<f32>, view_pos_z: f32, screen_size: vec2<f32>) -> u32 {
|
||||||
|
let x_frac = frag_coord.x / screen_size.x;
|
||||||
|
let y_frac = frag_coord.y / screen_size.y;
|
||||||
|
|
||||||
|
let x = clamp(u32(x_frac * f32(CLUSTER_COUNT_X)), 0u, CLUSTER_COUNT_X - 1u);
|
||||||
|
let y = clamp(u32(y_frac * f32(CLUSTER_COUNT_Y)), 0u, CLUSTER_COUNT_Y - 1u);
|
||||||
|
|
||||||
|
// Z: logarithmic depth
|
||||||
|
let depth = -view_pos_z; // view-space z is negative
|
||||||
|
let depth_clamped = clamp(depth, NEAR_PLANE, FAR_PLANE);
|
||||||
|
let log_depth = log2(depth_clamped);
|
||||||
|
let z = clamp(u32((log_depth / log2(FAR_PLANE / NEAR_PLANE)) * f32(CLUSTER_COUNT_Z)), 0u, CLUSTER_COUNT_Z - 1u);
|
||||||
|
|
||||||
|
return x + y * CLUSTER_COUNT_X + z * CLUSTER_COUNT_X * CLUSTER_COUNT_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_nan_f32(x: f32) -> bool {
|
||||||
|
return x != x;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_nan_vec3(v: vec3<f32>) -> bool {
|
||||||
|
return any(vec3<bool>(v != v));
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(input: VSOutput) -> @location(0) vec4<f32> {
|
||||||
|
var lighting: vec3<f32> = vec3<f32>(0.0);
|
||||||
|
let always_lit = (input.flags & 0x1u) != 0u;
|
||||||
|
|
||||||
|
let cluster_id = compute_cluster_id(input.position, input.world_pos.z, globals.resolution);
|
||||||
|
let offset_info = cluster_offsets[cluster_id];
|
||||||
|
let offset = offset_info.x;
|
||||||
|
let count = offset_info.y;
|
||||||
|
|
||||||
|
for (var i = 0u; i < count; i = i + 1u) {
|
||||||
|
let light_index = cluster_light_indices[offset + i];
|
||||||
|
let light = all_lights[light_index];
|
||||||
|
var light_contrib: vec3<f32> = vec3<f32>(0.0);
|
||||||
|
|
||||||
|
let light_dir = normalize(light.position - input.world_pos);
|
||||||
|
let diff = max(dot(input.normal, light_dir), 0.0);
|
||||||
|
|
||||||
|
switch (light.light_type) {
|
||||||
|
case 0u: { // Directional
|
||||||
|
light_contrib = light.color * light.intensity * diff;
|
||||||
|
}
|
||||||
|
case 1u: { // Point
|
||||||
|
let dist = distance(light.position, input.world_pos);
|
||||||
|
if (dist < light.range) {
|
||||||
|
let attenuation = 1.0 / (dist * dist);
|
||||||
|
light_contrib = light.color * light.intensity * diff * attenuation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2u: { // Spot
|
||||||
|
let spot_dir = normalize(-light.direction);
|
||||||
|
let angle = dot(spot_dir, light_dir);
|
||||||
|
if (angle > light.outer_cutoff) {
|
||||||
|
let intensity = clamp((angle - light.outer_cutoff) / (light.inner_cutoff - light.outer_cutoff), 0.0, 1.0);
|
||||||
|
let dist = distance(light.position, input.world_pos);
|
||||||
|
let attenuation = 1.0 / (dist * dist);
|
||||||
|
light_contrib = light.color * light.intensity * diff * attenuation * intensity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default: {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!always_lit) {
|
||||||
|
lighting += light_contrib;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (always_lit) {
|
||||||
|
lighting = vec3<f32>(1.0, 1.0, 1.0) * 2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return vec4<f32>(input.frag_color * lighting, 1.0);
|
||||||
|
}
|
||||||
174
solar_engine/src/simulator.rs
Normal file
174
solar_engine/src/simulator.rs
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
use cgmath::{InnerSpace, Vector3};
|
||||||
|
use crate::body::Body;
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
const G: f64 = 6.67430e-11;
|
||||||
|
|
||||||
|
pub struct Simulator {
|
||||||
|
pub bodies: Vec<Body>,
|
||||||
|
pub time: f64,
|
||||||
|
timewarp: u32
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn distance_squared(a: Vector3<f64>, b: Vector3<f64>) -> f64 {
|
||||||
|
let d = a - b;
|
||||||
|
d.magnitude2()
|
||||||
|
}
|
||||||
|
|
||||||
|
const MAX_TIMEWARP: u32 = 536870912;
|
||||||
|
|
||||||
|
impl Simulator {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
bodies: Vec::new(),
|
||||||
|
time: 0.0,
|
||||||
|
timewarp: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_body(&mut self, body: Body) {
|
||||||
|
self.bodies.push(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(&mut self, dt: f64) {
|
||||||
|
let n = self.bodies.len();
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct State {
|
||||||
|
position: Vector3<f64>,
|
||||||
|
velocity: Vector3<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let original_states: Vec<State> = self
|
||||||
|
.bodies
|
||||||
|
.iter()
|
||||||
|
.map(|b| State {
|
||||||
|
position: b.position,
|
||||||
|
velocity: b.velocity,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let masses: Vec<f64> = self.bodies.iter().map(|b| b.mass).collect();
|
||||||
|
|
||||||
|
fn compute_accelerations(states: &[State], masses: &[f64], ownership: &HashMap<usize, usize>) -> Vec<Vector3<f64>> {
|
||||||
|
let mut accels = vec![Vector3::new(0.0, 0.0, 0.0); states.len()];
|
||||||
|
|
||||||
|
for (&i, &j) in ownership {
|
||||||
|
let r = states[j].position - states[i].position;
|
||||||
|
let dist_sq = r.magnitude2();
|
||||||
|
let dist = dist_sq.sqrt();
|
||||||
|
|
||||||
|
if dist < 1e-8 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let force = G * masses[i] * masses[j] / dist_sq;
|
||||||
|
let accel = force * r / (dist * masses[i]);
|
||||||
|
|
||||||
|
accels[i] += accel;
|
||||||
|
}
|
||||||
|
|
||||||
|
accels
|
||||||
|
}
|
||||||
|
|
||||||
|
let ownership = self.compute_soi_owners();
|
||||||
|
let k1_pos = original_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||||
|
let k1_vel = compute_accelerations(&original_states, &masses, &ownership);
|
||||||
|
|
||||||
|
let mut temp_states: Vec<State> = original_states.iter().enumerate().map(|(i, s)| {
|
||||||
|
State {
|
||||||
|
position: s.position + k1_pos[i] * (dt / 2.0),
|
||||||
|
velocity: s.velocity + k1_vel[i] * (dt / 2.0),
|
||||||
|
}
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let k2_pos = temp_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||||
|
let k2_vel = compute_accelerations(&temp_states, &masses, &ownership);
|
||||||
|
|
||||||
|
for i in 0..n {
|
||||||
|
temp_states[i].position = original_states[i].position + k2_pos[i] * (dt / 2.0);
|
||||||
|
temp_states[i].velocity = original_states[i].velocity + k2_vel[i] * (dt / 2.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let k3_pos = temp_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||||
|
let k3_vel = compute_accelerations(&temp_states, &masses, &ownership);
|
||||||
|
|
||||||
|
for i in 0..n {
|
||||||
|
temp_states[i].position = original_states[i].position + k3_pos[i] * dt;
|
||||||
|
temp_states[i].velocity = original_states[i].velocity + k3_vel[i] * dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
let k4_pos = temp_states.iter().map(|s| s.velocity).collect::<Vec<_>>();
|
||||||
|
let k4_vel = compute_accelerations(&temp_states, &masses, &ownership);
|
||||||
|
|
||||||
|
for i in 0..n {
|
||||||
|
let body = &mut self.bodies[i];
|
||||||
|
|
||||||
|
body.position += (k1_pos[i] + 2.0 * k2_pos[i] + 2.0 * k3_pos[i] + k4_pos[i]) * (dt / 6.0);
|
||||||
|
body.velocity += (k1_vel[i] + 2.0 * k2_vel[i] + 2.0 * k3_vel[i] + k4_vel[i]) * (dt / 6.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.time += dt;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_soi_owners(&self) -> HashMap<usize, usize> {
|
||||||
|
let mut ownership = HashMap::new();
|
||||||
|
for (i, body) in self.bodies.iter().enumerate() {
|
||||||
|
let mut min_distance = f64::MAX;
|
||||||
|
let mut dominant_index = None;
|
||||||
|
|
||||||
|
for (j, other) in self.bodies.iter().enumerate() {
|
||||||
|
if i == j {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let r = (body.position - other.position).magnitude();
|
||||||
|
let soi_radius = r * (body.mass / other.mass).powf(2.0 / 5.0);
|
||||||
|
|
||||||
|
if r < soi_radius && r < min_distance {
|
||||||
|
min_distance = r;
|
||||||
|
dominant_index = Some(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(j) = dominant_index {
|
||||||
|
ownership.insert(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ownership
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn increase_timewarp(&mut self) {
|
||||||
|
if let Some(new) = self.timewarp.checked_mul(2) {
|
||||||
|
if new <= MAX_TIMEWARP {
|
||||||
|
self.timewarp = new;
|
||||||
|
} else {
|
||||||
|
println!("Timewarp is already at maximum ({}).", MAX_TIMEWARP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Timewarp multiplication would overflow.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrease_timewarp(&mut self) {
|
||||||
|
if let Some(new) = self.timewarp.checked_div(2) {
|
||||||
|
if new >= 1 {
|
||||||
|
self.timewarp = new;
|
||||||
|
} else {
|
||||||
|
println!("Timewarp is already at minimum.");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("Timewarp is already at minimum.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reset_timewarp(&mut self) {
|
||||||
|
self.timewarp = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_timewarp(&self) -> u32 {
|
||||||
|
self.timewarp
|
||||||
|
}
|
||||||
|
}
|
||||||
215
solar_engine/src/state.rs
Normal file
215
solar_engine/src/state.rs
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
use std::sync::{Arc};
|
||||||
|
use log::info;
|
||||||
|
use pollster::FutureExt;
|
||||||
|
use wgpu::{Adapter, Device, Instance, PresentMode, Queue, Surface, SurfaceCapabilities, SurfaceConfiguration, SurfaceError};
|
||||||
|
use winit::dpi::PhysicalSize;
|
||||||
|
use winit::window::{Window};
|
||||||
|
use crate::camera::Camera;
|
||||||
|
use crate::geometry_manager::GeometryManager;
|
||||||
|
use crate::globals::GlobalsManager;
|
||||||
|
use crate::instance_manager::InstanceManager;
|
||||||
|
use crate::light::{LightManager};
|
||||||
|
use crate::renderer::{RenderInstance, Renderer};
|
||||||
|
|
||||||
|
pub struct SampleCount(pub u32);
|
||||||
|
|
||||||
|
impl SampleCount {
|
||||||
|
pub fn get(&self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct State<'a> {
|
||||||
|
surface: Surface<'a>,
|
||||||
|
device: Device,
|
||||||
|
queue: Queue,
|
||||||
|
config: SurfaceConfiguration,
|
||||||
|
sample_count: SampleCount,
|
||||||
|
size: PhysicalSize<u32>,
|
||||||
|
|
||||||
|
window: Arc<Window>,
|
||||||
|
pub camera: Camera,
|
||||||
|
|
||||||
|
pub globals: GlobalsManager,
|
||||||
|
pub geometry_manager: GeometryManager,
|
||||||
|
pub instance_manager: InstanceManager,
|
||||||
|
pub light_manager: LightManager,
|
||||||
|
pub renderer: Renderer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> State<'a> {
|
||||||
|
pub(crate) fn new(window: Window) -> Self {
|
||||||
|
let window = Arc::new(window);
|
||||||
|
let size = window.inner_size();
|
||||||
|
|
||||||
|
let instance = Self::create_gpu_instance();
|
||||||
|
|
||||||
|
let surface = instance.create_surface(window.clone()).unwrap();
|
||||||
|
|
||||||
|
let adapter = Self::create_adapter(instance, &surface);
|
||||||
|
|
||||||
|
let (device, queue) = Self::create_device(&adapter);
|
||||||
|
|
||||||
|
let capabilities = surface.get_capabilities(&adapter);
|
||||||
|
let config = Self::create_surface_config(size, capabilities);
|
||||||
|
surface.configure(&device, &config);
|
||||||
|
|
||||||
|
let sample_count = SampleCount(Self::probe_msaa_support(&device, &config));
|
||||||
|
info!("MSAA sample count: {}", sample_count.get());
|
||||||
|
|
||||||
|
let camera = Camera::new(config.width as f32 / config.height as f32);
|
||||||
|
|
||||||
|
let globals = GlobalsManager::new(&device, config.width, config.height, &camera);
|
||||||
|
let geometry_manager = GeometryManager::new(&device);
|
||||||
|
let instance_manager = InstanceManager::new(&device);
|
||||||
|
let mut light_manager = LightManager::new(&device, 100);
|
||||||
|
|
||||||
|
let renderer = Renderer::new(
|
||||||
|
&device,
|
||||||
|
&config,
|
||||||
|
globals.layout(),
|
||||||
|
&mut light_manager,
|
||||||
|
&camera,
|
||||||
|
sample_count.get(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
surface,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
config,
|
||||||
|
sample_count,
|
||||||
|
size,
|
||||||
|
window,
|
||||||
|
camera,
|
||||||
|
globals,
|
||||||
|
geometry_manager,
|
||||||
|
instance_manager,
|
||||||
|
light_manager,
|
||||||
|
renderer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn probe_msaa_support(device: &Device, config: &SurfaceConfiguration) -> u32 {
|
||||||
|
pollster::block_on(async {
|
||||||
|
for &count in &[16, 8, 4, 2] {
|
||||||
|
device.push_error_scope(wgpu::ErrorFilter::Validation);
|
||||||
|
|
||||||
|
let _ = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: Some("MSAA Probe"),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: 4,
|
||||||
|
height: 4,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: count,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: config.format,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
|
||||||
|
if device.pop_error_scope().await.is_none() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
1 // fallback
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_surface_config(size: PhysicalSize<u32>, capabilities: SurfaceCapabilities) -> wgpu::SurfaceConfiguration {
|
||||||
|
let surface_format = capabilities.formats.iter()
|
||||||
|
.find(|f| f.is_srgb())
|
||||||
|
.copied()
|
||||||
|
.unwrap_or(capabilities.formats[0]);
|
||||||
|
|
||||||
|
SurfaceConfiguration {
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
||||||
|
format: surface_format,
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
present_mode: PresentMode::AutoVsync,
|
||||||
|
alpha_mode: capabilities.alpha_modes[0],
|
||||||
|
view_formats: vec![],
|
||||||
|
desired_maximum_frame_latency: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_device(adapter: &Adapter) -> (Device, Queue) {
|
||||||
|
adapter.request_device(
|
||||||
|
&wgpu::DeviceDescriptor {
|
||||||
|
required_features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||||
|
required_limits: wgpu::Limits::default(),
|
||||||
|
memory_hints: Default::default(),
|
||||||
|
label: None,
|
||||||
|
trace: Default::default(),
|
||||||
|
}).block_on().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_adapter(instance: Instance, surface: &Surface) -> Adapter {
|
||||||
|
instance.request_adapter(
|
||||||
|
&wgpu::RequestAdapterOptions {
|
||||||
|
power_preference: wgpu::PowerPreference::default(),
|
||||||
|
compatible_surface: Some(&surface),
|
||||||
|
force_fallback_adapter: false,
|
||||||
|
}
|
||||||
|
).block_on().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_gpu_instance() -> Instance {
|
||||||
|
Instance::new(&wgpu::InstanceDescriptor {
|
||||||
|
backends: wgpu::Backends::PRIMARY,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resize(&mut self, new_size: PhysicalSize<u32>) {
|
||||||
|
if new_size.width > 0 && new_size.height > 0 {
|
||||||
|
self.size = new_size;
|
||||||
|
self.config.width = new_size.width;
|
||||||
|
self.config.height = new_size.height;
|
||||||
|
self.surface.configure(&self.device, &self.config);
|
||||||
|
|
||||||
|
self.camera.set_aspect(new_size.width as f32 / new_size.height as f32);
|
||||||
|
self.globals.resize(new_size.width, new_size.height);
|
||||||
|
self.renderer.resize(&self.device, new_size.width, new_size.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(&mut self) -> Result<(), SurfaceError> {
|
||||||
|
let output = self.surface.get_current_texture()?;
|
||||||
|
let view = output.texture.create_view(&Default::default());
|
||||||
|
|
||||||
|
self.renderer.render_frame(
|
||||||
|
&self.device,
|
||||||
|
&self.queue,
|
||||||
|
output,
|
||||||
|
&view,
|
||||||
|
self.config.format,
|
||||||
|
&mut self.globals,
|
||||||
|
&self.camera,
|
||||||
|
&mut self.light_manager,
|
||||||
|
&self.geometry_manager,
|
||||||
|
&self.instance_manager,
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_instances(&mut self, instances: Vec<RenderInstance>) {
|
||||||
|
self.instance_manager.set_instances(&self.device, &self.queue, instances);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn window(&self) -> &Window {
|
||||||
|
&self.window
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn camera_mut(&mut self) -> &mut crate::camera::Camera {
|
||||||
|
&mut self.camera
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn camera(&self) -> &crate::camera::Camera {
|
||||||
|
&self.camera
|
||||||
|
}
|
||||||
|
}
|
||||||
189
src/camera.rs
189
src/camera.rs
@ -1,189 +0,0 @@
|
|||||||
use cgmath::*;
|
|
||||||
use std::f32::consts::FRAC_PI_2;
|
|
||||||
use std::time::Duration;
|
|
||||||
use winit::dpi::PhysicalPosition;
|
|
||||||
use winit::event::*;
|
|
||||||
use winit::keyboard::KeyCode;
|
|
||||||
|
|
||||||
const SAFE_FRAC_PI_2: f32 = FRAC_PI_2 - 0.0001;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Camera {
|
|
||||||
pub position: Point3<f32>,
|
|
||||||
yaw: Rad<f32>,
|
|
||||||
pitch: Rad<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Camera {
|
|
||||||
pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
|
|
||||||
position: V,
|
|
||||||
yaw: Y,
|
|
||||||
pitch: P,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
position: position.into(),
|
|
||||||
yaw: yaw.into(),
|
|
||||||
pitch: pitch.into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
|
||||||
let (sin_pitch, cos_pitch) = self.pitch.0.sin_cos();
|
|
||||||
let (sin_yaw, cos_yaw) = self.yaw.0.sin_cos();
|
|
||||||
|
|
||||||
Matrix4::look_to_rh(
|
|
||||||
self.position,
|
|
||||||
Vector3::new(cos_pitch * cos_yaw, sin_pitch, cos_pitch * sin_yaw).normalize(),
|
|
||||||
Vector3::unit_y(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Projection {
|
|
||||||
aspect: f32,
|
|
||||||
fovy: Rad<f32>,
|
|
||||||
znear: f32,
|
|
||||||
zfar: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Projection {
|
|
||||||
pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
aspect: width as f32 / height as f32,
|
|
||||||
fovy: fovy.into(),
|
|
||||||
znear,
|
|
||||||
zfar,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn resize(&mut self, width: u32, height: u32) {
|
|
||||||
self.aspect = width as f32 / height as f32;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn calc_matrix(&self) -> Matrix4<f32> {
|
|
||||||
// UDPATE
|
|
||||||
perspective(self.fovy, self.aspect, self.znear, self.zfar)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CameraController {
|
|
||||||
amount_left: f32,
|
|
||||||
amount_right: f32,
|
|
||||||
amount_forward: f32,
|
|
||||||
amount_backward: f32,
|
|
||||||
amount_up: f32,
|
|
||||||
amount_down: f32,
|
|
||||||
rotate_horizontal: f32,
|
|
||||||
rotate_vertical: f32,
|
|
||||||
scroll: f32,
|
|
||||||
speed: f32,
|
|
||||||
sensitivity: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CameraController {
|
|
||||||
pub fn new(speed: f32, sensitivity: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
amount_left: 0.0,
|
|
||||||
amount_right: 0.0,
|
|
||||||
amount_forward: 0.0,
|
|
||||||
amount_backward: 0.0,
|
|
||||||
amount_up: 0.0,
|
|
||||||
amount_down: 0.0,
|
|
||||||
rotate_horizontal: 0.0,
|
|
||||||
rotate_vertical: 0.0,
|
|
||||||
scroll: 0.0,
|
|
||||||
speed,
|
|
||||||
sensitivity,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_keyboard(&mut self, key: KeyCode, state: ElementState) -> bool {
|
|
||||||
let amount = if state == ElementState::Pressed {
|
|
||||||
1.0
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
match key {
|
|
||||||
KeyCode::KeyW | KeyCode::ArrowUp => {
|
|
||||||
self.amount_forward = amount;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
KeyCode::KeyS | KeyCode::ArrowDown => {
|
|
||||||
self.amount_backward = amount;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
KeyCode::KeyA | KeyCode::ArrowLeft => {
|
|
||||||
self.amount_left = amount;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
KeyCode::KeyD | KeyCode::ArrowRight => {
|
|
||||||
self.amount_right = amount;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
KeyCode::ShiftLeft => {
|
|
||||||
self.amount_up = amount;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
KeyCode::ControlLeft => {
|
|
||||||
self.amount_down = amount;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
|
|
||||||
self.rotate_horizontal += mouse_dx as f32 * self.sensitivity;
|
|
||||||
self.rotate_vertical += mouse_dy as f32 * self.sensitivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn process_scroll(&mut self, delta: &MouseScrollDelta) {
|
|
||||||
self.scroll = match delta {
|
|
||||||
MouseScrollDelta::LineDelta(_, scroll) => scroll * 4.0 * self.speed,
|
|
||||||
MouseScrollDelta::PixelDelta(PhysicalPosition { y: scroll, .. }) => *scroll as f32 * self.speed,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) {
|
|
||||||
let dt = dt.as_secs_f32();
|
|
||||||
|
|
||||||
// Move forward/backward and left/right
|
|
||||||
let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
|
|
||||||
let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
|
|
||||||
let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
|
|
||||||
camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
|
|
||||||
camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
|
|
||||||
|
|
||||||
// Move in/out (aka. "zoom")
|
|
||||||
// Note: this isn't an actual zoom. The camera's position
|
|
||||||
// changes when zooming. I've added this to make it easier
|
|
||||||
// to get closer to an object you want to focus on.
|
|
||||||
let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
|
|
||||||
let scrollward =
|
|
||||||
Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
|
|
||||||
camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
|
|
||||||
self.scroll = 0.0;
|
|
||||||
|
|
||||||
// Move up/down. Since we don't use roll, we can just
|
|
||||||
// modify the y coordinate directly.
|
|
||||||
camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;
|
|
||||||
|
|
||||||
// Rotate
|
|
||||||
camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt;
|
|
||||||
camera.pitch += Rad(-self.rotate_vertical) * self.sensitivity * dt;
|
|
||||||
|
|
||||||
// If process_mouse isn't called every frame, these values
|
|
||||||
// will not get set to zero, and the camera will rotate
|
|
||||||
// when moving in a non cardinal direction.
|
|
||||||
self.rotate_horizontal = 0.0;
|
|
||||||
self.rotate_vertical = 0.0;
|
|
||||||
|
|
||||||
// Keep the camera's angle from going too high/low.
|
|
||||||
if camera.pitch < -Rad(SAFE_FRAC_PI_2) {
|
|
||||||
camera.pitch = -Rad(SAFE_FRAC_PI_2);
|
|
||||||
} else if camera.pitch > Rad(SAFE_FRAC_PI_2) {
|
|
||||||
camera.pitch = Rad(SAFE_FRAC_PI_2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
99
src/debug.rs
99
src/debug.rs
@ -1,99 +0,0 @@
|
|||||||
use std::mem::size_of;
|
|
||||||
|
|
||||||
use wgpu::util::{BufferInitDescriptor, DeviceExt};
|
|
||||||
|
|
||||||
use crate::create_render_pipeline;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct PositionColor {
|
|
||||||
position: [f32; 3],
|
|
||||||
color: [f32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
const AXIS_COLORS: &'static [PositionColor] = &[
|
|
||||||
// X
|
|
||||||
PositionColor {
|
|
||||||
position: [0.0, 0.0, 0.0],
|
|
||||||
color: [0.5, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
PositionColor {
|
|
||||||
position: [1.0, 0.0, 0.0],
|
|
||||||
color: [1.0, 0.0, 0.0],
|
|
||||||
},
|
|
||||||
// Y
|
|
||||||
PositionColor {
|
|
||||||
position: [0.0, 0.0, 0.0],
|
|
||||||
color: [0.0, 0.5, 0.0],
|
|
||||||
},
|
|
||||||
PositionColor {
|
|
||||||
position: [0.0, 1.0, 0.0],
|
|
||||||
color: [0.0, 1.0, 0.0],
|
|
||||||
},
|
|
||||||
// Z
|
|
||||||
PositionColor {
|
|
||||||
position: [0.0, 0.0, 0.0],
|
|
||||||
color: [0.0, 0.0, 0.5],
|
|
||||||
},
|
|
||||||
PositionColor {
|
|
||||||
position: [0.0, 0.0, 1.0],
|
|
||||||
color: [0.0, 0.0, 1.0],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const POSITION_COLOR_LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout {
|
|
||||||
array_stride: size_of::<PositionColor>() as _,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &wgpu::vertex_attr_array![
|
|
||||||
0 => Float32x3,
|
|
||||||
1 => Float32x3,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct Debug {
|
|
||||||
color_lines: wgpu::RenderPipeline,
|
|
||||||
axis: wgpu::Buffer,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug {
|
|
||||||
pub fn new(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
camera_layout: &wgpu::BindGroupLayout,
|
|
||||||
color_format: wgpu::TextureFormat,
|
|
||||||
) -> Self {
|
|
||||||
let axis = device.create_buffer_init(&BufferInitDescriptor {
|
|
||||||
label: Some("Debug::axis"),
|
|
||||||
contents: bytemuck::cast_slice(AXIS_COLORS),
|
|
||||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
|
||||||
});
|
|
||||||
|
|
||||||
let shader = wgpu::include_wgsl!("debug.wgsl");
|
|
||||||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[camera_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
let color_lines = create_render_pipeline(
|
|
||||||
device,
|
|
||||||
&layout,
|
|
||||||
color_format,
|
|
||||||
None,
|
|
||||||
&[POSITION_COLOR_LAYOUT],
|
|
||||||
wgpu::PrimitiveTopology::LineList,
|
|
||||||
shader,
|
|
||||||
);
|
|
||||||
|
|
||||||
Self { color_lines, axis }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn draw_axis<'a: 'b, 'b>(
|
|
||||||
&'a self,
|
|
||||||
pass: &'b mut wgpu::RenderPass<'a>,
|
|
||||||
camera: &'a wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
pass.set_pipeline(&self.color_lines);
|
|
||||||
pass.set_bind_group(0, camera, &[]);
|
|
||||||
pass.set_vertex_buffer(0, self.axis.slice(..));
|
|
||||||
pass.draw(0..AXIS_COLORS.len() as u32, 0..1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
const PI: f32 = 3.1415926535897932384626433832795;
|
|
||||||
|
|
||||||
struct Face {
|
|
||||||
forward: vec3<f32>,
|
|
||||||
up: vec3<f32>,
|
|
||||||
right: vec3<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0)
|
|
||||||
@binding(0)
|
|
||||||
var src: texture_2d<f32>;
|
|
||||||
|
|
||||||
@group(0)
|
|
||||||
@binding(1)
|
|
||||||
var dst: texture_storage_2d_array<rgba32float, write>;
|
|
||||||
|
|
||||||
|
|
||||||
@compute
|
|
||||||
@workgroup_size(16, 16, 1)
|
|
||||||
fn compute_equirect_to_cubemap(
|
|
||||||
@builtin(global_invocation_id)
|
|
||||||
gid: vec3<u32>,
|
|
||||||
) {
|
|
||||||
// If texture size is not divisible by 32 we
|
|
||||||
// need to make sure we don't try to write to
|
|
||||||
// pixels that don't exist.
|
|
||||||
if gid.x >= u32(textureDimensions(dst).x) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var FACES: array<Face, 6> = array(
|
|
||||||
// FACES +X
|
|
||||||
Face(
|
|
||||||
vec3(1.0, 0.0, 0.0), // forward
|
|
||||||
vec3(0.0, 1.0, 0.0), // up
|
|
||||||
vec3(0.0, 0.0, -1.0), // right
|
|
||||||
),
|
|
||||||
// FACES -X
|
|
||||||
Face (
|
|
||||||
vec3(-1.0, 0.0, 0.0),
|
|
||||||
vec3(0.0, 1.0, 0.0),
|
|
||||||
vec3(0.0, 0.0, 1.0),
|
|
||||||
),
|
|
||||||
// FACES +Y
|
|
||||||
Face (
|
|
||||||
vec3(0.0, -1.0, 0.0),
|
|
||||||
vec3(0.0, 0.0, 1.0),
|
|
||||||
vec3(1.0, 0.0, 0.0),
|
|
||||||
),
|
|
||||||
// FACES -Y
|
|
||||||
Face (
|
|
||||||
vec3(0.0, 1.0, 0.0),
|
|
||||||
vec3(0.0, 0.0, -1.0),
|
|
||||||
vec3(1.0, 0.0, 0.0),
|
|
||||||
),
|
|
||||||
// FACES +Z
|
|
||||||
Face (
|
|
||||||
vec3(0.0, 0.0, 1.0),
|
|
||||||
vec3(0.0, 1.0, 0.0),
|
|
||||||
vec3(1.0, 0.0, 0.0),
|
|
||||||
),
|
|
||||||
// FACES -Z
|
|
||||||
Face (
|
|
||||||
vec3(0.0, 0.0, -1.0),
|
|
||||||
vec3(0.0, 1.0, 0.0),
|
|
||||||
vec3(-1.0, 0.0, 0.0),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get texture coords relative to cubemap face
|
|
||||||
let dst_dimensions = vec2<f32>(textureDimensions(dst));
|
|
||||||
let cube_uv = vec2<f32>(gid.xy) / dst_dimensions * 2.0 - 1.0;
|
|
||||||
|
|
||||||
// Get spherical coordinate from cube_uv
|
|
||||||
let face = FACES[gid.z];
|
|
||||||
let spherical = normalize(face.forward + face.right * cube_uv.x + face.up * cube_uv.y);
|
|
||||||
|
|
||||||
// Get coordinate on the equirectangular texture
|
|
||||||
let inv_atan = vec2(0.1591, 0.3183);
|
|
||||||
let eq_uv = vec2(atan2(spherical.z, spherical.x), asin(spherical.y)) * inv_atan + 0.5;
|
|
||||||
let eq_pixel = vec2<i32>(eq_uv * vec2<f32>(textureDimensions(src)));
|
|
||||||
|
|
||||||
// We use textureLoad() as textureSample() is not allowed in compute shaders
|
|
||||||
var sample = textureLoad(src, eq_pixel, 0);
|
|
||||||
|
|
||||||
textureStore(dst, gid.xy, gid.z, sample);
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 28 KiB |
160
src/hdr.rs
160
src/hdr.rs
@ -1,160 +0,0 @@
|
|||||||
use wgpu::Operations;
|
|
||||||
|
|
||||||
use crate::{create_render_pipeline, texture};
|
|
||||||
|
|
||||||
/// Owns the render texture and controls tonemapping
|
|
||||||
pub struct HdrPipeline {
|
|
||||||
pipeline: wgpu::RenderPipeline,
|
|
||||||
bind_group: wgpu::BindGroup,
|
|
||||||
texture: texture::Texture,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
layout: wgpu::BindGroupLayout,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HdrPipeline {
|
|
||||||
pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self {
|
|
||||||
let width = config.width;
|
|
||||||
let height = config.height;
|
|
||||||
|
|
||||||
// We could use `Rgba32Float`, but that requires some extra
|
|
||||||
// features to be enabled.
|
|
||||||
let format = wgpu::TextureFormat::Rgba16Float;
|
|
||||||
|
|
||||||
let texture = texture::Texture::create_2d_texture(
|
|
||||||
device,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
format,
|
|
||||||
wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
wgpu::FilterMode::Nearest,
|
|
||||||
Some("Hdr::texture"),
|
|
||||||
);
|
|
||||||
|
|
||||||
let layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("Hdr::layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
// The Rgba16Float format cannot be filtered
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
multisampled: false,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("Hdr::bind_group"),
|
|
||||||
layout: &layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&texture.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&texture.sampler),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
let shader = wgpu::include_wgsl!("hdr.wgsl");
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[&layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let pipeline = create_render_pipeline(
|
|
||||||
device,
|
|
||||||
&pipeline_layout,
|
|
||||||
config.format.add_srgb_suffix(),
|
|
||||||
None,
|
|
||||||
&[],
|
|
||||||
wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
shader,
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
pipeline,
|
|
||||||
bind_group,
|
|
||||||
layout,
|
|
||||||
texture,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
format,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize the HDR texture
|
|
||||||
pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
|
|
||||||
self.texture = texture::Texture::create_2d_texture(
|
|
||||||
device,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
wgpu::TextureFormat::Rgba16Float,
|
|
||||||
wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
wgpu::FilterMode::Nearest,
|
|
||||||
Some("Hdr::texture"),
|
|
||||||
);
|
|
||||||
self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("Hdr::bind_group"),
|
|
||||||
layout: &self.layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&self.texture.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&self.texture.sampler),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
self.width = width;
|
|
||||||
self.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exposes the HDR texture
|
|
||||||
pub fn view(&self) -> &wgpu::TextureView {
|
|
||||||
&self.texture.view
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The format of the HDR texture
|
|
||||||
pub fn format(&self) -> wgpu::TextureFormat {
|
|
||||||
self.format
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This renders the internal HDR texture to the [TextureView]
|
|
||||||
/// supplied as parameter.
|
|
||||||
pub fn process(&self, encoder: &mut wgpu::CommandEncoder, output: &wgpu::TextureView) {
|
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("Hdr::process"),
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &output,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: Operations {
|
|
||||||
load: wgpu::LoadOp::Load,
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
occlusion_query_set: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
pass.set_pipeline(&self.pipeline);
|
|
||||||
pass.set_bind_group(0, &self.bind_group, &[]);
|
|
||||||
pass.draw(0..3, 0..1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
55
src/hdr.wgsl
55
src/hdr.wgsl
@ -1,55 +0,0 @@
|
|||||||
// Maps HDR values to linear values
|
|
||||||
// Based on http://www.oscars.org/science-technology/sci-tech-projects/aces
|
|
||||||
fn aces_tone_map(hdr: vec3<f32>) -> vec3<f32> {
|
|
||||||
let m1 = mat3x3(
|
|
||||||
0.59719, 0.07600, 0.02840,
|
|
||||||
0.35458, 0.90834, 0.13383,
|
|
||||||
0.04823, 0.01566, 0.83777,
|
|
||||||
);
|
|
||||||
let m2 = mat3x3(
|
|
||||||
1.60475, -0.10208, -0.00327,
|
|
||||||
-0.53108, 1.10813, -0.07276,
|
|
||||||
-0.07367, -0.00605, 1.07602,
|
|
||||||
);
|
|
||||||
let v = m1 * hdr;
|
|
||||||
let a = v * (v + 0.0245786) - 0.000090537;
|
|
||||||
let b = v * (0.983729 * v + 0.4329510) + 0.238081;
|
|
||||||
return clamp(m2 * (a / b), vec3(0.0), vec3(1.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@location(0) uv: vec2<f32>,
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(
|
|
||||||
@builtin(vertex_index) vi: u32,
|
|
||||||
) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
// Generate a triangle that covers the whole screen
|
|
||||||
out.uv = vec2<f32>(
|
|
||||||
f32((vi << 1u) & 2u),
|
|
||||||
f32(vi & 2u),
|
|
||||||
);
|
|
||||||
out.clip_position = vec4<f32>(out.uv * 2.0 - 1.0, 0.0, 1.0);
|
|
||||||
// We need to invert the y coordinate so the image
|
|
||||||
// is not upside down
|
|
||||||
out.uv.y = 1.0 - out.uv.y;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0)
|
|
||||||
@binding(0)
|
|
||||||
var hdr_image: texture_2d<f32>;
|
|
||||||
|
|
||||||
@group(0)
|
|
||||||
@binding(1)
|
|
||||||
var hdr_sampler: sampler;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(vs: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let hdr = textureSample(hdr_image, hdr_sampler, vs.uv);
|
|
||||||
let sdr = aces_tone_map(hdr.rgb);
|
|
||||||
return vec4(sdr, hdr.a);
|
|
||||||
}
|
|
||||||
947
src/lib.rs
947
src/lib.rs
@ -1,947 +0,0 @@
|
|||||||
use std::{f32::consts::PI, iter};
|
|
||||||
use std::sync::Arc;
|
|
||||||
use cgmath::prelude::*;
|
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
use winit::{
|
|
||||||
event::*,
|
|
||||||
event_loop::EventLoop,
|
|
||||||
keyboard::{KeyCode, PhysicalKey},
|
|
||||||
window::Window,
|
|
||||||
};
|
|
||||||
use winit::application::ApplicationHandler;
|
|
||||||
use winit::dpi::{PhysicalPosition, PhysicalSize};
|
|
||||||
use winit::event_loop::ActiveEventLoop;
|
|
||||||
use winit::window::WindowId;
|
|
||||||
|
|
||||||
mod camera;
|
|
||||||
mod hdr;
|
|
||||||
mod model;
|
|
||||||
mod resources;
|
|
||||||
mod texture;
|
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
mod debug;
|
|
||||||
|
|
||||||
use model::{DrawLight, DrawModel, Vertex};
|
|
||||||
|
|
||||||
const NUM_INSTANCES_PER_ROW: u32 = 10;
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
struct CameraUniform {
|
|
||||||
view_position: [f32; 4],
|
|
||||||
view: [[f32; 4]; 4],
|
|
||||||
view_proj: [[f32; 4]; 4],
|
|
||||||
inv_proj: [[f32; 4]; 4],
|
|
||||||
inv_view: [[f32; 4]; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CameraUniform {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
view_position: [0.0; 4],
|
|
||||||
view: cgmath::Matrix4::identity().into(), // NEW!
|
|
||||||
view_proj: cgmath::Matrix4::identity().into(),
|
|
||||||
inv_proj: cgmath::Matrix4::identity().into(), // NEW!
|
|
||||||
inv_view: cgmath::Matrix4::identity().into(), // NEW!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_view_proj(&mut self, camera: &camera::Camera, projection: &camera::Projection) {
|
|
||||||
self.view_position = camera.position.to_homogeneous().into();
|
|
||||||
let proj = projection.calc_matrix();
|
|
||||||
let view = camera.calc_matrix();
|
|
||||||
let view_proj = proj * view;
|
|
||||||
self.view = view.into();
|
|
||||||
self.view_proj = view_proj.into();
|
|
||||||
self.inv_proj = proj.invert().unwrap().into();
|
|
||||||
self.inv_view = view.transpose().into();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Instance {
|
|
||||||
position: cgmath::Vector3<f32>,
|
|
||||||
rotation: cgmath::Quaternion<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Instance {
|
|
||||||
fn to_raw(&self) -> InstanceRaw {
|
|
||||||
InstanceRaw {
|
|
||||||
model: (cgmath::Matrix4::from_translation(self.position)
|
|
||||||
* cgmath::Matrix4::from(self.rotation))
|
|
||||||
.into(),
|
|
||||||
normal: cgmath::Matrix3::from(self.rotation).into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
struct InstanceRaw {
|
|
||||||
model: [[f32; 4]; 4],
|
|
||||||
normal: [[f32; 3]; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl model::Vertex for InstanceRaw {
|
|
||||||
fn desc() -> wgpu::VertexBufferLayout<'static> {
|
|
||||||
use std::mem;
|
|
||||||
wgpu::VertexBufferLayout {
|
|
||||||
array_stride: mem::size_of::<InstanceRaw>() as wgpu::BufferAddress,
|
|
||||||
// We need to switch from using a step mode of Vertex to Instance
|
|
||||||
// This means that our shaders will only change to use the next
|
|
||||||
// instance when the shader starts processing a new instance
|
|
||||||
step_mode: wgpu::VertexStepMode::Instance,
|
|
||||||
attributes: &[
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 0,
|
|
||||||
// While our vertex shader only uses locations 0, and 1 now, in later tutorials we'll
|
|
||||||
// be using 2, 3, and 4, for Vertex. We'll start at slot 5 not conflict with them later
|
|
||||||
shader_location: 5,
|
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
|
||||||
},
|
|
||||||
// A mat4 takes up 4 vertex slots as it is technically 4 vec4s. We need to define a slot
|
|
||||||
// for each vec4. We don't have to do this in code though.
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 6,
|
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 7,
|
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 8,
|
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 9,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 19]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 10,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 22]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 11,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
struct LightUniform {
|
|
||||||
position: [f32; 3],
|
|
||||||
// Due to uniforms requiring 16 byte (4 float) spacing, we need to use a padding field here
|
|
||||||
_padding: u32,
|
|
||||||
color: [f32; 3],
|
|
||||||
_padding2: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StateApplication<'a> {
|
|
||||||
state: Option<State<'a>>,
|
|
||||||
last_render_time: instant::Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> StateApplication<'a> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
state: None,
|
|
||||||
last_render_time: instant::Instant::now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> ApplicationHandler for StateApplication<'a>{
|
|
||||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
|
||||||
let window = event_loop
|
|
||||||
.create_window(Window::default_attributes().with_title(env!("CARGO_PKG_NAME")))
|
|
||||||
.unwrap();
|
|
||||||
self.state = Some(pollster::block_on(State::new(window)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn window_event(&mut self, event_loop: &ActiveEventLoop, window_id: WindowId, event: WindowEvent) {
|
|
||||||
let window = self.state.as_ref().unwrap().window();
|
|
||||||
|
|
||||||
if window.id() == window_id {
|
|
||||||
if !self.state.as_mut().unwrap().input(&event) {
|
|
||||||
match event {
|
|
||||||
WindowEvent::CloseRequested => {
|
|
||||||
event_loop.exit();
|
|
||||||
}
|
|
||||||
WindowEvent::Resized(physical_size) => {
|
|
||||||
self.state.as_mut().unwrap().resize(physical_size);
|
|
||||||
}
|
|
||||||
WindowEvent::RedrawRequested => {
|
|
||||||
let now = instant::Instant::now();
|
|
||||||
let dt = now - self.last_render_time;
|
|
||||||
self.last_render_time = now;
|
|
||||||
|
|
||||||
self.state.as_mut().unwrap().update(dt);
|
|
||||||
self.state.as_mut().unwrap().render().unwrap();
|
|
||||||
}
|
|
||||||
_ => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
|
||||||
let window = self.state.as_ref().unwrap().window();
|
|
||||||
window.request_redraw();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct State<'a> {
|
|
||||||
window: Arc<Window>,
|
|
||||||
size: PhysicalSize<u32>,
|
|
||||||
surface: wgpu::Surface<'a>,
|
|
||||||
device: wgpu::Device,
|
|
||||||
queue: wgpu::Queue,
|
|
||||||
config: wgpu::SurfaceConfiguration,
|
|
||||||
render_pipeline: wgpu::RenderPipeline,
|
|
||||||
obj_model: model::Model,
|
|
||||||
camera: camera::Camera,
|
|
||||||
projection: camera::Projection,
|
|
||||||
camera_controller: camera::CameraController,
|
|
||||||
camera_uniform: CameraUniform,
|
|
||||||
camera_buffer: wgpu::Buffer,
|
|
||||||
camera_bind_group: wgpu::BindGroup,
|
|
||||||
last_mouse_position: PhysicalPosition<f64>,
|
|
||||||
instances: Vec<Instance>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
instance_buffer: wgpu::Buffer,
|
|
||||||
depth_texture: texture::Texture,
|
|
||||||
light_uniform: LightUniform,
|
|
||||||
light_buffer: wgpu::Buffer,
|
|
||||||
light_bind_group: wgpu::BindGroup,
|
|
||||||
light_render_pipeline: wgpu::RenderPipeline,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
debug_material: model::Material,
|
|
||||||
mouse_pressed: bool,
|
|
||||||
hdr: hdr::HdrPipeline,
|
|
||||||
environment_bind_group: wgpu::BindGroup,
|
|
||||||
sky_pipeline: wgpu::RenderPipeline,
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
debug: debug::Debug,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_render_pipeline(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
layout: &wgpu::PipelineLayout,
|
|
||||||
color_format: wgpu::TextureFormat,
|
|
||||||
depth_format: Option<wgpu::TextureFormat>,
|
|
||||||
vertex_layouts: &[wgpu::VertexBufferLayout],
|
|
||||||
topology: wgpu::PrimitiveTopology, // NEW!
|
|
||||||
shader: wgpu::ShaderModuleDescriptor,
|
|
||||||
) -> wgpu::RenderPipeline {
|
|
||||||
let shader = device.create_shader_module(shader);
|
|
||||||
|
|
||||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
|
||||||
label: Some(&format!("{:?}", shader)),
|
|
||||||
layout: Some(layout),
|
|
||||||
vertex: wgpu::VertexState {
|
|
||||||
module: &shader,
|
|
||||||
entry_point: "vs_main",
|
|
||||||
buffers: vertex_layouts,
|
|
||||||
compilation_options: Default::default(),
|
|
||||||
},
|
|
||||||
fragment: Some(wgpu::FragmentState {
|
|
||||||
module: &shader,
|
|
||||||
entry_point: "fs_main",
|
|
||||||
targets: &[Some(wgpu::ColorTargetState {
|
|
||||||
format: color_format,
|
|
||||||
blend: None,
|
|
||||||
write_mask: wgpu::ColorWrites::ALL,
|
|
||||||
})],
|
|
||||||
compilation_options: Default::default(),
|
|
||||||
}),
|
|
||||||
primitive: wgpu::PrimitiveState {
|
|
||||||
topology, // NEW!
|
|
||||||
strip_index_format: None,
|
|
||||||
front_face: wgpu::FrontFace::Ccw,
|
|
||||||
cull_mode: Some(wgpu::Face::Back),
|
|
||||||
// Setting this to anything other than Fill requires Features::NON_FILL_POLYGON_MODE
|
|
||||||
polygon_mode: wgpu::PolygonMode::Fill,
|
|
||||||
// Requires Features::DEPTH_CLIP_CONTROL
|
|
||||||
unclipped_depth: false,
|
|
||||||
// Requires Features::CONSERVATIVE_RASTERIZATION
|
|
||||||
conservative: false,
|
|
||||||
},
|
|
||||||
depth_stencil: depth_format.map(|format| wgpu::DepthStencilState {
|
|
||||||
format,
|
|
||||||
depth_write_enabled: true,
|
|
||||||
depth_compare: wgpu::CompareFunction::LessEqual, // UDPATED!
|
|
||||||
stencil: wgpu::StencilState::default(),
|
|
||||||
bias: wgpu::DepthBiasState::default(),
|
|
||||||
}),
|
|
||||||
multisample: wgpu::MultisampleState {
|
|
||||||
count: 1,
|
|
||||||
mask: !0,
|
|
||||||
alpha_to_coverage_enabled: false,
|
|
||||||
},
|
|
||||||
// If the pipeline will be used with a multiview render pass, this
|
|
||||||
// indicates how many array layers the attachments will have.
|
|
||||||
multiview: None,
|
|
||||||
cache: None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> State<'a> {
|
|
||||||
async fn new(window: Window) -> Self {
|
|
||||||
let window_arc = Arc::new(window);
|
|
||||||
let size = window_arc.inner_size();
|
|
||||||
|
|
||||||
// The instance is a handle to our GPU
|
|
||||||
// BackendBit::PRIMARY => Vulkan + Metal + DX12 + Browser WebGPU
|
|
||||||
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
|
|
||||||
backends: wgpu::Backends::PRIMARY,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let surface = instance.create_surface(window_arc.clone()).unwrap();
|
|
||||||
|
|
||||||
let adapter = instance
|
|
||||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
|
||||||
power_preference: wgpu::PowerPreference::default(),
|
|
||||||
compatible_surface: Some(&surface),
|
|
||||||
force_fallback_adapter: false,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let (device, queue) = adapter
|
|
||||||
.request_device(
|
|
||||||
&wgpu::DeviceDescriptor {
|
|
||||||
label: None,
|
|
||||||
// UPDATED!
|
|
||||||
required_features: wgpu::Features::empty(),
|
|
||||||
// UPDATED!
|
|
||||||
required_limits: wgpu::Limits {
|
|
||||||
// Increase from 2048 to 8192
|
|
||||||
max_texture_dimension_2d: 8192,
|
|
||||||
..wgpu::Limits::default()
|
|
||||||
},
|
|
||||||
memory_hints: Default::default(),
|
|
||||||
},
|
|
||||||
None, // Trace path
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let surface_caps = surface.get_capabilities(&adapter);
|
|
||||||
// Shader code in this tutorial assumes an Srgb surface texture. Using a different
|
|
||||||
// one will result all the colors comming out darker. If you want to support non
|
|
||||||
// Srgb surfaces, you'll need to account for that when drawing to the frame.
|
|
||||||
let surface_format = surface_caps
|
|
||||||
.formats
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.find(|f| f.is_srgb())
|
|
||||||
.unwrap_or(surface_caps.formats[0]);
|
|
||||||
let config = wgpu::SurfaceConfiguration {
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
|
|
||||||
format: surface_format,
|
|
||||||
width: size.width,
|
|
||||||
height: size.height,
|
|
||||||
present_mode: surface_caps.present_modes[0],
|
|
||||||
alpha_mode: surface_caps.alpha_modes[0],
|
|
||||||
// NEW!
|
|
||||||
view_formats: vec![surface_format.add_srgb_suffix()],
|
|
||||||
desired_maximum_frame_latency: 2,
|
|
||||||
};
|
|
||||||
|
|
||||||
let texture_bind_group_layout =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
// normal map
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 2,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
multisampled: false,
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 3,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: Some("texture_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let camera = camera::Camera::new((0.0, 5.0, 10.0), cgmath::Deg(-90.0), cgmath::Deg(-20.0));
|
|
||||||
let projection =
|
|
||||||
camera::Projection::new(config.width, config.height, cgmath::Deg(45.0), 0.1, 100.0);
|
|
||||||
let camera_controller = camera::CameraController::new(4.0, 0.4);
|
|
||||||
|
|
||||||
let mut camera_uniform = CameraUniform::new();
|
|
||||||
camera_uniform.update_view_proj(&camera, &projection);
|
|
||||||
|
|
||||||
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some("Camera Buffer"),
|
|
||||||
contents: bytemuck::cast_slice(&[camera_uniform]),
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
const SPACE_BETWEEN: f32 = 3.0;
|
|
||||||
let instances = (0..NUM_INSTANCES_PER_ROW)
|
|
||||||
.flat_map(|z| {
|
|
||||||
(0..NUM_INSTANCES_PER_ROW).map(move |x| {
|
|
||||||
let x = SPACE_BETWEEN * (x as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0);
|
|
||||||
let z = SPACE_BETWEEN * (z as f32 - NUM_INSTANCES_PER_ROW as f32 / 2.0);
|
|
||||||
|
|
||||||
let position = cgmath::Vector3 { x, y: 0.0, z };
|
|
||||||
|
|
||||||
let rotation = if position.is_zero() {
|
|
||||||
cgmath::Quaternion::from_axis_angle(
|
|
||||||
cgmath::Vector3::unit_z(),
|
|
||||||
cgmath::Deg(0.0),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0))
|
|
||||||
};
|
|
||||||
|
|
||||||
Instance { position, rotation }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let instance_data = instances.iter().map(Instance::to_raw).collect::<Vec<_>>();
|
|
||||||
let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some("Instance Buffer"),
|
|
||||||
contents: bytemuck::cast_slice(&instance_data),
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
});
|
|
||||||
|
|
||||||
let camera_bind_group_layout =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
|
||||||
label: Some("camera_bind_group_layout"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout: &camera_bind_group_layout,
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: camera_buffer.as_entire_binding(),
|
|
||||||
}],
|
|
||||||
label: Some("camera_bind_group"),
|
|
||||||
});
|
|
||||||
|
|
||||||
let obj_model =
|
|
||||||
resources::load_model("cube.obj", &device, &queue, &texture_bind_group_layout)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let light_uniform = LightUniform {
|
|
||||||
position: [2.0, 2.0, 2.0],
|
|
||||||
_padding: 0,
|
|
||||||
color: [1.0, 1.0, 1.0],
|
|
||||||
_padding2: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let light_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some("Light VB"),
|
|
||||||
contents: bytemuck::cast_slice(&[light_uniform]),
|
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
|
||||||
});
|
|
||||||
|
|
||||||
let light_bind_group_layout =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Buffer {
|
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
|
||||||
has_dynamic_offset: false,
|
|
||||||
min_binding_size: None,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
}],
|
|
||||||
label: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let light_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout: &light_bind_group_layout,
|
|
||||||
entries: &[wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: light_buffer.as_entire_binding(),
|
|
||||||
}],
|
|
||||||
label: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let depth_texture =
|
|
||||||
texture::Texture::create_depth_texture(&device, &config, "depth_texture");
|
|
||||||
|
|
||||||
let hdr = hdr::HdrPipeline::new(&device, &config);
|
|
||||||
|
|
||||||
let hdr_loader = resources::HdrLoader::new(&device);
|
|
||||||
let sky_bytes = resources::load_binary("skybox.hdr").await.unwrap();
|
|
||||||
let sky_texture = hdr_loader.from_equirectangular_bytes(
|
|
||||||
&device,
|
|
||||||
&queue,
|
|
||||||
&sky_bytes,
|
|
||||||
1080,
|
|
||||||
Some("Sky Texture"),
|
|
||||||
).unwrap();
|
|
||||||
|
|
||||||
let environment_layout =
|
|
||||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("environment_layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::Cube,
|
|
||||||
multisampled: false,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
|
||||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
let environment_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label: Some("environment_bind_group"),
|
|
||||||
layout: &environment_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&sky_texture.view()),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(sky_texture.sampler()),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
let render_pipeline_layout =
|
|
||||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Render Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[
|
|
||||||
&texture_bind_group_layout,
|
|
||||||
&camera_bind_group_layout,
|
|
||||||
&light_bind_group_layout,
|
|
||||||
&environment_layout, // UPDATED!
|
|
||||||
],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let render_pipeline = {
|
|
||||||
let shader = wgpu::ShaderModuleDescriptor {
|
|
||||||
label: Some("Normal Shader"),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
|
|
||||||
};
|
|
||||||
create_render_pipeline(
|
|
||||||
&device,
|
|
||||||
&render_pipeline_layout,
|
|
||||||
hdr.format(),
|
|
||||||
Some(texture::Texture::DEPTH_FORMAT),
|
|
||||||
&[model::ModelVertex::desc(), InstanceRaw::desc()],
|
|
||||||
wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
shader,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let light_render_pipeline = {
|
|
||||||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Light Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[&camera_bind_group_layout, &light_bind_group_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
let shader = wgpu::ShaderModuleDescriptor {
|
|
||||||
label: Some("Light Shader"),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(include_str!("light.wgsl").into()),
|
|
||||||
};
|
|
||||||
create_render_pipeline(
|
|
||||||
&device,
|
|
||||||
&layout,
|
|
||||||
hdr.format(),
|
|
||||||
Some(texture::Texture::DEPTH_FORMAT),
|
|
||||||
&[model::ModelVertex::desc()],
|
|
||||||
wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
shader,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// NEW!
|
|
||||||
let sky_pipeline = {
|
|
||||||
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: Some("Sky Pipeline Layout"),
|
|
||||||
bind_group_layouts: &[&camera_bind_group_layout, &environment_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
let shader = wgpu::include_wgsl!("sky.wgsl");
|
|
||||||
create_render_pipeline(
|
|
||||||
&device,
|
|
||||||
&layout,
|
|
||||||
hdr.format(),
|
|
||||||
Some(texture::Texture::DEPTH_FORMAT),
|
|
||||||
&[],
|
|
||||||
wgpu::PrimitiveTopology::TriangleList,
|
|
||||||
shader,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
let debug_material = {
|
|
||||||
let diffuse_bytes = include_bytes!("../res/cobble-diffuse.png");
|
|
||||||
let normal_bytes = include_bytes!("../res/cobble-normal.png");
|
|
||||||
|
|
||||||
let diffuse_texture = texture::Texture::from_bytes(
|
|
||||||
&device,
|
|
||||||
&queue,
|
|
||||||
diffuse_bytes,
|
|
||||||
"res/alt-diffuse.png",
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let normal_texture = texture::Texture::from_bytes(
|
|
||||||
&device,
|
|
||||||
&queue,
|
|
||||||
normal_bytes,
|
|
||||||
"res/alt-normal.png",
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
model::Material::new(
|
|
||||||
&device,
|
|
||||||
"alt-material",
|
|
||||||
diffuse_texture,
|
|
||||||
normal_texture,
|
|
||||||
&texture_bind_group_layout,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
let debug = debug::Debug::new(&device, &camera_bind_group_layout, surface_format);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
window: window_arc,
|
|
||||||
size,
|
|
||||||
surface,
|
|
||||||
device,
|
|
||||||
queue,
|
|
||||||
config,
|
|
||||||
render_pipeline,
|
|
||||||
obj_model,
|
|
||||||
camera,
|
|
||||||
projection,
|
|
||||||
camera_controller,
|
|
||||||
camera_buffer,
|
|
||||||
camera_bind_group,
|
|
||||||
last_mouse_position: PhysicalPosition::new(0.0, 0.0),
|
|
||||||
camera_uniform,
|
|
||||||
instances,
|
|
||||||
instance_buffer,
|
|
||||||
depth_texture,
|
|
||||||
light_uniform,
|
|
||||||
light_buffer,
|
|
||||||
light_bind_group,
|
|
||||||
light_render_pipeline,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
debug_material,
|
|
||||||
mouse_pressed: false,
|
|
||||||
hdr,
|
|
||||||
environment_bind_group,
|
|
||||||
sky_pipeline,
|
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
debug,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window(&self) -> &Window {
|
|
||||||
&self.window
|
|
||||||
}
|
|
||||||
|
|
||||||
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
|
|
||||||
if new_size.width > 0 && new_size.height > 0 {
|
|
||||||
println!("Resizing to {:?}", new_size);
|
|
||||||
|
|
||||||
// Begrenzen der Dimensionen auf GPU-Limits
|
|
||||||
let max_texture_dimension = self.device.limits().max_texture_dimension_2d as u32;
|
|
||||||
let width = new_size.width.min(max_texture_dimension);
|
|
||||||
let height = new_size.height.min(max_texture_dimension);
|
|
||||||
|
|
||||||
self.projection.resize(width, height);
|
|
||||||
self.hdr.resize(&self.device, width, height);
|
|
||||||
self.size = PhysicalSize { width, height }; // Aktualisieren auf die begrenzten Dimensionen
|
|
||||||
self.config.width = width;
|
|
||||||
self.config.height = height;
|
|
||||||
|
|
||||||
self.surface.configure(&self.device, &self.config);
|
|
||||||
self.depth_texture =
|
|
||||||
texture::Texture::create_depth_texture(&self.device, &self.config, "depth_texture");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn input(&mut self, event: &WindowEvent) -> bool {
|
|
||||||
println!("Handle event {:?}", event);
|
|
||||||
match event {
|
|
||||||
WindowEvent::KeyboardInput {
|
|
||||||
event:
|
|
||||||
KeyEvent {
|
|
||||||
physical_key: PhysicalKey::Code(key),
|
|
||||||
state,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => self.camera_controller.process_keyboard(*key, *state),
|
|
||||||
WindowEvent::MouseWheel { delta, .. } => {
|
|
||||||
self.camera_controller.process_scroll(delta);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
WindowEvent::MouseInput {
|
|
||||||
button: MouseButton::Left,
|
|
||||||
state,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
self.mouse_pressed = *state == ElementState::Pressed;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
|
||||||
if self.mouse_pressed {
|
|
||||||
let delta_x = position.x as f64 - self.last_mouse_position.x as f64;
|
|
||||||
let delta_y = position.y as f64 - self.last_mouse_position.y as f64;
|
|
||||||
self.camera_controller.process_mouse(delta_x, delta_y);
|
|
||||||
}
|
|
||||||
self.last_mouse_position = *position;
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, dt: std::time::Duration) {
|
|
||||||
self.camera_controller.update_camera(&mut self.camera, dt);
|
|
||||||
self.camera_uniform
|
|
||||||
.update_view_proj(&self.camera, &self.projection);
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.camera_buffer,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&[self.camera_uniform]),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Update the light
|
|
||||||
let old_position: cgmath::Vector3<_> = self.light_uniform.position.into();
|
|
||||||
self.light_uniform.position = (cgmath::Quaternion::from_axis_angle(
|
|
||||||
(0.0, 1.0, 0.0).into(),
|
|
||||||
cgmath::Deg(PI * dt.as_secs_f32()),
|
|
||||||
) * old_position)
|
|
||||||
.into();
|
|
||||||
self.queue.write_buffer(
|
|
||||||
&self.light_buffer,
|
|
||||||
0,
|
|
||||||
bytemuck::cast_slice(&[self.light_uniform]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
|
||||||
let output = self.surface.get_current_texture()?;
|
|
||||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor {
|
|
||||||
format: Some(self.config.format.add_srgb_suffix()),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut encoder = self
|
|
||||||
.device
|
|
||||||
.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
|
||||||
label: Some("Render Encoder"),
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("Render Pass"),
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: self.hdr.view(), // UPDATED!
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
|
||||||
r: 0.1,
|
|
||||||
g: 0.2,
|
|
||||||
b: 0.3,
|
|
||||||
a: 1.0,
|
|
||||||
}),
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
|
|
||||||
view: &self.depth_texture.view,
|
|
||||||
depth_ops: Some(wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Clear(1.0),
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
}),
|
|
||||||
stencil_ops: None,
|
|
||||||
}),
|
|
||||||
occlusion_query_set: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..));
|
|
||||||
render_pass.set_pipeline(&self.light_render_pipeline);
|
|
||||||
render_pass.draw_light_model(
|
|
||||||
&self.obj_model,
|
|
||||||
&self.camera_bind_group,
|
|
||||||
&self.light_bind_group,
|
|
||||||
);
|
|
||||||
|
|
||||||
render_pass.set_pipeline(&self.render_pipeline);
|
|
||||||
render_pass.draw_model_instanced(
|
|
||||||
&self.obj_model,
|
|
||||||
0..self.instances.len() as u32,
|
|
||||||
&self.camera_bind_group,
|
|
||||||
&self.light_bind_group,
|
|
||||||
&self.environment_bind_group,
|
|
||||||
);
|
|
||||||
|
|
||||||
render_pass.set_pipeline(&self.sky_pipeline);
|
|
||||||
render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
|
|
||||||
render_pass.set_bind_group(1, &self.environment_bind_group, &[]);
|
|
||||||
render_pass.draw(0..3, 0..1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NEW!
|
|
||||||
// Apply tonemapping
|
|
||||||
self.hdr.process(&mut encoder, &view);
|
|
||||||
|
|
||||||
#[cfg(feature = "debug")]
|
|
||||||
{
|
|
||||||
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
|
||||||
label: Some("Debug"),
|
|
||||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
|
||||||
view: &view,
|
|
||||||
resolve_target: None,
|
|
||||||
ops: wgpu::Operations {
|
|
||||||
load: wgpu::LoadOp::Load,
|
|
||||||
store: wgpu::StoreOp::Store,
|
|
||||||
},
|
|
||||||
})],
|
|
||||||
depth_stencil_attachment: None,
|
|
||||||
occlusion_query_set: None,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
self.debug.draw_axis(&mut pass, &self.camera_bind_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.queue.submit(iter::once(encoder.finish()));
|
|
||||||
output.present();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn run() {
|
|
||||||
env_logger::init();
|
|
||||||
|
|
||||||
let event_loop = EventLoop::new().unwrap();
|
|
||||||
/*let title = env!("CARGO_PKG_NAME");
|
|
||||||
let window = winit::window::WindowBuilder::new()
|
|
||||||
.with_title(title)
|
|
||||||
// Limit dimensions to 1080p
|
|
||||||
.with_inner_size(winit::dpi::PhysicalSize::new(1920, 1080))
|
|
||||||
.build(&event_loop)
|
|
||||||
.unwrap();*/
|
|
||||||
|
|
||||||
let mut window_state = StateApplication::new();
|
|
||||||
let _ = event_loop.run_app(&mut window_state);
|
|
||||||
|
|
||||||
/*let mut state = State::new(&window).await.unwrap();
|
|
||||||
let mut last_render_time = instant::Instant::now();
|
|
||||||
event_loop.run(move |event, control_flow| {
|
|
||||||
match event {
|
|
||||||
Event::DeviceEvent {
|
|
||||||
event: DeviceEvent::MouseMotion{ delta, },
|
|
||||||
.. // We're not using device_id currently
|
|
||||||
} => if state.mouse_pressed {
|
|
||||||
state.camera_controller.process_mouse(delta.0, delta.1)
|
|
||||||
}
|
|
||||||
// UPDATED!
|
|
||||||
Event::WindowEvent {
|
|
||||||
ref event,
|
|
||||||
window_id,
|
|
||||||
} if window_id == state.window().id() && !state.input(event) => {
|
|
||||||
match event {
|
|
||||||
WindowEvent::CloseRequested
|
|
||||||
| WindowEvent::KeyboardInput {
|
|
||||||
event:
|
|
||||||
KeyEvent {
|
|
||||||
state: ElementState::Pressed,
|
|
||||||
physical_key: PhysicalKey::Code(KeyCode::Escape),
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} => control_flow.exit(),
|
|
||||||
WindowEvent::Resized(physical_size) => {
|
|
||||||
state.resize(*physical_size);
|
|
||||||
}
|
|
||||||
WindowEvent::RedrawRequested => {
|
|
||||||
state.window().request_redraw();
|
|
||||||
let now = instant::Instant::now();
|
|
||||||
let dt = now - last_render_time;
|
|
||||||
last_render_time = now;
|
|
||||||
state.update(dt);
|
|
||||||
match state.render() {
|
|
||||||
Ok(_) => {}
|
|
||||||
// Reconfigure the surface if it's lost or outdated
|
|
||||||
Err(wgpu::SurfaceError::Lost | wgpu::SurfaceError::Outdated) => state.resize(state.size),
|
|
||||||
// The system is out of memory, we should probably quit
|
|
||||||
Err(wgpu::SurfaceError::OutOfMemory) => control_flow.exit(),
|
|
||||||
// We're ignoring timeouts
|
|
||||||
Err(wgpu::SurfaceError::Timeout) => log::warn!("Surface timeout"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}).unwrap();*/
|
|
||||||
}
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
// Vertex shader
|
|
||||||
|
|
||||||
struct Camera {
|
|
||||||
view_pos: vec4<f32>,
|
|
||||||
view: mat4x4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
inv_proj: mat4x4<f32>,
|
|
||||||
inv_view: mat4x4<f32>,
|
|
||||||
}
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> camera: Camera;
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
position: vec3<f32>,
|
|
||||||
color: vec3<f32>,
|
|
||||||
}
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> light: Light;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
@location(0) color: vec3<f32>,
|
|
||||||
};
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(
|
|
||||||
model: VertexInput,
|
|
||||||
) -> VertexOutput {
|
|
||||||
let scale = 0.25;
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.clip_position = camera.view_proj * vec4<f32>(model.position * scale + light.position, 1.0);
|
|
||||||
out.color = light.color;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fragment shader
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
return vec4<f32>(in.color, 1.0);
|
|
||||||
}
|
|
||||||
93
src/main.rs
93
src/main.rs
@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
This program is for simulating orbits of planets around a star.
|
|
||||||
A body can be a star, planet or moon. (Doesn't matter for now)
|
|
||||||
*/
|
|
||||||
|
|
||||||
use orbital_simulation::run;
|
|
||||||
|
|
||||||
// Gravitational constant
|
|
||||||
static G: f64 = 6.67430e-11;
|
|
||||||
|
|
||||||
struct Body {
|
|
||||||
name: String,
|
|
||||||
mass: f64,
|
|
||||||
radius: f64,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_gravitational_force(body1: &Body, body2: &Body, distance: f64) -> f64 {
|
|
||||||
(G * body1.mass * body2.mass) / distance.powi(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_required_velocity(body1: &Body, body2: &Body, distance: f64, force: f64) -> f64 {
|
|
||||||
(force * distance / body2.mass).sqrt()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// simulate a two body system to simplify the problem
|
|
||||||
let sun = Body {
|
|
||||||
name: "Sun".to_string(),
|
|
||||||
mass: 1.989e30,
|
|
||||||
radius: 6.9634e8,
|
|
||||||
};
|
|
||||||
|
|
||||||
let earth = Body {
|
|
||||||
name: "Earth".to_string(),
|
|
||||||
mass: 5.972e24,
|
|
||||||
radius: 6.371e6,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*// Calculate the velocity pulling the earth towards the sun
|
|
||||||
let distance = 1.496e11;
|
|
||||||
let force = calculate_gravitational_force(&sun, &earth, distance);
|
|
||||||
|
|
||||||
let velocity = calculate_required_velocity(&sun, &earth, distance, force);
|
|
||||||
|
|
||||||
println!("The velocity of the earth is: {} m/s", velocity);*/
|
|
||||||
|
|
||||||
// Now we simulate a whole orbit around the sun
|
|
||||||
let distance = 1.496e11;
|
|
||||||
let force = calculate_gravitational_force(&sun, &earth, distance);
|
|
||||||
let velocity = calculate_required_velocity(&sun, &earth, distance, force);
|
|
||||||
|
|
||||||
let mut time = 0.0;
|
|
||||||
let mut position = 0.0;
|
|
||||||
let mut velocity = velocity;
|
|
||||||
let mut acceleration = 0.0;
|
|
||||||
let mut force = force;
|
|
||||||
let mut distance = distance;
|
|
||||||
let mut mass = earth.mass;
|
|
||||||
let mut radius = earth.radius;
|
|
||||||
|
|
||||||
let dt = 1.0;
|
|
||||||
let steps = 1000;
|
|
||||||
|
|
||||||
for _ in 0..steps {
|
|
||||||
// Calculate the acceleration
|
|
||||||
acceleration = force / mass;
|
|
||||||
|
|
||||||
// Calculate the new position
|
|
||||||
position += velocity * dt + 0.5 * acceleration * dt.powi(2);
|
|
||||||
|
|
||||||
// Calculate the new velocity
|
|
||||||
velocity += acceleration * dt;
|
|
||||||
|
|
||||||
// Calculate the new distance
|
|
||||||
distance = position;
|
|
||||||
|
|
||||||
// Calculate the new force
|
|
||||||
force = calculate_gravitational_force(&sun, &earth, distance);
|
|
||||||
|
|
||||||
// Calculate the new mass
|
|
||||||
mass = earth.mass;
|
|
||||||
|
|
||||||
// Calculate the new radius
|
|
||||||
radius = earth.radius;
|
|
||||||
|
|
||||||
// Calculate the new time
|
|
||||||
time += dt;
|
|
||||||
|
|
||||||
println!("Time: {} s, Position: {} m, Velocity: {} m/s, Acceleration: {} m/s^2, Force: {} N, Distance: {} m, Mass: {} kg, Radius: {} m", time, position, velocity, acceleration, force, distance, mass, radius);
|
|
||||||
}
|
|
||||||
|
|
||||||
pollster::block_on(run());
|
|
||||||
}
|
|
||||||
350
src/model.rs
350
src/model.rs
@ -1,350 +0,0 @@
|
|||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
use crate::texture;
|
|
||||||
|
|
||||||
pub trait Vertex {
|
|
||||||
fn desc() -> wgpu::VertexBufferLayout<'static>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
|
||||||
pub struct ModelVertex {
|
|
||||||
pub position: [f32; 3],
|
|
||||||
pub tex_coords: [f32; 2],
|
|
||||||
pub normal: [f32; 3],
|
|
||||||
pub tangent: [f32; 3],
|
|
||||||
pub bitangent: [f32; 3],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Vertex for ModelVertex {
|
|
||||||
fn desc() -> wgpu::VertexBufferLayout<'static> {
|
|
||||||
use std::mem;
|
|
||||||
wgpu::VertexBufferLayout {
|
|
||||||
array_stride: mem::size_of::<ModelVertex>() as wgpu::BufferAddress,
|
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
|
||||||
attributes: &[
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: 0,
|
|
||||||
shader_location: 0,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 1,
|
|
||||||
format: wgpu::VertexFormat::Float32x2,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 2,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
// Tangent and bitangent
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 3,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
wgpu::VertexAttribute {
|
|
||||||
offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
|
|
||||||
shader_location: 4,
|
|
||||||
format: wgpu::VertexFormat::Float32x3,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Material {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub name: String,
|
|
||||||
#[allow(unused)]
|
|
||||||
pub diffuse_texture: texture::Texture,
|
|
||||||
#[allow(unused)]
|
|
||||||
pub normal_texture: texture::Texture,
|
|
||||||
pub bind_group: wgpu::BindGroup,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Material {
|
|
||||||
pub fn new(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
name: &str,
|
|
||||||
diffuse_texture: texture::Texture,
|
|
||||||
normal_texture: texture::Texture,
|
|
||||||
layout: &wgpu::BindGroupLayout,
|
|
||||||
) -> Self {
|
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 2,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&normal_texture.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 3,
|
|
||||||
resource: wgpu::BindingResource::Sampler(&normal_texture.sampler),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
label: Some(name),
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
name: String::from(name),
|
|
||||||
diffuse_texture,
|
|
||||||
normal_texture,
|
|
||||||
bind_group,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Mesh {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub name: String,
|
|
||||||
pub vertex_buffer: wgpu::Buffer,
|
|
||||||
pub index_buffer: wgpu::Buffer,
|
|
||||||
pub num_elements: u32,
|
|
||||||
pub material: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Model {
|
|
||||||
pub meshes: Vec<Mesh>,
|
|
||||||
pub materials: Vec<Material>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DrawModel<'a> {
|
|
||||||
#[allow(unused)]
|
|
||||||
fn draw_mesh(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'a Mesh,
|
|
||||||
material: &'a Material,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
fn draw_mesh_instanced(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'a Mesh,
|
|
||||||
material: &'a Material,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
fn draw_model(
|
|
||||||
&mut self,
|
|
||||||
model: &'a Model,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
fn draw_model_instanced(
|
|
||||||
&mut self,
|
|
||||||
model: &'a Model,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
#[allow(unused)]
|
|
||||||
fn draw_model_instanced_with_material(
|
|
||||||
&mut self,
|
|
||||||
model: &'a Model,
|
|
||||||
material: &'a Material,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> DrawModel<'b> for wgpu::RenderPass<'a>
|
|
||||||
where
|
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
fn draw_mesh(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'b Mesh,
|
|
||||||
material: &'b Material,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
self.draw_mesh_instanced(
|
|
||||||
mesh,
|
|
||||||
material,
|
|
||||||
0..1,
|
|
||||||
camera_bind_group,
|
|
||||||
light_bind_group,
|
|
||||||
environment_bind_group,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_mesh_instanced(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'b Mesh,
|
|
||||||
material: &'b Material,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
|
||||||
self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
|
||||||
self.set_bind_group(0, &material.bind_group, &[]);
|
|
||||||
self.set_bind_group(1, camera_bind_group, &[]);
|
|
||||||
self.set_bind_group(2, light_bind_group, &[]);
|
|
||||||
self.set_bind_group(3, environment_bind_group, &[]);
|
|
||||||
self.draw_indexed(0..mesh.num_elements, 0, instances);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_model(
|
|
||||||
&mut self,
|
|
||||||
model: &'b Model,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
self.draw_model_instanced(
|
|
||||||
model,
|
|
||||||
0..1,
|
|
||||||
camera_bind_group,
|
|
||||||
light_bind_group,
|
|
||||||
environment_bind_group,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_model_instanced(
|
|
||||||
&mut self,
|
|
||||||
model: &'b Model,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'b wgpu::BindGroup, // NEW!
|
|
||||||
) {
|
|
||||||
for mesh in &model.meshes {
|
|
||||||
let material = &model.materials[mesh.material];
|
|
||||||
self.draw_mesh_instanced(
|
|
||||||
mesh,
|
|
||||||
material,
|
|
||||||
instances.clone(),
|
|
||||||
camera_bind_group,
|
|
||||||
light_bind_group,
|
|
||||||
environment_bind_group,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_model_instanced_with_material(
|
|
||||||
&mut self,
|
|
||||||
model: &'b Model,
|
|
||||||
material: &'b Material,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
environment_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
for mesh in &model.meshes {
|
|
||||||
self.draw_mesh_instanced(
|
|
||||||
mesh,
|
|
||||||
material,
|
|
||||||
instances.clone(),
|
|
||||||
camera_bind_group,
|
|
||||||
light_bind_group,
|
|
||||||
environment_bind_group,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait DrawLight<'a> {
|
|
||||||
#[allow(unused)]
|
|
||||||
fn draw_light_mesh(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'a Mesh,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
fn draw_light_mesh_instanced(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'a Mesh,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
|
|
||||||
fn draw_light_model(
|
|
||||||
&mut self,
|
|
||||||
model: &'a Model,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
fn draw_light_model_instanced(
|
|
||||||
&mut self,
|
|
||||||
model: &'a Model,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'a wgpu::BindGroup,
|
|
||||||
light_bind_group: &'a wgpu::BindGroup,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b> DrawLight<'b> for wgpu::RenderPass<'a>
|
|
||||||
where
|
|
||||||
'b: 'a,
|
|
||||||
{
|
|
||||||
fn draw_light_mesh(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'b Mesh,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
self.draw_light_mesh_instanced(mesh, 0..1, camera_bind_group, light_bind_group);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_light_mesh_instanced(
|
|
||||||
&mut self,
|
|
||||||
mesh: &'b Mesh,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
|
|
||||||
self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
|
|
||||||
self.set_bind_group(0, camera_bind_group, &[]);
|
|
||||||
self.set_bind_group(1, light_bind_group, &[]);
|
|
||||||
self.draw_indexed(0..mesh.num_elements, 0, instances);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn draw_light_model(
|
|
||||||
&mut self,
|
|
||||||
model: &'b Model,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
self.draw_light_model_instanced(model, 0..1, camera_bind_group, light_bind_group);
|
|
||||||
}
|
|
||||||
fn draw_light_model_instanced(
|
|
||||||
&mut self,
|
|
||||||
model: &'b Model,
|
|
||||||
instances: Range<u32>,
|
|
||||||
camera_bind_group: &'b wgpu::BindGroup,
|
|
||||||
light_bind_group: &'b wgpu::BindGroup,
|
|
||||||
) {
|
|
||||||
for mesh in &model.meshes {
|
|
||||||
self.draw_light_mesh_instanced(
|
|
||||||
mesh,
|
|
||||||
instances.clone(),
|
|
||||||
camera_bind_group,
|
|
||||||
light_bind_group,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
347
src/resources.rs
347
src/resources.rs
@ -1,347 +0,0 @@
|
|||||||
use std::io::{BufReader, Cursor};
|
|
||||||
|
|
||||||
use cfg_if::cfg_if;
|
|
||||||
use image::codecs::hdr::HdrDecoder;
|
|
||||||
use wgpu::util::DeviceExt;
|
|
||||||
|
|
||||||
use crate::{model, texture};
|
|
||||||
|
|
||||||
pub async fn load_string(file_name: &str) -> anyhow::Result<String> {
|
|
||||||
let path = std::path::Path::new(env!("OUT_DIR"))
|
|
||||||
.join("res")
|
|
||||||
.join(file_name);
|
|
||||||
let txt = std::fs::read_to_string(path)?;
|
|
||||||
|
|
||||||
Ok(txt)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_binary(file_name: &str) -> anyhow::Result<Vec<u8>> {
|
|
||||||
let path = std::path::Path::new(env!("OUT_DIR"))
|
|
||||||
.join("res")
|
|
||||||
.join(file_name);
|
|
||||||
let data = std::fs::read(path)?;
|
|
||||||
|
|
||||||
Ok(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_texture(
|
|
||||||
file_name: &str,
|
|
||||||
is_normal_map: bool,
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
) -> anyhow::Result<texture::Texture> {
|
|
||||||
let data = load_binary(file_name).await?;
|
|
||||||
texture::Texture::from_bytes(device, queue, &data, file_name, is_normal_map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_model(
|
|
||||||
file_name: &str,
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
layout: &wgpu::BindGroupLayout,
|
|
||||||
) -> anyhow::Result<model::Model> {
|
|
||||||
let obj_text = load_string(file_name).await?;
|
|
||||||
let obj_cursor = Cursor::new(obj_text);
|
|
||||||
let mut obj_reader = BufReader::new(obj_cursor);
|
|
||||||
|
|
||||||
let (models, obj_materials) = tobj::load_obj_buf_async(
|
|
||||||
&mut obj_reader,
|
|
||||||
&tobj::LoadOptions {
|
|
||||||
triangulate: true,
|
|
||||||
single_index: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
|p| async move {
|
|
||||||
let mat_text = load_string(&p).await.unwrap();
|
|
||||||
tobj::load_mtl_buf(&mut BufReader::new(Cursor::new(mat_text)))
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let mut materials = Vec::new();
|
|
||||||
for m in obj_materials? {
|
|
||||||
let diffuse_texture = load_texture(&m.diffuse_texture, false, device, queue).await?;
|
|
||||||
let normal_texture = load_texture(&m.normal_texture, true, device, queue).await?;
|
|
||||||
|
|
||||||
materials.push(model::Material::new(
|
|
||||||
device,
|
|
||||||
&m.name,
|
|
||||||
diffuse_texture,
|
|
||||||
normal_texture,
|
|
||||||
layout,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let meshes = models
|
|
||||||
.into_iter()
|
|
||||||
.map(|m| {
|
|
||||||
let mut vertices = (0..m.mesh.positions.len() / 3)
|
|
||||||
.map(|i| model::ModelVertex {
|
|
||||||
position: [
|
|
||||||
m.mesh.positions[i * 3],
|
|
||||||
m.mesh.positions[i * 3 + 1],
|
|
||||||
m.mesh.positions[i * 3 + 2],
|
|
||||||
],
|
|
||||||
tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]],
|
|
||||||
normal: [
|
|
||||||
m.mesh.normals[i * 3],
|
|
||||||
m.mesh.normals[i * 3 + 1],
|
|
||||||
m.mesh.normals[i * 3 + 2],
|
|
||||||
],
|
|
||||||
// We'll calculate these later
|
|
||||||
tangent: [0.0; 3],
|
|
||||||
bitangent: [0.0; 3],
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
let indices = &m.mesh.indices;
|
|
||||||
let mut triangles_included = vec![0; vertices.len()];
|
|
||||||
|
|
||||||
// Calculate tangents and bitangets. We're going to
|
|
||||||
// use the triangles, so we need to loop through the
|
|
||||||
// indices in chunks of 3
|
|
||||||
for c in indices.chunks(3) {
|
|
||||||
let v0 = vertices[c[0] as usize];
|
|
||||||
let v1 = vertices[c[1] as usize];
|
|
||||||
let v2 = vertices[c[2] as usize];
|
|
||||||
|
|
||||||
let pos0: cgmath::Vector3<_> = v0.position.into();
|
|
||||||
let pos1: cgmath::Vector3<_> = v1.position.into();
|
|
||||||
let pos2: cgmath::Vector3<_> = v2.position.into();
|
|
||||||
|
|
||||||
let uv0: cgmath::Vector2<_> = v0.tex_coords.into();
|
|
||||||
let uv1: cgmath::Vector2<_> = v1.tex_coords.into();
|
|
||||||
let uv2: cgmath::Vector2<_> = v2.tex_coords.into();
|
|
||||||
|
|
||||||
// Calculate the edges of the triangle
|
|
||||||
let delta_pos1 = pos1 - pos0;
|
|
||||||
let delta_pos2 = pos2 - pos0;
|
|
||||||
|
|
||||||
// This will give us a direction to calculate the
|
|
||||||
// tangent and bitangent
|
|
||||||
let delta_uv1 = uv1 - uv0;
|
|
||||||
let delta_uv2 = uv2 - uv0;
|
|
||||||
|
|
||||||
// Solving the following system of equations will
|
|
||||||
// give us the tangent and bitangent.
|
|
||||||
// delta_pos1 = delta_uv1.x * T + delta_u.y * B
|
|
||||||
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
|
|
||||||
// Luckily, the place I found this equation provided
|
|
||||||
// the solution!
|
|
||||||
let r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
|
|
||||||
let tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
|
|
||||||
// We flip the bitangent to enable right-handed normal
|
|
||||||
// maps with wgpu texture coordinate system
|
|
||||||
let bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * -r;
|
|
||||||
|
|
||||||
// We'll use the same tangent/bitangent for each vertex in the triangle
|
|
||||||
vertices[c[0] as usize].tangent =
|
|
||||||
(tangent + cgmath::Vector3::from(vertices[c[0] as usize].tangent)).into();
|
|
||||||
vertices[c[1] as usize].tangent =
|
|
||||||
(tangent + cgmath::Vector3::from(vertices[c[1] as usize].tangent)).into();
|
|
||||||
vertices[c[2] as usize].tangent =
|
|
||||||
(tangent + cgmath::Vector3::from(vertices[c[2] as usize].tangent)).into();
|
|
||||||
vertices[c[0] as usize].bitangent =
|
|
||||||
(bitangent + cgmath::Vector3::from(vertices[c[0] as usize].bitangent)).into();
|
|
||||||
vertices[c[1] as usize].bitangent =
|
|
||||||
(bitangent + cgmath::Vector3::from(vertices[c[1] as usize].bitangent)).into();
|
|
||||||
vertices[c[2] as usize].bitangent =
|
|
||||||
(bitangent + cgmath::Vector3::from(vertices[c[2] as usize].bitangent)).into();
|
|
||||||
|
|
||||||
// Used to average the tangents/bitangents
|
|
||||||
triangles_included[c[0] as usize] += 1;
|
|
||||||
triangles_included[c[1] as usize] += 1;
|
|
||||||
triangles_included[c[2] as usize] += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Average the tangents/bitangents
|
|
||||||
for (i, n) in triangles_included.into_iter().enumerate() {
|
|
||||||
let denom = 1.0 / n as f32;
|
|
||||||
let v = &mut vertices[i];
|
|
||||||
v.tangent = (cgmath::Vector3::from(v.tangent) * denom).into();
|
|
||||||
v.bitangent = (cgmath::Vector3::from(v.bitangent) * denom).into();
|
|
||||||
}
|
|
||||||
|
|
||||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some(&format!("{:?} Vertex Buffer", file_name)),
|
|
||||||
contents: bytemuck::cast_slice(&vertices),
|
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
|
||||||
});
|
|
||||||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
|
||||||
label: Some(&format!("{:?} Index Buffer", file_name)),
|
|
||||||
contents: bytemuck::cast_slice(&m.mesh.indices),
|
|
||||||
usage: wgpu::BufferUsages::INDEX,
|
|
||||||
});
|
|
||||||
|
|
||||||
model::Mesh {
|
|
||||||
name: file_name.to_string(),
|
|
||||||
vertex_buffer,
|
|
||||||
index_buffer,
|
|
||||||
num_elements: m.mesh.indices.len() as u32,
|
|
||||||
material: m.mesh.material_id.unwrap_or(0),
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
|
|
||||||
Ok(model::Model { meshes, materials })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HdrLoader {
|
|
||||||
texture_format: wgpu::TextureFormat,
|
|
||||||
equirect_layout: wgpu::BindGroupLayout,
|
|
||||||
equirect_to_cubemap: wgpu::ComputePipeline,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HdrLoader {
|
|
||||||
pub fn new(device: &wgpu::Device) -> Self {
|
|
||||||
let module = device.create_shader_module(wgpu::include_wgsl!("equirectangular.wgsl"));
|
|
||||||
let texture_format = wgpu::TextureFormat::Rgba32Float;
|
|
||||||
let equirect_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
|
||||||
label: Some("HdrLoader::equirect_layout"),
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 0,
|
|
||||||
visibility: wgpu::ShaderStages::COMPUTE,
|
|
||||||
ty: wgpu::BindingType::Texture {
|
|
||||||
sample_type: wgpu::TextureSampleType::Float { filterable: false },
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2,
|
|
||||||
multisampled: false,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
wgpu::BindGroupLayoutEntry {
|
|
||||||
binding: 1,
|
|
||||||
visibility: wgpu::ShaderStages::COMPUTE,
|
|
||||||
ty: wgpu::BindingType::StorageTexture {
|
|
||||||
access: wgpu::StorageTextureAccess::WriteOnly,
|
|
||||||
format: texture_format,
|
|
||||||
view_dimension: wgpu::TextureViewDimension::D2Array,
|
|
||||||
},
|
|
||||||
count: None,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
|
||||||
label: None,
|
|
||||||
bind_group_layouts: &[&equirect_layout],
|
|
||||||
push_constant_ranges: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let equirect_to_cubemap =
|
|
||||||
device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
|
|
||||||
label: Some("equirect_to_cubemap"),
|
|
||||||
layout: Some(&pipeline_layout),
|
|
||||||
module: &module,
|
|
||||||
entry_point: "compute_equirect_to_cubemap",
|
|
||||||
compilation_options: Default::default(),
|
|
||||||
cache: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
equirect_to_cubemap,
|
|
||||||
texture_format,
|
|
||||||
equirect_layout,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_equirectangular_bytes(
|
|
||||||
&self,
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
data: &[u8],
|
|
||||||
dst_size: u32,
|
|
||||||
label: Option<&str>,
|
|
||||||
) -> anyhow::Result<texture::CubeTexture> {
|
|
||||||
let hdr_decoder = HdrDecoder::new(Cursor::new(data))?;
|
|
||||||
let meta = hdr_decoder.metadata();
|
|
||||||
|
|
||||||
let pixels = {
|
|
||||||
let mut pixels = vec![[0.0, 0.0, 0.0, 0.0]; meta.width as usize * meta.height as usize];
|
|
||||||
hdr_decoder.read_image_transform(
|
|
||||||
|pix| {
|
|
||||||
let rgb = pix.to_hdr();
|
|
||||||
[rgb.0[0], rgb.0[1], rgb.0[2], 1.0f32]
|
|
||||||
},
|
|
||||||
&mut pixels[..],
|
|
||||||
)?;
|
|
||||||
pixels
|
|
||||||
};
|
|
||||||
|
|
||||||
let src = texture::Texture::create_2d_texture(
|
|
||||||
device,
|
|
||||||
meta.width,
|
|
||||||
meta.height,
|
|
||||||
self.texture_format,
|
|
||||||
wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
|
||||||
wgpu::FilterMode::Linear,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
queue.write_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
texture: &src.texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: wgpu::Origin3d::ZERO,
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
},
|
|
||||||
&bytemuck::cast_slice(&pixels),
|
|
||||||
wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(src.size.width * std::mem::size_of::<[f32; 4]>() as u32),
|
|
||||||
rows_per_image: Some(src.size.height),
|
|
||||||
},
|
|
||||||
src.size,
|
|
||||||
);
|
|
||||||
|
|
||||||
let dst = texture::CubeTexture::create_2d(
|
|
||||||
device,
|
|
||||||
dst_size,
|
|
||||||
dst_size,
|
|
||||||
self.texture_format,
|
|
||||||
1,
|
|
||||||
wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
|
|
||||||
wgpu::FilterMode::Nearest,
|
|
||||||
label,
|
|
||||||
);
|
|
||||||
|
|
||||||
let dst_view = dst.texture().create_view(&wgpu::TextureViewDescriptor {
|
|
||||||
label,
|
|
||||||
dimension: Some(wgpu::TextureViewDimension::D2Array),
|
|
||||||
// array_layer_count: Some(6),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
|
||||||
label,
|
|
||||||
layout: &self.equirect_layout,
|
|
||||||
entries: &[
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 0,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&src.view),
|
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: wgpu::BindingResource::TextureView(&dst_view),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut encoder = device.create_command_encoder(&Default::default());
|
|
||||||
let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
|
|
||||||
label,
|
|
||||||
timestamp_writes: None,
|
|
||||||
});
|
|
||||||
|
|
||||||
let num_workgroups = (dst_size + 15) / 16;
|
|
||||||
pass.set_pipeline(&self.equirect_to_cubemap);
|
|
||||||
pass.set_bind_group(0, &bind_group, &[]);
|
|
||||||
pass.dispatch_workgroups(num_workgroups, num_workgroups, 6);
|
|
||||||
|
|
||||||
drop(pass);
|
|
||||||
|
|
||||||
queue.submit([encoder.finish()]);
|
|
||||||
|
|
||||||
Ok(dst)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
139
src/shader.wgsl
139
src/shader.wgsl
@ -1,139 +0,0 @@
|
|||||||
// Vertex shader
|
|
||||||
|
|
||||||
struct Camera {
|
|
||||||
view_pos: vec4<f32>,
|
|
||||||
view: mat4x4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
inv_proj: mat4x4<f32>,
|
|
||||||
inv_view: mat4x4<f32>,
|
|
||||||
}
|
|
||||||
@group(1) @binding(0)
|
|
||||||
var<uniform> camera: Camera;
|
|
||||||
|
|
||||||
struct Light {
|
|
||||||
position: vec3<f32>,
|
|
||||||
color: vec3<f32>,
|
|
||||||
}
|
|
||||||
@group(2) @binding(0)
|
|
||||||
var<uniform> light: Light;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position: vec3<f32>,
|
|
||||||
@location(1) tex_coords: vec2<f32>,
|
|
||||||
@location(2) normal: vec3<f32>,
|
|
||||||
@location(3) tangent: vec3<f32>,
|
|
||||||
@location(4) bitangent: vec3<f32>,
|
|
||||||
}
|
|
||||||
struct InstanceInput {
|
|
||||||
@location(5) model_matrix_0: vec4<f32>,
|
|
||||||
@location(6) model_matrix_1: vec4<f32>,
|
|
||||||
@location(7) model_matrix_2: vec4<f32>,
|
|
||||||
@location(8) model_matrix_3: vec4<f32>,
|
|
||||||
@location(9) normal_matrix_0: vec3<f32>,
|
|
||||||
@location(10) normal_matrix_1: vec3<f32>,
|
|
||||||
@location(11) normal_matrix_2: vec3<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
|
||||||
@location(0) tex_coords: vec2<f32>,
|
|
||||||
// Updated!
|
|
||||||
@location(1) world_position: vec3<f32>,
|
|
||||||
@location(2) world_view_position: vec3<f32>,
|
|
||||||
@location(3) world_light_position: vec3<f32>,
|
|
||||||
@location(4) world_normal: vec3<f32>,
|
|
||||||
@location(5) world_tangent: vec3<f32>,
|
|
||||||
@location(6) world_bitangent: vec3<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(
|
|
||||||
model: VertexInput,
|
|
||||||
instance: InstanceInput,
|
|
||||||
) -> VertexOutput {
|
|
||||||
let model_matrix = mat4x4<f32>(
|
|
||||||
instance.model_matrix_0,
|
|
||||||
instance.model_matrix_1,
|
|
||||||
instance.model_matrix_2,
|
|
||||||
instance.model_matrix_3,
|
|
||||||
);
|
|
||||||
let normal_matrix = mat3x3<f32>(
|
|
||||||
instance.normal_matrix_0,
|
|
||||||
instance.normal_matrix_1,
|
|
||||||
instance.normal_matrix_2,
|
|
||||||
);
|
|
||||||
|
|
||||||
// UPDATED!
|
|
||||||
let world_position = model_matrix * vec4<f32>(model.position, 1.0);
|
|
||||||
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.clip_position = camera.view_proj * world_position;
|
|
||||||
out.tex_coords = model.tex_coords;
|
|
||||||
out.world_normal = normalize(normal_matrix * model.normal);
|
|
||||||
out.world_tangent = normalize(normal_matrix * model.tangent);
|
|
||||||
out.world_bitangent = normalize(normal_matrix * model.bitangent);
|
|
||||||
out.world_position = world_position.xyz;
|
|
||||||
out.world_view_position = camera.view_pos.xyz;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fragment shader
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var t_diffuse: texture_2d<f32>;
|
|
||||||
@group(0)@binding(1)
|
|
||||||
var s_diffuse: sampler;
|
|
||||||
@group(0)@binding(2)
|
|
||||||
var t_normal: texture_2d<f32>;
|
|
||||||
@group(0) @binding(3)
|
|
||||||
var s_normal: sampler;
|
|
||||||
|
|
||||||
@group(3)
|
|
||||||
@binding(0)
|
|
||||||
var env_map: texture_cube<f32>;
|
|
||||||
@group(3)
|
|
||||||
@binding(1)
|
|
||||||
var env_sampler: sampler;
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
|
||||||
let object_normal: vec4<f32> = textureSample(t_normal, s_normal, in.tex_coords);
|
|
||||||
|
|
||||||
// NEW!
|
|
||||||
// Adjust the tangent and bitangent using the Gramm-Schmidt process
|
|
||||||
// This makes sure that they are perpedicular to each other and the
|
|
||||||
// normal of the surface.
|
|
||||||
let world_tangent = normalize(in.world_tangent - dot(in.world_tangent, in.world_normal) * in.world_normal);
|
|
||||||
let world_bitangent = cross(world_tangent, in.world_normal);
|
|
||||||
|
|
||||||
// Convert the normal sample to world space
|
|
||||||
let TBN = mat3x3(
|
|
||||||
world_tangent,
|
|
||||||
world_bitangent,
|
|
||||||
in.world_normal,
|
|
||||||
);
|
|
||||||
let tangent_normal = object_normal.xyz * 2.0 - 1.0;
|
|
||||||
let world_normal = TBN * tangent_normal;
|
|
||||||
|
|
||||||
// Create the lighting vectors
|
|
||||||
let light_dir = normalize(light.position - in.world_position);
|
|
||||||
let view_dir = normalize(in.world_view_position - in.world_position);
|
|
||||||
let half_dir = normalize(view_dir + light_dir);
|
|
||||||
|
|
||||||
let diffuse_strength = max(dot(world_normal, light_dir), 0.0);
|
|
||||||
let diffuse_color = light.color * diffuse_strength;
|
|
||||||
|
|
||||||
let specular_strength = pow(max(dot(world_normal, half_dir), 0.0), 32.0);
|
|
||||||
let specular_color = specular_strength * light.color;
|
|
||||||
|
|
||||||
// NEW!
|
|
||||||
// Calculate reflections
|
|
||||||
let world_reflect = reflect(-view_dir, world_normal);
|
|
||||||
let reflection = textureSample(env_map, env_sampler, world_reflect).rgb;
|
|
||||||
let shininess = 0.1;
|
|
||||||
|
|
||||||
let result = (diffuse_color + specular_color) * object_color.xyz + reflection * shininess;
|
|
||||||
|
|
||||||
return vec4<f32>(result, object_color.a);
|
|
||||||
}
|
|
||||||
45
src/sky.wgsl
45
src/sky.wgsl
@ -1,45 +0,0 @@
|
|||||||
struct Camera {
|
|
||||||
view_pos: vec4<f32>,
|
|
||||||
view: mat4x4<f32>,
|
|
||||||
view_proj: mat4x4<f32>,
|
|
||||||
inv_proj: mat4x4<f32>,
|
|
||||||
inv_view: mat4x4<f32>,
|
|
||||||
}
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> camera: Camera;
|
|
||||||
|
|
||||||
@group(1)
|
|
||||||
@binding(0)
|
|
||||||
var env_map: texture_cube<f32>;
|
|
||||||
@group(1)
|
|
||||||
@binding(1)
|
|
||||||
var env_sampler: sampler;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) frag_position: vec4<f32>,
|
|
||||||
@location(0) clip_position: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(
|
|
||||||
@builtin(vertex_index) id: u32,
|
|
||||||
) -> VertexOutput {
|
|
||||||
let uv = vec2<f32>(vec2<u32>(
|
|
||||||
id & 1u,
|
|
||||||
(id >> 1u) & 1u,
|
|
||||||
));
|
|
||||||
var out: VertexOutput;
|
|
||||||
out.clip_position = vec4(uv * 4.0 - 1.0, 1.0, 1.0);
|
|
||||||
out.frag_position = out.clip_position;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
let view_pos_homogeneous = camera.inv_proj * in.clip_position;
|
|
||||||
let view_ray_direction = view_pos_homogeneous.xyz / view_pos_homogeneous.w;
|
|
||||||
var ray_direction = normalize((camera.inv_view * vec4(view_ray_direction, 0.0)).xyz);
|
|
||||||
|
|
||||||
let sample = textureSample(env_map, env_sampler, ray_direction);
|
|
||||||
return sample;
|
|
||||||
}
|
|
||||||
255
src/texture.rs
255
src/texture.rs
@ -1,255 +0,0 @@
|
|||||||
use anyhow::*;
|
|
||||||
use image::GenericImageView;
|
|
||||||
|
|
||||||
pub struct Texture {
|
|
||||||
#[allow(unused)]
|
|
||||||
pub texture: wgpu::Texture,
|
|
||||||
pub view: wgpu::TextureView,
|
|
||||||
pub sampler: wgpu::Sampler,
|
|
||||||
pub size: wgpu::Extent3d,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Texture {
|
|
||||||
pub const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
|
|
||||||
|
|
||||||
pub fn create_depth_texture(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
config: &wgpu::SurfaceConfiguration,
|
|
||||||
label: &str,
|
|
||||||
) -> Self {
|
|
||||||
let size = wgpu::Extent3d {
|
|
||||||
width: config.width.max(1),
|
|
||||||
height: config.height.max(1),
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
};
|
|
||||||
let desc = wgpu::TextureDescriptor {
|
|
||||||
label: Some(label),
|
|
||||||
size,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format: Self::DEPTH_FORMAT,
|
|
||||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
|
|
||||||
view_formats: &[Self::DEPTH_FORMAT],
|
|
||||||
};
|
|
||||||
let texture = device.create_texture(&desc);
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
|
||||||
mag_filter: wgpu::FilterMode::Linear,
|
|
||||||
min_filter: wgpu::FilterMode::Linear,
|
|
||||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
|
||||||
compare: Some(wgpu::CompareFunction::LessEqual),
|
|
||||||
lod_min_clamp: 0.0,
|
|
||||||
lod_max_clamp: 100.0,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
texture,
|
|
||||||
view,
|
|
||||||
sampler,
|
|
||||||
size, // NEW!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn from_bytes(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
bytes: &[u8],
|
|
||||||
label: &str,
|
|
||||||
is_normal_map: bool,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let img = image::load_from_memory(bytes)?;
|
|
||||||
Self::from_image(device, queue, &img, Some(label), is_normal_map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_image(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
queue: &wgpu::Queue,
|
|
||||||
img: &image::DynamicImage,
|
|
||||||
label: Option<&str>,
|
|
||||||
is_normal_map: bool,
|
|
||||||
) -> Result<Self> {
|
|
||||||
let dimensions = img.dimensions();
|
|
||||||
let rgba = img.to_rgba8();
|
|
||||||
|
|
||||||
let format = if is_normal_map {
|
|
||||||
wgpu::TextureFormat::Rgba8Unorm
|
|
||||||
} else {
|
|
||||||
wgpu::TextureFormat::Rgba8UnormSrgb
|
|
||||||
};
|
|
||||||
let usage = wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST;
|
|
||||||
let size = wgpu::Extent3d {
|
|
||||||
width: img.width(),
|
|
||||||
height: img.height(),
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
};
|
|
||||||
let texture = Self::create_2d_texture(
|
|
||||||
device,
|
|
||||||
size.width,
|
|
||||||
size.height,
|
|
||||||
format,
|
|
||||||
usage,
|
|
||||||
wgpu::FilterMode::Linear,
|
|
||||||
label,
|
|
||||||
);
|
|
||||||
|
|
||||||
queue.write_texture(
|
|
||||||
wgpu::ImageCopyTexture {
|
|
||||||
aspect: wgpu::TextureAspect::All,
|
|
||||||
texture: &texture.texture,
|
|
||||||
mip_level: 0,
|
|
||||||
origin: wgpu::Origin3d::ZERO,
|
|
||||||
},
|
|
||||||
&rgba,
|
|
||||||
wgpu::ImageDataLayout {
|
|
||||||
offset: 0,
|
|
||||||
bytes_per_row: Some(4 * dimensions.0),
|
|
||||||
rows_per_image: Some(dimensions.1),
|
|
||||||
},
|
|
||||||
size,
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(texture)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn create_2d_texture(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
usage: wgpu::TextureUsages,
|
|
||||||
mag_filter: wgpu::FilterMode,
|
|
||||||
label: Option<&str>,
|
|
||||||
) -> Self {
|
|
||||||
let size = wgpu::Extent3d {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
depth_or_array_layers: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::create_texture(
|
|
||||||
device,
|
|
||||||
label,
|
|
||||||
size,
|
|
||||||
format,
|
|
||||||
usage,
|
|
||||||
wgpu::TextureDimension::D2,
|
|
||||||
mag_filter,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn create_texture(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
label: Option<&str>,
|
|
||||||
size: wgpu::Extent3d,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
usage: wgpu::TextureUsages,
|
|
||||||
dimension: wgpu::TextureDimension,
|
|
||||||
mag_filter: wgpu::FilterMode,
|
|
||||||
) -> Self {
|
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label,
|
|
||||||
size,
|
|
||||||
mip_level_count: 1,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension,
|
|
||||||
format,
|
|
||||||
usage,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
|
||||||
mag_filter,
|
|
||||||
min_filter: wgpu::FilterMode::Nearest,
|
|
||||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
texture,
|
|
||||||
view,
|
|
||||||
sampler,
|
|
||||||
size,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CubeTexture {
|
|
||||||
texture: wgpu::Texture,
|
|
||||||
sampler: wgpu::Sampler,
|
|
||||||
view: wgpu::TextureView,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CubeTexture {
|
|
||||||
pub fn create_2d(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
width: u32,
|
|
||||||
height: u32,
|
|
||||||
format: wgpu::TextureFormat,
|
|
||||||
mip_level_count: u32,
|
|
||||||
usage: wgpu::TextureUsages,
|
|
||||||
mag_filter: wgpu::FilterMode,
|
|
||||||
label: Option<&str>,
|
|
||||||
) -> Self {
|
|
||||||
let texture = device.create_texture(&wgpu::TextureDescriptor {
|
|
||||||
label,
|
|
||||||
size: wgpu::Extent3d {
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
// A cube has 6 sides, so we need 6 layers
|
|
||||||
depth_or_array_layers: 6,
|
|
||||||
},
|
|
||||||
mip_level_count,
|
|
||||||
sample_count: 1,
|
|
||||||
dimension: wgpu::TextureDimension::D2,
|
|
||||||
format,
|
|
||||||
usage,
|
|
||||||
view_formats: &[],
|
|
||||||
});
|
|
||||||
|
|
||||||
let view = texture.create_view(&wgpu::TextureViewDescriptor {
|
|
||||||
label,
|
|
||||||
dimension: Some(wgpu::TextureViewDimension::Cube),
|
|
||||||
array_layer_count: Some(6),
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
|
||||||
label,
|
|
||||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
|
||||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
|
||||||
mag_filter,
|
|
||||||
min_filter: wgpu::FilterMode::Nearest,
|
|
||||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
|
||||||
..Default::default()
|
|
||||||
});
|
|
||||||
|
|
||||||
Self {
|
|
||||||
texture,
|
|
||||||
sampler,
|
|
||||||
view,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn texture(&self) -> &wgpu::Texture {
|
|
||||||
&self.texture
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn view(&self) -> &wgpu::TextureView {
|
|
||||||
&self.view
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sampler(&self) -> &wgpu::Sampler {
|
|
||||||
&self.sampler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user