use crate::assets::Textures;
use crate::renderer::draw_list::{Batch, Casters};
use crate::renderer::mesh_cache::Store;
use crate::renderer::pipelines::{FrameBindings, Pipelines, Placing, Styles};
use crate::renderer::post::{NOTHING, SceneTarget};
use crate::renderer::shadows::Shadows;
use crate::renderer::skybox::GpuSky;
use crate::surface_style::GROUP;
const CAMERA: [wgpu::DynamicOffset; 1] = [0];
const SKY: core::ops::Range<u32> = 0..3;
pub(crate) const SKIN: u32 = 2;
pub(crate) struct Scene<'a> {
pub(crate) opaque: &'a [Batch],
pub(crate) cutout: &'a [Batch],
pub(crate) translucent: &'a [Batch],
pub(crate) additive: &'a [Batch],
pub(crate) casters: Casters<'a>,
pub(crate) meshes: &'a Store,
pub(crate) instances: &'a wgpu::Buffer,
pub(crate) pipelines: &'a Pipelines,
pub(crate) styles: &'a Styles,
pub(crate) frame: &'a FrameBindings,
pub(crate) textures: &'a Textures,
pub(crate) shadows: &'a Shadows,
pub(crate) sky: &'a GpuSky,
}
pub(crate) fn forward(
encoder: &mut wgpu::CommandEncoder,
target: &SceneTarget<'_>,
scene: &Scene<'_>,
) {
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("mirage-engine forward"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: target.color,
depth_slice: None,
resolve_target: target.resolve,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
view: target.depth,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(NOTHING),
store: wgpu::StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
multiview_mask: None,
});
draw(&mut pass, scene.pipelines.opaque(), scene.opaque, scene);
draw(&mut pass, scene.pipelines.cutout(), scene.cutout, scene);
sky(&mut pass, scene);
draw(
&mut pass,
scene.pipelines.transparent(),
scene.translucent,
scene,
);
draw(&mut pass, scene.pipelines.additive(), scene.additive, scene);
}
fn sky(pass: &mut wgpu::RenderPass<'_>, scene: &Scene<'_>) {
pass.set_pipeline(scene.pipelines.sky());
pass.set_bind_group(0, scene.sky.frame(), &CAMERA);
pass.draw(SKY, 0..1);
}
fn draw(pass: &mut wgpu::RenderPass<'_>, drawn: &Placing, batches: &[Batch], scene: &Scene<'_>) {
if batches.is_empty() {
return;
}
pass.set_bind_group(0, scene.sky.frame(), &CAMERA);
pass.set_bind_group(2, scene.shadows.bindings(), &[]);
pass.set_vertex_buffer(1, scene.instances.slice(..));
for run in batches.chunk_by(|batch, next| {
(batch.style, batch.turn, batch.skinned) == (next.style, next.turn, next.skinned)
}) {
let Some(first) = run.first() else {
continue;
};
match first.style {
None => pass.set_pipeline(drawn.of(first.turn, first.skinned)),
Some(style) => {
let Some((styled, values)) =
scene.styles.drawn_with(style, first.turn, first.skinned)
else {
continue;
};
pass.set_pipeline(styled);
pass.set_bind_group(GROUP, values, &[]);
}
}
for batch in run {
instanced(pass, batch, scene);
}
}
}
fn instanced(pass: &mut wgpu::RenderPass<'_>, batch: &Batch, scene: &Scene<'_>) {
let Some(mesh) = scene.meshes.uploaded(batch.mesh) else {
return;
};
let sampled = mesh
.part_texture(batch.part)
.unwrap_or_else(|| scene.textures.fallback());
pass.set_bind_group(1, sampled, &[]);
pass.set_vertex_buffer(0, mesh.vertices().slice(..));
if let Some(skin) = mesh.skin() {
pass.set_vertex_buffer(SKIN, skin.slice(..));
}
pass.set_index_buffer(mesh.indices().slice(..), wgpu::IndexFormat::Uint32);
pass.draw_indexed(batch.indices.clone(), 0, batch.instances.clone());
}