Reorganised: renderer;
This commit is contained in:
parent
905d05bc39
commit
afaad0ae0d
16 changed files with 1310 additions and 1170 deletions
47
src/app.rs
47
src/app.rs
|
|
@ -1,7 +1,7 @@
|
||||||
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::{ModelData, Renderer};
|
use crate::render::{loader, Renderer};
|
||||||
use crate::world::{World, ObjectData, OctreeDebug};
|
use crate::world::{World, ObjectData};
|
||||||
use glam::{Affine3, 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")]
|
||||||
|
|
@ -17,7 +17,11 @@ use winit::{
|
||||||
window::Window,
|
window::Window,
|
||||||
};
|
};
|
||||||
use crate::list::Id;
|
use crate::list::Id;
|
||||||
|
use crate::render::instance::{Material, ModelData};
|
||||||
|
use crate::render::instance::material::MaterialProperties;
|
||||||
|
use crate::render::texture::Texture;
|
||||||
use crate::state;
|
use crate::state;
|
||||||
|
use crate::world::debug::OctreeDebug;
|
||||||
|
|
||||||
struct Controller {
|
struct Controller {
|
||||||
buttons: HashMap<MouseButton, bool>,
|
buttons: HashMap<MouseButton, bool>,
|
||||||
|
|
@ -55,36 +59,31 @@ impl AppState {
|
||||||
let mut world = state.worlds.make(World::new());
|
let mut world = state.worlds.make(World::new());
|
||||||
state.renderer = Some(Renderer::new(&window).await?);
|
state.renderer = Some(Renderer::new(&window).await?);
|
||||||
let renderer = state.renderer.as_mut().unwrap();
|
let renderer = state.renderer.as_mut().unwrap();
|
||||||
let debug_file = renderer.load_from_gltf(include_bytes!("assets/debug.glb"));
|
let debug_file = loader::load_from_gltf(renderer,include_bytes!("assets/debug.glb"));
|
||||||
let debug_model = debug_file.first_object().unwrap().model.unwrap();
|
let debug_model = debug_file.first_object().unwrap().model.unwrap();
|
||||||
{
|
{
|
||||||
let file = renderer.load_from_gltf(include_bytes!("assets/sphere.glb"));
|
let file = loader::load_from_gltf(renderer,include_bytes!("assets/sphere.glb"));
|
||||||
let mut block = file.first_object().unwrap();
|
let mut block = file.first_object().unwrap();
|
||||||
let skybox = renderer.load_texture_from_bytes(
|
let skybox = Texture::load_from_file_bytes(renderer,include_bytes!("assets/skybox2.png"), None);
|
||||||
include_bytes!("assets/skybox2.png"),
|
renderer.set_skybox(skybox.unwrap());
|
||||||
Some(image::ImageFormat::Png),
|
let color = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/color.png"), None);
|
||||||
);
|
let normal = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/normal.png"), None);
|
||||||
renderer.set_skybox(skybox);
|
let roughness = Texture::load_from_file_bytes(renderer,include_bytes!("assets/plank/roughness.png"), None);
|
||||||
let color = renderer.load_texture_from_bytes(include_bytes!("assets/plank/color.png"), None);
|
let mat = Material::new(renderer, &color.unwrap(), &normal.unwrap(), &roughness.unwrap(), MaterialProperties::default());
|
||||||
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;
|
block.model.as_mut().unwrap().material = mat;
|
||||||
for x in -BLOCKS..=BLOCKS {
|
for x in -BLOCKS..=BLOCKS {
|
||||||
for y in -BLOCKS..=BLOCKS {
|
for y in -BLOCKS..=BLOCKS {
|
||||||
let id = world.add_object(state.objects.make(block.clone()));
|
let id = world.add_object(state.objects.make(block.clone()));
|
||||||
let block = state.objects.get(&id);
|
let block = state.objects.get(&id);
|
||||||
{
|
block.set_affine(Affine3::from_translation(
|
||||||
block.affine = Affine3::from_translation(
|
Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0)
|
||||||
Vec3::new(-x as f32 * 3.0, -2.0, -y as f32 * 3.0),
|
));
|
||||||
);
|
block.model.as_mut().unwrap().instance.color = Vec4::new(
|
||||||
block.model.as_mut().unwrap().instance.color = Vec4::new(
|
0.3,
|
||||||
0.3,
|
(x + BLOCKS) as f32 / BLOCKS as f32,
|
||||||
(x + BLOCKS) as f32 / BLOCKS as f32,
|
(y + BLOCKS) as f32 / BLOCKS as f32,
|
||||||
(y + BLOCKS) as f32 / BLOCKS as f32,
|
1.0,
|
||||||
1.0,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
18
src/lib.rs
18
src/lib.rs
|
|
@ -1,7 +1,23 @@
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
|
||||||
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 list;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
pub mod net;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PoolError(String);
|
||||||
|
|
||||||
|
impl Display for PoolError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Error for PoolError {}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#![recursion_limit = "256"]
|
#![recursion_limit = "256"]
|
||||||
|
|
||||||
use game::*;
|
use pool::*;
|
||||||
fn main() {
|
fn main() {
|
||||||
app::run().unwrap();
|
app::run().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
0
src/net/mod.rs
Normal file
0
src/net/mod.rs
Normal file
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::render::{MaterialProperties, Texture};
|
use crate::render::{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};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use wgpu::{Device, Queue};
|
use wgpu::{Device, Queue};
|
||||||
|
use crate::render::instance::material::MaterialProperties;
|
||||||
|
|
||||||
pub(crate) struct Eye {
|
pub(crate) struct Eye {
|
||||||
pub(crate) frame: Affine3A,
|
pub(crate) frame: Affine3A,
|
||||||
|
|
@ -16,6 +17,7 @@ pub(crate) struct Eye {
|
||||||
pub(crate) camera_buffer: wgpu::Buffer,
|
pub(crate) camera_buffer: wgpu::Buffer,
|
||||||
pub(crate) layout: wgpu::BindGroupLayout,
|
pub(crate) layout: wgpu::BindGroupLayout,
|
||||||
pub(crate) group: wgpu::BindGroup,
|
pub(crate) group: wgpu::BindGroup,
|
||||||
|
pub sky_pipeline: wgpu::RenderPipeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -49,7 +51,7 @@ impl Eye {
|
||||||
bytemuck::cast_slice(&[self.environment]),
|
bytemuck::cast_slice(&[self.environment]),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pub(crate) fn new(device: &Device, width: u32, height: u32, skybox: Texture) -> Eye {
|
pub(crate) fn new(device: &Device, config: &wgpu::SurfaceConfiguration, 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(&[
|
||||||
|
|
@ -116,6 +118,51 @@ impl Eye {
|
||||||
label: Some("eye_bind_group_layout"),
|
label: Some("eye_bind_group_layout"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let shader = device.create_shader_module(wgpu::include_wgsl!("sky.wgsl"));
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("Render Pipeline Layout"),
|
||||||
|
bind_group_layouts: &[Some(&layout)],
|
||||||
|
immediate_size: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
//todo: use a pipeline cache!
|
||||||
|
let sky_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("Sky Pipeline"),
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: Some("vs_sky"),
|
||||||
|
compilation_options: Default::default(),
|
||||||
|
buffers: &[],
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: Some("fs_sky"),
|
||||||
|
compilation_options: Default::default(),
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
// 4.
|
||||||
|
format: config.format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
front_face: wgpu::FrontFace::Cw,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
depth_write_enabled: Some(false),
|
||||||
|
depth_compare: Some(wgpu::CompareFunction::LessEqual),
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview_mask: None,
|
||||||
|
cache: None,
|
||||||
|
});
|
||||||
|
|
||||||
let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox);
|
let group = Eye::bind_group(&layout, device, &camera_buffer, &environment_buffer, skybox);
|
||||||
|
|
||||||
Eye {
|
Eye {
|
||||||
|
|
@ -129,6 +176,7 @@ impl Eye {
|
||||||
layout,
|
layout,
|
||||||
environment,
|
environment,
|
||||||
environment_buffer,
|
environment_buffer,
|
||||||
|
sky_pipeline,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn bind_group(
|
fn bind_group(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ struct Eye {
|
||||||
frame: mat4x4<f32>,
|
frame: mat4x4<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Vertex shader
|
|
||||||
@group(0) @binding(0)
|
@group(0) @binding(0)
|
||||||
var<uniform> eye: Eye;
|
var<uniform> eye: Eye;
|
||||||
@group(0) @binding(1)
|
@group(0) @binding(1)
|
||||||
|
|
@ -143,32 +142,4 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
let result = (diffuse_effect.xyz + specular_color.xyz);
|
let result = (diffuse_effect.xyz + specular_color.xyz);
|
||||||
|
|
||||||
return vec4<f32>(result.xyz,object_color.a);
|
return vec4<f32>(result.xyz,object_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SkyOutput {
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) pos: vec4<f32> // unadulterated by WGSL
|
|
||||||
}
|
|
||||||
|
|
||||||
const TRI_VERTICES = array(
|
|
||||||
vec4(-1.0, -1.0, 1.0, 1.0),
|
|
||||||
vec4(-1.0, 1.0, 1.0, 1.0),
|
|
||||||
vec4( 1.0, -1.0, 1.0, 1.0),
|
|
||||||
vec4( 1.0, 1.0, 1.0, 1.0),
|
|
||||||
vec4(-1.0, 1.0, 1.0, 1.0),
|
|
||||||
vec4( 1.0, -1.0, 1.0, 1.0),
|
|
||||||
);
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
|
|
||||||
var out: SkyOutput;
|
|
||||||
out.position = TRI_VERTICES[index];
|
|
||||||
out.pos = out.position;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
|
|
||||||
let look = rotation(eye.frame) * in.pos.xyz;
|
|
||||||
return sky_aspect(look);
|
|
||||||
}
|
|
||||||
130
src/render/instance/material.rs
Normal file
130
src/render/instance/material.rs
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
use wgpu::Device;
|
||||||
|
use crate::list::{FreeList, Id};
|
||||||
|
use crate::render::Renderer;
|
||||||
|
use crate::render::texture::Texture;
|
||||||
|
|
||||||
|
pub struct Materials {
|
||||||
|
pub(crate) layout: wgpu::BindGroupLayout,
|
||||||
|
pub(crate) list: FreeList<Material>,
|
||||||
|
default: Material,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Materials {
|
||||||
|
pub(crate) fn new(device: &Device, default: Texture) -> Materials {
|
||||||
|
fn texture_bind_group_layout_entry(binding: u32) -> wgpu::BindGroupLayoutEntry {
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
multisampled: false,
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout =
|
||||||
|
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
texture_bind_group_layout_entry(1),
|
||||||
|
texture_bind_group_layout_entry(2),
|
||||||
|
texture_bind_group_layout_entry(3),
|
||||||
|
],
|
||||||
|
label: Some("texture_bind_group_layout"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let default = Material::_new(
|
||||||
|
&device,
|
||||||
|
&layout,
|
||||||
|
&default,
|
||||||
|
&default,
|
||||||
|
&default,
|
||||||
|
MaterialProperties::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Materials {
|
||||||
|
layout,
|
||||||
|
list: Default::default(),
|
||||||
|
default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Material {
|
||||||
|
pub(crate) group: wgpu::BindGroup,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MaterialProperties {
|
||||||
|
pub(crate) edge: wgpu::AddressMode,
|
||||||
|
pub(crate) filter: wgpu::FilterMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MaterialProperties {
|
||||||
|
fn default() -> MaterialProperties {
|
||||||
|
MaterialProperties {
|
||||||
|
edge: wgpu::AddressMode::Repeat,
|
||||||
|
filter: wgpu::FilterMode::Linear,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaterialProperties {
|
||||||
|
pub(crate) fn sampler(&self, device: &wgpu::Device) -> wgpu::Sampler {
|
||||||
|
device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
address_mode_u: self.edge,
|
||||||
|
address_mode_v: self.edge,
|
||||||
|
address_mode_w: self.edge,
|
||||||
|
mag_filter: self.filter,
|
||||||
|
min_filter: self.filter,
|
||||||
|
mipmap_filter: wgpu::MipmapFilterMode::Nearest,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material {
|
||||||
|
pub(crate) fn _new(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
layout: &wgpu::BindGroupLayout,
|
||||||
|
base: &Texture,
|
||||||
|
normal: &Texture,
|
||||||
|
reflect: &Texture,
|
||||||
|
config: MaterialProperties,
|
||||||
|
) -> Material {
|
||||||
|
let sampler = config.sampler(&device);
|
||||||
|
let group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&base.view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 2,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&normal.view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 3,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&reflect.view),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: Some("diffuse_bind_group"),
|
||||||
|
});
|
||||||
|
Material { group }
|
||||||
|
}
|
||||||
|
pub fn new(renderer: &mut Renderer, base: &Texture, normal: &Texture, reflect: &Texture, config: MaterialProperties) -> Id<Material> {
|
||||||
|
renderer.instances.materials.list.make(Material::_new(&renderer.device, &renderer.instances.materials.layout, base, normal, reflect, config)).id
|
||||||
|
}
|
||||||
|
}
|
||||||
376
src/render/instance/mod.rs
Normal file
376
src/render/instance/mod.rs
Normal file
|
|
@ -0,0 +1,376 @@
|
||||||
|
pub mod material;
|
||||||
|
|
||||||
|
use std::ops::Range;
|
||||||
|
use bytemuck::{Pod, Zeroable};
|
||||||
|
use glam::{Affine3, Mat4, Vec4};
|
||||||
|
use wgpu::{BindGroup, Device};
|
||||||
|
use wgpu::naga::FastHashMap;
|
||||||
|
use crate::list::{FreeList, Id, RefId};
|
||||||
|
use crate::render::{instance, Renderer};
|
||||||
|
use crate::render::eye::Eye;
|
||||||
|
pub(crate) use crate::render::instance::material::{Material, Materials};
|
||||||
|
use crate::render::mesh::{Mesh, TangentVertex};
|
||||||
|
use crate::render::texture::Texture;
|
||||||
|
use crate::world::ObjectData;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Pod, Zeroable, Copy, Clone)]
|
||||||
|
pub struct ModelInstance {
|
||||||
|
pub transform: Mat4,
|
||||||
|
pub color: Vec4,
|
||||||
|
pub lights: [u16; 16],
|
||||||
|
pub point_lights: u32,
|
||||||
|
pub spot_lights: u32,
|
||||||
|
pub metal: f32,
|
||||||
|
pub rough: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Pod, Copy, Clone, Zeroable)]
|
||||||
|
pub struct LightInstance {
|
||||||
|
pub location: Vec4,
|
||||||
|
pub rotation: Vec4,
|
||||||
|
pub color: Vec4,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LightData {
|
||||||
|
pub instance: LightInstance,
|
||||||
|
pub transform: Affine3,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ModelData {
|
||||||
|
pub instance: ModelInstance,
|
||||||
|
pub material: Id<Material>,
|
||||||
|
pub mesh: Id<Mesh>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Instances {
|
||||||
|
pub materials: Materials,
|
||||||
|
light_instances: FreeList<LightData>,
|
||||||
|
model_instances: FreeList<ModelData>,
|
||||||
|
pub(crate) pipeline: wgpu::RenderPipeline,
|
||||||
|
light_count: usize,
|
||||||
|
light_buffer: wgpu::Buffer,
|
||||||
|
model_count: usize,
|
||||||
|
pub(crate) model_buffer: wgpu::Buffer,
|
||||||
|
models_flag: bool,
|
||||||
|
pub(crate) program: Program,
|
||||||
|
models: FastHashMap<Id<Mesh>, FastHashMap<Id<Material>, FastHashMap<Id<ObjectData>,bool>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Code {
|
||||||
|
Material(Material),
|
||||||
|
Mesh(Mesh),
|
||||||
|
Draw(Range<u32>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Program(pub Vec<Code>);
|
||||||
|
|
||||||
|
impl Program {
|
||||||
|
fn push(&mut self, item: Code) {
|
||||||
|
self.0.push(item)
|
||||||
|
}
|
||||||
|
fn new() -> Program {
|
||||||
|
Program(Vec::new())
|
||||||
|
}
|
||||||
|
pub(crate) fn render(self, pass: &mut wgpu::RenderPass) {
|
||||||
|
let mut count = 0;
|
||||||
|
let mut indexed = false;
|
||||||
|
for code in self.0 {
|
||||||
|
match code {
|
||||||
|
Code::Material(material) => pass.set_bind_group(
|
||||||
|
Renderer::SIMPLE_RENDER_TEXTURE_GROUP_POSITION,
|
||||||
|
&material.group,
|
||||||
|
&[],
|
||||||
|
),
|
||||||
|
Code::Mesh(Mesh {vertices, indices, ..}) => {
|
||||||
|
pass.set_vertex_buffer(0, vertices.0.slice(..));
|
||||||
|
if let Some(indices) = indices {
|
||||||
|
pass.set_index_buffer(indices.0.slice(..), wgpu::IndexFormat::Uint32);
|
||||||
|
count = indices.1;
|
||||||
|
indexed = true;
|
||||||
|
} else {
|
||||||
|
count = vertices.1;
|
||||||
|
indexed = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Code::Draw(instances) => {
|
||||||
|
if indexed {
|
||||||
|
pass.draw_indexed(0..count, 0, instances)
|
||||||
|
} else {
|
||||||
|
pass.draw(0..count, instances);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instances {
|
||||||
|
const MIN_SIZE: u64 = 64;
|
||||||
|
pub fn register_object(&mut self, mut object: RefId<ObjectData>) {
|
||||||
|
let model = object.model.as_mut().unwrap();
|
||||||
|
self.model_count += 1;
|
||||||
|
self.models
|
||||||
|
.entry(model.mesh.clone()).or_default()
|
||||||
|
.entry(model.material.clone()).or_default()
|
||||||
|
.insert(object.into(),self.models_flag);
|
||||||
|
}
|
||||||
|
/*pub fn register_light(&mut self, light: Id<LightData>) {
|
||||||
|
self.light_count += 1;
|
||||||
|
self.lights.insert(light);
|
||||||
|
}*/
|
||||||
|
pub fn reallocate_buffer(
|
||||||
|
device: &wgpu::Device,
|
||||||
|
buffer: &mut wgpu::Buffer,
|
||||||
|
count: usize,
|
||||||
|
item_size: usize,
|
||||||
|
) {
|
||||||
|
let size = buffer.size() / item_size as wgpu::BufferAddress;
|
||||||
|
if count > Instances::MIN_SIZE as usize {
|
||||||
|
let mut reallocate: Option<usize> = None;
|
||||||
|
if count < (size / 2) as usize {
|
||||||
|
reallocate = Some(count / 2);
|
||||||
|
} else if count > size as usize {
|
||||||
|
reallocate = Some(count * 2);
|
||||||
|
}
|
||||||
|
if let Some(new_size) = reallocate {
|
||||||
|
*buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Instance Buffer"),
|
||||||
|
size: (item_size * new_size) as wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::VERTEX,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*pub fn write_lights(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) {
|
||||||
|
//SimpleInstances::reallocate_buffer(device, queue, &mut self.light_ref_buffer, self.light_ref_last, size_of::<u32>());
|
||||||
|
/*
|
||||||
|
let mut light_ref_buffer = queue.write_buffer_with(
|
||||||
|
&self.light_ref_buffer,
|
||||||
|
0 as wgpu::BufferAddress,
|
||||||
|
wgpu::BufferSize::new(self.instance_buffer.size()).unwrap()
|
||||||
|
).unwrap();
|
||||||
|
*/
|
||||||
|
Instances::reallocate_buffer(
|
||||||
|
device,
|
||||||
|
&mut self.light_buffer,
|
||||||
|
self.light_count,
|
||||||
|
size_of::<WorldLight>(),
|
||||||
|
);
|
||||||
|
let mut buffer = queue
|
||||||
|
.write_buffer_with(
|
||||||
|
&self.light_buffer,
|
||||||
|
0 as wgpu::BufferAddress,
|
||||||
|
wgpu::BufferSize::new(self.light_buffer.size()).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let stride = size_of::<WorldLight>();
|
||||||
|
for (new_index, (light, index)) in self.lights.iter_mut().enumerate() {
|
||||||
|
*index = new_index + 1;
|
||||||
|
let begin = *index * stride;
|
||||||
|
buffer
|
||||||
|
.slice(begin..begin + stride)
|
||||||
|
.copy_from_slice(bytemuck::cast_slice(&[light.0.borrow().instance]));
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
pub(crate) fn write_instances(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
mesh_list: &mut FreeList<Mesh>,
|
||||||
|
objects_list: &mut FreeList<ObjectData>,
|
||||||
|
) {
|
||||||
|
Instances::reallocate_buffer(
|
||||||
|
device,
|
||||||
|
&mut self.model_buffer,
|
||||||
|
self.model_count,
|
||||||
|
size_of::<ModelInstance>(),
|
||||||
|
);
|
||||||
|
let mut buffer = queue
|
||||||
|
.write_buffer_with(
|
||||||
|
&self.model_buffer,
|
||||||
|
0 as wgpu::BufferAddress,
|
||||||
|
wgpu::BufferSize::new(self.model_buffer.size()).unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let mut index: u32 = 0;
|
||||||
|
let stride = size_of::<ModelInstance>();
|
||||||
|
for (mesh, materials) in self.models.iter_mut() {
|
||||||
|
self.program.push(Code::Mesh(mesh_list.get(mesh).clone()));
|
||||||
|
for (material, objects) in materials.iter_mut() {
|
||||||
|
self.program.push(Code::Material(self.materials.list.get(material).clone()));
|
||||||
|
let before = index;
|
||||||
|
objects.retain(|object,flag| {
|
||||||
|
let object = objects_list.get(object);
|
||||||
|
if *flag == self.models_flag {
|
||||||
|
let begin = index as usize * stride;
|
||||||
|
buffer.slice(begin..begin + stride).copy_from_slice(bytemuck::cast_slice(&[object.model.as_mut().unwrap().instance]));
|
||||||
|
index += 1;
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.program.push(Code::Draw(before..index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//println!("objects: {}",index);
|
||||||
|
self.models_flag = !self.models_flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: size_of::<ModelInstance>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Instance,
|
||||||
|
attributes: &[
|
||||||
|
// todo: is this too big?
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: 0,
|
||||||
|
shader_location: 4,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 4]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 5,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 8]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 6,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 12]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 7,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 8,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 9,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 10,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 16]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 11,
|
||||||
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(device: &wgpu::Device, eye: &Eye, config: &wgpu::SurfaceConfiguration, default: Texture) -> Instances {
|
||||||
|
let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Instance Buffer"),
|
||||||
|
size: (size_of::<ModelInstance>() * Instances::MIN_SIZE as usize)
|
||||||
|
as wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
let light_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Light Buffer"),
|
||||||
|
size: (size_of::<LightInstance>() * Instances::MIN_SIZE as usize)
|
||||||
|
as wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
/*let light_ref_buffer = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Light Ref Buffer"),
|
||||||
|
size: (size_of::<u32>() * SimpleInstances::MIN_SIZE as usize) as wgpu::BufferAddress,
|
||||||
|
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
});*/
|
||||||
|
|
||||||
|
let materials = Materials::new(device, default);
|
||||||
|
|
||||||
|
let shader = device.create_shader_module(wgpu::include_wgsl!("instance.wgsl"));
|
||||||
|
|
||||||
|
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||||
|
label: Some("Render Pipeline Layout"),
|
||||||
|
bind_group_layouts: &[Some(&eye.layout), Some(&materials.layout)],
|
||||||
|
immediate_size: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
let instance_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||||
|
label: Some("Render Pipeline"),
|
||||||
|
layout: Some(&pipeline_layout),
|
||||||
|
vertex: wgpu::VertexState {
|
||||||
|
module: &shader,
|
||||||
|
entry_point: Some("vs_main"),
|
||||||
|
buffers: &[TangentVertex::desc(), Instances::desc()],
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
},
|
||||||
|
fragment: Some(wgpu::FragmentState {
|
||||||
|
// 3.
|
||||||
|
module: &shader,
|
||||||
|
entry_point: Some("fs_main"),
|
||||||
|
targets: &[Some(wgpu::ColorTargetState {
|
||||||
|
// 4.
|
||||||
|
format: config.format,
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||||
|
}),
|
||||||
|
primitive: Default::default(), /*wgpu::PrimitiveState {
|
||||||
|
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||||
|
strip_index_format: None,
|
||||||
|
front_face: wgpu::FrontFace::Ccw,
|
||||||
|
cull_mode: None,
|
||||||
|
polygon_mode: wgpu::PolygonMode::Fill,
|
||||||
|
unclipped_depth: false,
|
||||||
|
conservative: false,
|
||||||
|
}*/
|
||||||
|
depth_stencil: Some(wgpu::DepthStencilState {
|
||||||
|
stencil: wgpu::StencilState::default(),
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
depth_write_enabled: Some(true),
|
||||||
|
depth_compare: Some(wgpu::CompareFunction::Less),
|
||||||
|
bias: wgpu::DepthBiasState::default(),
|
||||||
|
}),
|
||||||
|
multisample: wgpu::MultisampleState {
|
||||||
|
count: 1,
|
||||||
|
mask: !0,
|
||||||
|
alpha_to_coverage_enabled: false,
|
||||||
|
},
|
||||||
|
multiview_mask: None,
|
||||||
|
cache: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
Instances {
|
||||||
|
materials,
|
||||||
|
light_instances: Default::default(),
|
||||||
|
model_instances: Default::default(),
|
||||||
|
pipeline: instance_pipeline,
|
||||||
|
light_count: 0,
|
||||||
|
//light_ref_last: 0,
|
||||||
|
model_count: 0,
|
||||||
|
light_buffer,
|
||||||
|
model_buffer: instance_buffer,
|
||||||
|
//light_ref_buffer,
|
||||||
|
//lights: Default::default(),
|
||||||
|
models_flag: false,
|
||||||
|
program: Program(Vec::new()),
|
||||||
|
models: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
302
src/render/loader.rs
Normal file
302
src/render/loader.rs
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use glam::{Mat4, Vec3, Vec4};
|
||||||
|
use gltf::Semantic;
|
||||||
|
use wgpu::{Device, Queue};
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
use crate::list::{FreeList, Id, RefId};
|
||||||
|
use crate::render::instance::{Material, Materials, ModelData, ModelInstance};
|
||||||
|
use crate::render::instance::material::MaterialProperties;
|
||||||
|
use crate::render::mesh::{Mesh, TangentVertex};
|
||||||
|
use crate::render::texture::{Texture, TextureProperties};
|
||||||
|
use crate::render::{Renderer, Textures};
|
||||||
|
use crate::world::{ObjectData, AABB, ColliderData, Shape};
|
||||||
|
|
||||||
|
pub struct TreeNode {
|
||||||
|
object: Option<ObjectData>,
|
||||||
|
children: Vec<TreeNode>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeNode {
|
||||||
|
pub fn first_object(&self) -> Option<ObjectData> {
|
||||||
|
if let Some(object) = self.object.clone() {
|
||||||
|
Some(object)
|
||||||
|
} else {
|
||||||
|
for child in self.children.iter() {
|
||||||
|
if let Some(object) = child.first_object() {
|
||||||
|
return Some(object);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type GltfVertexBufferKey = (Option<usize>, Option<usize>, Option<usize>, Option<usize>);
|
||||||
|
|
||||||
|
pub fn new_texture_from_gltf(
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
info: &gltf::texture::Texture,
|
||||||
|
images: &Vec<gltf::image::Data>,
|
||||||
|
) -> Option<Texture> {
|
||||||
|
if let Some(image) = images.get(info.source().index()) {
|
||||||
|
let mut new_pixels = Vec::new();
|
||||||
|
let pixels: &Vec<u8>;
|
||||||
|
match image.format {
|
||||||
|
gltf::image::Format::R8G8B8 => {
|
||||||
|
for pixel in image.pixels.chunks(3) {
|
||||||
|
new_pixels.push(pixel[0]);
|
||||||
|
new_pixels.push(pixel[0]);
|
||||||
|
new_pixels.push(pixel[0]);
|
||||||
|
new_pixels.push(255);
|
||||||
|
}
|
||||||
|
pixels = &new_pixels;
|
||||||
|
}
|
||||||
|
gltf::image::Format::R8G8B8A8 => pixels = &image.pixels,
|
||||||
|
_ => return None,
|
||||||
|
}
|
||||||
|
Some(Texture::load(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
pixels.as_slice(),
|
||||||
|
TextureProperties {
|
||||||
|
width: image.width,
|
||||||
|
height: image.height,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new_mesh_from_gltf(
|
||||||
|
device: &Device,
|
||||||
|
primitive: gltf::Primitive,
|
||||||
|
meshes: &mut HashMap<GltfVertexBufferKey, Mesh>,
|
||||||
|
buffers: &[gltf::buffer::Data],
|
||||||
|
) -> Mesh {
|
||||||
|
let position_index = primitive
|
||||||
|
.get(&Semantic::Positions)
|
||||||
|
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||||
|
let normals_index = primitive
|
||||||
|
.get(&Semantic::Normals)
|
||||||
|
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||||
|
let tex_coords_index = primitive
|
||||||
|
.get(&Semantic::TexCoords(0))
|
||||||
|
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||||
|
let indices_index = primitive
|
||||||
|
.indices()
|
||||||
|
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||||
|
let tangent_index = primitive
|
||||||
|
.get(&Semantic::Tangents)
|
||||||
|
.and_then(|it| it.view()).map(|it| it.buffer().index());
|
||||||
|
let key = (
|
||||||
|
position_index,
|
||||||
|
normals_index,
|
||||||
|
tex_coords_index,
|
||||||
|
indices_index,
|
||||||
|
);
|
||||||
|
let insert = || {
|
||||||
|
let mut tangents = false;
|
||||||
|
let reader = primitive.reader(|buffer| Some(&buffers[buffer.index()]));
|
||||||
|
let mut vertex_data: Vec<TangentVertex>;
|
||||||
|
let mut aabb = AABB(Vec3::ZERO,Vec3::ZERO);
|
||||||
|
if let Some(positions) = reader.read_positions() {
|
||||||
|
vertex_data = Vec::with_capacity(positions.len());
|
||||||
|
let mut normal = reader.read_normals().map(|it| it.into_iter());
|
||||||
|
let mut tex_coord = reader
|
||||||
|
.read_tex_coords(0)
|
||||||
|
.map(|it| it.into_f32().into_iter());
|
||||||
|
let mut tangent = reader.read_tangents().map(|it| it.into_iter());
|
||||||
|
tangents = tangent.is_some();
|
||||||
|
for position in positions {
|
||||||
|
aabb = aabb.extend_to(Vec3::from(position));
|
||||||
|
vertex_data.push(TangentVertex {
|
||||||
|
position,
|
||||||
|
normal: if let Some(ref mut normals) = normal {
|
||||||
|
if let Some(normal) = normals.next() {
|
||||||
|
normal
|
||||||
|
} else {
|
||||||
|
[0.0; 3]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
[0.0; 3]
|
||||||
|
},
|
||||||
|
tex_coord: if let Some(ref mut tex_coords) = tex_coord {
|
||||||
|
tex_coords.next()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
.unwrap_or([0.0; 2]),
|
||||||
|
tangent: [0.0; 3], // todo: tangent from model
|
||||||
|
bitangent: [0.0; 3],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
vertex_data = Vec::new();
|
||||||
|
}
|
||||||
|
let vertex_buffer =
|
||||||
|
device
|
||||||
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Vertex Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(vertex_data.as_slice()),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
|
});
|
||||||
|
let vertices_result = (vertex_buffer, vertex_data.len() as u32);
|
||||||
|
let indices_result = if let Some(indices) = reader.read_indices() {
|
||||||
|
let index_data: Vec<u32> = indices.into_u32().collect();
|
||||||
|
Some((
|
||||||
|
device
|
||||||
|
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Index Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(index_data.as_slice()),
|
||||||
|
usage: wgpu::BufferUsages::INDEX,
|
||||||
|
}),
|
||||||
|
index_data.len() as u32,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
Mesh {
|
||||||
|
indices: indices_result,
|
||||||
|
vertices: vertices_result,
|
||||||
|
aabb,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
meshes.entry(key).or_insert_with(insert).clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_node_from_gltf(
|
||||||
|
node: gltf::Node,
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
mesh_map: &mut HashMap<GltfVertexBufferKey, Mesh>,
|
||||||
|
texture_map: &mut HashMap<usize, Texture>,
|
||||||
|
materials: &mut Materials,
|
||||||
|
textures: &Textures,
|
||||||
|
meshes: &mut FreeList<Mesh>,
|
||||||
|
images: &Vec<gltf::image::Data>,
|
||||||
|
buffers: &Vec<gltf::buffer::Data>,
|
||||||
|
) -> TreeNode {
|
||||||
|
let mut tree_node = TreeNode {
|
||||||
|
object: None,
|
||||||
|
children: Vec::new(),
|
||||||
|
name: node.name().unwrap_or("Node").to_string(),
|
||||||
|
};
|
||||||
|
if let Some(mesh) = node.mesh() {
|
||||||
|
let len = mesh.primitives().len();
|
||||||
|
for primitive in mesh.primitives() {
|
||||||
|
let pbr = primitive.material().pbr_metallic_roughness();
|
||||||
|
let material = primitive.material();
|
||||||
|
let color = pbr.base_color_factor();
|
||||||
|
let metal = pbr.metallic_factor();
|
||||||
|
let rough = pbr.roughness_factor();
|
||||||
|
let light = primitive.material().emissive_factor();
|
||||||
|
let base = match pbr.base_color_texture() {
|
||||||
|
Some(info) => texture_map
|
||||||
|
.entry(info.texture().source().index())
|
||||||
|
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
|
||||||
|
.clone(),
|
||||||
|
None => textures.default.clone(),
|
||||||
|
};
|
||||||
|
let reflect = match pbr.metallic_roughness_texture() {
|
||||||
|
Some(info) => texture_map
|
||||||
|
.entry(info.texture().source().index())
|
||||||
|
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
|
||||||
|
.clone(),
|
||||||
|
None => textures.default.clone(),
|
||||||
|
};
|
||||||
|
let normal = match material.normal_texture() {
|
||||||
|
Some(info) => texture_map
|
||||||
|
.entry(info.texture().source().index())
|
||||||
|
.or_insert_with(|| new_texture_from_gltf(device,queue,&info.texture(), &images).unwrap_or_else(|| textures.default.clone()))
|
||||||
|
.clone(),
|
||||||
|
None => textures.default.clone(),
|
||||||
|
};
|
||||||
|
let material = materials.list.make(Material::_new(
|
||||||
|
&device,
|
||||||
|
&materials.layout,
|
||||||
|
&base,
|
||||||
|
&normal,
|
||||||
|
&reflect,
|
||||||
|
MaterialProperties {
|
||||||
|
edge: wgpu::AddressMode::Repeat,
|
||||||
|
filter: wgpu::FilterMode::Linear,
|
||||||
|
},
|
||||||
|
)).into();;
|
||||||
|
let mesh = new_mesh_from_gltf(device, primitive, mesh_map, buffers);
|
||||||
|
let aabb = mesh.aabb;
|
||||||
|
let object = ObjectData {
|
||||||
|
model: Some(ModelData {
|
||||||
|
instance: ModelInstance {
|
||||||
|
transform: Mat4::default(),
|
||||||
|
color: Vec4::from_array(color),
|
||||||
|
lights: [0; 16],
|
||||||
|
point_lights: 0,
|
||||||
|
spot_lights: 0,
|
||||||
|
metal,
|
||||||
|
rough,
|
||||||
|
},
|
||||||
|
material,
|
||||||
|
mesh: meshes.make(mesh).id,
|
||||||
|
}),
|
||||||
|
collider: ColliderData { shape: Shape::Sphere(aabb.1.distance(aabb.0)) },
|
||||||
|
affine: Default::default(),
|
||||||
|
asleep: false,
|
||||||
|
};
|
||||||
|
if len == 1 {
|
||||||
|
tree_node.object = Some(object);
|
||||||
|
} else {
|
||||||
|
tree_node.children.push(TreeNode {
|
||||||
|
object: Some(object),
|
||||||
|
children: Vec::new(),
|
||||||
|
name: "Primitive".to_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for node in node.children() {
|
||||||
|
tree_node
|
||||||
|
.children
|
||||||
|
.push(load_node_from_gltf(node, device, queue, mesh_map, texture_map, materials, textures, meshes, images, buffers));
|
||||||
|
}
|
||||||
|
tree_node
|
||||||
|
}
|
||||||
|
pub fn _load_from_gltf(device: &Device, queue: &Queue, meshes: &mut FreeList<Mesh>, materials: &mut Materials, textures: &mut Textures, slice: impl AsRef<[u8]>) -> TreeNode {
|
||||||
|
let mut root = TreeNode {
|
||||||
|
object: None,
|
||||||
|
children: Vec::new(),
|
||||||
|
name: "Root".to_string(),
|
||||||
|
};
|
||||||
|
if let Ok((document, buffers, images)) = gltf::import_slice(slice) {
|
||||||
|
let mut mesh_map: HashMap<GltfVertexBufferKey, Mesh> = HashMap::new();
|
||||||
|
let mut texture_map: HashMap<usize, Texture> = HashMap::new();
|
||||||
|
for scene in document.scenes() {
|
||||||
|
let mut scene_node = TreeNode {
|
||||||
|
object: None,
|
||||||
|
children: Vec::new(),
|
||||||
|
name: scene.name().unwrap_or("Scene").to_string(),
|
||||||
|
};
|
||||||
|
for node in scene.nodes() {
|
||||||
|
scene_node.children.push(load_node_from_gltf(
|
||||||
|
node,
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
&mut mesh_map,
|
||||||
|
&mut texture_map,
|
||||||
|
materials,
|
||||||
|
textures,
|
||||||
|
meshes,
|
||||||
|
&images,
|
||||||
|
&buffers,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
root.children.push(scene_node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
root
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_from_gltf(renderer: &mut Renderer, slice: impl AsRef<[u8]>) -> TreeNode {
|
||||||
|
_load_from_gltf(&renderer.device, &renderer.queue, &mut renderer.meshes, &mut renderer.instances.materials, &mut renderer.textures, slice)
|
||||||
|
}
|
||||||
51
src/render/mesh.rs
Normal file
51
src/render/mesh.rs
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
use crate::world::AABB;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct TangentVertex {
|
||||||
|
pub(crate) position: [f32; 3],
|
||||||
|
pub(crate) normal: [f32; 3],
|
||||||
|
pub(crate) tex_coord: [f32; 2],
|
||||||
|
pub(crate) tangent: [f32; 3],
|
||||||
|
pub(crate) bitangent: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct Vertex {
|
||||||
|
position: [f32; 3],
|
||||||
|
normal: [f32; 3],
|
||||||
|
tex_coord: [f32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TangentVertex {
|
||||||
|
pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
|
||||||
|
wgpu::VertexBufferLayout {
|
||||||
|
array_stride: size_of::<TangentVertex>() as wgpu::BufferAddress,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &[
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: 0,
|
||||||
|
shader_location: 0,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 1,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: size_of::<[f32; 6]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 2,
|
||||||
|
format: wgpu::VertexFormat::Float32x2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub struct Mesh {
|
||||||
|
pub(crate) indices: Option<(wgpu::Buffer, u32)>,
|
||||||
|
pub(crate) vertices: (wgpu::Buffer, u32),
|
||||||
|
pub(crate) aabb: AABB,
|
||||||
|
}
|
||||||
1044
src/render/mod.rs
1044
src/render/mod.rs
File diff suppressed because it is too large
Load diff
72
src/render/sky.wgsl
Normal file
72
src/render/sky.wgsl
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
struct Environment {
|
||||||
|
ambient: vec4<f32>,
|
||||||
|
light: vec4<f32>,
|
||||||
|
dir: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Eye {
|
||||||
|
// from camera to screen
|
||||||
|
proj: mat4x4<f32>,
|
||||||
|
// from screen to camera
|
||||||
|
inv: mat4x4<f32>,
|
||||||
|
// world to camera
|
||||||
|
view: mat4x4<f32>,
|
||||||
|
// camera transform
|
||||||
|
frame: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var<uniform> eye: Eye;
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var<uniform> environment: Environment;
|
||||||
|
@group(0) @binding(2)
|
||||||
|
var sky_sampler: sampler;
|
||||||
|
@group(0) @binding(3)
|
||||||
|
var sky_texture: texture_2d<f32>;
|
||||||
|
|
||||||
|
// written on the 60th sleepless hour
|
||||||
|
fn sky_aspect(look: vec3<f32>) -> vec4<f32> {
|
||||||
|
var pi = 3.14159;
|
||||||
|
let u_angle = atan2(look.x,look.z);
|
||||||
|
let u = (u_angle/pi) * 0.5 + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||||
|
let v_angle = atan2(-look.y,sqrt(look.x * look.x + look.z * look.z));
|
||||||
|
let v = (v_angle/pi) - 0.5;//(v_angle/pi) + 0.5; // from -pi/2 -> pi/2 into 0 -> 1
|
||||||
|
let uv = vec2<f32>(u,v); // Get UV on skybox
|
||||||
|
return textureSample(sky_texture, sky_sampler, uv);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SkyOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) pos: vec4<f32> // unadulterated by WGSL
|
||||||
|
}
|
||||||
|
|
||||||
|
const VERTICES = array(
|
||||||
|
vec4(-1.0, -1.0, 1.0, 1.0),
|
||||||
|
vec4(-1.0, 1.0, 1.0, 1.0),
|
||||||
|
vec4( 1.0, -1.0, 1.0, 1.0),
|
||||||
|
vec4( 1.0, 1.0, 1.0, 1.0),
|
||||||
|
vec4(-1.0, 1.0, 1.0, 1.0),
|
||||||
|
vec4( 1.0, -1.0, 1.0, 1.0),
|
||||||
|
);
|
||||||
|
|
||||||
|
fn rotation(it: mat4x4<f32>) -> mat3x3<f32> {
|
||||||
|
return mat3x3<f32>(
|
||||||
|
it[0].xyz,
|
||||||
|
it[1].xyz,
|
||||||
|
it[2].xyz,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_sky(@builtin(vertex_index) index: u32) -> SkyOutput {
|
||||||
|
var out: SkyOutput;
|
||||||
|
out.position = VERTICES[index];
|
||||||
|
out.pos = out.position;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_sky(in: SkyOutput) -> @location(0) vec4<f32> {
|
||||||
|
let look = rotation(eye.frame) * in.pos.xyz;
|
||||||
|
return sky_aspect(look);
|
||||||
|
}
|
||||||
115
src/render/texture.rs
Normal file
115
src/render/texture.rs
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
use image::EncodableLayout;
|
||||||
|
use wgpu::{Device, Queue};
|
||||||
|
use crate::render::Renderer;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
|
pub struct Texture {
|
||||||
|
texture: wgpu::Texture,
|
||||||
|
pub(crate) view: wgpu::TextureView,
|
||||||
|
}
|
||||||
|
pub struct TextureProperties {
|
||||||
|
pub(crate) width: u32,
|
||||||
|
pub(crate) height: u32,
|
||||||
|
}
|
||||||
|
// todo: use texture compression!
|
||||||
|
impl Texture {
|
||||||
|
pub fn depth(config: &wgpu::SurfaceConfiguration, device: &Device) -> Texture {
|
||||||
|
let size = wgpu::Extent3d {
|
||||||
|
// 2.
|
||||||
|
width: config.width.max(1),
|
||||||
|
height: config.height.max(1),
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
};
|
||||||
|
let desc = wgpu::TextureDescriptor {
|
||||||
|
label: Some("depth texture"),
|
||||||
|
size,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Depth32Float,
|
||||||
|
usage: wgpu::TextureUsages::RENDER_ATTACHMENT // 3.
|
||||||
|
| wgpu::TextureUsages::TEXTURE_BINDING,
|
||||||
|
view_formats: &[],
|
||||||
|
};
|
||||||
|
let _depth_texture = device.create_texture(&desc);
|
||||||
|
let depth_view = _depth_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
Texture {
|
||||||
|
texture: _depth_texture,
|
||||||
|
view: depth_view,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn load(
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
slice: impl AsRef<[u8]>,
|
||||||
|
properties: TextureProperties,
|
||||||
|
) -> Texture {
|
||||||
|
let size = wgpu::Extent3d {
|
||||||
|
width: properties.width,
|
||||||
|
height: properties.height,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
};
|
||||||
|
let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
size,
|
||||||
|
mip_level_count: 1,
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
label: Some("texture"),
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let diffuse_texture_view =
|
||||||
|
diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
queue.write_texture(
|
||||||
|
wgpu::TexelCopyTextureInfo {
|
||||||
|
texture: &diffuse_texture,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
},
|
||||||
|
slice.as_ref(),
|
||||||
|
wgpu::TexelCopyBufferLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(4 * size.width),
|
||||||
|
rows_per_image: Some(size.height),
|
||||||
|
},
|
||||||
|
size,
|
||||||
|
);
|
||||||
|
Texture {
|
||||||
|
texture: diffuse_texture,
|
||||||
|
view: diffuse_texture_view,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn _load_from_file_bytes(
|
||||||
|
device: &Device,
|
||||||
|
queue: &Queue,
|
||||||
|
slice: impl AsRef<[u8]>,
|
||||||
|
format: Option<image::ImageFormat>,
|
||||||
|
) -> Option<Texture> {
|
||||||
|
let image = if let Some(format) = format {
|
||||||
|
image::load_from_memory_with_format(slice.as_ref(), format)
|
||||||
|
} else {
|
||||||
|
image::load_from_memory(slice.as_ref())
|
||||||
|
};
|
||||||
|
if let Ok(data) = image {
|
||||||
|
let data = data.into_rgba8();
|
||||||
|
Some(Texture::load(
|
||||||
|
device,
|
||||||
|
queue,
|
||||||
|
data.as_bytes(),
|
||||||
|
TextureProperties {
|
||||||
|
width: data.width(),
|
||||||
|
height: data.height(),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
println!("failed to load texture! error: {:?}", image.unwrap_err());
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_from_file_bytes(renderer: &mut Renderer, slice: impl AsRef<[u8]>, format: Option<image::ImageFormat>) -> Option<Texture> {
|
||||||
|
Texture::_load_from_file_bytes(&renderer.device,&renderer.queue,slice,format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ use crate::{render, world};
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub objects: FreeList<world::ObjectData>,
|
pub objects: FreeList<world::ObjectData>,
|
||||||
pub lights: FreeList<render::LightData>,
|
pub lights: FreeList<render::instance::LightData>,
|
||||||
pub worlds: FreeList<world::World>,
|
pub worlds: FreeList<world::World>,
|
||||||
pub renderer: Option<render::Renderer>,
|
pub renderer: Option<render::Renderer>,
|
||||||
pub screens: FreeList<render::Screen>
|
pub screens: FreeList<render::Screen>
|
||||||
|
|
|
||||||
100
src/world/debug.rs
Normal file
100
src/world/debug.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
||||||
|
use glam::{IVec3, Mat4, Quat, Vec3};
|
||||||
|
use wgpu::naga::FastHashMap;
|
||||||
|
use crate::list::{FreeList, Id};
|
||||||
|
use crate::render;
|
||||||
|
use crate::world::{ColliderData, ObjectData, OctreeBlock, Shape, World};
|
||||||
|
|
||||||
|
pub struct OctreeDebug {
|
||||||
|
map: FastHashMap<IVec3, Id<ObjectData>>
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum OctreeDebugOperation {
|
||||||
|
Add(IVec3,i32),
|
||||||
|
Remove(IVec3),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for OctreeDebug {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OctreeDebug {
|
||||||
|
pub fn new() -> OctreeDebug {
|
||||||
|
OctreeDebug {
|
||||||
|
map: FastHashMap::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn set_block(
|
||||||
|
&mut self,
|
||||||
|
blocks: &mut FreeList<OctreeBlock>,
|
||||||
|
block: Id<OctreeBlock>,
|
||||||
|
objects: &mut FreeList<ObjectData>,
|
||||||
|
debug: &Option<render::instance::ModelData>,
|
||||||
|
pos: IVec3,
|
||||||
|
mut size: i32
|
||||||
|
) {
|
||||||
|
let entry = self.map.get(&pos);
|
||||||
|
if let Some(debug) = debug {
|
||||||
|
if entry.is_none() {
|
||||||
|
let mut model = debug.clone();
|
||||||
|
model.instance.transform = Mat4::from_scale_rotation_translation(Vec3::splat(size as f32),Quat::IDENTITY,pos.as_vec3());
|
||||||
|
self.map.insert(pos, objects.make(ObjectData {
|
||||||
|
model: Some(model),
|
||||||
|
collider: ColliderData {
|
||||||
|
shape: Shape::Sphere(size as f32),
|
||||||
|
},
|
||||||
|
affine: Default::default(),
|
||||||
|
asleep: false,
|
||||||
|
}).id);
|
||||||
|
println!("pos {} size {}",pos,size)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if entry.is_some() {
|
||||||
|
objects.remove(self.map.remove(&pos).unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
size /= 2;
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[0].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug, pos - (IVec3::new(-1, -1, -1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[1].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,-1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[2].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,-1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[3].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,-1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[4].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,-1,1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[5].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,-1,1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[6].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(-1,1,1) * size / 2), size);
|
||||||
|
}
|
||||||
|
if let Some(block) = blocks.get(&block).blocks[7].exists() {
|
||||||
|
self.set_block(blocks,block,objects,debug,pos - (IVec3::new(1,1,1) * size / 2), size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn set(&mut self, objects: &mut FreeList<ObjectData>, world: &mut World, debug: Option<render::instance::ModelData>) {
|
||||||
|
for (index,block) in world.matter.map.iter_mut() {
|
||||||
|
self.set_block(
|
||||||
|
&mut world.matter.blocks,
|
||||||
|
block.clone(),
|
||||||
|
objects,
|
||||||
|
&debug,
|
||||||
|
index * OctreeBlock::CHUNK_SIZE,
|
||||||
|
OctreeBlock::CHUNK_SIZE,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn register(&mut self, objects: &mut FreeList<ObjectData>, renderer: &mut render::Renderer) {
|
||||||
|
for (pos,block) in self.map.iter() {
|
||||||
|
renderer.instances.register_object(objects.get_ref(block.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
138
src/world/mod.rs
138
src/world/mod.rs
|
|
@ -1,3 +1,6 @@
|
||||||
|
pub mod debug;
|
||||||
|
|
||||||
|
use crate::render::instance::ModelData;
|
||||||
use glam::{Affine3, IVec3, Mat4, Quat, Vec3};
|
use glam::{Affine3, IVec3, Mat4, Quat, Vec3};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
||||||
|
|
@ -43,15 +46,22 @@ impl ColliderData {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ObjectData {
|
pub struct ObjectData {
|
||||||
pub model: Option<render::ModelData>,
|
pub model: Option<render::instance::ModelData>,
|
||||||
pub collider: ColliderData,
|
pub collider: ColliderData,
|
||||||
pub affine: Affine3,
|
pub affine: Affine3,
|
||||||
pub asleep: bool,
|
pub asleep: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ObjectData {
|
||||||
|
pub fn set_affine(&mut self, affine: Affine3) {
|
||||||
|
self.affine = affine;
|
||||||
|
self.asleep = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
pub enum InterestType {
|
pub enum InterestType {
|
||||||
Light(Id<render::LightData>),
|
Light(Id<render::instance::LightData>),
|
||||||
Object(Id<ObjectData>)
|
Object(Id<ObjectData>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -119,100 +129,6 @@ impl Default for World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OctreeDebug {
|
|
||||||
map: FastHashMap<IVec3, Id<ObjectData>>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum OctreeDebugOperation {
|
|
||||||
Add(IVec3,i32),
|
|
||||||
Remove(IVec3),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for OctreeDebug {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OctreeDebug {
|
|
||||||
pub fn new() -> OctreeDebug {
|
|
||||||
OctreeDebug {
|
|
||||||
map: FastHashMap::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn set_block(
|
|
||||||
&mut self,
|
|
||||||
blocks: &mut FreeList<OctreeBlock>,
|
|
||||||
block: Id<OctreeBlock>,
|
|
||||||
objects: &mut FreeList<ObjectData>,
|
|
||||||
debug: &Option<render::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)
|
|
||||||
}
|
|
||||||
} 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 {
|
impl World {
|
||||||
pub fn new() -> World {
|
pub fn new() -> World {
|
||||||
World {
|
World {
|
||||||
|
|
@ -228,15 +144,15 @@ impl World {
|
||||||
object.id
|
object.id
|
||||||
}
|
}
|
||||||
pub fn remove_object(&mut self, object: Id<ObjectData>) {
|
pub fn remove_object(&mut self, object: Id<ObjectData>) {
|
||||||
|
|
||||||
}
|
}
|
||||||
pub fn step(
|
pub fn step(
|
||||||
&mut self,
|
&mut self,
|
||||||
maybe_renderer: &mut Option<render::Renderer>,
|
maybe_renderer: &mut Option<render::Renderer>,
|
||||||
objects: &mut FreeList<ObjectData>,
|
objects: &mut FreeList<ObjectData>,
|
||||||
_lights: &mut FreeList<render::LightData>,
|
_lights: &mut FreeList<render::instance::LightData>,
|
||||||
) {
|
) {
|
||||||
/*fn reinsert(
|
fn reinsert(
|
||||||
block_id: &Id<OctreeBlock>,
|
block_id: &Id<OctreeBlock>,
|
||||||
blocks: &mut FreeList<OctreeBlock>,
|
blocks: &mut FreeList<OctreeBlock>,
|
||||||
interests: &mut FreeList<InterestNode>,
|
interests: &mut FreeList<InterestNode>,
|
||||||
|
|
@ -247,10 +163,6 @@ impl World {
|
||||||
let interest = interests.get(&interest_id);
|
let interest = interests.get(&interest_id);
|
||||||
match interest.interest.it {
|
match interest.interest.it {
|
||||||
InterestType::Object(ref object_id) => {
|
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)
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
@ -263,8 +175,8 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (_pos,id) in self.matter.map.iter() {
|
for (_pos,id) in self.matter.map.iter() {
|
||||||
reinsert(id, &mut self.matter.blocks, &mut self.matter.interests, objects)
|
reinsert(id, &mut self.matter.blocks, &mut self.matter.nodes, objects)
|
||||||
}*/
|
}
|
||||||
if let Some(renderer) = maybe_renderer {
|
if let Some(renderer) = maybe_renderer {
|
||||||
fn consider(
|
fn consider(
|
||||||
block_id: &Id<OctreeBlock>,
|
block_id: &Id<OctreeBlock>,
|
||||||
|
|
@ -294,7 +206,7 @@ impl World {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (_pos,id) in self.matter.map.iter() {
|
for (_pos,id) in self.matter.map.iter() {
|
||||||
consider(id, renderer, &mut self.matter.blocks, &mut self.matter.interests, objects)
|
consider(id, renderer, &mut self.matter.blocks, &mut self.matter.nodes, objects)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -340,14 +252,14 @@ struct OctreeBlock {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OctreeBlock {
|
impl OctreeBlock {
|
||||||
fn push(&mut self, interests: &mut FreeList<InterestNode>, interest: Interest) {
|
fn push_interest(&mut self, interests: &mut FreeList<InterestNode>, interest: Interest) {
|
||||||
self.first = interests.make(InterestNode {
|
self.first = interests.make(InterestNode {
|
||||||
interest: interest.clone(),
|
interest: interest.clone(),
|
||||||
next: self.first.clone(),
|
next: self.first.clone(),
|
||||||
}).id.maybe();
|
}).id.maybe();
|
||||||
self.interests += 1;
|
self.interests += 1;
|
||||||
}
|
}
|
||||||
fn remove(&mut self, interests: &mut FreeList<InterestNode>, needle: Interest) {
|
fn remove_interest(&mut self, interests: &mut FreeList<InterestNode>, needle: Interest) { // todo: needed?
|
||||||
let mut maybe_last = MaybeId::NULL;
|
let mut maybe_last = MaybeId::NULL;
|
||||||
let mut maybe_this = self.first.clone();
|
let mut maybe_this = self.first.clone();
|
||||||
while let Some(this) = maybe_this.exists() {
|
while let Some(this) = maybe_this.exists() {
|
||||||
|
|
@ -376,7 +288,7 @@ impl OctreeBlock {
|
||||||
size: i32,
|
size: i32,
|
||||||
) {
|
) {
|
||||||
if pos == IVec3::ZERO || blocks.get(&it).interests < OctreeBlock::MAX_IDEAL_INTEREST as u32 {
|
if pos == IVec3::ZERO || blocks.get(&it).interests < OctreeBlock::MAX_IDEAL_INTEREST as u32 {
|
||||||
blocks.get(&it).push(interests,interest)
|
blocks.get(&it).push_interest(interests, interest)
|
||||||
} else {
|
} else {
|
||||||
let mut index = 0;
|
let mut index = 0;
|
||||||
let x = if pos.x > 0 {
|
let x = if pos.x > 0 {
|
||||||
|
|
@ -413,8 +325,9 @@ impl OctreeBlock {
|
||||||
|
|
||||||
type Blocks = [MaybeId<OctreeBlock>; 8];
|
type Blocks = [MaybeId<OctreeBlock>; 8];
|
||||||
|
|
||||||
struct OctreeMap {
|
pub struct OctreeMap {
|
||||||
interests: FreeList<InterestNode>,
|
interests: FreeList<Interest>,
|
||||||
|
nodes: FreeList<InterestNode>,
|
||||||
blocks: FreeList<OctreeBlock>,
|
blocks: FreeList<OctreeBlock>,
|
||||||
map: FastHashMap<IVec3, Id<OctreeBlock>>,
|
map: FastHashMap<IVec3, Id<OctreeBlock>>,
|
||||||
}
|
}
|
||||||
|
|
@ -446,7 +359,7 @@ impl OctreeMap {
|
||||||
OctreeBlock::place(
|
OctreeBlock::place(
|
||||||
block.clone(),
|
block.clone(),
|
||||||
&mut self.blocks,
|
&mut self.blocks,
|
||||||
&mut self.interests,
|
&mut self.nodes,
|
||||||
interest.clone(),
|
interest.clone(),
|
||||||
pos,
|
pos,
|
||||||
OctreeBlock::CHUNK_SIZE,
|
OctreeBlock::CHUNK_SIZE,
|
||||||
|
|
@ -458,6 +371,7 @@ impl OctreeMap {
|
||||||
fn new() -> OctreeMap {
|
fn new() -> OctreeMap {
|
||||||
OctreeMap {
|
OctreeMap {
|
||||||
interests: Default::default(),
|
interests: Default::default(),
|
||||||
|
nodes: Default::default(),
|
||||||
blocks: Default::default(),
|
blocks: Default::default(),
|
||||||
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
|
map: FastHashMap::with_hasher(BuildHasherDefault::default()),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue