bevy_mesh/
morph.rs

1use super::Mesh;
2use bevy_asset::{Handle, RenderAssetUsages};
3use bevy_ecs::prelude::*;
4use bevy_image::Image;
5use bevy_math::Vec3;
6use bevy_reflect::prelude::*;
7use bytemuck::{Pod, Zeroable};
8use core::iter;
9use derive_more::derive::{Display, Error};
10use wgpu::{Extent3d, TextureDimension, TextureFormat};
11
12const MAX_TEXTURE_WIDTH: u32 = 2048;
13// NOTE: "component" refers to the element count of math objects,
14// Vec3 has 3 components, Mat2 has 4 components.
15const MAX_COMPONENTS: u32 = MAX_TEXTURE_WIDTH * MAX_TEXTURE_WIDTH;
16
17/// Max target count available for [morph targets](MorphWeights).
18pub const MAX_MORPH_WEIGHTS: usize = 64;
19
20#[derive(Error, Display, Clone, Debug)]
21pub enum MorphBuildError {
22    #[display(
23        "Too many vertexĂ—components in morph target, max is {MAX_COMPONENTS}, \
24        got {vertex_count}Ă—{component_count} = {}",
25        *vertex_count * *component_count as usize
26    )]
27    TooManyAttributes {
28        vertex_count: usize,
29        component_count: u32,
30    },
31    #[display(
32        "Bevy only supports up to {} morph targets (individual poses), tried to \
33        create a model with {target_count} morph targets",
34        MAX_MORPH_WEIGHTS
35    )]
36    TooManyTargets { target_count: usize },
37}
38
39/// An image formatted for use with [`MorphWeights`] for rendering the morph target.
40#[derive(Debug)]
41pub struct MorphTargetImage(pub Image);
42
43impl MorphTargetImage {
44    /// Generate textures for each morph target.
45    ///
46    /// This accepts an "iterator of [`MorphAttributes`] iterators". Each item iterated in the top level
47    /// iterator corresponds "the attributes of a specific morph target".
48    ///
49    /// Each pixel of the texture is a component of morph target animated
50    /// attributes. So a set of 9 pixels is this morph's displacement for
51    /// position, normal and tangents of a single vertex (each taking 3 pixels).
52    pub fn new(
53        targets: impl ExactSizeIterator<Item = impl Iterator<Item = MorphAttributes>>,
54        vertex_count: usize,
55        asset_usage: RenderAssetUsages,
56    ) -> Result<Self, MorphBuildError> {
57        let max = MAX_TEXTURE_WIDTH;
58        let target_count = targets.len();
59        if target_count > MAX_MORPH_WEIGHTS {
60            return Err(MorphBuildError::TooManyTargets { target_count });
61        }
62        let component_count = (vertex_count * MorphAttributes::COMPONENT_COUNT) as u32;
63        let Some((Rect(width, height), padding)) = lowest_2d(component_count, max) else {
64            return Err(MorphBuildError::TooManyAttributes {
65                vertex_count,
66                component_count,
67            });
68        };
69        let data = targets
70            .flat_map(|mut attributes| {
71                let layer_byte_count = (padding + component_count) as usize * size_of::<f32>();
72                let mut buffer = Vec::with_capacity(layer_byte_count);
73                for _ in 0..vertex_count {
74                    let Some(to_add) = attributes.next() else {
75                        break;
76                    };
77                    buffer.extend_from_slice(bytemuck::bytes_of(&to_add));
78                }
79                // Pad each layer so that they fit width * height
80                buffer.extend(iter::repeat(0).take(padding as usize * size_of::<f32>()));
81                debug_assert_eq!(buffer.len(), layer_byte_count);
82                buffer
83            })
84            .collect();
85        let extents = Extent3d {
86            width,
87            height,
88            depth_or_array_layers: target_count as u32,
89        };
90        let image = Image::new(
91            extents,
92            TextureDimension::D3,
93            data,
94            TextureFormat::R32Float,
95            asset_usage,
96        );
97        Ok(MorphTargetImage(image))
98    }
99}
100
101/// Controls the [morph targets] for all child `Mesh3d` entities. In most cases, [`MorphWeights`] should be considered
102/// the "source of truth" when writing morph targets for meshes. However you can choose to write child [`MeshMorphWeights`]
103/// if your situation requires more granularity. Just note that if you set [`MorphWeights`], it will overwrite child
104/// [`MeshMorphWeights`] values.
105///
106/// This exists because Bevy's [`Mesh`] corresponds to a _single_ surface / material, whereas morph targets
107/// as defined in the GLTF spec exist on "multi-primitive meshes" (where each primitive is its own surface with its own material).
108/// Therefore in Bevy [`MorphWeights`] an a parent entity are the "canonical weights" from a GLTF perspective, which then
109/// synchronized to child `Mesh3d` / [`MeshMorphWeights`] (which correspond to "primitives" / "surfaces" from a GLTF perspective).
110///
111/// Add this to the parent of one or more [`Entities`](`Entity`) with a `Mesh3d` with a [`MeshMorphWeights`].
112///
113/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
114#[derive(Reflect, Default, Debug, Clone, Component)]
115#[reflect(Debug, Component, Default)]
116pub struct MorphWeights {
117    weights: Vec<f32>,
118    /// The first mesh primitive assigned to these weights
119    first_mesh: Option<Handle<Mesh>>,
120}
121impl MorphWeights {
122    pub fn new(
123        weights: Vec<f32>,
124        first_mesh: Option<Handle<Mesh>>,
125    ) -> Result<Self, MorphBuildError> {
126        if weights.len() > MAX_MORPH_WEIGHTS {
127            let target_count = weights.len();
128            return Err(MorphBuildError::TooManyTargets { target_count });
129        }
130        Ok(MorphWeights {
131            weights,
132            first_mesh,
133        })
134    }
135    /// The first child `Mesh3d` primitive controlled by these weights.
136    /// This can be used to look up metadata information such as [`Mesh::morph_target_names`].
137    pub fn first_mesh(&self) -> Option<&Handle<Mesh>> {
138        self.first_mesh.as_ref()
139    }
140    pub fn weights(&self) -> &[f32] {
141        &self.weights
142    }
143    pub fn weights_mut(&mut self) -> &mut [f32] {
144        &mut self.weights
145    }
146}
147
148/// Control a specific [`Mesh`] instance's [morph targets]. These control the weights of
149/// specific "mesh primitives" in scene formats like GLTF. They can be set manually, but
150/// in most cases they should "automatically" synced by setting the [`MorphWeights`] component
151/// on a parent entity.
152///
153/// See [`MorphWeights`] for more details on Bevy's morph target implementation.
154///
155/// Add this to an [`Entity`] with a `Mesh3d` with a [`MorphAttributes`] set
156/// to control individual weights of each morph target.
157///
158/// [morph targets]: https://en.wikipedia.org/wiki/Morph_target_animation
159#[derive(Reflect, Default, Debug, Clone, Component)]
160#[reflect(Debug, Component, Default)]
161pub struct MeshMorphWeights {
162    weights: Vec<f32>,
163}
164impl MeshMorphWeights {
165    pub fn new(weights: Vec<f32>) -> Result<Self, MorphBuildError> {
166        if weights.len() > MAX_MORPH_WEIGHTS {
167            let target_count = weights.len();
168            return Err(MorphBuildError::TooManyTargets { target_count });
169        }
170        Ok(MeshMorphWeights { weights })
171    }
172    pub fn weights(&self) -> &[f32] {
173        &self.weights
174    }
175    pub fn weights_mut(&mut self) -> &mut [f32] {
176        &mut self.weights
177    }
178    pub fn clear_weights(&mut self) {
179        self.weights.clear();
180    }
181    pub fn extend_weights(&mut self, weights: &[f32]) {
182        self.weights.extend(weights);
183    }
184}
185
186/// Attributes **differences** used for morph targets.
187///
188/// See [`MorphTargetImage`] for more information.
189#[derive(Copy, Clone, PartialEq, Pod, Zeroable, Default)]
190#[repr(C)]
191pub struct MorphAttributes {
192    /// The vertex position difference between base mesh and this target.
193    pub position: Vec3,
194    /// The vertex normal difference between base mesh and this target.
195    pub normal: Vec3,
196    /// The vertex tangent difference between base mesh and this target.
197    ///
198    /// Note that tangents are a `Vec4`, but only the `xyz` components are
199    /// animated, as the `w` component is the sign and cannot be animated.
200    pub tangent: Vec3,
201}
202impl From<[Vec3; 3]> for MorphAttributes {
203    fn from([position, normal, tangent]: [Vec3; 3]) -> Self {
204        MorphAttributes {
205            position,
206            normal,
207            tangent,
208        }
209    }
210}
211impl MorphAttributes {
212    /// How many components `MorphAttributes` has.
213    ///
214    /// Each `Vec3` has 3 components, we have 3 `Vec3`, for a total of 9.
215    pub const COMPONENT_COUNT: usize = 9;
216
217    pub fn new(position: Vec3, normal: Vec3, tangent: Vec3) -> Self {
218        MorphAttributes {
219            position,
220            normal,
221            tangent,
222        }
223    }
224}
225
226struct Rect(u32, u32);
227
228/// Find the smallest rectangle of maximum edge size `max_edge` that contains
229/// at least `min_includes` cells. `u32` is how many extra cells the rectangle
230/// has.
231///
232/// The following rectangle contains 27 cells, and its longest edge is 9:
233/// ```text
234/// ----------------------------
235/// |1 |2 |3 |4 |5 |6 |7 |8 |9 |
236/// ----------------------------
237/// |2 |  |  |  |  |  |  |  |  |
238/// ----------------------------
239/// |3 |  |  |  |  |  |  |  |  |
240/// ----------------------------
241/// ```
242///
243/// Returns `None` if `max_edge` is too small to build a rectangle
244/// containing `min_includes` cells.
245fn lowest_2d(min_includes: u32, max_edge: u32) -> Option<(Rect, u32)> {
246    (1..=max_edge)
247        .filter_map(|a| {
248            let b = min_includes.div_ceil(a);
249            let diff = (a * b).checked_sub(min_includes)?;
250            Some((Rect(a, b), diff))
251        })
252        .filter_map(|(rect, diff)| (rect.1 <= max_edge).then_some((rect, diff)))
253        .min_by_key(|(_, diff)| *diff)
254}