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