use std::sync::{Arc, RwLock};
use crate::procedural::{IndexBuffer, RenderMesh};
use crate::resource::gpu_vector::{AllocationType, BufferType, GPUVec};
use crate::resource::vertex_index::VertexIndex;
use glamx::{Vec2, Vec3};
pub struct GpuMesh3d {
coords: Arc<RwLock<GPUVec<Vec3>>>,
faces: Arc<RwLock<GPUVec<[VertexIndex; 3]>>>,
normals: Arc<RwLock<GPUVec<Vec3>>>,
uvs: Arc<RwLock<GPUVec<Vec2>>>,
edges: Option<Arc<RwLock<GPUVec<[VertexIndex; 2]>>>>,
skin_vertices: Option<SkinVertexData>,
morph: Option<MorphTargets>,
}
#[derive(Clone)]
pub struct SkinVertexData {
joints: Arc<RwLock<GPUVec<[u32; 4]>>>,
weights: Arc<RwLock<GPUVec<[f32; 4]>>>,
}
impl SkinVertexData {
pub fn new(joints: Vec<[u32; 4]>, weights: Vec<[f32; 4]>) -> Self {
SkinVertexData {
joints: Arc::new(RwLock::new(GPUVec::new(
joints,
BufferType::Storage,
AllocationType::StaticDraw,
))),
weights: Arc::new(RwLock::new(GPUVec::new(
weights,
BufferType::Storage,
AllocationType::StaticDraw,
))),
}
}
}
#[derive(Clone)]
pub struct MorphTargets {
num_targets: usize,
num_vertices: usize,
positions: Arc<RwLock<GPUVec<[f32; 4]>>>,
normals: Option<Arc<RwLock<GPUVec<[f32; 4]>>>>,
}
impl MorphTargets {
pub fn new(
num_targets: usize,
num_vertices: usize,
positions: Vec<[f32; 4]>,
normals: Option<Vec<[f32; 4]>>,
) -> Self {
MorphTargets {
num_targets,
num_vertices,
positions: Arc::new(RwLock::new(GPUVec::new(
positions,
BufferType::Storage,
AllocationType::StaticDraw,
))),
normals: normals.map(|n| {
Arc::new(RwLock::new(GPUVec::new(
n,
BufferType::Storage,
AllocationType::StaticDraw,
)))
}),
}
}
pub fn num_targets(&self) -> usize {
self.num_targets
}
pub fn num_vertices(&self) -> usize {
self.num_vertices
}
pub fn has_normals(&self) -> bool {
self.normals.is_some()
}
}
impl GpuMesh3d {
pub fn new(
coords: Vec<Vec3>,
faces: Vec<[VertexIndex; 3]>,
normals: Option<Vec<Vec3>>,
uvs: Option<Vec<Vec2>>,
dynamic_draw: bool,
) -> GpuMesh3d {
let normals = match normals {
Some(ns) => ns,
None => GpuMesh3d::compute_normals_array(&coords[..], &faces[..]),
};
let uvs = match uvs {
Some(us) => us,
None => vec![Vec2::ZERO; coords.len()],
};
let location = if dynamic_draw {
AllocationType::DynamicDraw
} else {
AllocationType::StaticDraw
};
let cs = Arc::new(RwLock::new(GPUVec::new(
coords,
BufferType::Array,
location,
)));
let fs = Arc::new(RwLock::new(GPUVec::new(
faces,
BufferType::ElementArray,
location,
)));
let ns = Arc::new(RwLock::new(GPUVec::new(
normals,
BufferType::Array,
location,
)));
let us = Arc::new(RwLock::new(GPUVec::new(uvs, BufferType::Array, location)));
GpuMesh3d::new_with_gpu_vectors(cs, fs, ns, us)
}
pub fn from_render_mesh(mesh: RenderMesh, dynamic_draw: bool) -> GpuMesh3d {
let mut mesh = mesh;
mesh.unify_index_buffer();
let RenderMesh {
coords,
normals,
uvs,
indices,
} = mesh;
let faces: Vec<[VertexIndex; 3]> = indices
.unwrap_unified()
.into_iter()
.map(|idx| {
[
idx[0] as VertexIndex,
idx[1] as VertexIndex,
idx[2] as VertexIndex,
]
})
.collect();
GpuMesh3d::new(coords, faces, normals, uvs, dynamic_draw)
}
pub fn to_render_mesh(&self) -> Option<RenderMesh> {
if !self.coords.read().unwrap().is_on_ram()
|| !self.faces.read().unwrap().is_on_ram()
|| !self.normals.read().unwrap().is_on_ram()
|| !self.uvs.read().unwrap().is_on_ram()
{
return None;
}
let coords = self.coords.read().unwrap().to_owned();
let faces = self.faces.read().unwrap().to_owned();
let normals = self.normals.read().unwrap().to_owned();
let uvs = self.uvs.read().unwrap().to_owned();
Some(RenderMesh::new(
coords.unwrap(),
normals,
uvs,
Some(IndexBuffer::Unified(faces.unwrap().into_iter().collect())),
))
}
pub fn new_with_gpu_vectors(
coords: Arc<RwLock<GPUVec<Vec3>>>,
faces: Arc<RwLock<GPUVec<[VertexIndex; 3]>>>,
normals: Arc<RwLock<GPUVec<Vec3>>>,
uvs: Arc<RwLock<GPUVec<Vec2>>>,
) -> GpuMesh3d {
GpuMesh3d {
coords,
faces,
normals,
uvs,
edges: None,
skin_vertices: None,
morph: None,
}
}
pub fn set_skin_vertices(&mut self, skin: SkinVertexData) {
self.skin_vertices = Some(skin);
}
pub fn has_skin_vertices(&self) -> bool {
self.skin_vertices.is_some()
}
pub fn set_morph_targets(&mut self, morph: MorphTargets) {
self.morph = Some(morph);
}
pub fn has_morph(&self) -> bool {
self.morph.is_some()
}
pub fn morph_target_count(&self) -> usize {
self.morph.as_ref().map(|m| m.num_targets).unwrap_or(0)
}
pub fn morph_vertex_count(&self) -> usize {
self.morph.as_ref().map(|m| m.num_vertices).unwrap_or(0)
}
pub fn has_morph_normals(&self) -> bool {
self.morph
.as_ref()
.map(|m| m.has_normals())
.unwrap_or(false)
}
pub fn morph_positions(&self) -> Option<&Arc<RwLock<GPUVec<[f32; 4]>>>> {
self.morph.as_ref().map(|m| &m.positions)
}
pub fn morph_normals(&self) -> Option<&Arc<RwLock<GPUVec<[f32; 4]>>>> {
self.morph.as_ref().and_then(|m| m.normals.as_ref())
}
pub fn skin_joints(&self) -> Option<&Arc<RwLock<GPUVec<[u32; 4]>>>> {
self.skin_vertices.as_ref().map(|s| &s.joints)
}
pub fn skin_weights(&self) -> Option<&Arc<RwLock<GPUVec<[f32; 4]>>>> {
self.skin_vertices.as_ref().map(|s| &s.weights)
}
pub fn ensure_skin_on_gpu(&self) -> Option<(&wgpu::Buffer, &wgpu::Buffer)> {
let skin = self.skin_vertices.as_ref()?;
skin.joints.write().unwrap().load_to_gpu();
skin.weights.write().unwrap().load_to_gpu();
let joints = skin.joints.read().unwrap();
let weights = skin.weights.read().unwrap();
if joints.buffer().is_none() || weights.buffer().is_none() {
return None;
}
unsafe {
let joints_ptr = joints.buffer().unwrap() as *const wgpu::Buffer;
let weights_ptr = weights.buffer().unwrap() as *const wgpu::Buffer;
Some((&*joints_ptr, &*weights_ptr))
}
}
pub fn ensure_morph_on_gpu(&self) -> Option<(&wgpu::Buffer, Option<&wgpu::Buffer>)> {
let morph = self.morph.as_ref()?;
morph.positions.write().unwrap().load_to_gpu();
if let Some(n) = &morph.normals {
n.write().unwrap().load_to_gpu();
}
let positions = morph.positions.read().unwrap();
positions.buffer()?;
unsafe {
let pos_ptr = positions.buffer().unwrap() as *const wgpu::Buffer;
let nrm_ptr = match &morph.normals {
Some(n) => n
.read()
.unwrap()
.buffer()
.map(|b| &*(b as *const wgpu::Buffer)),
None => None,
};
Some((&*pos_ptr, nrm_ptr))
}
}
pub fn ensure_on_gpu(
&mut self,
) -> Option<(&wgpu::Buffer, &wgpu::Buffer, &wgpu::Buffer, &wgpu::Buffer)> {
self.coords.write().unwrap().load_to_gpu();
self.faces.write().unwrap().load_to_gpu();
self.normals.write().unwrap().load_to_gpu();
self.uvs.write().unwrap().load_to_gpu();
let coords = self.coords.read().unwrap();
let faces = self.faces.read().unwrap();
let normals = self.normals.read().unwrap();
let uvs = self.uvs.read().unwrap();
if coords.buffer().is_none()
|| faces.buffer().is_none()
|| normals.buffer().is_none()
|| uvs.buffer().is_none()
{
return None;
}
unsafe {
let coords_ptr = coords.buffer().unwrap() as *const wgpu::Buffer;
let faces_ptr = faces.buffer().unwrap() as *const wgpu::Buffer;
let normals_ptr = normals.buffer().unwrap() as *const wgpu::Buffer;
let uvs_ptr = uvs.buffer().unwrap() as *const wgpu::Buffer;
Some((&*coords_ptr, &*faces_ptr, &*normals_ptr, &*uvs_ptr))
}
}
pub fn coords_buffer(&self) -> Option<&wgpu::Buffer> {
None
}
pub fn faces_buffer(&self) -> Option<&wgpu::Buffer> {
None
}
pub fn normals_buffer(&self) -> Option<&wgpu::Buffer> {
None
}
pub fn uvs_buffer(&self) -> Option<&wgpu::Buffer> {
None
}
pub fn ensure_edges(&mut self) {
if self.edges.is_none() {
let mut edges = Vec::new();
for face in self.faces.read().unwrap().data().as_ref().unwrap() {
edges.push([face[0], face[1]]);
edges.push([face[1], face[2]]);
edges.push([face[2], face[0]]);
}
let gpu_edges =
GPUVec::new(edges, BufferType::ElementArray, AllocationType::StaticDraw);
self.edges = Some(Arc::new(RwLock::new(gpu_edges)));
}
}
pub fn ensure_edges_on_gpu(&mut self) {
self.ensure_edges();
self.edges.as_mut().unwrap().write().unwrap().load_to_gpu();
}
pub fn edges(&self) -> &Option<Arc<RwLock<GPUVec<[VertexIndex; 2]>>>> {
&self.edges
}
pub fn num_pts(&self) -> usize {
self.faces.read().unwrap().len() * 3
}
pub fn num_indices(&self) -> u32 {
(self.faces.read().unwrap().len() * 3) as u32
}
pub fn num_edge_indices(&self) -> u32 {
self.edges
.as_ref()
.map(|e| (e.read().unwrap().len() * 2) as u32)
.unwrap_or(0)
}
pub fn recompute_normals(&mut self) {
GpuMesh3d::compute_normals(
&self.coords.read().unwrap().data().as_ref().unwrap()[..],
&self.faces.read().unwrap().data().as_ref().unwrap()[..],
self.normals.write().unwrap().data_mut().as_mut().unwrap(),
);
}
pub fn faces(&self) -> &Arc<RwLock<GPUVec<[VertexIndex; 3]>>> {
&self.faces
}
pub fn normals(&self) -> &Arc<RwLock<GPUVec<Vec3>>> {
&self.normals
}
pub fn coords(&self) -> &Arc<RwLock<GPUVec<Vec3>>> {
&self.coords
}
pub fn uvs(&self) -> &Arc<RwLock<GPUVec<Vec2>>> {
&self.uvs
}
pub fn compute_normals_array(coordinates: &[Vec3], faces: &[[VertexIndex; 3]]) -> Vec<Vec3> {
let mut res = Vec::new();
GpuMesh3d::compute_normals(coordinates, faces, &mut res);
res
}
pub fn compute_normals(
coordinates: &[Vec3],
faces: &[[VertexIndex; 3]],
normals: &mut Vec<Vec3>,
) {
let mut divisor: Vec<f32> = vec![0f32; coordinates.len()];
normals.clear();
normals.extend(std::iter::repeat_n(Vec3::ZERO, coordinates.len()));
for f in faces.iter() {
let edge1 = coordinates[f[1] as usize] - coordinates[f[0] as usize];
let edge2 = coordinates[f[2] as usize] - coordinates[f[0] as usize];
let cross = edge1.cross(edge2);
let normal = if cross != Vec3::ZERO {
cross.normalize()
} else {
cross
};
normals[f[0] as usize] += normal;
normals[f[1] as usize] += normal;
normals[f[2] as usize] += normal;
divisor[f[0] as usize] += 1.0;
divisor[f[1] as usize] += 1.0;
divisor[f[2] as usize] += 1.0;
}
for (n, divisor) in normals.iter_mut().zip(divisor.iter()) {
*n /= *divisor
}
}
}
impl From<RenderMesh> for GpuMesh3d {
fn from(value: RenderMesh) -> Self {
Self::from_render_mesh(value, false)
}
}