use std::sync::LazyLock;
use awsm_renderer_core::buffers::BufferUsage;
use slotmap::Key;
use crate::{
materials::{MaterialKey, Materials},
meshes::{
buffer_info::MeshBufferInfo,
buffer_info::{MeshBufferCustomVertexAttributeInfo, MeshBufferVertexAttributeInfo},
morphs::{MaterialMorphKey, Morphs},
AwsmMeshError, Mesh, MeshKey,
},
};
pub const MATERIAL_MESH_META_MORPH_MATERIAL_BITMASK_NORMAL: u32 = 1;
pub const MATERIAL_MESH_META_MORPH_MATERIAL_BITMASK_TANGENT: u32 = 1 << 1;
pub const MATERIAL_MESH_META_BYTE_SIZE: usize = 96;
pub const MATERIAL_MESH_META_BYTE_ALIGNMENT: usize = 256;
pub const MATERIAL_MESH_META_RECEIVE_SHADOWS_OFFSET: usize = 17 * 4;
pub const MATERIAL_MESH_META_MATERIAL_OFFSET_OFFSET: usize = 6 * 4;
pub const MATERIAL_MESH_META_RECEIVE_DECALS_OFFSET: usize = 18 * 4;
pub const MATERIAL_MESH_META_SHADOW_RECEIVER_GATE_OFFSET: usize = 19 * 4;
pub static MATERIAL_BUFFER_USAGE: LazyLock<BufferUsage> = LazyLock::new(|| {
BufferUsage::new()
.with_copy_dst()
.with_storage()
.with_uniform()
});
pub struct MaterialMeshMeta<'a> {
pub mesh_key: MeshKey,
pub material_key: MaterialKey,
pub material_morph_key: Option<MaterialMorphKey>,
pub custom_attribute_indices_offset: usize,
pub custom_attribute_data_offset: usize,
pub visibility_geometry_data_offset: Option<usize>,
pub transform_offset: usize,
pub normal_matrix_offset: usize,
pub buffer_info: &'a MeshBufferInfo,
pub materials: &'a Materials,
pub morphs: &'a Morphs,
pub mesh: &'a Mesh,
}
fn calculate_uv_sets_index(buffer_info: &MeshBufferInfo) -> u32 {
let mut offset_floats = 0;
for attr in &buffer_info.triangles.vertex_attributes {
if let MeshBufferVertexAttributeInfo::Custom(custom) = attr {
match custom {
MeshBufferCustomVertexAttributeInfo::Colors { .. } => {
offset_floats += attr.vertex_size() / 4;
}
MeshBufferCustomVertexAttributeInfo::TexCoords { .. } => {
break;
}
}
}
}
offset_floats as u32
}
fn calculate_color_sets_index(buffer_info: &MeshBufferInfo) -> u32 {
let mut offset_floats = 0;
for attr in &buffer_info.triangles.vertex_attributes {
if let MeshBufferVertexAttributeInfo::Custom(custom) = attr {
match custom {
MeshBufferCustomVertexAttributeInfo::TexCoords { .. } => {
offset_floats += attr.vertex_size() / 4;
}
MeshBufferCustomVertexAttributeInfo::Colors { .. } => {
break;
}
}
}
}
offset_floats as u32
}
fn calculate_attribute_counts(buffer_info: &MeshBufferInfo) -> (u32, u32) {
let mut uv_set_count = 0u32;
let mut color_set_count = 0u32;
for attr in &buffer_info.triangles.vertex_attributes {
if let MeshBufferVertexAttributeInfo::Custom(custom) = attr {
match custom {
MeshBufferCustomVertexAttributeInfo::TexCoords { index, .. } => {
uv_set_count = uv_set_count.max(*index + 1);
}
MeshBufferCustomVertexAttributeInfo::Colors { index, .. } => {
color_set_count = color_set_count.max(*index + 1);
}
}
}
}
(uv_set_count, color_set_count)
}
impl<'a> MaterialMeshMeta<'a> {
pub fn to_bytes(
self,
) -> std::result::Result<[u8; MATERIAL_MESH_META_BYTE_SIZE], AwsmMeshError> {
let Self {
mesh_key,
material_key,
material_morph_key,
buffer_info,
custom_attribute_indices_offset,
custom_attribute_data_offset,
visibility_geometry_data_offset,
transform_offset,
normal_matrix_offset,
materials,
morphs,
mesh,
} = self;
let mut result = [0u8; MATERIAL_MESH_META_BYTE_SIZE];
let mut offset = 0;
let mut push_u32 = |value: u32| {
result[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
offset += 4;
};
let mesh_key_u64 = mesh_key.data().as_ffi();
let (mesh_key_u32_high, mesh_key_u32_low) = (
(mesh_key_u64 >> 32) as u32,
(mesh_key_u64 & 0xFFFFFFFF) as u32,
);
push_u32(mesh_key_u32_high);
push_u32(mesh_key_u32_low);
if let Some(morph_key) = material_morph_key {
let info = morphs.material.get_info(morph_key)?;
push_u32(info.targets_len as u32);
push_u32(morphs.material.weights_buffer_offset(morph_key)? as u32);
push_u32(morphs.material.values_buffer_offset(morph_key)? as u32);
let mut bitmask = 0;
if info.attributes.normal {
bitmask |= MATERIAL_MESH_META_MORPH_MATERIAL_BITMASK_NORMAL;
}
if info.attributes.tangent {
bitmask |= MATERIAL_MESH_META_MORPH_MATERIAL_BITMASK_TANGENT;
}
push_u32(bitmask);
} else {
push_u32(0);
push_u32(0);
push_u32(0);
push_u32(0);
}
push_u32(materials.buffer_offset(material_key)? as u32);
push_u32(transform_offset as u32);
push_u32(normal_matrix_offset as u32);
push_u32(custom_attribute_indices_offset as u32);
push_u32(custom_attribute_data_offset as u32);
push_u32(buffer_info.triangles.vertex_attribute_stride() as u32);
let uv_sets_index = calculate_uv_sets_index(buffer_info);
push_u32(uv_sets_index);
let (uv_set_count, color_set_count) = calculate_attribute_counts(buffer_info);
push_u32(uv_set_count);
push_u32(color_set_count);
push_u32(visibility_geometry_data_offset.unwrap_or_default() as u32);
push_u32(if mesh.hud { 1 } else { 0 });
push_u32(if mesh.receive_shadows { 1 } else { 0 });
push_u32(if mesh.receive_decals { 1 } else { 0 });
push_u32(1);
push_u32(calculate_color_sets_index(buffer_info));
let alpha_cutoff = materials.alpha_cutoff(material_key).unwrap_or(0.0);
push_u32(alpha_cutoff.to_bits());
push_u32(0);
push_u32(0);
Ok(result)
}
}