nightshade 0.13.1

A cross-platform data-oriented game engine.
Documentation
mod batching;
mod bind_groups;
mod compute;
mod constructor;
mod incremental;
mod materials;
mod mesh_registry;
mod resize;
mod uniforms;

use crate::render::wgpu::passes::geometry::HizPass;
use std::collections::{HashMap, HashSet};

use super::types::{MeshBoundsAABB, MeshBoundsData, MeshData, MeshLodInfo, MeshRenderStateInner};
use super::world_state::{WorldGpuBuffers, WorldRenderState};

pub(super) struct MaterialBindGroupContext<'a> {
    pub registered_textures: &'a HashMap<String, (wgpu::TextureView, wgpu::Sampler)>,
    pub dummy_white_view: &'a wgpu::TextureView,
    pub dummy_black_view: &'a wgpu::TextureView,
    pub dummy_normal_view: &'a wgpu::TextureView,
    pub dummy_sampler: &'a wgpu::Sampler,
    pub texture_bind_group_layout: &'a wgpu::BindGroupLayout,
}

pub struct MeshPass {
    pub(super) opaque_pipeline: wgpu::RenderPipeline,
    pub(super) opaque_double_sided_pipeline: wgpu::RenderPipeline,
    pub(super) overlay_opaque_pipeline: wgpu::RenderPipeline,
    pub(super) overlay_opaque_double_sided_pipeline: wgpu::RenderPipeline,
    pub(super) oit_pipeline: wgpu::RenderPipeline,
    pub(super) oit_composite_pipeline: wgpu::RenderPipeline,

    pub(super) oit_accum_texture: wgpu::Texture,
    pub(super) oit_accum_view: wgpu::TextureView,
    pub(super) oit_reveal_texture: wgpu::Texture,
    pub(super) oit_reveal_view: wgpu::TextureView,
    pub(super) oit_composite_bind_group: wgpu::BindGroup,
    pub(super) oit_composite_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) oit_sampler: wgpu::Sampler,

    pub(super) overlay_depth_texture: wgpu::Texture,
    pub(super) overlay_depth_view: wgpu::TextureView,
    pub(super) oit_texture_size: (u32, u32),

    pub(super) uniform_buffer: wgpu::Buffer,
    pub(super) uniform_bind_group: wgpu::BindGroup,
    pub(super) overlay_uniform_buffer: wgpu::Buffer,
    pub(super) overlay_uniform_bind_group: wgpu::BindGroup,

    pub(super) instance_bind_group_layout: wgpu::BindGroupLayout,

    pub(super) texture_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) scene_bind_group: wgpu::BindGroup,
    pub(super) scene_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) shadow_sampler: wgpu::Sampler,
    pub(super) spotlight_shadow_buffer: wgpu::Buffer,
    pub(super) point_shadow_buffer: wgpu::Buffer,
    pub(super) point_shadow_sampler: wgpu::Sampler,
    pub(super) ibl_sampler: wgpu::Sampler,

    pub(super) dummy_white_view: wgpu::TextureView,
    pub(super) dummy_black_view: wgpu::TextureView,
    pub(super) dummy_normal_view: wgpu::TextureView,
    pub(super) dummy_sampler: wgpu::Sampler,

    pub(super) vertex_buffer: wgpu::Buffer,
    pub(super) index_buffer: wgpu::Buffer,
    pub(super) vertex_buffer_size: u64,
    pub(super) index_buffer_size: u64,

    pub(super) meshes: HashMap<String, u32>,
    pub(super) mesh_data: Vec<MeshData>,

    pub(super) registered_textures: HashMap<String, (wgpu::TextureView, wgpu::Sampler)>,
    pub(super) newly_registered_textures: HashSet<String>,

    pub(super) current_vertex_offset: u32,
    pub(super) current_index_offset: u32,

    pub(super) compaction_frame_counter: u64,
    pub(super) last_vertex_utilization: f32,
    pub(super) last_index_utilization: f32,

    pub(super) culling_pipeline: wgpu::ComputePipeline,
    pub(super) culling_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) culling_uniform_buffer: wgpu::Buffer,

    pub(super) mesh_bounds_buffer: wgpu::Buffer,
    pub(super) mesh_bounds_buffer_size: usize,
    pub(super) mesh_bounds_data: Vec<MeshBoundsData>,
    pub(super) mesh_aabbs_buffer: wgpu::Buffer,
    pub(super) mesh_aabbs_buffer_size: usize,
    pub(super) mesh_aabbs_data: Vec<MeshBoundsAABB>,
    pub(super) mesh_lod_buffer: wgpu::Buffer,
    pub(super) mesh_lod_buffer_size: usize,
    pub(super) mesh_lod_data: Vec<MeshLodInfo>,
    pub(super) mesh_lod_mesh_ids: HashMap<u32, Vec<u32>>,
    pub(super) hiz_pass: HizPass,

    pub(super) depth_prepass_pipeline: wgpu::RenderPipeline,
    pub(super) phase1_culling_uniform_buffer: wgpu::Buffer,

    pub(super) morph_displacement_buffer: wgpu::Buffer,
    pub(super) morph_displacement_buffer_size: u64,
    pub(super) current_morph_displacement_offset: u32,

    pub(super) brdf_lut_view: wgpu::TextureView,
    pub(super) irradiance_view: wgpu::TextureView,
    pub(super) prefiltered_view: wgpu::TextureView,
    pub(super) irradiance_b_view: wgpu::TextureView,
    pub(super) prefiltered_b_view: wgpu::TextureView,
    pub(super) point_shadow_cubemap_view: wgpu::TextureView,

    pub(super) last_prepared_world_id: Option<u64>,
    pub(super) current_world_id: u64,
    pub(super) world_states: HashMap<u64, WorldRenderState>,
    pub(super) frame_counter: u64,
    pub(super) resize_full_rebuild_pending: bool,

    pub(super) cluster_bounds_buffer: wgpu::Buffer,
    pub(super) cluster_bounds_pipeline: wgpu::ComputePipeline,
    pub(super) cluster_assign_pipeline: wgpu::ComputePipeline,
    pub(super) _cluster_bounds_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) cluster_assign_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) view_matrix_buffer: wgpu::Buffer,

    pub(super) transform_compute_pipeline: wgpu::ComputePipeline,
    pub(super) transform_compute_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) transform_compute_uniforms_buffer: wgpu::Buffer,
    pub(super) transform_compute_staging_buffer: wgpu::Buffer,
    pub(super) instanced_compute_pipeline: wgpu::ComputePipeline,
    pub(super) instanced_compute_bind_group_layout: wgpu::BindGroupLayout,
    pub(super) instanced_compute_uniforms_buffer: wgpu::Buffer,
    pub(super) instanced_compute_staging_buffer: wgpu::Buffer,
    pub preserve_hiz: bool,
    cached_oit_states: HashMap<(u32, u32), CachedOitState>,
    pub(crate) frame_dirty: Option<MeshRenderStateInner>,
    pub(crate) dirty_mesh_names: HashSet<String>,
}

struct CachedOitState {
    oit_accum_texture: wgpu::Texture,
    oit_accum_view: wgpu::TextureView,
    oit_reveal_texture: wgpu::Texture,
    oit_reveal_view: wgpu::TextureView,
    oit_composite_bind_group: wgpu::BindGroup,
    overlay_depth_texture: wgpu::Texture,
    overlay_depth_view: wgpu::TextureView,
}

fn create_shader_module(device: &wgpu::Device, label: &str, source: &str) -> wgpu::ShaderModule {
    let source = strip_specular_color_texture(source);
    device.create_shader_module(wgpu::ShaderModuleDescriptor {
        label: Some(label),
        source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::from(source)),
    })
}

fn strip_specular_color_texture(source: &str) -> String {
    let mut result = String::with_capacity(source.len());
    let mut lines = source.lines().peekable();
    while let Some(line) = lines.next() {
        let trimmed = line.trim();
        if trimmed == "@group(2) @binding(16)" || trimmed == "@group(2) @binding(17)" {
            lines.next();
            continue;
        }
        if trimmed.starts_with("if material.has_specular_color_texture") {
            for inner in lines.by_ref() {
                if inner.trim() == "}" {
                    break;
                }
            }
            continue;
        }
        result.push_str(line);
        result.push('\n');
    }
    result
}

fn texture_bind_group_layout_entries() -> Vec<wgpu::BindGroupLayoutEntry> {
    let texture_entry = |binding: u32| wgpu::BindGroupLayoutEntry {
        binding,
        visibility: wgpu::ShaderStages::FRAGMENT,
        ty: wgpu::BindingType::Texture {
            sample_type: wgpu::TextureSampleType::Float { filterable: true },
            view_dimension: wgpu::TextureViewDimension::D2,
            multisampled: false,
        },
        count: None,
    };
    let sampler_entry = |binding: u32| wgpu::BindGroupLayoutEntry {
        binding,
        visibility: wgpu::ShaderStages::FRAGMENT,
        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
        count: None,
    };

    let entries = vec![
        texture_entry(0),
        sampler_entry(1),
        texture_entry(2),
        sampler_entry(3),
        texture_entry(4),
        sampler_entry(5),
        texture_entry(6),
        sampler_entry(7),
        texture_entry(8),
        sampler_entry(9),
        texture_entry(10),
        sampler_entry(11),
        texture_entry(12),
        sampler_entry(13),
        texture_entry(14),
        sampler_entry(15),
    ];

    entries
}

impl MeshPass {
    #[inline]
    pub(super) fn state(&self) -> &WorldRenderState {
        self.world_states.get(&self.current_world_id).unwrap()
    }

    #[inline]
    pub(super) fn state_mut(&mut self) -> &mut WorldRenderState {
        self.world_states.get_mut(&self.current_world_id).unwrap()
    }

    #[inline]
    pub(super) fn gpu(&self) -> &WorldGpuBuffers {
        self.world_states
            .get(&self.current_world_id)
            .unwrap()
            .gpu_buffers
            .as_ref()
            .unwrap()
    }

    #[inline]
    pub(super) fn gpu_mut(&mut self) -> &mut WorldGpuBuffers {
        self.world_states
            .get_mut(&self.current_world_id)
            .unwrap()
            .gpu_buffers
            .as_mut()
            .unwrap()
    }
}