15 Commits

Author SHA1 Message Date
38d29a9303 piping the texture attachments 2021-03-23 22:01:33 -07:00
afc20a0efa tweaks 2021-03-18 23:26:53 -07:00
5d4e6f8bed tweaks 2021-03-16 21:28:15 -07:00
8cc76b7052 target views 2021-03-10 00:19:06 -08:00
bd2e2557b8 compiling state 2021-03-09 23:07:07 -08:00
7fb89e7d6e thinking about shaders 2021-02-24 18:30:45 -08:00
813d2f6f2f exploring other rendering styles 2021-02-23 21:18:11 -08:00
14b0948b9c actually, the interface for writing isn't too bad. It just sucks when you want to write data from a system 2021-02-22 21:24:41 -08:00
54e89a28ba I optimized for rendering, and made a horrific writing API, smh 2021-02-21 21:06:02 -08:00
ebf5aa0ac4 I like the way this renders, but the implementation is an absolute disaster... 2021-02-21 17:40:19 -08:00
4a041cc833 mutzing about with a fps renderer. I am satisfied with this imgui thing I have going on. But it's a bit bone headed to have these two components so tightly coupled 2021-02-21 02:55:43 -08:00
e376241aae shadowing is back, need to figure out how I want to handle these direcitonal lights.... 2021-02-20 23:43:40 -08:00
c625dc61af fixed the lights, but I somehow broke shadowing 2021-02-20 21:25:38 -08:00
da2c6a436a adding texturing, getting sidetracked by config loading 2021-02-20 20:56:20 -08:00
2b77153f10 config works great, mesh colliders v mesh colliders are very sticky... 2021-02-20 03:43:57 -08:00
29 changed files with 3609 additions and 2297 deletions

View File

@@ -33,7 +33,7 @@ toml = "0.5.8"
cgmath = "0.18.0" cgmath = "0.18.0"
rapier3d = { version = "0.5.0", features = ["simd-nightly", "parallel"] } rapier3d = { version = "0.5.0", features = ["simd-nightly", "parallel"] }
nalgebra = "0.24.1" nalgebra = "0.24.1"
legion = "0.3.1" legion = "0.4.0"
wgpu = "0.7.0" wgpu = "0.7.0"
imgui = "0.7.0" imgui = "0.7.0"
imgui-wgpu = "0.14.0" imgui-wgpu = "0.14.0"

View File

@@ -9,16 +9,48 @@ type = "PhysicsEntity"
mesh = "ball.obj" mesh = "ball.obj"
[entities.position] [entities.position]
x = 15.0
y = 15.0
z = 15.0
[entities.position.rotation]
x = 0.0 x = 0.0
y = 0.0 y = 0.0
z = 0.0 z = 0.0
[entities.physics]
body_status = "static"
[entities.physics.cuboid]
x = 1.0
y = 1.0
z = 1.0
[[entities]] [[entities]]
name = "camera.1" name = "camera.1"
type = "Camera" type = "Camera"
[entities.position]
x = 15.0
y = 15.0
z = 15.0
[[entities]] [[entities]]
name = "light.1" name = "light.1"
type = "Light" type = "Light"
mesh = "light.obj" mesh = "light.obj"
[entities.position]
x = 25.0
y = 75.0
z = 20.0
[[entities]]
name = "light.2"
type = "Light"
mesh = "light.obj"
[entities.position]
x = 50.0
y = 75.0
z = 20.0

26
note.md Normal file
View File

@@ -0,0 +1,26 @@
# Texture Samplers
|GLSL sampler|OpenGL texture enum|Texture type|
|---|---|---|
gsampler1D | GL_TEXTURE_1D | 1D texture
gsampler2D | GL_TEXTURE_2D | 2D texture
gsampler3D | GL_TEXTURE_3D | 3D texture
gsamplerCube | GL_TEXTURE_CUBE_MAP | Cubemap Texture
gsampler2DRect | GL_TEXTURE_RECTANGLE | Rectangle Texture
gsampler1DArray | GL_TEXTURE_1D_ARRAY | 1D Array Texture
gsampler2DArray | GL_TEXTURE_2D_ARRAY | 2D Array Texture
gsamplerCubeArray | GL_TEXTURE_CUBE_MAP_ARRAY | Cubemap Array Texture
gsamplerBuffer | GL_TEXTURE_BUFFER | Buffer Texture
gsampler2DMS | GL_TEXTURE_2D_MULTISAMPLE | Multisample Texture
gsampler2DMSArray | GL_TEXTURE_2D_MULTISAMPLE_ARRAY | Multisample Array Texture
# Shadow Samplers
|GLSL sampler|OpenGL texture enum|
|---|---|
sampler1DShadow | GL_TEXTURE_1D
sampler2DShadow | GL_TEXTURE_2D
samplerCubeShadow | GL_TEXTURE_CUBE_MAP
sampler2DRectShadow | GL_TEXTURE_RECTANGLE
sampler1DArrayShadow | GL_TEXTURE_1D_ARRAY
sampler2DArrayShadow | GL_TEXTURE_2D_ARRAY
samplerCubeArrayShadow | GL_TEXTURE_CUBE_MAP_ARRAY

View File

@@ -2,8 +2,8 @@
# Material Count: 1 # Material Count: 1
newmtl None newmtl None
Ns 0 Ns 500
Ka 0.000000 0.000000 0.000000 Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8 Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8 Ks 0.8 0.8 0.8
d 1 d 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl Material
Ns 323.999994
Ka 1.000000 1.000000 1.000000
Kd 0.800000 0.800000 0.800000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.450000
d 1.000000
illum 2

View File

@@ -0,0 +1,46 @@
# Blender v2.91.2 OBJ File: ''
# www.blender.org
mtllib textured-cube.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vt 0.875000 0.500000
vt 0.625000 0.750000
vt 0.625000 0.500000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.000000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.625000 0.250000
vt 0.875000 0.750000
vt 0.625000 1.000000
vn 0.0000 1.0000 0.0000
vn 0.0000 0.0000 1.0000
vn -1.0000 0.0000 0.0000
vn 0.0000 -1.0000 0.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
usemtl Material
s off
f 5/1/1 3/2/1 1/3/1
f 3/2/2 8/4/2 4/5/2
f 7/6/3 6/7/3 8/8/3
f 2/9/4 8/10/4 6/11/4
f 1/3/5 4/5/5 2/9/5
f 5/12/6 2/9/6 6/7/6
f 5/1/1 7/13/1 3/2/1
f 3/2/2 7/14/2 8/4/2
f 7/6/3 5/12/3 6/7/3
f 2/9/4 4/5/4 8/10/4
f 1/3/5 3/2/5 4/5/5
f 5/12/6 1/3/6 2/9/6

View File

@@ -0,0 +1,10 @@
# Blender MTL File: 'None'
# Material Count: 1
newmtl None
Ns 500
Ka 0.8 0.8 0.8
Kd 0.8 0.8 0.8
Ks 0.8 0.8 0.8
d 1
illum 2

2372
resources/tilted-terrain.obj Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,10 @@ layout(set = 0, binding = 0) uniform Globals {
mat4 u_ViewProj; mat4 u_ViewProj;
}; };
layout(set = 0, binding = 1) uniform Globals {
mat4 u_ViewProj;
};
layout(set = 1, binding = 0) uniform Entity { layout(set = 1, binding = 0) uniform Entity {
mat4 u_World; mat4 u_World;
vec4 u_Color; vec4 u_Color;

View File

@@ -30,18 +30,23 @@ layout(set = 1, binding = 0) uniform Entity {
}; };
float fetch_shadow(int light_id, vec4 homogeneous_coords) { float fetch_shadow(int light_id, vec4 homogeneous_coords) {
// homogeneous coords is the depth of the previously rendered
// fragment, from the lights perspective. If it's less than 0
// then it's behind the light, so it's obviously in shadow
if (homogeneous_coords.w <= 0.0) { if (homogeneous_coords.w <= 0.0) {
return 1.0; return 1.0;
} }
// compensate for the Y-flip difference between the NDC and texture coordinates // compensate for the Y-flip difference between the normalized device
// coordinates (NDC) and texture coordinates
const vec2 flip_correction = vec2(0.5, -0.5); const vec2 flip_correction = vec2(0.5, -0.5);
// compute texture coordinates for shadow lookup // compute texture coordinates for shadow lookup
vec4 light_local = vec4( vec4 light_local = vec4(
// I don't know what kind of jank shit is going on on this line
homogeneous_coords.xy * flip_correction/homogeneous_coords.w + 0.5, homogeneous_coords.xy * flip_correction/homogeneous_coords.w + 0.5,
light_id, light_id, // array layer
homogeneous_coords.z / homogeneous_coords.w homogeneous_coords.z / homogeneous_coords.w // D-Ref
); );
// do the lookup, using HW PCF and comparison // do the lookup, using HW percentage closer filtering(PCF) and comparison
return texture(sampler2DArrayShadow(t_Shadow, s_Shadow), light_local); return texture(sampler2DArrayShadow(t_Shadow, s_Shadow), light_local);
} }

View File

@@ -0,0 +1,22 @@
#version 450
layout(location = 0) in vec3 v_Normal;
layout(location = 1) in vec4 v_Position;
layout(location = 2) in vec2 v_Uv;
layout(location = 0) out vec4 o_Normal;
layout(location = 1) out vec4 o_Position;
layout(location = 2) out vec4 o_Uv;
// so I need to have my 3 color attachments and 1? sampler.
layout(set = 0, binding = 1) uniform texture2DArray t_Uv;
layout(set = 0, binding = 2) uniform gsampler2D s_Uv;
// I also need my 1 depth texture and its sampler
layout(set = 0, binding = 3) uniform texture2DArray t_Shadow;
layout(set = 0, binding = 4) uniform samplerShadow s_Shadow;
void main() {
}

View File

@@ -0,0 +1,21 @@
#version 450
layout(location = 0) in vec4 a_Pos;
layout(location = 0) out vec3 v_Normal;
layout(location = 1) out vec4 v_Position;
layout(location = 2) out vec2 v_Uv;
layout(set = 0, binding = 0) uniform Globals {
mat4 u_ViewProj;
};
layout(set = 1, binding = 0) uniform Entity {
mat4 u_World;
vec4 u_Color;
};
void main() {
gl_Position = u_ViewProj * u_World * vec4(a_Pos);
}

View File

@@ -5,7 +5,8 @@ use cgmath::{Decomposed, InnerSpace, Matrix4, Point3, Rad, Vector3};
use winit_24::dpi::{LogicalPosition, PhysicalPosition}; use winit_24::dpi::{LogicalPosition, PhysicalPosition};
use winit_24::event::{ElementState, MouseScrollDelta, VirtualKeyCode}; use winit_24::event::{ElementState, MouseScrollDelta, VirtualKeyCode};
use crate::render::OPENGL_TO_WGPU_MATRIX; use crate::render::OPENGL_TO_WGPU_MATRIX;
use imgui::Condition;
use imgui::*;
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Camera { pub struct Camera {
@@ -150,6 +151,7 @@ impl CameraController {
} }
pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) { pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) {
// Move forward/backward and left/right // Move forward/backward and left/right
let view_vector = Vector3::new( let view_vector = Vector3::new(
(1.0 * camera.pitch.0.sin() * camera.yaw.0.sin()), (1.0 * camera.pitch.0.sin() * camera.yaw.0.sin()),

View File

@@ -1,16 +1,20 @@
use wgpu::{TextureView, Buffer, BindGroup};
use std::sync::Arc; use std::sync::Arc;
use rapier3d::dynamics::{RigidBody, RigidBodyHandle};
use rapier3d::geometry::ColliderHandle;
use rapier3d::geometry::Collider as r3dCollider;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use cgmath::Deg;
use cgmath::{Deg, Euler};
use rapier3d::dynamics::{RigidBody, RigidBodyHandle};
use rapier3d::geometry::Collider as r3dCollider;
use rapier3d::geometry::ColliderHandle;
use wgpu::{BindGroup, Buffer, TextureView};
use crate::runtime::state::{TomlPositionDescription, TomlRotationDescription};
use imgui::Ui;
// a component is any type that is 'static, sized, send and sync // a component is any type that is 'static, sized, send and sync
pub struct ImguiWindow<'a, T> {
pub struct ImguiWindow<'a> { pub window: fn() -> imgui::Window<'a>,
pub window: imgui::Window<'a>, pub func: fn(&Ui, Vec<&T>),
} }
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@@ -28,6 +32,62 @@ pub struct Position {
pub rot: cgmath::Euler<Deg<f32>>, pub rot: cgmath::Euler<Deg<f32>>,
} }
impl Default for Position {
fn default() -> Self {
Position {
x: 0.0,
y: 0.0,
z: 0.0,
rot: Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0),
},
}
}
}
impl From<TomlPositionDescription> for Position {
fn from(pos: TomlPositionDescription) -> Self {
let euler = match pos.rot {
None => Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0),
},
Some(v) => Euler {
x: Deg(v.x),
y: Deg(v.y),
z: Deg(v.z),
},
};
Position {
x: pos.x,
y: pos.y,
z: pos.z,
rot: euler,
}
}
}
impl From<Option<TomlPositionDescription>> for Position {
fn from(pos: Option<TomlPositionDescription>) -> Self {
match pos {
None => Position {
x: 0.0,
y: 0.0,
z: 0.0,
rot: Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0),
},
},
Some(v) => Position::from(v),
}
}
}
#[derive(Clone, Default, PartialEq, Eq, Hash, Copy, Debug)] #[derive(Clone, Default, PartialEq, Eq, Hash, Copy, Debug)]
pub struct RangeCopy<Idx> { pub struct RangeCopy<Idx> {
pub start: Idx, pub start: Idx,

View File

@@ -25,15 +25,15 @@ pub struct DirectionalLight {
pub fov: f32, pub fov: f32,
pub depth: RangeCopy<f32>, pub depth: RangeCopy<f32>,
pub target_view: Arc<TextureView>, pub target_view: Arc<TextureView>,
pub pos: Position,
} }
impl DirectionalLight { impl DirectionalLight {
pub fn to_raw(&self) -> LightRaw { pub fn to_raw(&self, pos: &Position) -> LightRaw {
use cgmath::{Deg, EuclideanSpace, Matrix4, PerspectiveFov, Point3, Vector3}; use cgmath::{Deg, EuclideanSpace, Matrix4, PerspectiveFov, Point3, Vector3};
let point3d = Point3::new(self.pos.x, self.pos.y, self.pos.z); let point3d = Point3::new(pos.x, pos.y, pos.z);
let mx_view = Matrix4::look_at(point3d, Point3::origin(), Vector3::unit_y()); let point3d_2 = Point3::new(pos.x, pos.y - 1.0, pos.z);
let mx_view = Matrix4::look_at(point3d, point3d_2, Vector3::unit_z());
let projection = PerspectiveFov { let projection = PerspectiveFov {
fovy: Deg(self.fov).into(), fovy: Deg(self.fov).into(),
@@ -46,7 +46,7 @@ impl DirectionalLight {
mx_correction * cgmath::Matrix4::from(projection.to_perspective()) * mx_view; mx_correction * cgmath::Matrix4::from(projection.to_perspective()) * mx_view;
LightRaw { LightRaw {
proj: *mx_view_proj.as_ref(), proj: *mx_view_proj.as_ref(),
pos: [self.pos.x, self.pos.y, self.pos.z, 1.0], pos: [pos.x, pos.y, pos.z, 1.0],
color: [ color: [
self.color.r as f32, self.color.r as f32,
self.color.g as f32, self.color.g as f32,

View File

@@ -1,16 +1,17 @@
extern crate env_logger;
extern crate imgui; extern crate imgui;
extern crate imgui_wgpu; extern crate imgui_wgpu;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
extern crate tobj;
extern crate winit_24;
extern crate env_logger;
extern crate toml;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
extern crate tobj;
extern crate toml;
extern crate winit_24;
use std::collections::HashMap;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::fs;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
@@ -20,6 +21,7 @@ use cgmath::{
}; };
use futures::executor::block_on; use futures::executor::block_on;
use futures::task::LocalSpawn; use futures::task::LocalSpawn;
use futures::FutureExt;
use gilrs::Event as GilEvent; use gilrs::Event as GilEvent;
use gilrs::{Gamepad, Gilrs}; use gilrs::{Gamepad, Gilrs};
use imgui::FontSource; use imgui::FontSource;
@@ -28,6 +30,7 @@ use imgui::*;
use imgui_wgpu::{Renderer as ImguiRenderer, RendererConfig as ImguiRendererConfig}; use imgui_wgpu::{Renderer as ImguiRenderer, RendererConfig as ImguiRendererConfig};
use legion::systems::{SyncResources, UnsafeResources}; use legion::systems::{SyncResources, UnsafeResources};
use legion::*; use legion::*;
use log::LevelFilter;
use rapier3d::counters::Timer; use rapier3d::counters::Timer;
use rapier3d::dynamics::{ use rapier3d::dynamics::{
IntegrationParameters, JointSet, RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet, IntegrationParameters, JointSet, RigidBody, RigidBodyBuilder, RigidBodyHandle, RigidBodySet,
@@ -40,6 +43,8 @@ use rapier3d::pipeline::PhysicsPipeline;
use wgpu::{BindGroup, Buffer, TextureView}; use wgpu::{BindGroup, Buffer, TextureView};
use wgpu_subscriber; use wgpu_subscriber;
use winit_24::event::DeviceEvent::MouseMotion; use winit_24::event::DeviceEvent::MouseMotion;
use winit_24::event::{ElementState, VirtualKeyCode};
use winit_24::event_loop::EventLoopProxy;
use winit_24::platform::unix::x11::ffi::Time; use winit_24::platform::unix::x11::ffi::Time;
use winit_24::window::Window; use winit_24::window::Window;
use winit_24::{ use winit_24::{
@@ -54,13 +59,9 @@ use crate::imgui_supp::extended_winit_imgui_support;
use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform}; use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform};
use crate::owned_event::{OwnedEvent, OwnedEventExtension}; use crate::owned_event::{OwnedEvent, OwnedEventExtension};
use crate::physics::state::PhysicsState; use crate::physics::state::PhysicsState;
use std::fs; use crate::render::system::ImguiPerformanceProfilerLine;
use winit_24::event::{VirtualKeyCode, ElementState};
use std::collections::HashMap;
use futures::FutureExt;
use log::LevelFilter;
use crate::runtime::state::RuntimeState; use crate::runtime::state::RuntimeState;
use winit_24::event_loop::EventLoopProxy; use winit_24::dpi::PhysicalSize;
mod camera; mod camera;
mod components; mod components;
@@ -92,33 +93,38 @@ https://github.com/amethyst/legion
ECS ECS
animation animation
config / save loading (sorta!)
render 3d (good!) render 3d (good!)
input/io (yep!) input/io (yep!)
collision / physics (yep!) collision / physics (yep!)
entities & behaviours (got the entities!) entities & behaviours (got the entities!)
scripting! scripting!
Todo:
Load scene imgui interface w/ toml files
FPS graph port from voxel raycaster
better imgui interface with components & systems
Figure out eventing, GameInput, passing all events, etc.
+ texturing
*/ */
//log::info!(""); //log::info!("");
// ImGUI works on more or less a global state. which is MegaLame // ImGUI works on more or less an unsafe global state. which is MegaLame
static mut CURRENT_UI: Option<imgui::Ui<'static>> = None; static mut CURRENT_UI: Option<imgui::Ui<'static>> = None;
pub unsafe fn current_ui<'a>() -> Option<&'a imgui::Ui<'a>> { pub unsafe fn current_ui<'a>() -> Option<&'a imgui::Ui<'a>> {
CURRENT_UI.as_ref() CURRENT_UI.as_ref()
} }
fn main() { fn main() {
let logger = env_logger::builder()
let logger = env_logger::builder().filter(Some("minimal_viable_game_engine"), LevelFilter::Info).init(); .filter(Some("minimal_viable_game_engine"), LevelFilter::Info)
.init();
// for i in settings.get("entities") {
//
// }
let mut world = World::default(); let mut world = World::default();
@@ -129,10 +135,13 @@ fn main() {
let mut load_schedule = Schedule::builder() let mut load_schedule = Schedule::builder()
.add_system(runtime::system::runtime_load_system()) .add_system(runtime::system::runtime_load_system())
.add_system(runtime::system::runtime_spawn_system()) .add_system(runtime::system::runtime_spawn_system())
.flush()
.build(); .build();
let mut render_schedule = Schedule::builder() let mut render_schedule = Schedule::builder()
.add_system(render::system::render_imgui_system())
.add_system(render::system::render_test_system()) .add_system(render::system::render_test_system())
.add_system(render::system::render_performance_flag_system())
.build(); .build();
let mut update_schedule = Schedule::builder() let mut update_schedule = Schedule::builder()
@@ -149,11 +158,14 @@ fn main() {
let event_loop = EventLoop::<OwnedEventExtension>::with_user_event(); let event_loop = EventLoop::<OwnedEventExtension>::with_user_event();
let mut builder = winit_24::window::WindowBuilder::new(); let mut builder = winit_24::window::WindowBuilder::new();
builder = builder.with_title("MVGE"); builder = builder.with_title("MVGE");
builder = builder.with_inner_size(PhysicalSize::new(1200,900));
let window = builder.build(&event_loop).unwrap(); let window = builder.build(&event_loop).unwrap();
let mut resources = Resources::default(); let mut resources = Resources::default();
// Load up all the resources // Load up all the resources
{ {
let mut imgui_context = imgui::Context::create(); let mut imgui_context = imgui::Context::create();
@@ -194,7 +206,7 @@ fn main() {
// Physics // Physics
let (physics_state, physics_pipeline) = let (physics_state, physics_pipeline) =
PhysicsState::build(rapier3d::math::Vector::new(0.0, -9.81, 0.05)); PhysicsState::build(rapier3d::math::Vector::new(0.0, -9.81, 0.0));
resources.insert(physics_state); resources.insert(physics_state);
resources.insert(physics_pipeline); resources.insert(physics_pipeline);
@@ -240,21 +252,26 @@ fn main() {
// conditionally, and run the fps locked renderer // conditionally, and run the fps locked renderer
event::Event::MainEventsCleared => { event::Event::MainEventsCleared => {
event_schedule.execute(&mut world, &mut resources); event_schedule.execute(&mut world, &mut resources);
imgui_prepare_schedule.execute(&mut world, &mut resources);
resources resources
.get_mut::<Vec<OwnedEvent<OwnedEventExtension>>>() .get_mut::<Vec<OwnedEvent<OwnedEventExtension>>>()
.unwrap() .unwrap()
.clear(); .clear();
imgui_prepare_schedule.execute(&mut world, &mut resources);
let (step_size, elapsed_time) = { let (step_size, elapsed_time) = {
// deltatime since last frame let mut loop_state = resources.get_mut::<LoopState>().unwrap();
let loop_state = resources.get::<LoopState>().unwrap();
( (
loop_state.step_size, loop_state.step_size,
loop_state.start_time.elapsed().as_secs_f32(), loop_state.start_time.elapsed().as_secs_f32(),
) )
}; };
delta_time = elapsed_time - current_time; delta_time = elapsed_time - current_time;
{
let mut loop_state = resources.get_mut::<LoopState>().unwrap();
loop_state.delta_time = Duration::from_secs_f32(delta_time);
}
current_time = elapsed_time; current_time = elapsed_time;
if delta_time > 0.02 { if delta_time > 0.02 {
delta_time = 0.02; delta_time = 0.02;
@@ -263,10 +280,10 @@ fn main() {
while accumulator_time - step_size >= step_size { while accumulator_time - step_size >= step_size {
accumulator_time -= step_size; accumulator_time -= step_size;
// ==== DELTA TIME LOCKED ==== // ==== DELTA TIME LOCKED ====
update_schedule.execute(&mut world, &mut resources); update_schedule.execute(&mut world, &mut resources);
} }
// ==== FPS LOCKED ==== // ==== FPS LOCKED ====
render_schedule.execute(&mut world, &mut resources); render_schedule.execute(&mut world, &mut resources);
} }
@@ -287,6 +304,7 @@ fn main() {
event: winit_24::event::DeviceEvent::Key(keyboard_input), event: winit_24::event::DeviceEvent::Key(keyboard_input),
.. ..
} => { } => {
if keyboard_input.virtual_keycode.is_some() {
match keyboard_input.virtual_keycode.unwrap() { match keyboard_input.virtual_keycode.unwrap() {
VirtualKeyCode::Escape => { VirtualKeyCode::Escape => {
if keyboard_input.state == ElementState::Pressed { if keyboard_input.state == ElementState::Pressed {
@@ -295,7 +313,8 @@ fn main() {
//d //d
} }
} }
_ => () _ => (),
}
} }
} }
event::Event::WindowEvent { event::Event::WindowEvent {
@@ -318,7 +337,6 @@ fn main() {
} }
pub fn setup_gamepad(event_loop: &EventLoop<OwnedEventExtension>) { pub fn setup_gamepad(event_loop: &EventLoop<OwnedEventExtension>) {
let event_loop_proxy = event_loop.create_proxy(); let event_loop_proxy = event_loop.create_proxy();
std::thread::spawn(move || { std::thread::spawn(move || {
@@ -360,4 +378,3 @@ pub fn setup_gamepad(event_loop: &EventLoop<OwnedEventExtension>) {
} }
}); });
} }

View File

@@ -10,9 +10,11 @@ use rapier3d::geometry::{BroadPhase, ColliderSet, NarrowPhase};
use rapier3d::pipeline::{PhysicsPipeline, ChannelEventCollector}; use rapier3d::pipeline::{PhysicsPipeline, ChannelEventCollector};
use crate::camera::{Camera, CameraController}; use crate::camera::{Camera, CameraController};
use crate::components::{Collider, LoopState, Mesh, Physics, Position}; use crate::components::{Collider, LoopState, Mesh, Physics, Position, ImguiWindow};
use imgui::FontSource; use imgui::{FontSource, Condition};
use imgui::*;
use crate::physics::state::PhysicsState; use crate::physics::state::PhysicsState;
use crate::render::system::ImguiGenericOutputLine;
#[system] #[system]
@@ -73,14 +75,22 @@ pub fn run_physics(
} }
} }
#[system] #[system]
#[write_component(Camera)] #[write_component(Camera)]
#[write_component(CameraController)] #[write_component(CameraController)]
#[write_component(ImguiGenericOutputLine)]
pub fn update_camera(world: &mut SubWorld, #[resource] loop_state: &mut LoopState) { pub fn update_camera(world: &mut SubWorld, #[resource] loop_state: &mut LoopState) {
let mut query = <(&mut Camera, &mut CameraController)>::query(); let mut query = <(&mut Camera, &mut CameraController)>::query();
for (mut camera, controller) in query.iter_mut(world) { for (mut camera, controller) in query.iter_mut(world) {
controller.update_camera(&mut camera, loop_state.step_size) controller.update_camera(&mut camera, loop_state.step_size)
} }
let mut query = <(&mut Camera, &mut CameraController, &mut ImguiGenericOutputLine)>::query();
for (mut camera, controller, ui_entry) in query.iter_mut(world) {
ui_entry.label = format!("Camera: {:.2}, {:.2}, {:.2}", camera.position.x, camera.position.y, camera.position.z);
}
} }
#[system] #[system]
@@ -93,6 +103,7 @@ pub fn update_models(
#[resource] physics_state: &mut PhysicsState, #[resource] physics_state: &mut PhysicsState,
#[resource] physics_pipeline: &mut PhysicsPipeline, #[resource] physics_pipeline: &mut PhysicsPipeline,
) { ) {
// Make sure all the entities we care about are added to the system // Make sure all the entities we care about are added to the system
let mut query = <(&mut Collider, &mut Physics, &mut Mesh, &mut Position)>::query(); let mut query = <(&mut Collider, &mut Physics, &mut Mesh, &mut Position)>::query();
for (collider, physics, mesh, position) in query.iter_mut(world) { for (collider, physics, mesh, position) in query.iter_mut(world) {

View File

@@ -66,3 +66,12 @@ pub struct ShadowUniforms {
unsafe impl Pod for ShadowUniforms {} unsafe impl Pod for ShadowUniforms {}
unsafe impl Zeroable for ShadowUniforms {} unsafe impl Zeroable for ShadowUniforms {}
#[repr(C)]
#[derive(Clone, Copy, Debug)]
pub struct CameraProjectionView {
proj: [[f32; 4]; 4],
}
unsafe impl Pod for CameraProjectionView {}
unsafe impl Zeroable for CameraProjectionView {}

View File

@@ -18,7 +18,11 @@ use legion::world::SubWorld;
use legion::*; use legion::*;
use rapier3d::parry::motion::RigidMotionComposition; use rapier3d::parry::motion::RigidMotionComposition;
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use wgpu::{BackendBit, BindGroup, BindGroupLayout, Buffer, BufferBindingType, Device, FragmentState, Instance, Queue, Surface, SwapChain, SwapChainDescriptor, SwapChainFrame, TextureView, VertexState, CommandEncoder}; use wgpu::{
BackendBit, BindGroup, BindGroupLayout, Buffer, BufferBindingType, CommandEncoder, Device,
Features, FragmentState, Instance, Queue, Surface, SwapChain, SwapChainDescriptor,
SwapChainFrame, TextureView, VertexState,
};
use winit_24::dpi::PhysicalSize; use winit_24::dpi::PhysicalSize;
use winit_24::platform::unix::x11::ffi::Time; use winit_24::platform::unix::x11::ffi::Time;
use winit_24::window::Window; use winit_24::window::Window;
@@ -26,24 +30,23 @@ use winit_24::window::Window;
use crate::camera::{Camera, CameraController}; use crate::camera::{Camera, CameraController};
use crate::components::{Mesh, Position, RangeCopy}; use crate::components::{Mesh, Position, RangeCopy};
use crate::current_ui; use crate::current_ui;
use crate::geometry::{load_obj, Vertex, RawMesh}; use crate::geometry::{load_obj, RawMesh, Vertex};
use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform}; use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform};
use crate::light::{DirectionalLight, LightRaw}; use crate::light::{DirectionalLight, LightRaw};
use crate::render::{EntityUniforms, ShadowUniforms, ForwardUniforms}; use crate::render::{CameraProjectionView, EntityUniforms, ForwardUniforms, ShadowUniforms};
/// A render pass consists of a pipeline, bindgroup, and uniform buf /// A render pass consists of a pipeline, bindgroup, and uniform buf
/// The uniform buf is just the ShadowUniforms or ForwardUniforms /// The uniform buf is just the ShadowUniforms or ForwardUniforms
/// They are uniform across all cu's.
/// And the bindgroup is just the localbindgroup (the EntityUniforms) and the rest /// And the bindgroup is just the localbindgroup (the EntityUniforms) and the rest
///
pub struct Pass { pub struct Pass {
pub pipeline: wgpu::RenderPipeline, pub pipeline: wgpu::RenderPipeline,
pub bind_group: wgpu::BindGroup, pub bind_group: wgpu::BindGroup,
pub uniform_buf: wgpu::Buffer, pub uniform_buf: wgpu::Buffer,
} }
pub struct RenderState { pub struct RenderState {
swapchain: SwapChain, swapchain: SwapChain,
swapchain_description: SwapChainDescriptor, swapchain_description: SwapChainDescriptor,
instance: Arc<Instance>, instance: Arc<Instance>,
@@ -61,6 +64,15 @@ pub struct RenderState {
pub(in crate::render) forward_pass: Pass, pub(in crate::render) forward_pass: Pass,
pub(in crate::render) forward_depth: wgpu::TextureView, pub(in crate::render) forward_depth: wgpu::TextureView,
// Gbuffer bs
pub(in crate::render) gbuffer_pass: Pass,
pub(in crate::render) gbuffer_cam_projection_buffer: wgpu::Buffer,
pub(in crate::render) gbuffer_depth_texture: wgpu::TextureView,
pub(in crate::render) gbuffer_depth_views: Vec<Arc<TextureView>>,
pub(in crate::render) gbuffer_target_texture: wgpu::TextureView,
pub(in crate::render) gbuffer_target_views: Vec<Arc<TextureView>>,
// this is for the set=1 entity uniforms
entity_bind_group_layout: BindGroupLayout, entity_bind_group_layout: BindGroupLayout,
pub(in crate::render) light_uniform_buf: wgpu::Buffer, pub(in crate::render) light_uniform_buf: wgpu::Buffer,
@@ -73,12 +85,14 @@ pub struct RenderState {
impl RenderState { impl RenderState {
const MAX_LIGHTS: usize = 10; const MAX_LIGHTS: usize = 10;
const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
const G_BUFFER_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba32Float;
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d { const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d {
width: 512, width: 1024,
height: 512, height: 1024,
depth: Self::MAX_LIGHTS as u32, depth: Self::MAX_LIGHTS as u32,
}; };
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
/// Generate a projection matrix with no look-at yet /// Generate a projection matrix with no look-at yet
pub fn generate_matrix(&self) -> cgmath::Matrix4<f32> { pub fn generate_matrix(&self) -> cgmath::Matrix4<f32> {
@@ -109,10 +123,7 @@ impl RenderState {
} }
/// Create a buffer for a mesh /// Create a buffer for a mesh
fn create_buffer( fn create_buffer(device: &wgpu::Device, raw_mesh: &RawMesh) -> (Arc<Buffer>, Arc<Buffer>) {
device: &wgpu::Device,
raw_mesh: &RawMesh,
) -> (Arc<Buffer>, Arc<Buffer>) {
let vertex_buf = Arc::new( let vertex_buf = Arc::new(
device.create_buffer_init(&wgpu::util::BufferInitDescriptor { device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("vertex-buffer"), label: Some("vertex-buffer"),
@@ -133,8 +144,11 @@ impl RenderState {
} }
/// Take a meshes raw representation and upload it to a GPU buffer /// Take a meshes raw representation and upload it to a GPU buffer
pub fn upload_mesh_to_buffer(&mut self, mesh: &RawMesh, color: Option<wgpu::Color>) -> Result<Mesh, String> { pub fn upload_mesh_to_buffer(
&mut self,
mesh: &RawMesh,
color: Option<wgpu::Color>,
) -> Result<Mesh, String> {
let index_count = mesh.indices.len() * 3; // TODO bad bad bad bad! let index_count = mesh.indices.len() * 3; // TODO bad bad bad bad!
let (vertex_buf, index_buf) = RenderState::create_buffer(&self.device, mesh); let (vertex_buf, index_buf) = RenderState::create_buffer(&self.device, mesh);
@@ -174,7 +188,11 @@ impl RenderState {
} }
/// explicitly load from file, and upload to gpu the mesh /// explicitly load from file, and upload to gpu the mesh
pub fn load_mesh_to_buffer(&mut self, filepath: &str, color: Option<wgpu::Color>) -> Result<Mesh, String> { pub fn load_mesh_to_buffer(
&mut self,
filepath: &str,
color: Option<wgpu::Color>,
) -> Result<Mesh, String> {
let raw_mesh = load_obj(filepath)?; let raw_mesh = load_obj(filepath)?;
self.upload_mesh_to_buffer(&raw_mesh, color) self.upload_mesh_to_buffer(&raw_mesh, color)
} }
@@ -196,25 +214,114 @@ impl RenderState {
b: 0.5, b: 0.5,
a: 1.0, a: 1.0,
}, },
fov: 45.0, fov: 90.0,
depth: RangeCopy { depth: RangeCopy {
start: 1.0, start: 1.0,
end: 20.0, end: 200.0,
}, },
target_view: target.clone(), target_view: target.clone(),
pos: Position {
x: 0.0,
y: 0.0,
z: 0.0,
rot: Euler {
x: Deg(0.0),
y: Deg(-25.0),
z: Deg(0.0),
},
},
} }
} }
// Create a render pass with the ability to add external bindings
pub fn create_render_pass(
device: &Device,
external_binding_layout: &BindGroupLayout,
vertex_description: &wgpu::VertexBufferLayout,
uniform_size: u64,
label: Option<&str>,
) -> Option<Pass> {
// The g-buffers camera projection matrix that we stream data to
let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: Some(format!("{} uniform-buffer", label.unwrap_or("unnamed")).as_str()),
size: uniform_size,
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
// Uniform bind group for the view matrix
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some(format!("{} bind group layout", label.unwrap_or("unnamed")).as_str()),
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0, // global
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
min_binding_size: wgpu::BufferSize::new(uniform_size),
has_dynamic_offset: false,
},
count: None,
}],
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some(format!("{} uniform bind group", label.unwrap_or("unnamed")).as_str()),
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer {
buffer: &uniform_buf,
offset: 0,
size: wgpu::BufferSize::new(uniform_size),
},
}],
});
let vs_module =
device.create_shader_module(&wgpu::include_spirv!("../../shaders/bake.vert.spv"));
let fs_module =
device.create_shader_module(&wgpu::include_spirv!("../../shaders/bake.frag.spv"));
// The bind group layouts are accessed by layout(set = X, binding = x)
// in the shader.
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some(format!("{} pipeline layout", label.unwrap_or("unnamed")).as_str()),
bind_group_layouts: &[&bind_group_layout, external_binding_layout],
push_constant_ranges: &[],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some(format!("{} render pipeline", label.unwrap_or("unnamed")).as_str()),
layout: Some(&pipeline_layout),
vertex: VertexState {
module: &vs_module,
entry_point: "main",
buffers: &[vertex_description.clone()],
},
fragment: Some(FragmentState {
module: &fs_module,
entry_point: "main",
targets: &[],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState {
constant: 2, // corresponds to bilinear filtering
slope_scale: 2.0,
clamp: 0.0,
},
clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING),
}),
multisample: wgpu::MultisampleState::default(),
});
Some(Pass {
pipeline,
bind_group,
uniform_buf,
})
}
pub fn init(window: &Window, imgui_context: &mut ImguiContext) -> RenderState { pub fn init(window: &Window, imgui_context: &mut ImguiContext) -> RenderState {
// Grab the GPU instance, and query its features // Grab the GPU instance, and query its features
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
@@ -302,10 +409,149 @@ impl RenderState {
}); });
/* /*
There appear to be two passes required for shadows, the shadow pass, and the forward pass The shadow pass renders from the perspective of each camera and applies that to the shadow
Need to open this up in renderdoc and see what it's actually doing texture. Due to light FOV and the shadow texture itself, it's rather difficult to have
hands-off global lighting / point lights
*/ */
// This pass is just going to forward the vertex info to the fragments
// And they are going to render to the gbuffer
let g_buffer_pass = Self::create_render_pass(
&device,
&entity_bind_group_layout,
&vb_desc,
mem::size_of::<CameraProjectionView>() as wgpu::BufferAddress,
Some("g-buffer"),
);
let shadow_pass = Self::create_render_pass(
&device,
&entity_bind_group_layout,
&vb_desc,
mem::size_of::<ShadowUniforms>() as wgpu::BufferAddress,
Some("shadow-pass"),
);
let g_buffer_pass = {
let uniform_size = mem::size_of::<CameraProjectionView>() as wgpu::BufferAddress;
// The g-buffers camera projection matrix that we stream data to
let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("g-buffers CameraProjectionView uniform buffer"),
size: uniform_size,
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
// Uniform bind group for the view matrix
let bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("g-buffer input pass bindgroup layout (cam)"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0, // global
visibility: wgpu::ShaderStage::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
min_binding_size: wgpu::BufferSize::new(uniform_size),
has_dynamic_offset: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1, // texture2DArray
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture {
multisampled: false,
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2Array,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2, // samplerShadow
visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler {
filtering: false,
comparison: true,
},
count: None,
},
],
});
//sudo rm /usr/bin/ld
//sudo ln -s /usr/bin/lld /usr/bin/ld
//sudo ln -s /usr/bin/x86_64-linux-gnu-ld /usr/bin/ld
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("g-buffers uniform bind group"),
layout: &bind_group_layout,
entries: &[wgpu::BindGroupEntry {
binding: 0, // globals
resource: wgpu::BindingResource::Buffer {
buffer: &uniform_buf,
offset: 0,
size: wgpu::BufferSize::new(uniform_size),
},
}],
});
let vs_module =
device.create_shader_module(&wgpu::include_spirv!("../../shaders/bake.vert.spv"));
let fs_module =
device.create_shader_module(&wgpu::include_spirv!("../../shaders/bake.frag.spv"));
// The bind group layouts are accessed by layout(set = X, binding = x)
// in the shader.
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("g-buffer input pipeline layout"),
bind_group_layouts: &[&bind_group_layout, &entity_bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("g-buffer"),
layout: Some(&pipeline_layout),
vertex: VertexState {
module: &vs_module,
entry_point: "main",
buffers: &[vb_desc.clone()],
},
fragment: Some(FragmentState {
module: &fs_module,
entry_point: "main",
targets: &[],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back,
..Default::default()
},
depth_stencil: Some(wgpu::DepthStencilState {
format: wgpu::TextureFormat::Depth32Float,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::LessEqual,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState {
constant: 2, // corresponds to bilinear filtering
slope_scale: 2.0,
clamp: 0.0,
},
clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING),
}),
multisample: wgpu::MultisampleState::default(),
});
Pass {
pipeline,
bind_group,
uniform_buf,
}
};
let shadow_pass = { let shadow_pass = {
let uniform_size = mem::size_of::<ShadowUniforms>() as wgpu::BufferAddress; let uniform_size = mem::size_of::<ShadowUniforms>() as wgpu::BufferAddress;
@@ -356,9 +602,9 @@ impl RenderState {
// Create the render pipeline // Create the render pipeline
let vs_module = let vs_module =
device.create_shader_module(&wgpu::include_spirv!("../../resources/bake.vert.spv")); device.create_shader_module(&wgpu::include_spirv!("../../shaders/bake.vert.spv"));
let fs_module = let fs_module =
device.create_shader_module(&wgpu::include_spirv!("../../resources/bake.frag.spv")); device.create_shader_module(&wgpu::include_spirv!("../../shaders/bake.frag.spv"));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("shadow"), label: Some("shadow"),
@@ -402,6 +648,19 @@ impl RenderState {
} }
}; };
// Pre init the gbuffers camera projection uniform
let gbuffer_cam_uniform_size =
mem::size_of::<CameraProjectionView>() as wgpu::BufferAddress;
let gbuffer_cam_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("g-buffer camera projection uniform buffer"),
size: gbuffer_cam_uniform_size,
usage: wgpu::BufferUsage::UNIFORM
| wgpu::BufferUsage::COPY_SRC
| wgpu::BufferUsage::COPY_DST,
mapped_at_creation: false,
});
// Pre init the light uniform, with slots enough for MAX_LIGHTS // Pre init the light uniform, with slots enough for MAX_LIGHTS
let light_uniform_size = let light_uniform_size =
(Self::MAX_LIGHTS * mem::size_of::<LightRaw>()) as wgpu::BufferAddress; (Self::MAX_LIGHTS * mem::size_of::<LightRaw>()) as wgpu::BufferAddress;
@@ -447,8 +706,22 @@ impl RenderState {
50.0, 50.0,
); );
let g_buffer_camera_projection_matrix = CameraProjectionView {
proj: *mx_projection.as_ref(),
};
let g_buffer_camera_projection_uniform =
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("g-buffer camera projection uniform buffer"),
contents: bytemuck::bytes_of(&g_buffer_camera_projection_matrix),
usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST,
});
let forward_pass = { let forward_pass = {
// Create pipeline layout // Create pipeline layout
let forward_uniform_size =
mem::size_of::<ForwardUniforms>() as wgpu::BufferAddress;
let bind_group_layout = let bind_group_layout =
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[ entries: &[
@@ -457,11 +730,7 @@ impl RenderState {
visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Buffer { ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform, ty: wgpu::BufferBindingType::Uniform,
min_binding_size: wgpu::BufferSize::new(mem::size_of::< min_binding_size: wgpu::BufferSize::new(forward_uniform_size),
ForwardUniforms,
>(
)
as _),
has_dynamic_offset: false, has_dynamic_offset: false,
}, },
count: None, count: None,
@@ -477,7 +746,7 @@ impl RenderState {
count: None, count: None,
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 2, binding: 2, // texture2DArray
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Texture { ty: wgpu::BindingType::Texture {
multisampled: false, multisampled: false,
@@ -487,7 +756,7 @@ impl RenderState {
count: None, count: None,
}, },
wgpu::BindGroupLayoutEntry { wgpu::BindGroupLayoutEntry {
binding: 3, binding: 3, // samplerShadow
visibility: wgpu::ShaderStage::FRAGMENT, visibility: wgpu::ShaderStage::FRAGMENT,
ty: wgpu::BindingType::Sampler { ty: wgpu::BindingType::Sampler {
filtering: false, filtering: false,
@@ -569,10 +838,10 @@ impl RenderState {
}); });
// Create the render pipeline // Create the render pipeline
let vs_module = let vs_module = device
device.create_shader_module(&wgpu::include_spirv!("../../resources/forward.vert.spv")); .create_shader_module(&wgpu::include_spirv!("../../shaders/forward.vert.spv"));
let fs_module = let fs_module = device
device.create_shader_module(&wgpu::include_spirv!("../../resources/forward.frag.spv")); .create_shader_module(&wgpu::include_spirv!("../../shaders/forward.frag.spv"));
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("main"), label: Some("main"),
@@ -610,7 +879,7 @@ impl RenderState {
} }
}; };
let depth_texture = device.create_texture(&wgpu::TextureDescriptor { let forward_depth_texture = device.create_texture(&wgpu::TextureDescriptor {
size: wgpu::Extent3d { size: wgpu::Extent3d {
width: sc_desc.width, width: sc_desc.width,
height: sc_desc.height, height: sc_desc.height,
@@ -621,9 +890,73 @@ impl RenderState {
dimension: wgpu::TextureDimension::D2, dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT, format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::RENDER_ATTACHMENT, usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
label: Some("Depth Texture"), label: Some("Forward Depth Texture"),
}); });
let g_buffer_depth_texture = device.create_texture(&wgpu::TextureDescriptor {
size: wgpu::Extent3d {
width: sc_desc.width,
height: sc_desc.height,
depth: 5,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::RENDER_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
label: Some("g-buffer depth texture"),
});
// I need one of these for each of the g-buffer elements I'm calculating
let mut g_buffer_depth_texture_views = (0..4)
.map(|i| {
Arc::new(
g_buffer_depth_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("g-buffer depth texture"),
format: None,
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: i as u32,
array_layer_count: NonZeroU32::new(1),
}),
)
})
.collect::<Vec<_>>();
let g_buffer_data_texture = device.create_texture(&wgpu::TextureDescriptor {
size: wgpu::Extent3d {
width: sc_desc.width,
height: sc_desc.height,
depth: 5,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: Self::DEPTH_FORMAT,
usage: wgpu::TextureUsage::RENDER_ATTACHMENT | wgpu::TextureUsage::SAMPLED,
label: Some("g-buffer depth texture"),
});
// I need one of these for each of the g-buffer elements I'm calculating
let mut g_buffer_data_texture_views = (0..4)
.map(|i| {
Arc::new(
g_buffer_depth_texture.create_view(&wgpu::TextureViewDescriptor {
label: Some("g-buffer depth texture"),
format: None,
dimension: Some(wgpu::TextureViewDimension::D2),
aspect: wgpu::TextureAspect::All,
base_mip_level: 0,
level_count: None,
base_array_layer: i as u32,
array_layer_count: NonZeroU32::new(1),
}),
)
})
.collect::<Vec<_>>();
// Imgui renderer // Imgui renderer
let renderer_config = ImguiRendererConfig { let renderer_config = ImguiRendererConfig {
texture_format: sc_desc.format, texture_format: sc_desc.format,
@@ -633,6 +966,15 @@ impl RenderState {
ImguiRenderer::new(&mut imgui_context.context, &device, &queue, renderer_config); ImguiRenderer::new(&mut imgui_context.context, &device, &queue, renderer_config);
RenderState { RenderState {
gbuffer_pass: g_buffer_pass,
gbuffer_cam_projection_buffer: g_buffer_camera_projection_uniform,
gbuffer_depth_texture: g_buffer_depth_texture
.create_view(&wgpu::TextureViewDescriptor::default()),
gbuffer_depth_views: g_buffer_depth_texture_views,
gbuffer_target_texture: g_buffer_data_texture
.create_view(&wgpu::TextureViewDescriptor::default()),
gbuffer_target_views: g_buffer_data_texture_views,
swapchain: swap_chain, swapchain: swap_chain,
queue: queue, queue: queue,
size, size,
@@ -640,7 +982,8 @@ impl RenderState {
lights_are_dirty: true, lights_are_dirty: true,
shadow_pass, shadow_pass,
forward_pass, forward_pass,
forward_depth: depth_texture.create_view(&wgpu::TextureViewDescriptor::default()), forward_depth: forward_depth_texture
.create_view(&wgpu::TextureViewDescriptor::default()),
entity_bind_group_layout: entity_bind_group_layout, entity_bind_group_layout: entity_bind_group_layout,
shadow_target_views: shadow_target_views, shadow_target_views: shadow_target_views,
light_uniform_buf, light_uniform_buf,
@@ -695,9 +1038,8 @@ impl RenderState {
} }
} }
/* /*
*/ */

View File

@@ -1,37 +1,42 @@
use std::{iter, num::NonZeroU32, ops::Range, rc::Rc};
use std::cell::RefCell; use std::cell::RefCell;
use std::iter::Chain;
use std::slice::Iter;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::thread::current; use std::thread::current;
use std::time::Duration; use std::time::Duration;
use std::{iter, num::NonZeroU32, ops::Range, rc::Rc};
use bytemuck::__core::mem;
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
use bytemuck::__core::mem;
use cgmath::{ use cgmath::{
vec3, Decomposed, Deg, Euler, InnerSpace, Matrix4, Point3, Quaternion, Rad, Rotation3, Decomposed, Deg, Euler, InnerSpace, Matrix4, Point3, Quaternion, Rad, Rotation3, Transform,
Transform, Vector3, vec3, Vector3,
}; };
use futures::executor::LocalPool; use futures::executor::LocalPool;
use imgui::sys::ImGuiContext;
use imgui::*; use imgui::*;
use imgui::sys::ImGuiContext;
use imgui_wgpu::{Renderer as ImguiRenderer, RendererConfig as ImguiRendererConfig}; use imgui_wgpu::{Renderer as ImguiRenderer, RendererConfig as ImguiRendererConfig};
use legion::world::SubWorld;
use legion::*; use legion::*;
use legion::world::SubWorld;
use rapier3d::parry::motion::RigidMotionComposition; use rapier3d::parry::motion::RigidMotionComposition;
use wgpu::{
BackendBit, BindGroup, BindGroupLayout, Buffer, BufferBindingType, CommandEncoder, Device,
FragmentState, Instance, Queue, Surface, SwapChain, SwapChainDescriptor, SwapChainFrame,
TextureView, VertexState,
};
use wgpu::util::DeviceExt; use wgpu::util::DeviceExt;
use wgpu::{BackendBit, BindGroup, BindGroupLayout, Buffer, BufferBindingType, Device, FragmentState, Instance, Queue, Surface, SwapChain, SwapChainDescriptor, SwapChainFrame, TextureView, VertexState, CommandEncoder};
use winit_24::dpi::PhysicalSize; use winit_24::dpi::PhysicalSize;
use winit_24::platform::unix::x11::ffi::Time; use winit_24::platform::unix::x11::ffi::Time;
use winit_24::window::Window; use winit_24::window::Window;
use crate::camera::{Camera, CameraController}; use crate::camera::{Camera, CameraController};
use crate::components::{Mesh, Position, RangeCopy}; use crate::components::{ImguiWindow, LoopState, Mesh, Position, RangeCopy};
use crate::current_ui; use crate::current_ui;
use crate::geometry::{load_obj, Vertex}; use crate::geometry::{load_obj, Vertex};
use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform}; use crate::imgui_supp::imgui_support::{ImguiContext, ImguiPlatform};
use crate::light::{DirectionalLight, LightRaw}; use crate::light::{DirectionalLight, LightRaw};
use crate::render::state::{RenderState}; use crate::render::{EntityUniforms, ForwardUniforms, insert_debug_marker_checked, pop_debug_group_checked, push_debug_group_checked};
use crate::render::{push_debug_group_checked, insert_debug_marker_checked, pop_debug_group_checked, EntityUniforms}; use crate::render::state::RenderState;
#[system] #[system]
#[write_component(Camera)] #[write_component(Camera)]
@@ -53,14 +58,129 @@ pub fn imgui_prepare(
unsafe { crate::CURRENT_UI = Some(std::mem::transmute(imgui_context.frame())) } unsafe { crate::CURRENT_UI = Some(std::mem::transmute(imgui_context.frame())) }
} }
fn run_imgui_render_step<G: 'static + Sized + Send + Sync>(world: &mut SubWorld, ui: &Ui) {
let mut component_query = <(&G)>::query();
let mut window_query = <(&ImguiWindow<G>)>::query();
let mut window_data = None;
let mut window_func = None;
for (window) in window_query.iter(world) {
window_data = Some((window.window)());
window_func = Some(window.func);
}
if window_data.is_some() {
let mut v = Vec::new();
for (component_state) in component_query.iter(world) {
v.push(component_state)
}
window_data
.unwrap()
.build(&ui, || (window_func.unwrap())(ui, v));
}
}
/// Go through each "global" window-data component and render it's data
#[system]
#[write_component(ImguiWindow < ImguiPerformanceProfilerLine >)]
#[write_component(ImguiPerformanceProfilerLine)]
#[write_component(ImguiWindow < ImguiGenericOutputLine >)]
#[write_component(ImguiGenericOutputLine)]
pub fn render_imgui(world: &mut SubWorld, #[resource] loop_state: &mut LoopState) {
let ui = unsafe { crate::current_ui().unwrap() };
// Pull out the window associated with this type, and render each of the components in the sytem
run_imgui_render_step::<ImguiGenericOutputLine>(world, &ui);
run_imgui_render_step::<ImguiPerformanceProfilerLine>(world, &ui);
}
// This would be the shared state for all imgui generic output things
pub struct ImguiGenericOutputLine {
pub label: String,
}
impl ImguiGenericOutputLine {
pub fn new(label: String) -> ImguiGenericOutputLine {
ImguiGenericOutputLine { label }
}
}
// This would be the shared state for all imgui performance window things
pub struct ImguiPerformanceProfilerLine {
pub label: String,
list_of_fps: [f32; 400],
index: usize,
scale_min: f32,
pub scale_max: f32,
}
impl ImguiPerformanceProfilerLine {
fn add_sample(&mut self, sample: f32) {
self.list_of_fps[self.index] = sample;
if self.index >= 399 {
self.scale_max = self.list_of_fps.iter().cloned().fold(0. / 0., f32::max);
self.index = 0;
} else {
self.index += 1;
}
if self.scale_max < sample {
self.scale_max = sample;
}
}
pub fn iter_data_from_head(&self) -> Chain<Iter<f32>, Iter<f32>> {
let (left, right) = self.list_of_fps.split_at(self.index);
right.iter().chain(left.iter())
}
pub fn current_average_label(&self) -> (f32, String) {
let (left, right) = self.list_of_fps.split_at(self.index);
(
(left
.iter()
.rev()
.chain(right.iter().rev())
.take(50)
.sum::<f32>()
/ 50.0),
"FPS".to_string(),
)
}
pub fn new(label: String) -> ImguiPerformanceProfilerLine {
ImguiPerformanceProfilerLine {
label,
list_of_fps: [0.0; 400],
index: 0,
scale_min: 0.0,
scale_max: 0.0,
}
}
}
#[system]
#[write_component(ImguiPerformanceProfilerLine)]
pub fn render_performance_flag(world: &mut SubWorld, #[resource] loop_state: &mut LoopState) {
let delta_time = loop_state.delta_time.as_secs_f32();
let mut query = <(&mut ImguiPerformanceProfilerLine)>::query();
for (mut profiler) in query.iter_mut(world) {
profiler.add_sample(delta_time);
}
}
#[system] #[system]
#[write_component(Camera)] #[write_component(Camera)]
#[write_component(Position)] #[write_component(Position)]
#[write_component(Point3<f32>)] #[write_component(ImguiPerformanceProfilerLine)]
#[write_component(Point3 < f32 >)]
#[write_component(Mesh)] #[write_component(Mesh)]
#[write_component(DirectionalLight)] #[write_component(DirectionalLight)]
pub fn render_test( pub fn render_test(
world: &mut SubWorld, world: &mut SubWorld,
#[resource] loop_state: &mut LoopState,
#[resource] renderer: &mut RenderState, #[resource] renderer: &mut RenderState,
#[resource] winit_window: &mut Window, #[resource] winit_window: &mut Window,
#[resource] imgui_context: &mut Arc<Mutex<ImguiContext>>, #[resource] imgui_context: &mut Arc<Mutex<ImguiContext>>,
@@ -72,7 +192,6 @@ pub fn render_test(
push_debug_group_checked("start render function", &mut encoder); push_debug_group_checked("start render function", &mut encoder);
let frame = renderer.get_current_frame(); let frame = renderer.get_current_frame();
// Update the camera uniform buffers, need to make it support selection of // Update the camera uniform buffers, need to make it support selection of
@@ -115,25 +234,76 @@ pub fn render_test(
mesh_stack.push(mesh.clone()); mesh_stack.push(mesh.clone());
} }
// Update the light uniforms only if flagged
if renderer.lights_are_dirty { if renderer.lights_are_dirty {
renderer.lights_are_dirty = false; renderer.lights_are_dirty = false;
let mut query = <(&mut DirectionalLight, &mut Point3<f32>)>::query(); let mut query = <(&mut DirectionalLight, &mut Position)>::query();
for (i, (light, pos)) in query.iter_mut(world).enumerate() { for (i, (light, pos)) in query.iter_mut(world).enumerate() {
renderer.queue.write_buffer( renderer.queue.write_buffer(
&renderer.light_uniform_buf, &renderer.light_uniform_buf,
(i * mem::size_of::<LightRaw>()) as wgpu::BufferAddress, (i * mem::size_of::<LightRaw>()) as wgpu::BufferAddress,
bytemuck::bytes_of(&light.to_raw()), bytemuck::bytes_of(&light.to_raw(&pos)),
); );
} }
} }
// Render the g buffer
push_debug_group_checked("g-buffer stuff", &mut encoder);
{
// insert_debug_marker_checked("render entities", &mut encoder);
// The render pass for the g buffer.. Does it need any attachments
// the shadow pass uses a target view as the input, which are just views on
// top of the shadow texture. So the attachment is a RW buffer which clears on load
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("render pass"),
color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &renderer.gbuffer_target_views.get(0).unwrap(),
resolve_target: None,
ops: Default::default(),
},
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &renderer.gbuffer_target_views.get(1).unwrap(),
resolve_target: None,
ops: Default::default(),
},
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &renderer.gbuffer_target_views.get(2).unwrap(),
resolve_target: None,
ops: Default::default(),
}],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &renderer.gbuffer_target_views.get(3).unwrap(),
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
});
pass.set_pipeline(&renderer.gbuffer_pass.pipeline);
pass.set_bind_group(0, &renderer.gbuffer_pass.bind_group, &[]);
// for mesh in &mesh_stack {
// pass.set_bind_group(1, &mesh.bind_group, &[]);
// // TODO, pipe through this index format through the mesh
// pass.set_index_buffer(mesh.index_buffer.slice(..), mesh.index_format);
// pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
// pass.draw_indexed(0..mesh.index_count as u32, 0, 0..1);
// }
}
pop_debug_group_checked(&mut encoder);
// Render the shadow textures
push_debug_group_checked("shadow passes", &mut encoder); push_debug_group_checked("shadow passes", &mut encoder);
let mut query = <(&mut DirectionalLight, &mut Position)>::query();
let mut query = <(&mut DirectionalLight, &mut Point3<f32>)>::query();
for (i, (light, pos)) in query.iter_mut(world).enumerate() { for (i, (light, pos)) in query.iter_mut(world).enumerate() {
insert_debug_marker_checked(&format!("shadow pass {} (light at position {:?})", i, pos), &mut encoder); insert_debug_marker_checked(
&format!("shadow pass {} (light at position {:?})", i, pos),
&mut encoder,
);
// The light uniform buffer already has the projection, // The light uniform buffer already has the projection,
// let's just copy it over to the shadow uniform buffer. // let's just copy it over to the shadow uniform buffer.
@@ -147,7 +317,7 @@ pub fn render_test(
insert_debug_marker_checked("render entities", &mut encoder); insert_debug_marker_checked("render entities", &mut encoder);
//
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("render pass"), label: Some("render pass"),
color_attachments: &[], color_attachments: &[],
@@ -171,11 +341,10 @@ pub fn render_test(
pass.draw_indexed(0..mesh.index_count as u32, 0, 0..1); pass.draw_indexed(0..mesh.index_count as u32, 0, 0..1);
} }
} }
pop_debug_group_checked(&mut encoder); pop_debug_group_checked(&mut encoder);
// forward pass
push_debug_group_checked("forward rendering pass", &mut encoder);
// And then render the viewport
push_debug_group_checked("forward rendering pass", &mut encoder);
{ {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("forward render pass"), label: Some("forward render pass"),
@@ -206,6 +375,7 @@ pub fn render_test(
let mut query = <(&mut Position, &mut Mesh)>::query(); let mut query = <(&mut Position, &mut Mesh)>::query();
// I could use that mesh stack here lol
for (pos, mesh) in query.iter_mut(world) { for (pos, mesh) in query.iter_mut(world) {
pass.set_bind_group(1, &mesh.bind_group, &[]); pass.set_bind_group(1, &mesh.bind_group, &[]);
// TODO: Pipe this in through the mesh // TODO: Pipe this in through the mesh
@@ -214,24 +384,18 @@ pub fn render_test(
pass.draw_indexed(0..mesh.index_count as u32, 0, 0..1); pass.draw_indexed(0..mesh.index_count as u32, 0, 0..1);
} }
} }
pop_debug_group_checked(&mut encoder);
pop_debug_group_checked(&mut encoder); pop_debug_group_checked(&mut encoder);
// Run the Imgui render
push_debug_group_checked("imgui rendering pass", &mut encoder);
{ {
let mut imgui_context = &mut imgui_context.lock().unwrap().context; let mut imgui_context = &mut imgui_context.lock().unwrap().context;
let mut imgui_platform = &mut imgui_platform.lock().unwrap().platform; let mut imgui_platform = &mut imgui_platform.lock().unwrap().platform;
//imgui_state.context.io_mut().update_delta_time(Duration::new(0,160));
let ui = unsafe { crate::current_ui().unwrap() }; let ui = unsafe { crate::current_ui().unwrap() };
let window = imgui::Window::new(im_str!("Hello too"));
window
.size([400.0, 100.0], Condition::FirstUseEver)
.position([50.0, 50.0], Condition::FirstUseEver)
.build(&ui, || {
ui.text(im_str!("Frametime: {:?}", 10.0));
});
// ui.show_demo_window(&mut true); // ui.show_demo_window(&mut true);
imgui_platform.prepare_render(&ui, &winit_window); imgui_platform.prepare_render(&ui, &winit_window);
@@ -260,6 +424,10 @@ pub fn render_test(
.render(draw_data, &renderer.queue, &renderer.device, &mut rpass) .render(draw_data, &renderer.queue, &renderer.device, &mut rpass)
.expect("Rendering failed"); .expect("Rendering failed");
} }
pop_debug_group_checked(&mut encoder);
// the startfunction debug group
pop_debug_group_checked(&mut encoder);
renderer.queue.submit(iter::once(encoder.finish())); renderer.queue.submit(iter::once(encoder.finish()));
} }

View File

@@ -17,11 +17,55 @@ use crate::components::{Collider, LoopState, Mesh, Physics, Position};
use crate::geometry::{load_obj, RawMesh}; use crate::geometry::{load_obj, RawMesh};
use std::io::Read; use std::io::Read;
pub struct EntityMeta {
#[derive(Deserialize, Clone)]
pub struct TomlBallPhysicsBodyDescription {
pub radius: String
}
#[derive(Deserialize, Clone)]
pub struct TomlCuboidPhysicsBodyDescription {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Deserialize, Clone)]
pub struct TomlPhysicsDescription {
pub body_status: String,
pub ball: Option<TomlBallPhysicsBodyDescription>,
pub cuboid: Option<TomlCuboidPhysicsBodyDescription>,
}
#[derive(Deserialize, Clone)]
pub struct TomlRotationDescription {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Deserialize, Clone)]
pub struct TomlPositionDescription {
pub x: f32,
pub y: f32,
pub z: f32,
pub rot: Option<TomlRotationDescription>,
}
#[derive(Deserialize, Clone)]
pub struct TomlEntityDescription {
pub name: String, pub name: String,
pub ent_type: String, #[serde(rename = "type")]
pub type_name: String,
pub mesh: Option<String>, pub mesh: Option<String>,
pub position: Option<Position> pub position: Option<TomlPositionDescription>,
pub physics: Option<TomlPhysicsDescription>,
}
#[derive(Deserialize, Clone)]
pub struct TomlEntityContainer {
pub entities: Vec<TomlEntityDescription>,
} }
pub struct RuntimeState { pub struct RuntimeState {
@@ -29,38 +73,15 @@ pub struct RuntimeState {
mesh_cache: HashMap<String, RawMesh>, mesh_cache: HashMap<String, RawMesh>,
} }
#[derive(Deserialize)]
pub struct TomlPositionDescription {
pub x: f32,
pub y: f32,
pub z: f32,
}
#[derive(Deserialize)]
pub struct TomlEntityDescription {
pub name: String,
#[serde(rename = "type")]
pub type_name: String,
pub mesh: Option<String>,
pub position: Option<TomlPositionDescription>,
}
#[derive(Deserialize)]
pub struct TomlEntityContainer {
pub entities: Vec<TomlEntityDescription>,
}
impl RuntimeState { impl RuntimeState {
pub fn new() -> RuntimeState { pub fn new() -> RuntimeState {
// TODO: Hook this file to the gui
let mut file = fs::File::open("./conf/entity_spawns.toml").unwrap(); let mut file = fs::File::open("./conf/entity_spawns.toml").unwrap();
let mut content = String::new(); let mut content = String::new();
file.read_to_string(&mut content).unwrap(); file.read_to_string(&mut content).unwrap();
// TODO: gracefully fail
let mut settings : TomlEntityContainer = toml::from_str(content.as_str()).unwrap(); let mut settings : TomlEntityContainer = toml::from_str(content.as_str()).unwrap();
// settings
// // File::with_name(..) is shorthand for File::from(Path::new(..))
// .merge(File::with_name("conf/entity_spawns.toml"))
// .unwrap();
RuntimeState { RuntimeState {
config_db: settings, config_db: settings,
@@ -72,40 +93,8 @@ impl RuntimeState {
self.mesh_cache.get(mesh) self.mesh_cache.get(mesh)
} }
pub fn get_configured_entities(&mut self) -> Vec<EntityMeta> { pub fn get_entities(&mut self) -> Vec<TomlEntityDescription> {
let mut out = Vec::new(); self.config_db.entities.clone()
for entity in &self.config_db.entities {
let position = match &entity.position {
None => { None }
Some(pos) => {
Some(Position {
x: pos.x,
y: pos.y,
z: pos.z,
rot: Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0)
}
})
}
};
// log::info!("{:?}", position);
// log::info!("{:?}", entity);
// log::info!("{:?}", entity.into_table());
// log::info!("{:?}", entity.into_array());
out.push(EntityMeta {
name: entity.name.clone(),
ent_type: entity.type_name.clone(),
mesh: entity.mesh.clone(),
position: position
});
}
out
} }
pub fn preload_meshes(&mut self, resources_path: PathBuf) { pub fn preload_meshes(&mut self, resources_path: PathBuf) {

View File

@@ -1,33 +1,119 @@
use std::f32::consts::PI;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Instant; use std::time::Instant;
use cgmath::{Euler, Quaternion, Deg, Rad, Point3}; use cgmath::{Deg, Euler, Point3, Quaternion, Rad};
use imgui::FontSource; use imgui::*;
use legion::*; use imgui::{Condition, FontSource, Ui};
use legion::IntoQuery;
use legion::systems::CommandBuffer; use legion::systems::CommandBuffer;
use legion::world::SubWorld; use legion::world::SubWorld;
use legion::IntoQuery;
use legion::*;
use nalgebra::Quaternion as naQuaternion; use nalgebra::Quaternion as naQuaternion;
use rapier3d::dynamics::{IntegrationParameters, JointSet, RigidBodyBuilder, RigidBodySet}; use rapier3d::dynamics::{
IntegrationParameters, JointSet, MassProperties, RigidBodyBuilder, RigidBodySet,
};
use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase}; use rapier3d::geometry::{BroadPhase, ColliderBuilder, ColliderSet, NarrowPhase};
use rapier3d::na::{Isometry3, Vector, Vector3}; use rapier3d::na::{Isometry3, Vector, Vector3};
use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline}; use rapier3d::pipeline::{ChannelEventCollector, PhysicsPipeline};
use crate::camera::{Camera, CameraController}; use crate::camera::{Camera, CameraController};
use crate::components::{Collider, LoopState, Mesh, Physics, Position}; use crate::components::{Collider, ImguiWindow, LoopState, Mesh, Physics, Position};
use crate::geometry::RawMesh; use crate::geometry::RawMesh;
use crate::physics::state::PhysicsState; use crate::physics::state::PhysicsState;
use crate::render::state::RenderState; use crate::render::state::RenderState;
use crate::render::system::{ImguiGenericOutputLine, ImguiPerformanceProfilerLine};
use crate::runtime::state::RuntimeState; use crate::runtime::state::RuntimeState;
use std::f32::consts::PI;
pub fn quad_color(color: [f32; 4]) -> [[f32; 4]; 4] {
[color, color, color, color]
}
#[system] #[system]
#[write_component(Mesh)] #[write_component(Mesh)]
pub fn runtime_load( pub fn runtime_load(
cmd: &mut CommandBuffer,
world: &mut SubWorld, world: &mut SubWorld,
#[resource] runtime_state: &mut RuntimeState, #[resource] runtime_state: &mut RuntimeState,
) { ) {
runtime_state.preload_meshes(PathBuf::from("./resources")); runtime_state.preload_meshes(PathBuf::from("./resources"));
let entity: Entity = cmd.push((ImguiWindow {
// a window that does everything for the performance profiler
window: || {
imgui::Window::new(im_str!("Performance Profiler"))
.size([400.0, 200.0], Condition::FirstUseEver)
.position([10.0, 10.0], Condition::FirstUseEver)
},
func: |ui: &Ui, a: Vec<&ImguiPerformanceProfilerLine>| {
// ui.text(im_str!("Performance Graph"));
let draw_list = ui.get_window_draw_list();
let top_left = ui.cursor_screen_pos();
let region_size = ui.content_region_avail();
let region_size = [region_size[0] * 0.80, region_size[1]];
// Fill rect
let qcolor = quad_color([0.5, 0.5, 1.0, 0.1]);
draw_list.add_rect_filled_multicolor(
top_left,
[top_left[0] + (region_size[0]), top_left[1] + region_size[1]],
qcolor[0],
qcolor[1],
qcolor[2],
qcolor[3],
);
for profiler_line in a {
let x_scale = (region_size[0]) / 400.0;
let y_scale = region_size[1] / profiler_line.scale_max;
profiler_line
.iter_data_from_head()
.fold((0, 0.0f32), |accum, &fps_val| {
let x1 = accum.0 as f32 * x_scale + top_left[0];
let y1 = top_left[1] + region_size[1] - accum.1 * y_scale;
let x2 = (accum.0 as f32 + 1.0) * x_scale + top_left[0];
let y2 = top_left[1] + region_size[1] - fps_val * y_scale;
let p1 = [x1, y1];
let p2 = [x2, y2];
draw_list
.add_line(p1, p2, [1.0, 1.0, 0.0, 0.8])
.thickness(1.0)
.build();
(accum.0 + 1, fps_val)
});
let text_x = (region_size[0] + top_left[0]);
let text_y = top_left[1] + region_size[1]
- profiler_line.current_average_label().0 * y_scale;
draw_list.add_text(
[text_x, text_y],
[1.0, 1.0, 0.0, 1.0],
format!(
"{} {:.0}",
profiler_line.current_average_label().1,
1.0 / profiler_line.current_average_label().0
),
);
}
},
},));
let entity: Entity = cmd.push((ImguiPerformanceProfilerLine::new("RenderFPS".to_string()),));
let entity: Entity = cmd.push((ImguiWindow {
// a window that does everything for the performance profiler
window: || {
imgui::Window::new(im_str!("Generic Output"))
.size([400.0, 500.0], Condition::FirstUseEver)
.position([50.0, 250.0], Condition::FirstUseEver)
},
func: |ui: &Ui, a: Vec<&ImguiGenericOutputLine>| {
for label in a {
ui.text(im_str!("{}", label.label));
}
},
},));
} }
#[system] #[system]
@@ -38,54 +124,48 @@ pub fn runtime_spawn(
#[resource] runtime_state: &mut RuntimeState, #[resource] runtime_state: &mut RuntimeState,
#[resource] renderer: &mut RenderState, #[resource] renderer: &mut RenderState,
) { ) {
for entity in &runtime_state.get_entities() {
for entity in runtime_state.get_configured_entities(){ match entity.type_name.as_ref() {
match entity.ent_type.as_ref() {
"PhysicsEntity" => { "PhysicsEntity" => {
let mesh_name = entity.mesh.unwrap(); let mesh_name = entity.mesh.as_ref().unwrap();
let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) { let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
None => { None => {
log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name); log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
continue; continue;
} }
Some(mesh) => mesh Some(mesh) => mesh,
}; };
let mut dynamic_body = RigidBodyBuilder::new_dynamic() let collider = ColliderBuilder::trimesh(
.can_sleep(false) raw_mesh.vertices.iter().map(|v| v.position()).collect(),
.mass(1.0) raw_mesh.indices.clone(),
.translation(0.0, 0.0, 0.0) )
.build(); .build();
let collider = ColliderBuilder::trimesh( let collider = ColliderBuilder::capsule_y(2.0, 1.0).build();
raw_mesh.vertices
.iter()
.map(|v| v.position())
.collect(),
raw_mesh.indices.clone(),
).build();
let gpu_mesh_buffer = renderer.upload_mesh_to_buffer( let gpu_mesh_buffer = renderer
.upload_mesh_to_buffer(
raw_mesh, raw_mesh,
Some(wgpu::Color { Some(wgpu::Color {
r: 1.0, r: 1.0,
g: 0.7, g: 0.7,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
}) }),
).unwrap(); )
.unwrap();
let position = Position::from(entity.position.clone());
let mut dynamic_body = RigidBodyBuilder::new_dynamic()
.can_sleep(false)
.mass(100.0)
.translation(position.x, position.y, position.z)
.build();
let entity: Entity = cmd.push(( let entity: Entity = cmd.push((
Position { position,
x: 0.0,
y: 20.0,
z: 0.0,
rot: Euler {
x: Deg(25.0),
y: Deg(45.0),
z: Deg(15.0),
},
},
gpu_mesh_buffer, gpu_mesh_buffer,
Physics { Physics {
rigid_body: dynamic_body, rigid_body: dynamic_body,
@@ -95,52 +175,48 @@ pub fn runtime_spawn(
collider: collider, collider: collider,
collider_handle: None, collider_handle: None,
}, },
ImguiGenericOutputLine::new("wahoo! from a physics entity".to_string()),
)); ));
}, }
"Terrain" => { "Terrain" => {
let mesh_name = entity.mesh.unwrap(); let mesh_name = entity.mesh.clone().unwrap();
let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) { let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
None => { None => {
log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name); log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
continue; continue;
} }
Some(mesh) => mesh Some(mesh) => mesh,
}; };
let position = Position::from(entity.position.clone());
let mut static_body = RigidBodyBuilder::new_static() let mut static_body = RigidBodyBuilder::new_static()
.position(Isometry3::new(Vector3::new(0.0, -8.0, 0.0), Vector::y())) .position(Isometry3::new(
Vector3::new(position.x, position.y, position.z),
Vector::y(),
))
.build(); .build();
let mesh_collider = ColliderBuilder::trimesh( let mesh_collider = ColliderBuilder::trimesh(
raw_mesh.vertices raw_mesh.vertices.iter().map(|v| v.position()).collect(),
.iter()
.map(|v| v.position())
.collect(),
raw_mesh.indices.clone(), raw_mesh.indices.clone(),
).build(); )
.build();
let gpu_mesh_buffer = renderer.upload_mesh_to_buffer( let gpu_mesh_buffer = renderer
.upload_mesh_to_buffer(
raw_mesh, raw_mesh,
Some(wgpu::Color { Some(wgpu::Color {
r: 1.0, r: 1.0,
g: 0.7, g: 0.7,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
}) }),
).unwrap(); )
.unwrap();
let entity: Entity = cmd.push(( let entity: Entity = cmd.push((
Position { position,
x: 0.0,
y: -8.0,
z: 0.0,
rot: Euler {
x: Deg(0.0),
y: Deg(0.0),
z: Deg(0.0),
},
},
gpu_mesh_buffer, gpu_mesh_buffer,
Physics { Physics {
rigid_body: static_body, rigid_body: static_body,
@@ -151,63 +227,52 @@ pub fn runtime_spawn(
collider_handle: None, collider_handle: None,
}, },
)); ));
}, }
"Camera" => { "Camera" => {
let position = Position::from(entity.position.clone());
let entity: Entity = cmd.push(( let entity: Entity = cmd.push((
Camera { Camera {
position: cgmath::Point3 { position: cgmath::Point3 {
x: 0.0, x: position.x,
y: 0.0, y: position.y,
z: 10.0, z: position.z,
}, },
yaw: Rad(-PI), yaw: Rad(PI / 2.0),
pitch: Rad(PI / 2.0), pitch: Rad(PI / 2.0 + 25.0),
}, },
CameraController::new(3.0, 1.0), CameraController::new(5.0, 1.0),
ImguiGenericOutputLine::new("wahoo! from a camera".to_string()),
)); ));
}, }
"Light" => { "Light" => {
let mesh_name = entity.mesh.unwrap(); let mesh_name = entity.mesh.clone().unwrap();
let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) { let raw_mesh = match runtime_state.get_mesh(mesh_name.as_str()) {
None => { None => {
log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name); log::warn!("Skipping entity with invalid mesh file {:?} ", mesh_name);
continue; continue;
} }
Some(mesh) => mesh Some(mesh) => mesh,
}; };
let gpu_mesh_buffer = renderer.upload_mesh_to_buffer( let position = Position::from(entity.position.clone());
let gpu_mesh_buffer = renderer
.upload_mesh_to_buffer(
raw_mesh, raw_mesh,
Some(wgpu::Color { Some(wgpu::Color {
r: 1.0, r: 1.0,
g: 0.7, g: 0.7,
b: 0.3, b: 0.3,
a: 1.0, a: 1.0,
}) }),
).unwrap(); )
.unwrap();
let light_entity: Entity = cmd.push(( let light_entity: Entity =
Position { cmd.push((position, gpu_mesh_buffer, renderer.create_light()));
x: 5.0,
y: 10.0,
z: 5.0,
rot: Euler {
x: Deg(0.0),
y: Deg(-25.0),
z: Deg(0.0),
},
},
gpu_mesh_buffer,
renderer.create_light(),
));
} }
_ => {}
_ => {},
} }
} }
} }