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>,
|
@location(0) tex_coords: vec2<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CameraUniform {
|
||||||
|
view_proj: mat4x4<f32>,
|
||||||
|
};
|
||||||
|
|
||||||
@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)
|
||||||
|
var<uniform> camera: CameraUniform;
|
||||||
|
|
||||||
@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 = 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;
|
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};
|
use crate::render::{vertex::Vertex, mesh::Mesh, material::Material};
|
||||||
|
|
||||||
#[derive(Component, Clone)]
|
#[derive(Component, Clone)]
|
||||||
pub struct Model2dComponent {
|
pub struct MeshComponent {
|
||||||
pub mesh: Mesh,
|
pub mesh: Mesh,
|
||||||
pub material: Material,
|
pub material: Material,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Model2dComponent {
|
impl MeshComponent {
|
||||||
pub fn new(mesh: Mesh, material: Material) -> Self {
|
pub fn new(mesh: Mesh, material: Material) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mesh,
|
mesh,
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod model_2d;
|
pub mod mesh;
|
||||||
pub mod transform;
|
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 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 {
|
struct GameLoop {
|
||||||
window: Arc<Window>,
|
window: Arc<Window>,
|
||||||
|
@ -122,15 +122,15 @@ impl GameLoop {
|
||||||
|
|
||||||
let mut world = self.world.lock().await;
|
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));
|
transform.transform.translate_vec3(glam::Vec3::new(0.0, 0.001, 0.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ent, (transform,)) in world.query::<(&TransformComponent,)>().iter() {
|
for (ent, (transform,)) in world.query::<(&TransformComponent,)>().iter() {
|
||||||
println!("transform pos: {:?}", transform.transform);
|
println!("transform pos: {:?}", transform.transform);
|
||||||
}
|
} */
|
||||||
|
|
||||||
self.renderer.as_mut().prepare(&world).await;
|
self.renderer.as_mut().prepare(&mut world).await;
|
||||||
drop(world);
|
drop(world);
|
||||||
|
|
||||||
match self.renderer.as_mut().render().await {
|
match self.renderer.as_mut().render().await {
|
||||||
|
|
11
src/main.rs
11
src/main.rs
|
@ -5,7 +5,7 @@ mod resources;
|
||||||
mod ecs;
|
mod ecs;
|
||||||
mod math;
|
mod math;
|
||||||
|
|
||||||
use ecs::components::model_2d::Model2dComponent;
|
use ecs::components::mesh::MeshComponent;
|
||||||
|
|
||||||
use ecs::components::transform::TransformComponent;
|
use ecs::components::transform::TransformComponent;
|
||||||
use game::Game;
|
use game::Game;
|
||||||
|
@ -13,6 +13,7 @@ use hecs::World;
|
||||||
|
|
||||||
use crate::render::material::Material;
|
use crate::render::material::Material;
|
||||||
use crate::render::texture::Texture;
|
use crate::render::texture::Texture;
|
||||||
|
use crate::ecs::components::camera::CameraComponent;
|
||||||
//use specs::*;
|
//use specs::*;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
|
@ -63,11 +64,11 @@ impl Point3d {
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let mut world = World::new();
|
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_bytes = include_bytes!("../res/happy-tree.png");
|
||||||
let diffuse_texture = Texture::from_bytes(diffuse_bytes).unwrap();
|
let diffuse_texture = Texture::from_bytes(diffuse_bytes).unwrap();
|
||||||
world.spawn((Model2dComponent::new(
|
world.spawn((MeshComponent::new(
|
||||||
render::mesh::Mesh {
|
render::mesh::Mesh {
|
||||||
vertices: crate::render::vertex::VERTICES.to_vec(),
|
vertices: crate::render::vertex::VERTICES.to_vec(),
|
||||||
indices: Some(crate::render::vertex::INDICES.to_vec())
|
indices: Some(crate::render::vertex::INDICES.to_vec())
|
||||||
|
@ -78,9 +79,7 @@ async fn main() {
|
||||||
TransformComponent::new(),
|
TransformComponent::new(),
|
||||||
));
|
));
|
||||||
|
|
||||||
for (id, (point2d,)) in world.query::<(&Point2d,)>().iter() {
|
world.spawn((CameraComponent::new(),));
|
||||||
println!("Entity {} is at 2d position: {:?}", id.id(), point2d);
|
|
||||||
}
|
|
||||||
|
|
||||||
Game::initialize().await
|
Game::initialize().await
|
||||||
.with_world(world)
|
.with_world(world)
|
||||||
|
|
|
@ -5,4 +5,13 @@ pub mod angle;
|
||||||
pub use angle::*;
|
pub use angle::*;
|
||||||
|
|
||||||
pub mod transform;
|
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,
|
||||||
|
]);
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,4 +7,5 @@ pub mod render_job;
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
pub mod shader_loader;
|
pub mod shader_loader;
|
||||||
pub mod material;
|
pub mod material;
|
||||||
|
pub mod camera;
|
|
@ -6,25 +6,27 @@ use std::borrow::Cow;
|
||||||
use async_std::sync::Mutex;
|
use async_std::sync::Mutex;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use tracing::debug;
|
use tracing::{debug, warn};
|
||||||
use wgpu::{BindGroup, BindGroupLayout};
|
use wgpu::{BindGroup, BindGroupLayout};
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use hecs::{World, Entity};
|
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::ecs::components::transform::TransformComponent;
|
||||||
use crate::math::Transform;
|
use crate::math::Transform;
|
||||||
use crate::resources;
|
use crate::resources;
|
||||||
|
|
||||||
|
use super::camera::Camera;
|
||||||
use super::desc_buf_lay::DescVertexBufferLayout;
|
use super::desc_buf_lay::DescVertexBufferLayout;
|
||||||
use super::texture::RenderTexture;
|
use super::texture::RenderTexture;
|
||||||
use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
|
use super::{render_pipeline::FullRenderPipeline, vertex::{VERTICES}, render_buffer::BufferStorage, render_job::RenderJob, mesh::Mesh};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Renderer {
|
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 render(&mut self) -> Result<(), wgpu::SurfaceError>;
|
||||||
async fn on_resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>);
|
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
|
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_buffer: wgpu::Buffer,
|
||||||
|
transform_bind_group: wgpu::BindGroup,
|
||||||
|
|
||||||
|
inuse_camera: Camera,
|
||||||
|
camera_buffer: wgpu::Buffer,
|
||||||
|
camera_bind_group: wgpu::BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicRenderer {
|
impl BasicRenderer {
|
||||||
|
@ -140,7 +146,7 @@ impl BasicRenderer {
|
||||||
label: Some("texture_bind_group_layout"),
|
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 {
|
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some("Shader"),
|
label: Some("Shader"),
|
||||||
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)),
|
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(&shader_src)),
|
||||||
|
@ -181,9 +187,45 @@ impl BasicRenderer {
|
||||||
label: Some("transform_bind_group"),
|
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();
|
let mut pipelines = HashMap::new();
|
||||||
pipelines.insert(0, Arc::new(FullRenderPipeline::new(&device, &config, &shader,
|
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 {
|
Self {
|
||||||
window,
|
window,
|
||||||
|
@ -204,10 +246,14 @@ impl BasicRenderer {
|
||||||
|
|
||||||
transform_buffer,
|
transform_buffer,
|
||||||
transform_bind_group,
|
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 mesh = &model_2d.mesh;
|
||||||
|
|
||||||
let vertex_buffer = self.device.create_buffer_init(
|
let vertex_buffer = self.device.create_buffer_init(
|
||||||
|
@ -294,8 +340,8 @@ impl BasicRenderer {
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Renderer for BasicRenderer {
|
impl Renderer for BasicRenderer {
|
||||||
async fn prepare(&mut self, main_world: &World) {
|
async fn prepare(&mut self, main_world: &mut World) {
|
||||||
for (entity, (model, transform)) in main_world.query::<(&Model2dComponent, Option<&TransformComponent>)>().iter() {
|
for (entity, (model, transform)) in main_world.query::<(&MeshComponent, Option<&TransformComponent>)>().iter() {
|
||||||
let transform = match transform {
|
let transform = match transform {
|
||||||
Some(transform) => {
|
Some(transform) => {
|
||||||
Some(transform.transform)
|
Some(transform.transform)
|
||||||
|
@ -315,10 +361,19 @@ impl Renderer for BasicRenderer {
|
||||||
if self.buffer_storage.get(&entity).is_none() {
|
if self.buffer_storage.get(&entity).is_none() {
|
||||||
let buffers = self.create_model_buffers(model);
|
let buffers = self.create_model_buffers(model);
|
||||||
self.buffer_storage.insert(entity, buffers);
|
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>) {
|
fn add_render_pipeline(&mut self, shader_id: u32, pipeline: Arc<FullRenderPipeline>) {
|
||||||
|
@ -369,6 +424,9 @@ impl Renderer for BasicRenderer {
|
||||||
} else {
|
} else {
|
||||||
debug!("//TODO: clear transform uniform if the RenderJob doesn't have a transform.");
|
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() {
|
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
|
let indices_len = indices.count().unwrap(); // index buffers will have count, if not thats a bug
|
||||||
|
|
Loading…
Reference in New Issue