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