Skip to main content

bevy_render/mesh/
mod.rs

1pub mod allocator;
2#[cfg(feature = "morph")]
3pub mod morph;
4
5use crate::{
6    mesh::allocator::{ElementClass, MeshAllocationKey, MeshAllocator, MeshSlabId},
7    render_asset::{
8        prepare_assets, AssetExtractionError, PrepareAssetError, RenderAsset, RenderAssetPlugin,
9    },
10    render_resource::Buffer,
11    renderer::{RenderDevice, RenderQueue},
12    texture::GpuImage,
13    Render, RenderApp, RenderSystems,
14};
15use allocator::MeshAllocatorPlugin;
16use bevy_app::{App, Plugin};
17use bevy_asset::{AssetId, Assets, Handle, RenderAssetUsages};
18use bevy_camera::primitives::MeshAabb;
19use bevy_ecs::{
20    prelude::*,
21    system::{
22        lifetimeless::{SRes, SResMut},
23        SystemParamItem,
24    },
25};
26use bevy_encase_derive::ShaderType;
27pub use bevy_mesh::*;
28use bevy_shader::load_shader_library;
29use bytemuck::{Pod, Zeroable};
30use glam::{Vec3, Vec4};
31use wgpu::IndexFormat;
32
33#[cfg(feature = "morph")]
34use crate::mesh::morph::RenderMorphTargetAllocator;
35
36/// Makes sure that [`Mesh`]es are extracted and prepared for the GPU.
37/// Does *not* add the [`Mesh`] as an asset. Use [`MeshPlugin`] for that.
38pub struct MeshRenderAssetPlugin;
39
40impl Plugin for MeshRenderAssetPlugin {
41    fn build(&self, app: &mut App) {
42        load_shader_library!(app, "metadata_types.wesl");
43
44        app
45            // 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready
46            .add_plugins(RenderAssetPlugin::<RenderMesh, GpuImage>::default())
47            .add_plugins(MeshAllocatorPlugin);
48
49        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
50            return;
51        };
52
53        render_app
54            .init_resource::<MeshVertexBufferLayouts>()
55            .add_systems(
56                Render,
57                prepare_mesh_metadata_fallback_buffer
58                    .in_set(RenderSystems::PrepareAssets)
59                    .after(prepare_assets::<RenderMesh>),
60            );
61    }
62
63    fn finish(&self, app: &mut App) {
64        let mut mesh_assets = app.world_mut().resource_mut::<Assets<Mesh>>();
65        let handle = mesh_assets.add({
66            let mut mesh = Mesh::new(PrimitiveTopology::PointList, RenderAssetUsages::all())
67                .with_inserted_attribute(
68                    Mesh::ATTRIBUTE_POSITION,
69                    VertexAttributeValues::Float32x3(vec![[0.0; 3]]),
70                )
71                .with_inserted_indices(Indices::U16(vec![0]));
72            mesh.compress_positions().unwrap();
73            mesh
74        });
75
76        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
77            return;
78        };
79
80        render_app.insert_resource(MeshMetadataFallbackMesh(handle));
81
82        #[cfg(feature = "morph")]
83        crate::GpuResourceAppExt::init_gpu_resource::<RenderMorphTargetAllocator>(render_app);
84    }
85}
86
87#[derive(Resource)]
88pub struct MeshMetadataFallbackMesh(pub Handle<Mesh>);
89
90/// Metadata slab ID and buffer of [`MeshMetadataFallbackMesh`],
91/// used to fill bind group for mesh without metadata.
92#[derive(Resource)]
93pub struct MeshMetadataFallbackBuffer {
94    pub slab_id: MeshSlabId,
95    pub buffer: Buffer,
96}
97
98pub fn prepare_mesh_metadata_fallback_buffer(
99    mut commands: Commands,
100    mesh_allocator: Res<MeshAllocator>,
101    metadata_fallback_mesh: Res<MeshMetadataFallbackMesh>,
102) {
103    let slab_id = mesh_allocator
104        .key_to_slab
105        .get(&MeshAllocationKey::new(
106            metadata_fallback_mesh.0.id(),
107            ElementClass::Metadata,
108        ))
109        .cloned()
110        .unwrap();
111    let buffer = mesh_allocator.buffer_for_slab(slab_id).unwrap().clone();
112    commands.insert_resource(MeshMetadataFallbackBuffer { slab_id, buffer });
113}
114
115/// Per-mesh metadata, stored in [`crate::mesh::allocator::MeshAllocator`].
116/// Currently this is used to decompress vertex.
117#[derive(Default, Pod, Zeroable, Clone, Copy, Debug, ShaderType)]
118#[repr(C)]
119pub struct MeshMetadata {
120    // AABB for decompressing positions.
121    pub aabb_center: Vec3,
122    pub pad1: u32,
123    // AABB for decompressing positions.
124    pub aabb_half_extents: Vec3,
125    pub pad2: u32,
126    // UV channels range for decompressing UVs coordinates.
127    pub uv_channels_min_and_extents: [Vec4; 2],
128}
129
130/// The render world representation of a [`Mesh`].
131#[derive(Debug, Clone)]
132pub struct RenderMesh {
133    /// The number of vertices in the mesh.
134    pub vertex_count: u32,
135
136    /// The 3D center of the mesh in model space.
137    pub aabb_center: Vec3,
138
139    /// Information about the mesh data buffers, including whether the mesh uses
140    /// indices or not.
141    pub buffer_info: RenderMeshBufferInfo,
142
143    /// Precomputed pipeline key bits for this mesh.
144    pub key_bits: BaseMeshPipelineKey,
145
146    /// A reference to the vertex buffer layout.
147    ///
148    /// Combined with [`RenderMesh::buffer_info`], this specifies the complete
149    /// layout of the buffers associated with this mesh.
150    pub layout: MeshVertexBufferLayoutRef,
151}
152
153impl RenderMesh {
154    /// Returns the primitive topology of this mesh (triangles, triangle strips,
155    /// etc.)
156    #[inline]
157    pub fn primitive_topology(&self) -> PrimitiveTopology {
158        self.key_bits.primitive_topology()
159    }
160
161    /// Returns true if this mesh uses an index buffer or false otherwise.
162    #[inline]
163    pub fn indexed(&self) -> bool {
164        matches!(self.buffer_info, RenderMeshBufferInfo::Indexed { .. })
165    }
166
167    #[inline]
168    pub fn index_format(&self) -> Option<IndexFormat> {
169        match self.buffer_info {
170            RenderMeshBufferInfo::Indexed { index_format, .. } => Some(index_format),
171            RenderMeshBufferInfo::NonIndexed => None,
172        }
173    }
174
175    #[inline]
176    pub fn has_morph_targets(&self) -> bool {
177        self.key_bits.contains(BaseMeshPipelineKey::MORPH_TARGETS)
178    }
179}
180
181/// The index/vertex buffer info of a [`RenderMesh`].
182#[derive(Debug, Clone)]
183pub enum RenderMeshBufferInfo {
184    Indexed {
185        count: u32,
186        index_format: IndexFormat,
187    },
188    NonIndexed,
189}
190
191impl RenderAsset for RenderMesh {
192    type SourceAsset = Mesh;
193
194    #[cfg(not(feature = "morph"))]
195    type Param = (
196        SRes<RenderDevice>,
197        SRes<RenderQueue>,
198        SResMut<MeshVertexBufferLayouts>,
199        (),
200    );
201    #[cfg(feature = "morph")]
202    type Param = (
203        SRes<RenderDevice>,
204        SRes<RenderQueue>,
205        SResMut<MeshVertexBufferLayouts>,
206        SResMut<RenderMorphTargetAllocator>,
207    );
208
209    #[inline]
210    fn asset_usage(mesh: &Self::SourceAsset) -> RenderAssetUsages {
211        mesh.asset_usage
212    }
213
214    fn take_gpu_data(
215        source: &mut Self::SourceAsset,
216        _previous_gpu_asset: Option<&Self>,
217    ) -> Result<Self::SourceAsset, AssetExtractionError> {
218        source
219            .take_gpu_data()
220            .map_err(|_| AssetExtractionError::AlreadyExtracted)
221    }
222
223    fn byte_len(mesh: &Self::SourceAsset) -> Option<usize> {
224        let mut vertex_size = 0;
225        for attribute_data in mesh.attributes() {
226            let vertex_format = attribute_data.0.format;
227            vertex_size += vertex_format.size() as usize;
228        }
229
230        let vertex_count = mesh.count_vertices();
231        let index_bytes = mesh.get_index_buffer_bytes().map(<[_]>::len).unwrap_or(0);
232        Some(vertex_size * vertex_count + index_bytes)
233    }
234
235    /// Converts the extracted mesh into a [`RenderMesh`].
236    fn prepare_asset(
237        mesh: Self::SourceAsset,
238        _mesh_id: AssetId<Self::SourceAsset>,
239        (
240            _render_device,
241            _render_queue,
242            mesh_vertex_buffer_layouts,
243            _render_morph_targets_allocator,
244        ): &mut SystemParamItem<Self::Param>,
245        _: Option<&Self>,
246    ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
247        let (buffer_info, index_format) = match mesh.indices() {
248            Some(indices) => (
249                RenderMeshBufferInfo::Indexed {
250                    count: indices.len() as u32,
251                    index_format: indices.into(),
252                },
253                Some(indices.into()),
254            ),
255            None => (RenderMeshBufferInfo::NonIndexed, None),
256        };
257
258        let mesh_vertex_buffer_layout =
259            mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
260
261        let key_bits = BaseMeshPipelineKey::from_primitive_topology_and_strip_index(
262            mesh.primitive_topology(),
263            index_format,
264        );
265        #[cfg(feature = "morph")]
266        let key_bits = if mesh.morph_targets().is_some() {
267            key_bits | BaseMeshPipelineKey::MORPH_TARGETS
268        } else {
269            key_bits
270        };
271
272        // Place the morph displacements in an image if necessary.
273        #[cfg(feature = "morph")]
274        if let Some(morph_targets) = mesh.morph_targets() {
275            _render_morph_targets_allocator.allocate(
276                _render_device,
277                _render_queue,
278                _mesh_id,
279                morph_targets,
280                mesh.count_vertices(),
281            );
282        }
283
284        Ok(RenderMesh {
285            vertex_count: mesh.count_vertices() as u32,
286            aabb_center: match mesh.get_aabb() {
287                Some(aabb) => aabb.center.into(),
288                None => Vec3::ZERO,
289            },
290            buffer_info,
291            key_bits,
292            layout: mesh_vertex_buffer_layout,
293        })
294    }
295
296    fn unload_asset(
297        _mesh_id: AssetId<Self::SourceAsset>,
298        (_, _, _, _render_morph_targets_allocator): &mut SystemParamItem<Self::Param>,
299    ) {
300        // Free the morph target images if necessary.
301        #[cfg(feature = "morph")]
302        _render_morph_targets_allocator.free(_mesh_id);
303    }
304}