use std::cell::RefCell;
use std::rc::Rc;
use crate::draw2d::Color;
use crate::ecs::{MeshId, TextureId};
use crate::gpu::GpuContext;
use crate::mesh::{Mesh, Transform};
use crate::mesh_pass::{DrawCall, MeshPass};
use crate::render_graph::{RenderContext, RenderNode};
use crate::texture::Texture;
pub struct QueuedMesh {
pub mesh: MeshId,
pub transform: Transform,
pub color: Color,
pub texture: Option<TextureId>,
}
pub struct MeshQueue {
pub meshes: Vec<Mesh>,
pub textures: Vec<Texture>,
pub draw_queue: Vec<QueuedMesh>,
}
impl MeshQueue {
pub fn new() -> Self {
Self {
meshes: Vec::new(),
textures: Vec::new(),
draw_queue: Vec::new(),
}
}
pub fn add_mesh(&mut self, mesh: Mesh) -> MeshId {
let idx = self.meshes.len();
self.meshes.push(mesh);
MeshId(idx)
}
pub fn add_texture(&mut self, texture: Texture) -> TextureId {
let idx = self.textures.len();
self.textures.push(texture);
TextureId(idx)
}
pub fn draw(&mut self, mesh: MeshId, transform: Transform, color: Color) {
self.draw_queue.push(QueuedMesh {
mesh,
transform,
color,
texture: None,
});
}
pub fn draw_textured(
&mut self,
mesh: MeshId,
transform: Transform,
color: Color,
texture: TextureId,
) {
self.draw_queue.push(QueuedMesh {
mesh,
transform,
color,
texture: Some(texture),
});
}
pub fn clear_queue(&mut self) {
self.draw_queue.clear();
}
}
impl Default for MeshQueue {
fn default() -> Self {
Self::new()
}
}
pub struct MeshNode {
pub pass: MeshPass,
pub queue: Rc<RefCell<MeshQueue>>,
pub clear_color: Option<wgpu::Color>,
}
impl MeshNode {
pub fn new(gpu: &GpuContext, queue: Rc<RefCell<MeshQueue>>) -> Self {
Self {
pass: MeshPass::new(gpu),
queue,
clear_color: None, }
}
pub fn with_clear(mut self, color: wgpu::Color) -> Self {
self.clear_color = Some(color);
self
}
}
impl RenderNode for MeshNode {
fn execute(
&self,
ctx: &mut RenderContext,
target: &wgpu::TextureView,
input: Option<&wgpu::TextureView>,
) {
let queue = self.queue.borrow();
let draw_calls: Vec<DrawCall> = queue
.draw_queue
.iter()
.filter_map(|q| {
queue.meshes.get(q.mesh.0).map(|mesh| DrawCall {
mesh,
transform: q.transform,
color: q.color,
texture: q.texture.and_then(|t| queue.textures.get(t.0)),
})
})
.collect();
if let Some(input_view) = input {
let mut blit_pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Mesh Blit Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
self.pass.blit(ctx.gpu, &mut blit_pass, input_view);
}
if draw_calls.is_empty() {
if input.is_none() {
let _clear_pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Mesh Clear Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(
self.clear_color.unwrap_or(wgpu::Color::BLACK),
),
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
}
return;
}
let load_op = if input.is_some() {
wgpu::LoadOp::Load
} else {
match self.clear_color {
Some(color) => wgpu::LoadOp::Clear(color),
None => wgpu::LoadOp::Clear(wgpu::Color::BLACK),
}
};
let mut render_pass = ctx.encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Mesh Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target,
resolve_target: None,
ops: wgpu::Operations {
load: load_op,
store: wgpu::StoreOp::Store,
},
depth_slice: None,
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: &self.pass.depth_view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
self.pass
.render(ctx.gpu, &mut render_pass, ctx.camera, ctx.time, &draw_calls);
}
fn check_hot_reload(&mut self, gpu: &GpuContext) {
self.pass.ensure_depth_size(gpu);
}
}