logo
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Raw mesh is a procedural mesh builder, all you can do with it is to insert vertices
//! one-by-one and it will automatically build faces by skipping duplicated vertices.
//! Main usage of it - optimize "triangle soup" into mesh so adjacent faces will have
//! shared edges. Raw mesh itself does not have any methods, it is just a final result
//! of RawMeshBuilder.

use crate::{
    core::{algebra::Vector3, math::TriangleDefinition},
    utils::hash_as_bytes,
};
use fxhash::{FxBuildHasher, FxHashSet};
use std::hash::{Hash, Hasher};

#[derive(Copy, Clone)]
struct IndexedStorage<T> {
    index: u32,
    vertex: T,
}

/// Raw vertex is just a point in 3d space that supports hashing.
pub struct RawVertex {
    /// An X component.
    pub x: f32,
    /// An Y component.
    pub y: f32,
    /// An Z component.
    pub z: f32,
}

impl PartialEq for RawVertex {
    fn eq(&self, other: &Self) -> bool {
        self.x == other.x && self.y == other.y && self.z == other.z
    }
}

impl From<Vector3<f32>> for RawVertex {
    fn from(v: Vector3<f32>) -> Self {
        Self {
            x: v.x,
            y: v.y,
            z: v.z,
        }
    }
}

impl RawVertex {
    fn validate(&self) {
        debug_assert!(!self.x.is_nan());
        debug_assert!(!self.y.is_nan());
        debug_assert!(!self.z.is_nan());
    }
}

impl Hash for RawVertex {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.validate();
        hash_as_bytes(self, state);
    }
}

impl<T> PartialEq for IndexedStorage<T>
where
    T: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        self.vertex == other.vertex
    }
}

impl<T> Eq for IndexedStorage<T> where T: PartialEq {}

impl<T> Hash for IndexedStorage<T>
where
    T: Hash,
{
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.vertex.hash(state)
    }
}

impl<T> Default for RawMeshBuilder<T>
where
    T: Hash + PartialEq,
{
    fn default() -> Self {
        Self {
            vertices: Default::default(),
            indices: Default::default(),
        }
    }
}

/// See module docs.
#[derive(Clone)]
pub struct RawMeshBuilder<T>
where
    T: Hash + PartialEq,
{
    vertices: FxHashSet<IndexedStorage<T>>,
    indices: Vec<u32>,
}

/// See module docs.
pub struct RawMesh<T> {
    /// Vertices of mesh.
    pub vertices: Vec<T>,
    /// Triangles of mesh. Each triangle contains indices of vertices.
    pub triangles: Vec<TriangleDefinition>,
}

impl<T> RawMeshBuilder<T>
where
    T: Hash + PartialEq,
{
    /// Creates new builder with given start values of capacity for internal
    /// buffers. These values doesn't need to be precise.
    pub fn new(vertices: usize, indices: usize) -> Self {
        Self {
            // We can't use plain `with_capacity` with FxHashSet,
            // we need to specify the hahser manually too
            // (https://internals.rust-lang.org/t/hashmap-set-new-with-capacity-and-buildhasher/15622).
            vertices: FxHashSet::with_capacity_and_hasher(vertices, FxBuildHasher::default()),
            indices: Vec::with_capacity(indices),
        }
    }

    /// Inserts new vertex in mesh. Index buffer is populated automatically -
    /// when duplicate vertex is found, it not added into vertices array, but its
    /// index gets added into indices array.
    pub fn insert(&mut self, vertex: T) -> bool {
        let mut wrapper = IndexedStorage::<T> { index: 0, vertex };
        if let Some(existing) = self.vertices.get(&wrapper) {
            self.indices.push(existing.index);
            false
        } else {
            wrapper.index = self.vertices.len() as u32;
            self.indices.push(wrapper.index);
            self.vertices.insert(wrapper);
            true
        }
    }

    /// Creates new raw mesh from internal set of vertices and indices. If last "triangle" has
    /// insufficient vertex count (less than 3), it will be discarded.
    pub fn build(self) -> RawMesh<T> {
        let mut vertices = self.vertices.into_iter().collect::<Vec<_>>();
        vertices.sort_unstable_by_key(|w| w.index);
        RawMesh {
            vertices: vertices.into_iter().map(|w| w.vertex).collect(),
            triangles: self
                .indices
                .chunks_exact(3)
                .map(|i| TriangleDefinition([i[0], i[1], i[2]]))
                .collect(),
        }
    }
}