Implemented Octrees, refactored heap allocations for FreeLists and some spring-cleaning. Builds and runs, octrees fail to form correctly and reinsertion steps are unimplemented.

This commit is contained in:
paladin 2026-09-13 11:22:19 +01:00
parent cf965d489c
commit 905d05bc39
17 changed files with 918 additions and 630 deletions

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
target target
.DS_Store .DS_Store
Cargo.lock

10
.idea/.gitignore generated vendored
View file

@ -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

6
.idea/Pool.iml generated
View file

@ -3,13 +3,7 @@
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> <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$/target" />
<excludeFolder url="file://$MODULE_DIR$/src/mars/target" />
<excludeFolder url="file://$MODULE_DIR$/lang/target" />
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />

View file

@ -1,5 +0,0 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
</state>
</component>

View file

@ -1,8 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="project">
<words>
<w>forloop</w>
<w>vmcase</w>
</words>
</dictionary>
</component>

View file

@ -1,14 +1,18 @@
[package] [package]
#![recursion_limit = "256"] #![recursion_limit = "256"]
name = "game" name = "pool"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[features]
big_ids = []
check_ids = []
[profile.release] [profile.release]
strip = true strip = true
[[bin]] [[bin]]
name = "game" name = "pool"
path = "src/main.rs" path = "src/main.rs"
[dependencies] [dependencies]
@ -17,7 +21,7 @@ anyhow = "1.0"
winit = { version = "0.30", features = ["android-native-activity"] } winit = { version = "0.30", features = ["android-native-activity"] }
env_logger = "0.11.10" env_logger = "0.11.10"
log = "0.4" log = "0.4"
wgpu = "29.0.3" wgpu = "29.0.4"
pollster = "0.4.0" pollster = "0.4.0"
glam = { version = "0.33.3", features = [ "bytemuck" ] } glam = { version = "0.33.3", features = [ "bytemuck" ] }
console_error_panic_hook = "0.1.7" console_error_panic_hook = "0.1.7"
@ -27,7 +31,7 @@ mars = { path = "../Mars" }
[target.'cfg(target_arch = "wasm32")'.dependencies] [target.'cfg(target_arch = "wasm32")'.dependencies]
console_error_panic_hook = "0.1.6" 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 = "0.2.121"
wasm-bindgen-futures = "0.4.71" wasm-bindgen-futures = "0.4.71"
console_log = "1.0.0" console_log = "1.0.0"

View file

@ -1,6 +1,6 @@
_Pool. Hop in, the water's warm._ _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. Taking inspiration from the Roblox game engine, Pool ships with its own scripting language, Mars, and shares a similar instance-service model.

View file

@ -1,8 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
// Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu // Credit of most code to https://sotrh.github.io/learn-wgpu/ since I'm not familiar with wgpu
use crate::render::Renderer; use crate::render::{ModelData, Renderer};
use crate::world::{SimpleObject, World}; use crate::world::{World, ObjectData, OctreeDebug};
use glam::{Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4}; use glam::{Affine3, Affine3A, EulerRot, Mat4, Quat, Vec2, Vec3, Vec4};
use std::sync::Arc; use std::sync::Arc;
#[cfg(target_arch = "wasm32")] #[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -16,6 +16,8 @@ use winit::{
keyboard::{KeyCode, PhysicalKey}, keyboard::{KeyCode, PhysicalKey},
window::Window, window::Window,
}; };
use crate::list::Id;
use crate::state;
struct Controller { struct Controller {
buttons: HashMap<MouseButton, bool>, buttons: HashMap<MouseButton, bool>,
@ -34,68 +36,66 @@ impl Controller {
} }
pub struct AppState { pub struct AppState {
world: World, state: state::State,
world: Id<World>,
debug: OctreeDebug,
debug_model: ModelData,
controller: Controller, controller: Controller,
window: Arc<Window>, window: Arc<Window>,
clients: Vec<SimpleObject>,
} }
const BLOCKS: i32 = 4; const BLOCKS: i32 = 2;
impl AppState { impl AppState {
// We don't need this to be async right now, // We don't need this to be async right now,
// but we will in the next tutorial // but we will in the next tutorial
pub async fn new(window: Arc<Window>) -> Result<AppState, Box<dyn std::error::Error>> { pub async fn new(window: Arc<Window>) -> Result<AppState, Box<dyn std::error::Error>> {
let mut world = World::new(); let mut state = state::State::new();
world.add_renderer(Renderer::new(&window).await?); let (world,debug,debug_model) = {
let mut clients = Vec::new(); let mut world = state.worlds.make(World::new());
{ state.renderer = Some(Renderer::new(&window).await?);
let file = world let renderer = state.renderer.as_mut().unwrap();
.renderer let debug_file = renderer.load_from_gltf(include_bytes!("assets/debug.glb"));
.as_mut() let debug_model = debug_file.first_object().unwrap().model.unwrap();
.unwrap() {
.load_from_gltf(include_bytes!("assets/sphere.glb")); let file = renderer.load_from_gltf(include_bytes!("assets/sphere.glb"));
let block = file.first_object().unwrap(); let mut block = file.first_object().unwrap();
let skybox = world.renderer.as_mut().unwrap().load_texture_from_bytes( let skybox = renderer.load_texture_from_bytes(
include_bytes!("assets/skybox2.png"), include_bytes!("assets/skybox2.png"),
Some(image::ImageFormat::Png), Some(image::ImageFormat::Png),
); );
world.renderer().set_skybox(skybox); renderer.set_skybox(skybox);
let color = world let color = renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"), None);
.renderer() let normal = renderer.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"), None);
.load_texture_from_bytes(include_bytes!("assets/plank/color.png"), None); let roughness = renderer.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"), None);
let normal = world let mat = renderer.new_material(&color, &normal, &roughness);
.renderer() block.model.as_mut().unwrap().material = mat;
.load_texture_from_bytes(include_bytes!("assets/plank/normal.png"), None); for x in -BLOCKS..=BLOCKS {
let roughness = world for y in -BLOCKS..=BLOCKS {
.renderer() let id = world.add_object(state.objects.make(block.clone()));
.load_texture_from_bytes(include_bytes!("assets/plank/roughness.png"), None); let block = state.objects.get(&id);
let mat = world.renderer().new_material(&color, &normal, &roughness); {
block.0.borrow_mut().model.as_mut().unwrap().material = mat; block.affine = Affine3::from_translation(
for x in -BLOCKS..BLOCKS { Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0),
for y in -BLOCKS..BLOCKS { );
let block = block.hard_clone(); block.model.as_mut().unwrap().instance.color = Vec4::new(
world.add_object(block.clone()); 0.3,
{ (x + BLOCKS) as f32 / BLOCKS as f32,
let mut model = block.0.borrow_mut(); (y + BLOCKS) as f32 / BLOCKS as f32,
model.model.as_mut().unwrap().instance.transform = Mat4::from_translation( 1.0,
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,
);
} }
clients.push(block)
} }
} }
} let debug = OctreeDebug::new();
(world.id,debug,debug_model)
};
Ok(Self { Ok(Self {
state,
world, world,
clients, debug,
debug_model,
controller: Controller::new(), controller: Controller::new(),
window, window,
}) })
@ -108,7 +108,7 @@ impl AppState {
pub fn resize(&mut self, width: u32, height: u32) { pub fn resize(&mut self, width: u32, height: u32) {
if width > 0 && height > 0 { 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::Escape, true) => event_loop.exit(),
(KeyCode::Space, true) => {} (KeyCode::Space, true) => {}
(KeyCode::KeyR, 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<f64>) { fn handle_mouse_moved(&mut self, position: PhysicalPosition<f64>) {
if let Some(true) = self.controller.buttons.get(&MouseButton::Right) { 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.x as f32 - self.controller.mouse.x,
position.y as f32 - self.controller.mouse.y, position.y as f32 - self.controller.mouse.y,
); );
@ -182,6 +182,8 @@ impl ApplicationHandler<AppState> for App {
let window = Arc::new(event_loop.create_window(window_attributes).unwrap()); let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
window.set_title("Pool");
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
// If we are not on web we can use pollster to // If we are not on web we can use pollster to
@ -238,15 +240,14 @@ impl ApplicationHandler<AppState> for App {
WindowEvent::CloseRequested => event_loop.exit(), WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(size) => state.resize(size.width, size.height), WindowEvent::Resized(size) => state.resize(size.width, size.height),
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
use std::time::Instant;
let now = Instant::now();
state.update(); state.update();
let mut movement = Vec3::new(0.0, 0.0, 0.0); let mut movement = Vec3::new(0.0, 0.0, 0.0);
let pressed = |keycode: KeyCode| { let pressed = |keycode: KeyCode| {
if let Some(true) = state.controller.keys.get(&keycode) { matches!(state.controller.keys.get(&keycode), Some(true))
true
} else {
false
}
}; };
if pressed(KeyCode::KeyA) { if pressed(KeyCode::KeyA) {
@ -267,15 +268,12 @@ impl ApplicationHandler<AppState> for App {
if pressed(KeyCode::KeyQ) { if pressed(KeyCode::KeyQ) {
movement.y -= 1.0; movement.y -= 1.0;
} }
let world = state.state.worlds.get(&state.world);
state state.debug.set(&mut state.state.objects, world, Some(state.debug_model.clone()));
.world state.debug.register(&mut state.state.objects,state.state.renderer.as_mut().unwrap());
.renderer world.step(&mut state.state.renderer, &mut state.state.objects, &mut state.state.lights);
.as_mut() state.state.renderer.as_mut().unwrap().eye.control(movement * 0.1);
.unwrap() match state.state.renderer.as_mut().unwrap().render(&state.window,&mut state.state.objects) {
.eye
.control(movement * 0.1);
match state.world.renderer.as_mut().unwrap().render(&state.window) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
// Log the error and exit gracefully // Log the error and exit gracefully
@ -283,19 +281,9 @@ impl ApplicationHandler<AppState> for App {
event_loop.exit(); event_loop.exit();
} }
} }
for object in state.clients.iter() {
object let elapsed = now.elapsed();
.0 //println!("Elapsed {:.2?}",elapsed);
.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),
);
}
} }
WindowEvent::MouseInput { WindowEvent::MouseInput {
button, button,

View file

@ -95,17 +95,20 @@ fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
return textureSample(sky_texture, sky_sampler, uv); return textureSample(sky_texture, sky_sampler, uv);
} }
fn rotation(mat: mat4x4<f32>) -> mat3x3<f32> { fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
return mat3x3<f32>( return mat3x3<f32>(
mat[0].xyz, it[0].xyz,
mat[1].xyz, it[1].xyz,
mat[2].xyz, it[2].xyz,
); );
} }
fn translation(mat: mat4x4<f32>) -> vec4<f32> { fn translation(it: mat4x4<f32>) -> vec4<f32> {
//return vec4<f32>(mat[0][3],mat[1][3],mat[2][3],mat[3][3]); return it[3];
return mat[3]; }
fn light_aspect(light: vec3<f32>, dir: vec3<f32>) -> vec4<f32> {
return vec4<f32>(0.0,0.0,0.0,0.0);
} }
@fragment @fragment
@ -131,7 +134,7 @@ 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_strength = pow(max(dot(tangent_normal, half_dir), 0.0), 32.0);
//let specular_color = specular_strength * environment.light.xyz; //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); let diffuse_brightness = length(diffuse_color);

Binary file not shown.

View file

@ -2,4 +2,6 @@
pub mod app; pub mod app;
pub mod render; pub mod render;
pub mod web; pub mod web;
pub mod world; pub mod world;
pub mod list;
pub mod state;

185
src/list.rs Normal file
View file

@ -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<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(),
}
}
}
}

View file

@ -1,4 +1,4 @@
use crate::render::{MaterialProperties, SimpleTexture}; use crate::render::{MaterialProperties, Texture};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use glam::camera::lh::proj::directx::perspective; use glam::camera::lh::proj::directx::perspective;
use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4}; use glam::{Affine3A, EulerRot, Mat3A, Mat4, Vec3, Vec4};
@ -49,7 +49,7 @@ impl Eye {
bytemuck::cast_slice(&[self.environment]), 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 { let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"), label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[ contents: bytemuck::cast_slice(&[
@ -136,7 +136,7 @@ impl Eye {
device: &wgpu::Device, device: &wgpu::Device,
camera: &wgpu::Buffer, camera: &wgpu::Buffer,
environment: &wgpu::Buffer, environment: &wgpu::Buffer,
skybox: SimpleTexture, skybox: Texture,
) -> wgpu::BindGroup { ) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor { device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout, layout: &layout,
@ -163,7 +163,7 @@ impl Eye {
label: Some("eye_bind_group"), 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.group = Eye::bind_group(
&self.layout, &self.layout,
device, device,
@ -172,8 +172,9 @@ impl Eye {
texture, texture,
) )
} }
pub(crate) fn resize(&mut self, width: u32, height: u32) { pub(crate) fn resize(&mut self, queue: &Queue, width: u32, height: u32) {
self.aspect_ratio = width as f32 / height as f32 self.aspect_ratio = width as f32 / height as f32;
self.write(queue);
} }
pub(crate) fn control(&mut self, delta: Vec3) { pub(crate) fn control(&mut self, delta: Vec3) {
self.frame *= Affine3A::from_translation(delta); self.frame *= Affine3A::from_translation(delta);

View file

@ -1,7 +1,7 @@
pub mod eye; pub mod eye;
use crate::render::eye::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 bytemuck::{Pod, Zeroable};
use glam::prelude::*; use glam::prelude::*;
use gltf::Semantic; use gltf::Semantic;
@ -18,12 +18,18 @@ use wgpu::naga::{FastHashMap, FastHashSet};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use wgpu::{Device, Queue}; use wgpu::{Device, Queue};
use winit::window::Window; 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)] #[repr(C)]
#[derive(Pod, Zeroable, Copy, Clone)] #[derive(Pod, Zeroable, Copy, Clone)]
pub struct SimpleModelInstance { pub struct ModelInstance {
pub transform: Mat4, pub transform: Mat4,
pub color: Vec4, pub color: Vec4,
pub lights: [u16; 16], pub lights: [u16; 16],
@ -35,64 +41,61 @@ pub struct SimpleModelInstance {
#[repr(C)] #[repr(C)]
#[derive(Pod, Copy, Clone, Zeroable)] #[derive(Pod, Copy, Clone, Zeroable)]
pub struct SimpleLightInstance { pub struct LightInstance {
pub location: Vec4, pub location: Vec4,
pub rotation: Vec4, pub rotation: Vec4,
pub color: Vec4, pub color: Vec4,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleLightData { pub struct LightData {
pub index: usize, pub instance: LightInstance,
pub instance: SimpleLightInstance,
pub transform: Affine3, pub transform: Affine3,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleModelData { pub struct ModelData {
pub instance: SimpleModelInstance, pub instance: ModelInstance,
pub material: Id<SimpleMaterial>, pub material: Id<Material>,
pub mesh: Id<SimpleMesh>, pub mesh: Id<Mesh>,
} }
pub struct SimpleInstances { pub struct Instances {
light_count: usize, light_count: usize,
light_buffer: wgpu::Buffer, light_buffer: wgpu::Buffer,
instance_count: usize, instance_count: usize,
instance_buffer: wgpu::Buffer, instance_buffer: wgpu::Buffer,
//light_ref_last: usize, models_flag: bool,
//light_ref_buffer: wgpu::Buffer, program: Program,
objects: models: FastHashMap<Id<Mesh>, FastHashMap<Id<Material>, FastHashMap<Id<ObjectData>,bool>>>,
FastHashMap<Id<SimpleMesh>, FastHashMap<Id<SimpleMaterial>, FastHashSet<SimpleObject>>>,
lights: FastHashMap<SimpleLight, usize>,
} }
enum SimpleRenderCode { enum Code {
Material(wgpu::BindGroup), Material(Material),
Mesh((wgpu::Buffer, u32), Option<(wgpu::Buffer, u32)>), Mesh(Mesh),
Draw(Range<u32>), Draw(Range<u32>),
} }
struct SimpleRenderProgram(Vec<SimpleRenderCode>); struct Program(Vec<Code>);
impl SimpleRenderProgram { impl Program {
fn push(&mut self, item: SimpleRenderCode) { fn push(&mut self, item: Code) {
self.0.push(item) self.0.push(item)
} }
fn new() -> SimpleRenderProgram { fn new() -> Program {
SimpleRenderProgram(Vec::new()) Program(Vec::new())
} }
fn render(self, pass: &mut wgpu::RenderPass) { fn render(self, pass: &mut wgpu::RenderPass) {
let mut count = 0; let mut count = 0;
let mut indexed = false; let mut indexed = false;
for code in self.0 { for code in self.0 {
match code { match code {
SimpleRenderCode::Material(material) => pass.set_bind_group( Code::Material(material) => pass.set_bind_group(
Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION, 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(..)); pass.set_vertex_buffer(0, vertices.0.slice(..));
if let Some(indices) = indices { if let Some(indices) = indices {
pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32); pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32);
@ -103,7 +106,7 @@ impl SimpleRenderProgram {
indexed = false; indexed = false;
} }
} }
SimpleRenderCode::Draw(instances) => { Code::Draw(instances) => {
if indexed { if indexed {
pass.draw_indexed(0..count, 0, instances) pass.draw_indexed(0..count, 0, instances)
} else { } else {
@ -115,42 +118,20 @@ impl SimpleRenderProgram {
} }
} }
impl SimpleInstances { impl Instances {
const MIN_SIZE: u64 = 64; const MIN_SIZE: u64 = 64;
pub fn add_object(&mut self, object: SimpleObject) { pub fn register_object(&mut self, mut object: RefId<ObjectData>) {
if let Some(ref model) = object.0.borrow().model { let model = object.model.as_mut().unwrap();
self.instance_count += 1; self.instance_count += 1;
self.objects self.models
.entry(model.mesh.clone()) .entry(model.mesh.clone()).or_default()
.or_insert(FastHashMap::with_hasher(BuildHasherDefault::new())) .entry(model.material.clone()).or_default()
.entry(model.material.clone()) .insert(object.into(),self.models_flag);
.or_insert(FastHashSet::with_hasher(BuildHasherDefault::new()))
.insert(object.clone());
}
} }
/*pub fn register_light(&mut self, light: Id<LightData>) {
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) {
self.light_count += 1; self.light_count += 1;
self.lights.insert(light, 0); self.lights.insert(light);
} }*/
pub fn remove_light(&mut self, light: &SimpleLight) {
self.light_count -= 1;
self.lights.remove(light);
}
pub fn reallocate_buffer( pub fn reallocate_buffer(
device: &wgpu::Device, device: &wgpu::Device,
buffer: &mut wgpu::Buffer, buffer: &mut wgpu::Buffer,
@ -158,7 +139,7 @@ impl SimpleInstances {
item_size: usize, item_size: usize,
) { ) {
let size = buffer.size() / item_size as wgpu::BufferAddress; 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<usize> = None; let mut reallocate: Option<usize> = None;
if count < (size / 2) as usize { if count < (size / 2) as usize {
reallocate = Some(count / 2); 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::<u32>()); //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( let mut light_ref_buffer = queue.write_buffer_with(
@ -184,11 +165,11 @@ impl SimpleInstances {
wgpu::BufferSize::new(self.instance_buffer.size()).unwrap() wgpu::BufferSize::new(self.instance_buffer.size()).unwrap()
).unwrap(); ).unwrap();
*/ */
SimpleInstances::reallocate_buffer( Instances::reallocate_buffer(
device, device,
&mut self.light_buffer, &mut self.light_buffer,
self.light_count, self.light_count,
size_of::<SimpleLight>(), size_of::<WorldLight>(),
); );
let mut buffer = queue let mut buffer = queue
.write_buffer_with( .write_buffer_with(
@ -197,7 +178,7 @@ impl SimpleInstances {
wgpu::BufferSize::new(self.light_buffer.size()).unwrap(), wgpu::BufferSize::new(self.light_buffer.size()).unwrap(),
) )
.unwrap(); .unwrap();
let stride = size_of::<SimpleLight>(); let stride = size_of::<WorldLight>();
for (new_index, (light, index)) in self.lights.iter_mut().enumerate() { for (new_index, (light, index)) in self.lights.iter_mut().enumerate() {
*index = new_index + 1; *index = new_index + 1;
let begin = *index * stride; let begin = *index * stride;
@ -205,18 +186,20 @@ impl SimpleInstances {
.slice(begin..begin + stride) .slice(begin..begin + stride)
.copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance])); .copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance]));
} }
} }*/
fn write_instances( fn write_instances(
&mut self, &mut self,
mesh_list: &mut FreeList<Mesh>,
material_list: &mut FreeList<Material>,
objects_list: &mut FreeList<ObjectData>,
device: &wgpu::Device, device: &wgpu::Device,
queue: &wgpu::Queue, queue: &wgpu::Queue,
) -> SimpleRenderProgram { ) {
let mut program = SimpleRenderProgram::new(); Instances::reallocate_buffer(
SimpleInstances::reallocate_buffer(
device, device,
&mut self.instance_buffer, &mut self.instance_buffer,
self.instance_count, self.instance_count,
size_of::<SimpleModelInstance>(), size_of::<ModelInstance>(),
); );
let mut buffer = queue let mut buffer = queue
.write_buffer_with( .write_buffer_with(
@ -226,37 +209,33 @@ impl SimpleInstances {
) )
.unwrap(); .unwrap();
let mut index: u32 = 0; let mut index: u32 = 0;
let stride = size_of::<SimpleModelInstance>(); let stride = size_of::<ModelInstance>();
for (mesh, materials) in self.objects.iter() { for (mesh, materials) in self.models.iter_mut() {
program.push(SimpleRenderCode::Mesh( self.program.push(Code::Mesh(mesh_list.get(mesh).clone()));
mesh.it.vertices.clone(), for (material, objects) in materials.iter_mut() {
mesh.it.indices.clone(), self.program.push(Code::Material(material_list.get(material).clone()));
));
for (material, objects) in materials.iter() {
program.push(SimpleRenderCode::Material(material.it.group.clone()));
let before = index; let before = index;
for object in objects.iter() { objects.retain(|object,flag| {
let begin = index as usize * stride; let object = objects_list.get(object);
buffer if *flag == self.models_flag {
.slice(begin..begin + stride) let begin = index as usize * stride;
.copy_from_slice(bytemuck::cast_slice(&[object buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.model.as_mut().unwrap().instance]));
.0 index += 1;
.borrow() true
.model } else {
.as_ref() false
.unwrap() }
.instance])); });
index += 1; self.program.push(Code::Draw(before..index));
}
program.push(SimpleRenderCode::Draw(Range::from(before..index)));
} }
} }
program //println!("objects: {}",index);
self.models_flag = !self.models_flag;
} }
fn desc() -> wgpu::VertexBufferLayout<'static> { fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
array_stride: size_of::<SimpleModelInstance>() as wgpu::BufferAddress, array_stride: size_of::<ModelInstance>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Instance, step_mode: wgpu::VertexStepMode::Instance,
attributes: &[ attributes: &[
// todo: is this too big? // 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 { let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Instance Buffer"), label: Some("Instance Buffer"),
size: (size_of::<SimpleModelInstance>() * SimpleInstances::MIN_SIZE as usize) size: (size_of::<ModelInstance>() * Instances::MIN_SIZE as usize)
as wgpu::BufferAddress, as wgpu::BufferAddress,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
}); });
let light_buffer = device.create_buffer(&wgpu::BufferDescriptor { let light_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Buffer"), label: Some("Light Buffer"),
size: (size_of::<SimpleLightInstance>() * SimpleInstances::MIN_SIZE as usize) size: (size_of::<LightInstance>() * Instances::MIN_SIZE as usize)
as wgpu::BufferAddress, as wgpu::BufferAddress,
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
@ -325,38 +304,44 @@ impl SimpleInstances {
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false, mapped_at_creation: false,
});*/ });*/
SimpleInstances { Instances {
light_count: 0, light_count: 0,
//light_ref_last: 0, //light_ref_last: 0,
instance_count: 0, instance_count: 0,
light_buffer, light_buffer,
instance_buffer, instance_buffer,
//light_ref_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 { pub struct Renderer {
id_count: u64,
surface: wgpu::Surface<'static>, surface: wgpu::Surface<'static>,
config: wgpu::SurfaceConfiguration, config: wgpu::SurfaceConfiguration,
device: wgpu::Device, device: wgpu::Device,
queue: wgpu::Queue, queue: wgpu::Queue,
materials: FreeList<Material>,
textures: FreeList<Texture>,
meshes: FreeList<Mesh>,
light_instances: FreeList<LightData>,
model_instances: FreeList<ModelData>,
pub(crate) eye: eye::Eye, pub(crate) eye: eye::Eye,
material_layout: wgpu::BindGroupLayout, material_layout: wgpu::BindGroupLayout,
default_texture: SimpleTexture, default_texture: Texture,
default_material: SimpleMaterial, default_material: Material,
sky_pipeline: wgpu::RenderPipeline, sky_pipeline: wgpu::RenderPipeline,
instance_pipeline: wgpu::RenderPipeline, instance_pipeline: wgpu::RenderPipeline,
depth_texture: SimpleTexture, depth_texture: Texture,
pub(crate) instances: SimpleInstances, pub(crate) instances: Instances,
} }
#[repr(C)] #[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
struct SimpleVertex { struct TangentVertex {
position: [f32; 3], position: [f32; 3],
normal: [f32; 3], normal: [f32; 3],
tex_coord: [f32; 2], tex_coord: [f32; 2],
@ -364,10 +349,18 @@ struct SimpleVertex {
bitangent: [f32; 3], 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> { fn desc() -> wgpu::VertexBufferLayout<'static> {
wgpu::VertexBufferLayout { wgpu::VertexBufferLayout {
array_stride: size_of::<SimpleVertex>() as wgpu::BufferAddress, array_stride: size_of::<TangentVertex>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex, step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[ attributes: &[
wgpu::VertexAttribute { wgpu::VertexAttribute {
@ -390,12 +383,13 @@ impl SimpleVertex {
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct SimpleMesh { pub struct Mesh {
indices: Option<(wgpu::Buffer, u32)>, indices: Option<(wgpu::Buffer, u32)>,
vertices: (wgpu::Buffer, u32), vertices: (wgpu::Buffer, u32),
aabb: AABB,
} }
#[derive(Debug, Clone, Hash, Eq, PartialEq)] #[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct SimpleTexture { pub struct Texture {
texture: wgpu::Texture, texture: wgpu::Texture,
view: wgpu::TextureView, view: wgpu::TextureView,
} }
@ -404,13 +398,38 @@ pub struct TextureProperties {
height: u32, height: u32,
} }
// todo: use texture compression! // 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( pub fn load(
device: &Device, device: &Device,
queue: &Queue, queue: &Queue,
slice: impl AsRef<[u8]>, slice: impl AsRef<[u8]>,
properties: TextureProperties, properties: TextureProperties,
) -> SimpleTexture { ) -> Texture {
let size = wgpu::Extent3d { let size = wgpu::Extent3d {
width: properties.width, width: properties.width,
height: properties.height, height: properties.height,
@ -443,7 +462,7 @@ impl SimpleTexture {
}, },
size, size,
); );
SimpleTexture { Texture {
texture: diffuse_texture, texture: diffuse_texture,
view: diffuse_texture_view, view: diffuse_texture_view,
} }
@ -451,30 +470,10 @@ impl SimpleTexture {
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleMaterial { pub struct Material {
group: wgpu::BindGroup, group: wgpu::BindGroup,
} }
#[derive(Clone)]
pub struct Id<T> {
id: u64,
it: T,
}
impl<T> Hash for Id<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state)
}
}
impl<T> PartialEq for Id<T> {
fn eq(&self, other: &Self) -> bool {
other.id == self.id
}
}
impl<T> Eq for Id<T> {}
struct MaterialProperties { struct MaterialProperties {
edge: wgpu::AddressMode, edge: wgpu::AddressMode,
filter: wgpu::FilterMode, filter: wgpu::FilterMode,
@ -503,15 +502,15 @@ impl MaterialProperties {
} }
} }
impl SimpleMaterial { impl Material {
fn new( fn new(
device: &wgpu::Device, device: &wgpu::Device,
layout: &wgpu::BindGroupLayout, layout: &wgpu::BindGroupLayout,
base: &SimpleTexture, base: &Texture,
normal: &SimpleTexture, normal: &Texture,
reflect: &SimpleTexture, reflect: &Texture,
config: MaterialProperties, config: MaterialProperties,
) -> SimpleMaterial { ) -> Material {
let sampler = config.sampler(&device); let sampler = config.sampler(&device);
let group = device.create_bind_group(&wgpu::BindGroupDescriptor { let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: layout, layout: layout,
@ -535,7 +534,7 @@ impl SimpleMaterial {
], ],
label: Some("diffuse_bind_group"), label: Some("diffuse_bind_group"),
}); });
SimpleMaterial { group } Material { group }
} }
} }
@ -550,14 +549,14 @@ impl Display for PoolError {
impl Error for PoolError {} impl Error for PoolError {}
pub struct SimpleTreeNode { pub struct TreeNode {
object: Option<SimpleObject>, object: Option<ObjectData>,
children: Vec<SimpleTreeNode>, children: Vec<TreeNode>,
name: String, name: String,
} }
impl SimpleTreeNode { impl TreeNode {
pub fn first_object(&self) -> Option<SimpleObject> { pub fn first_object(&self) -> Option<ObjectData> {
if let Some(object) = self.object.clone() { if let Some(object) = self.object.clone() {
Some(object) Some(object)
} else { } else {
@ -577,18 +576,14 @@ impl Renderer {
const SIMPLE_RENDER_EYE_GROUP_POSITION: u32 = 0; const SIMPLE_RENDER_EYE_GROUP_POSITION: u32 = 0;
const SIMPLE_RENDER_TEXTURE_GROUP_POSITION: u32 = 1; const SIMPLE_RENDER_TEXTURE_GROUP_POSITION: u32 = 1;
const SIMPLE_RENDER_MODEL_GROUP_POSITION: u32 = 2; const SIMPLE_RENDER_MODEL_GROUP_POSITION: u32 = 2;
pub fn new_id<T>(&mut self, value: T) -> Id<T> { pub fn write_instances(&mut self, objects: &mut FreeList<ObjectData>) {
self.id_count += 1; self.instances.write_instances(&mut self.meshes, &mut self.materials, objects, &self.device, &self.queue);
Id {
it: value,
id: self.id_count,
}
} }
pub fn load_texture_from_bytes( pub fn load_texture_from_bytes(
&mut self, &mut self,
slice: impl AsRef<[u8]>, slice: impl AsRef<[u8]>,
format: Option<image::ImageFormat>, format: Option<image::ImageFormat>,
) -> SimpleTexture { ) -> Texture {
let image = if let Some(format) = format { let image = if let Some(format) = format {
image::load_from_memory_with_format(slice.as_ref(), format) image::load_from_memory_with_format(slice.as_ref(), format)
} else { } else {
@ -596,7 +591,7 @@ impl Renderer {
}; };
if let Ok(data) = image { if let Ok(data) = image {
let data = data.into_rgba8(); let data = data.into_rgba8();
SimpleTexture::load( Texture::load(
&self.device, &self.device,
&self.queue, &self.queue,
data.as_bytes(), data.as_bytes(),
@ -612,11 +607,11 @@ impl Renderer {
} }
pub fn new_material( pub fn new_material(
&mut self, &mut self,
base: &SimpleTexture, base: &Texture,
normal: &SimpleTexture, normal: &Texture,
reflect: &SimpleTexture, reflect: &Texture,
) -> Id<SimpleMaterial> { ) -> Id<Material> {
self.new_id(SimpleMaterial::new( self.materials.make(Material::new(
&self.device, &self.device,
&self.material_layout, &self.material_layout,
base, base,
@ -626,13 +621,13 @@ impl Renderer {
edge: wgpu::AddressMode::Repeat, edge: wgpu::AddressMode::Repeat,
filter: wgpu::FilterMode::Linear, filter: wgpu::FilterMode::Linear,
}, },
)) )).into()
} }
pub fn new_texture_from_gltf( pub fn new_texture_from_gltf(
&mut self, &mut self,
info: &gltf::texture::Texture, info: &gltf::texture::Texture,
images: &Vec<gltf::image::Data>, images: &Vec<gltf::image::Data>,
) -> SimpleTexture { ) -> Texture {
if let Some(image) = images.get(info.source().index()) { if let Some(image) = images.get(info.source().index()) {
let mut new_pixels = Vec::new(); let mut new_pixels = Vec::new();
let pixels: &Vec<u8>; let pixels: &Vec<u8>;
@ -649,7 +644,7 @@ impl Renderer {
gltf::image::Format::R8G8B8A8 => pixels = &image.pixels, gltf::image::Format::R8G8B8A8 => pixels = &image.pixels,
_ => return self.default_texture.clone(), _ => return self.default_texture.clone(),
} }
SimpleTexture::load( Texture::load(
&self.device, &self.device,
&self.queue, &self.queue,
pixels.as_slice(), pixels.as_slice(),
@ -665,42 +660,38 @@ impl Renderer {
pub fn new_mesh_from_gltf( pub fn new_mesh_from_gltf(
&mut self, &mut self,
primitive: gltf::Primitive, primitive: gltf::Primitive,
meshes: &mut HashMap<GltfVertexBufferKey, SimpleMesh>, meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
buffers: &[gltf::buffer::Data], buffers: &[gltf::buffer::Data],
) -> Id<SimpleMesh> { ) -> RefId<Mesh> {
let position_index = primitive let position_index = primitive
.get(&Semantic::Positions) .get(&Semantic::Positions)
.and_then(|it| it.view()) .and_then(|it| it.view()).map(|it| it.buffer().index());
.and_then(|it| Some(it.buffer().index()));
let normals_index = primitive let normals_index = primitive
.get(&Semantic::Normals) .get(&Semantic::Normals)
.and_then(|it| it.view()) .and_then(|it| it.view()).map(|it| it.buffer().index());
.and_then(|it| Some(it.buffer().index()));
let tex_coords_index = primitive let tex_coords_index = primitive
.get(&Semantic::TexCoords(0)) .get(&Semantic::TexCoords(0))
.and_then(|it| it.view()) .and_then(|it| it.view()).map(|it| it.buffer().index());
.and_then(|it| Some(it.buffer().index()));
let indices_index = primitive let indices_index = primitive
.indices() .indices()
.and_then(|it| it.view()) .and_then(|it| it.view()).map(|it| it.buffer().index());
.and_then(|it| Some(it.buffer().index()));
let tangent_index = primitive let tangent_index = primitive
.get(&Semantic::Tangents) .get(&Semantic::Tangents)
.and_then(|it| it.view()) .and_then(|it| it.view()).map(|it| it.buffer().index());
.and_then(|it| Some(it.buffer().index()));
let key = ( let key = (
position_index, position_index,
normals_index, normals_index,
tex_coords_index, tex_coords_index,
indices_index, indices_index,
); );
self.new_id( self.meshes.make(
meshes meshes
.entry(key) .entry(key)
.or_insert_with(|| { .or_insert_with(|| {
let mut tangents = false; let mut tangents = false;
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()])); let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
let mut vertex_data: Vec<SimpleVertex>; let mut vertex_data: Vec<TangentVertex>;
let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO);
if let Some(positions) = reader.read_positions() { if let Some(positions) = reader.read_positions() {
vertex_data = Vec::with_capacity(positions.len()); vertex_data = Vec::with_capacity(positions.len());
let mut normal = reader.read_normals().map(|it| it.into_iter()); 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()); let mut tangent = reader.read_tangents().map(|it| it.into_iter());
tangents = tangent.is_some(); tangents = tangent.is_some();
for position in positions { for position in positions {
vertex_data.push(SimpleVertex { aabb = aabb.extend_to(Vec3::from(position));
vertex_data.push(TangentVertex {
position, position,
normal: if let Some(ref mut normals) = normal { normal: if let Some(ref mut normals) = normal {
if let Some(normal) = normals.next() { if let Some(normal) = normals.next() {
@ -756,9 +748,10 @@ impl Renderer {
} else { } else {
None None
}; };
SimpleMesh { Mesh {
indices: indices_result, indices: indices_result,
vertices: vertices_result, vertices: vertices_result,
aabb,
} }
}) })
.clone(), .clone(),
@ -767,12 +760,12 @@ impl Renderer {
pub fn load_node_from_gltf( pub fn load_node_from_gltf(
&mut self, &mut self,
node: gltf::Node, node: gltf::Node,
meshes: &mut HashMap<GltfVertexBufferKey, SimpleMesh>, meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
textures: &mut HashMap<usize, SimpleTexture>, textures: &mut HashMap<usize, Texture>,
images: &Vec<gltf::image::Data>, images: &Vec<gltf::image::Data>,
buffers: &Vec<gltf::buffer::Data>, buffers: &Vec<gltf::buffer::Data>,
) -> SimpleTreeNode { ) -> TreeNode {
let mut tree_node = SimpleTreeNode { let mut tree_node = TreeNode {
object: None, object: None,
children: Vec::new(), children: Vec::new(),
name: node.name().unwrap_or("Node").to_string(), name: node.name().unwrap_or("Node").to_string(),
@ -809,9 +802,10 @@ impl Renderer {
}; };
let material = self.new_material(&base, &normal, &reflect); let material = self.new_material(&base, &normal, &reflect);
let mesh = self.new_mesh_from_gltf(primitive, meshes, buffers); let mesh = self.new_mesh_from_gltf(primitive, meshes, buffers);
let object = SimpleObject(Rc::new(RefCell::new(SimpleObjectData { let aabb = mesh.aabb;
model: Some(SimpleModelData { let object = ObjectData {
instance: SimpleModelInstance { model: Some(ModelData {
instance: ModelInstance {
transform: Mat4::default(), transform: Mat4::default(),
color: Vec4::from_array(color), color: Vec4::from_array(color),
lights: [0; 16], lights: [0; 16],
@ -821,17 +815,16 @@ impl Renderer {
rough, rough,
}, },
material, material,
mesh, mesh: mesh.into(),
}), }),
collider: SimpleColliderData { collider: ColliderData { shape: Shape::Sphere(aabb.1.distance(aabb.0)) },
transform: Mat4::default(), affine: Default::default(),
shape: Shape::None, asleep: false,
}, };
})));
if len == 1 { if len == 1 {
tree_node.object = Some(object); tree_node.object = Some(object);
} else { } else {
tree_node.children.push(SimpleTreeNode { tree_node.children.push(TreeNode {
object: Some(object), object: Some(object),
children: Vec::new(), children: Vec::new(),
name: "Primitive".to_string(), name: "Primitive".to_string(),
@ -846,17 +839,17 @@ impl Renderer {
} }
tree_node tree_node
} }
pub fn load_from_gltf(&mut self, slice: impl AsRef<[u8]>) -> SimpleTreeNode { pub fn load_from_gltf(&mut self, slice: impl AsRef<[u8]>) -> TreeNode {
let mut root = SimpleTreeNode { let mut root = TreeNode {
object: None, object: None,
children: Vec::new(), children: Vec::new(),
name: "Root".to_string(), name: "Root".to_string(),
}; };
if let Ok((document, buffers, images)) = gltf::import_slice(slice) { if let Ok((document, buffers, images)) = gltf::import_slice(slice) {
let mut meshes: HashMap<GltfVertexBufferKey, SimpleMesh> = HashMap::new(); let mut meshes: HashMap<GltfVertexBufferKey, Mesh> = HashMap::new();
let mut textures: HashMap<usize, SimpleTexture> = HashMap::new(); let mut textures: HashMap<usize, Texture> = HashMap::new();
for scene in document.scenes() { for scene in document.scenes() {
let mut scene_node = SimpleTreeNode { let mut scene_node = TreeNode {
object: None, object: None,
children: Vec::new(), children: Vec::new(),
name: scene.name().unwrap_or("Scene").to_string(), name: scene.name().unwrap_or("Scene").to_string(),
@ -876,12 +869,14 @@ impl Renderer {
root root
} }
pub fn resize(&mut self, width: u32, height: u32) { 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.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: Texture) {
pub fn set_skybox(&mut self, skybox: SimpleTexture) {
self.eye.skybox(&self.device, skybox); self.eye.skybox(&self.device, skybox);
} }
pub async fn new(window: &Arc<Window>) -> anyhow::Result<Self> { pub async fn new(window: &Arc<Window>) -> anyhow::Result<Self> {
@ -917,6 +912,7 @@ impl Renderer {
power_preference: wgpu::PowerPreference::default(), power_preference: wgpu::PowerPreference::default(),
compatible_surface: Some(&surface), compatible_surface: Some(&surface),
force_fallback_adapter: false, force_fallback_adapter: false,
//apply_limit_buckets: false,
}) })
.await?; .await?;
@ -950,9 +946,10 @@ impl Renderer {
let config = wgpu::SurfaceConfiguration { let config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT, usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: surface_format, format: surface_format,
//color_space: Default::default(),
width, width,
height, height,
present_mode: surface_caps.present_modes[0], present_mode: wgpu::PresentMode::Fifo,
alpha_mode: surface_caps.alpha_modes[0], alpha_mode: surface_caps.alpha_modes[0],
view_formats: vec![], view_formats: vec![],
desired_maximum_frame_latency: 2, desired_maximum_frame_latency: 2,
@ -961,7 +958,7 @@ impl Renderer {
let shader = let shader =
device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl")); device.create_shader_module(wgpu::include_wgsl!("../assets/SimpleShader.wgsl"));
let default_texture = SimpleTexture::load( let default_texture = Texture::load(
&device, &device,
&queue, &queue,
[0, 0, 255, 255], [0, 0, 255, 255],
@ -1027,7 +1024,7 @@ impl Renderer {
vertex: wgpu::VertexState { vertex: wgpu::VertexState {
module: &shader, module: &shader,
entry_point: Some("vs_main"), entry_point: Some("vs_main"),
buffers: &[SimpleVertex::desc(), SimpleInstances::desc()], buffers: &[TangentVertex::desc(), Instances::desc()],
compilation_options: wgpu::PipelineCompilationOptions::default(), compilation_options: wgpu::PipelineCompilationOptions::default(),
}, },
fragment: Some(wgpu::FragmentState { fragment: Some(wgpu::FragmentState {
@ -1104,48 +1101,11 @@ impl Renderer {
cache: None, cache: None,
}); });
let size = wgpu::Extent3d { let instances = Instances::new(&device);
// 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 = SimpleInstances::new(&device); let depth_texture = Texture::depth(&config,&device);
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { let default_material = Material::new(
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&DEFAULT_VERTICES),
usage: wgpu::BufferUsages::VERTEX,
});
let default_material = SimpleMaterial::new(
&device, &device,
&texture_bind_group_layout, &texture_bind_group_layout,
&default_texture, &default_texture,
@ -1155,14 +1115,10 @@ impl Renderer {
); );
Ok(Renderer { Ok(Renderer {
id_count: 0,
material_layout: texture_bind_group_layout, material_layout: texture_bind_group_layout,
default_texture, default_texture,
default_material, default_material,
depth_texture: SimpleTexture { depth_texture,
texture: depth_texture,
view: depth_view,
},
instances, instances,
sky_pipeline, sky_pipeline,
instance_pipeline, instance_pipeline,
@ -1170,28 +1126,31 @@ impl Renderer {
config, config,
device, device,
queue, queue,
materials: Default::default(),
textures: Default::default(),
meshes: Default::default(),
light_instances: Default::default(),
model_instances: Default::default(),
eye, eye,
}) })
} }
pub(crate) fn render(&mut self, window: &Arc<Window>) -> anyhow::Result<()> { pub(crate) fn render(&mut self, window: &Arc<Window>, objects: &mut FreeList<ObjectData>) -> anyhow::Result<()> {
window.request_redraw(); let surface_texture = match self.surface.get_current_texture() {
let mut resize_renderer = false;
let output = match self.surface.get_current_texture() {
wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture, wgpu::CurrentSurfaceTexture::Success(surface_texture) => surface_texture,
wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => { wgpu::CurrentSurfaceTexture::Suboptimal(surface_texture) => {
resize_renderer = true; //moved reconfigure to avoid a crash when resizing window println!("suboptimal");
surface_texture self.surface.configure(&self.device, &self.config);
return Ok(());
} }
wgpu::CurrentSurfaceTexture::Timeout wgpu::CurrentSurfaceTexture::Timeout
| wgpu::CurrentSurfaceTexture::Occluded | wgpu::CurrentSurfaceTexture::Occluded
| wgpu::CurrentSurfaceTexture::Validation => { | wgpu::CurrentSurfaceTexture::Validation => {
// Skip this frame println!("timeout");
return Ok(()); return Ok(());
} }
wgpu::CurrentSurfaceTexture::Outdated => { wgpu::CurrentSurfaceTexture::Outdated => {
self.surface.configure(&self.device, &self.config); self.surface.configure(&self.device, &self.config);
println!("outdated");
return Ok(()); return Ok(());
} }
wgpu::CurrentSurfaceTexture::Lost => { wgpu::CurrentSurfaceTexture::Lost => {
@ -1200,17 +1159,19 @@ impl Renderer {
anyhow::bail!("Lost device"); 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 let mut encoder = self
.device .device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { .create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Render Encoder"), 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 { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
@ -1245,7 +1206,7 @@ impl Renderer {
pass.set_pipeline(&self.instance_pipeline); pass.set_pipeline(&self.instance_pipeline);
pass.set_bind_group(0, &self.eye.group, &[]); pass.set_bind_group(0, &self.eye.group, &[]);
pass.set_vertex_buffer(1, self.instances.instance_buffer.slice(..)); 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, &[]); pass.set_bind_group(1, &self.default_material.group, &[]);
@ -1255,24 +1216,24 @@ impl Renderer {
self.queue.submit(Some(encoder.finish())); 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);
// its rust just being stupid
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
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);
/* /*
also in this snippet call the functions to also in this snippet call the functions to
a: resize window resolution a: resize window resolution // todo: idk why it's ignoring me
b: resize projection b: resize projection // done: made eye.rs write to eye buffer
currently when you resize the window the projection matrix is not updated 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 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 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 i would add it myself but i don't know wGPU or rust all that well sooooo
*/ */
}
window.request_redraw();
Ok(()) Ok(())
} }

19
src/state.rs Normal file
View file

@ -0,0 +1,19 @@
use crate::list::FreeList;
use crate::{render, world};
#[derive(Default)]
pub struct State {
pub objects: FreeList<world::ObjectData>,
pub lights: FreeList<render::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()
}
}
}

0
src/ui/mod.rs Normal file
View file

View file

@ -1,116 +1,304 @@
use crate::render::{Renderer, SimpleLightData, SimpleModelData}; use glam::{Affine3, IVec3, Mat4, Quat, Vec3};
use glam::{IVec3, Mat4, Vec3};
use std::cell::RefCell; use std::cell::RefCell;
use std::hash::{BuildHasherDefault, Hash, Hasher}; use std::hash::{BuildHasherDefault, Hash, Hasher};
use std::path::absolute;
use std::rc::Rc; use std::rc::Rc;
use mars::vm::Object;
use wgpu::naga::{FastHashMap, FastHashSet}; use wgpu::naga::{FastHashMap, FastHashSet};
use crate::{list, render};
use crate::list::{FreeList, Id, MaybeId, RefId, SingleId};
#[derive(Clone)] #[derive(Clone)]
pub enum Shape { pub enum Shape {
Block(Vec3), Block(Vec3),
Sphere(f32), Sphere(f32),
None,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleColliderData { pub struct ColliderData {
pub transform: Mat4,
pub shape: Shape, pub shape: Shape,
} }
impl SimpleColliderData { impl ColliderData {
fn aabb(&self) -> Option<AABB> { fn aabb(&self, affine: Affine3) -> AABB {
let (_scale, _rotation, translation) = self.transform.to_scale_rotation_translation(); let (_scale, _rotation, translation) = affine.to_scale_rotation_translation();
match self.shape { match self.shape {
Shape::Block(size) => { Shape::Block(size) => {
let radius_offset = Vec3::splat(size.length()); let radius_offset = Vec3::splat(size.length());
Some(AABB( AABB(
translation - radius_offset, translation - radius_offset,
translation + radius_offset, translation + radius_offset,
)) )
} }
Shape::Sphere(radius) => { Shape::Sphere(radius) => {
let radius_offset = Vec3::splat(radius); let radius_offset = Vec3::splat(radius);
Some(AABB( AABB(
translation - radius_offset, translation - radius_offset,
translation + radius_offset, translation + radius_offset,
)) )
} }
Shape::None => None,
} }
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleObjectData { pub struct ObjectData {
pub model: Option<SimpleModelData>, pub model: Option<render::ModelData>,
pub collider: SimpleColliderData, pub collider: ColliderData,
pub affine: Affine3,
pub asleep: bool,
}
#[derive(Clone, Eq, PartialEq)]
pub enum InterestType {
Light(Id<render::LightData>),
Object(Id<ObjectData>)
} }
#[derive(Clone)] #[derive(Clone)]
pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>); pub struct Interest {
it: InterestType,
impl PartialEq for SimpleLight { aabb: AABB,
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)] #[derive(Clone)]
pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>); pub struct InterestNode {
interest: Interest,
next: MaybeId<InterestNode>,
}
impl SimpleObject { #[derive(Clone, Copy, Debug, PartialEq)]
pub(crate) fn hard_clone(&self) -> SimpleObject { pub struct AABB(pub(crate) Vec3, pub(crate) Vec3);
SimpleObject {
0: Rc::new(RefCell::new(SimpleObjectData { impl AABB {
model: self.0.borrow().model.clone(), pub(crate) fn new(translation: Vec3, size: Vec3) -> AABB {
collider: self.0.borrow().collider.clone(), 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<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,
impl PartialEq for SimpleObject { blocks: &mut FreeList<OctreeBlock>,
fn eq(&self, other: &Self) -> bool { block: Id<OctreeBlock>,
Rc::ptr_eq(&self.0, &other.0) objects: &mut FreeList<ObjectData>,
} debug: &Option<render::ModelData>,
} pos: IVec3,
size: i32
impl Eq for SimpleObject {} ) {
let entry = self.map.get(&pos);
impl Hash for SimpleObject { if let Some(debug) = debug {
fn hash<H: Hasher>(&self, state: &mut H) { if entry.is_none() {
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state); 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),
#[derive(Eq, Hash, PartialEq, Clone)] collider: ColliderData {
pub enum Interest { shape: Shape::Sphere(size as f32),
Light(SimpleLight), },
Object(SimpleObject), affine: Default::default(),
} asleep: false,
}).id);
impl Interest { println!("pos {} size {}",pos,size)
fn aabb(&self) -> Option<AABB> {
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()),
))
} }
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<ObjectData>, world: &mut World, debug: Option<render::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()));
}
}
}
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::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) => {
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<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.interests, objects)
}
}
}
fn sync(&mut self) {}
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -125,188 +313,153 @@ impl Material {
Material { Material {
volume: 0, volume: 0,
velocity: Vec3::ZERO, velocity: Vec3::ZERO,
material: 0, material: 0
} }
} }
} }
#[derive(Clone, Copy)] impl OctreeBlock {
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<Renderer>,
}
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<SimpleObject>) {
for (index, item) in self.map.it.iter_mut() {}
}
fn step(&mut self) {}
fn sync(&mut self) {}
}
type VecMap<T> = FastHashMap<IVec3,T>;
struct Level<T,S> {
sub_level:
entries: T
}
struct MatterLevel<T> {
blocks: Option<VecMap<Material>>
}
struct InterestLevel<T> {
blocks: Option<VecMap<Interest>>
}
struct HashedMap {
matter:
}
impl HashedMap {
fn new() -> HashedMap {
HashedMap {
it: FastHashMap::default()
}
}
}
/*impl Block {
const FLOOR: i32 = 4; const FLOOR: i32 = 4;
const CHUNK_SIZE: i32 = Block::FLOOR * 64; const CHUNK_SIZE: i32 = OctreeBlock::FLOOR * 64;
const MAX_IDEAL_INTEREST: usize = 4; const MAX_IDEAL_INTEREST: usize = 4;
fn new() -> Block { fn new() -> OctreeBlock {
Block { OctreeBlock {
debug: None, interests: 0,
material: Material::new(), material: Material::new(),
interests: FastHashSet::default(), first: MaybeId::NULL,
blocks: None, blocks: [MaybeId::NULL;8],
} }
} }
} }
type Blocks = Box<[Block; 8]>; struct OctreeBlock {
interests: u32, // 1
struct Block { first: MaybeId<InterestNode>, // 1
debug: Option<SimpleObject>, material: Material, // 4
interests: FastHashSet<Interest>, blocks: Blocks, // 8
material: Material,
blocks: Option<Blocks>,
} }
impl Block { impl OctreeBlock {
pub fn set_debug(&mut self, value: Option<SimpleObject>, pos: Vec3, size: f32) {} fn push(&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(&mut self, interests: &mut FreeList<InterestNode>, 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<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(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 { type Blocks = [MaybeId<OctreeBlock>; 8];
fn new() -> Self;
fn place_with_aabb(&mut self, interest: Interest, aabb: AABB);
}
struct OctreeMap { struct OctreeMap {
it: FastHashMap<IVec3, Block>, interests: FreeList<InterestNode>,
blocks: FreeList<OctreeBlock>,
map: FastHashMap<IVec3, Id<OctreeBlock>>,
} }
impl OctreeMap { impl OctreeMap {
fn place_with_index(&mut self, interest: Interest, mut index: IVec3) { fn place_interest(&mut self, interest: Interest) {
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) {
let mut d = 4; 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 { while offset.element_sum() > (d * 2) as f32 {
d *= 2 d *= 2
} }
let iaabb0 = (aabb.0 / d as f32).floor().as_ivec3(); let iaabb = IAABB(
let iaabb1 = (aabb.1 / d as f32).ceil().as_ivec3(); (interest.aabb.0 / d as f32).floor().as_ivec3() + IVec3::splat(d / 2),
for x in iaabb0.x..iaabb1.x { (interest.aabb.1 / d as f32).floor().as_ivec3() + IVec3::splat(d / 2),
for y in iaabb0.y..iaabb1.y { );
for z in iaabb0.z..iaabb1.z { println!("attempting place I: {} {} {} A: {} {}",iaabb.0,iaabb.1,d,interest.aabb.0,interest.aabb.1);
self.place_with_index(interest.clone(), IVec3::new(x, y, z) * d as i32); 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 { fn new() -> OctreeMap {
OctreeMap { OctreeMap {
it: FastHashMap::with_hasher(BuildHasherDefault::default()), interests: Default::default(),
blocks: Default::default(),
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
} }
} }
}*/ }