syrillian_render 0.7.1

Renderer of the Syrillian Game Engine
Documentation
use crate::cache::AssetCache;
use crate::model_uniform::ModelUniform;
use crate::proxies::mesh_proxy::{MeshUniformIndex, RuntimeMeshData};
use crate::proxies::{PROXY_PRIORITY_SOLID, SceneProxy, SceneProxyBinding};
use crate::rendering::GPUDrawCtx;
use crate::rendering::renderer::Renderer;
use crate::rendering::uniform::ShaderUniform;
use crate::{must_pipeline, proxy_data, proxy_data_mut, try_activate_shader};
use glamx::{Affine3A, Vec3, Vec4};
use std::any::Any;
use syrillian_asset::mesh::bone::BoneData;
use syrillian_asset::store::AssetStore;
use syrillian_asset::{HMesh, HShader};
use syrillian_utils::debug_panic;
use tracing::warn;
use wgpu::util::{BufferInitDescriptor, DeviceExt};
use wgpu::{Buffer, BufferUsages, Device, Queue};

#[derive(Debug)]
pub struct GPUDebugProxyData {
    line_data: Option<Buffer>,
    model_uniform: Option<RuntimeMeshData>,
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct DebugLine {
    pub start: Vec3,
    pub start_color: Vec4,
    pub end: Vec3,
    pub end_color: Vec4,
}

#[repr(C)]
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct DebugLineVertex {
    position: [f32; 3],
    color: [f32; 4],
}

impl DebugLineVertex {
    fn new(position: Vec3, color: Vec4) -> Self {
        Self {
            position: [position.x, position.y, position.z],
            color: [color.x, color.y, color.z, color.w],
        }
    }
}

#[derive(Debug)]
pub struct DebugSceneProxy {
    pub lines: Vec<DebugLine>,
    pub meshes: Vec<HMesh>,
    pub color: Vec4,
    pub override_transform: Option<Affine3A>,
}

impl SceneProxy for DebugSceneProxy {
    fn setup_render(&mut self, renderer: &Renderer, model_mat: &Affine3A) -> Box<dyn Any + Send> {
        let line_data = self.new_line_buffer(&renderer.state.device);
        let transform = self.override_transform.unwrap_or(*model_mat);
        let model_uniform =
            self.new_mesh_buffer(&renderer.cache, &renderer.state.device, &transform);

        Box::new(GPUDebugProxyData {
            line_data,
            model_uniform,
        })
    }

    fn refresh_transform(
        &mut self,
        renderer: &Renderer,
        data: &mut (dyn Any + Send),
        local_to_world: &Affine3A,
    ) {
        let data: &mut GPUDebugProxyData = proxy_data_mut!(data);

        let transform = self.override_transform.unwrap_or(*local_to_world);
        self.update_mesh_buffer(
            data,
            &renderer.cache,
            &renderer.state.device,
            &renderer.state.queue,
            &transform,
        );
    }

    fn update_render(
        &mut self,
        renderer: &Renderer,
        data: &mut (dyn Any + Send),
        _local_to_world: &Affine3A,
    ) {
        let data: &mut GPUDebugProxyData = proxy_data_mut!(data);

        // TODO: Reuse or Resize buffer
        data.line_data = self.new_line_buffer(&renderer.state.device);
    }

    fn render(&self, renderer: &Renderer, ctx: &GPUDrawCtx, binding: &SceneProxyBinding) {
        let data = proxy_data!(binding.proxy_data());
        let cache = &renderer.cache;
        self.render_lines(data, cache, ctx);
        self.render_meshes(data, cache, ctx)
    }

    fn priority(&self, _store: &AssetStore) -> u32 {
        PROXY_PRIORITY_SOLID
    }
}

impl Default for DebugSceneProxy {
    fn default() -> Self {
        Self {
            lines: vec![],
            meshes: vec![],
            color: Vec4::new(1.0, 1.0, 1.0, 1.0),
            override_transform: None,
        }
    }
}

impl DebugSceneProxy {
    fn new_line_buffer(&self, device: &Device) -> Option<Buffer> {
        if self.lines.is_empty() {
            return None;
        }

        let mut vertices = Vec::with_capacity(self.lines.len() * 2);
        for line in &self.lines {
            vertices.push(DebugLineVertex::new(line.start, line.start_color));
            vertices.push(DebugLineVertex::new(line.end, line.end_color));
        }

        Some(device.create_buffer_init(&BufferInitDescriptor {
            label: Some("Debug Ray Data Buffer"),
            contents: bytemuck::cast_slice(vertices.as_slice()),
            usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
        }))
    }

    fn new_mesh_buffer(
        &self,
        cache: &AssetCache,
        device: &Device,
        model_mat: &Affine3A,
    ) -> Option<RuntimeMeshData> {
        if self.meshes.is_empty() {
            return None;
        }

        let bgl = cache.bgl_model();
        let mesh_data = ModelUniform {
            model_mat: (*model_mat).into(),
        };
        let uniform = ShaderUniform::builder(bgl)
            .with_buffer_data(&mesh_data)
            .with_buffer_data(&BoneData::DUMMY)
            .build(device);

        Some(RuntimeMeshData { mesh_data, uniform })
    }

    fn update_mesh_buffer(
        &self,
        data: &mut GPUDebugProxyData,
        cache: &AssetCache,
        device: &Device,
        queue: &Queue,
        model_mat: &Affine3A,
    ) {
        if self.meshes.is_empty() {
            return;
        }

        let model_uniform = match data.model_uniform.take() {
            None => self.new_mesh_buffer(cache, device, model_mat),
            Some(mut model_uniform) => {
                model_uniform.mesh_data.model_mat = (*model_mat).into();
                let mesh_buffer = model_uniform.uniform.buffer(MeshUniformIndex::MeshData);
                queue.write_buffer(mesh_buffer, 0, bytemuck::bytes_of(&model_uniform.mesh_data));
                Some(model_uniform)
            }
        };

        data.model_uniform = model_uniform;
    }

    pub fn single_mesh(mesh: HMesh) -> Self {
        let mut proxy = Self::default();
        proxy.meshes.push(mesh);
        proxy
    }

    pub fn set_override_transform(&mut self, transform: Affine3A) {
        self.override_transform = Some(transform);
    }

    fn render_lines(&self, data: &GPUDebugProxyData, cache: &AssetCache, ctx: &GPUDrawCtx) {
        if self.lines.is_empty() {
            return;
        }

        let Some(line_buffer) = &data.line_data else {
            debug_panic!("Lines exist but line buffer was not prepared when rendering.");
            return;
        };

        let mut pass = ctx.pass.write();
        let shader = cache.shader(HShader::DEBUG_LINES);
        try_activate_shader!(shader, &mut pass, ctx => return);

        pass.set_vertex_buffer(0, line_buffer.slice(..));
        let vertices = self.lines.len() as u32 * 2;
        pass.draw(0..vertices, 0..1);
    }

    fn render_meshes(&self, data: &GPUDebugProxyData, cache: &AssetCache, ctx: &GPUDrawCtx) {
        if self.meshes.is_empty() {
            return;
        }

        let Some(data) = &data.model_uniform else {
            debug_panic!("Meshes exist but mesh buffer was not prepared when rendering.");
            return;
        };

        for mesh in self.meshes.iter().copied() {
            let Some(runtime_mesh) = cache.meshes.try_get(mesh, cache) else {
                warn!("Couldn't render {}", mesh.ident_fmt());
                continue;
            };

            let shader = cache.shader(HShader::DEBUG_EDGES);
            let groups = shader.bind_groups();
            must_pipeline!(pipeline = shader, ctx.pass_type => return);

            let mut pass = ctx.pass.write();

            pass.set_pipeline(pipeline);
            pass.set_immediates(0, bytemuck::bytes_of(&self.color));
            pass.set_bind_group(groups.render, ctx.render_bind_group, &[]);
            if let Some(idx) = groups.model {
                pass.set_bind_group(idx, data.uniform.bind_group(), &[]);
            }

            runtime_mesh.draw_all(&mut pass);
        }
    }
}