Merge branch 'feature/simple-lighting' into 'main'
ci/woodpecker/push/build Pipeline was successful
Details
ci/woodpecker/push/build Pipeline was successful
Details
This commit is contained in:
commit
a288c9b26c
|
@ -1142,6 +1142,15 @@ dependencies = [
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itertools"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
|
||||||
|
dependencies = [
|
||||||
|
"either",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.9"
|
version = "1.0.9"
|
||||||
|
@ -1290,6 +1299,7 @@ dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"image",
|
"image",
|
||||||
"instant",
|
"instant",
|
||||||
|
"itertools",
|
||||||
"lyra-resource",
|
"lyra-resource",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"quote",
|
"quote",
|
||||||
|
|
|
@ -37,4 +37,5 @@ aligned-vec = "0.5.0"
|
||||||
tracing-appender = "0.2.2"
|
tracing-appender = "0.2.2"
|
||||||
stopwatch = "0.0.7"
|
stopwatch = "0.0.7"
|
||||||
petgraph = "0.6.4"
|
petgraph = "0.6.4"
|
||||||
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
uuid = { version = "1.5.0", features = ["v4", "fast-rng"] }
|
||||||
|
itertools = "0.11.0"
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub struct FreeFlyCamera {
|
||||||
impl Default for FreeFlyCamera {
|
impl Default for FreeFlyCamera {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
speed: 3.0,
|
speed: 4.0,
|
||||||
look_speed: 0.09,
|
look_speed: 0.09,
|
||||||
mouse_sensitivity: 0.4,
|
mouse_sensitivity: 0.4,
|
||||||
look_with_keys: false,
|
look_with_keys: false,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::window::{CursorGrabMode, WindowOptions}, change_tracker::Ct};
|
use lyra_engine::{math::{self, Vec3}, ecs::{World, components::{transform::TransformComponent, camera::CameraComponent, model::ModelComponent, DeltaTime}, EventQueue, SimpleSystem, Component, Criteria, CriteriaSchedule, BatchedSystem}, math::Transform, input::{KeyCode, InputButtons, MouseMotion, ActionHandler, Layout, Action, ActionKind, LayoutId, ActionMapping, Binding, ActionSource, ActionMappingId, InputActionPlugin, ActionState}, game::Game, plugin::Plugin, render::{window::{CursorGrabMode, WindowOptions}, light::PointLight}, change_tracker::Ct};
|
||||||
use lyra_engine::assets::{ResourceManager, Model};
|
use lyra_engine::assets::{ResourceManager, Model};
|
||||||
|
|
||||||
mod free_fly_camera;
|
mod free_fly_camera;
|
||||||
|
@ -69,7 +69,7 @@ async fn main() {
|
||||||
let mut resman = world.get_resource_mut::<ResourceManager>().unwrap();
|
let mut resman = world.get_resource_mut::<ResourceManager>().unwrap();
|
||||||
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
//let diffuse_texture = resman.request::<Texture>("assets/happy-tree.png").unwrap();
|
||||||
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
let antique_camera_model = resman.request::<Model>("assets/AntiqueCamera.glb").unwrap();
|
||||||
//let cube_model = resman.request::<Model>("assets/texture-sep/texture-sep.gltf").unwrap();
|
let cube_model = resman.request::<Model>("assets/cube-texture-bin.glb").unwrap();
|
||||||
drop(resman);
|
drop(resman);
|
||||||
|
|
||||||
/* world.spawn((
|
/* world.spawn((
|
||||||
|
@ -82,6 +82,38 @@ async fn main() {
|
||||||
TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)),
|
TransformComponent::from(Transform::from_xyz(0.0, -5.0, -10.0)),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
/* let light = PointLight {
|
||||||
|
color: Vec3::new(1.0, 1.0, 1.0),
|
||||||
|
position: Vec3::new(0.0, -5.0, -8.0),
|
||||||
|
constant: 1.0,
|
||||||
|
linear: 0.09,
|
||||||
|
quadratic: 0.032,
|
||||||
|
};
|
||||||
|
world.spawn((light,)); */
|
||||||
|
world.spawn((
|
||||||
|
PointLight {
|
||||||
|
color: Vec3::new(1.0, 1.0, 1.0),
|
||||||
|
intensity: 1.0,
|
||||||
|
constant: 1.0,
|
||||||
|
linear: 0.045,
|
||||||
|
quadratic: 0.0075,
|
||||||
|
},
|
||||||
|
TransformComponent::from(Transform::from_xyz(-2.5, 0.0, -10.0)),
|
||||||
|
ModelComponent(cube_model.clone()),
|
||||||
|
));
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
PointLight {
|
||||||
|
color: Vec3::new(0.361, 0.984, 0.0),
|
||||||
|
intensity: 1.0,
|
||||||
|
constant: 1.0,
|
||||||
|
linear: 0.045,
|
||||||
|
quadratic: 0.0075,
|
||||||
|
},
|
||||||
|
TransformComponent::from(Transform::from_xyz(2.5, 0.0, -10.0)),
|
||||||
|
ModelComponent(cube_model),
|
||||||
|
));
|
||||||
|
|
||||||
let mut camera = CameraComponent::new_3d();
|
let mut camera = CameraComponent::new_3d();
|
||||||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 7.5);
|
camera.transform.translation += math::Vec3::new(0.0, 0.0, 7.5);
|
||||||
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
//camera.transform.rotate_y(Angle::Degrees(-25.0));
|
||||||
|
|
|
@ -35,6 +35,23 @@ impl Projection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct CameraUniform {
|
||||||
|
pub view_proj: glam::Mat4,
|
||||||
|
// vec4 is used because of the uniforms 16 byte spacing requirement
|
||||||
|
pub view_pos: glam::Vec4,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CameraUniform {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
view_proj: glam::Mat4::IDENTITY,
|
||||||
|
view_pos: Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct RenderCamera {
|
pub struct RenderCamera {
|
||||||
view_proj: glam::Mat4,
|
view_proj: glam::Mat4,
|
||||||
|
|
|
@ -0,0 +1,209 @@
|
||||||
|
pub mod point;
|
||||||
|
use std::{collections::{VecDeque, HashMap}, num::NonZeroU64, marker::PhantomData};
|
||||||
|
|
||||||
|
use edict::query::EpochOf;
|
||||||
|
pub use point::*;
|
||||||
|
use tracing::debug;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use crate::{math::Transform, ecs::components::TransformComponent};
|
||||||
|
|
||||||
|
const MAX_LIGHT_COUNT: usize = 32;
|
||||||
|
|
||||||
|
pub struct LightBuffer<U: Default + bytemuck::Pod + bytemuck::Zeroable> {
|
||||||
|
_phantom: PhantomData<U>,
|
||||||
|
/// The max amount of light casters that could fit in this buffer.
|
||||||
|
pub max_count: usize,
|
||||||
|
/// The current amount of light casters in this buffer.
|
||||||
|
pub current_count: usize,
|
||||||
|
/// The buffer index for a specific entity/caster.
|
||||||
|
used_indexes: HashMap<edict::EntityId, usize>,
|
||||||
|
/// Indexes that were being used but are no longer needed.
|
||||||
|
dead_indexes: VecDeque<usize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: Default + bytemuck::Pod + bytemuck::Zeroable> LightBuffer<U> {
|
||||||
|
pub fn new(layout: &wgpu::BindGroupLayout, max_count: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
_phantom: PhantomData::default(),
|
||||||
|
max_count,
|
||||||
|
current_count: 0,
|
||||||
|
used_indexes: HashMap::new(),
|
||||||
|
dead_indexes: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_light(&self, entity: edict::EntityId) -> bool {
|
||||||
|
self.used_indexes.contains_key(&entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update an existing light in the light buffer.
|
||||||
|
pub fn update_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) {
|
||||||
|
let buffer_idx = *self.used_indexes.get(&entity)
|
||||||
|
.expect("Entity for Light is not in buffer!");
|
||||||
|
|
||||||
|
lights_buffer[buffer_idx] = light;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new light to the light buffer.
|
||||||
|
pub fn add_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) {
|
||||||
|
let buffer_idx = match self.dead_indexes.pop_front() {
|
||||||
|
Some(i) => i,
|
||||||
|
None => {
|
||||||
|
let i = self.current_count;
|
||||||
|
self.current_count += 1;
|
||||||
|
i
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
self.used_indexes.insert(entity, buffer_idx);
|
||||||
|
self.update_light(lights_buffer, entity, light);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update, or add a new caster, to the light buffer.
|
||||||
|
pub fn update_or_add(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId, light: U) {
|
||||||
|
if self.used_indexes.contains_key(&entity) {
|
||||||
|
self.update_light(lights_buffer, entity, light);
|
||||||
|
} else {
|
||||||
|
self.add_light(lights_buffer, entity, light);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a caster from the buffer, returns true if it was removed.
|
||||||
|
pub fn remove_light(&mut self, lights_buffer: &mut [U; MAX_LIGHT_COUNT], entity: edict::EntityId) -> bool {
|
||||||
|
if let Some(removed_idx) = self.used_indexes.remove(&entity) {
|
||||||
|
self.dead_indexes.push_back(removed_idx);
|
||||||
|
self.current_count -= 1;
|
||||||
|
lights_buffer[removed_idx] = U::default();
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LightUniformBuffers {
|
||||||
|
pub buffer: wgpu::Buffer,
|
||||||
|
pub bindgroup_layout: wgpu::BindGroupLayout,
|
||||||
|
pub bindgroup: wgpu::BindGroup,
|
||||||
|
pub lights_uniform: LightsUniform,
|
||||||
|
pub point_lights: LightBuffer<PointLightUniform>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LightUniformBuffers {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let buffer = device.create_buffer(
|
||||||
|
&wgpu::BufferDescriptor {
|
||||||
|
label: Some("Lights Uniform buffer"),
|
||||||
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
|
size: mem::size_of::<LightsUniform>() as u64,
|
||||||
|
mapped_at_creation: false,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let bindgroup_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Buffer {
|
||||||
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
|
has_dynamic_offset: false,
|
||||||
|
min_binding_size: None,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("lights_bind_group_layout"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let bindgroup = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
layout: &bindgroup_layout,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::Buffer(
|
||||||
|
wgpu::BufferBinding {
|
||||||
|
buffer: &buffer,
|
||||||
|
offset: 0,
|
||||||
|
size: Some(NonZeroU64::new(mem::size_of::<LightsUniform>() as u64).unwrap())
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
],
|
||||||
|
label: Some("light_bind_group"),
|
||||||
|
});
|
||||||
|
|
||||||
|
let point_lights = LightBuffer::new(&bindgroup_layout, MAX_LIGHT_COUNT);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
buffer,
|
||||||
|
bindgroup_layout,
|
||||||
|
bindgroup,
|
||||||
|
lights_uniform: LightsUniform::default(),
|
||||||
|
point_lights,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_lights(&mut self, queue: &wgpu::Queue, world_epoch: edict::epoch::EpochId, world: &edict::World) {
|
||||||
|
for (entity, point_light, transform, light_epoch, transform_epoch)
|
||||||
|
in world.query::<(edict::Entities, &PointLight, &TransformComponent, EpochOf<PointLight>, EpochOf<TransformComponent>)>().iter() {
|
||||||
|
|
||||||
|
if !self.point_lights.has_light(entity) || light_epoch == world_epoch || transform_epoch == world_epoch {
|
||||||
|
let uniform = PointLightUniform::from_bundle(point_light, &transform.transform);
|
||||||
|
self.point_lights.update_or_add(&mut self.lights_uniform.point_lights, entity, uniform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.lights_uniform.point_light_count = self.point_lights.current_count as u32;
|
||||||
|
queue.write_buffer(&self.buffer, 0, bytemuck::cast_slice(&[self.lights_uniform]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct LightsUniform {
|
||||||
|
point_lights: [PointLightUniform; MAX_LIGHT_COUNT],
|
||||||
|
point_light_count: u32,
|
||||||
|
_padding: [u32; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
|
pub struct PointLightUniform {
|
||||||
|
/// The position of the light
|
||||||
|
/// vec4 is used here for gpu padding, w is ignored in the shader
|
||||||
|
pub position: glam::Vec4,
|
||||||
|
/// The color of the light
|
||||||
|
/// vec4 is used here for gpu padding, w is ignored in the shader
|
||||||
|
pub color: glam::Vec4,
|
||||||
|
/// The intensity of the light
|
||||||
|
/// This works by just multiplying the result of the lighting
|
||||||
|
/// calculations by this scalar
|
||||||
|
pub intensity: f32,
|
||||||
|
/// The constant used in the quadratic attenuation calculation. Its best to leave this at 1.0
|
||||||
|
pub constant: f32,
|
||||||
|
/// The linear factor used in the quadratic attenuation calculation.
|
||||||
|
pub linear: f32,
|
||||||
|
/// The quadratic factor used in the quadratic attenuation calculation.
|
||||||
|
pub quadratic: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PointLightUniform {
|
||||||
|
/// Create the PointLightUniform from an ECS bundle
|
||||||
|
pub fn from_bundle(light: &PointLight, transform: &Transform) -> Self {
|
||||||
|
Self {
|
||||||
|
position: glam::Vec4::new(transform.translation.x, transform.translation.y, transform.translation.z, 0.0),
|
||||||
|
//_padding: 0,
|
||||||
|
color: glam::Vec4::new(light.color.x, light.color.y, light.color.z, 0.0),
|
||||||
|
//_padding2: 0,
|
||||||
|
intensity: light.intensity,
|
||||||
|
constant: light.constant,
|
||||||
|
linear: light.linear,
|
||||||
|
quadratic: light.quadratic,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Copy, Clone, edict::Component)]
|
||||||
|
pub struct PointLight {
|
||||||
|
pub color: glam::Vec3,
|
||||||
|
pub intensity: f32,
|
||||||
|
pub constant: f32,
|
||||||
|
pub linear: f32,
|
||||||
|
pub quadratic: f32,
|
||||||
|
}
|
|
@ -10,4 +10,5 @@ pub mod shader_loader;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
pub mod camera;
|
pub mod camera;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
pub mod transform_buffer_storage;
|
pub mod transform_buffer_storage;
|
||||||
|
pub mod light;
|
|
@ -8,6 +8,7 @@ use edict::query::EpochOf;
|
||||||
use edict::{EntityId, Entities};
|
use edict::{EntityId, Entities};
|
||||||
use glam::Vec3;
|
use glam::Vec3;
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
|
use itertools::izip;
|
||||||
use tracing::{debug, warn};
|
use tracing::{debug, warn};
|
||||||
use wgpu::{BindGroup, BindGroupLayout, Limits};
|
use wgpu::{BindGroup, BindGroupLayout, Limits};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
@ -16,10 +17,12 @@ use winit::window::Window;
|
||||||
use crate::ecs::components::camera::CameraComponent;
|
use crate::ecs::components::camera::CameraComponent;
|
||||||
use crate::ecs::components::model::ModelComponent;
|
use crate::ecs::components::model::ModelComponent;
|
||||||
use crate::ecs::components::transform::TransformComponent;
|
use crate::ecs::components::transform::TransformComponent;
|
||||||
use crate::math::Transform;
|
use crate::math::{Transform, self};
|
||||||
|
use crate::render::light::PointLightUniform;
|
||||||
|
|
||||||
use super::camera::RenderCamera;
|
use super::camera::{RenderCamera, CameraUniform};
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
|
use super::light::{PointLight, LightUniformBuffers};
|
||||||
use super::texture::RenderTexture;
|
use super::texture::RenderTexture;
|
||||||
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
|
use super::transform_buffer_storage::{TransformBufferIndices, TransformBuffers};
|
||||||
use super::vertex::Vertex;
|
use super::vertex::Vertex;
|
||||||
|
@ -87,6 +90,8 @@ pub struct BasicRenderer {
|
||||||
texture_bind_group_layout: BindGroupLayout,
|
texture_bind_group_layout: BindGroupLayout,
|
||||||
default_texture_bind_group: BindGroup,
|
default_texture_bind_group: BindGroup,
|
||||||
depth_buffer_texture: RenderTexture,
|
depth_buffer_texture: RenderTexture,
|
||||||
|
|
||||||
|
light_buffers: LightUniformBuffers,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicRenderer {
|
impl BasicRenderer {
|
||||||
|
@ -239,7 +244,7 @@ impl BasicRenderer {
|
||||||
let camera_buffer = device.create_buffer_init(
|
let camera_buffer = device.create_buffer_init(
|
||||||
&wgpu::util::BufferInitDescriptor {
|
&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("Camera Buffer"),
|
label: Some("Camera Buffer"),
|
||||||
contents: bytemuck::cast_slice(&[glam::Mat4::IDENTITY]),
|
contents: bytemuck::cast_slice(&[CameraUniform::default()]),
|
||||||
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -248,7 +253,7 @@ impl BasicRenderer {
|
||||||
entries: &[
|
entries: &[
|
||||||
wgpu::BindGroupLayoutEntry {
|
wgpu::BindGroupLayoutEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
visibility: wgpu::ShaderStages::VERTEX,
|
visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
|
||||||
ty: wgpu::BindingType::Buffer {
|
ty: wgpu::BindingType::Buffer {
|
||||||
ty: wgpu::BufferBindingType::Uniform,
|
ty: wgpu::BufferBindingType::Uniform,
|
||||||
has_dynamic_offset: false,
|
has_dynamic_offset: false,
|
||||||
|
@ -293,6 +298,8 @@ impl BasicRenderer {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let light_uniform_buffers = LightUniformBuffers::new(&device);
|
||||||
|
|
||||||
let mut s = Self {
|
let mut s = Self {
|
||||||
window,
|
window,
|
||||||
surface,
|
surface,
|
||||||
|
@ -323,13 +330,16 @@ impl BasicRenderer {
|
||||||
default_texture_bind_group: default_tex_bindgroup,
|
default_texture_bind_group: default_tex_bindgroup,
|
||||||
depth_buffer_texture: depth_texture,
|
depth_buffer_texture: depth_texture,
|
||||||
entity_last_transforms: HashMap::new(),
|
entity_last_transforms: HashMap::new(),
|
||||||
|
|
||||||
|
light_buffers: light_uniform_buffers,
|
||||||
};
|
};
|
||||||
|
|
||||||
// create the default pipelines
|
// create the default pipelines
|
||||||
let mut pipelines = HashMap::new();
|
let mut pipelines = HashMap::new();
|
||||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&s.device, &s.config, &shader,
|
||||||
vec![super::vertex::Vertex::desc(),],
|
vec![super::vertex::Vertex::desc(),],
|
||||||
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout])));
|
vec![&s.texture_bind_group_layout, &s.transform_bind_group_layout, &camera_bind_group_layout,
|
||||||
|
&s.light_buffers.bindgroup_layout])));
|
||||||
s.render_pipelines = pipelines;
|
s.render_pipelines = pipelines;
|
||||||
|
|
||||||
s
|
s
|
||||||
|
@ -376,12 +386,14 @@ impl BasicRenderer {
|
||||||
let positions = mesh.position().unwrap();
|
let positions = mesh.position().unwrap();
|
||||||
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
let tex_coords: Vec<glam::Vec2> = mesh.tex_coords().cloned()
|
||||||
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
|
.unwrap_or_else(|| vec![glam::Vec2::new(0.0, 0.0); positions.len()]);
|
||||||
|
let normals = mesh.normals().unwrap();
|
||||||
|
|
||||||
assert!(positions.len() == tex_coords.len());
|
assert!(positions.len() == tex_coords.len() && positions.len() == normals.len());
|
||||||
|
|
||||||
let vertex_inputs: Vec<Vertex> = std::iter::zip(positions, tex_coords)
|
let mut vertex_inputs = vec![];
|
||||||
.map(|(v, t)| Vertex::new(*v, t))
|
for (v, t, n) in izip!(positions.iter(), tex_coords.iter(), normals.iter()) {
|
||||||
.collect();
|
vertex_inputs.push(Vertex::new(*v, *t, *n));
|
||||||
|
}
|
||||||
|
|
||||||
let vertex_buffer = self.device.create_buffer_init(
|
let vertex_buffer = self.device.create_buffer_init(
|
||||||
&wgpu::util::BufferInitDescriptor {
|
&wgpu::util::BufferInitDescriptor {
|
||||||
|
@ -518,6 +530,7 @@ impl BasicRenderer {
|
||||||
impl Renderer for BasicRenderer {
|
impl Renderer for BasicRenderer {
|
||||||
fn prepare(&mut self, main_world: &mut edict::World) {
|
fn prepare(&mut self, main_world: &mut edict::World) {
|
||||||
let last_epoch = main_world.epoch();
|
let last_epoch = main_world.epoch();
|
||||||
|
debug!("Last epoch: {last_epoch:?}");
|
||||||
|
|
||||||
let mut alive_entities = HashSet::new();
|
let mut alive_entities = HashSet::new();
|
||||||
|
|
||||||
|
@ -588,10 +601,17 @@ impl Renderer for BasicRenderer {
|
||||||
|
|
||||||
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
|
if let Some(camera) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
|
||||||
let view_proj = self.inuse_camera.update_view_projection(camera);
|
let view_proj = self.inuse_camera.update_view_projection(camera);
|
||||||
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[*view_proj]));
|
let pos = camera.transform.translation;
|
||||||
|
let uniform = CameraUniform {
|
||||||
|
view_proj: *view_proj,
|
||||||
|
view_pos: glam::Vec4::new(pos.x, pos.y, pos.z, 0.0),
|
||||||
|
};
|
||||||
|
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniform]));
|
||||||
} else {
|
} else {
|
||||||
warn!("Missing camera!");
|
warn!("Missing camera!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.light_buffers.update_lights(&self.queue, last_epoch, &main_world);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
fn render(&mut self) -> Result<(), wgpu::SurfaceError> {
|
||||||
|
@ -650,6 +670,12 @@ impl Renderer for BasicRenderer {
|
||||||
|
|
||||||
// Bind camera
|
// Bind camera
|
||||||
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
|
||||||
|
|
||||||
|
// bind light
|
||||||
|
//render_pass.set_bind_group(3, &self.point_light_bind_group, &[]);
|
||||||
|
render_pass.set_bind_group(3, &self.light_buffers.bindgroup, &[]);
|
||||||
|
////self.light_buffers.bind_lights(&mut render_pass, 3);
|
||||||
|
|
||||||
|
|
||||||
// if this mesh uses indices, use them to draw the mesh
|
// if this mesh uses indices, use them to draw the mesh
|
||||||
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
if let Some((idx_type, indices)) = buffers.buffer_indices.as_ref() {
|
||||||
|
|
|
@ -1,32 +1,63 @@
|
||||||
// Vertex shader
|
// Vertex shader
|
||||||
|
|
||||||
|
const max_light_count: u32 = 32u;
|
||||||
|
|
||||||
struct VertexInput {
|
struct VertexInput {
|
||||||
@location(0) position: vec3<f32>,
|
@location(0) position: vec3<f32>,
|
||||||
@location(1) tex_coords: vec2<f32>,
|
@location(1) tex_coords: vec2<f32>,
|
||||||
|
@location(2) normal: vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct VertexOutput {
|
struct VertexOutput {
|
||||||
@builtin(position) clip_position: vec4<f32>,
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
@location(0) tex_coords: vec2<f32>,
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
@location(1) world_position: vec3<f32>,
|
||||||
|
@location(2) world_normal: vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CameraUniform {
|
struct CameraUniform {
|
||||||
view_proj: mat4x4<f32>,
|
view_proj: mat4x4<f32>,
|
||||||
|
view_pos: vec4<f32>,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PointLight {
|
||||||
|
position: vec4<f32>,
|
||||||
|
color: vec4<f32>,
|
||||||
|
intensity: f32,
|
||||||
|
constant: f32,
|
||||||
|
linear: f32,
|
||||||
|
quadratic: f32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Lights {
|
||||||
|
point_lights: array<PointLight, max_light_count>,
|
||||||
|
point_light_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
@group(1) @binding(0)
|
@group(1) @binding(0)
|
||||||
var<uniform> u_model_transform: mat4x4<f32>;
|
var<uniform> u_model_transform: mat4x4<f32>;
|
||||||
|
|
||||||
@group(2) @binding(0)
|
@group(2) @binding(0)
|
||||||
var<uniform> camera: CameraUniform;
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
|
||||||
|
@group(3) @binding(0)
|
||||||
|
var<uniform> u_lights: Lights;
|
||||||
|
|
||||||
@vertex
|
@vertex
|
||||||
fn vs_main(
|
fn vs_main(
|
||||||
model: VertexInput,
|
model: VertexInput,
|
||||||
) -> VertexOutput {
|
) -> VertexOutput {
|
||||||
var out: VertexOutput;
|
var out: VertexOutput;
|
||||||
|
|
||||||
out.tex_coords = model.tex_coords;
|
out.tex_coords = model.tex_coords;
|
||||||
out.clip_position = camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
|
out.clip_position = u_camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
|
||||||
|
|
||||||
|
out.world_normal = (u_model_transform * vec4<f32>(model.normal, 0.0)).xyz;
|
||||||
|
|
||||||
|
//out.world_normal = model.normal;
|
||||||
|
var world_position: vec4<f32> = u_model_transform * vec4<f32>(model.position, 1.0);
|
||||||
|
out.world_position = world_position.xyz;
|
||||||
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,5 +70,50 @@ var s_diffuse: sampler;
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
return textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||||
|
|
||||||
|
var light_res = vec3<f32>(0.0);
|
||||||
|
for (var i = 0u; i < u_lights.point_light_count; i++) {
|
||||||
|
light_res += blinn_phong_point_light(in.world_position, in.world_normal, u_lights.point_lights[i]);
|
||||||
|
}
|
||||||
|
let light_object_res = light_res * object_color.xyz;
|
||||||
|
|
||||||
|
return vec4<f32>(light_object_res, object_color.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn blinn_phong_point_light(world_pos: vec3<f32>, world_norm: vec3<f32>, point_light: PointLight) -> vec3<f32> {
|
||||||
|
let light_color = point_light.color.xyz;
|
||||||
|
let light_pos = point_light.position.xyz;
|
||||||
|
let camera_view_pos = u_camera.view_pos.xyz;
|
||||||
|
|
||||||
|
// We don't need (or want) much ambient light, so 0.1 is fine
|
||||||
|
let ambient_strength = 0.1;
|
||||||
|
var ambient_color = light_color * ambient_strength;
|
||||||
|
|
||||||
|
//// diffuse ////
|
||||||
|
let light_dir = normalize(light_pos - world_pos);
|
||||||
|
|
||||||
|
let diffuse_strength = max(dot(world_norm, light_dir), 0.0);
|
||||||
|
var diffuse_color = light_color * diffuse_strength;
|
||||||
|
//// end of diffuse ////
|
||||||
|
|
||||||
|
//// specular ////
|
||||||
|
let view_dir = normalize(camera_view_pos - world_pos);
|
||||||
|
let half_dir = normalize(view_dir + light_dir);
|
||||||
|
|
||||||
|
let specular_strength = pow(max(dot(world_norm, half_dir), 0.0), 32.0);
|
||||||
|
var specular_color = specular_strength * light_color;
|
||||||
|
//// end of specular ////
|
||||||
|
|
||||||
|
//// point light attenuation ////
|
||||||
|
let distance = length(light_pos - world_pos);
|
||||||
|
let attenuation = 1.0 / (point_light.constant + point_light.linear * distance +
|
||||||
|
point_light.quadratic * (distance * distance));
|
||||||
|
|
||||||
|
ambient_color *= attenuation * point_light.intensity;
|
||||||
|
diffuse_color *= attenuation * point_light.intensity;
|
||||||
|
specular_color *= attenuation * point_light.intensity;
|
||||||
|
//// end of point light attenuation ////
|
||||||
|
|
||||||
|
return (ambient_color + diffuse_color + specular_color);
|
||||||
|
}
|
|
@ -4,14 +4,15 @@ use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pub position: glam::Vec3,
|
pub position: glam::Vec3,
|
||||||
pub tex_coords: glam::Vec2
|
pub tex_coords: glam::Vec2,
|
||||||
|
pub normals: glam::Vec3,
|
||||||
//pub color: [f32; 3], // TODO: add color again
|
//pub color: [f32; 3], // TODO: add color again
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Vertex {
|
impl Vertex {
|
||||||
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2) -> Self {
|
pub fn new(position: glam::Vec3, tex_coords: glam::Vec2, normals: glam::Vec3) -> Self {
|
||||||
Self {
|
Self {
|
||||||
position, tex_coords
|
position, tex_coords, normals
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +32,11 @@ impl DescVertexBufferLayout for Vertex {
|
||||||
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||||
shader_location: 1,
|
shader_location: 1,
|
||||||
format: wgpu::VertexFormat::Float32x2, // Vec2
|
format: wgpu::VertexFormat::Float32x2, // Vec2
|
||||||
|
},
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: std::mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
|
||||||
|
shader_location: 2,
|
||||||
|
format: wgpu::VertexFormat::Float32x3, // Vec3
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue