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}