fyrox_impl/utils/raw_mesh.rs
1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Raw mesh is a procedural mesh builder, all you can do with it is to insert vertices
22//! one-by-one and it will automatically build faces by skipping duplicated vertices.
23//! Main usage of it - optimize "triangle soup" into mesh so adjacent faces will have
24//! shared edges. Raw mesh itself does not have any methods, it is just a final result
25//! of RawMeshBuilder.
26
27use crate::{
28 core::hash_as_bytes,
29 core::{algebra::Vector3, math::TriangleDefinition},
30};
31use bytemuck::{Pod, Zeroable};
32use fxhash::{FxBuildHasher, FxHashSet};
33use std::hash::{Hash, Hasher};
34#[derive(Copy, Clone)]
35struct IndexedStorage<T> {
36 index: u32,
37 vertex: T,
38}
39
40/// Raw vertex is just a point in 3d space that supports hashing.
41#[repr(C)]
42#[derive(Copy, Clone, Pod, Zeroable)]
43pub struct RawVertex {
44 /// An X component.
45 pub x: f32,
46 /// An Y component.
47 pub y: f32,
48 /// An Z component.
49 pub z: f32,
50}
51
52impl PartialEq for RawVertex {
53 fn eq(&self, other: &Self) -> bool {
54 self.x == other.x && self.y == other.y && self.z == other.z
55 }
56}
57
58impl From<Vector3<f32>> for RawVertex {
59 fn from(v: Vector3<f32>) -> Self {
60 Self {
61 x: v.x,
62 y: v.y,
63 z: v.z,
64 }
65 }
66}
67
68impl RawVertex {
69 fn validate(&self) {
70 debug_assert!(!self.x.is_nan());
71 debug_assert!(!self.y.is_nan());
72 debug_assert!(!self.z.is_nan());
73 }
74}
75
76impl Hash for RawVertex {
77 fn hash<H: Hasher>(&self, state: &mut H) {
78 self.validate();
79 hash_as_bytes(self, state);
80 }
81}
82
83impl<T> PartialEq for IndexedStorage<T>
84where
85 T: PartialEq,
86{
87 fn eq(&self, other: &Self) -> bool {
88 self.vertex == other.vertex
89 }
90}
91
92impl<T> Eq for IndexedStorage<T> where T: PartialEq {}
93
94impl<T> Hash for IndexedStorage<T>
95where
96 T: Hash,
97{
98 fn hash<H: Hasher>(&self, state: &mut H) {
99 self.vertex.hash(state)
100 }
101}
102
103impl<T> Default for RawMeshBuilder<T>
104where
105 T: Hash + PartialEq,
106{
107 fn default() -> Self {
108 Self {
109 vertices: Default::default(),
110 indices: Default::default(),
111 }
112 }
113}
114
115/// See module docs.
116#[derive(Clone)]
117pub struct RawMeshBuilder<T>
118where
119 T: Hash + PartialEq,
120{
121 vertices: FxHashSet<IndexedStorage<T>>,
122 indices: Vec<u32>,
123}
124
125/// See module docs.
126#[derive(Default, Debug, Clone)]
127pub struct RawMesh<T> {
128 /// Vertices of mesh.
129 pub vertices: Vec<T>,
130 /// Triangles of mesh. Each triangle contains indices of vertices.
131 pub triangles: Vec<TriangleDefinition>,
132}
133
134impl<T> RawMeshBuilder<T>
135where
136 T: Hash + PartialEq,
137{
138 /// Creates new builder with given start values of capacity for internal
139 /// buffers. These values doesn't need to be precise.
140 pub fn new(vertices: usize, indices: usize) -> Self {
141 Self {
142 // We can't use plain `with_capacity` with FxHashSet,
143 // we need to specify the hahser manually too
144 // (https://internals.rust-lang.org/t/hashmap-set-new-with-capacity-and-buildhasher/15622).
145 vertices: FxHashSet::with_capacity_and_hasher(vertices, FxBuildHasher::default()),
146 indices: Vec::with_capacity(indices),
147 }
148 }
149
150 /// Inserts new vertex in mesh. Index buffer is populated automatically -
151 /// when duplicate vertex is found, it not added into vertices array, but its
152 /// index gets added into indices array.
153 pub fn insert(&mut self, vertex: T) -> bool {
154 let mut wrapper = IndexedStorage::<T> { index: 0, vertex };
155 if let Some(existing) = self.vertices.get(&wrapper) {
156 self.indices.push(existing.index);
157 false
158 } else {
159 wrapper.index = self.vertices.len() as u32;
160 self.indices.push(wrapper.index);
161 self.vertices.insert(wrapper);
162 true
163 }
164 }
165
166 /// Returns total amount of vertices in the mesh builder so far.
167 pub fn vertex_count(&self) -> usize {
168 self.vertices.len()
169 }
170
171 /// Creates new raw mesh from internal set of vertices and indices. If last "triangle" has
172 /// insufficient vertex count (less than 3), it will be discarded.
173 pub fn build(self) -> RawMesh<T> {
174 let mut vertices = self.vertices.into_iter().collect::<Vec<_>>();
175 vertices.sort_unstable_by_key(|w| w.index);
176 RawMesh {
177 vertices: vertices.into_iter().map(|w| w.vertex).collect(),
178 triangles: self
179 .indices
180 .chunks_exact(3)
181 .map(|i| TriangleDefinition([i[0], i[1], i[2]]))
182 .collect(),
183 }
184 }
185}