Skip to main content

renderling/geometry/
cpu.rs

1//! CPU side of the [super::geometry](geometry) module.
2use std::sync::{Arc, Mutex};
3
4use craballoc::{
5    runtime::{IsRuntime, WgpuRuntime},
6    slab::{SlabAllocator, SlabBuffer},
7    value::{GpuArray, Hybrid, HybridArray, IsContainer},
8};
9use crabslab::{Array, Id};
10use glam::{Mat4, UVec2, Vec4};
11
12use crate::{
13    camera::Camera,
14    geometry::{
15        shader::{GeometryDescriptor, SkinDescriptor},
16        MorphTarget, Vertex,
17    },
18    transform::{shader::TransformDescriptor, NestedTransform, Transform},
19    types::{GpuCpuArray, GpuOnlyArray},
20};
21
22/// A contiguous array of vertices, staged on the GPU.
23///
24/// The type variable `Ct` denotes whether the staged data lives on the GPU
25/// only, or on the CPU and the GPU.
26///
27/// # Note
28/// The amount of data staged in `Vertices` can potentially be very large, and
29/// it is common to unload the data from the CPU with
30/// [`Vertices::into_gpu_only`].
31///
32/// The only reason to keep data on the CPU is if it needs to be inspected and
33/// modified _in place_. This type of modification can be done with
34/// [`Vertices::modify_vertex`].
35///
36/// After unloading it is still possible to _replace_ a [`Vertex`] at a
37/// specific index using [`Vertices::set_vertex`].
38pub struct Vertices<Ct: IsContainer = GpuCpuArray> {
39    inner: Ct::Container<Vertex>,
40}
41
42impl<Ct> Clone for Vertices<Ct>
43where
44    Ct: IsContainer,
45    Ct::Container<Vertex>: Clone,
46{
47    fn clone(&self) -> Self {
48        Self {
49            inner: self.inner.clone(),
50        }
51    }
52}
53
54impl<Ct> std::fmt::Debug for Vertices<Ct>
55where
56    Ct: IsContainer<Pointer<Vertex> = Array<Vertex>>,
57{
58    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
59        f.debug_struct("Vertices")
60            .field("array", &Ct::get_pointer(&self.inner))
61            .finish()
62    }
63}
64
65impl From<Vertices> for Vertices<GpuOnlyArray> {
66    fn from(value: Vertices) -> Self {
67        value.into_gpu_only()
68    }
69}
70
71impl From<&Vertices> for Vertices<GpuOnlyArray> {
72    fn from(value: &Vertices) -> Self {
73        value.clone().into_gpu_only()
74    }
75}
76
77impl From<&Vertices<GpuOnlyArray>> for Vertices<GpuOnlyArray> {
78    fn from(value: &Vertices<GpuOnlyArray>) -> Self {
79        value.clone()
80    }
81}
82
83impl Vertices {
84    /// Stage a new array of vertex data on the GPU.
85    ///
86    /// The resulting `Vertices` will live on the GPU and CPU, allowing for
87    /// modification. If you would like to unload the CPU side, use
88    /// [`Vertices::into_gpu_only`].
89    pub(crate) fn new(
90        slab: &SlabAllocator<WgpuRuntime>,
91        vertices: impl IntoIterator<Item = Vertex>,
92    ) -> Self {
93        Vertices {
94            inner: slab.new_array(vertices),
95        }
96    }
97
98    /// Unload the CPU side of vertex data.
99    ///
100    /// After this operation the data can still be updated using
101    /// [`Vertices::set_item`], but it cannot be modified in-place.
102    pub fn into_gpu_only(self) -> Vertices<GpuOnlyArray> {
103        Vertices {
104            inner: self.inner.into_gpu_only(),
105        }
106    }
107
108    /// Returns a [`Vertex`] at a specific index, if any.
109    pub fn get_vertex(&self, index: usize) -> Option<Vertex> {
110        self.inner.get(index)
111    }
112
113    /// Return all vertices as a vector.
114    pub fn get_vec(&self) -> Vec<Vertex> {
115        self.inner.get_vec()
116    }
117
118    /// Modify a vertex at a specific index, if it exists.
119    pub fn modify_vertex<T: 'static>(
120        &self,
121        index: usize,
122        f: impl FnOnce(&mut Vertex) -> T,
123    ) -> Option<T> {
124        self.inner.modify(index, f)
125    }
126}
127
128impl<T> Vertices<T>
129where
130    T: IsContainer<Pointer<Vertex> = Array<Vertex>>,
131{
132    /// Returns a pointer to the underlying data on the GPU.
133    pub fn array(&self) -> Array<Vertex> {
134        T::get_pointer(&self.inner)
135    }
136}
137
138impl Vertices<GpuOnlyArray> {
139    /// Set the [`Vertex`] at the given index to the given value, if the item at
140    /// the index exists.
141    pub fn set_vertex(&self, index: usize, value: &Vertex) {
142        self.inner.set_item(index, value)
143    }
144}
145
146/// A contiguous array of indices, staged on the GPU.
147/// The type variable `Ct` denotes whether the data lives on the GPU only, or on
148/// the CPU and the GPU.
149pub struct Indices<Ct: IsContainer = GpuCpuArray> {
150    inner: Ct::Container<u32>,
151}
152
153impl<Ct> std::fmt::Debug for Indices<Ct>
154where
155    Ct: IsContainer<Pointer<u32> = Array<u32>>,
156{
157    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
158        f.debug_struct("Indices")
159            .field("array", &Ct::get_pointer(&self.inner))
160            .finish()
161    }
162}
163
164impl<Ct> Clone for Indices<Ct>
165where
166    Ct: IsContainer,
167    Ct::Container<u32>: Clone,
168{
169    fn clone(&self) -> Self {
170        Self {
171            inner: self.inner.clone(),
172        }
173    }
174}
175
176impl From<Indices> for Indices<GpuOnlyArray> {
177    fn from(value: Indices) -> Self {
178        value.into_gpu_only()
179    }
180}
181
182impl From<&Indices> for Indices<GpuOnlyArray> {
183    fn from(value: &Indices) -> Self {
184        value.clone().into_gpu_only()
185    }
186}
187
188impl From<&Indices<GpuOnlyArray>> for Indices<GpuOnlyArray> {
189    fn from(value: &Indices<GpuOnlyArray>) -> Self {
190        value.clone()
191    }
192}
193
194impl<T> Indices<T>
195where
196    T: IsContainer<Pointer<u32> = Array<u32>>,
197{
198    /// Returns a pointer to the underlying data on the GPU.
199    pub fn array(&self) -> Array<u32> {
200        T::get_pointer(&self.inner)
201    }
202}
203
204impl Indices {
205    /// Stage a new array of index data on the GPU.
206    ///
207    /// The resulting `Indices` will live on the GPU and CPU, allowing for
208    /// modification. If you would like to unload the CPU side, use
209    /// [`Indices::into_gpu_only`].
210    pub fn new(geometry: &Geometry, indices: impl IntoIterator<Item = u32>) -> Self {
211        Indices {
212            inner: geometry.slab.new_array(indices),
213        }
214    }
215
216    /// Unload the CPU side of this index data.
217    pub fn into_gpu_only(self) -> Indices<GpuOnlyArray> {
218        Indices {
219            inner: self.inner.into_gpu_only(),
220        }
221    }
222}
223
224/// Holds morph targets for animated nodes.
225#[derive(Clone)]
226pub struct MorphTargets {
227    // Held onto so the values don't drop from under us
228    _targets: Arc<Vec<GpuArray<MorphTarget>>>,
229    arrays: HybridArray<Array<MorphTarget>>,
230}
231
232impl From<&MorphTargets> for MorphTargets {
233    fn from(value: &MorphTargets) -> Self {
234        value.clone()
235    }
236}
237
238impl std::fmt::Debug for MorphTargets {
239    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
240        f.debug_struct("MorphTargets")
241            .field("arrays", &self.arrays)
242            .field("targets", &"...")
243            .finish()
244    }
245}
246
247impl MorphTargets {
248    pub(crate) fn new(
249        slab: &SlabAllocator<impl IsRuntime>,
250        morph_targets: impl IntoIterator<Item = impl IntoIterator<Item = MorphTarget>>,
251    ) -> Self {
252        let targets = morph_targets
253            .into_iter()
254            .map(|verts| slab.new_array(verts).into_gpu_only())
255            .collect::<Vec<_>>();
256        let arrays = slab.new_array(targets.iter().map(|ts| ts.array()));
257        Self {
258            _targets: targets.into(),
259            arrays,
260        }
261    }
262    /// Returns a pointer to the underlying morph targets data on the GPU.
263    pub fn array(&self) -> Array<Array<MorphTarget>> {
264        self.arrays.array()
265    }
266}
267
268/// Holds morph targets weights for animated nodes.
269#[derive(Clone, Debug)]
270pub struct MorphTargetWeights {
271    inner: HybridArray<f32>,
272}
273
274impl From<&MorphTargetWeights> for MorphTargetWeights {
275    fn from(value: &MorphTargetWeights) -> Self {
276        value.clone()
277    }
278}
279
280impl MorphTargetWeights {
281    pub(crate) fn new(
282        slab: &SlabAllocator<impl IsRuntime>,
283        data: impl IntoIterator<Item = f32>,
284    ) -> Self {
285        Self {
286            inner: slab.new_array(data),
287        }
288    }
289
290    /// Returns a pointer to the underlying morph targets weights data on the
291    /// GPU.
292    pub fn array(&self) -> Array<f32> {
293        self.inner.array()
294    }
295
296    /// Return the weight at the given index, if any.
297    pub fn get_item(&self, index: usize) -> Option<f32> {
298        self.inner.get(index)
299    }
300
301    /// Update the weight at the given index.
302    pub fn set_item(&self, index: usize, weight: f32) {
303        self.inner.set_item(index, weight);
304    }
305}
306
307/// Wrapper around the geometry slab, which holds mesh data and more.
308#[derive(Clone)]
309pub struct Geometry {
310    slab: SlabAllocator<WgpuRuntime>,
311    descriptor: Hybrid<GeometryDescriptor>,
312    /// A plain white cube to use as default geometry.
313    default_vertices: Vertices,
314    /// Holds the current camera just in case the user drops it,
315    /// this way we never lose a camera that is in use. Dropping
316    /// the camera would cause a blank screen, which is very confusing
317    /// =(
318    camera: Arc<Mutex<Option<Camera>>>,
319}
320
321impl AsRef<WgpuRuntime> for Geometry {
322    fn as_ref(&self) -> &WgpuRuntime {
323        self.slab.runtime()
324    }
325}
326
327impl AsRef<SlabAllocator<WgpuRuntime>> for Geometry {
328    fn as_ref(&self) -> &SlabAllocator<WgpuRuntime> {
329        &self.slab
330    }
331}
332
333impl Geometry {
334    pub fn new(runtime: impl AsRef<WgpuRuntime>, resolution: UVec2, atlas_size: UVec2) -> Self {
335        let runtime = runtime.as_ref();
336        let slab = SlabAllocator::new(runtime, "geometry", wgpu::BufferUsages::empty());
337        let descriptor = slab.new_value(GeometryDescriptor {
338            atlas_size,
339            resolution,
340            ..Default::default()
341        });
342        let default_vertices = Vertices::new(
343            &slab,
344            crate::math::unit_cube().into_iter().map(|(p, n)| {
345                Vertex::default()
346                    .with_position(p)
347                    .with_normal(n)
348                    .with_color(Vec4::ONE)
349            }),
350        );
351        Self {
352            slab,
353            descriptor,
354            default_vertices,
355            camera: Default::default(),
356        }
357    }
358
359    pub fn runtime(&self) -> &WgpuRuntime {
360        self.as_ref()
361    }
362
363    pub fn slab_allocator(&self) -> &SlabAllocator<WgpuRuntime> {
364        self.as_ref()
365    }
366
367    pub fn descriptor(&self) -> &Hybrid<GeometryDescriptor> {
368        &self.descriptor
369    }
370
371    /// Returns the vertices of a white unit cube.
372    pub fn default_vertices(&self) -> &Vertices {
373        &self.default_vertices
374    }
375
376    #[must_use]
377    pub fn commit(&self) -> SlabBuffer<wgpu::Buffer> {
378        self.slab.commit()
379    }
380
381    /// Create a new camera.
382    ///
383    /// If this is the first camera created, it will be automatically used.
384    pub fn new_camera(&self) -> Camera {
385        let c = Camera::new(&self.slab);
386        if self.descriptor.get().camera_id.is_none() {
387            self.use_camera(&c);
388        }
389        c
390    }
391
392    /// Set all geometry to use the given camera.
393    pub fn use_camera(&self, camera: &Camera) {
394        let id = camera.id();
395        log::info!("using camera: {id:?}");
396        // Save a clone so we never lose the active camera, even if the user
397        // drops it
398        self.descriptor.modify(|cfg| cfg.camera_id = id);
399        *self.camera.lock().expect("geometry camera lock") = Some(camera.clone());
400    }
401
402    /// Stage a new transform.
403    pub fn new_transform(&self) -> Transform {
404        Transform::new(&self.slab)
405    }
406
407    /// Stage vertex geometry data on the GPU.
408    pub fn new_vertices(&self, vertices: impl IntoIterator<Item = Vertex>) -> Vertices {
409        Vertices::new(self.slab_allocator(), vertices)
410    }
411
412    /// Stage indices that point to offsets of an array of vertices.
413    pub fn new_indices(&self, indices: impl IntoIterator<Item = u32>) -> Indices {
414        Indices::new(self, indices)
415    }
416
417    /// Stage new morph targets on the GPU.
418    pub fn new_morph_targets(
419        &self,
420        data: impl IntoIterator<Item = impl IntoIterator<Item = MorphTarget>>,
421    ) -> MorphTargets {
422        MorphTargets::new(&self.slab, data)
423    }
424
425    /// Create new morph target weights.
426    pub fn new_morph_target_weights(
427        &self,
428        data: impl IntoIterator<Item = f32>,
429    ) -> MorphTargetWeights {
430        MorphTargetWeights::new(&self.slab, data)
431    }
432
433    /// Create a new array of matrices.
434    pub fn new_matrices(&self, data: impl IntoIterator<Item = Mat4>) -> HybridArray<Mat4> {
435        self.slab.new_array(data)
436    }
437
438    pub fn new_skin(
439        &self,
440        joints: impl IntoIterator<Item = impl Into<SkinJoint>>,
441        inverse_bind_matrices: impl IntoIterator<Item = impl Into<Mat4>>,
442    ) -> Skin {
443        Skin::new(self.slab_allocator(), joints, inverse_bind_matrices)
444    }
445}
446
447/// A vertex skin.
448///
449/// For more info on vertex skinning, see
450/// <https://github.khronos.org/glTF-Tutorials/gltfTutorial/gltfTutorial_019_SimpleSkin.html>
451#[derive(Clone)]
452pub struct Skin {
453    descriptor: Hybrid<SkinDescriptor>,
454    joints: HybridArray<Id<TransformDescriptor>>,
455    // Held onto so the transforms don't drop from under us
456    _skin_joints: Arc<Mutex<Vec<SkinJoint>>>,
457    // Contains the 4x4 inverse-bind matrices.
458    //
459    // When None, each matrix is assumed to be the 4x4 identity matrix which implies that the
460    // inverse-bind matrices were pre-applied.
461    _inverse_bind_matrices: Arc<Mutex<Option<GpuArray<Mat4>>>>,
462}
463
464impl From<&Skin> for Skin {
465    fn from(value: &Skin) -> Self {
466        value.clone()
467    }
468}
469
470impl core::fmt::Debug for Skin {
471    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
472        f.debug_struct("Skin")
473            .field("descriptor", &self.descriptor)
474            .field("joints", &self.joints)
475            .field("joint_transforms", &"...")
476            .field("inverse_bind_matrices", &"...")
477            .finish()
478    }
479}
480
481impl Skin {
482    /// Stage a new skin on the GPU.
483    pub fn new(
484        slab: &SlabAllocator<impl IsRuntime>,
485        joints: impl IntoIterator<Item = impl Into<SkinJoint>>,
486        inverse_bind_matrices: impl IntoIterator<Item = impl Into<Mat4>>,
487    ) -> Self {
488        let descriptor = slab.new_value(SkinDescriptor::default());
489        let skin_joints = joints.into_iter().map(|t| t.into()).collect::<Vec<_>>();
490        let joints = skin_joints.iter().map(|sj| sj.0.id()).collect::<Vec<_>>();
491        let inverse_bind_matrices = inverse_bind_matrices
492            .into_iter()
493            .map(|m| m.into())
494            .collect::<Vec<_>>();
495        let inverse_bind_matrices = if inverse_bind_matrices.is_empty() {
496            None
497        } else {
498            Some(slab.new_array(inverse_bind_matrices).into_gpu_only())
499        };
500
501        Skin {
502            descriptor,
503            joints: slab.new_array(joints),
504            // We hold on to the transforms so they don't get dropped if the user drops them.
505            _skin_joints: Arc::new(Mutex::new(skin_joints)),
506            _inverse_bind_matrices: Arc::new(Mutex::new(inverse_bind_matrices)),
507        }
508    }
509
510    /// Return a pointer to the underlying descriptor data on the GPU.
511    pub fn id(&self) -> Id<SkinDescriptor> {
512        self.descriptor.id()
513    }
514
515    /// Return a copy of the underlying descriptor.
516    pub fn descriptor(&self) -> SkinDescriptor {
517        self.descriptor.get()
518    }
519}
520
521/// A joint in a skinned rigging.
522///
523/// This is a thin wrapper over [`Transform`] and
524/// [`NestedTransform`]. You can create a [`SkinJoint`]
525/// from either of those types using the [`From`] trait.
526///
527/// You can also pass an iterator of either [`Transform`] or [`NestedTransform`]
528/// to [`Stage::new_skin`](crate::stage::Stage::new_skin).
529pub struct SkinJoint(pub(crate) Transform);
530
531impl From<Transform> for SkinJoint {
532    fn from(transform: Transform) -> Self {
533        SkinJoint(transform)
534    }
535}
536
537impl From<&Transform> for SkinJoint {
538    fn from(transform: &Transform) -> Self {
539        transform.clone().into()
540    }
541}
542
543impl From<NestedTransform> for SkinJoint {
544    fn from(value: NestedTransform) -> Self {
545        SkinJoint(value.global_transform)
546    }
547}
548
549impl From<&NestedTransform> for SkinJoint {
550    fn from(value: &NestedTransform) -> Self {
551        value.clone().into()
552    }
553}