add simple 3d camera, rename Model2dComponent to MeshComponent, rename base shader file

This commit is contained in:
SeanOMik 2023-05-15 01:02:45 -04:00
parent f8d2f7c3f1
commit 5d8cb19212
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
10 changed files with 180 additions and 28 deletions

View File

@ -10,16 +10,23 @@ struct VertexOutput {
@location(0) tex_coords: vec2<f32>,
}
struct CameraUniform {
view_proj: mat4x4<f32>,
};
@group(1) @binding(0)
var<uniform> u_model_transform: mat4x4<f32>;
@group(2) @binding(0)
var<uniform> camera: CameraUniform;
@vertex
fn vs_main(
model: VertexInput,
) -> VertexOutput {
var out: VertexOutput;
out.tex_coords = model.tex_coords;
out.clip_position = u_model_transform * vec4<f32>(model.position, 1.0);
out.clip_position = camera.view_proj * u_model_transform * vec4<f32>(model.position, 1.0);
return out;
}

56
src/ecs/components/camera.rs Executable file
View File

@ -0,0 +1,56 @@
use winit::dpi::PhysicalSize;
use crate::math::{Angle, OPENGL_TO_WGPU_MATRIX};
pub struct CameraComponent {
eye: glam::Vec3,
target: glam::Vec3,
up: glam::Vec3,
aspect_ratio: Option<f32>,
fov_y: Angle,
znear: f32,
zfar: f32,
}
impl Default for CameraComponent {
fn default() -> Self {
Self {
// position the camera one unit up and 2 units back
// +z is out of the screen
eye: (0.0, 1.0, 2.0).into(),
// have it look at the origin
target: (0.0, 0.0, 0.0).into(),
// which way is "up"
up: (0.0, 1.0, 0.0).into(),
aspect_ratio: None,//config.width as f32 / config.height as f32,
fov_y: Angle::Degrees(45.0),
znear: 0.1,
zfar: 100.0,
}
}
}
impl CameraComponent {
pub fn new() -> Self {
Self::default()
}
/// Set the camera aspect ratio. This should only be used internally.
pub(crate) fn set_aspect_ratio(&mut self, size: PhysicalSize<u32>) {
self.aspect_ratio = Some(size.width as f32 / size.height as f32);
}
/// Check if the camera is ready to be used in rendering.
pub fn is_ready(&self) -> bool {
self.aspect_ratio.is_some()
}
pub fn get_view_projection_matrix(&self) -> glam::Mat4 {
let aspect_ratio = self.aspect_ratio.expect("ERROR: Camera aspect ratio was not set!");
let view = glam::Mat4::look_at_rh(self.eye, self.target, self.up);
let projection = glam::Mat4::perspective_rh_gl(self.fov_y.to_radians(), aspect_ratio, self.znear, self.zfar);
OPENGL_TO_WGPU_MATRIX * projection * view
}
}

View File

@ -3,12 +3,12 @@ use specs::{Component, DenseVecStorage};
use crate::render::{vertex::Vertex, mesh::Mesh, material::Material};
#[derive(Component, Clone)]
pub struct Model2dComponent {
pub struct MeshComponent {
pub mesh: Mesh,
pub material: Material,
}
impl Model2dComponent {
impl MeshComponent {
pub fn new(mesh: Mesh, material: Material) -> Self {
Self {
mesh,

View File

@ -1,2 +1,3 @@
pub mod model_2d;
pub mod transform;
pub mod mesh;
pub mod transform;
pub mod camera;

View File

@ -12,7 +12,7 @@ use tracing_subscriber::{
use winit::{window::{WindowBuilder, Window}, event::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode}, event_loop::{EventLoop, ControlFlow}};
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::components::{model_2d::Model2dComponent, transform::TransformComponent}};
use crate::{render::{renderer::{Renderer, BasicRenderer}, render_job::RenderJob}, input_event::InputEvent, ecs::components::{mesh::MeshComponent, transform::TransformComponent}};
struct GameLoop {
window: Arc<Window>,
@ -122,15 +122,15 @@ impl GameLoop {
let mut world = self.world.lock().await;
for (ent, (transform,)) in world.query_mut::<(&mut TransformComponent,)>() {
/* for (ent, (transform,)) in world.query_mut::<(&mut TransformComponent,)>() {
transform.transform.translate_vec3(glam::Vec3::new(0.0, 0.001, 0.0));
}
for (ent, (transform,)) in world.query::<(&TransformComponent,)>().iter() {
println!("transform pos: {:?}", transform.transform);
}
} */
self.renderer.as_mut().prepare(&world).await;
self.renderer.as_mut().prepare(&mut world).await;
drop(world);
match self.renderer.as_mut().render().await {

View File

@ -5,7 +5,7 @@ mod resources;
mod ecs;
mod math;
use ecs::components::model_2d::Model2dComponent;
use ecs::components::mesh::MeshComponent;
use ecs::components::transform::TransformComponent;
use game::Game;
@ -13,6 +13,7 @@ use hecs::World;
use crate::render::material::Material;
use crate::render::texture::Texture;
use crate::ecs::components::camera::CameraComponent;
//use specs::*;
#[derive(Debug, Default)]
@ -63,11 +64,11 @@ impl Point3d {
async fn main() {
let mut world = World::new();
world.spawn((Point2d::new(10, 10), Point3d::new(50, 50, 50)));
//world.spawn((Point2d::new(10, 10), Point3d::new(50, 50, 50)));
let diffuse_bytes = include_bytes!("../res/happy-tree.png");
let diffuse_texture = Texture::from_bytes(diffuse_bytes).unwrap();
world.spawn((Model2dComponent::new(
world.spawn((MeshComponent::new(
render::mesh::Mesh {
vertices: crate::render::vertex::VERTICES.to_vec(),
indices: Some(crate::render::vertex::INDICES.to_vec())
@ -78,9 +79,7 @@ async fn main() {
TransformComponent::new(),
));
for (id, (point2d,)) in world.query::<(&Point2d,)>().iter() {
println!("Entity {} is at 2d position: {:?}", id.id(), point2d);
}
world.spawn((CameraComponent::new(),));
Game::initialize().await
.with_world(world)

View File

@ -5,4 +5,13 @@ pub mod angle;
pub use angle::*;
pub mod transform;
pub use transform::*;
pub use transform::*;
/// A matrix used to convert an OpenGL matrix to a matrix compatible with Wgpu
#[rustfmt::skip]
pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = glam::Mat4::from_cols_array(&[
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.5, 0.0,
0.0, 0.0, 0.5, 1.0,
]);

21
src/render/camera.rs Executable file
View File

@ -0,0 +1,21 @@
use winit::dpi::PhysicalSize;
use crate::{math::{Angle, OPENGL_TO_WGPU_MATRIX}, ecs::components::camera::CameraComponent};
#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Camera {
projection: glam::Mat4
}
impl Camera {
pub fn new() -> Self {
Self {
projection: glam::Mat4::IDENTITY,
}
}
pub fn update_view_projection(&mut self, camera: &CameraComponent) {
self.projection = camera.get_view_projection_matrix().into();
}
}

View File

@ -7,4 +7,5 @@ pub mod render_job;
pub mod mesh;
pub mod texture;
pub mod shader_loader;
pub mod material;
pub mod material;
pub mod camera;

View File

@ -6,25 +6,27 @@ use std::borrow::Cow;
use async_std::sync::Mutex;
use async_trait::async_trait;
use tracing::debug;
use tracing::{debug, warn};
use wgpu::{BindGroup, BindGroupLayout};
use wgpu::util::DeviceExt;
use winit::window::Window;
use hecs::{World, Entity};
use crate::ecs::components::model_2d::Model2dComponent;
use crate::ecs::components::camera::CameraComponent;
use crate::ecs::components::mesh::MeshComponent;
use crate::ecs::components::transform::TransformComponent;
use crate::math::Transform;
use crate::resources;
use super::camera::Camera;
use super::desc_buf_lay::DescVertexBufferLayout;
use super::texture::RenderTexture;
use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
#[async_trait]
pub trait Renderer {
async fn prepare(&mut self, main_world: &World);
async fn prepare(&mut self, main_world: &mut World);
async fn render(&mut self) -> Result<(), wgpu::SurfaceError>;
async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
@ -56,8 +58,12 @@ pub struct BasicRenderer {
buffer_storage: HashMap<Entity, RenderBufferStorage>, // TODO: clean up left over buffers from deleted entities/components
transform_bind_group: wgpu::BindGroup,
transform_buffer: wgpu::Buffer,
transform_bind_group: wgpu::BindGroup,
inuse_camera: Camera,
camera_buffer: wgpu::Buffer,
camera_bind_group: wgpu::BindGroup,
}
impl BasicRenderer {
@ -140,7 +146,7 @@ impl BasicRenderer {
label: Some("texture_bind_group_layout"),
});
let shader_src = resources::load_string("shader/base_2d.wgsl").await.expect("Failed to load shader!");
let shader_src = resources::load_string("shader/base.wgsl").await.expect("Failed to load shader!");
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)),
@ -181,9 +187,45 @@ impl BasicRenderer {
label: Some("transform_bind_group"),
});
let camera_buffer = device.create_buffer_init(
&wgpu::util::BufferInitDescriptor {
label: Some("Camera Buffer"),
contents: bytemuck::cast_slice(&[Camera::new()]),
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
}
);
let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}
],
label: Some("camera_bind_group_layout"),
});
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &camera_bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: camera_buffer.as_entire_binding(),
}
],
label: Some("camera_bind_group"),
});
let mut pipelines = HashMap::new();
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader,
vec![super::vertex::Vertex::desc(),], vec![&texture_bind_group_layout, &transform_bind_group_layout])));
vec![super::vertex::Vertex::desc(),],
vec![&texture_bind_group_layout, &transform_bind_group_layout, &camera_bind_group_layout])));
Self {
window,
@ -204,10 +246,14 @@ impl BasicRenderer {
transform_buffer,
transform_bind_group,
inuse_camera: Camera::new(),
camera_buffer,
camera_bind_group,
}
}
fn create_model_buffers(&mut self, model_2d: &Model2dComponent) -> RenderBufferStorage {
fn create_model_buffers(&mut self, model_2d: &MeshComponent) -> RenderBufferStorage {
let mesh = &model_2d.mesh;
let vertex_buffer = self.device.create_buffer_init(
@ -294,8 +340,8 @@ impl BasicRenderer {
#[async_trait]
impl Renderer for BasicRenderer {
async fn prepare(&mut self, main_world: &World) {
for (entity, (model, transform)) in main_world.query::<(&Model2dComponent, Option<&TransformComponent>)>().iter() {
async fn prepare(&mut self, main_world: &mut World) {
for (entity, (model, transform)) in main_world.query::<(&MeshComponent, Option<&TransformComponent>)>().iter() {
let transform = match transform {
Some(transform) => {
Some(transform.transform)
@ -315,10 +361,19 @@ impl Renderer for BasicRenderer {
if self.buffer_storage.get(&entity).is_none() {
let buffers = self.create_model_buffers(model);
self.buffer_storage.insert(entity, buffers);
} else {
}
}
if let Some((_e, (camera,))) = main_world.query_mut::<(&mut CameraComponent,)>().into_iter().next() {
//if !camera.is_ready() {
camera.set_aspect_ratio(self.size);
//}
self.inuse_camera.update_view_projection(camera);
self.queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[self.inuse_camera]));
} else {
warn!("Missing camera!");
}
}
fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc<FullRenderPipeline>) {
@ -369,6 +424,9 @@ impl Renderer for BasicRenderer {
} else {
debug!("//TODO: clear transform uniform if the RenderJob doesn't have a transform.");
}
// There will always be a camera (hopefully)
render_pass.set_bind_group(2, &self.camera_bind_group, &[]);
if let Some(indices) = buffers.buffer_indices.as_ref() {
let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug