Skip to main content

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}