bevy_render/mesh/
mod.rs

1pub mod allocator;
2use crate::{
3    render_asset::{
4        AssetExtractionError, PrepareAssetError, RenderAsset, RenderAssetPlugin, RenderAssets,
5    },
6    texture::GpuImage,
7    RenderApp,
8};
9use allocator::MeshAllocatorPlugin;
10use bevy_app::{App, Plugin};
11use bevy_asset::{AssetId, RenderAssetUsages};
12use bevy_ecs::{
13    prelude::*,
14    system::{
15        lifetimeless::{SRes, SResMut},
16        SystemParamItem,
17    },
18};
19#[cfg(feature = "morph")]
20use bevy_mesh::morph::{MeshMorphWeights, MorphWeights};
21use bevy_mesh::*;
22use wgpu::IndexFormat;
23
24/// Makes sure that [`Mesh`]es are extracted and prepared for the GPU.
25/// Does *not* add the [`Mesh`] as an asset. Use [`MeshPlugin`] for that.
26pub struct MeshRenderAssetPlugin;
27
28impl Plugin for MeshRenderAssetPlugin {
29    fn build(&self, app: &mut App) {
30        app
31            // 'Mesh' must be prepared after 'Image' as meshes rely on the morph target image being ready
32            .add_plugins(RenderAssetPlugin::<RenderMesh, GpuImage>::default())
33            .add_plugins(MeshAllocatorPlugin);
34
35        let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
36            return;
37        };
38
39        render_app.init_resource::<MeshVertexBufferLayouts>();
40    }
41}
42
43/// [Inherit weights](inherit_weights) from glTF mesh parent entity to direct
44/// bevy mesh child entities (ie: glTF primitive).
45#[cfg(feature = "morph")]
46pub struct MorphPlugin;
47#[cfg(feature = "morph")]
48impl Plugin for MorphPlugin {
49    fn build(&self, app: &mut App) {
50        app.add_systems(
51            bevy_app::PostUpdate,
52            inherit_weights.in_set(InheritWeightSystems),
53        );
54    }
55}
56
57/// Bevy meshes are gltf primitives, [`MorphWeights`] on the bevy node entity
58/// should be inherited by children meshes.
59///
60/// Only direct children are updated, to fulfill the expectations of glTF spec.
61#[cfg(feature = "morph")]
62pub fn inherit_weights(
63    morph_nodes: Query<(&Children, &MorphWeights), (Without<Mesh3d>, Changed<MorphWeights>)>,
64    mut morph_primitives: Query<&mut MeshMorphWeights, With<Mesh3d>>,
65) {
66    for (children, parent_weights) in &morph_nodes {
67        let mut iter = morph_primitives.iter_many_mut(children);
68        while let Some(mut child_weight) = iter.fetch_next() {
69            child_weight.clear_weights();
70            child_weight.extend_weights(parent_weights.weights());
71        }
72    }
73}
74
75/// The render world representation of a [`Mesh`].
76#[derive(Debug, Clone)]
77pub struct RenderMesh {
78    /// The number of vertices in the mesh.
79    pub vertex_count: u32,
80
81    /// Morph targets for the mesh, if present.
82    #[cfg(feature = "morph")]
83    pub morph_targets: Option<crate::render_resource::TextureView>,
84
85    /// Information about the mesh data buffers, including whether the mesh uses
86    /// indices or not.
87    pub buffer_info: RenderMeshBufferInfo,
88
89    /// Precomputed pipeline key bits for this mesh.
90    pub key_bits: BaseMeshPipelineKey,
91
92    /// A reference to the vertex buffer layout.
93    ///
94    /// Combined with [`RenderMesh::buffer_info`], this specifies the complete
95    /// layout of the buffers associated with this mesh.
96    pub layout: MeshVertexBufferLayoutRef,
97}
98
99impl RenderMesh {
100    /// Returns the primitive topology of this mesh (triangles, triangle strips,
101    /// etc.)
102    #[inline]
103    pub fn primitive_topology(&self) -> PrimitiveTopology {
104        self.key_bits.primitive_topology()
105    }
106
107    /// Returns true if this mesh uses an index buffer or false otherwise.
108    #[inline]
109    pub fn indexed(&self) -> bool {
110        matches!(self.buffer_info, RenderMeshBufferInfo::Indexed { .. })
111    }
112}
113
114/// The index/vertex buffer info of a [`RenderMesh`].
115#[derive(Debug, Clone)]
116pub enum RenderMeshBufferInfo {
117    Indexed {
118        count: u32,
119        index_format: IndexFormat,
120    },
121    NonIndexed,
122}
123
124impl RenderAsset for RenderMesh {
125    type SourceAsset = Mesh;
126    type Param = (
127        SRes<RenderAssets<GpuImage>>,
128        SResMut<MeshVertexBufferLayouts>,
129    );
130
131    #[inline]
132    fn asset_usage(mesh: &Self::SourceAsset) -> RenderAssetUsages {
133        mesh.asset_usage
134    }
135
136    fn take_gpu_data(
137        source: &mut Self::SourceAsset,
138        _previous_gpu_asset: Option<&Self>,
139    ) -> Result<Self::SourceAsset, AssetExtractionError> {
140        source
141            .take_gpu_data()
142            .map_err(|_| AssetExtractionError::AlreadyExtracted)
143    }
144
145    fn byte_len(mesh: &Self::SourceAsset) -> Option<usize> {
146        let mut vertex_size = 0;
147        for attribute_data in mesh.attributes() {
148            let vertex_format = attribute_data.0.format;
149            vertex_size += vertex_format.size() as usize;
150        }
151
152        let vertex_count = mesh.count_vertices();
153        let index_bytes = mesh.get_index_buffer_bytes().map(<[_]>::len).unwrap_or(0);
154        Some(vertex_size * vertex_count + index_bytes)
155    }
156
157    /// Converts the extracted mesh into a [`RenderMesh`].
158    fn prepare_asset(
159        mesh: Self::SourceAsset,
160        _: AssetId<Self::SourceAsset>,
161        (_images, mesh_vertex_buffer_layouts): &mut SystemParamItem<Self::Param>,
162        _: Option<&Self>,
163    ) -> Result<Self, PrepareAssetError<Self::SourceAsset>> {
164        #[cfg(feature = "morph")]
165        let morph_targets = match mesh.morph_targets() {
166            Some(mt) => {
167                let Some(target_image) = _images.get(mt) else {
168                    return Err(PrepareAssetError::RetryNextUpdate(mesh));
169                };
170                Some(target_image.texture_view.clone())
171            }
172            None => None,
173        };
174
175        let buffer_info = match mesh.indices() {
176            Some(indices) => RenderMeshBufferInfo::Indexed {
177                count: indices.len() as u32,
178                index_format: indices.into(),
179            },
180            None => RenderMeshBufferInfo::NonIndexed,
181        };
182
183        let mesh_vertex_buffer_layout =
184            mesh.get_mesh_vertex_buffer_layout(mesh_vertex_buffer_layouts);
185
186        let key_bits = BaseMeshPipelineKey::from_primitive_topology(mesh.primitive_topology());
187        #[cfg(feature = "morph")]
188        let key_bits = if mesh.morph_targets().is_some() {
189            key_bits | BaseMeshPipelineKey::MORPH_TARGETS
190        } else {
191            key_bits
192        };
193
194        Ok(RenderMesh {
195            vertex_count: mesh.count_vertices() as u32,
196            buffer_info,
197            key_bits,
198            layout: mesh_vertex_buffer_layout,
199            #[cfg(feature = "morph")]
200            morph_targets,
201        })
202    }
203}