render: implement 2d sprite rendering
This commit is contained in:
parent
4816b7333e
commit
315924f920
|
@ -4,6 +4,24 @@
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug lyra dim_2d example",
|
||||||
|
"cargo": {
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"--manifest-path", "${workspaceFolder}/examples/2d/Cargo.toml"
|
||||||
|
//"--bin=testbed",
|
||||||
|
],
|
||||||
|
"filter": {
|
||||||
|
"name": "dim_2d",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/examples/2d"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "lldb",
|
"type": "lldb",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
|
|
|
@ -414,12 +414,12 @@ impl Node for MeshPass {
|
||||||
view,
|
view,
|
||||||
resolve_target: None,
|
resolve_target: None,
|
||||||
ops: wgpu::Operations {
|
ops: wgpu::Operations {
|
||||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
load: wgpu::LoadOp::Load,/* wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
r: 0.1,
|
r: 0.1,
|
||||||
g: 0.2,
|
g: 0.2,
|
||||||
b: 0.3,
|
b: 0.3,
|
||||||
a: 1.0,
|
a: 1.0,
|
||||||
}),
|
}), */
|
||||||
store: wgpu::StoreOp::Store,
|
store: wgpu::StoreOp::Store,
|
||||||
},
|
},
|
||||||
})],
|
})],
|
||||||
|
@ -441,9 +441,9 @@ impl Node for MeshPass {
|
||||||
|
|
||||||
for job in render_meshes.iter() {
|
for job in render_meshes.iter() {
|
||||||
// get the mesh (containing vertices) and the buffers from storage
|
// get the mesh (containing vertices) and the buffers from storage
|
||||||
let buffers = mesh_buffers.get(&job.mesh_uuid);
|
let buffers = mesh_buffers.get(&job.asset_uuid);
|
||||||
if buffers.is_none() {
|
if buffers.is_none() {
|
||||||
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
|
warn!("Skipping job since its mesh is missing {:?}", job.asset_uuid);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let buffers = buffers.unwrap();
|
let buffers = buffers.unwrap();
|
||||||
|
|
|
@ -29,4 +29,7 @@ mod mesh_prepare;
|
||||||
pub use mesh_prepare::*;
|
pub use mesh_prepare::*;
|
||||||
|
|
||||||
mod transform;
|
mod transform;
|
||||||
pub use transform::*;
|
pub use transform::*;
|
||||||
|
|
||||||
|
mod sprite;
|
||||||
|
pub use sprite::*;
|
|
@ -963,9 +963,9 @@ fn light_shadow_pass_impl<'a>(
|
||||||
|
|
||||||
for job in render_meshes.iter() {
|
for job in render_meshes.iter() {
|
||||||
// get the mesh (containing vertices) and the buffers from storage
|
// get the mesh (containing vertices) and the buffers from storage
|
||||||
let buffers = mesh_buffers.get(&job.mesh_uuid);
|
let buffers = mesh_buffers.get(&job.asset_uuid);
|
||||||
if buffers.is_none() {
|
if buffers.is_none() {
|
||||||
warn!("Skipping job since its mesh is missing {:?}", job.mesh_uuid);
|
warn!("Skipping job since its mesh is missing {:?}", job.asset_uuid);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let buffers = buffers.unwrap();
|
let buffers = buffers.unwrap();
|
||||||
|
|
|
@ -0,0 +1,404 @@
|
||||||
|
use std::{
|
||||||
|
cell::RefMut,
|
||||||
|
collections::{HashMap, VecDeque},
|
||||||
|
rc::Rc,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
use glam::{Vec2, Vec3};
|
||||||
|
use image::GenericImageView;
|
||||||
|
use lyra_ecs::{
|
||||||
|
query::{Entities, ResMut}, AtomicRef, Entity, ResourceData
|
||||||
|
};
|
||||||
|
use lyra_game_derive::RenderGraphLabel;
|
||||||
|
use lyra_resource::{Image, Texture};
|
||||||
|
use tracing::{info, instrument, warn};
|
||||||
|
use uuid::Uuid;
|
||||||
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
render::{
|
||||||
|
graph::{Node, NodeDesc, NodeType, SlotAttribute},
|
||||||
|
render_job::RenderJob,
|
||||||
|
resource::{
|
||||||
|
FragmentState, PipelineDescriptor, RenderPipeline, RenderPipelineDescriptor, Shader,
|
||||||
|
VertexState,
|
||||||
|
},
|
||||||
|
texture::{res_filter_to_wgpu, res_wrap_to_wgpu},
|
||||||
|
transform_buffer_storage::{TransformBuffers, TransformIndex},
|
||||||
|
vertex::Vertex2D,
|
||||||
|
},
|
||||||
|
sprite::Sprite,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{BasePassSlots, RenderAssets};
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||||
|
pub struct SpritePassLabel;
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, Clone, PartialEq, RenderGraphLabel)]
|
||||||
|
pub enum SpritePassSlots {
|
||||||
|
SpriteTexture,
|
||||||
|
SpriteTextureView,
|
||||||
|
SpriteTextureSampler,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SpriteTexture {
|
||||||
|
texture: wgpu::Texture,
|
||||||
|
texture_sampler: wgpu::Sampler,
|
||||||
|
texture_bg: Arc<wgpu::BindGroup>,
|
||||||
|
|
||||||
|
vertex_buffers: wgpu::Buffer,
|
||||||
|
index_buffers: wgpu::Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct SpritePass {
|
||||||
|
pipeline: Option<RenderPipeline>,
|
||||||
|
texture_bgl: Option<Arc<wgpu::BindGroupLayout>>,
|
||||||
|
jobs: VecDeque<RenderJob>,
|
||||||
|
|
||||||
|
transform_buffers: Option<ResourceData>,
|
||||||
|
sprite_textures: Option<ResourceData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpritePass {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(self, device, sprite))]
|
||||||
|
fn create_vertex_index_buffers(
|
||||||
|
&mut self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
sprite: &Sprite,
|
||||||
|
) -> (wgpu::Buffer, wgpu::Buffer) {
|
||||||
|
let tex_dims = sprite
|
||||||
|
.texture
|
||||||
|
.data_ref()
|
||||||
|
.map(|t| t.dimensions());
|
||||||
|
if tex_dims.is_none() {
|
||||||
|
info!("Sprite texture is not loaded, not rendering it until it is!");
|
||||||
|
todo!("Wait until texture is loaded");
|
||||||
|
}
|
||||||
|
let tex_dims = tex_dims.unwrap();
|
||||||
|
|
||||||
|
let vertices = vec![
|
||||||
|
// top left
|
||||||
|
Vertex2D::new(Vec3::new(0.0, 0.0, 0.0), Vec2::new(0.0, 1.0)),
|
||||||
|
// bottom left
|
||||||
|
Vertex2D::new(Vec3::new(0.0, tex_dims.1 as f32, 0.0), Vec2::new(0.0, 0.0)),
|
||||||
|
// top right
|
||||||
|
Vertex2D::new(Vec3::new(tex_dims.0 as f32, 0.0, 0.0), Vec2::new(1.0, 1.0)),
|
||||||
|
// bottom right
|
||||||
|
Vertex2D::new(
|
||||||
|
Vec3::new(tex_dims.0 as f32, tex_dims.1 as f32, 0.0),
|
||||||
|
Vec2::new(1.0, 0.0),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Vertex Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(vertices.as_slice()),
|
||||||
|
usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
let contents: [u32; 6] = [
|
||||||
|
//3, 1, 0,
|
||||||
|
//0, 2, 3
|
||||||
|
3, 1, 0, // second tri
|
||||||
|
0, 2, 3, // first tri
|
||||||
|
//0, 2, 3, // second tri
|
||||||
|
];
|
||||||
|
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
|
label: Some("Index Buffer"),
|
||||||
|
contents: bytemuck::cast_slice(&contents),
|
||||||
|
usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||||
|
});
|
||||||
|
|
||||||
|
(vertex_buffer, index_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_sprite_texture(
|
||||||
|
&self,
|
||||||
|
device: &wgpu::Device,
|
||||||
|
queue: &wgpu::Queue,
|
||||||
|
uuid: &Uuid,
|
||||||
|
image: &Image,
|
||||||
|
) -> Option<(wgpu::Texture, wgpu::Sampler, wgpu::BindGroup)> {
|
||||||
|
let uuid_str = uuid.to_string();
|
||||||
|
let image_dim = image.dimensions();
|
||||||
|
let tex = device.create_texture(&wgpu::TextureDescriptor {
|
||||||
|
label: Some(&format!("sprite_texture_{}", uuid_str)),
|
||||||
|
size: wgpu::Extent3d {
|
||||||
|
width: image_dim.0,
|
||||||
|
height: image_dim.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
mip_level_count: 1, // TODO
|
||||||
|
sample_count: 1,
|
||||||
|
dimension: wgpu::TextureDimension::D2,
|
||||||
|
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||||
|
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||||
|
view_formats: &[],
|
||||||
|
});
|
||||||
|
let tex_view = tex.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
|
||||||
|
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||||
|
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||||
|
mag_filter: wgpu::FilterMode::Nearest,
|
||||||
|
min_filter: wgpu::FilterMode::Nearest,
|
||||||
|
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
queue.write_texture(
|
||||||
|
wgpu::ImageCopyTexture {
|
||||||
|
aspect: wgpu::TextureAspect::All,
|
||||||
|
texture: &tex,
|
||||||
|
mip_level: 0,
|
||||||
|
origin: wgpu::Origin3d::ZERO,
|
||||||
|
},
|
||||||
|
&image.to_rgba8(),
|
||||||
|
wgpu::ImageDataLayout {
|
||||||
|
offset: 0,
|
||||||
|
bytes_per_row: Some(4 * image_dim.0),
|
||||||
|
rows_per_image: Some(image_dim.1),
|
||||||
|
},
|
||||||
|
wgpu::Extent3d {
|
||||||
|
width: image_dim.0,
|
||||||
|
height: image_dim.1,
|
||||||
|
depth_or_array_layers: 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let bgl = self.texture_bgl.as_ref().unwrap();
|
||||||
|
let tex_bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
|
label: Some(&format!("sprite_texture_bg_{}", uuid_str)),
|
||||||
|
layout: bgl,
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 0,
|
||||||
|
resource: wgpu::BindingResource::TextureView(&tex_view),
|
||||||
|
},
|
||||||
|
wgpu::BindGroupEntry {
|
||||||
|
binding: 1,
|
||||||
|
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Some((tex, sampler, tex_bg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node for SpritePass {
|
||||||
|
fn desc(
|
||||||
|
&mut self,
|
||||||
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
|
) -> crate::render::graph::NodeDesc {
|
||||||
|
let device = &graph.device;
|
||||||
|
|
||||||
|
let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||||
|
label: Some("bgl_sprite_main"),
|
||||||
|
entries: &[
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 0,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Texture {
|
||||||
|
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||||
|
view_dimension: wgpu::TextureViewDimension::D2,
|
||||||
|
multisampled: false,
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
wgpu::BindGroupLayoutEntry {
|
||||||
|
binding: 1,
|
||||||
|
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||||
|
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
|
||||||
|
count: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
self.texture_bgl = Some(Arc::new(bgl));
|
||||||
|
|
||||||
|
let mut desc = NodeDesc::new(NodeType::Render, None, vec![]);
|
||||||
|
|
||||||
|
desc.add_buffer_slot(BasePassSlots::Camera, SlotAttribute::Input, None);
|
||||||
|
|
||||||
|
desc
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare(
|
||||||
|
&mut self,
|
||||||
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
|
world: &mut lyra_ecs::World,
|
||||||
|
_: &mut crate::render::graph::RenderGraphContext,
|
||||||
|
) {
|
||||||
|
let device = graph.device();
|
||||||
|
let vt = graph.view_target();
|
||||||
|
|
||||||
|
if self.pipeline.is_none() {
|
||||||
|
let shader = Rc::new(Shader {
|
||||||
|
label: Some("sprite_shader".into()),
|
||||||
|
source: include_str!("../../shaders/2d/sprite_main.wgsl").to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
let diffuse_bgl = self.texture_bgl.clone().unwrap();
|
||||||
|
let camera_bgl = graph.bind_group_layout(BasePassSlots::Camera).clone();
|
||||||
|
let transforms = world
|
||||||
|
.get_resource::<TransformBuffers>()
|
||||||
|
.expect("Missing transform buffers");
|
||||||
|
let transform_bgl = transforms.bindgroup_layout.clone();
|
||||||
|
|
||||||
|
self.pipeline = Some(RenderPipeline::create(
|
||||||
|
device,
|
||||||
|
&RenderPipelineDescriptor {
|
||||||
|
label: Some("sprite_pass".into()),
|
||||||
|
layouts: vec![diffuse_bgl, transform_bgl, camera_bgl],
|
||||||
|
push_constant_ranges: vec![],
|
||||||
|
vertex: VertexState {
|
||||||
|
module: shader.clone(),
|
||||||
|
entry_point: "vs_main".into(),
|
||||||
|
buffers: vec![Vertex2D::desc().into()],
|
||||||
|
},
|
||||||
|
fragment: Some(FragmentState {
|
||||||
|
module: shader,
|
||||||
|
entry_point: "fs_main".into(),
|
||||||
|
targets: vec![Some(wgpu::ColorTargetState {
|
||||||
|
format: vt.format(),
|
||||||
|
blend: Some(wgpu::BlendState::REPLACE),
|
||||||
|
write_mask: wgpu::ColorWrites::ALL,
|
||||||
|
})],
|
||||||
|
}),
|
||||||
|
depth_stencil: None,
|
||||||
|
primitive: wgpu::PrimitiveState {
|
||||||
|
cull_mode: Some(wgpu::Face::Back),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
multisample: wgpu::MultisampleState::default(),
|
||||||
|
multiview: None,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
|
drop(transforms);
|
||||||
|
world.add_resource_default_if_absent::<RenderAssets<SpriteTexture>>();
|
||||||
|
let sprite_textures = world
|
||||||
|
.get_resource_data::<RenderAssets<SpriteTexture>>()
|
||||||
|
.expect("Missing sprite texture store");
|
||||||
|
self.sprite_textures = Some(sprite_textures.clone());
|
||||||
|
|
||||||
|
let transforms = world
|
||||||
|
.get_resource_data::<TransformBuffers>()
|
||||||
|
.expect("Missing transform buffers");
|
||||||
|
self.transform_buffers = Some(transforms.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
let queue = &graph.queue;
|
||||||
|
for (entity, sprite, transform_idx, mut sprite_store) in world
|
||||||
|
.view::<(
|
||||||
|
Entities,
|
||||||
|
&Sprite,
|
||||||
|
&TransformIndex,
|
||||||
|
ResMut<RenderAssets<SpriteTexture>>,
|
||||||
|
)>()
|
||||||
|
.iter()
|
||||||
|
{
|
||||||
|
if let Some(image) = sprite.texture.data_ref() {
|
||||||
|
let texture_uuid = sprite.texture.uuid();
|
||||||
|
if !sprite_store.contains_key(&texture_uuid) {
|
||||||
|
// returns `None` if the Texture image is not loaded.
|
||||||
|
if let Some((tex, samp, tex_bg)) =
|
||||||
|
self.load_sprite_texture(device, queue, &texture_uuid, &image)
|
||||||
|
{
|
||||||
|
let (vertex, index) = self.create_vertex_index_buffers(device, &sprite);
|
||||||
|
|
||||||
|
sprite_store.insert(
|
||||||
|
texture_uuid,
|
||||||
|
SpriteTexture {
|
||||||
|
texture: tex,
|
||||||
|
texture_sampler: samp,
|
||||||
|
texture_bg: Arc::new(tex_bg),
|
||||||
|
vertex_buffers: vertex,
|
||||||
|
index_buffers: index,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.jobs.push_back(RenderJob {
|
||||||
|
entity,
|
||||||
|
shader_id: 0,
|
||||||
|
asset_uuid: texture_uuid,
|
||||||
|
transform_id: *transform_idx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn execute(
|
||||||
|
&mut self,
|
||||||
|
graph: &mut crate::render::graph::RenderGraph,
|
||||||
|
_: &crate::render::graph::NodeDesc,
|
||||||
|
context: &mut crate::render::graph::RenderGraphContext,
|
||||||
|
) {
|
||||||
|
let pipeline = self.pipeline.as_ref().unwrap();
|
||||||
|
|
||||||
|
let sprite_store = self.sprite_textures.clone().unwrap();
|
||||||
|
let sprite_store: AtomicRef<RenderAssets<SpriteTexture>> = sprite_store.get();
|
||||||
|
let transforms = self.transform_buffers.clone().unwrap();
|
||||||
|
let transforms: AtomicRef<TransformBuffers> = transforms.get();
|
||||||
|
|
||||||
|
let vt = graph.view_target();
|
||||||
|
let view = vt.render_view();
|
||||||
|
|
||||||
|
let camera_bg = graph.bind_group(BasePassSlots::Camera);
|
||||||
|
|
||||||
|
{
|
||||||
|
let encoder = context.encoder.as_mut().unwrap();
|
||||||
|
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
|
label: Some("sprite_pass"),
|
||||||
|
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||||
|
view,
|
||||||
|
resolve_target: None,
|
||||||
|
ops: wgpu::Operations {
|
||||||
|
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||||
|
r: 0.1,
|
||||||
|
g: 0.2,
|
||||||
|
b: 0.3,
|
||||||
|
a: 1.0,
|
||||||
|
}),
|
||||||
|
store: wgpu::StoreOp::Store,
|
||||||
|
},
|
||||||
|
})],
|
||||||
|
depth_stencil_attachment: None,
|
||||||
|
timestamp_writes: None,
|
||||||
|
occlusion_query_set: None,
|
||||||
|
});
|
||||||
|
pass.set_pipeline(pipeline);
|
||||||
|
|
||||||
|
while let Some(job) = self.jobs.pop_front() {
|
||||||
|
let sprite = sprite_store.get(&job.asset_uuid)
|
||||||
|
.expect("failed to find SpriteTexture for job asset_uuid");
|
||||||
|
|
||||||
|
pass.set_bind_group(0, &sprite.texture_bg, &[]);
|
||||||
|
|
||||||
|
// Get the bindgroup for job's transform and bind to it using an offset.
|
||||||
|
let bindgroup = transforms.bind_group(job.transform_id);
|
||||||
|
let offset = transforms.buffer_offset(job.transform_id);
|
||||||
|
pass.set_bind_group(1, bindgroup, &[offset]);
|
||||||
|
|
||||||
|
pass.set_bind_group(2, camera_bg, &[]);
|
||||||
|
|
||||||
|
pass.set_vertex_buffer(
|
||||||
|
0,
|
||||||
|
sprite.vertex_buffers.slice(..),
|
||||||
|
);
|
||||||
|
pass.set_index_buffer(sprite.index_buffers.slice(..), wgpu::IndexFormat::Uint32);
|
||||||
|
pass.draw_indexed(0..6, 0, 0..1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,12 +32,12 @@ pub struct InterpTransform {
|
||||||
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
#[derive(Default, Debug, Clone, Copy, Hash, RenderGraphLabel)]
|
||||||
pub struct TransformsNodeLabel;
|
pub struct TransformsNodeLabel;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Default)]
|
||||||
pub struct TransformsNode {}
|
pub struct TransformsNode;
|
||||||
|
|
||||||
impl TransformsNode {
|
impl TransformsNode {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {}
|
Self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use super::transform_buffer_storage::TransformIndex;
|
||||||
pub struct RenderJob {
|
pub struct RenderJob {
|
||||||
pub entity: Entity,
|
pub entity: Entity,
|
||||||
pub shader_id: u64,
|
pub shader_id: u64,
|
||||||
pub mesh_uuid: uuid::Uuid,
|
pub asset_uuid: uuid::Uuid,
|
||||||
pub transform_id: TransformIndex,
|
pub transform_id: TransformIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ impl RenderJob {
|
||||||
Self {
|
Self {
|
||||||
entity,
|
entity,
|
||||||
shader_id,
|
shader_id,
|
||||||
mesh_uuid: mesh_buffer_id,
|
asset_uuid: mesh_buffer_id,
|
||||||
transform_id
|
transform_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use lyra_game_derive::RenderGraphLabel;
|
||||||
use tracing::{debug, instrument, warn};
|
use tracing::{debug, instrument, warn};
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
|
|
||||||
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, ShadowMapsPass, ShadowMapsPassLabel, SubGraphNode, TransformsNode, TransformsNodeLabel, ViewTarget};
|
use crate::render::graph::{BasePass, BasePassLabel, BasePassSlots, FxaaPass, FxaaPassLabel, LightBasePass, LightBasePassLabel, LightCullComputePass, LightCullComputePassLabel, MeshPass, MeshPrepNode, MeshPrepNodeLabel, MeshesPassLabel, PresentPass, PresentPassLabel, RenderGraphLabelValue, RenderTarget, ShadowMapsPass, ShadowMapsPassLabel, SpritePass, SpritePassLabel, SubGraphNode, TransformsNode, TransformsNodeLabel, ViewTarget};
|
||||||
|
|
||||||
use super::graph::RenderGraph;
|
use super::graph::RenderGraph;
|
||||||
use super::{resource::RenderPipeline, render_job::RenderJob};
|
use super::{resource::RenderPipeline, render_job::RenderJob};
|
||||||
|
@ -163,6 +163,10 @@ impl BasicRenderer {
|
||||||
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new(material_bgl));
|
forward_plus_graph.add_node(MeshesPassLabel, MeshPass::new(material_bgl));
|
||||||
forward_plus_graph.add_edge(TransformsNodeLabel, MeshPrepNodeLabel);
|
forward_plus_graph.add_edge(TransformsNodeLabel, MeshPrepNodeLabel);
|
||||||
|
|
||||||
|
debug!("Adding sprite pass");
|
||||||
|
forward_plus_graph.add_node(SpritePassLabel, SpritePass::new());
|
||||||
|
forward_plus_graph.add_edge(TransformsNodeLabel, SpritePassLabel);
|
||||||
|
|
||||||
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
forward_plus_graph.add_edge(LightBasePassLabel, LightCullComputePassLabel);
|
||||||
forward_plus_graph.add_edge(LightCullComputePassLabel, MeshesPassLabel);
|
forward_plus_graph.add_edge(LightCullComputePassLabel, MeshesPassLabel);
|
||||||
forward_plus_graph.add_edge(MeshPrepNodeLabel, MeshesPassLabel);
|
forward_plus_graph.add_edge(MeshPrepNodeLabel, MeshesPassLabel);
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
const ALPHA_CUTOFF = 0.1;
|
||||||
|
|
||||||
|
struct VertexInput {
|
||||||
|
@location(0) position: vec3<f32>,
|
||||||
|
@location(1) tex_coords: vec2<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct VertexOutput {
|
||||||
|
@builtin(position) clip_position: vec4<f32>,
|
||||||
|
@location(0) tex_coords: vec2<f32>,
|
||||||
|
@location(1) world_position: vec3<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TransformData {
|
||||||
|
transform: mat4x4<f32>,
|
||||||
|
normal_matrix: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CameraUniform {
|
||||||
|
view: mat4x4<f32>,
|
||||||
|
inverse_projection: mat4x4<f32>,
|
||||||
|
view_projection: mat4x4<f32>,
|
||||||
|
projection: mat4x4<f32>,
|
||||||
|
position: vec3<f32>,
|
||||||
|
tile_debug: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(1) @binding(0)
|
||||||
|
var<uniform> u_model_transform_data: TransformData;
|
||||||
|
|
||||||
|
@group(2) @binding(0)
|
||||||
|
var<uniform> u_camera: CameraUniform;
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn vs_main(
|
||||||
|
in: VertexInput,
|
||||||
|
) -> VertexOutput {
|
||||||
|
var out: VertexOutput;
|
||||||
|
var world_position: vec4<f32> = u_model_transform_data.transform * vec4<f32>(in.position, 1.0);
|
||||||
|
out.world_position = world_position.xyz;
|
||||||
|
out.tex_coords = in.tex_coords;
|
||||||
|
out.clip_position = u_camera.view_projection * world_position;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0)
|
||||||
|
var t_diffuse: texture_2d<f32>;
|
||||||
|
|
||||||
|
@group(0) @binding(1)
|
||||||
|
var s_diffuse: sampler;
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let object_color: vec4<f32> = textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||||
|
if (object_color.a < ALPHA_CUTOFF) {
|
||||||
|
discard;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object_color;
|
||||||
|
}
|
|
@ -75,7 +75,7 @@ impl Vertex2D {
|
||||||
|
|
||||||
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||||
wgpu::VertexBufferLayout {
|
wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
|
array_stride: std::mem::size_of::<Vertex2D>() as wgpu::BufferAddress,
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
attributes: &[
|
attributes: &[
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Pivot {
|
||||||
|
|
||||||
#[derive(Clone, Component, Reflect)]
|
#[derive(Clone, Component, Reflect)]
|
||||||
pub struct Sprite {
|
pub struct Sprite {
|
||||||
pub texture: ResHandle<lyra_resource::Texture>,
|
pub texture: ResHandle<lyra_resource::Image>,
|
||||||
pub color: Vec3,
|
pub color: Vec3,
|
||||||
pub pivot: Pivot,
|
pub pivot: Pivot,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
use lyra_engine::{
|
use lyra_engine::{
|
||||||
assets::ResourceManager, gltf::Gltf, ecs::query::View, game::App, input::{
|
assets::{Image, ResourceManager, Texture}, ecs::query::View, game::App, gltf::Gltf, input::{
|
||||||
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
Action, ActionHandler, ActionKind, ActionMapping, ActionMappingId, ActionSource,
|
||||||
InputActionPlugin, KeyCode, LayoutId,
|
InputActionPlugin, KeyCode, LayoutId,
|
||||||
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
}, math::{self, Transform, Vec3}, render::light::directional::DirectionalLight, scene::{
|
||||||
CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform,
|
system_update_world_transforms, CameraComponent, FreeFlyCamera, FreeFlyCameraPlugin, WorldTransform, ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN, ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN
|
||||||
ACTLBL_LOOK_LEFT_RIGHT, ACTLBL_LOOK_ROLL, ACTLBL_LOOK_UP_DOWN,
|
}, sprite::{self, Sprite}, winit::WindowOptions
|
||||||
ACTLBL_MOVE_FORWARD_BACKWARD, ACTLBL_MOVE_LEFT_RIGHT, ACTLBL_MOVE_UP_DOWN,
|
|
||||||
}, winit::WindowOptions
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_std::main]
|
#[async_std::main]
|
||||||
|
@ -85,7 +83,8 @@ async fn main() {
|
||||||
.with_plugin(setup_scene_plugin)
|
.with_plugin(setup_scene_plugin)
|
||||||
.with_plugin(action_handler_plugin)
|
.with_plugin(action_handler_plugin)
|
||||||
//.with_plugin(camera_debug_plugin)
|
//.with_plugin(camera_debug_plugin)
|
||||||
.with_plugin(FreeFlyCameraPlugin);
|
.with_plugin(FreeFlyCameraPlugin)
|
||||||
|
.with_system("system_update_world_transforms", system_update_world_transforms, &[]);
|
||||||
a.run();
|
a.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,14 +112,28 @@ fn setup_scene_plugin(app: &mut App) {
|
||||||
|
|
||||||
cube_gltf.wait_recurse_dependencies_load();
|
cube_gltf.wait_recurse_dependencies_load();
|
||||||
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
|
let cube_mesh = &cube_gltf.data_ref().unwrap().scenes[0];
|
||||||
drop(resman);
|
|
||||||
|
|
||||||
|
let image = resman.request::<Image>("../assets/Egg_item.png")
|
||||||
|
.unwrap();
|
||||||
|
image.wait_recurse_dependencies_load();
|
||||||
|
|
||||||
|
drop(resman);
|
||||||
world.spawn((
|
world.spawn((
|
||||||
cube_mesh.clone(),
|
cube_mesh.clone(),
|
||||||
WorldTransform::default(),
|
WorldTransform::default(),
|
||||||
Transform::from_xyz(0.0, 0.0, -2.0),
|
Transform::from_xyz(0.0, 0.0, -2.0),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
world.spawn((
|
||||||
|
Sprite {
|
||||||
|
texture: image,
|
||||||
|
color: Vec3::ONE,
|
||||||
|
pivot: sprite::Pivot::Center,
|
||||||
|
},
|
||||||
|
WorldTransform::default(),
|
||||||
|
Transform::from_xyz(0.0, 0.0, -20.0),
|
||||||
|
));
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
let mut light_tran = Transform::from_xyz(1.5, 2.5, 0.0);
|
||||||
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
light_tran.scale = Vec3::new(0.5, 0.5, 0.5);
|
||||||
|
@ -136,7 +149,7 @@ fn setup_scene_plugin(app: &mut App) {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut camera = CameraComponent::new_2d();
|
let mut camera = CameraComponent::new_3d();
|
||||||
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
camera.transform.translation += math::Vec3::new(0.0, 0.0, 5.5);
|
||||||
world.spawn((camera, FreeFlyCamera::default()));
|
world.spawn((camera, FreeFlyCamera::default()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Egg_item.png
|
Loading…
Reference in New Issue