From 3ac392bdf3996f87de029065aa595e2f87f6e03a Mon Sep 17 00:00:00 2001 From: QuantumEntangledAndy Date: Mon, 18 Jan 2021 16:58:53 +0700 Subject: [PATCH 1/6] Use octtree for normals and uvs seperatly --- gaiku-common/src/boundary.rs | 1 + gaiku-common/src/mesh.rs | 290 +++++++++++++++++++---------------- 2 files changed, 162 insertions(+), 129 deletions(-) diff --git a/gaiku-common/src/boundary.rs b/gaiku-common/src/boundary.rs index 928a268..37a369f 100644 --- a/gaiku-common/src/boundary.rs +++ b/gaiku-common/src/boundary.rs @@ -41,6 +41,7 @@ impl Boundary { && self.end.z > point.z } + #[allow(dead_code)] pub fn intersects(&self, range: &Boundary) -> bool { !(range.start.x > self.start.x || range.start.y > self.start.y diff --git a/gaiku-common/src/mesh.rs b/gaiku-common/src/mesh.rs index 88e2f28..d8084c2 100755 --- a/gaiku-common/src/mesh.rs +++ b/gaiku-common/src/mesh.rs @@ -1,4 +1,9 @@ use crate::boundary::Boundary; +use std::{ + collections::HashSet, + convert::TryInto, + sync::{Arc, Mutex}, +}; /// Base common denominator across all the mesh implementations used. pub trait Meshify { @@ -167,57 +172,16 @@ impl Meshify for Mesh { */ } -#[derive(Clone, Debug)] -struct MeshBuilderData { - position: [f32; 3], - normal: Option<[f32; 3]>, - uv: Option<[f32; 2]>, - atlas_index: u16, - index: u32, -} - -impl MeshBuilderData { - fn new( - position: [f32; 3], - normal: Option<[f32; 3]>, - uv: Option<[f32; 2]>, - atlas_index: u16, - index: u32, - ) -> Self { - MeshBuilderData { - position, - normal, - uv, - atlas_index, - index, - } - } -} - -impl From<([f32; 3], Option<[f32; 3]>, Option<[f32; 2]>, u16, u32)> for MeshBuilderData { - fn from( - (position, normal, uv, atlas_index, index): ( - [f32; 3], - Option<[f32; 3]>, - Option<[f32; 2]>, - u16, - u32, - ), - ) -> Self { - MeshBuilderData::new(position, normal, uv, atlas_index, index) - } -} - #[derive(Debug)] enum MeshBuilderOctreeNode { - Leaf(Vec<(MeshBuilderData, Boundary)>), + Leaf(Vec<([f32; 3], Boundary, usize)>), Subtree(Box<[MeshBuilderOctree; 8]>), } enum InsertResult { AlreadyExists(u32), FailedInsert, - Inserted, + Inserted(u32), OutOfBounds, } @@ -226,51 +190,56 @@ struct MeshBuilderOctree { bucket: usize, node: MeshBuilderOctreeNode, split_at: usize, + current_index: Arc>, } impl MeshBuilderOctree { fn new(boundary: Boundary, bucket: usize, split_at: usize) -> Self { + Self::new_with_index(boundary, bucket, split_at, Arc::new(Mutex::new(0))) + } + + fn new_with_index( + boundary: Boundary, + bucket: usize, + split_at: usize, + index: Arc>, + ) -> Self { Self { boundary, bucket, node: MeshBuilderOctreeNode::Leaf(vec![]), split_at, + current_index: index, } } - fn insert(&mut self, leaf: &MeshBuilderData) -> InsertResult { - if self.boundary.contains(&leaf.position.into()) { + fn insert(&mut self, leaf: &[f32; 3]) -> InsertResult { + if self.boundary.contains(&leaf.clone().into()) { match &mut self.node { MeshBuilderOctreeNode::Leaf(leafs) => { - let leaf_normal = if let Some(normal) = leaf.normal { - Some(Boundary::new(normal, [1e-5, 1e-5, 1e-5])) - } else { - None - }; - - for (data, position) in leafs.iter() { - if position.contains(&leaf.position.into()) - && data.atlas_index == leaf.atlas_index - && if let (Some(leaf_normal), Some(data_normal)) = (leaf_normal.as_ref(), data.normal) - { - leaf_normal.contains(&data_normal.into()) - } else { - false - } - { - return InsertResult::AlreadyExists(data.index); + for (_, position, index) in leafs.iter() { + if position.contains(&leaf.clone().into()) { + return InsertResult::AlreadyExists((*index).try_into().unwrap()); } } - let boundary = Boundary::new(leaf.position, [1e-5, 1e-5, 1e-5]); - leafs.push((leaf.clone(), boundary)); + let boundary = Boundary::new(leaf.clone(), [1e-5, 1e-5, 1e-5]); + let mut current_index = (*self.current_index).lock().unwrap(); + let new_index = *current_index; + leafs.push((leaf.clone(), boundary, new_index)); + *current_index += 1; if leafs.len() > self.split_at && self.bucket > 0 { let leafs = leafs.clone(); - let mut nodes = subdivide(&self.boundary, self.bucket, self.split_at); - for (leaf, _) in leafs.iter() { + let mut nodes = subdivide( + &self.boundary, + self.bucket, + self.split_at, + self.current_index.clone(), + ); + for (leaf, _, _) in leafs.iter() { for node in nodes.iter_mut() { - if let InsertResult::Inserted = node.insert(leaf) { + if let InsertResult::Inserted(_) = node.insert(leaf) { break; } } @@ -279,12 +248,12 @@ impl MeshBuilderOctree { self.node = MeshBuilderOctreeNode::Subtree(nodes); } - InsertResult::Inserted + InsertResult::Inserted(new_index.try_into().unwrap()) } MeshBuilderOctreeNode::Subtree(nodes) => { for node in nodes.iter_mut() { match node.insert(leaf) { - InsertResult::Inserted => return InsertResult::Inserted, + InsertResult::Inserted(index) => return InsertResult::Inserted(index), InsertResult::AlreadyExists(index) => return InsertResult::AlreadyExists(index), InsertResult::FailedInsert => return InsertResult::FailedInsert, _ => {} @@ -299,18 +268,36 @@ impl MeshBuilderOctree { } } - fn get_all(&self) -> Vec { + fn get_all_ww_index(&self) -> Vec<([f32; 3], usize)> { + // Unlike get_all this returns unsorted but with the index included match &self.node { MeshBuilderOctreeNode::Leaf(leafs) => { - leafs.iter().map(|(d, _)| d.clone()).collect::>() + // Get data + index to sort by + leafs + .iter() + .map(|(d, _, index)| (d.clone(), *index)) + .collect::>() } MeshBuilderOctreeNode::Subtree(nodes) => nodes .iter() - .map(|n| n.get_all()) + .map(|n| n.get_all_ww_index()) .flatten() .collect::>(), } } + + fn get_all(&self) -> Vec<[f32; 3]> { + // This gets all data sorted by index + + // Get all data with the index + let mut raw_table = self.get_all_ww_index(); + + // sort it by that index + raw_table.sort_by(|(_, a_index), (_, b_index)| a_index.partial_cmp(&b_index).unwrap()); + + // return the sorted data only not the index + raw_table.iter().map(|(d, _)| d.clone()).collect::>() + } } impl std::fmt::Debug for MeshBuilderOctree { @@ -339,18 +326,28 @@ impl From<[f32; 3]> for Position { /// Helper component that makes easy to build a triangle list mesh. #[derive(Debug)] pub struct MeshBuilder { - current_index: u32, - indices: Vec, - cache: MeshBuilderOctree, + unique_nodes: HashSet, + vertex_cache: MeshBuilderOctree, + normal_cache: MeshBuilderOctree, + uv_cache: MeshBuilderOctree, +} + +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +struct NodeIndices { + vertex: usize, + normal: Option, + uv: Option, + atlas: u16, } impl MeshBuilder { /// Crates a new mesh centered at a position and size. pub fn create(center: [f32; 3], size: [f32; 3]) -> Self { Self { - current_index: 0, - indices: vec![], - cache: MeshBuilderOctree::new(Boundary::new(center, size), 3, 25), + unique_nodes: HashSet::new(), + vertex_cache: MeshBuilderOctree::new(Boundary::new(center, size), 3, 25), + normal_cache: MeshBuilderOctree::new(Boundary::new([0., 0., 0.], [2., 2., 2.]), 3, 25), + uv_cache: MeshBuilderOctree::new(Boundary::new([0.5, 0.5, 0.5], [1., 1., 1.]), 3, 25), } } @@ -364,16 +361,40 @@ impl MeshBuilder { uv: Option<[f32; 2]>, atlas_index: u16, ) { - let mesh_data = MeshBuilderData::new(position, normal, uv, atlas_index, self.current_index); - match self.cache.insert(&mesh_data) { - InsertResult::Inserted => { - self.indices.push(self.current_index); - self.current_index += 1; + let vertex_index = match self.vertex_cache.insert(&position) { + InsertResult::Inserted(index) => index.try_into().unwrap(), + InsertResult::AlreadyExists(index) => index.try_into().unwrap(), + InsertResult::FailedInsert => panic!("Failed to insert position {:?}", position), + InsertResult::OutOfBounds => panic!("Out of bounds position {:?}", position), + }; + let normal_index = if let Some(normal) = normal { + match self.normal_cache.insert(&normal) { + InsertResult::Inserted(index) => Some(index.try_into().unwrap()), + InsertResult::AlreadyExists(index) => Some(index.try_into().unwrap()), + InsertResult::FailedInsert => panic!("Failed to insert normal {:?}", normal), + InsertResult::OutOfBounds => panic!("Out of bounds normal {:?}", normal), } - InsertResult::AlreadyExists(index) => self.indices.push(index), - InsertResult::FailedInsert => panic!("Failed to insert {:?}", mesh_data), - InsertResult::OutOfBounds => panic!("Out of bounds {:?}", mesh_data), - } + } else { + None + }; + let uv_index = if let Some(uv) = uv { + match self.normal_cache.insert(&[uv[0], uv[1], 0.]) { + // Ignore w coordinate for now + InsertResult::Inserted(index) => Some(index.try_into().unwrap()), + InsertResult::AlreadyExists(index) => Some(index.try_into().unwrap()), + InsertResult::FailedInsert => panic!("Failed to insert uv {:?}", uv), + InsertResult::OutOfBounds => panic!("Out of bounds uv {:?}", uv), + } + } else { + None + }; + + self.unique_nodes.insert(NodeIndices { + vertex: vertex_index, + normal: normal_index, + uv: uv_index, + atlas: atlas_index, + }); } /// Inserts the triangle and generate the index if needed, otherwise use an existing index. @@ -425,26 +446,36 @@ impl MeshBuilder { where M: Meshify, { - if !self.indices.is_empty() { - let mut data = self.cache.get_all(); - data.sort_by(|a, b| a.index.partial_cmp(&b.index).unwrap()); + if !self.unique_nodes.is_empty() { + let vertex_table = self.vertex_cache.get_all(); + let normal_table = self.normal_cache.get_all(); + let uv_table = self.uv_cache.get_all(); - let indices = self.indices.clone(); - let mut positions = vec![]; - let mut normals = vec![]; - let mut uvs = vec![]; + // Iter of a hashset is in arbitary order so we collect to ensure order is fixed for rest of build + let unique_nodes: Vec = self.unique_nodes.iter().cloned().collect(); - for row in data.iter() { - positions.push(row.position); - - if let Some(normal) = row.normal { - normals.push(normal); - } - - if let Some(uv) = row.uv { - uvs.push(uv); - } - } + let indices = unique_nodes + .iter() + .enumerate() + .map(|(i, _)| i.try_into().unwrap()) + .collect(); + let positions = unique_nodes + .iter() + .map(|d| vertex_table[d.vertex].clone()) + .collect(); + let normals = unique_nodes + .iter() + .filter(|d| d.normal.is_some()) // Might be better to use a dud value like [0.,0.,0.] instead + .map(|d| normal_table[d.normal.unwrap()].clone()) + .collect(); + let uvs = unique_nodes + .iter() + .filter(|d| d.uv.is_some()) // Might be better to use a dud value like [0.,0.] instead + .map(|d| { + let uvw = uv_table[d.uv.unwrap()].clone(); + [uvw[0], uvw[2]] + }) + .collect(); Some(M::with(indices, positions, normals, uvs)) } else { @@ -460,7 +491,12 @@ impl Default for MeshBuilder { } #[allow(clippy::many_single_char_names)] -fn subdivide(boundary: &Boundary, bucket: usize, split_at: usize) -> Box<[MeshBuilderOctree; 8]> { +fn subdivide( + boundary: &Boundary, + bucket: usize, + split_at: usize, + current_index: Arc>, +) -> Box<[MeshBuilderOctree; 8]> { let w = boundary.size.x / 2.0; let h = boundary.size.y / 2.0; let d = boundary.size.z / 2.0; @@ -476,45 +512,53 @@ fn subdivide(boundary: &Boundary, bucket: usize, split_at: usize) -> Box<[MeshBu let new_bucket = bucket - 1; Box::new([ - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x - hw, y + hh, z + hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x + hw, y + hh, z + hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x - hw, y + hh, z - hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x + hw, y + hh, z - hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x - hw, y - hh, z + hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x + hw, y - hh, z + hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x - hw, y - hh, z - hd], size), new_bucket, split_at, + current_index.clone(), ), - MeshBuilderOctree::new( + MeshBuilderOctree::new_with_index( Boundary::new([x + hw, y - hh, z - hd], size), new_bucket, split_at, + current_index.clone(), ), ]) } @@ -530,13 +574,7 @@ mod test { for x in 0..4 { for y in 0..4 { for z in 0..4 { - tree.insert(&MeshBuilderData::new( - [x as f32, y as f32, z as f32], - None, - None, - 0, - 0, - )); + tree.insert([x as f32, y as f32, z as f32]); } } } @@ -553,13 +591,7 @@ mod test { let mut tree = MeshBuilderOctree::new(Boundary::new([0.0, 0.0, 0.0], [16.0, 16.0, 16.0]), 3, 25); - match tree.insert(&MeshBuilderData::new( - [0.0, 0.0, 0.0], - Some([0.0, 0.0, 0.0]), - Some([0.0, 0.0]), - 0, - 0, - )) { + match tree.insert(&[0.0, 0.0, 0.0]) { InsertResult::Inserted => assert!(true), _ => assert!(false), } @@ -572,7 +604,7 @@ mod test { let mut tree = MeshBuilderOctree::new(Boundary::new([8.0, 8.0, 8.0], [16.0, 16.0, 16.0]), 3, 25); - match tree.insert(&MeshBuilderData::new([3.5, 16.0, 12.5], None, None, 0, 0)) { + match tree.insert(&[3.5, 16.0, 12.5]) { InsertResult::Inserted => assert!(true), _ => { println!("{:#?}", &tree); From 28b4bbb81d94b5da010e83de69c917a64e75ac72 Mon Sep 17 00:00:00 2001 From: QuantumEntangledAndy Date: Mon, 18 Jan 2021 19:33:47 +0700 Subject: [PATCH 2/6] Bug fix and use AtomicUsize for octtree counter --- gaiku-common/src/mesh.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/gaiku-common/src/mesh.rs b/gaiku-common/src/mesh.rs index d8084c2..7050c19 100755 --- a/gaiku-common/src/mesh.rs +++ b/gaiku-common/src/mesh.rs @@ -2,7 +2,10 @@ use crate::boundary::Boundary; use std::{ collections::HashSet, convert::TryInto, - sync::{Arc, Mutex}, + sync::{ + atomic::{AtomicUsize, Ordering}, + Arc, + }, }; /// Base common denominator across all the mesh implementations used. @@ -190,19 +193,19 @@ struct MeshBuilderOctree { bucket: usize, node: MeshBuilderOctreeNode, split_at: usize, - current_index: Arc>, + current_index: Arc, } impl MeshBuilderOctree { fn new(boundary: Boundary, bucket: usize, split_at: usize) -> Self { - Self::new_with_index(boundary, bucket, split_at, Arc::new(Mutex::new(0))) + Self::new_with_index(boundary, bucket, split_at, Arc::new(AtomicUsize::new(0))) } fn new_with_index( boundary: Boundary, bucket: usize, split_at: usize, - index: Arc>, + index: Arc, ) -> Self { Self { boundary, @@ -224,10 +227,8 @@ impl MeshBuilderOctree { } let boundary = Boundary::new(leaf.clone(), [1e-5, 1e-5, 1e-5]); - let mut current_index = (*self.current_index).lock().unwrap(); - let new_index = *current_index; + let new_index = (*self.current_index).fetch_add(1, Ordering::SeqCst); leafs.push((leaf.clone(), boundary, new_index)); - *current_index += 1; if leafs.len() > self.split_at && self.bucket > 0 { let leafs = leafs.clone(); @@ -378,7 +379,7 @@ impl MeshBuilder { None }; let uv_index = if let Some(uv) = uv { - match self.normal_cache.insert(&[uv[0], uv[1], 0.]) { + match self.uv_cache.insert(&[uv[0], uv[1], 0.]) { // Ignore w coordinate for now InsertResult::Inserted(index) => Some(index.try_into().unwrap()), InsertResult::AlreadyExists(index) => Some(index.try_into().unwrap()), @@ -495,7 +496,7 @@ fn subdivide( boundary: &Boundary, bucket: usize, split_at: usize, - current_index: Arc>, + current_index: Arc, ) -> Box<[MeshBuilderOctree; 8]> { let w = boundary.size.x / 2.0; let h = boundary.size.y / 2.0; From 408d7091a9ef01ba413fdb6a20e286000b7f3786 Mon Sep 17 00:00:00 2001 From: QuantumEntangledAndy Date: Mon, 18 Jan 2021 20:31:20 +0700 Subject: [PATCH 3/6] Only increment index when inserting a new vert into the octtree --- gaiku-common/src/mesh.rs | 45 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/gaiku-common/src/mesh.rs b/gaiku-common/src/mesh.rs index 7050c19..cf6bdea 100755 --- a/gaiku-common/src/mesh.rs +++ b/gaiku-common/src/mesh.rs @@ -1,6 +1,5 @@ use crate::boundary::Boundary; use std::{ - collections::HashSet, convert::TryInto, sync::{ atomic::{AtomicUsize, Ordering}, @@ -216,7 +215,7 @@ impl MeshBuilderOctree { } } - fn insert(&mut self, leaf: &[f32; 3]) -> InsertResult { + fn insert_with_index(&mut self, leaf: &[f32; 3], next_index: usize) -> InsertResult { if self.boundary.contains(&leaf.clone().into()) { match &mut self.node { MeshBuilderOctreeNode::Leaf(leafs) => { @@ -227,8 +226,7 @@ impl MeshBuilderOctree { } let boundary = Boundary::new(leaf.clone(), [1e-5, 1e-5, 1e-5]); - let new_index = (*self.current_index).fetch_add(1, Ordering::SeqCst); - leafs.push((leaf.clone(), boundary, new_index)); + leafs.push((leaf.clone(), boundary, next_index)); if leafs.len() > self.split_at && self.bucket > 0 { let leafs = leafs.clone(); @@ -238,9 +236,9 @@ impl MeshBuilderOctree { self.split_at, self.current_index.clone(), ); - for (leaf, _, _) in leafs.iter() { + for (leaf, _, current_index) in leafs.iter() { for node in nodes.iter_mut() { - if let InsertResult::Inserted(_) = node.insert(leaf) { + if let InsertResult::Inserted(_) = node.insert_with_index(leaf, *current_index) { break; } } @@ -249,11 +247,11 @@ impl MeshBuilderOctree { self.node = MeshBuilderOctreeNode::Subtree(nodes); } - InsertResult::Inserted(new_index.try_into().unwrap()) + InsertResult::Inserted(next_index.try_into().unwrap()) } MeshBuilderOctreeNode::Subtree(nodes) => { for node in nodes.iter_mut() { - match node.insert(leaf) { + match node.insert_with_index(leaf, next_index) { InsertResult::Inserted(index) => return InsertResult::Inserted(index), InsertResult::AlreadyExists(index) => return InsertResult::AlreadyExists(index), InsertResult::FailedInsert => return InsertResult::FailedInsert, @@ -268,6 +266,14 @@ impl MeshBuilderOctree { InsertResult::OutOfBounds } } + fn insert(&mut self, leaf: &[f32; 3]) -> InsertResult { + let next_index = (*self.current_index).load(Ordering::SeqCst); + let result = self.insert_with_index(leaf, next_index); + if let InsertResult::Inserted(_) = result { + (*self.current_index).fetch_add(1, Ordering::SeqCst); + } + result + } fn get_all_ww_index(&self) -> Vec<([f32; 3], usize)> { // Unlike get_all this returns unsorted but with the index included @@ -327,7 +333,7 @@ impl From<[f32; 3]> for Position { /// Helper component that makes easy to build a triangle list mesh. #[derive(Debug)] pub struct MeshBuilder { - unique_nodes: HashSet, + nodes: Vec, vertex_cache: MeshBuilderOctree, normal_cache: MeshBuilderOctree, uv_cache: MeshBuilderOctree, @@ -345,7 +351,7 @@ impl MeshBuilder { /// Crates a new mesh centered at a position and size. pub fn create(center: [f32; 3], size: [f32; 3]) -> Self { Self { - unique_nodes: HashSet::new(), + nodes: Default::default(), vertex_cache: MeshBuilderOctree::new(Boundary::new(center, size), 3, 25), normal_cache: MeshBuilderOctree::new(Boundary::new([0., 0., 0.], [2., 2., 2.]), 3, 25), uv_cache: MeshBuilderOctree::new(Boundary::new([0.5, 0.5, 0.5], [1., 1., 1.]), 3, 25), @@ -390,7 +396,7 @@ impl MeshBuilder { None }; - self.unique_nodes.insert(NodeIndices { + self.nodes.push(NodeIndices { vertex: vertex_index, normal: normal_index, uv: uv_index, @@ -447,29 +453,30 @@ impl MeshBuilder { where M: Meshify, { - if !self.unique_nodes.is_empty() { + if !self.nodes.is_empty() { let vertex_table = self.vertex_cache.get_all(); let normal_table = self.normal_cache.get_all(); let uv_table = self.uv_cache.get_all(); - // Iter of a hashset is in arbitary order so we collect to ensure order is fixed for rest of build - let unique_nodes: Vec = self.unique_nodes.iter().cloned().collect(); - - let indices = unique_nodes + let indices = self + .nodes .iter() .enumerate() .map(|(i, _)| i.try_into().unwrap()) .collect(); - let positions = unique_nodes + let positions = self + .nodes .iter() .map(|d| vertex_table[d.vertex].clone()) .collect(); - let normals = unique_nodes + let normals = self + .nodes .iter() .filter(|d| d.normal.is_some()) // Might be better to use a dud value like [0.,0.,0.] instead .map(|d| normal_table[d.normal.unwrap()].clone()) .collect(); - let uvs = unique_nodes + let uvs = self + .nodes .iter() .filter(|d| d.uv.is_some()) // Might be better to use a dud value like [0.,0.] instead .map(|d| { From 539a32778a26f8cdd857d90084e4923ebb95979d Mon Sep 17 00:00:00 2001 From: QuantumEntangledAndy Date: Tue, 19 Jan 2021 10:06:44 +0700 Subject: [PATCH 4/6] Use hashmap to remove duplicate vertex indices during bake --- gaiku-common/src/mesh.rs | 72 +++++++++++++++++++++++++--------------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/gaiku-common/src/mesh.rs b/gaiku-common/src/mesh.rs index cf6bdea..b9f770a 100755 --- a/gaiku-common/src/mesh.rs +++ b/gaiku-common/src/mesh.rs @@ -1,8 +1,9 @@ use crate::boundary::Boundary; use std::{ + collections::HashMap, convert::TryInto, sync::{ - atomic::{AtomicUsize, Ordering}, + atomic::{AtomicU32, AtomicUsize, Ordering}, Arc, }, }; @@ -333,10 +334,12 @@ impl From<[f32; 3]> for Position { /// Helper component that makes easy to build a triangle list mesh. #[derive(Debug)] pub struct MeshBuilder { - nodes: Vec, + unique_nodes: HashMap, + indices: Vec, + current_index: Arc, vertex_cache: MeshBuilderOctree, normal_cache: MeshBuilderOctree, - uv_cache: MeshBuilderOctree, + uvw_cache: MeshBuilderOctree, } #[derive(Hash, Eq, PartialEq, Clone, Debug)] @@ -351,10 +354,12 @@ impl MeshBuilder { /// Crates a new mesh centered at a position and size. pub fn create(center: [f32; 3], size: [f32; 3]) -> Self { Self { - nodes: Default::default(), + unique_nodes: Default::default(), + indices: Default::default(), + current_index: Arc::new(AtomicU32::new(0)), vertex_cache: MeshBuilderOctree::new(Boundary::new(center, size), 3, 25), normal_cache: MeshBuilderOctree::new(Boundary::new([0., 0., 0.], [2., 2., 2.]), 3, 25), - uv_cache: MeshBuilderOctree::new(Boundary::new([0.5, 0.5, 0.5], [1., 1., 1.]), 3, 25), + uvw_cache: MeshBuilderOctree::new(Boundary::new([0.5, 0.5, 0.5], [1., 1., 1.]), 3, 25), } } @@ -385,7 +390,7 @@ impl MeshBuilder { None }; let uv_index = if let Some(uv) = uv { - match self.uv_cache.insert(&[uv[0], uv[1], 0.]) { + match self.uvw_cache.insert(&[uv[0], uv[1], 0.]) { // Ignore w coordinate for now InsertResult::Inserted(index) => Some(index.try_into().unwrap()), InsertResult::AlreadyExists(index) => Some(index.try_into().unwrap()), @@ -396,12 +401,18 @@ impl MeshBuilder { None }; - self.nodes.push(NodeIndices { - vertex: vertex_index, - normal: normal_index, - uv: uv_index, - atlas: atlas_index, - }); + let arc_ci = self.current_index.clone(); // to avoid borrowing issues inside the closure + let index = self + .unique_nodes + .entry(NodeIndices { + vertex: vertex_index, + normal: normal_index, + uv: uv_index, + atlas: atlas_index, + }) + .or_insert_with(|| (*arc_ci).fetch_add(1, Ordering::SeqCst)); + + self.indices.push(*index); } /// Inserts the triangle and generate the index if needed, otherwise use an existing index. @@ -453,38 +464,45 @@ impl MeshBuilder { where M: Meshify, { - if !self.nodes.is_empty() { + if !self.unique_nodes.is_empty() { let vertex_table = self.vertex_cache.get_all(); let normal_table = self.normal_cache.get_all(); - let uv_table = self.uv_cache.get_all(); + let uvw_table = self.uvw_cache.get_all(); - let indices = self - .nodes - .iter() - .enumerate() - .map(|(i, _)| i.try_into().unwrap()) - .collect(); - let positions = self - .nodes + // HashMaps have aribtary order so we fix that by converting to a vec before anything else + let unique_nodes: Vec = { + let mut temp: Vec<(usize, NodeIndices)> = self + .unique_nodes + .iter() + .map(|(d, i)| ((*i).try_into().unwrap(), d.clone())) + .collect(); + // We sort by our index + temp.sort_by(|(a_index, _), (b_index, _)| a_index.partial_cmp(&b_index).unwrap()); + temp.iter().map(|(_, d)| d.clone()).collect() + }; + + let indices = self.indices.clone(); + let positions: Vec<[f32; 3]> = unique_nodes .iter() .map(|d| vertex_table[d.vertex].clone()) .collect(); - let normals = self - .nodes + let normals = unique_nodes .iter() .filter(|d| d.normal.is_some()) // Might be better to use a dud value like [0.,0.,0.] instead .map(|d| normal_table[d.normal.unwrap()].clone()) .collect(); - let uvs = self - .nodes + let uvs: Vec<[f32; 2]> = unique_nodes .iter() .filter(|d| d.uv.is_some()) // Might be better to use a dud value like [0.,0.] instead .map(|d| { - let uvw = uv_table[d.uv.unwrap()].clone(); + let uvw = uvw_table[d.uv.unwrap()].clone(); [uvw[0], uvw[2]] }) .collect(); + println!("position: {}, uvs: {}", positions.len(), uvs.len()); + println!("uvs[0]: {:?}, uvs[1]: {:?}", uvs.get(0), uvs.get(1)); + Some(M::with(indices, positions, normals, uvs)) } else { None From 8d112a60e55aaca12559f37d333c10e1479d79c8 Mon Sep 17 00:00:00 2001 From: QuantumEntangledAndy Date: Tue, 19 Jan 2021 10:19:52 +0700 Subject: [PATCH 5/6] Add bounding sphere to amethyst example --- gaiku-amethyst/examples/terrain.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/gaiku-amethyst/examples/terrain.rs b/gaiku-amethyst/examples/terrain.rs index ef17545..7218bfd 100644 --- a/gaiku-amethyst/examples/terrain.rs +++ b/gaiku-amethyst/examples/terrain.rs @@ -17,6 +17,7 @@ use amethyst::{ palette::{rgb::Rgb, Srgb}, plugins::{RenderShaded3D, RenderSkybox, RenderToWindow}, types::{DefaultBackend, TextureData}, + visibility::BoundingSphere, ActiveCamera, Camera, Material, MaterialDefaults, Mesh, RenderingBundle, }, ui::{RenderUi, UiBundle}, @@ -133,7 +134,8 @@ impl GameLoad { for chunk in chunks.iter() { let mesh = VoxelBaker::bake::(chunk, &options).unwrap(); if let Some(mesh) = mesh { - meshes.push((mesh, chunk.position())); + let dimensions = [chunk.width(), chunk.height(), chunk.depth()]; + meshes.push((mesh, chunk.position(), dimensions)); } } @@ -145,7 +147,7 @@ impl GameLoad { } else { Matrix4::identity() }; - for (mut mesh_gox, position) in meshes { + for (mut mesh_gox, position, dimensions) in meshes { let (mesh, mat) = { if swap_axes { // Swap y/z for amethyst coordinate system @@ -198,7 +200,25 @@ impl GameLoad { pos.set_translation(position_trans); pos.set_scale(scale); - let _voxel = world.create_entity().with(mesh).with(mat).with(pos).build(); + let radius = + ((dimensions[0].pow(2) + dimensions[1].pow(2) + dimensions[2].pow(2)) as f32).sqrt() * 0.60; + let center = [ + dimensions[0] as f32 / 2., + dimensions[1] as f32 / 2., + dimensions[2] as f32 / 2., + ]; + let bounding_sphere = BoundingSphere { + center: center.into(), + radius, + }; + + let _voxel = world + .create_entity() + .with(mesh) + .with(mat) + .with(pos) + .with(bounding_sphere) + .build(); } } } From 30ff80270d8b1fb567356d87c0898c36336cac08 Mon Sep 17 00:00:00 2001 From: QuantumEntangledAndy Date: Tue, 19 Jan 2021 10:30:06 +0700 Subject: [PATCH 6/6] Bug fixes on the uvmap during bake and other baking tests --- gaiku-common/src/mesh.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/gaiku-common/src/mesh.rs b/gaiku-common/src/mesh.rs index b9f770a..45067a1 100755 --- a/gaiku-common/src/mesh.rs +++ b/gaiku-common/src/mesh.rs @@ -496,13 +496,10 @@ impl MeshBuilder { .filter(|d| d.uv.is_some()) // Might be better to use a dud value like [0.,0.] instead .map(|d| { let uvw = uvw_table[d.uv.unwrap()].clone(); - [uvw[0], uvw[2]] + [uvw[0], uvw[1]] }) .collect(); - println!("position: {}, uvs: {}", positions.len(), uvs.len()); - println!("uvs[0]: {:?}, uvs[1]: {:?}", uvs.get(0), uvs.get(1)); - Some(M::with(indices, positions, normals, uvs)) } else { None @@ -600,7 +597,7 @@ mod test { for x in 0..4 { for y in 0..4 { for z in 0..4 { - tree.insert([x as f32, y as f32, z as f32]); + tree.insert(&[x as f32, y as f32, z as f32]); } } } @@ -618,7 +615,7 @@ mod test { MeshBuilderOctree::new(Boundary::new([0.0, 0.0, 0.0], [16.0, 16.0, 16.0]), 3, 25); match tree.insert(&[0.0, 0.0, 0.0]) { - InsertResult::Inserted => assert!(true), + InsertResult::Inserted(_) => assert!(true), _ => assert!(false), } @@ -631,7 +628,7 @@ mod test { MeshBuilderOctree::new(Boundary::new([8.0, 8.0, 8.0], [16.0, 16.0, 16.0]), 3, 25); match tree.insert(&[3.5, 16.0, 12.5]) { - InsertResult::Inserted => assert!(true), + InsertResult::Inserted(_) => assert!(true), _ => { println!("{:#?}", &tree); assert!(false)