use glam::{Mat4, Vec3};
use crate::{
bounds::Aabb,
materials::{Material, MaterialKey},
meshes::{
buffer_info::{
MeshBufferCustomVertexAttributeInfo, MeshBufferGeometryMorphInfo, MeshBufferSkinInfo,
MeshBufferVertexAttributeInfo,
},
mesh::Mesh,
MeshKey,
},
transforms::TransformKey,
AwsmRenderer,
};
#[derive(Debug, Clone, Default)]
pub struct RawMeshData {
pub positions: Vec<[f32; 3]>,
pub normals: Option<Vec<[f32; 3]>>,
pub uv_sets: Vec<Vec<[f32; 2]>>,
pub colors: Option<Vec<[f32; 4]>>,
pub indices: Vec<u32>,
pub skin: Option<RawSkin>,
pub morph: Option<RawMorph>,
}
#[derive(Debug, Clone)]
pub struct RawSkin {
pub joints: Vec<TransformKey>,
pub inverse_bind_matrices: Vec<Mat4>,
pub set_count: usize,
pub index_weights: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct RawMorph {
pub info: MeshBufferGeometryMorphInfo,
pub weights: Vec<u8>,
pub values: Vec<u8>,
}
impl RawMeshData {
pub fn vertex_count(&self) -> usize {
self.positions.len()
}
pub fn triangle_count(&self) -> usize {
self.indices.len() / 3
}
fn ensure_normals(&mut self) {
if self.normals.is_some() {
return;
}
let mut acc = vec![Vec3::ZERO; self.positions.len()];
let positions: Vec<Vec3> = self
.positions
.iter()
.map(|p| Vec3::from_array(*p))
.collect();
for tri in self.indices.chunks_exact(3) {
let i0 = tri[0] as usize;
let i1 = tri[1] as usize;
let i2 = tri[2] as usize;
let a = positions[i0];
let b = positions[i1];
let c = positions[i2];
let n = (b - a).cross(c - a);
acc[i0] += n;
acc[i1] += n;
acc[i2] += n;
}
self.normals = Some(
acc.into_iter()
.map(|n| n.normalize_or_zero().to_array())
.collect(),
);
}
fn aabb(&self) -> Option<Aabb> {
if self.positions.is_empty() {
return None;
}
let mut min = Vec3::splat(f32::INFINITY);
let mut max = Vec3::splat(f32::NEG_INFINITY);
for p in &self.positions {
let v = Vec3::from_array(*p);
min = min.min(v);
max = max.max(v);
}
Some(Aabb { min, max })
}
pub(crate) fn into_geometry_source(
mut self,
front_face: awsm_renderer_core::pipeline::primitive::FrontFace,
) -> crate::meshes::geometry::GeometrySource {
self.ensure_normals();
let aabb = self.aabb();
let vertex_count = self.vertex_count();
let triangle_count = self.triangle_count();
let mut attribute_index_bytes: Vec<u8> = Vec::with_capacity(triangle_count * 12);
for tri in self.indices.chunks_exact(3) {
attribute_index_bytes.extend_from_slice(&tri[0].to_le_bytes());
attribute_index_bytes.extend_from_slice(&tri[1].to_le_bytes());
attribute_index_bytes.extend_from_slice(&tri[2].to_le_bytes());
}
let mut vertex_attributes: Vec<MeshBufferVertexAttributeInfo> = Vec::new();
let mut custom_attribute_bytes: Vec<u8> = Vec::new();
for (index, _) in self.uv_sets.iter().enumerate() {
vertex_attributes.push(MeshBufferVertexAttributeInfo::Custom(
MeshBufferCustomVertexAttributeInfo::TexCoords {
index: index as u32,
data_size: 4,
component_len: 2,
},
));
}
let has_colors = self.colors.is_some();
if has_colors {
vertex_attributes.push(MeshBufferVertexAttributeInfo::Custom(
MeshBufferCustomVertexAttributeInfo::Colors {
index: 0,
data_size: 4,
component_len: 4,
},
));
}
for v in 0..vertex_count {
for set in &self.uv_sets {
let uv = set[v];
custom_attribute_bytes.extend_from_slice(&uv[0].to_le_bytes());
custom_attribute_bytes.extend_from_slice(&uv[1].to_le_bytes());
}
if let Some(colors) = self.colors.as_ref() {
let c = colors[v];
for comp in c {
custom_attribute_bytes.extend_from_slice(&comp.to_le_bytes());
}
}
}
crate::meshes::geometry::GeometrySource {
normals: self.normals.expect("ensure_normals filled this"),
positions: self.positions,
uvs0: self.uv_sets.into_iter().next(),
tangents: None,
indices: self.indices,
front_face,
vertex_attributes,
custom_attribute_bytes,
attribute_index_bytes,
aabb,
geometry_morph_key: None,
geometry_morph_info: None,
material_morph_key: None,
material_morph_info: None,
skin_key: None,
skin_info: None,
}
}
}
pub(crate) fn material_wants_tangents(mat: &Material) -> bool {
match mat {
Material::Pbr(m) => {
m.normal_tex.is_some() || m.clearcoat.as_ref().is_some_and(|c| c.normal_tex.is_some())
}
_ => false,
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AddMeshOpts {
pub instanced: bool,
pub hud: bool,
pub hidden: bool,
pub double_sided: Option<bool>,
}
impl AwsmRenderer {
pub fn register_geometry(
&mut self,
source: crate::meshes::geometry::GeometrySource,
) -> crate::meshes::geometry::GeometryKey {
self.meshes.register_geometry(source)
}
pub fn add_mesh(
&mut self,
geometry: crate::meshes::geometry::GeometryKey,
material_key: MaterialKey,
transform_key: TransformKey,
opts: AddMeshOpts,
) -> crate::error::Result<MeshKey> {
let double_sided = opts.double_sided.unwrap_or_else(|| {
self.materials
.get(material_key)
.map(Material::double_sided)
.unwrap_or(false)
});
let mesh = Mesh::new(
transform_key,
material_key,
double_sided,
opts.instanced,
opts.hud,
opts.hidden,
);
Ok(self.meshes.bind_mesh(mesh, geometry)?)
}
pub fn add_raw_mesh(
&mut self,
mut data: RawMeshData,
transform_key: TransformKey,
material_key: MaterialKey,
) -> crate::error::Result<MeshKey> {
if data.positions.is_empty() || data.indices.len() % 3 != 0 {
return Err(crate::error::AwsmError::Mesh(
crate::meshes::error::AwsmMeshError::MeshListEmpty,
));
}
let skin = data.skin.take();
let morph = data.morph.take();
let skin_bits = match skin {
Some(s) => {
let info = MeshBufferSkinInfo {
set_count: s.set_count,
index_weights_size: s.index_weights.len(),
};
let key = self.meshes.skins.insert(
s.joints,
&s.inverse_bind_matrices,
s.set_count,
&s.index_weights,
)?;
Some((key, info))
}
None => None,
};
let morph_bits = match morph {
Some(m) => {
let key = self.meshes.morphs.geometry.insert_raw(
m.info.clone(),
&m.weights,
&m.values,
)?;
Some((key, m.info))
}
None => None,
};
let mut source =
data.into_geometry_source(awsm_renderer_core::pipeline::primitive::FrontFace::Ccw);
if let Some((key, info)) = skin_bits {
source.skin_key = Some(key);
source.skin_info = Some(info);
}
if let Some((key, info)) = morph_bits {
source.geometry_morph_key = Some(key);
source.geometry_morph_info = Some(info);
}
let geometry = self.register_geometry(source);
let mesh_key = self.add_mesh(
geometry,
material_key,
transform_key,
AddMeshOpts::default(),
)?;
let wired = self
.meshes
.resolve_one(geometry, &self.materials, &self.transforms)?;
for mesh_key in wired {
self.sync_spatial_for_mesh(mesh_key);
}
Ok(mesh_key)
}
}