/* wearechat - generic multiplayer game with voip Copyright (C) 2025 metamuffin This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License only. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ use crate::load_texture; use anyhow::Result; use gltf::{Mesh, Node, buffer::Data}; use log::{debug, info}; use std::path::Path; use weareshared::{ Affine3A, Vec3A, resources::{AttributeArray, IndexArray, MeshPart, Prefab}, store::ResourceStore, }; pub fn import_mesh( mesh: Mesh, buffers: &[Data], store: &ResourceStore, path_base: &Path, node: &Node, prefab: &mut Prefab, webp: bool, ) -> Result<()> { Ok(for p in mesh.primitives() { let reader = p.reader(|buf| Some(&buffers[buf.index()])); let va_position = reader .read_positions() .map(|iter| { let mut pos_x = vec![]; let mut pos_y = vec![]; let mut pos_z = vec![]; for p in iter { pos_x.push(p[0]); pos_y.push(p[1]); pos_z.push(p[2]); } info!("{} vertex positions", pos_x.len()); Ok::<_, anyhow::Error>([ store.set(&AttributeArray(pos_x))?, store.set(&AttributeArray(pos_y))?, store.set(&AttributeArray(pos_z))?, ]) }) .transpose()?; let va_normal = reader .read_normals() .map(|iter| { let mut normal_x = vec![]; let mut normal_y = vec![]; let mut normal_z = vec![]; for p in iter { normal_x.push(p[0]); normal_y.push(p[1]); normal_z.push(p[2]); } info!("{} vertex normals", normal_x.len()); Ok::<_, anyhow::Error>([ store.set(&AttributeArray(normal_x))?, store.set(&AttributeArray(normal_y))?, store.set(&AttributeArray(normal_z))?, ]) }) .transpose()?; let va_texcoord = reader .read_tex_coords(0) .map(|iter| { let mut texcoord_u = vec![]; let mut texcoord_v = vec![]; for p in iter.into_f32() { texcoord_u.push(p[0]); texcoord_v.push(p[1]); } info!("{} vertex texture coordinates", texcoord_u.len()); Ok::<_, anyhow::Error>([ store.set(&AttributeArray(texcoord_u))?, store.set(&AttributeArray(texcoord_v))?, ]) }) .transpose()?; let va_albedo = reader .read_colors(0) .map(|iter| { let mut color_r = vec![]; let mut color_g = vec![]; let mut color_b = vec![]; for p in iter.into_rgb_f32() { color_r.push(p[0]); color_g.push(p[1]); color_b.push(p[2]); } info!("{} vertex colors", color_r.len()); Ok::<_, anyhow::Error>([ store.set(&AttributeArray(color_r))?, store.set(&AttributeArray(color_g))?, store.set(&AttributeArray(color_b))?, ]) }) .transpose()?; let va_alpha = reader .read_colors(0) .map(|iter| { let mut color_a = vec![]; for p in iter.into_rgba_f32() { color_a.push(p[3]); } let o = if color_a.iter().any(|x| *x != 1.) { info!("{} vertex transmissions", color_a.len()); Some(store.set(&AttributeArray(color_a))?) } else { debug!("vertex transmission pruned"); None }; Ok::<_, anyhow::Error>(o) }) .transpose()? .flatten(); let index = reader .read_indices() .unwrap() .into_u32() .map(|e| e as u16) .array_chunks::<3>() .collect::>(); info!("{} indecies", index.len() * 3); let index = Some(store.set(&IndexArray(index))?); let mut tex_albedo = None; let mut tex_alpha = None; if let Some(tex) = p.material().pbr_metallic_roughness().base_color_texture() { let r = load_texture( "albedo", &store, path_base, &buffers, &tex.texture().source().source(), webp, )?; tex_albedo = Some(r.clone()); tex_alpha = Some(r.clone()); } let mut tex_normal = None; if let Some(tex) = p.material().normal_texture() { tex_normal = Some(load_texture( "normal", &store, path_base, &buffers, &tex.texture().source().source(), webp, )?); } let mut tex_emission = None; if let Some(tex) = p.material().emissive_texture() { tex_emission = Some(load_texture( "emission", &store, path_base, &buffers, &tex.texture().source().source(), webp, )?); } let mut tex_roughness = None; let mut tex_metallic = None; if let Some(tex) = p .material() .pbr_metallic_roughness() .metallic_roughness_texture() { let r = load_texture( "metallic+roughness", &store, path_base, &buffers, &tex.texture().source().source(), webp, )?; tex_roughness = Some(r.clone()); tex_metallic = Some(r.clone()); } let g_metallic = Some(p.material().pbr_metallic_roughness().metallic_factor()); let g_roughness = Some(p.material().pbr_metallic_roughness().roughness_factor()); let base_color = p.material().pbr_metallic_roughness().base_color_factor(); let g_albedo = if base_color[0] != 1. || base_color[1] != 1. || base_color[2] != 1. { info!( "global albedo is r={},g={},b={}", base_color[0], base_color[1], base_color[2] ); Some(Vec3A::new(base_color[0], base_color[1], base_color[2])) } else { debug!("global albedo pruned"); None }; let g_alpha = if base_color[3] != 1. { info!("global transmission is {}", base_color[3]); Some(base_color[3]) } else { debug!("global transmission pruned"); None }; let emission = p.material().emissive_factor(); let g_emission = if emission[0] != 0. || emission[1] != 0. || emission[2] != 0. { info!( "global emission is r={},g={},b={}", base_color[0], base_color[1], base_color[2] ); Some(Vec3A::new(emission[0], emission[1], emission[2])) } else { debug!("global emission pruned"); None }; let mesh = store.set(&MeshPart { g_albedo, g_alpha, g_metallic, g_roughness, g_emission, va_position, va_normal, va_texcoord, va_albedo, va_alpha, tex_albedo, tex_normal, tex_roughness, tex_metallic, tex_alpha, tex_emission, index, g_transmission: None, tex_transmission: None, va_transmission: None, va_emission: None, // not supported by gltf va_metallic: None, // not supported by gltf va_roughness: None, // not supported by gltf })?; let mat = node.transform().matrix(); let aff = Affine3A::from_cols_array_2d(&[ [mat[0][0], mat[0][1], mat[0][2]], [mat[1][0], mat[1][1], mat[1][2]], [mat[2][0], mat[2][1], mat[2][2]], [mat[3][0], mat[3][1], mat[3][2]], ]); prefab.mesh.push((aff, mesh)) }) }