use std::{collections::HashMap, rc::Rc};
use anyhow::Result;
use cad_import::{
structure::{CADData, Material, Node, Shape},
ID,
};
use glow::HasContext;
use nalgebra_glm::Mat4;
use super::gpu_mesh::GPUMesh;
pub struct GPUMeshWithMaterial<C: HasContext> {
pub material: Rc<Material>,
pub mesh: GPUMesh<C>,
}
pub struct GPUShape<C: HasContext> {
pub parts: Vec<GPUMeshWithMaterial<C>>,
}
pub struct GPUShapeInstance {
pub transform: Mat4,
pub shape_index: usize,
}
pub struct GPUData<C: HasContext> {
shapes: Vec<GPUShape<C>>,
instances: Vec<GPUShapeInstance>,
}
impl<C: HasContext> GPUData<C> {
pub fn new() -> Self {
Self {
shapes: Vec::new(),
instances: Vec::new(),
}
}
pub fn add_cad_data(&mut self, context: &C, cad_data: &CADData) -> Result<()> {
let root_node = cad_data.get_root_node();
let traversal_context = TraversalContext::new(root_node);
let mut traversal_data = TraversalData::new();
self.traverse(context, root_node, traversal_context, &mut traversal_data)?;
Ok(())
}
pub fn get_shapes(&self) -> &[GPUShape<C>] {
&self.shapes
}
pub fn get_instances(&self) -> &[GPUShapeInstance] {
&self.instances
}
fn traverse(
&mut self,
context: &C,
node: &Node,
traversal_context: TraversalContext,
traversal_data: &mut TraversalData,
) -> Result<()> {
let shapes = node.get_shapes();
for shape in shapes {
let shape_index = self.get_shape_index(context, shape, traversal_data)?;
self.instances.push(GPUShapeInstance {
transform: traversal_context.transform,
shape_index,
});
}
for child in node.get_children().iter() {
let child_traversal_context = traversal_context.derive(child);
self.traverse(context, child, child_traversal_context, traversal_data)?;
}
Ok(())
}
fn get_shape_index(
&mut self,
context: &C,
shape: &Shape,
traversal_data: &mut TraversalData,
) -> Result<usize> {
let shape_id = shape.get_id();
match traversal_data.shape_map.get(&shape_id) {
Some(index) => {
return Ok(*index);
}
None => {}
}
let index = traversal_data.shape_map.len();
let gpu_shape = Self::create_gpu_shape(context, shape)?;
self.shapes.push(gpu_shape);
traversal_data.shape_map.insert(shape_id, index);
Ok(index)
}
fn create_gpu_shape(context: &C, shape: &Shape) -> Result<GPUShape<C>> {
let mut parts = Vec::with_capacity(shape.get_parts().len());
for part in shape.get_parts() {
let material = part.get_material();
let gpu_mesh = GPUMesh::new(context, part.get_mesh().as_ref())?;
let gpu_part = GPUMeshWithMaterial {
material: material.clone(),
mesh: gpu_mesh,
};
parts.push(gpu_part);
}
Ok(GPUShape { parts })
}
}
#[derive(Clone)]
struct TraversalContext {
transform: Mat4,
}
impl TraversalContext {
pub fn new(root_node: &Node) -> Self {
let transform: Mat4 = match root_node.get_transform() {
Some(t) => t,
None => Mat4::identity(),
};
Self { transform }
}
pub fn derive(&self, node: &Node) -> Self {
let mut result = self.clone();
match node.get_transform() {
Some(t) => {
result.transform *= t;
}
None => {}
}
result
}
}
struct TraversalData {
pub shape_map: HashMap<ID, usize>,
}
impl TraversalData {
pub fn new() -> Self {
Self {
shape_map: HashMap::new(),
}
}
}