add simple 3d camera, rename Model2dComponent to MeshComponent, rename base shader file
This commit is contained in:
parent
77d29d68c8
commit
3fc8cefa0e
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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,
|
|
@ -1,2 +1,3 @@
|
|||
pub mod model_2d;
|
||||
pub mod mesh;
|
||||
pub mod transform;
|
||||
pub mod camera;
|
|
@ -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 {
|
||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -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)
|
||||
|
|
|
@ -6,3 +6,12 @@ pub use angle::*;
|
|||
|
||||
pub mod 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,
|
||||
]);
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -8,3 +8,4 @@ pub mod mesh;
|
|||
pub mod texture;
|
||||
pub mod shader_loader;
|
||||
pub mod material;
|
||||
pub mod camera;
|
|
@ -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>) {
|
||||
|
@ -370,6 +425,9 @@ impl Renderer for BasicRenderer {
|
|||
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
|
||||
|
||||
|
|
Loading…
Reference in New Issue