Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/mesh/morph.rs
30636 views
1
use bevy_asset::AssetId;
2
use bevy_ecs::{
3
resource::Resource,
4
world::{FromWorld, World},
5
};
6
use bevy_log::error;
7
use bevy_mesh::{
8
morph::{MorphAttributes, MorphBuildError, MAX_MORPH_WEIGHTS, MAX_TEXTURE_WIDTH},
9
Mesh,
10
};
11
use bevy_platform::collections::HashMap;
12
use wgpu::{
13
Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages,
14
TextureViewDescriptor,
15
};
16
use wgpu_types::TextureDataOrder;
17
18
use crate::{
19
render_resource::{Buffer, Texture, TextureView},
20
renderer::{RenderDevice, RenderQueue},
21
};
22
23
/// An image formatted for use with [`bevy_mesh::morph::MorphWeights`] for
24
/// rendering the morph target, containing the vertex displacements.
25
///
26
/// We only use these if storage buffers aren't supported on the current
27
/// platform. Otherwise, we store the mesh displacements in a storage buffer,
28
/// managed by the mesh allocator.
29
#[derive(Clone, Debug)]
30
pub struct MorphTargetImage {
31
/// The texture containing the vertex displacements.
32
pub texture: Texture,
33
/// A view into the texture, suitable for attaching to the vertex shader.
34
pub texture_view: TextureView,
35
}
36
37
impl MorphTargetImage {
38
/// Generate textures for each morph target.
39
///
40
/// This accepts an "iterator of [`MorphAttributes`] iterators". Each item
41
/// iterated in the top level iterator corresponds "the attributes of a
42
/// specific morph target".
43
///
44
/// Each pixel of the texture is a component of morph target animated
45
/// attributes. So a set of 9 pixels is this morph's displacement for
46
/// position, normal and tangents of a single vertex (each taking 3 pixels).
47
pub fn new(
48
render_device: &RenderDevice,
49
render_queue: &RenderQueue,
50
targets: &[MorphAttributes],
51
vertex_count: usize,
52
) -> Result<Self, MorphBuildError> {
53
let max = MAX_TEXTURE_WIDTH;
54
let target_count = targets.len() / vertex_count;
55
if target_count > MAX_MORPH_WEIGHTS {
56
return Err(MorphBuildError::TooManyTargets { target_count });
57
}
58
let component_count = (vertex_count * MorphAttributes::COMPONENT_COUNT) as u32;
59
let Some((Rect(width, height), padding)) = lowest_2d(component_count, max) else {
60
return Err(MorphBuildError::TooManyAttributes {
61
vertex_count,
62
component_count,
63
});
64
};
65
let data: Vec<u8> = targets
66
.chunks(vertex_count)
67
.flat_map(|attributes| {
68
let layer_byte_count = (padding + component_count) as usize * size_of::<f32>();
69
let mut buffer = Vec::with_capacity(layer_byte_count);
70
for to_add in attributes {
71
buffer.extend_from_slice(bytemuck::bytes_of(&[
72
to_add.position,
73
to_add.normal,
74
to_add.tangent,
75
]));
76
}
77
// Pad each layer so that they fit width * height
78
buffer.extend(core::iter::repeat_n(0, padding as usize * size_of::<f32>()));
79
debug_assert_eq!(buffer.len(), layer_byte_count);
80
buffer
81
})
82
.collect();
83
let extents = Extent3d {
84
width,
85
height,
86
depth_or_array_layers: target_count as u32,
87
};
88
let texture = render_device.create_texture_with_data(
89
render_queue,
90
&TextureDescriptor {
91
label: Some("morph target image"),
92
size: extents,
93
mip_level_count: 1,
94
sample_count: 1,
95
dimension: TextureDimension::D3,
96
format: TextureFormat::R32Float,
97
usage: TextureUsages::COPY_DST | TextureUsages::TEXTURE_BINDING,
98
view_formats: &[],
99
},
100
TextureDataOrder::LayerMajor,
101
&data,
102
);
103
let texture_view = texture.create_view(&TextureViewDescriptor {
104
label: Some("morph target texture view"),
105
..TextureViewDescriptor::default()
106
});
107
Ok(MorphTargetImage {
108
texture,
109
texture_view,
110
})
111
}
112
}
113
114
/// Stores the images for all morph target displacement data, if the current
115
/// platform doesn't support storage buffers.
116
///
117
/// If the current platform does support storage buffers, the mesh allocator
118
/// stores displacement data instead.
119
#[derive(Resource)]
120
pub enum RenderMorphTargetAllocator {
121
/// The variant used when the current platform doesn't support storage
122
/// buffers.
123
Image {
124
/// Maps the ID of each mesh to the image containing its morph target
125
/// displacements.
126
mesh_id_to_image: HashMap<AssetId<Mesh>, MorphTargetImage>,
127
},
128
/// The variant used when the current platform does support storage buffers.
129
///
130
/// In this case, this resource is empty, because the mesh allocator stores
131
/// displacements instead.
132
Storage,
133
}
134
135
impl FromWorld for RenderMorphTargetAllocator {
136
fn from_world(world: &mut World) -> RenderMorphTargetAllocator {
137
let render_device = world.resource::<RenderDevice>();
138
if bevy_render::storage_buffers_are_unsupported(&render_device.limits()) {
139
RenderMorphTargetAllocator::Image {
140
mesh_id_to_image: HashMap::default(),
141
}
142
} else {
143
RenderMorphTargetAllocator::Storage
144
}
145
}
146
}
147
148
/// A reference to the resource in which morph displacements for a mesh are
149
/// stored.
150
#[derive(Clone, Copy)]
151
pub enum MorphTargetsResource<'a> {
152
/// The [`MorphTargetImage`].
153
///
154
/// This variant is used when storage buffers aren't supported on the
155
/// current platform.
156
Texture(&'a TextureView),
157
158
/// The slab containing the morph target displacements.
159
///
160
/// This variant is used when storage buffers are supported on the current
161
/// platform.
162
Storage(&'a Buffer),
163
}
164
165
impl RenderMorphTargetAllocator {
166
/// Allocates morph target displacements for the given mesh.
167
///
168
/// If storage buffers aren't supported on the current platform, this method
169
/// creates a new [`MorphTargetImage`] and stores it inside the allocator.
170
///
171
/// If storage buffers are supported on the current platform, this method
172
/// does nothing, as morph target displacements are instead managed by the
173
/// mesh allocator.
174
pub fn allocate(
175
&mut self,
176
render_device: &RenderDevice,
177
render_queue: &RenderQueue,
178
mesh_id: AssetId<Mesh>,
179
targets: &[MorphAttributes],
180
vertex_count: usize,
181
) {
182
match *self {
183
RenderMorphTargetAllocator::Image {
184
ref mut mesh_id_to_image,
185
} => {
186
if let Ok(morph_target_image) =
187
MorphTargetImage::new(render_device, render_queue, targets, vertex_count)
188
{
189
mesh_id_to_image.insert(mesh_id, morph_target_image);
190
}
191
}
192
193
RenderMorphTargetAllocator::Storage => {
194
// Do nothing. Morph target displacements are managed by the
195
// mesh allocator in this case.
196
}
197
}
198
}
199
200
/// Frees the storage associated with morph target displacements for the
201
/// mesh with the given ID.
202
///
203
/// If the current platform doesn't support storage buffers, this drops the
204
/// reference to the [`MorphTargetImage`] that stores the data for the
205
/// mesh's morph target displacements. If the current platform does support
206
/// storage buffers, this method does nothing, as morph target displacements
207
/// are managed by the mesh allocator in this case.
208
///
209
/// If passed a mesh without morph targets, this method does nothing.
210
pub fn free(&mut self, mesh_id: AssetId<Mesh>) {
211
match *self {
212
RenderMorphTargetAllocator::Image {
213
ref mut mesh_id_to_image,
214
} => {
215
if mesh_id_to_image.remove(&mesh_id).is_none() {
216
error!(
217
"Attempted to free a morph target allocation that wasn't allocated: {:?}",
218
mesh_id
219
);
220
}
221
}
222
RenderMorphTargetAllocator::Storage => {
223
// Do nothing. Morph target displacements are managed by the
224
// mesh allocator in this case.
225
}
226
}
227
}
228
229
/// Returns the [`MorphTargetImage`] containing the packed morph target
230
/// displacements for the mesh with the given ID, if that image is present.
231
///
232
/// A [`MorphTargetImage`] is only available if storage buffers aren't
233
/// supported on the given platform. If storage buffers are supported, this
234
/// method returns `None`, as the mesh allocator stores the morph target
235
/// displacements in that case.
236
pub fn get_image(&self, mesh_id: AssetId<Mesh>) -> Option<MorphTargetImage> {
237
match *self {
238
RenderMorphTargetAllocator::Image {
239
ref mesh_id_to_image,
240
} => mesh_id_to_image.get(&mesh_id).cloned(),
241
RenderMorphTargetAllocator::Storage => None,
242
}
243
}
244
}
245
246
struct Rect(u32, u32);
247
248
/// Find the smallest rectangle of maximum edge size `max_edge` that contains
249
/// at least `min_includes` cells. `u32` is how many extra cells the rectangle
250
/// has.
251
///
252
/// The following rectangle contains 27 cells, and its longest edge is 9:
253
/// ```text
254
/// ----------------------------
255
/// |1 |2 |3 |4 |5 |6 |7 |8 |9 |
256
/// ----------------------------
257
/// |2 | | | | | | | | |
258
/// ----------------------------
259
/// |3 | | | | | | | | |
260
/// ----------------------------
261
/// ```
262
///
263
/// Returns `None` if `max_edge` is too small to build a rectangle
264
/// containing `min_includes` cells.
265
fn lowest_2d(min_includes: u32, max_edge: u32) -> Option<(Rect, u32)> {
266
(1..=max_edge)
267
.filter_map(|a| {
268
let b = min_includes.div_ceil(a);
269
let diff = (a * b).checked_sub(min_includes)?;
270
Some((Rect(a, b), diff))
271
})
272
.filter_map(|(rect, diff)| (rect.1 <= max_edge).then_some((rect, diff)))
273
.min_by_key(|(_, diff)| *diff)
274
}
275
276