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
.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">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/stupid_display/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/mars/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/lang/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/stupid_display/target" />
<excludeFolder url="file://$MODULE_DIR$/target" />
<excludeFolder url="file://$MODULE_DIR$/src/mars/target" />
<excludeFolder url="file://$MODULE_DIR$/lang/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />

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]
#![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"

View file

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

View file

@ -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<MouseButton, bool>,
@ -34,68 +36,66 @@ impl Controller {
}
pub struct AppState {
world: World,
state: state::State,
world: Id<World>,
debug: OctreeDebug,
debug_model: ModelData,
controller: Controller,
window: Arc<Window>,
clients: Vec<SimpleObject>,
}
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<Window>) -> Result<AppState, Box<dyn std::error::Error>> {
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<f64>) {
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<AppState> for App {
let window = Arc::new(event_loop.create_window(window_attributes).unwrap());
window.set_title("Pool");
#[cfg(not(target_arch = "wasm32"))]
{
// If we are not on web we can use pollster to
@ -238,15 +240,14 @@ impl ApplicationHandler<AppState> for App {
WindowEvent::CloseRequested => event_loop.exit(),
WindowEvent::Resized(size) => state.resize(size.width, size.height),
WindowEvent::RedrawRequested => {
use std::time::Instant;
let now = Instant::now();
state.update();
let mut movement = Vec3::new(0.0, 0.0, 0.0);
let pressed = |keycode: KeyCode| {
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<AppState> 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<AppState> 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,

View file

@ -95,17 +95,20 @@ fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
return textureSample(sky_texture, sky_sampler, uv);
}
fn rotation(mat: mat4x4<f32>) -> mat3x3<f32> {
fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
return mat3x3<f32>(
mat[0].xyz,
mat[1].xyz,
mat[2].xyz,
it[0].xyz,
it[1].xyz,
it[2].xyz,
);
}
fn translation(mat: mat4x4<f32>) -> vec4<f32> {
//return vec4<f32>(mat[0][3],mat[1][3],mat[2][3],mat[3][3]);
return mat[3];
fn translation(it: mat4x4<f32>) -> vec4<f32> {
return it[3];
}
fn light_aspect(light: vec3<f32>, dir: vec3<f32>) -> vec4<f32> {
return vec4<f32>(0.0,0.0,0.0,0.0);
}
@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_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);

Binary file not shown.

View file

@ -2,4 +2,6 @@
pub mod app;
pub mod render;
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 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);

View file

@ -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<SimpleMaterial>,
pub mesh: Id<SimpleMesh>,
pub struct ModelData {
pub instance: ModelInstance,
pub material: Id<Material>,
pub mesh: Id<Mesh>,
}
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<Id<SimpleMesh>, FastHashMap<Id<SimpleMaterial>, FastHashSet<SimpleObject>>>,
lights: FastHashMap<SimpleLight, usize>,
models_flag: bool,
program: Program,
models: FastHashMap<Id<Mesh>, FastHashMap<Id<Material>, FastHashMap<Id<ObjectData>,bool>>>,
}
enum SimpleRenderCode {
Material(wgpu::BindGroup),
Mesh((wgpu::Buffer, u32), Option<(wgpu::Buffer, u32)>),
enum Code {
Material(Material),
Mesh(Mesh),
Draw(Range<u32>),
}
struct SimpleRenderProgram(Vec<SimpleRenderCode>);
struct Program(Vec<Code>);
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<ObjectData>) {
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<LightData>) {
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<usize> = 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::<u32>());
/*
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::<SimpleLight>(),
size_of::<WorldLight>(),
);
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::<SimpleLight>();
let stride = size_of::<WorldLight>();
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<Mesh>,
material_list: &mut FreeList<Material>,
objects_list: &mut FreeList<ObjectData>,
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::<SimpleModelInstance>(),
size_of::<ModelInstance>(),
);
let mut buffer = queue
.write_buffer_with(
@ -226,37 +209,33 @@ impl SimpleInstances {
)
.unwrap();
let mut index: u32 = 0;
let stride = size_of::<SimpleModelInstance>();
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::<ModelInstance>();
for (mesh, materials) in self.models.iter_mut() {
self.program.push(Code::Mesh(mesh_list.get(mesh).clone()));
for (material, objects) in materials.iter_mut() {
self.program.push(Code::Material(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::<SimpleModelInstance>() as wgpu::BufferAddress,
array_stride: size_of::<ModelInstance>() 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::<SimpleModelInstance>() * SimpleInstances::MIN_SIZE as usize)
size: (size_of::<ModelInstance>() * Instances::MIN_SIZE as usize)
as wgpu::BufferAddress,
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let light_buffer = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Light Buffer"),
size: (size_of::<SimpleLightInstance>() * SimpleInstances::MIN_SIZE as usize)
size: (size_of::<LightInstance>() * 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<Material>,
textures: FreeList<Texture>,
meshes: FreeList<Mesh>,
light_instances: FreeList<LightData>,
model_instances: FreeList<ModelData>,
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::<SimpleVertex>() as wgpu::BufferAddress,
array_stride: size_of::<TangentVertex>() 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<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 {
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<SimpleObject>,
children: Vec<SimpleTreeNode>,
pub struct TreeNode {
object: Option<ObjectData>,
children: Vec<TreeNode>,
name: String,
}
impl SimpleTreeNode {
pub fn first_object(&self) -> Option<SimpleObject> {
impl TreeNode {
pub fn first_object(&self) -> Option<ObjectData> {
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<T>(&mut self, value: T) -> Id<T> {
self.id_count += 1;
Id {
it: value,
id: self.id_count,
}
pub fn write_instances(&mut self, objects: &mut FreeList<ObjectData>) {
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<image::ImageFormat>,
) -> 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<SimpleMaterial> {
self.new_id(SimpleMaterial::new(
base: &Texture,
normal: &Texture,
reflect: &Texture,
) -> Id<Material> {
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<gltf::image::Data>,
) -> SimpleTexture {
) -> Texture {
if let Some(image) = images.get(info.source().index()) {
let mut new_pixels = Vec::new();
let pixels: &Vec<u8>;
@ -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<GltfVertexBufferKey, SimpleMesh>,
meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
buffers: &[gltf::buffer::Data],
) -> Id<SimpleMesh> {
) -> RefId<Mesh> {
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<SimpleVertex>;
let mut vertex_data: Vec<TangentVertex>;
let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO);
if let Some(positions) = reader.read_positions() {
vertex_data = Vec::with_capacity(positions.len());
let mut normal = reader.read_normals().map(|it| it.into_iter());
@ -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<GltfVertexBufferKey, SimpleMesh>,
textures: &mut HashMap<usize, SimpleTexture>,
meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
textures: &mut HashMap<usize, Texture>,
images: &Vec<gltf::image::Data>,
buffers: &Vec<gltf::buffer::Data>,
) -> 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<GltfVertexBufferKey, SimpleMesh> = HashMap::new();
let mut textures: HashMap<usize, SimpleTexture> = HashMap::new();
let mut meshes: HashMap<GltfVertexBufferKey, Mesh> = HashMap::new();
let mut textures: HashMap<usize, Texture> = 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<Window>) -> anyhow::Result<Self> {
@ -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<Window>) -> 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<Window>, objects: &mut FreeList<ObjectData>) -> 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(())
}

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::{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<AABB> {
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<SimpleModelData>,
pub collider: SimpleColliderData,
pub struct ObjectData {
pub model: Option<render::ModelData>,
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)]
pub struct SimpleLight(pub Rc<RefCell<SimpleLightData>>);
impl PartialEq for SimpleLight {
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Eq for SimpleLight {}
impl Hash for SimpleLight {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleLight>).hash(state)
}
pub struct Interest {
it: InterestType,
aabb: AABB,
}
#[derive(Clone)]
pub struct SimpleObject(pub Rc<RefCell<SimpleObjectData>>);
pub struct InterestNode {
interest: Interest,
next: MaybeId<InterestNode>,
}
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<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()
}
}
}
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<H: Hasher>(&self, state: &mut H) {
(self.0.as_ptr() as *const RefCell<SimpleModelData>).hash(state);
}
}
#[derive(Eq, Hash, PartialEq, Clone)]
pub enum Interest {
Light(SimpleLight),
Object(SimpleObject),
}
impl Interest {
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()),
))
fn set_block(
&mut self,
blocks: &mut FreeList<OctreeBlock>,
block: Id<OctreeBlock>,
objects: &mut FreeList<ObjectData>,
debug: &Option<render::ModelData>,
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<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)]
@ -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<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 {
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<SimpleObject>,
interests: FastHashSet<Interest>,
material: Material,
blocks: Option<Blocks>,
struct OctreeBlock {
interests: u32, // 1
first: MaybeId<InterestNode>, // 1
material: Material, // 4
blocks: Blocks, // 8
}
impl Block {
pub fn set_debug(&mut self, value: Option<SimpleObject>, pos: Vec3, size: f32) {}
impl OctreeBlock {
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 {
fn new() -> Self;
fn place_with_aabb(&mut self, interest: Interest, aabb: AABB);
}
type Blocks = [MaybeId<OctreeBlock>; 8];
struct OctreeMap {
it: FastHashMap<IVec3, Block>,
interests: FreeList<InterestNode>,
blocks: FreeList<OctreeBlock>,
map: FastHashMap<IVec3, Id<OctreeBlock>>,
}
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()),
}
}
}*/
}