renderling/
geometry.rs

1//! Types and functions for staging geometry.
2use crate::math::IsVector;
3use crabslab::SlabItem;
4
5#[cfg(cpu)]
6mod cpu;
7#[cfg(cpu)]
8pub use cpu::*;
9use glam::{Vec2, Vec3, Vec4};
10
11pub mod shader;
12
13/// A displacement target.
14///
15/// Use to displace vertices using weights defined on the mesh.
16///
17/// For more info on morph targets in general, see
18/// <https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#morph-targets>
19#[derive(Clone, Copy, Default, PartialEq, SlabItem)]
20#[cfg_attr(cpu, derive(Debug))]
21pub struct MorphTarget {
22    pub position: Vec3,
23    pub normal: Vec3,
24    pub tangent: Vec3,
25    // TODO: Extend MorphTargets to include UV and Color.
26    // I think this would take a contribution to the `gltf` crate.
27}
28
29/// A vertex in a mesh.
30#[derive(Clone, Copy, core::fmt::Debug, PartialEq, SlabItem)]
31pub struct Vertex {
32    pub position: Vec3,
33    pub color: Vec4,
34    pub uv0: Vec2,
35    pub uv1: Vec2,
36    pub normal: Vec3,
37    pub tangent: Vec4,
38    // Indices that point to this vertex's 'joint' transforms.
39    pub joints: [u32; 4],
40    // The weights of influence that each joint has over this vertex
41    pub weights: [f32; 4],
42}
43
44impl Default for Vertex {
45    fn default() -> Self {
46        Self {
47            position: Default::default(),
48            color: Vec4::ONE,
49            uv0: Vec2::ZERO,
50            uv1: Vec2::ZERO,
51            normal: Vec3::Z,
52            tangent: Vec4::Y,
53            joints: [0; 4],
54            weights: [0.0; 4],
55        }
56    }
57}
58
59impl Vertex {
60    pub fn with_position(mut self, p: impl Into<Vec3>) -> Self {
61        self.position = p.into();
62        self
63    }
64
65    pub fn with_color(mut self, c: impl Into<Vec4>) -> Self {
66        self.color = c.into();
67        self
68    }
69
70    pub fn with_uv0(mut self, uv: impl Into<Vec2>) -> Self {
71        self.uv0 = uv.into();
72        self
73    }
74
75    pub fn with_uv1(mut self, uv: impl Into<Vec2>) -> Self {
76        self.uv1 = uv.into();
77        self
78    }
79
80    pub fn with_normal(mut self, n: impl Into<Vec3>) -> Self {
81        self.normal = n.into();
82        self
83    }
84
85    pub fn with_tangent(mut self, t: impl Into<Vec4>) -> Self {
86        self.tangent = t.into();
87        self
88    }
89
90    pub fn generate_normal(a: Vec3, b: Vec3, c: Vec3) -> Vec3 {
91        let ab = a - b;
92        let ac = a - c;
93        ab.cross(ac).normalize()
94    }
95
96    pub fn generate_tangent(a: Vec3, a_uv: Vec2, b: Vec3, b_uv: Vec2, c: Vec3, c_uv: Vec2) -> Vec4 {
97        let ab = b - a;
98        let ac = c - a;
99        let n = ab.cross(ac);
100        let d_uv1 = b_uv - a_uv;
101        let d_uv2 = c_uv - a_uv;
102        let denom = d_uv1.x * d_uv2.y - d_uv2.x * d_uv1.y;
103        let denom_sign = if denom >= 0.0 { 1.0 } else { -1.0 };
104        let denom = denom.abs().max(f32::EPSILON) * denom_sign;
105        let f = 1.0 / denom;
106        let s = f * Vec3::new(
107            d_uv2.y * ab.x - d_uv1.y * ac.x,
108            d_uv2.y * ab.y - d_uv1.y * ac.y,
109            d_uv2.y * ab.z - d_uv1.y * ac.z,
110        );
111        let t = f * Vec3::new(
112            d_uv1.x * ac.x - d_uv2.x * ab.x,
113            d_uv1.x * ac.y - d_uv2.x * ab.y,
114            d_uv1.x * ac.z - d_uv2.x * ab.z,
115        );
116        let n_cross_t_dot_s_sign = if n.cross(t).dot(s) >= 0.0 { 1.0 } else { -1.0 };
117        (s - s.dot(n) * n)
118            .alt_norm_or_zero()
119            .extend(n_cross_t_dot_s_sign)
120    }
121
122    #[cfg(cpu)]
123    /// A triangle list mesh of points.
124    pub fn cube_mesh() -> [Vertex; 36] {
125        let mut mesh = [Vertex::default(); 36];
126        let unit_cube = crate::math::unit_cube();
127        debug_assert_eq!(36, unit_cube.len());
128        for (i, (position, normal)) in unit_cube.into_iter().enumerate() {
129            mesh[i].position = position;
130            mesh[i].normal = normal;
131        }
132        mesh
133    }
134}