Compare commits
1 commit
master
...
funny_voxe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cb12591f5d |
30 changed files with 4678 additions and 1963 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,2 @@
|
|||
target
|
||||
.DS_Store
|
||||
Cargo.lock
|
||||
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Ignored default folder with query files
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
6
.idea/Pool.iml
generated
6
.idea/Pool.iml
generated
|
|
@ -3,7 +3,13 @@
|
|||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/stupid_display/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/mars/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/lang/src" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/stupid_display/target" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/src/mars/target" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/lang/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
|
|
|||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
8
.idea/dictionaries/project.xml
generated
Normal file
8
.idea/dictionaries/project.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>forloop</w>
|
||||
<w>vmcase</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
||||
3033
Cargo.lock
generated
Normal file
3033
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
14
Cargo.toml
14
Cargo.toml
|
|
@ -1,18 +1,14 @@
|
|||
[package]
|
||||
#![recursion_limit = "256"]
|
||||
name = "pool"
|
||||
name = "game"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
big_ids = []
|
||||
check_ids = []
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
[[bin]]
|
||||
name = "pool"
|
||||
name = "game"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
|
|
@ -21,17 +17,19 @@ anyhow = "1.0"
|
|||
winit = { version = "0.30", features = ["android-native-activity"] }
|
||||
env_logger = "0.11.10"
|
||||
log = "0.4"
|
||||
wgpu = "29.0.4"
|
||||
wgpu = "29.0.3"
|
||||
pollster = "0.4.0"
|
||||
glam = { version = "0.33.3", features = [ "bytemuck" ] }
|
||||
console_error_panic_hook = "0.1.7"
|
||||
bytemuck = { version = "1.25.0", features = [ "derive" ]}
|
||||
image = { version = "0.24", default-features = false, features = ["png", "jpeg"]}
|
||||
mars = { path = "../Mars" }
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.6"
|
||||
wgpu = { version = "30.0.1", features = ["webgl"]}
|
||||
wgpu = { version = "29.0.3", features = ["webgl"]}
|
||||
wasm-bindgen = "0.2.121"
|
||||
wasm-bindgen-futures = "0.4.71"
|
||||
console_log = "1.0.0"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
_Pool. Hop in, the water's warm._
|
||||
|
||||
Pool is a lightweight web-enabled multipurpose engine for UI apps and multiplayer games with physics.
|
||||
Pool is a lightweight web-enabled multipurpose engine for UI apps and multiplayer games with physics designed to push the boundaries of what defines a contemporary user experience.
|
||||
|
||||
Taking inspiration from the Roblox game engine, Pool ships with its own scripting language, Mars, and shares a similar instance-service model.
|
||||
|
||||
|
|
|
|||
210
src/app.rs
210
src/app.rs
|
|
@ -1,8 +1,10 @@
|
|||
use std::collections::HashMap;
|
||||
// Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu
|
||||
use crate::render::{loader, Renderer};
|
||||
use crate::world::{World, ObjectData};
|
||||
use glam::{Affine3, Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4};
|
||||
use crate::render::Renderer;
|
||||
use crate::world::{SimpleObject, World};
|
||||
use crate::permutation_array;
|
||||
use crate::noise2d;
|
||||
use glam::{Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4};
|
||||
use std::sync::Arc;
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
|
@ -16,12 +18,8 @@ use winit::{
|
|||
keyboard::{KeyCode, PhysicalKey},
|
||||
window::Window,
|
||||
};
|
||||
use crate::list::Id;
|
||||
use crate::render::instance::{Material, ModelData};
|
||||
use crate::render::instance::material::MaterialProperties;
|
||||
use crate::render::texture::Texture;
|
||||
use crate::state;
|
||||
use crate::world::debug::OctreeDebug;
|
||||
use crate::noise2d::Noise2D;
|
||||
use crate::permutation_array::PermutationArray;
|
||||
|
||||
struct Controller {
|
||||
buttons: HashMap<MouseButton, bool>,
|
||||
|
|
@ -40,66 +38,76 @@ impl Controller {
|
|||
}
|
||||
|
||||
pub struct AppState {
|
||||
state: state::State,
|
||||
world: Id<World>,
|
||||
debug: OctreeDebug,
|
||||
debug_model: ModelData,
|
||||
world: World,
|
||||
controller: Controller,
|
||||
window: Arc<Window>,
|
||||
clients: Vec<SimpleObject>,
|
||||
chunks: Vec<(i32, i32)>,
|
||||
Permutation: PermutationArray,
|
||||
}
|
||||
|
||||
const BLOCKS: i32 = 2;
|
||||
const BLOCKS: i32 = 100;
|
||||
|
||||
|
||||
impl AppState {
|
||||
// We don't need this to be async right now,
|
||||
// but we will in the next tutorial
|
||||
pub async fn new(window: Arc<Window>) -> Result<AppState, Box<dyn std::error::Error>> {
|
||||
let mut state = state::State::new();
|
||||
let (world,debug,debug_model) = {
|
||||
let mut world = state.worlds.make(World::new());
|
||||
state.renderer = Some(Renderer::new(&window).await?);
|
||||
let renderer = state.renderer.as_mut().unwrap();
|
||||
let debug_file = loader::load_from_gltf(renderer,include_bytes!("assets/debug.glb"));
|
||||
let debug_model = debug_file.first_object().unwrap().model.unwrap();
|
||||
let mut world = World::new();
|
||||
world.add_renderer(Renderer::new(&window).await?);
|
||||
let mut Permutation: PermutationArray = PermutationArray::new();
|
||||
Permutation.generate_permutation_array(0);
|
||||
let mut chunks = Vec::new();
|
||||
let mut clients = Vec::new();
|
||||
{
|
||||
let file = loader::load_from_gltf(renderer,include_bytes!("assets/sphere.glb"));
|
||||
let mut block = file.first_object().unwrap();
|
||||
let skybox = Texture::load_from_file_bytes(renderer,include_bytes!("assets/skybox2.png"), None);
|
||||
renderer.set_skybox(skybox.unwrap());
|
||||
let color = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/color.png"), None);
|
||||
let normal = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/normal.png"), None);
|
||||
let roughness = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/roughness.png"), None);
|
||||
let mat = Material::new(renderer, &color.unwrap(), &normal.unwrap(), &roughness.unwrap(), MaterialProperties::default());
|
||||
block.model.as_mut().unwrap().material = mat;
|
||||
for x in -BLOCKS..=BLOCKS {
|
||||
for y in -BLOCKS..=BLOCKS {
|
||||
let id = world.add_object(state.objects.make(block.clone()));
|
||||
let block = state.objects.get(&id);
|
||||
block.set_affine(Affine3::from_translation(
|
||||
Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0)
|
||||
));
|
||||
block.model.as_mut().unwrap().instance.color = Vec4::new(
|
||||
0.3,
|
||||
(x + BLOCKS) as f32 / BLOCKS as f32,
|
||||
(y + BLOCKS) as f32 / BLOCKS as f32,
|
||||
1.0,
|
||||
let file = world
|
||||
.renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.load_from_gltf(include_bytes!("assets/cube.glb"));
|
||||
let block = file.first_object().unwrap();
|
||||
let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes(
|
||||
include_bytes!("assets/skybox1.png"),
|
||||
Some(image::ImageFormat::Png),
|
||||
);
|
||||
world.renderer.as_mut().unwrap().set_skybox(skybox);
|
||||
//let color = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"));
|
||||
//let normal = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"));
|
||||
//let roughness = &renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"));
|
||||
//let mat = renderer.new_material(color,normal,roughness);
|
||||
// for x in -BLOCKS..BLOCKS {
|
||||
// for y in -BLOCKS..BLOCKS {
|
||||
// let block = block.hard_clone();
|
||||
// world.add_object(block.clone());
|
||||
// {
|
||||
// let mut model = block.0.borrow_mut();
|
||||
// model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(
|
||||
// Vec3::new(-x as f32 * 1.0, Noise2D::fractal_noise(x as f64 * 0.1, y as f64*0.1, 5, 10.0, 0.05, &Permutation).round() as i64 as f32, -y as f32 * 1.0),
|
||||
// )
|
||||
// * Mat4::from_rotation_z(0f32 /*(x * y) as f32 / 1.23*/);
|
||||
// model.model.as_mut().unwrap().instance.color = Vec4::new(
|
||||
// 0.3,
|
||||
// (x + BLOCKS) as f32 / BLOCKS as f32,
|
||||
// (y + BLOCKS) as f32 / BLOCKS as f32,
|
||||
// 1.0,
|
||||
// );
|
||||
// }
|
||||
// clients.push(block)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
let debug = OctreeDebug::new();
|
||||
(world.id,debug,debug_model)
|
||||
};
|
||||
Ok(Self {
|
||||
state,
|
||||
world,
|
||||
debug,
|
||||
debug_model,
|
||||
clients,
|
||||
controller: Controller::new(),
|
||||
window,
|
||||
chunks,
|
||||
Permutation
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn bounds(&self) -> (u32, u32) {
|
||||
let size = self.window.inner_size();
|
||||
(size.width, size.height)
|
||||
|
|
@ -107,7 +115,7 @@ impl AppState {
|
|||
|
||||
pub fn resize(&mut self, width: u32, height: u32) {
|
||||
if width > 0 && height > 0 {
|
||||
self.state.renderer.as_mut().unwrap().resize(width, height);
|
||||
self.world.renderer.as_mut().unwrap().resize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +128,7 @@ impl AppState {
|
|||
(KeyCode::Escape, true) => event_loop.exit(),
|
||||
(KeyCode::Space, true) => {}
|
||||
(KeyCode::KeyR, true) => {
|
||||
self.state.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
|
||||
self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
@ -129,7 +137,7 @@ impl AppState {
|
|||
|
||||
fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) {
|
||||
if let Some(true) = self.controller.buttons.get(&MouseButton::Right) {
|
||||
self.state.renderer.as_mut().unwrap().eye.rotate(
|
||||
self.world.renderer.as_mut().unwrap().eye.rotate(
|
||||
position.x as f32 - self.controller.mouse.x,
|
||||
position.y as f32 - self.controller.mouse.y,
|
||||
);
|
||||
|
|
@ -158,6 +166,42 @@ impl App {
|
|||
proxy,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn GenerateChunk(ChunkX: i32, ChunkY: i32, size: i32, state: &mut AppState){
|
||||
if(state.chunks.contains(&(ChunkX, ChunkY))){
|
||||
return;
|
||||
}
|
||||
let file = state.world
|
||||
.renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.load_from_gltf(include_bytes!("assets/cube.glb"));
|
||||
let block = file.first_object().unwrap();
|
||||
|
||||
for x in 0..size {
|
||||
for y in 0..size {
|
||||
let worldPosX = ChunkX * size + x;
|
||||
let worldPosY = ChunkY * size + y;
|
||||
let block = block.hard_clone();
|
||||
state.world.add_object(block.clone());
|
||||
{
|
||||
let mut model = block.0.borrow_mut();
|
||||
model.model.as_mut().unwrap().instance.transform = Mat4::from_translation(
|
||||
Vec3::new(worldPosX as f32 * 1.0, Noise2D::fractal_noise(worldPosX as f64 * 0.25, worldPosY as f64*0.25, 10, 20.0, 0.05, &state.Permutation).round() as i64 as f32, worldPosY as f32 * 1.0),
|
||||
)
|
||||
* Mat4::from_rotation_z(0f32 /*(x * y) as f32 / 1.23*/);
|
||||
model.model.as_mut().unwrap().instance.color = Vec4::new(
|
||||
0.3,
|
||||
(x + size) as f32 / size as f32,
|
||||
(y + size) as f32 / size as f32,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
state.clients.push(block);
|
||||
}
|
||||
}
|
||||
state.chunks.push((ChunkX, ChunkY));
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplicationHandler<AppState> for App {
|
||||
|
|
@ -181,8 +225,6 @@ impl ApplicationHandler<AppState> for App {
|
|||
|
||||
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
|
||||
|
||||
window.set_title("Pool");
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
// If we are not on web we can use pollster to
|
||||
|
|
@ -239,14 +281,15 @@ impl ApplicationHandler<AppState> for App {
|
|||
WindowEvent::CloseRequested => event_loop.exit(),
|
||||
WindowEvent::Resized(size) => state.resize(size.width, size.height),
|
||||
WindowEvent::RedrawRequested => {
|
||||
use std::time::Instant;
|
||||
let now = Instant::now();
|
||||
|
||||
state.update();
|
||||
let mut movement = Vec3::new(0.0, 0.0, 0.0);
|
||||
|
||||
let pressed = |keycode: KeyCode| {
|
||||
matches!(state.controller.keys.get(&keycode), Some(true))
|
||||
if let Some(true) = state.controller.keys.get(&keycode) {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
|
||||
if pressed(KeyCode::KeyA) {
|
||||
|
|
@ -267,12 +310,30 @@ impl ApplicationHandler<AppState> for App {
|
|||
if pressed(KeyCode::KeyQ) {
|
||||
movement.y -= 1.0;
|
||||
}
|
||||
let world = state.state.worlds.get(&state.world);
|
||||
state.debug.set(&mut state.state.objects, world, Some(state.debug_model.clone()));
|
||||
state.debug.register(&mut state.state.objects,state.state.renderer.as_mut().unwrap());
|
||||
world.step(&mut state.state.renderer, &mut state.state.objects, &mut state.state.lights);
|
||||
state.state.renderer.as_mut().unwrap().eye.control(movement * 0.1);
|
||||
match state.state.renderer.as_mut().unwrap().render(&state.window,&mut state.state.objects) {
|
||||
|
||||
let PosX = state.world.renderer.as_ref().unwrap().eye.frame.translation.x;
|
||||
let Posy = state.world.renderer.as_ref().unwrap().eye.frame.translation.z;
|
||||
|
||||
let WorldPosX = (PosX / 16.0).floor() as i32;
|
||||
let WorldPosY = (Posy / 16.0).floor() as i32;
|
||||
let startX = WorldPosX - 5;
|
||||
let endX = WorldPosX + 5;
|
||||
let startY = WorldPosY - 5;
|
||||
let endY = WorldPosY + 5;
|
||||
|
||||
for x in startX..endX {
|
||||
for y in startY..endY {
|
||||
App::GenerateChunk(x, y, 16, state);
|
||||
}
|
||||
}
|
||||
state
|
||||
.world
|
||||
.renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.eye
|
||||
.control(movement * 0.5);
|
||||
match state.world.renderer.as_mut().unwrap().render(&state.window) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
// Log the error and exit gracefully
|
||||
|
|
@ -280,9 +341,19 @@ impl ApplicationHandler<AppState> for App {
|
|||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
|
||||
let elapsed = now.elapsed();
|
||||
//println!("Elapsed {:.2?}",elapsed);
|
||||
for object in state.clients.iter() {
|
||||
object
|
||||
.0
|
||||
.borrow_mut()
|
||||
.model
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.instance
|
||||
.transform *= Mat4::from_rotation_translation(
|
||||
Quat::from_euler(EulerRot::XYZ,0.00,0.00,0.00 /*0.001, -0.001, 0.001*/),
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
);
|
||||
}
|
||||
}
|
||||
WindowEvent::MouseInput {
|
||||
button,
|
||||
|
|
@ -302,6 +373,9 @@ impl ApplicationHandler<AppState> for App {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
pub fn run() -> anyhow::Result<()> {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ struct Eye {
|
|||
frame: mat4x4<f32>,
|
||||
}
|
||||
|
||||
// Vertex shader
|
||||
@group(0) @binding(0)
|
||||
var<uniform> eye: Eye;
|
||||
@group(0) @binding(1)
|
||||
|
|
@ -87,36 +88,31 @@ var t_rough: texture_2d<f32>;
|
|||
fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
|
||||
var pi = 3.14159;
|
||||
let u_angle = atan2(look.x,look.z);
|
||||
let u = (u_angle/pi) * 0.5 + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||
let u = (u_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||
let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z));
|
||||
let v = (v_angle/pi) - 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||
let v = (v_angle/pi) + 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||
let uv = vec2<f32>(u,v); // Get UV on skybox
|
||||
return textureSample(sky_texture, sky_sampler, uv);
|
||||
}
|
||||
|
||||
fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
|
||||
fn rotation(mat: mat4x4<f32>) -> mat3x3<f32> {
|
||||
return mat3x3<f32>(
|
||||
it[0].xyz,
|
||||
it[1].xyz,
|
||||
it[2].xyz,
|
||||
mat[0].xyz,
|
||||
mat[1].xyz,
|
||||
mat[2].xyz,
|
||||
);
|
||||
}
|
||||
|
||||
fn translation(it: mat4x4<f32>) -> vec4<f32> {
|
||||
return it[3];
|
||||
}
|
||||
|
||||
fn light_aspect(light: vec3<f32>, dir: vec3<f32>) -> vec4<f32> {
|
||||
return vec4<f32>(0.0,0.0,0.0,0.0);
|
||||
fn translation(mat: mat4x4<f32>) -> vec4<f32> {
|
||||
//return vec4<f32>(mat[0][3],mat[1][3],mat[2][3],mat[3][3]);
|
||||
return mat[3];
|
||||
}
|
||||
|
||||
@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_diffuse, in.tex_coords);
|
||||
let object_rough: vec4<f32> = textureSample(t_rough, s_diffuse, in.tex_coords);
|
||||
|
||||
let diffuse_result = object_color.xyz * environment.ambient.xyz;
|
||||
let diffuse_color = object_color.xyz;
|
||||
let specular_color = vec3<f32>(0.0,0.0,0.0);
|
||||
let reflection = sky_aspect(reflect(normalize(in.world_position-translation(eye.frame).xyz),normalize(in.world_normal)));
|
||||
|
|
@ -133,13 +129,35 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
|||
//let specular_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0);
|
||||
//let specular_color = specular_strength * environment.light.xyz;
|
||||
|
||||
let reflect_factor = pow(object_rough.x,3);
|
||||
let result = (environment.ambient.xyz + diffuse_color.xyz + specular_color.xyz) * object_color.xyz;
|
||||
|
||||
let diffuse_brightness = length(diffuse_color);
|
||||
|
||||
let diffuse_effect = (diffuse_result * (1 - reflect_factor)) + reflection.xyz * reflect_factor * diffuse_brightness;
|
||||
|
||||
let result = (diffuse_effect.xyz + specular_color.xyz);
|
||||
|
||||
return vec4<f32>(result.xyz,object_color.a);
|
||||
return vec4<f32>(reflection.xyz,object_color.a);
|
||||
}
|
||||
|
||||
struct SkyOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) pos: vec4<f32> // unadulterated by WGSL
|
||||
}
|
||||
|
||||
const TRI_VERTICES = array(
|
||||
vec4(-1.0, -1.0, 1.0, 1.0),
|
||||
vec4(-1.0, 1.0, 1.0, 1.0),
|
||||
vec4( 1.0, -1.0, 1.0, 1.0),
|
||||
vec4( 1.0, 1.0, 1.0, 1.0),
|
||||
vec4(-1.0, 1.0, 1.0, 1.0),
|
||||
vec4( 1.0, -1.0, 1.0, 1.0),
|
||||
);
|
||||
|
||||
@vertex
|
||||
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
|
||||
var out: SkyOutput;
|
||||
out.position = TRI_VERTICES[index];
|
||||
out.pos = out.position;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
|
||||
let look = rotation(eye.frame) * in.pos.xyz;
|
||||
return sky_aspect(look);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 892 KiB |
20
src/lib.rs
20
src/lib.rs
|
|
@ -1,23 +1,7 @@
|
|||
#![recursion_limit = "256"]
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
pub mod app;
|
||||
pub mod render;
|
||||
pub mod web;
|
||||
pub mod world;
|
||||
pub mod list;
|
||||
pub mod state;
|
||||
pub mod net;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PoolError(String);
|
||||
|
||||
impl Display for PoolError {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for PoolError {}
|
||||
pub mod permutation_array;
|
||||
pub mod noise2d;
|
||||
185
src/list.rs
185
src/list.rs
|
|
@ -1,185 +0,0 @@
|
|||
use std::hash::{Hash, Hasher};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::ManuallyDrop;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::Mutex;
|
||||
|
||||
type Index = u32;
|
||||
|
||||
pub struct Element<T> {
|
||||
it: Option<T>,
|
||||
count: Index,
|
||||
}
|
||||
|
||||
pub struct FreeList<T> {
|
||||
items: Vec<Option<T>>,
|
||||
free: Vec<Index>,
|
||||
}
|
||||
|
||||
#[derive(Eq,Hash,PartialEq)]
|
||||
pub struct SingleId<T> {
|
||||
index: Index,
|
||||
phantom_data: PhantomData<T>
|
||||
}
|
||||
|
||||
impl<T> From<SingleId<T>> for Id<T> {
|
||||
fn from(value: SingleId<T>) -> Self {
|
||||
Id {
|
||||
index: value.index,
|
||||
phantom_data: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> SingleId<T> {
|
||||
pub(crate) fn shared(self) -> Id<T> {
|
||||
Id {
|
||||
index: self.index,
|
||||
phantom_data: self.phantom_data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Id<T> {
|
||||
index: Index,
|
||||
phantom_data: PhantomData<T>,
|
||||
}
|
||||
|
||||
pub struct RefId<'a, T> {
|
||||
pub id: Id<T>,
|
||||
it: &'a mut T
|
||||
}
|
||||
|
||||
impl<'a, T> Deref for RefId<'a, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.it
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> DerefMut for RefId<'a, T> {
|
||||
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.it
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<RefId<'_, T>> for Id<T> {
|
||||
fn from(value: RefId<T>) -> Self {
|
||||
value.id
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Id<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Id {
|
||||
index: self.index,
|
||||
phantom_data: PhantomData::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq for Id<T> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.index == other.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Id<T> {}
|
||||
|
||||
impl<T> Hash for Id<T> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.index.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Id<T> {
|
||||
pub fn maybe(self) -> MaybeId<T> {
|
||||
MaybeId(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MaybeId<T>(Id<T>);
|
||||
|
||||
impl<T> From<Id<T>> for MaybeId<T> {
|
||||
fn from(value: Id<T>) -> Self {
|
||||
MaybeId(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for MaybeId<T> {
|
||||
fn clone(&self) -> Self {
|
||||
if self.0.index != u32::MAX {
|
||||
MaybeId(self.0.clone())
|
||||
} else {
|
||||
MaybeId::NULL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MaybeId<T> {
|
||||
pub const NULL: MaybeId<T> = MaybeId(Id { index: u32::MAX, phantom_data: PhantomData {}, });
|
||||
pub fn unwrap(self) -> Id<T> {
|
||||
if self.0.index == u32::MAX {
|
||||
panic!()
|
||||
} else {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
pub fn exists(&self) -> Option<Id<T>> {
|
||||
if self.0.index == u32::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(self.0.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Default for FreeList<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FreeList<T> {
|
||||
pub fn new() -> FreeList<T> {
|
||||
FreeList {
|
||||
items: Vec::new(),
|
||||
free: Vec::new(),
|
||||
}
|
||||
}
|
||||
pub fn get(&mut self, id: &Id<T>) -> &mut T {
|
||||
self.items[id.index as usize].as_mut().unwrap()
|
||||
}
|
||||
pub fn get_ref(&mut self, id: Id<T>) -> RefId<'_, T> {
|
||||
let it = self.get(&id);
|
||||
RefId { id, it }
|
||||
}
|
||||
pub fn remove(&mut self, id: Id<T>) -> T {
|
||||
self.free.push(id.index);
|
||||
self.items[id.index as usize].take().unwrap()
|
||||
}
|
||||
pub fn make(&mut self, value: T) -> RefId<'_, T> { // todo: shouldn't panic if allocation fails
|
||||
if let Some(free) = self.free.pop() {
|
||||
self.items[free as usize] = Some(value);
|
||||
RefId {
|
||||
id: Id {
|
||||
index: free,
|
||||
phantom_data: PhantomData,
|
||||
},
|
||||
it: self.items[free as usize].as_mut().unwrap(),
|
||||
}
|
||||
} else {
|
||||
self.items.push(Some(value));
|
||||
let index = (self.items.len() - 1) as Index;
|
||||
RefId {
|
||||
id: Id {
|
||||
index,
|
||||
phantom_data: PhantomData,
|
||||
},
|
||||
it: self.items[index as usize].as_mut().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
#![recursion_limit = "256"]
|
||||
|
||||
use pool::*;
|
||||
use game::*;
|
||||
fn main() {
|
||||
app::run().unwrap();
|
||||
}
|
||||
|
|
|
|||
97
src/noise2d.rs
Normal file
97
src/noise2d.rs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
use crate::permutation_array;
|
||||
use crate::permutation_array::PermutationArray;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Vector2d {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
}
|
||||
|
||||
impl Vector2d {
|
||||
pub fn new(x: f64, y: f64) -> Self {
|
||||
Self { x, y }
|
||||
}
|
||||
|
||||
pub fn dot(&self, other: Vector2d) -> f64 {
|
||||
self.x * other.x + self.y * other.y
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Noise2D;
|
||||
|
||||
impl Noise2D {
|
||||
pub fn gen_gradient_vector(p: i32) -> Vector2d {
|
||||
match p & 3 {
|
||||
0 => Vector2d::new(1.0, 1.0),
|
||||
1 => Vector2d::new(-1.0, 1.0),
|
||||
2 => Vector2d::new(-1.0, -1.0),
|
||||
_ => Vector2d::new(1.0, -1.0),
|
||||
}
|
||||
}
|
||||
|
||||
fn fade(t: f64) -> f64 {
|
||||
t * t * t * (t * (t * 6.0 - 15.0) + 10.0)
|
||||
}
|
||||
|
||||
fn lerp(t: f64, x: f64, y: f64) -> f64 {
|
||||
x + t * (y - x)
|
||||
}
|
||||
|
||||
pub fn noise_2d(x: f64, y: f64, permutation: &[i32; 512]) -> f64 {
|
||||
let x_floor = x.floor();
|
||||
let y_floor = y.floor();
|
||||
|
||||
let x_grid = (x_floor as i32) & 255;
|
||||
let y_grid = (y_floor as i32) & 255;
|
||||
|
||||
let xd = x - x_floor;
|
||||
let yd = y - y_floor;
|
||||
|
||||
let tr = Vector2d::new(xd - 1.0, yd - 1.0);
|
||||
let tl = Vector2d::new(xd, yd - 1.0);
|
||||
let br = Vector2d::new(xd - 1.0, yd);
|
||||
let bl = Vector2d::new(xd, yd);
|
||||
|
||||
let x_idx = x_grid as usize;
|
||||
let y_idx = y_grid as usize;
|
||||
|
||||
let value_tr = permutation[permutation[x_idx + 1] as usize + y_idx + 1];
|
||||
let value_tl = permutation[permutation[x_idx] as usize + y_idx + 1];
|
||||
let value_br = permutation[permutation[x_idx + 1] as usize + y_idx];
|
||||
let value_bl = permutation[permutation[x_idx] as usize + y_idx];
|
||||
|
||||
let d_tr = tr.dot(Self::gen_gradient_vector(value_tr));
|
||||
let d_tl = tl.dot(Self::gen_gradient_vector(value_tl));
|
||||
let d_br = br.dot(Self::gen_gradient_vector(value_br));
|
||||
let d_bl = bl.dot(Self::gen_gradient_vector(value_bl));
|
||||
|
||||
let u = Self::fade(xd);
|
||||
let v = Self::fade(yd);
|
||||
|
||||
Self::lerp(
|
||||
u,
|
||||
Self::lerp(v, d_bl, d_tl),
|
||||
Self::lerp(v, d_br, d_tr),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn fractal_noise(
|
||||
x: f64,
|
||||
y: f64,
|
||||
octaves: i32,
|
||||
mut amplitude: f64,
|
||||
mut frequency: f64,
|
||||
permutation: &PermutationArray,
|
||||
) -> f64 {
|
||||
let mut fractal_noise = 0.0;
|
||||
|
||||
for _ in 0..octaves {
|
||||
let layer = amplitude * Self::noise_2d(x * frequency, y * frequency, permutation.get_permutation());
|
||||
fractal_noise += layer;
|
||||
amplitude *= 0.5;
|
||||
frequency *= 2.0;
|
||||
}
|
||||
|
||||
fractal_noise
|
||||
}
|
||||
}
|
||||
39
src/permutation_array.rs
Normal file
39
src/permutation_array.rs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
|
||||
//i translated the Terrain4J Noise2D to Rust so i can mess around with the cubes
|
||||
pub struct PermutationArray {
|
||||
permutation: [i32; 512],
|
||||
}
|
||||
|
||||
impl PermutationArray {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
permutation: [0; 512],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_permutation(&self) -> &[i32; 512] {
|
||||
&self.permutation
|
||||
}
|
||||
|
||||
pub fn generate_permutation_array(&mut self, seed: i64) {
|
||||
let mut seed_bytes = [0u8; 32];
|
||||
seed_bytes[0..8].copy_from_slice(&(seed as u64).to_le_bytes());
|
||||
let mut rnd = ChaCha8Rng::from_seed(seed_bytes);
|
||||
|
||||
self.permutation = [0; 512];
|
||||
for i in 0..256 {
|
||||
self.permutation[i] = i as i32;
|
||||
}
|
||||
|
||||
for p in (1..256).rev() {
|
||||
let index = rnd.gen_range(0..=p);
|
||||
self.permutation.swap(p, index);
|
||||
}
|
||||
|
||||
for i in 0..256 {
|
||||
self.permutation[i + 256] = self.permutation[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,9 @@
|
|||
use crate::render::{Texture};
|
||||
use crate::render::{MaterialProperties, SimpleTexture};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use glam::camera::lh::proj::directx::perspective;
|
||||
use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4};
|
||||
use wgpu::util::DeviceExt;
|
||||
use wgpu::{Device, Queue};
|
||||
use crate::render::instance::material::MaterialProperties;
|
||||
|
||||
pub(crate) struct Eye {
|
||||
pub(crate) frame: Affine3A,
|
||||
|
|
@ -17,7 +16,6 @@ pub(crate) struct Eye {
|
|||
pub(crate) camera_buffer: wgpu::Buffer,
|
||||
pub(crate) layout: wgpu::BindGroupLayout,
|
||||
pub(crate) group: wgpu::BindGroup,
|
||||
pub sky_pipeline: wgpu::RenderPipeline,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
|
@ -51,7 +49,7 @@ impl Eye {
|
|||
bytemuck::cast_slice(&[self.environment]),
|
||||
);
|
||||
}
|
||||
pub(crate) fn new(device: &Device, config: &wgpu::SurfaceConfiguration, width: u32, height: u32, skybox: Texture) -> Eye {
|
||||
pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: SimpleTexture) -> Eye {
|
||||
let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Camera Buffer"),
|
||||
contents: bytemuck::cast_slice(&[
|
||||
|
|
@ -65,7 +63,7 @@ impl Eye {
|
|||
|
||||
let dir = Vec3::new(1.0, 0.5, 1.0).normalize();
|
||||
let environment = Environment {
|
||||
ambient: Vec4::new(0.1, 0.1, 0.1, 0.0),
|
||||
ambient: Vec4::new(0.15, 0.15, 0.15, 0.0),
|
||||
light: Vec4::new(1.0, 1.0, 1.0, 0.0),
|
||||
dir: Vec4::new(dir.x, dir.y, dir.z, 0.0),
|
||||
};
|
||||
|
|
@ -118,51 +116,6 @@ impl Eye {
|
|||
label: Some("eye_bind_group_layout"),
|
||||
});
|
||||
|
||||
let shader = device.create_shader_module(wgpu::include_wgsl!("sky.wgsl"));
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[Some(&layout)],
|
||||
immediate_size: 0,
|
||||
});
|
||||
|
||||
//todo: use a pipeline cache!
|
||||
let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Sky Pipeline"),
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: Some("vs_sky"),
|
||||
compilation_options: Default::default(),
|
||||
buffers: &[],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: Some("fs_sky"),
|
||||
compilation_options: Default::default(),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
// 4.
|
||||
format: config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
front_face: wgpu::FrontFace::Cw,
|
||||
..Default::default()
|
||||
},
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
depth_write_enabled: Some(false),
|
||||
depth_compare: Some(wgpu::CompareFunction::LessEqual),
|
||||
stencil: wgpu::StencilState::default(),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState::default(),
|
||||
multiview_mask: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox);
|
||||
|
||||
Eye {
|
||||
|
|
@ -176,7 +129,6 @@ impl Eye {
|
|||
layout,
|
||||
environment,
|
||||
environment_buffer,
|
||||
sky_pipeline,
|
||||
}
|
||||
}
|
||||
fn bind_group(
|
||||
|
|
@ -184,7 +136,7 @@ impl Eye {
|
|||
device: &wgpu::Device,
|
||||
camera: &wgpu::Buffer,
|
||||
environment: &wgpu::Buffer,
|
||||
skybox: Texture,
|
||||
skybox: SimpleTexture,
|
||||
) -> wgpu::BindGroup {
|
||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &layout,
|
||||
|
|
@ -211,7 +163,7 @@ impl Eye {
|
|||
label: Some("eye_bind_group"),
|
||||
})
|
||||
}
|
||||
pub fn skybox(&mut self, device: &wgpu::Device, texture: Texture) {
|
||||
pub fn skybox(&mut self, device: &wgpu::Device, texture: SimpleTexture) {
|
||||
self.group = Eye::bind_group(
|
||||
&self.layout,
|
||||
device,
|
||||
|
|
@ -220,9 +172,8 @@ impl Eye {
|
|||
texture,
|
||||
)
|
||||
}
|
||||
pub(crate) fn resize(&mut self, queue: &Queue, width: u32, height: u32) {
|
||||
self.aspect_ratio = width as f32 / height as f32;
|
||||
self.write(queue);
|
||||
pub(crate) fn resize(&mut self, width: u32, height: u32) {
|
||||
self.aspect_ratio = width as f32 / height as f32
|
||||
}
|
||||
pub(crate) fn control(&mut self, delta: Vec3) {
|
||||
self.frame *= Affine3A::from_translation(delta);
|
||||
|
|
|
|||
|
|
@ -1,130 +0,0 @@
|
|||
use wgpu::Device;
|
||||
use crate::list::{FreeList, Id};
|
||||
use crate::render::Renderer;
|
||||
use crate::render::texture::Texture;
|
||||
|
||||
pub struct Materials {
|
||||
pub(crate) layout: wgpu::BindGroupLayout,
|
||||
pub(crate) list: FreeList<Material>,
|
||||
default: Material,
|
||||
}
|
||||
|
||||
impl Materials {
|
||||
pub(crate) fn new(device: &Device, default: Texture) -> Materials {
|
||||
fn texture_bind_group_layout_entry(binding: u32) -> wgpu::BindGroupLayoutEntry {
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
count: None,
|
||||
}
|
||||
}
|
||||
|
||||
let layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
texture_bind_group_layout_entry(1),
|
||||
texture_bind_group_layout_entry(2),
|
||||
texture_bind_group_layout_entry(3),
|
||||
],
|
||||
label: Some("texture_bind_group_layout"),
|
||||
});
|
||||
|
||||
let default = Material::_new(
|
||||
&device,
|
||||
&layout,
|
||||
&default,
|
||||
&default,
|
||||
&default,
|
||||
MaterialProperties::default(),
|
||||
);
|
||||
|
||||
Materials {
|
||||
layout,
|
||||
list: Default::default(),
|
||||
default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Material {
|
||||
pub(crate) group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
pub struct MaterialProperties {
|
||||
pub(crate) edge: wgpu::AddressMode,
|
||||
pub(crate) filter: wgpu::FilterMode,
|
||||
}
|
||||
|
||||
impl Default for MaterialProperties {
|
||||
fn default() -> MaterialProperties {
|
||||
MaterialProperties {
|
||||
edge: wgpu::AddressMode::Repeat,
|
||||
filter: wgpu::FilterMode::Linear,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MaterialProperties {
|
||||
pub(crate) fn sampler(&self, device: &wgpu::Device) -> wgpu::Sampler {
|
||||
device.create_sampler(&wgpu::SamplerDescriptor {
|
||||
address_mode_u: self.edge,
|
||||
address_mode_v: self.edge,
|
||||
address_mode_w: self.edge,
|
||||
mag_filter: self.filter,
|
||||
min_filter: self.filter,
|
||||
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Material {
|
||||
pub(crate) fn _new(
|
||||
device: &wgpu::Device,
|
||||
layout: &wgpu::BindGroupLayout,
|
||||
base: &Texture,
|
||||
normal: &Texture,
|
||||
reflect: &Texture,
|
||||
config: MaterialProperties,
|
||||
) -> Material {
|
||||
let sampler = config.sampler(&device);
|
||||
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::TextureView(&base.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 2,
|
||||
resource: wgpu::BindingResource::TextureView(&normal.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 3,
|
||||
resource: wgpu::BindingResource::TextureView(&reflect.view),
|
||||
},
|
||||
],
|
||||
label: Some("diffuse_bind_group"),
|
||||
});
|
||||
Material { group }
|
||||
}
|
||||
pub fn new(renderer: &mut Renderer, base: &Texture, normal: &Texture, reflect: &Texture, config: MaterialProperties) -> Id<Material> {
|
||||
renderer.instances.materials.list.make(Material::_new(&renderer.device, &renderer.instances.materials.layout, base, normal, reflect, config)).id
|
||||
}
|
||||
}
|
||||
|
|
@ -1,376 +0,0 @@
|
|||
pub mod material;
|
||||
|
||||
use std::ops::Range;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use glam::{Affine3, Mat4, Vec4};
|
||||
use wgpu::{BindGroup, Device};
|
||||
use wgpu::naga::FastHashMap;
|
||||
use crate::list::{FreeList, Id, RefId};
|
||||
use crate::render::{instance, Renderer};
|
||||
use crate::render::eye::Eye;
|
||||
pub(crate) use crate::render::instance::material::{Material, Materials};
|
||||
use crate::render::mesh::{Mesh, TangentVertex};
|
||||
use crate::render::texture::Texture;
|
||||
use crate::world::ObjectData;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||
pub struct ModelInstance {
|
||||
pub transform: Mat4,
|
||||
pub color: Vec4,
|
||||
pub lights: [u16; 16],
|
||||
pub point_lights: u32,
|
||||
pub spot_lights: u32,
|
||||
pub metal: f32,
|
||||
pub rough: f32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Pod, Copy, Clone, Zeroable)]
|
||||
pub struct LightInstance {
|
||||
pub location: Vec4,
|
||||
pub rotation: Vec4,
|
||||
pub color: Vec4,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LightData {
|
||||
pub instance: LightInstance,
|
||||
pub transform: Affine3,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ModelData {
|
||||
pub instance: ModelInstance,
|
||||
pub material: Id<Material>,
|
||||
pub mesh: Id<Mesh>,
|
||||
}
|
||||
|
||||
pub struct Instances {
|
||||
pub materials: Materials,
|
||||
light_instances: FreeList<LightData>,
|
||||
model_instances: FreeList<ModelData>,
|
||||
pub(crate) pipeline: wgpu::RenderPipeline,
|
||||
light_count: usize,
|
||||
light_buffer: wgpu::Buffer,
|
||||
model_count: usize,
|
||||
pub(crate) model_buffer: wgpu::Buffer,
|
||||
models_flag: bool,
|
||||
pub(crate) program: Program,
|
||||
models: FastHashMap<Id<Mesh>, FastHashMap<Id<Material>, FastHashMap<Id<ObjectData>,bool>>>,
|
||||
}
|
||||
|
||||
pub enum Code {
|
||||
Material(Material),
|
||||
Mesh(Mesh),
|
||||
Draw(Range<u32>),
|
||||
}
|
||||
|
||||
pub struct Program(pub Vec<Code>);
|
||||
|
||||
impl Program {
|
||||
fn push(&mut self, item: Code) {
|
||||
self.0.push(item)
|
||||
}
|
||||
fn new() -> Program {
|
||||
Program(Vec::new())
|
||||
}
|
||||
pub(crate) fn render(self, pass: &mut wgpu::RenderPass) {
|
||||
let mut count = 0;
|
||||
let mut indexed = false;
|
||||
for code in self.0 {
|
||||
match code {
|
||||
Code::Material(material) => pass.set_bind_group(
|
||||
Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION,
|
||||
&material.group,
|
||||
&[],
|
||||
),
|
||||
Code::Mesh(Mesh {vertices, indices, ..}) => {
|
||||
pass.set_vertex_buffer(0, vertices.0.slice(..));
|
||||
if let Some(indices) = indices {
|
||||
pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32);
|
||||
count = indices.1;
|
||||
indexed = true;
|
||||
} else {
|
||||
count = vertices.1;
|
||||
indexed = false;
|
||||
}
|
||||
}
|
||||
Code::Draw(instances) => {
|
||||
if indexed {
|
||||
pass.draw_indexed(0..count, 0, instances)
|
||||
} else {
|
||||
pass.draw(0..count, instances);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Instances {
|
||||
const MIN_SIZE: u64 = 64;
|
||||
pub fn register_object(&mut self, mut object: RefId<ObjectData>) {
|
||||
let model = object.model.as_mut().unwrap();
|
||||
self.model_count += 1;
|
||||
self.models
|
||||
.entry(model.mesh.clone()).or_default()
|
||||
.entry(model.material.clone()).or_default()
|
||||
.insert(object.into(),self.models_flag);
|
||||
}
|
||||
/*pub fn register_light(&mut self, light: Id<LightData>) {
|
||||
self.light_count += 1;
|
||||
self.lights.insert(light);
|
||||
}*/
|
||||
pub fn reallocate_buffer(
|
||||
device: &wgpu::Device,
|
||||
buffer: &mut wgpu::Buffer,
|
||||
count: usize,
|
||||
item_size: usize,
|
||||
) {
|
||||
let size = buffer.size() / item_size as wgpu::BufferAddress;
|
||||
if count > Instances::MIN_SIZE as usize {
|
||||
let mut reallocate: Option<usize> = None;
|
||||
if count < (size / 2) as usize {
|
||||
reallocate = Some(count / 2);
|
||||
} else if count > size as usize {
|
||||
reallocate = Some(count * 2);
|
||||
}
|
||||
if let Some(new_size) = reallocate {
|
||||
*buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Instance Buffer"),
|
||||
size: (item_size * new_size) as wgpu::BufferAddress,
|
||||
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
/*pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
|
||||
//SimpleInstances::reallocate_buffer(device, queue, &mut self.light_ref_buffer, self.light_ref_last, size_of::<u32>());
|
||||
/*
|
||||
let mut light_ref_buffer = queue.write_buffer_with(
|
||||
&self.light_ref_buffer,
|
||||
0 as wgpu::BufferAddress,
|
||||
wgpu::BufferSize::new(self.instance_buffer.size()).unwrap()
|
||||
).unwrap();
|
||||
*/
|
||||
Instances::reallocate_buffer(
|
||||
device,
|
||||
&mut self.light_buffer,
|
||||
self.light_count,
|
||||
size_of::<WorldLight>(),
|
||||
);
|
||||
let mut buffer = queue
|
||||
.write_buffer_with(
|
||||
&self.light_buffer,
|
||||
0 as wgpu::BufferAddress,
|
||||
wgpu::BufferSize::new(self.light_buffer.size()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let stride = size_of::<WorldLight>();
|
||||
for (new_index, (light, index)) in self.lights.iter_mut().enumerate() {
|
||||
*index = new_index + 1;
|
||||
let begin = *index * stride;
|
||||
buffer
|
||||
.slice(begin..begin + stride)
|
||||
.copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance]));
|
||||
}
|
||||
}*/
|
||||
pub(crate) fn write_instances(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
mesh_list: &mut FreeList<Mesh>,
|
||||
objects_list: &mut FreeList<ObjectData>,
|
||||
) {
|
||||
Instances::reallocate_buffer(
|
||||
device,
|
||||
&mut self.model_buffer,
|
||||
self.model_count,
|
||||
size_of::<ModelInstance>(),
|
||||
);
|
||||
let mut buffer = queue
|
||||
.write_buffer_with(
|
||||
&self.model_buffer,
|
||||
0 as wgpu::BufferAddress,
|
||||
wgpu::BufferSize::new(self.model_buffer.size()).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut index: u32 = 0;
|
||||
let stride = size_of::<ModelInstance>();
|
||||
for (mesh, materials) in self.models.iter_mut() {
|
||||
self.program.push(Code::Mesh(mesh_list.get(mesh).clone()));
|
||||
for (material, objects) in materials.iter_mut() {
|
||||
self.program.push(Code::Material(self.materials.list.get(material).clone()));
|
||||
let before = index;
|
||||
objects.retain(|object,flag| {
|
||||
let object = objects_list.get(object);
|
||||
if *flag == self.models_flag {
|
||||
let begin = index as usize * stride;
|
||||
buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.model.as_mut().unwrap().instance]));
|
||||
index += 1;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
self.program.push(Code::Draw(before..index));
|
||||
}
|
||||
}
|
||||
//println!("objects: {}",index);
|
||||
self.models_flag = !self.models_flag;
|
||||
}
|
||||
|
||||
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: size_of::<ModelInstance>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Instance,
|
||||
attributes: &[
|
||||
// todo: is this too big?
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
||||
shader_location: 7,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||
shader_location: 8,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||
shader_location: 9,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||
shader_location: 10,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||
shader_location: 11,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(device: &wgpu::Device, eye: &Eye, config: &wgpu::SurfaceConfiguration, default: Texture) -> Instances {
|
||||
let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Instance Buffer"),
|
||||
size: (size_of::<ModelInstance>() * Instances::MIN_SIZE as usize)
|
||||
as wgpu::BufferAddress,
|
||||
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
let light_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Light Buffer"),
|
||||
size: (size_of::<LightInstance>() * Instances::MIN_SIZE as usize)
|
||||
as wgpu::BufferAddress,
|
||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});
|
||||
|
||||
/*let light_ref_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||
label: Some("Light Ref Buffer"),
|
||||
size: (size_of::<u32>() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress,
|
||||
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||
mapped_at_creation: false,
|
||||
});*/
|
||||
|
||||
let materials = Materials::new(device, default);
|
||||
|
||||
let shader = device.create_shader_module(wgpu::include_wgsl!("instance.wgsl"));
|
||||
|
||||
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Render Pipeline Layout"),
|
||||
bind_group_layouts: &[Some(&eye.layout), Some(&materials.layout)],
|
||||
immediate_size: 0,
|
||||
});
|
||||
|
||||
let instance_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: &[TangentVertex::desc(), Instances::desc()],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
// 3.
|
||||
module: &shader,
|
||||
entry_point: Some("fs_main"),
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
// 4.
|
||||
format: config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: Default::default(), /*wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
}*/
|
||||
depth_stencil: Some(wgpu::DepthStencilState {
|
||||
stencil: wgpu::StencilState::default(),
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
depth_write_enabled: Some(true),
|
||||
depth_compare: Some(wgpu::CompareFunction::Less),
|
||||
bias: wgpu::DepthBiasState::default(),
|
||||
}),
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview_mask: None,
|
||||
cache: None,
|
||||
});
|
||||
|
||||
Instances {
|
||||
materials,
|
||||
light_instances: Default::default(),
|
||||
model_instances: Default::default(),
|
||||
pipeline: instance_pipeline,
|
||||
light_count: 0,
|
||||
//light_ref_last: 0,
|
||||
model_count: 0,
|
||||
light_buffer,
|
||||
model_buffer: instance_buffer,
|
||||
//light_ref_buffer,
|
||||
//lights: Default::default(),
|
||||
models_flag: false,
|
||||
program: Program(Vec::new()),
|
||||
models: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render() {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,302 +0,0 @@
|
|||
use std::collections::HashMap;
|
||||
use glam::{Mat4, Vec3, Vec4};
|
||||
use gltf::Semantic;
|
||||
use wgpu::{Device, Queue};
|
||||
use wgpu::util::DeviceExt;
|
||||
use crate::list::{FreeList, Id, RefId};
|
||||
use crate::render::instance::{Material, Materials, ModelData, ModelInstance};
|
||||
use crate::render::instance::material::MaterialProperties;
|
||||
use crate::render::mesh::{Mesh, TangentVertex};
|
||||
use crate::render::texture::{Texture, TextureProperties};
|
||||
use crate::render::{Renderer, Textures};
|
||||
use crate::world::{ObjectData, AABB, ColliderData, Shape};
|
||||
|
||||
pub struct TreeNode {
|
||||
object: Option<ObjectData>,
|
||||
children: Vec<TreeNode>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl TreeNode {
|
||||
pub fn first_object(&self) -> Option<ObjectData> {
|
||||
if let Some(object) = self.object.clone() {
|
||||
Some(object)
|
||||
} else {
|
||||
for child in self.children.iter() {
|
||||
if let Some(object) = child.first_object() {
|
||||
return Some(object);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type GltfVertexBufferKey = (Option<usize>, Option<usize>, Option<usize>, Option<usize>);
|
||||
|
||||
pub fn new_texture_from_gltf(
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
info: &gltf::texture::Texture,
|
||||
images: &Vec<gltf::image::Data>,
|
||||
) -> Option<Texture> {
|
||||
if let Some(image) = images.get(info.source().index()) {
|
||||
let mut new_pixels = Vec::new();
|
||||
let pixels: &Vec<u8>;
|
||||
match image.format {
|
||||
gltf::image::Format::R8G8B8 => {
|
||||
for pixel in image.pixels.chunks(3) {
|
||||
new_pixels.push(pixel[0]);
|
||||
new_pixels.push(pixel[0]);
|
||||
new_pixels.push(pixel[0]);
|
||||
new_pixels.push(255);
|
||||
}
|
||||
pixels = &new_pixels;
|
||||
}
|
||||
gltf::image::Format::R8G8B8A8 => pixels = &image.pixels,
|
||||
_ => return None,
|
||||
}
|
||||
Some(Texture::load(
|
||||
device,
|
||||
queue,
|
||||
pixels.as_slice(),
|
||||
TextureProperties {
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn new_mesh_from_gltf(
|
||||
device: &Device,
|
||||
primitive: gltf::Primitive,
|
||||
meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
|
||||
buffers: &[gltf::buffer::Data],
|
||||
) -> Mesh {
|
||||
let position_index = primitive
|
||||
.get(&Semantic::Positions)
|
||||
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||
let normals_index = primitive
|
||||
.get(&Semantic::Normals)
|
||||
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||
let tex_coords_index = primitive
|
||||
.get(&Semantic::TexCoords(0))
|
||||
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||
let indices_index = primitive
|
||||
.indices()
|
||||
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||
let tangent_index = primitive
|
||||
.get(&Semantic::Tangents)
|
||||
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||
let key = (
|
||||
position_index,
|
||||
normals_index,
|
||||
tex_coords_index,
|
||||
indices_index,
|
||||
);
|
||||
let insert = || {
|
||||
let mut tangents = false;
|
||||
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
|
||||
let mut vertex_data: Vec<TangentVertex>;
|
||||
let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO);
|
||||
if let Some(positions) = reader.read_positions() {
|
||||
vertex_data = Vec::with_capacity(positions.len());
|
||||
let mut normal = reader.read_normals().map(|it| it.into_iter());
|
||||
let mut tex_coord = reader
|
||||
.read_tex_coords(0)
|
||||
.map(|it| it.into_f32().into_iter());
|
||||
let mut tangent = reader.read_tangents().map(|it| it.into_iter());
|
||||
tangents = tangent.is_some();
|
||||
for position in positions {
|
||||
aabb = aabb.extend_to(Vec3::from(position));
|
||||
vertex_data.push(TangentVertex {
|
||||
position,
|
||||
normal: if let Some(ref mut normals) = normal {
|
||||
if let Some(normal) = normals.next() {
|
||||
normal
|
||||
} else {
|
||||
[0.0; 3]
|
||||
}
|
||||
} else {
|
||||
[0.0; 3]
|
||||
},
|
||||
tex_coord: if let Some(ref mut tex_coords) = tex_coord {
|
||||
tex_coords.next()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.unwrap_or([0.0; 2]),
|
||||
tangent: [0.0; 3], // todo: tangent from model
|
||||
bitangent: [0.0; 3],
|
||||
})
|
||||
}
|
||||
} else {
|
||||
vertex_data = Vec::new();
|
||||
}
|
||||
let vertex_buffer =
|
||||
device
|
||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Vertex Buffer"),
|
||||
contents: bytemuck::cast_slice(vertex_data.as_slice()),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
let vertices_result = (vertex_buffer, vertex_data.len() as u32);
|
||||
let indices_result = if let Some(indices) = reader.read_indices() {
|
||||
let index_data: Vec<u32> = indices.into_u32().collect();
|
||||
Some((
|
||||
device
|
||||
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some("Index Buffer"),
|
||||
contents: bytemuck::cast_slice(index_data.as_slice()),
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
}),
|
||||
index_data.len() as u32,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
Mesh {
|
||||
indices: indices_result,
|
||||
vertices: vertices_result,
|
||||
aabb,
|
||||
}
|
||||
};
|
||||
meshes.entry(key).or_insert_with(insert).clone()
|
||||
}
|
||||
|
||||
pub fn load_node_from_gltf(
|
||||
node: gltf::Node,
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
mesh_map: &mut HashMap<GltfVertexBufferKey, Mesh>,
|
||||
texture_map: &mut HashMap<usize, Texture>,
|
||||
materials: &mut Materials,
|
||||
textures: &Textures,
|
||||
meshes: &mut FreeList<Mesh>,
|
||||
images: &Vec<gltf::image::Data>,
|
||||
buffers: &Vec<gltf::buffer::Data>,
|
||||
) -> TreeNode {
|
||||
let mut tree_node = TreeNode {
|
||||
object: None,
|
||||
children: Vec::new(),
|
||||
name: node.name().unwrap_or("Node").to_string(),
|
||||
};
|
||||
if let Some(mesh) = node.mesh() {
|
||||
let len = mesh.primitives().len();
|
||||
for primitive in mesh.primitives() {
|
||||
let pbr = primitive.material().pbr_metallic_roughness();
|
||||
let material = primitive.material();
|
||||
let color = pbr.base_color_factor();
|
||||
let metal = pbr.metallic_factor();
|
||||
let rough = pbr.roughness_factor();
|
||||
let light = primitive.material().emissive_factor();
|
||||
let base = match pbr.base_color_texture() {
|
||||
Some(info) => texture_map
|
||||
.entry(info.texture().source().index())
|
||||
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
|
||||
.clone(),
|
||||
None => textures.default.clone(),
|
||||
};
|
||||
let reflect = match pbr.metallic_roughness_texture() {
|
||||
Some(info) => texture_map
|
||||
.entry(info.texture().source().index())
|
||||
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
|
||||
.clone(),
|
||||
None => textures.default.clone(),
|
||||
};
|
||||
let normal = match material.normal_texture() {
|
||||
Some(info) => texture_map
|
||||
.entry(info.texture().source().index())
|
||||
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
|
||||
.clone(),
|
||||
None => textures.default.clone(),
|
||||
};
|
||||
let material = materials.list.make(Material::_new(
|
||||
&device,
|
||||
&materials.layout,
|
||||
&base,
|
||||
&normal,
|
||||
&reflect,
|
||||
MaterialProperties {
|
||||
edge: wgpu::AddressMode::Repeat,
|
||||
filter: wgpu::FilterMode::Linear,
|
||||
},
|
||||
)).into();;
|
||||
let mesh = new_mesh_from_gltf(device, primitive, mesh_map, buffers);
|
||||
let aabb = mesh.aabb;
|
||||
let object = ObjectData {
|
||||
model: Some(ModelData {
|
||||
instance: ModelInstance {
|
||||
transform: Mat4::default(),
|
||||
color: Vec4::from_array(color),
|
||||
lights: [0; 16],
|
||||
point_lights: 0,
|
||||
spot_lights: 0,
|
||||
metal,
|
||||
rough,
|
||||
},
|
||||
material,
|
||||
mesh: meshes.make(mesh).id,
|
||||
}),
|
||||
collider: ColliderData { shape: Shape::Sphere(aabb.1.distance(aabb.0)) },
|
||||
affine: Default::default(),
|
||||
asleep: false,
|
||||
};
|
||||
if len == 1 {
|
||||
tree_node.object = Some(object);
|
||||
} else {
|
||||
tree_node.children.push(TreeNode {
|
||||
object: Some(object),
|
||||
children: Vec::new(),
|
||||
name: "Primitive".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
for node in node.children() {
|
||||
tree_node
|
||||
.children
|
||||
.push(load_node_from_gltf(node, device, queue, mesh_map, texture_map, materials, textures, meshes, images, buffers));
|
||||
}
|
||||
tree_node
|
||||
}
|
||||
pub fn _load_from_gltf(device: &Device, queue: &Queue, meshes: &mut FreeList<Mesh>, materials: &mut Materials, textures: &mut Textures, slice: impl AsRef<[u8]>) -> TreeNode {
|
||||
let mut root = TreeNode {
|
||||
object: None,
|
||||
children: Vec::new(),
|
||||
name: "Root".to_string(),
|
||||
};
|
||||
if let Ok((document, buffers, images)) = gltf::import_slice(slice) {
|
||||
let mut mesh_map: HashMap<GltfVertexBufferKey, Mesh> = HashMap::new();
|
||||
let mut texture_map: HashMap<usize, Texture> = HashMap::new();
|
||||
for scene in document.scenes() {
|
||||
let mut scene_node = TreeNode {
|
||||
object: None,
|
||||
children: Vec::new(),
|
||||
name: scene.name().unwrap_or("Scene").to_string(),
|
||||
};
|
||||
for node in scene.nodes() {
|
||||
scene_node.children.push(load_node_from_gltf(
|
||||
node,
|
||||
device,
|
||||
queue,
|
||||
&mut mesh_map,
|
||||
&mut texture_map,
|
||||
materials,
|
||||
textures,
|
||||
meshes,
|
||||
&images,
|
||||
&buffers,
|
||||
));
|
||||
}
|
||||
root.children.push(scene_node)
|
||||
}
|
||||
}
|
||||
root
|
||||
}
|
||||
|
||||
pub fn load_from_gltf(renderer: &mut Renderer, slice: impl AsRef<[u8]>) -> TreeNode {
|
||||
_load_from_gltf(&renderer.device, &renderer.queue, &mut renderer.meshes, &mut renderer.instances.materials, &mut renderer.textures, slice)
|
||||
}
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
use crate::world::AABB;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct TangentVertex {
|
||||
pub(crate) position: [f32; 3],
|
||||
pub(crate) normal: [f32; 3],
|
||||
pub(crate) tex_coord: [f32; 2],
|
||||
pub(crate) tangent: [f32; 3],
|
||||
pub(crate) bitangent: [f32; 3],
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
pub struct Vertex {
|
||||
position: [f32; 3],
|
||||
normal: [f32; 3],
|
||||
tex_coord: [f32; 3],
|
||||
}
|
||||
|
||||
impl TangentVertex {
|
||||
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: size_of::<TangentVertex>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
offset: 0,
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x3,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
offset: size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Mesh {
|
||||
pub(crate) indices: Option<(wgpu::Buffer, u32)>,
|
||||
pub(crate) vertices: (wgpu::Buffer, u32),
|
||||
pub(crate) aabb: AABB,
|
||||
}
|
||||
1154
src/render/mod.rs
1154
src/render/mod.rs
File diff suppressed because it is too large
Load diff
|
|
@ -1,72 +0,0 @@
|
|||
struct Environment {
|
||||
ambient: vec4<f32>,
|
||||
light: vec4<f32>,
|
||||
dir: vec4<f32>,
|
||||
}
|
||||
|
||||
struct Eye {
|
||||
// from camera to screen
|
||||
proj: mat4x4<f32>,
|
||||
// from screen to camera
|
||||
inv: mat4x4<f32>,
|
||||
// world to camera
|
||||
view: mat4x4<f32>,
|
||||
// camera transform
|
||||
frame: mat4x4<f32>,
|
||||
}
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> eye: Eye;
|
||||
@group(0) @binding(1)
|
||||
var<uniform> environment: Environment;
|
||||
@group(0) @binding(2)
|
||||
var sky_sampler: sampler;
|
||||
@group(0) @binding(3)
|
||||
var sky_texture: texture_2d<f32>;
|
||||
|
||||
// written on the 60th sleepless hour
|
||||
fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
|
||||
var pi = 3.14159;
|
||||
let u_angle = atan2(look.x,look.z);
|
||||
let u = (u_angle/pi) * 0.5 + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||
let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z));
|
||||
let v = (v_angle/pi) - 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||
let uv = vec2<f32>(u,v); // Get UV on skybox
|
||||
return textureSample(sky_texture, sky_sampler, uv);
|
||||
}
|
||||
|
||||
struct SkyOutput {
|
||||
@builtin(position) position: vec4<f32>,
|
||||
@location(0) pos: vec4<f32> // unadulterated by WGSL
|
||||
}
|
||||
|
||||
const VERTICES = array(
|
||||
vec4(-1.0, -1.0, 1.0, 1.0),
|
||||
vec4(-1.0, 1.0, 1.0, 1.0),
|
||||
vec4( 1.0, -1.0, 1.0, 1.0),
|
||||
vec4( 1.0, 1.0, 1.0, 1.0),
|
||||
vec4(-1.0, 1.0, 1.0, 1.0),
|
||||
vec4( 1.0, -1.0, 1.0, 1.0),
|
||||
);
|
||||
|
||||
fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
|
||||
return mat3x3<f32>(
|
||||
it[0].xyz,
|
||||
it[1].xyz,
|
||||
it[2].xyz,
|
||||
);
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
|
||||
var out: SkyOutput;
|
||||
out.position = VERTICES[index];
|
||||
out.pos = out.position;
|
||||
return out;
|
||||
}
|
||||
|
||||
@fragment
|
||||
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
|
||||
let look = rotation(eye.frame) * in.pos.xyz;
|
||||
return sky_aspect(look);
|
||||
}
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
use image::EncodableLayout;
|
||||
use wgpu::{Device, Queue};
|
||||
use crate::render::Renderer;
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||
pub struct Texture {
|
||||
texture: wgpu::Texture,
|
||||
pub(crate) view: wgpu::TextureView,
|
||||
}
|
||||
pub struct TextureProperties {
|
||||
pub(crate) width: u32,
|
||||
pub(crate) height: u32,
|
||||
}
|
||||
// todo: use texture compression!
|
||||
impl Texture {
|
||||
pub fn depth(config: &wgpu::SurfaceConfiguration, device: &Device) -> Texture {
|
||||
let size = wgpu::Extent3d {
|
||||
// 2.
|
||||
width: config.width.max(1),
|
||||
height: config.height.max(1),
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let desc = wgpu::TextureDescriptor {
|
||||
label: Some("depth texture"),
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Depth32Float,
|
||||
usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
|
||||
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||
view_formats: &[],
|
||||
};
|
||||
let _depth_texture = device.create_texture(&desc);
|
||||
let depth_view = _depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
Texture {
|
||||
texture: _depth_texture,
|
||||
view: depth_view,
|
||||
}
|
||||
}
|
||||
pub fn load(
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
slice: impl AsRef<[u8]>,
|
||||
properties: TextureProperties,
|
||||
) -> Texture {
|
||||
let size = wgpu::Extent3d {
|
||||
width: properties.width,
|
||||
height: properties.height,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
label: Some("texture"),
|
||||
view_formats: &[],
|
||||
});
|
||||
let diffuse_texture_view =
|
||||
diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
queue.write_texture(
|
||||
wgpu::TexelCopyTextureInfo {
|
||||
texture: &diffuse_texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
},
|
||||
slice.as_ref(),
|
||||
wgpu::TexelCopyBufferLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: Some(4 * size.width),
|
||||
rows_per_image: Some(size.height),
|
||||
},
|
||||
size,
|
||||
);
|
||||
Texture {
|
||||
texture: diffuse_texture,
|
||||
view: diffuse_texture_view,
|
||||
}
|
||||
}
|
||||
fn _load_from_file_bytes(
|
||||
device: &Device,
|
||||
queue: &Queue,
|
||||
slice: impl AsRef<[u8]>,
|
||||
format: Option<image::ImageFormat>,
|
||||
) -> Option<Texture> {
|
||||
let image = if let Some(format) = format {
|
||||
image::load_from_memory_with_format(slice.as_ref(), format)
|
||||
} else {
|
||||
image::load_from_memory(slice.as_ref())
|
||||
};
|
||||
if let Ok(data) = image {
|
||||
let data = data.into_rgba8();
|
||||
Some(Texture::load(
|
||||
device,
|
||||
queue,
|
||||
data.as_bytes(),
|
||||
TextureProperties {
|
||||
width: data.width(),
|
||||
height: data.height(),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
println!("failed to load texture! error: {:?}", image.unwrap_err());
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_from_file_bytes(renderer: &mut Renderer, slice: impl AsRef<[u8]>, format: Option<image::ImageFormat>) -> Option<Texture> {
|
||||
Texture::_load_from_file_bytes(&renderer.device,&renderer.queue,slice,format)
|
||||
}
|
||||
}
|
||||
19
src/state.rs
19
src/state.rs
|
|
@ -1,19 +0,0 @@
|
|||
use crate::list::FreeList;
|
||||
use crate::{render, world};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
pub objects: FreeList<world::ObjectData>,
|
||||
pub lights: FreeList<render::instance::LightData>,
|
||||
pub worlds: FreeList<world::World>,
|
||||
pub renderer: Option<render::Renderer>,
|
||||
pub screens: FreeList<render::Screen>
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> State {
|
||||
State {
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,100 +0,0 @@
|
|||
use glam::{IVec3, Mat4, Quat, Vec3};
|
||||
use wgpu::naga::FastHashMap;
|
||||
use crate::list::{FreeList, Id};
|
||||
use crate::render;
|
||||
use crate::world::{ColliderData, ObjectData, OctreeBlock, Shape, World};
|
||||
|
||||
pub struct OctreeDebug {
|
||||
map: FastHashMap<IVec3, Id<ObjectData>>
|
||||
}
|
||||
|
||||
pub enum OctreeDebugOperation {
|
||||
Add(IVec3,i32),
|
||||
Remove(IVec3),
|
||||
}
|
||||
|
||||
impl Default for OctreeDebug {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl OctreeDebug {
|
||||
pub fn new() -> OctreeDebug {
|
||||
OctreeDebug {
|
||||
map: FastHashMap::default()
|
||||
}
|
||||
}
|
||||
fn set_block(
|
||||
&mut self,
|
||||
blocks: &mut FreeList<OctreeBlock>,
|
||||
block: Id<OctreeBlock>,
|
||||
objects: &mut FreeList<ObjectData>,
|
||||
debug: &Option<render::instance::ModelData>,
|
||||
pos: IVec3,
|
||||
mut size: i32
|
||||
) {
|
||||
let entry = self.map.get(&pos);
|
||||
if let Some(debug) = debug {
|
||||
if entry.is_none() {
|
||||
let mut model = debug.clone();
|
||||
model.instance.transform = Mat4::from_scale_rotation_translation(Vec3::splat(size as f32),Quat::IDENTITY,pos.as_vec3());
|
||||
self.map.insert(pos, objects.make(ObjectData {
|
||||
model: Some(model),
|
||||
collider: ColliderData {
|
||||
shape: Shape::Sphere(size as f32),
|
||||
},
|
||||
affine: Default::default(),
|
||||
asleep: false,
|
||||
}).id);
|
||||
println!("pos {} size {}",pos,size)
|
||||
}
|
||||
} else {
|
||||
if entry.is_some() {
|
||||
objects.remove(self.map.remove(&pos).unwrap());
|
||||
}
|
||||
}
|
||||
size /= 2;
|
||||
if let Some(block) = blocks.get(&block).blocks[0].exists() {
|
||||
self.set_block(blocks,block,objects,debug, pos - (IVec3::new(-1, -1, -1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[1].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,-1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[2].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,-1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[3].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,-1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[4].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,-1,1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[5].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[6].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,1) * size / 2), size);
|
||||
}
|
||||
if let Some(block) = blocks.get(&block).blocks[7].exists() {
|
||||
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,1) * size / 2), size);
|
||||
}
|
||||
}
|
||||
pub fn set(&mut self, objects: &mut FreeList<ObjectData>, world: &mut World, debug: Option<render::instance::ModelData>) {
|
||||
for (index,block) in world.matter.map.iter_mut() {
|
||||
self.set_block(
|
||||
&mut world.matter.blocks,
|
||||
block.clone(),
|
||||
objects,
|
||||
&debug,
|
||||
index * OctreeBlock::CHUNK_SIZE,
|
||||
OctreeBlock::CHUNK_SIZE,
|
||||
)
|
||||
}
|
||||
}
|
||||
pub fn register(&mut self, objects: &mut FreeList<ObjectData>, renderer: &mut render::Renderer) {
|
||||
for (pos,block) in self.map.iter() {
|
||||
renderer.instances.register_object(objects.get_ref(block.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
517
src/world/mod.rs
517
src/world/mod.rs
|
|
@ -1,379 +1,228 @@
|
|||
pub mod debug;
|
||||
|
||||
use crate::render::instance::ModelData;
|
||||
use glam::{Affine3, IVec3, Mat4, Quat, Vec3};
|
||||
use crate::render::{Renderer, SimpleLightData, SimpleModelData};
|
||||
use glam::{DVec3, IVec3, Mat4, UVec3, Vec3, Vec4Swizzles};
|
||||
use std::cell::RefCell;
|
||||
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
||||
use std::path::absolute;
|
||||
use std::ops::{Div, Mul};
|
||||
use std::rc::Rc;
|
||||
use mars::vm::Object;
|
||||
use wgpu::naga::{FastHashMap, FastHashSet};
|
||||
use crate::{list, render};
|
||||
use crate::list::{FreeList, Id, MaybeId, RefId, SingleId};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Shape {
|
||||
Block(Vec3),
|
||||
Sphere(f32),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ColliderData {
|
||||
pub struct SimpleColliderData {
|
||||
pub transform: Mat4,
|
||||
pub shape: Shape,
|
||||
pub radius: f32,
|
||||
}
|
||||
|
||||
impl ColliderData {
|
||||
fn aabb(&self, affine: Affine3) -> AABB {
|
||||
let (_scale, _rotation, translation) = affine.to_scale_rotation_translation();
|
||||
match self.shape {
|
||||
Shape::Block(size) => {
|
||||
let radius_offset = Vec3::splat(size.length());
|
||||
AABB(
|
||||
translation - radius_offset,
|
||||
translation + radius_offset,
|
||||
)
|
||||
}
|
||||
Shape::Sphere(radius) => {
|
||||
let radius_offset = Vec3::splat(radius);
|
||||
AABB(
|
||||
translation - radius_offset,
|
||||
translation + radius_offset,
|
||||
)
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct SimpleObjectData {
|
||||
pub model: Option<SimpleModelData>,
|
||||
pub collider: SimpleColliderData,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>);
|
||||
|
||||
impl PartialEq for SimpleLight {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for SimpleLight {}
|
||||
|
||||
impl Hash for SimpleLight {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ObjectData {
|
||||
pub model: Option<render::instance::ModelData>,
|
||||
pub collider: ColliderData,
|
||||
pub affine: Affine3,
|
||||
pub asleep: bool,
|
||||
}
|
||||
pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>);
|
||||
|
||||
impl ObjectData {
|
||||
pub fn set_affine(&mut self, affine: Affine3) {
|
||||
self.affine = affine;
|
||||
self.asleep = false;
|
||||
impl SimpleObject {
|
||||
pub(crate) fn hard_clone(&self) -> SimpleObject {
|
||||
SimpleObject {
|
||||
0: Rc::new(RefCell::new(SimpleObjectData {
|
||||
model: self.0.borrow().model.clone(),
|
||||
collider: self.0.borrow().collider.clone(),
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub enum InterestType {
|
||||
Light(Id<render::instance::LightData>),
|
||||
Object(Id<ObjectData>)
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Interest {
|
||||
it: InterestType,
|
||||
aabb: AABB,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InterestNode {
|
||||
interest: Interest,
|
||||
next: MaybeId<InterestNode>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct AABB(pub(crate) Vec3, pub(crate) Vec3);
|
||||
|
||||
impl AABB {
|
||||
pub(crate) fn new(translation: Vec3, size: Vec3) -> AABB {
|
||||
AABB(translation - size, translation + size)
|
||||
}
|
||||
pub fn extend_to(&self, point: Vec3) -> AABB {
|
||||
AABB(self.0.min(point),self.1.max(point))
|
||||
}
|
||||
pub fn bounded_by(&self, aabb: AABB) -> AABB {
|
||||
AABB(self.0.max(aabb.0), self.1.min(aabb.1))
|
||||
}
|
||||
fn offset(&self) -> Vec3 {
|
||||
self.1 - self.0
|
||||
}
|
||||
fn to_iaabb(&self) -> IAABB {
|
||||
IAABB(self.0.as_ivec3(),self.1.as_ivec3())
|
||||
impl PartialEq for SimpleObject {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
Rc::ptr_eq(&self.0, &other.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct IAABB(pub(crate) IVec3, pub(crate) IVec3);
|
||||
impl Eq for SimpleObject {}
|
||||
|
||||
impl IAABB {
|
||||
pub(crate) fn new(translation: IVec3, size: IVec3) -> IAABB {
|
||||
IAABB(translation - size, translation + size)
|
||||
impl Hash for SimpleObject {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state);
|
||||
}
|
||||
pub fn extend_to(&self, point: IVec3) -> IAABB {
|
||||
IAABB(self.0.min(point),self.1.max(point))
|
||||
}
|
||||
|
||||
#[derive(Eq, Hash, PartialEq, Clone)]
|
||||
enum Interest {
|
||||
Light(SimpleLight),
|
||||
Object(SimpleObject),
|
||||
}
|
||||
|
||||
struct Block {
|
||||
debug: Option<SimpleObject>,
|
||||
volume: u8,
|
||||
velocity: Vec3,
|
||||
material: u8,
|
||||
interests: FastHashSet<Interest>,
|
||||
blocks: [Option<Box<Block>>; 64],
|
||||
}
|
||||
|
||||
const TREE_ATTACK: usize = 4;
|
||||
const TREE_FLOOR: usize = 4;
|
||||
impl Block {
|
||||
fn new() -> Block {
|
||||
Block {
|
||||
debug: None,
|
||||
volume: 0,
|
||||
velocity: Vec3::ZERO,
|
||||
material: 0,
|
||||
interests: FastHashSet::with_hasher(BuildHasherDefault::default()),
|
||||
blocks: [const { None }; const { TREE_ATTACK * TREE_ATTACK * TREE_ATTACK }],
|
||||
}
|
||||
pub fn bounded_by(&self, aabb: IAABB) -> IAABB {
|
||||
IAABB(self.0.max(aabb.0), self.1.min(aabb.1))
|
||||
}
|
||||
fn offset(&self) -> IVec3 {
|
||||
self.1 - self.0
|
||||
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32, block_size: f32) {
|
||||
let cs = block_size / const { TREE_ATTACK as f32 };
|
||||
if rad < cs || block_size >= TREE_FLOOR as f32 {
|
||||
let rel = ((pos + block_size / 2.0) / cs).round().as_uvec3();
|
||||
let new_pos = pos - (rel.as_vec3() + cs / 2.0);
|
||||
let index = (rel.x + rel.y * 4 + rel.z * 16) as usize;
|
||||
match self.blocks[index] {
|
||||
Some(ref mut block) => {
|
||||
block.place_pos(interest, new_pos, rad, cs);
|
||||
}
|
||||
None => {
|
||||
let mut block = Box::new(Block::new());
|
||||
block.place_pos(interest, new_pos, rad, cs);
|
||||
self.blocks[index] = Some(block);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self.interests.insert(interest);
|
||||
}
|
||||
}
|
||||
fn debug_step(&mut self, value: bool) {
|
||||
match self.debug {
|
||||
Some(ref mut debug) => {
|
||||
if value {
|
||||
} else {
|
||||
}
|
||||
}
|
||||
None => {
|
||||
if value {
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
for maybe_block in self.blocks.iter_mut() {
|
||||
if let Some(block) = maybe_block {
|
||||
block.debug_step(value)
|
||||
}
|
||||
}
|
||||
fn to_aabb(self) -> AABB {
|
||||
AABB(self.0.as_vec3(),self.1.as_vec3())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct World {
|
||||
pub(crate) matter: OctreeMap,
|
||||
}
|
||||
|
||||
impl Default for World {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
chunk_size: u32,
|
||||
map: FastHashMap<IVec3, Block>,
|
||||
pub renderer: Option<Renderer>,
|
||||
debug_world_map: bool,
|
||||
debug_world_map_object: Option<SimpleObject>,
|
||||
}
|
||||
|
||||
impl World {
|
||||
pub fn new() -> World {
|
||||
World {
|
||||
matter: OctreeMap::new(),
|
||||
|
||||
}
|
||||
}
|
||||
pub fn add_object(&mut self, object: RefId<ObjectData>) -> Id<ObjectData> {
|
||||
self.matter.place_interest(Interest {
|
||||
it: InterestType::Object(object.id.clone()),
|
||||
aabb: object.collider.aabb(object.affine),
|
||||
});
|
||||
object.id
|
||||
}
|
||||
pub fn remove_object(&mut self, object: Id<ObjectData>) {
|
||||
|
||||
}
|
||||
pub fn step(
|
||||
&mut self,
|
||||
maybe_renderer: &mut Option<render::Renderer>,
|
||||
objects: &mut FreeList<ObjectData>,
|
||||
_lights: &mut FreeList<render::instance::LightData>,
|
||||
) {
|
||||
fn reinsert(
|
||||
block_id: &Id<OctreeBlock>,
|
||||
blocks: &mut FreeList<OctreeBlock>,
|
||||
interests: &mut FreeList<InterestNode>,
|
||||
objects: &mut FreeList<ObjectData>
|
||||
) {
|
||||
let mut maybe_interest = &blocks.get(block_id).first;
|
||||
while let Some(interest_id) = maybe_interest.exists() {
|
||||
let interest = interests.get(&interest_id);
|
||||
match interest.interest.it {
|
||||
InterestType::Object(ref object_id) => {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
maybe_interest = &interest.next;
|
||||
}
|
||||
for id in blocks.get(block_id).blocks.clone().iter() {
|
||||
if let Some(id) = id.exists() {
|
||||
reinsert(&id, blocks, interests, objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_pos,id) in self.matter.map.iter() {
|
||||
reinsert(id, &mut self.matter.blocks, &mut self.matter.nodes, objects)
|
||||
}
|
||||
if let Some(renderer) = maybe_renderer {
|
||||
fn consider(
|
||||
block_id: &Id<OctreeBlock>,
|
||||
renderer: &mut render::Renderer,
|
||||
blocks: &mut FreeList<OctreeBlock>,
|
||||
interests: &mut FreeList<InterestNode>,
|
||||
objects: &mut FreeList<ObjectData>
|
||||
) {
|
||||
let mut maybe_interest = &blocks.get(block_id).first;
|
||||
while let Some(interest_id) = maybe_interest.exists() {
|
||||
let interest = interests.get(&interest_id);
|
||||
match interest.interest.it {
|
||||
InterestType::Object(ref object_id) => {
|
||||
let mut object = objects.get_ref(object_id.clone());
|
||||
let transform = Mat4::from_mat3_translation(object.affine.matrix3,object.affine.translation);
|
||||
object.model.as_mut().unwrap().instance.transform = transform;
|
||||
renderer.instances.register_object(object)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
maybe_interest = &interest.next;
|
||||
}
|
||||
for id in blocks.get(block_id).blocks.clone().iter() {
|
||||
if let Some(id) = id.exists() {
|
||||
consider(&id, renderer, blocks, interests, objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_pos,id) in self.matter.map.iter() {
|
||||
consider(id, renderer, &mut self.matter.blocks, &mut self.matter.nodes, objects)
|
||||
}
|
||||
}
|
||||
}
|
||||
fn sync(&mut self) {}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct Material {
|
||||
volume: u8,
|
||||
velocity: Vec3,
|
||||
material: u8,
|
||||
}
|
||||
|
||||
impl Material {
|
||||
const fn new() -> Material {
|
||||
Material {
|
||||
volume: 0,
|
||||
velocity: Vec3::ZERO,
|
||||
material: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OctreeBlock {
|
||||
const FLOOR: i32 = 4;
|
||||
const CHUNK_SIZE: i32 = OctreeBlock::FLOOR * 64;
|
||||
const MAX_IDEAL_INTEREST: usize = 4;
|
||||
fn new() -> OctreeBlock {
|
||||
OctreeBlock {
|
||||
interests: 0,
|
||||
material: Material::new(),
|
||||
first: MaybeId::NULL,
|
||||
blocks: [MaybeId::NULL;8],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct OctreeBlock {
|
||||
interests: u32, // 1
|
||||
first: MaybeId<InterestNode>, // 1
|
||||
material: Material, // 4
|
||||
blocks: Blocks, // 8
|
||||
}
|
||||
|
||||
impl OctreeBlock {
|
||||
fn push_interest(&mut self, interests: &mut FreeList<InterestNode>, interest: Interest) {
|
||||
self.first = interests.make(InterestNode {
|
||||
interest: interest.clone(),
|
||||
next: self.first.clone(),
|
||||
}).id.maybe();
|
||||
self.interests += 1;
|
||||
}
|
||||
fn remove_interest(&mut self, interests: &mut FreeList<InterestNode>, needle: Interest) { // todo: needed?
|
||||
let mut maybe_last = MaybeId::NULL;
|
||||
let mut maybe_this = self.first.clone();
|
||||
while let Some(this) = maybe_this.exists() {
|
||||
let interest = interests.get(&this);
|
||||
let next = interest.next.clone();
|
||||
if interest.interest.it == needle.it {
|
||||
self.interests -= 1;
|
||||
if let Some(last) = maybe_last.exists() {
|
||||
interests.get(&last).next = interest.next.clone();
|
||||
} else {
|
||||
self.first = interest.next.clone();
|
||||
break;
|
||||
}
|
||||
}
|
||||
maybe_last = maybe_this;
|
||||
maybe_this = next;
|
||||
}
|
||||
panic!()
|
||||
}
|
||||
fn place(
|
||||
it: Id<OctreeBlock>,
|
||||
blocks: &mut FreeList<OctreeBlock>,
|
||||
interests: &mut FreeList<InterestNode>,
|
||||
interest: Interest,
|
||||
pos: IVec3,
|
||||
size: i32,
|
||||
) {
|
||||
if pos == IVec3::ZERO || blocks.get(&it).interests < OctreeBlock::MAX_IDEAL_INTEREST as u32 {
|
||||
blocks.get(&it).push_interest(interests, interest)
|
||||
} else {
|
||||
let mut index = 0;
|
||||
let x = if pos.x > 0 {
|
||||
index += 1;
|
||||
-size
|
||||
} else {
|
||||
size
|
||||
};
|
||||
let y = if pos.y > 0 {
|
||||
index += 2;
|
||||
-size
|
||||
} else {
|
||||
size
|
||||
};
|
||||
let z = if pos.z > 0 {
|
||||
index += 4;
|
||||
-size
|
||||
} else {
|
||||
size
|
||||
};
|
||||
let offset = IVec3::new(x,y,z) / 2;
|
||||
println!("pos {} {} {}",pos,pos + offset,size);
|
||||
let id = if let Some(id) = blocks.get(&it).blocks[index as usize].exists() {
|
||||
id
|
||||
} else {
|
||||
let id = blocks.make(OctreeBlock::new()).id;
|
||||
blocks.get(&it).blocks[index as usize] = id.clone().into();
|
||||
id
|
||||
};
|
||||
OctreeBlock::place(id,blocks,interests,interest,pos + offset,size / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Blocks = [MaybeId<OctreeBlock>; 8];
|
||||
|
||||
pub struct OctreeMap {
|
||||
interests: FreeList<Interest>,
|
||||
nodes: FreeList<InterestNode>,
|
||||
blocks: FreeList<OctreeBlock>,
|
||||
map: FastHashMap<IVec3, Id<OctreeBlock>>,
|
||||
}
|
||||
|
||||
impl OctreeMap {
|
||||
fn place_interest(&mut self, interest: Interest) {
|
||||
let mut d = 4;
|
||||
let offset = interest.aabb.1 - interest.aabb.0;
|
||||
while offset.element_sum() > (d * 2) as f32 {
|
||||
d *= 2
|
||||
}
|
||||
let iaabb = IAABB(
|
||||
(interest.aabb.0 / d as f32).floor().as_ivec3() + IVec3::splat(d / 2),
|
||||
(interest.aabb.1 / d as f32).floor().as_ivec3() + IVec3::splat(d / 2),
|
||||
);
|
||||
println!("attempting place I: {} {} {} A: {} {}",iaabb.0,iaabb.1,d,interest.aabb.0,interest.aabb.1);
|
||||
for x in iaabb.0.x..iaabb.1.x {
|
||||
for y in iaabb.0.y..iaabb.1.y {
|
||||
for z in iaabb.0.z..iaabb.1.z {
|
||||
let index = IVec3::new(x,y,z);
|
||||
let absolute_pos = index * d;
|
||||
let chunk_index = absolute_pos / OctreeBlock::CHUNK_SIZE;
|
||||
let chunk_pos = chunk_index * OctreeBlock::CHUNK_SIZE;
|
||||
let block = self.map.entry(chunk_index).or_insert_with(|| {
|
||||
self.blocks.make(OctreeBlock::new()).id
|
||||
});
|
||||
let pos = absolute_pos - chunk_pos;
|
||||
println!("placing {} {}",pos,OctreeBlock::CHUNK_SIZE);
|
||||
OctreeBlock::place(
|
||||
block.clone(),
|
||||
&mut self.blocks,
|
||||
&mut self.nodes,
|
||||
interest.clone(),
|
||||
pos,
|
||||
OctreeBlock::CHUNK_SIZE,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn new() -> OctreeMap {
|
||||
OctreeMap {
|
||||
interests: Default::default(),
|
||||
nodes: Default::default(),
|
||||
blocks: Default::default(),
|
||||
chunk_size: 512,
|
||||
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
|
||||
renderer: None,
|
||||
debug_world_map: false,
|
||||
debug_world_map_object: None,
|
||||
}
|
||||
}
|
||||
pub fn add_renderer(&mut self, renderer: Renderer) {
|
||||
self.renderer = Some(renderer);
|
||||
self.debug_world_map_object = self
|
||||
.renderer
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.load_from_gltf(include_bytes!("../assets/cube.glb"))
|
||||
.first_object()
|
||||
}
|
||||
pub fn add_object(&mut self, object: SimpleObject) {
|
||||
self.place(Interest::Object(object.clone()));
|
||||
if let Some(ref mut renderer) = self.renderer {
|
||||
renderer.instances.add_object(object)
|
||||
}
|
||||
}
|
||||
pub fn set_debug(&mut self, value: bool) {
|
||||
self.debug_world_map = value;
|
||||
}
|
||||
pub fn light_step(&mut self) {}
|
||||
pub fn object_step(&mut self) {}
|
||||
pub fn debug_step(&mut self) {
|
||||
self.light_step();
|
||||
self.object_step();
|
||||
for (index, item) in self.map.iter_mut() {
|
||||
item.debug_step(self.debug_world_map);
|
||||
}
|
||||
}
|
||||
fn place_pos(&mut self, interest: Interest, pos: Vec3, rad: f32) {
|
||||
let cs = self.chunk_size as f32;
|
||||
let c_rad = ((cs / 2.0) * (cs / 2.0)) * 3.0;
|
||||
for x in (pos.x - rad).div(cs) as i32..=(pos.x + rad).div(cs).ceil() as i32 {
|
||||
for y in (pos.y - rad).div(cs) as i32..=(pos.y + rad).div(cs).ceil() as i32 {
|
||||
for z in (pos.z - rad).div(cs) as i32..=(pos.z + rad).div(cs).ceil() as i32 {
|
||||
let block_pos = IVec3::new(x, y, z);
|
||||
let block_pos_f32 = block_pos.as_vec3();
|
||||
if block_pos_f32.distance_squared(pos) < rad + c_rad {
|
||||
self.map.entry(block_pos).or_insert_with(|| {
|
||||
let mut block = Block::new();
|
||||
block.place_pos(
|
||||
interest.clone(),
|
||||
pos - block_pos_f32,
|
||||
rad,
|
||||
self.chunk_size as f32,
|
||||
);
|
||||
block
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn place(&mut self, interest: Interest) {
|
||||
let (pos, rad) = match &interest {
|
||||
Interest::Light(light) => {
|
||||
let light = light.0.borrow();
|
||||
(light.instance.location.xyz(), light.instance.color.length())
|
||||
}
|
||||
Interest::Object(object) => {
|
||||
let collider = &object.0.borrow().collider;
|
||||
(
|
||||
collider.transform.to_scale_rotation_translation().2,
|
||||
collider.radius,
|
||||
)
|
||||
}
|
||||
};
|
||||
self.place_pos(interest, pos, rad);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue