/*
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_transmission = None;
if let Some(tex) = p
.material()
.transmission()
.map(|t| t.transmission_texture())
.flatten()
{
tex_transmission = Some(load_texture(
"transmission",
&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 alpha is {}", base_color[3]);
Some(base_color[3])
} else {
debug!("global alpha 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 transmission = p
.material()
.transmission()
.map(|t| t.transmission_factor())
.unwrap_or(0.);
let g_transmission = if transmission != 0. {
info!("global transmission is {transmission}");
Some(transmission)
} else {
debug!("global transmission pruned");
None
};
let mesh = store.set(&MeshPart {
g_albedo,
g_alpha,
g_metallic,
g_roughness,
g_emission,
g_transmission,
va_position,
va_normal,
va_texcoord,
va_albedo,
va_alpha,
tex_albedo,
tex_normal,
tex_roughness,
tex_metallic,
tex_alpha,
tex_emission,
tex_transmission,
index,
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))
})
}