diff --git a/.gitignore b/.gitignore index 54088d5..83ea247 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target .DS_Store +Cargo.lock \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 30cf57e..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# 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 diff --git a/.idea/Pool.iml b/.idea/Pool.iml index 0cd351f..cf84ae4 100644 --- a/.idea/Pool.iml +++ b/.idea/Pool.iml @@ -3,13 +3,7 @@ - - - - - - diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml deleted file mode 100644 index 4c3305b..0000000 --- a/.idea/dictionaries/project.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - forloop - vmcase - - - \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 0073a97..823c29a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,18 @@ [package] #![recursion_limit = "256"] -name = "game" +name = "pool" version = "0.1.0" edition = "2024" +[features] +big_ids = [] +check_ids = [] + [profile.release] strip = true [[bin]] -name = "game" +name = "pool" path = "src/main.rs" [dependencies] @@ -17,7 +21,7 @@ anyhow = "1.0" winit = { version = "0.30", features = ["android-native-activity"] } env_logger = "0.11.10" log = "0.4" -wgpu = "29.0.3" +wgpu = "29.0.4" pollster = "0.4.0" glam = { version = "0.33.3", features = [ "bytemuck" ] } console_error_panic_hook = "0.1.7" @@ -27,7 +31,7 @@ mars = { path = "../Mars" } [target.'cfg(target_arch = "wasm32")'.dependencies] console_error_panic_hook = "0.1.6" -wgpu = { version = "29.0.3", features = ["webgl"]} +wgpu = { version = "30.0.1", features = ["webgl"]} wasm-bindgen = "0.2.121" wasm-bindgen-futures = "0.4.71" console_log = "1.0.0" diff --git a/README.md b/README.md index 37a444e..c51d11c 100644 --- a/README.md +++ b/README.md @@ -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 designed to push the boundaries of what defines a contemporary user experience. +Pool is a lightweight web-enabled multipurpose engine for UI apps and multiplayer games with physics. Taking inspiration from the Roblox game engine, Pool ships with its own scripting language, Mars, and shares a similar instance-service model. diff --git a/src/app.rs b/src/app.rs index 0fb8a80..7c68fad 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,8 @@ 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::Renderer; -use crate::world::{SimpleObject, World}; -use glam::{Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4}; +use crate::render::{ModelData, Renderer}; +use crate::world::{World, ObjectData, OctreeDebug}; +use glam::{Affine3, Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4}; use std::sync::Arc; #[cfg(target_arch = "wasm32")] use wasm_bindgen::prelude::*; @@ -16,6 +16,8 @@ use winit::{ keyboard::{KeyCode, PhysicalKey}, window::Window, }; +use crate::list::Id; +use crate::state; struct Controller { buttons: HashMap, @@ -34,68 +36,66 @@ impl Controller { } pub struct AppState { - world: World, + state: state::State, + world: Id, + debug: OctreeDebug, + debug_model: ModelData, controller: Controller, window: Arc, - clients: Vec, } -const BLOCKS: i32 = 4; +const BLOCKS: i32 = 2; 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) -> Result> { - let mut world = World::new(); - world.add_renderer(Renderer::new(&window).await?); - let mut clients = Vec::new(); - { - let file = world - .renderer - .as_mut() - .unwrap() - .load_from_gltf(include_bytes!("assets/sphere.glb")); - let block = file.first_object().unwrap(); - let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes( - include_bytes!("assets/skybox2.png"), - Some(image::ImageFormat::Png), - ); - world.renderer().set_skybox(skybox); - let color = world - .renderer() - .load_texture_from_bytes(include_bytes!("assets/plank/color.png"), None); - let normal = world - .renderer() - .load_texture_from_bytes(include_bytes!("assets/plank/normal.png"), None); - let roughness = world - .renderer() - .load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"), None); - let mat = world.renderer().new_material(&color, &normal, &roughness); - block.0.borrow_mut().model.as_mut().unwrap().material = mat; - 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 * 3.0, -2.0, -y as f32 * 3.0), - ) - * Mat4::from_rotation_z((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, - ); + 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 = renderer.load_from_gltf(include_bytes!("assets/debug.glb")); + let debug_model = debug_file.first_object().unwrap().model.unwrap(); + { + let file = renderer.load_from_gltf(include_bytes!("assets/sphere.glb")); + let mut block = file.first_object().unwrap(); + let skybox = renderer.load_texture_from_bytes( + include_bytes!("assets/skybox2.png"), + Some(image::ImageFormat::Png), + ); + renderer.set_skybox(skybox); + let color = renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"), None); + let normal = renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"), None); + let roughness = renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"), None); + let mat = renderer.new_material(&color, &normal, &roughness); + 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.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, + ); + } } - clients.push(block) } } - } + let debug = OctreeDebug::new(); + (world.id,debug,debug_model) + }; Ok(Self { + state, world, - clients, + debug, + debug_model, controller: Controller::new(), window, }) @@ -108,7 +108,7 @@ impl AppState { pub fn resize(&mut self, width: u32, height: u32) { if width > 0 && height > 0 { - self.world.renderer.as_mut().unwrap().resize(width, height); + self.state.renderer.as_mut().unwrap().resize(width, height); } } @@ -121,7 +121,7 @@ impl AppState { (KeyCode::Escape, true) => event_loop.exit(), (KeyCode::Space, true) => {} (KeyCode::KeyR, true) => { - self.world.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY + self.state.renderer.as_mut().unwrap().eye.frame = Affine3A::IDENTITY } _ => {} } @@ -130,7 +130,7 @@ impl AppState { fn handle_mouse_moved(&mut self, position: PhysicalPosition) { if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { - self.world.renderer.as_mut().unwrap().eye.rotate( + self.state.renderer.as_mut().unwrap().eye.rotate( position.x as f32 - self.controller.mouse.x, position.y as f32 - self.controller.mouse.y, ); @@ -182,6 +182,8 @@ impl ApplicationHandler 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 @@ -238,15 +240,14 @@ impl ApplicationHandler 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| { - if let Some(true) = state.controller.keys.get(&keycode) { - true - } else { - false - } + matches!(state.controller.keys.get(&keycode), Some(true)) }; if pressed(KeyCode::KeyA) { @@ -267,15 +268,12 @@ impl ApplicationHandler for App { if pressed(KeyCode::KeyQ) { movement.y -= 1.0; } - - state - .world - .renderer - .as_mut() - .unwrap() - .eye - .control(movement * 0.1); - match state.world.renderer.as_mut().unwrap().render(&state.window) { + 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) { Ok(_) => {} Err(e) => { // Log the error and exit gracefully @@ -283,19 +281,9 @@ impl ApplicationHandler for App { event_loop.exit(); } } - 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.001, -0.001, 0.001), - Vec3::new(0.0, 0.0, 0.0), - ); - } + + let elapsed = now.elapsed(); + //println!("Elapsed {:.2?}",elapsed); } WindowEvent::MouseInput { button, diff --git a/src/assets/SimpleShader.wgsl b/src/assets/SimpleShader.wgsl index f8c7b12..87ac451 100644 --- a/src/assets/SimpleShader.wgsl +++ b/src/assets/SimpleShader.wgsl @@ -95,17 +95,20 @@ fn sky_aspect(look: vec3) -> vec4 { return textureSample(sky_texture, sky_sampler, uv); } -fn rotation(mat: mat4x4) -> mat3x3 { +fn rotation(it: mat4x4) -> mat3x3 { return mat3x3( - mat[0].xyz, - mat[1].xyz, - mat[2].xyz, + it[0].xyz, + it[1].xyz, + it[2].xyz, ); } -fn translation(mat: mat4x4) -> vec4 { - //return vec4(mat[0][3],mat[1][3],mat[2][3],mat[3][3]); - return mat[3]; +fn translation(it: mat4x4) -> vec4 { + return it[3]; +} + +fn light_aspect(light: vec3, dir: vec3) -> vec4 { + return vec4(0.0,0.0,0.0,0.0); } @fragment @@ -131,7 +134,7 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4 { //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 = max(object_rough.x * 0.5,object_rough.y); + let reflect_factor = pow(object_rough.x,3); let diffuse_brightness = length(diffuse_color); diff --git a/src/assets/debug.glb b/src/assets/debug.glb index 13fe9af..9f1f226 100644 Binary files a/src/assets/debug.glb and b/src/assets/debug.glb differ diff --git a/src/lib.rs b/src/lib.rs index 6c22f2e..45cd487 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,4 +2,6 @@ pub mod app; pub mod render; pub mod web; -pub mod world; \ No newline at end of file +pub mod world; +pub mod list; +pub mod state; \ No newline at end of file diff --git a/src/list.rs b/src/list.rs new file mode 100644 index 0000000..c1eb01f --- /dev/null +++ b/src/list.rs @@ -0,0 +1,185 @@ +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 { + it: Option, + count: Index, +} + +pub struct FreeList { + items: Vec>, + free: Vec, +} + +#[derive(Eq,Hash,PartialEq)] +pub struct SingleId { + index: Index, + phantom_data: PhantomData +} + +impl From> for Id { + fn from(value: SingleId) -> Self { + Id { + index: value.index, + phantom_data: PhantomData, + } + } +} + +impl SingleId { + pub(crate) fn shared(self) -> Id { + Id { + index: self.index, + phantom_data: self.phantom_data + } + } +} + +pub struct Id { + index: Index, + phantom_data: PhantomData, +} + +pub struct RefId<'a, T> { + pub id: Id, + 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 From> for Id { + fn from(value: RefId) -> Self { + value.id + } +} + +impl Clone for Id { + fn clone(&self) -> Self { + Id { + index: self.index, + phantom_data: PhantomData::default(), + } + } +} + +impl PartialEq for Id { + fn eq(&self, other: &Self) -> bool { + self.index == other.index + } +} + +impl Eq for Id {} + +impl Hash for Id { + fn hash(&self, state: &mut H) { + self.index.hash(state) + } +} + +impl Id { + pub fn maybe(self) -> MaybeId { + MaybeId(self) + } +} + +pub struct MaybeId(Id); + +impl From> for MaybeId { + fn from(value: Id) -> Self { + MaybeId(value) + } +} + +impl Clone for MaybeId { + fn clone(&self) -> Self { + if self.0.index != u32::MAX { + MaybeId(self.0.clone()) + } else { + MaybeId::NULL + } + } +} + +impl MaybeId { + pub const NULL: MaybeId = MaybeId(Id { index: u32::MAX, phantom_data: PhantomData {}, }); + pub fn unwrap(self) -> Id { + if self.0.index == u32::MAX { + panic!() + } else { + self.0 + } + } + pub fn exists(&self) -> Option> { + if self.0.index == u32::MAX { + None + } else { + Some(self.0.clone()) + } + } +} + +impl Default for FreeList { + fn default() -> Self { + Self::new() + } +} + +impl FreeList { + pub fn new() -> FreeList { + FreeList { + items: Vec::new(), + free: Vec::new(), + } + } + pub fn get(&mut self, id: &Id) -> &mut T { + self.items[id.index as usize].as_mut().unwrap() + } + pub fn get_ref(&mut self, id: Id) -> RefId<'_, T> { + let it = self.get(&id); + RefId { id, it } + } + pub fn remove(&mut self, id: Id) -> 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(), + } + } + } +} diff --git a/src/render/eye.rs b/src/render/eye.rs index 21fd621..83ca7e2 100644 --- a/src/render/eye.rs +++ b/src/render/eye.rs @@ -1,4 +1,4 @@ -use crate::render::{MaterialProperties, SimpleTexture}; +use crate::render::{MaterialProperties, Texture}; use bytemuck::{Pod, Zeroable}; use glam::camera::lh::proj::directx::perspective; use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; @@ -49,7 +49,7 @@ impl Eye { bytemuck::cast_slice(&[self.environment]), ); } - pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: SimpleTexture) -> Eye { + pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: Texture) -> Eye { let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Camera Buffer"), contents: bytemuck::cast_slice(&[ @@ -136,7 +136,7 @@ impl Eye { device: &wgpu::Device, camera: &wgpu::Buffer, environment: &wgpu::Buffer, - skybox: SimpleTexture, + skybox: Texture, ) -> wgpu::BindGroup { device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &layout, @@ -163,7 +163,7 @@ impl Eye { label: Some("eye_bind_group"), }) } - pub fn skybox(&mut self, device: &wgpu::Device, texture: SimpleTexture) { + pub fn skybox(&mut self, device: &wgpu::Device, texture: Texture) { self.group = Eye::bind_group( &self.layout, device, @@ -172,8 +172,9 @@ impl Eye { texture, ) } - pub(crate) fn resize(&mut self, width: u32, height: u32) { - self.aspect_ratio = width as f32 / height as f32 + 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 control(&mut self, delta: Vec3) { self.frame *= Affine3A::from_translation(delta); diff --git a/src/render/mod.rs b/src/render/mod.rs index f6d2761..e6da105 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,7 +1,7 @@ pub mod eye; use crate::render::eye::Eye; -use crate::world::{Shape, SimpleColliderData, SimpleLight, SimpleObject, SimpleObjectData}; +use crate::world::{Shape, ColliderData, ObjectData, World, AABB}; use bytemuck::{Pod, Zeroable}; use glam::prelude::*; use gltf::Semantic; @@ -18,12 +18,18 @@ use wgpu::naga::{FastHashMap, FastHashSet}; use wgpu::util::DeviceExt; use wgpu::{Device, Queue}; use winit::window::Window; +use crate::list::{FreeList, SingleId, Id, RefId}; +use crate::world; -const DEFAULT_VERTICES: [SimpleVertex; 0] = []; +const DEFAULT_VERTICES: [TangentVertex; 0] = []; + +pub struct Screen { + +} #[repr(C)] #[derive(Pod, Zeroable, Copy, Clone)] -pub struct SimpleModelInstance { +pub struct ModelInstance { pub transform: Mat4, pub color: Vec4, pub lights: [u16; 16], @@ -35,64 +41,61 @@ pub struct SimpleModelInstance { #[repr(C)] #[derive(Pod, Copy, Clone, Zeroable)] -pub struct SimpleLightInstance { +pub struct LightInstance { pub location: Vec4, pub rotation: Vec4, pub color: Vec4, } #[derive(Clone)] -pub struct SimpleLightData { - pub index: usize, - pub instance: SimpleLightInstance, +pub struct LightData { + pub instance: LightInstance, pub transform: Affine3, } #[derive(Clone)] -pub struct SimpleModelData { - pub instance: SimpleModelInstance, - pub material: Id, - pub mesh: Id, +pub struct ModelData { + pub instance: ModelInstance, + pub material: Id, + pub mesh: Id, } -pub struct SimpleInstances { +pub struct Instances { light_count: usize, light_buffer: wgpu::Buffer, instance_count: usize, instance_buffer: wgpu::Buffer, - //light_ref_last: usize, - //light_ref_buffer: wgpu::Buffer, - objects: - FastHashMap, FastHashMap, FastHashSet>>, - lights: FastHashMap, + models_flag: bool, + program: Program, + models: FastHashMap, FastHashMap, FastHashMap,bool>>>, } -enum SimpleRenderCode { - Material(wgpu::BindGroup), - Mesh((wgpu::Buffer, u32), Option<(wgpu::Buffer, u32)>), +enum Code { + Material(Material), + Mesh(Mesh), Draw(Range), } -struct SimpleRenderProgram(Vec); +struct Program(Vec); -impl SimpleRenderProgram { - fn push(&mut self, item: SimpleRenderCode) { +impl Program { + fn push(&mut self, item: Code) { self.0.push(item) } - fn new() -> SimpleRenderProgram { - SimpleRenderProgram(Vec::new()) + fn new() -> Program { + Program(Vec::new()) } fn render(self, pass: &mut wgpu::RenderPass) { let mut count = 0; let mut indexed = false; for code in self.0 { match code { - SimpleRenderCode::Material(material) => pass.set_bind_group( + Code::Material(material) => pass.set_bind_group( Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION, - &material, + &material.group, &[], ), - SimpleRenderCode::Mesh(vertices, indices) => { + 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); @@ -103,7 +106,7 @@ impl SimpleRenderProgram { indexed = false; } } - SimpleRenderCode::Draw(instances) => { + Code::Draw(instances) => { if indexed { pass.draw_indexed(0..count, 0, instances) } else { @@ -115,42 +118,20 @@ impl SimpleRenderProgram { } } -impl SimpleInstances { +impl Instances { const MIN_SIZE: u64 = 64; - pub fn add_object(&mut self, object: SimpleObject) { - if let Some(ref model) = object.0.borrow().model { - self.instance_count += 1; - self.objects - .entry(model.mesh.clone()) - .or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) - .entry(model.material.clone()) - .or_insert(FastHashSet::with_hasher(BuildHasherDefault::new())) - .insert(object.clone()); - } + pub fn register_object(&mut self, mut object: RefId) { + let model = object.model.as_mut().unwrap(); + self.instance_count += 1; + self.models + .entry(model.mesh.clone()).or_default() + .entry(model.material.clone()).or_default() + .insert(object.into(),self.models_flag); } - - pub fn remove_object(&mut self, object: SimpleObject) { - if let Some(ref model) = object.0.borrow().model { - self.instance_count -= 1; - self.objects - .entry(model.mesh.clone()) - .or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) - .entry(model.material.clone()) - .or_insert(FastHashSet::with_hasher(BuildHasherDefault::new())) - .remove(&object); - } - } - - pub fn add_light(&mut self, light: SimpleLight) { + /*pub fn register_light(&mut self, light: Id) { self.light_count += 1; - self.lights.insert(light, 0); - } - - pub fn remove_light(&mut self, light: &SimpleLight) { - self.light_count -= 1; - self.lights.remove(light); - } - + self.lights.insert(light); + }*/ pub fn reallocate_buffer( device: &wgpu::Device, buffer: &mut wgpu::Buffer, @@ -158,7 +139,7 @@ impl SimpleInstances { item_size: usize, ) { let size = buffer.size() / item_size as wgpu::BufferAddress; - if count > SimpleInstances::MIN_SIZE as usize { + if count > Instances::MIN_SIZE as usize { let mut reallocate: Option = None; if count < (size / 2) as usize { reallocate = Some(count / 2); @@ -175,7 +156,7 @@ impl SimpleInstances { } } } - pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + /*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::()); /* let mut light_ref_buffer = queue.write_buffer_with( @@ -184,11 +165,11 @@ impl SimpleInstances { wgpu::BufferSize::new(self.instance_buffer.size()).unwrap() ).unwrap(); */ - SimpleInstances::reallocate_buffer( + Instances::reallocate_buffer( device, &mut self.light_buffer, self.light_count, - size_of::(), + size_of::(), ); let mut buffer = queue .write_buffer_with( @@ -197,7 +178,7 @@ impl SimpleInstances { wgpu::BufferSize::new(self.light_buffer.size()).unwrap(), ) .unwrap(); - let stride = size_of::(); + let stride = size_of::(); for (new_index, (light, index)) in self.lights.iter_mut().enumerate() { *index = new_index + 1; let begin = *index * stride; @@ -205,18 +186,20 @@ impl SimpleInstances { .slice(begin..begin + stride) .copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance])); } - } + }*/ fn write_instances( &mut self, + mesh_list: &mut FreeList, + material_list: &mut FreeList, + objects_list: &mut FreeList, device: &wgpu::Device, queue: &wgpu::Queue, - ) -> SimpleRenderProgram { - let mut program = SimpleRenderProgram::new(); - SimpleInstances::reallocate_buffer( + ) { + Instances::reallocate_buffer( device, &mut self.instance_buffer, self.instance_count, - size_of::(), + size_of::(), ); let mut buffer = queue .write_buffer_with( @@ -226,37 +209,33 @@ impl SimpleInstances { ) .unwrap(); let mut index: u32 = 0; - let stride = size_of::(); - for (mesh, materials) in self.objects.iter() { - program.push(SimpleRenderCode::Mesh( - mesh.it.vertices.clone(), - mesh.it.indices.clone(), - )); - for (material, objects) in materials.iter() { - program.push(SimpleRenderCode::Material(material.it.group.clone())); + let stride = size_of::(); + 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(material_list.get(material).clone())); let before = index; - for object in objects.iter() { - let begin = index as usize * stride; - buffer - .slice(begin..begin + stride) - .copy_from_slice(bytemuck::cast_slice(&[object - .0 - .borrow() - .model - .as_ref() - .unwrap() - .instance])); - index += 1; - } - program.push(SimpleRenderCode::Draw(Range::from(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)); } } - program + //println!("objects: {}",index); + self.models_flag = !self.models_flag; } fn desc() -> wgpu::VertexBufferLayout<'static> { wgpu::VertexBufferLayout { - array_stride: size_of::() as wgpu::BufferAddress, + array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Instance, attributes: &[ // todo: is this too big? @@ -304,17 +283,17 @@ impl SimpleInstances { } } - pub fn new(device: &wgpu::Device) -> SimpleInstances { + pub fn new(device: &wgpu::Device) -> Instances { let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Instance Buffer"), - size: (size_of::() * SimpleInstances::MIN_SIZE as usize) + size: (size_of::() * 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::() * SimpleInstances::MIN_SIZE as usize) + size: (size_of::() * Instances::MIN_SIZE as usize) as wgpu::BufferAddress, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, @@ -325,38 +304,44 @@ impl SimpleInstances { usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, });*/ - SimpleInstances { + Instances { light_count: 0, //light_ref_last: 0, instance_count: 0, light_buffer, instance_buffer, //light_ref_buffer, - objects: FastHashMap::with_hasher(BuildHasherDefault::new()), - lights: Default::default(), + //lights: Default::default(), + models_flag: false, + program: Program(Vec::new()), + models: Default::default(), } } } pub struct Renderer { - id_count: u64, surface: wgpu::Surface<'static>, config: wgpu::SurfaceConfiguration, device: wgpu::Device, queue: wgpu::Queue, + materials: FreeList, + textures: FreeList, + meshes: FreeList, + light_instances: FreeList, + model_instances: FreeList, pub(crate) eye: eye::Eye, material_layout: wgpu::BindGroupLayout, - default_texture: SimpleTexture, - default_material: SimpleMaterial, + default_texture: Texture, + default_material: Material, sky_pipeline: wgpu::RenderPipeline, instance_pipeline: wgpu::RenderPipeline, - depth_texture: SimpleTexture, - pub(crate) instances: SimpleInstances, + depth_texture: Texture, + pub(crate) instances: Instances, } #[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] -struct SimpleVertex { +struct TangentVertex { position: [f32; 3], normal: [f32; 3], tex_coord: [f32; 2], @@ -364,10 +349,18 @@ struct SimpleVertex { bitangent: [f32; 3], } -impl SimpleVertex { +#[repr(C)] +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +struct Vertex { + position: [f32; 3], + normal: [f32; 3], + tex_coord: [f32; 3], +} + +impl TangentVertex { fn desc() -> wgpu::VertexBufferLayout<'static> { wgpu::VertexBufferLayout { - array_stride: size_of::() as wgpu::BufferAddress, + array_stride: size_of::() as wgpu::BufferAddress, step_mode: wgpu::VertexStepMode::Vertex, attributes: &[ wgpu::VertexAttribute { @@ -390,12 +383,13 @@ impl SimpleVertex { } } #[derive(Debug, Clone, PartialEq)] -pub struct SimpleMesh { +pub struct Mesh { indices: Option<(wgpu::Buffer, u32)>, vertices: (wgpu::Buffer, u32), + aabb: AABB, } #[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct SimpleTexture { +pub struct Texture { texture: wgpu::Texture, view: wgpu::TextureView, } @@ -404,13 +398,38 @@ pub struct TextureProperties { height: u32, } // todo: use texture compression! -impl SimpleTexture { +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, - ) -> SimpleTexture { + ) -> Texture { let size = wgpu::Extent3d { width: properties.width, height: properties.height, @@ -443,7 +462,7 @@ impl SimpleTexture { }, size, ); - SimpleTexture { + Texture { texture: diffuse_texture, view: diffuse_texture_view, } @@ -451,30 +470,10 @@ impl SimpleTexture { } #[derive(Clone)] -pub struct SimpleMaterial { +pub struct Material { group: wgpu::BindGroup, } -#[derive(Clone)] -pub struct Id { - id: u64, - it: T, -} - -impl Hash for Id { - fn hash(&self, state: &mut H) { - self.id.hash(state) - } -} - -impl PartialEq for Id { - fn eq(&self, other: &Self) -> bool { - other.id == self.id - } -} - -impl Eq for Id {} - struct MaterialProperties { edge: wgpu::AddressMode, filter: wgpu::FilterMode, @@ -503,15 +502,15 @@ impl MaterialProperties { } } -impl SimpleMaterial { +impl Material { fn new( device: &wgpu::Device, layout: &wgpu::BindGroupLayout, - base: &SimpleTexture, - normal: &SimpleTexture, - reflect: &SimpleTexture, + base: &Texture, + normal: &Texture, + reflect: &Texture, config: MaterialProperties, - ) -> SimpleMaterial { + ) -> Material { let sampler = config.sampler(&device); let group = device.create_bind_group(&wgpu::BindGroupDescriptor { layout: layout, @@ -535,7 +534,7 @@ impl SimpleMaterial { ], label: Some("diffuse_bind_group"), }); - SimpleMaterial { group } + Material { group } } } @@ -550,14 +549,14 @@ impl Display for PoolError { impl Error for PoolError {} -pub struct SimpleTreeNode { - object: Option, - children: Vec, +pub struct TreeNode { + object: Option, + children: Vec, name: String, } -impl SimpleTreeNode { - pub fn first_object(&self) -> Option { +impl TreeNode { + pub fn first_object(&self) -> Option { if let Some(object) = self.object.clone() { Some(object) } else { @@ -577,18 +576,14 @@ impl Renderer { const SIMPLE_RENDER_EYE_GROUP_POSITION: u32 = 0; const SIMPLE_RENDER_TEXTURE_GROUP_POSITION: u32 = 1; const SIMPLE_RENDER_MODEL_GROUP_POSITION: u32 = 2; - pub fn new_id(&mut self, value: T) -> Id { - self.id_count += 1; - Id { - it: value, - id: self.id_count, - } + pub fn write_instances(&mut self, objects: &mut FreeList) { + self.instances.write_instances(&mut self.meshes, &mut self.materials, objects, &self.device, &self.queue); } pub fn load_texture_from_bytes( &mut self, slice: impl AsRef<[u8]>, format: Option, - ) -> SimpleTexture { + ) -> Texture { let image = if let Some(format) = format { image::load_from_memory_with_format(slice.as_ref(), format) } else { @@ -596,7 +591,7 @@ impl Renderer { }; if let Ok(data) = image { let data = data.into_rgba8(); - SimpleTexture::load( + Texture::load( &self.device, &self.queue, data.as_bytes(), @@ -612,11 +607,11 @@ impl Renderer { } pub fn new_material( &mut self, - base: &SimpleTexture, - normal: &SimpleTexture, - reflect: &SimpleTexture, - ) -> Id { - self.new_id(SimpleMaterial::new( + base: &Texture, + normal: &Texture, + reflect: &Texture, + ) -> Id { + self.materials.make(Material::new( &self.device, &self.material_layout, base, @@ -626,13 +621,13 @@ impl Renderer { edge: wgpu::AddressMode::Repeat, filter: wgpu::FilterMode::Linear, }, - )) + )).into() } pub fn new_texture_from_gltf( &mut self, info: &gltf::texture::Texture, images: &Vec, - ) -> SimpleTexture { + ) -> Texture { if let Some(image) = images.get(info.source().index()) { let mut new_pixels = Vec::new(); let pixels: &Vec; @@ -649,7 +644,7 @@ impl Renderer { gltf::image::Format::R8G8B8A8 => pixels = &image.pixels, _ => return self.default_texture.clone(), } - SimpleTexture::load( + Texture::load( &self.device, &self.queue, pixels.as_slice(), @@ -665,42 +660,38 @@ impl Renderer { pub fn new_mesh_from_gltf( &mut self, primitive: gltf::Primitive, - meshes: &mut HashMap, + meshes: &mut HashMap, buffers: &[gltf::buffer::Data], - ) -> Id { + ) -> RefId { let position_index = primitive .get(&Semantic::Positions) - .and_then(|it| it.view()) - .and_then(|it| Some(it.buffer().index())); + .and_then(|it| it.view()).map(|it| it.buffer().index()); let normals_index = primitive .get(&Semantic::Normals) - .and_then(|it| it.view()) - .and_then(|it| Some(it.buffer().index())); + .and_then(|it| it.view()).map(|it| it.buffer().index()); let tex_coords_index = primitive .get(&Semantic::TexCoords(0)) - .and_then(|it| it.view()) - .and_then(|it| Some(it.buffer().index())); + .and_then(|it| it.view()).map(|it| it.buffer().index()); let indices_index = primitive .indices() - .and_then(|it| it.view()) - .and_then(|it| Some(it.buffer().index())); + .and_then(|it| it.view()).map(|it| it.buffer().index()); let tangent_index = primitive .get(&Semantic::Tangents) - .and_then(|it| it.view()) - .and_then(|it| Some(it.buffer().index())); + .and_then(|it| it.view()).map(|it| it.buffer().index()); let key = ( position_index, normals_index, tex_coords_index, indices_index, ); - self.new_id( + self.meshes.make( meshes .entry(key) .or_insert_with(|| { let mut tangents = false; let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); - let mut vertex_data: Vec; + let mut vertex_data: Vec; + 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()); @@ -710,7 +701,8 @@ impl Renderer { let mut tangent = reader.read_tangents().map(|it| it.into_iter()); tangents = tangent.is_some(); for position in positions { - vertex_data.push(SimpleVertex { + 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() { @@ -756,9 +748,10 @@ impl Renderer { } else { None }; - SimpleMesh { + Mesh { indices: indices_result, vertices: vertices_result, + aabb, } }) .clone(), @@ -767,12 +760,12 @@ impl Renderer { pub fn load_node_from_gltf( &mut self, node: gltf::Node, - meshes: &mut HashMap, - textures: &mut HashMap, + meshes: &mut HashMap, + textures: &mut HashMap, images: &Vec, buffers: &Vec, - ) -> SimpleTreeNode { - let mut tree_node = SimpleTreeNode { + ) -> TreeNode { + let mut tree_node = TreeNode { object: None, children: Vec::new(), name: node.name().unwrap_or("Node").to_string(), @@ -809,9 +802,10 @@ impl Renderer { }; let material = self.new_material(&base, &normal, &reflect); let mesh = self.new_mesh_from_gltf(primitive, meshes, buffers); - let object = SimpleObject(Rc::new(RefCell::new(SimpleObjectData { - model: Some(SimpleModelData { - instance: SimpleModelInstance { + let aabb = mesh.aabb; + let object = ObjectData { + model: Some(ModelData { + instance: ModelInstance { transform: Mat4::default(), color: Vec4::from_array(color), lights: [0; 16], @@ -821,17 +815,16 @@ impl Renderer { rough, }, material, - mesh, + mesh: mesh.into(), }), - collider: SimpleColliderData { - transform: Mat4::default(), - shape: Shape::None, - }, - }))); + 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(SimpleTreeNode { + tree_node.children.push(TreeNode { object: Some(object), children: Vec::new(), name: "Primitive".to_string(), @@ -846,17 +839,17 @@ impl Renderer { } tree_node } - pub fn load_from_gltf(&mut self, slice: impl AsRef<[u8]>) -> SimpleTreeNode { - let mut root = SimpleTreeNode { + pub fn load_from_gltf(&mut self, 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 meshes: HashMap = HashMap::new(); - let mut textures: HashMap = HashMap::new(); + let mut meshes: HashMap = HashMap::new(); + let mut textures: HashMap = HashMap::new(); for scene in document.scenes() { - let mut scene_node = SimpleTreeNode { + let mut scene_node = TreeNode { object: None, children: Vec::new(), name: scene.name().unwrap_or("Scene").to_string(), @@ -876,12 +869,14 @@ impl Renderer { root } pub fn resize(&mut self, width: u32, height: u32) { + println!("resize"); + self.config.width = width; + self.config.height = height; + self.depth_texture = Texture::depth(&self.config,&self.device); self.surface.configure(&self.device, &self.config); - self.eye.resize(width, height); + self.eye.resize(&self.queue, width, height); } - - - pub fn set_skybox(&mut self, skybox: SimpleTexture) { + pub fn set_skybox(&mut self, skybox: Texture) { self.eye.skybox(&self.device, skybox); } pub async fn new(window: &Arc) -> anyhow::Result { @@ -917,6 +912,7 @@ impl Renderer { power_preference: wgpu::PowerPreference::default(), compatible_surface: Some(&surface), force_fallback_adapter: false, + //apply_limit_buckets: false, }) .await?; @@ -950,9 +946,10 @@ impl Renderer { let config = wgpu::SurfaceConfiguration { usage: wgpu::TextureUsages::RENDER_ATTACHMENT, format: surface_format, + //color_space: Default::default(), width, height, - present_mode: surface_caps.present_modes[0], + present_mode: wgpu::PresentMode::Fifo, alpha_mode: surface_caps.alpha_modes[0], view_formats: vec![], desired_maximum_frame_latency: 2, @@ -961,7 +958,7 @@ impl Renderer { let shader = device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl")); - let default_texture = SimpleTexture::load( + let default_texture = Texture::load( &device, &queue, [0, 0, 255, 255], @@ -1027,7 +1024,7 @@ impl Renderer { vertex: wgpu::VertexState { module: &shader, entry_point: Some("vs_main"), - buffers: &[SimpleVertex::desc(), SimpleInstances::desc()], + buffers: &[TangentVertex::desc(), Instances::desc()], compilation_options: wgpu::PipelineCompilationOptions::default(), }, fragment: Some(wgpu::FragmentState { @@ -1104,48 +1101,11 @@ impl Renderer { cache: None, }); - 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()); - let depth_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - // 4. - 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::MipmapFilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), // 5. - lod_min_clamp: 0.0, - lod_max_clamp: 100.0, - ..Default::default() - }); + let instances = Instances::new(&device); - let instances = SimpleInstances::new(&device); + let depth_texture = Texture::depth(&config,&device); - let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&DEFAULT_VERTICES), - usage: wgpu::BufferUsages::VERTEX, - }); - - let default_material = SimpleMaterial::new( + let default_material = Material::new( &device, &texture_bind_group_layout, &default_texture, @@ -1155,14 +1115,10 @@ impl Renderer { ); Ok(Renderer { - id_count: 0, material_layout: texture_bind_group_layout, default_texture, default_material, - depth_texture: SimpleTexture { - texture: depth_texture, - view: depth_view, - }, + depth_texture, instances, sky_pipeline, instance_pipeline, @@ -1170,28 +1126,31 @@ impl Renderer { config, device, queue, + materials: Default::default(), + textures: Default::default(), + meshes: Default::default(), + light_instances: Default::default(), + model_instances: Default::default(), eye, }) } - pub(crate) fn render(&mut self, window: &Arc) -> anyhow::Result<()> { - window.request_redraw(); - - let mut resize_renderer = false; - - let output = match self.surface.get_current_texture() { + pub(crate) fn render(&mut self, window: &Arc, objects: &mut FreeList) -> anyhow::Result<()> { + let surface_texture = match self.surface.get_current_texture() { wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture, wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => { - resize_renderer = true; //moved reconfigure to avoid a crash when resizing window - surface_texture + println!("suboptimal"); + self.surface.configure(&self.device, &self.config); + return Ok(()); } wgpu::CurrentSurfaceTexture::Timeout | wgpu::CurrentSurfaceTexture::Occluded | wgpu::CurrentSurfaceTexture::Validation => { - // Skip this frame + println!("timeout"); return Ok(()); } wgpu::CurrentSurfaceTexture::Outdated => { self.surface.configure(&self.device, &self.config); + println!("outdated"); return Ok(()); } wgpu::CurrentSurfaceTexture::Lost => { @@ -1200,17 +1159,19 @@ impl Renderer { anyhow::bail!("Lost device"); } }; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let program = self.instances.write_instances(&self.device, &self.queue); + let view = surface_texture + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + self.eye.write(&self.queue); + self.instances.write_instances(&mut self.meshes, &mut self.materials, objects, &self.device, &self.queue); { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -1245,7 +1206,7 @@ impl Renderer { pass.set_pipeline(&self.instance_pipeline); pass.set_bind_group(0, &self.eye.group, &[]); pass.set_vertex_buffer(1, self.instances.instance_buffer.slice(..)); - program.render(&mut pass); + std::mem::replace(&mut self.instances.program,Program(Vec::new())).render(&mut pass); // todo: improve pass.set_bind_group(1, &self.default_material.group, &[]); @@ -1255,24 +1216,24 @@ impl Renderer { self.queue.submit(Some(encoder.finish())); - self.eye.write(&self.queue); + window.pre_present_notify(); - output.present(); + surface_texture.present(); + //self.queue.present(surface_texture); - - if(resize_renderer){ - std::mem::drop(view); //idk how this would affect a multi-pass system like deferred rendering, but this makes sure it's not gonna collide with resizing the swap chain or whatever wGPU does - self.surface.configure(&self.device, &self.config); + // its rust just being stupid + //std::mem::drop(view); // idk how this would affect a multi-pass system like deferred rendering, but this makes sure it's not gonna collide with resizing the swap chain or whatever wGPU does /* also in this snippet call the functions to - a: resize window resolution - b: resize projection + a: resize window resolution // todo: idk why it's ignoring me + b: resize projection // done: made eye.rs write to eye buffer currently when you resize the window the projection matrix is not updated and therefore it gets very skewed and weird looking if you resize the current small window to a big screen like mine(halbear) which is 3440x1440 i would add it myself but i don't know wGPU or rust all that well sooooo */ - } + + window.request_redraw(); Ok(()) } diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000..d9049fb --- /dev/null +++ b/src/state.rs @@ -0,0 +1,19 @@ +use crate::list::FreeList; +use crate::{render, world}; + +#[derive(Default)] +pub struct State { + pub objects: FreeList, + pub lights: FreeList, + pub worlds: FreeList, + pub renderer: Option, + pub screens: FreeList +} + +impl State { + pub fn new() -> State { + State { + ..Default::default() + } + } +} \ No newline at end of file diff --git a/src/ui/mod.rs b/src/ui/mod.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/world/mod.rs b/src/world/mod.rs index 0cdfdad..34c74d0 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -1,116 +1,304 @@ -use crate::render::{Renderer, SimpleLightData, SimpleModelData}; -use glam::{IVec3, Mat4, Vec3}; +use glam::{Affine3, IVec3, Mat4, Quat, Vec3}; use std::cell::RefCell; use std::hash::{BuildHasherDefault, Hash, Hasher}; +use std::path::absolute; 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 SimpleColliderData { - pub transform: Mat4, +pub struct ColliderData { pub shape: Shape, } -impl SimpleColliderData { - fn aabb(&self) -> Option { - let (_scale, _rotation, translation) = self.transform.to_scale_rotation_translation(); +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()); - Some(AABB( + AABB( translation - radius_offset, translation + radius_offset, - )) + ) } Shape::Sphere(radius) => { let radius_offset = Vec3::splat(radius); - Some(AABB( + AABB( translation - radius_offset, translation + radius_offset, - )) + ) } - Shape::None => None, } } } #[derive(Clone)] -pub struct SimpleObjectData { - pub model: Option, - pub collider: SimpleColliderData, +pub struct ObjectData { + pub model: Option, + pub collider: ColliderData, + pub affine: Affine3, + pub asleep: bool, +} + +#[derive(Clone, Eq, PartialEq)] +pub enum InterestType { + Light(Id), + Object(Id) } #[derive(Clone)] -pub struct SimpleLight(pub Rc>); - -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(&self, state: &mut H) { - (self.0.as_ptr() as *const RefCell).hash(state) - } +pub struct Interest { + it: InterestType, + aabb: AABB, } #[derive(Clone)] -pub struct SimpleObject(pub Rc>); +pub struct InterestNode { + interest: Interest, + next: MaybeId, +} -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, 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()) + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct IAABB(pub(crate) IVec3, pub(crate) IVec3); + +impl IAABB { + pub(crate) fn new(translation: IVec3, size: IVec3) -> IAABB { + IAABB(translation - size, translation + size) + } + pub fn extend_to(&self, point: IVec3) -> IAABB { + IAABB(self.0.min(point),self.1.max(point)) + } + 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 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() + } +} + +pub struct OctreeDebug { + map: FastHashMap> +} + +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() } } -} - -impl PartialEq for SimpleObject { - fn eq(&self, other: &Self) -> bool { - Rc::ptr_eq(&self.0, &other.0) - } -} - -impl Eq for SimpleObject {} - -impl Hash for SimpleObject { - fn hash(&self, state: &mut H) { - (self.0.as_ptr() as *const RefCell).hash(state); - } -} - -#[derive(Eq, Hash, PartialEq, Clone)] -pub enum Interest { - Light(SimpleLight), - Object(SimpleObject), -} - -impl Interest { - fn aabb(&self) -> Option { - match self { - Interest::Light(light) => { - let data = light.0.borrow(); - Some(AABB::new( - data.transform.to_scale_rotation_translation().2, - Vec3::splat(data.instance.color.length()), - )) + fn set_block( + &mut self, + blocks: &mut FreeList, + block: Id, + objects: &mut FreeList, + debug: &Option, + pos: IVec3, + 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) } - Interest::Object(object) => object.0.borrow().collider.aabb(), + } else { + if entry.is_some() { + objects.remove(self.map.remove(&pos).unwrap()); + } + } + if let Some(block) = blocks.get(&block).blocks[0].exists() { + self.set_block(blocks,block,objects,debug, pos - (IVec3::new(-1, -1, -1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[1].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,-1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[2].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,-1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[3].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,-1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[4].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,-1,1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[5].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[6].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,1) * size / 4), size / 2); + } + if let Some(block) = blocks.get(&block).blocks[7].exists() { + self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,1) * size / 4), size / 2); } } + pub fn set(&mut self, objects: &mut FreeList, world: &mut World, debug: Option) { + 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, renderer: &mut render::Renderer) { + for (pos,block) in self.map.iter() { + renderer.instances.register_object(objects.get_ref(block.clone())); + } + } +} + +impl World { + pub fn new() -> World { + World { + matter: OctreeMap::new(), + + } + } + pub fn add_object(&mut self, object: RefId) -> Id { + 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) { + + } + pub fn step( + &mut self, + maybe_renderer: &mut Option, + objects: &mut FreeList, + _lights: &mut FreeList, + ) { + /*fn reinsert( + block_id: &Id, + blocks: &mut FreeList, + interests: &mut FreeList, + objects: &mut FreeList + ) { + 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; + instances.register_object(object) + } + _ => {} + } + 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.interests, objects) + }*/ + if let Some(renderer) = maybe_renderer { + fn consider( + block_id: &Id, + renderer: &mut render::Renderer, + blocks: &mut FreeList, + interests: &mut FreeList, + objects: &mut FreeList + ) { + 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.interests, objects) + } + } + } + fn sync(&mut self) {} } #[derive(Copy, Clone)] @@ -125,188 +313,153 @@ impl Material { Material { volume: 0, velocity: Vec3::ZERO, - material: 0, + material: 0 } } } -#[derive(Clone, Copy)] -struct AABB(Vec3, Vec3); - -impl AABB { - fn new(translation: Vec3, size: Vec3) -> AABB { - AABB(translation - size, translation + size) - } - 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 - } -} - -pub struct World { - map: HashedMap, - pub renderer: Option, -} - -impl World { - pub fn new() -> World { - World { - map: HashedMap::new(), - renderer: None, - } - } - pub fn renderer(&mut self) -> &mut Renderer { - self.renderer.as_mut().unwrap() - } - pub fn add_renderer(&mut self, renderer: Renderer) { - self.renderer = Some(renderer); - } - pub fn add_object(&mut self, object: SimpleObject) { - if let Some(aabb) = object.0.borrow().collider.aabb() { - self.map - .place_with_aabb(Interest::Object(object.clone()), aabb); - } - if let Some(ref mut renderer) = self.renderer - && object.0.borrow().model.is_some() - { - renderer.instances.add_object(object.clone()) - } - } - pub fn remove_object(&mut self, object: SimpleObject) {} - pub fn set_debug(&mut self, value: Option) { - for (index, item) in self.map.it.iter_mut() {} - } - fn step(&mut self) {} - fn sync(&mut self) {} -} - -type VecMap = FastHashMap; - -struct Level { - sub_level: - entries: T -} - -struct MatterLevel { - blocks: Option> -} - -struct InterestLevel { - blocks: Option> -} - -struct HashedMap { - matter: -} - -impl HashedMap { - fn new() -> HashedMap { - HashedMap { - it: FastHashMap::default() - } - } -} - -/*impl Block { +impl OctreeBlock { const FLOOR: i32 = 4; - const CHUNK_SIZE: i32 = Block::FLOOR * 64; + const CHUNK_SIZE: i32 = OctreeBlock::FLOOR * 64; const MAX_IDEAL_INTEREST: usize = 4; - fn new() -> Block { - Block { - debug: None, + fn new() -> OctreeBlock { + OctreeBlock { + interests: 0, material: Material::new(), - interests: FastHashSet::default(), - blocks: None, + first: MaybeId::NULL, + blocks: [MaybeId::NULL;8], } } } -type Blocks = Box<[Block; 8]>; - -struct Block { - debug: Option, - interests: FastHashSet, - material: Material, - blocks: Option, +struct OctreeBlock { + interests: u32, // 1 + first: MaybeId, // 1 + material: Material, // 4 + blocks: Blocks, // 8 } -impl Block { - pub fn set_debug(&mut self, value: Option, pos: Vec3, size: f32) {} +impl OctreeBlock { + fn push(&mut self, interests: &mut FreeList, interest: Interest) { + self.first = interests.make(InterestNode { + interest: interest.clone(), + next: self.first.clone(), + }).id.maybe(); + self.interests += 1; + } + fn remove(&mut self, interests: &mut FreeList, needle: Interest) { + 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, + blocks: &mut FreeList, + interests: &mut FreeList, + interest: Interest, + pos: IVec3, + size: i32, + ) { + if pos == IVec3::ZERO || blocks.get(&it).interests < OctreeBlock::MAX_IDEAL_INTEREST as u32 { + blocks.get(&it).push(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); + } + } } -pub trait WorldMap { - fn new() -> Self; - fn place_with_aabb(&mut self, interest: Interest, aabb: AABB); -} +type Blocks = [MaybeId; 8]; struct OctreeMap { - it: FastHashMap, + interests: FreeList, + blocks: FreeList, + map: FastHashMap>, } impl OctreeMap { - fn place_with_index(&mut self, interest: Interest, mut index: IVec3) { - let chunk_index = (index + IVec3::splat(Block::CHUNK_SIZE / 2)) / Block::CHUNK_SIZE; - let mut block = self.it.entry(chunk_index).or_insert_with(|| Block::new()); - let mut offset_size = Block::CHUNK_SIZE / 4; - index = index - chunk_index * Block::CHUNK_SIZE; - while index != IVec3::ZERO { - let blocks = if let Some(ref mut blocks) = block.blocks { - blocks - } else if block.interests.len() < Block::MAX_IDEAL_INTEREST { - block.interests.insert(interest.clone()); - return; - } else { - block.blocks = Some(Box::new(core::array::from_fn(|_| Block::new()))); - block.blocks.as_mut().unwrap() - }; - let mut block_index = 0; - if index.x < 0 { - index.x += offset_size; - } else { - index.x -= offset_size; - block_index += 1; - } - if index.y < 0 { - index.y += offset_size; - } else { - index.y -= offset_size; - block_index += 2; - } - if index.z < 0 { - index.z += offset_size; - } else { - index.z -= offset_size; - block_index += 4; - } - block = &mut blocks[block_index]; - offset_size /= 2; - } - block.interests.insert(interest.clone()); - } -} - -impl WorldMap for OctreeMap { - fn place_with_aabb(&mut self, interest: Interest, aabb: AABB) { + fn place_interest(&mut self, interest: Interest) { let mut d = 4; - let offset = aabb.1 - aabb.0; + let offset = interest.aabb.1 - interest.aabb.0; while offset.element_sum() > (d * 2) as f32 { d *= 2 } - let iaabb0 = (aabb.0 / d as f32).floor().as_ivec3(); - let iaabb1 = (aabb.1 / d as f32).ceil().as_ivec3(); - for x in iaabb0.x..iaabb1.x { - for y in iaabb0.y..iaabb1.y { - for z in iaabb0.z..iaabb1.z { - self.place_with_index(interest.clone(), IVec3::new(x, y, z) * d as i32); + 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.interests, + interest.clone(), + pos, + OctreeBlock::CHUNK_SIZE, + ); } } } } fn new() -> OctreeMap { OctreeMap { - it: FastHashMap::with_hasher(BuildHasherDefault::default()), + interests: Default::default(), + blocks: Default::default(), + map: FastHashMap::with_hasher(BuildHasherDefault::default()), } } -}*/ +} \ No newline at end of file