diff --git a/crates/bevy_gltf/src/loader.rs b/crates/bevy_gltf/src/loader.rs index 25da841eb3664..a622e96f9f82b 100644 --- a/crates/bevy_gltf/src/loader.rs +++ b/crates/bevy_gltf/src/loader.rs @@ -33,6 +33,7 @@ use std::{ use thiserror::Error; use crate::{Gltf, GltfNode}; +use bevy_render::mesh::MeshData; /// An error that occurs when loading a GLTF file #[derive(Error, Debug)] @@ -112,40 +113,41 @@ async fn load_gltf<'a, 'b>( let reader = primitive.reader(|buffer| Some(&buffer_data[buffer.index()])); let primitive_topology = get_primitive_topology(primitive.mode())?; - let mut mesh = Mesh::new(primitive_topology); + let mut mesh_data = MeshData::default(); if let Some(vertex_attribute) = reader .read_positions() .map(|v| VertexAttributeValues::Float3(v.collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vertex_attribute); + mesh_data.set_attribute(MeshData::ATTRIBUTE_POSITION, vertex_attribute); } if let Some(vertex_attribute) = reader .read_normals() .map(|v| VertexAttributeValues::Float3(v.collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, vertex_attribute); + mesh_data.set_attribute(MeshData::ATTRIBUTE_NORMAL, vertex_attribute); } if let Some(vertex_attribute) = reader .read_tangents() .map(|v| VertexAttributeValues::Float4(v.collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_TANGENT, vertex_attribute); + mesh_data.set_attribute(MeshData::ATTRIBUTE_TANGENT, vertex_attribute); } if let Some(vertex_attribute) = reader .read_tex_coords(0) .map(|v| VertexAttributeValues::Float2(v.into_f32().collect())) { - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vertex_attribute); + mesh_data.set_attribute(MeshData::ATTRIBUTE_UV_0, vertex_attribute); } if let Some(indices) = reader.read_indices() { - mesh.set_indices(Some(Indices::U32(indices.into_u32().collect()))); + mesh_data.set_indices(Some(Indices::U32(indices.into_u32().collect()))); }; + let mesh = Mesh::new_static(primitive_topology, mesh_data); let mesh = load_context.set_labeled_asset(&primitive_label, LoadedAsset::new(mesh)); primitives.push(super::GltfPrimitive { mesh, diff --git a/crates/bevy_render/src/mesh/mesh.rs b/crates/bevy_render/src/mesh/mesh.rs index aece08891876f..1068f44d42524 100644 --- a/crates/bevy_render/src/mesh/mesh.rs +++ b/crates/bevy_render/src/mesh/mesh.rs @@ -1,3 +1,4 @@ +use crate::pipeline::{InputStepMode, VertexAttribute, VertexBufferLayout}; use crate::{ pipeline::{IndexFormat, PrimitiveTopology, RenderPipelines, VertexFormat}, renderer::{BufferInfo, BufferUsage, RenderResourceContext, RenderResourceId}, @@ -5,6 +6,7 @@ use crate::{ use bevy_app::prelude::EventReader; use bevy_asset::{AssetEvent, Assets, Handle}; use bevy_core::AsBytes; +use bevy_ecs::prelude::ResMut; use bevy_ecs::{ entity::Entity, query::{Changed, With}, @@ -13,11 +15,9 @@ use bevy_ecs::{ }; use bevy_math::*; use bevy_reflect::TypeUuid; -use std::borrow::Cow; - -use crate::pipeline::{InputStepMode, VertexAttribute, VertexBufferLayout}; +use bevy_utils::tracing::error; use bevy_utils::{HashMap, HashSet}; - +use std::borrow::Cow; pub const INDEX_BUFFER_ASSET_INDEX: u64 = 0; pub const VERTEX_ATTRIBUTE_BUFFER_ID: u64 = 10; @@ -203,35 +203,60 @@ impl From<&Indices> for IndexFormat { } } -// TODO: allow values to be unloaded after been submitting to the GPU to conserve memory +/// `bevy_utils::HashMap` with all defined vertex attributes (Positions, Normals, ...) for this +/// mesh. Attribute name maps to attribute values. +pub type MeshAttributes = HashMap, VertexAttributeValues>; + #[derive(Debug, TypeUuid, Clone)] #[uuid = "8ecbac0f-f545-4473-ad43-e1f4243af51e"] pub struct Mesh { primitive_topology: PrimitiveTopology, - /// `bevy_utils::HashMap` with all defined vertex attributes (Positions, Normals, ...) for this - /// mesh. Attribute name maps to attribute values. - attributes: HashMap, VertexAttributeValues>, + storage: MeshStorageState, +} + +#[derive(Debug, Clone, Default)] +pub struct MeshData { + attributes: MeshAttributes, indices: Option, + meta: MeshMetaInfo, } -/// Contains geometry in the form of a mesh. -/// -/// Often meshes are automatically generated by bevy's asset loaders or primitives, such as -/// [`crate::shape::Cube`] or [`crate::shape::Box`], but you can also construct -/// one yourself. -/// -/// Example of constructing a mesh: -/// ``` -/// # use bevy_render::mesh::{Mesh, Indices}; -/// # use bevy_render::pipeline::PrimitiveTopology; -/// fn create_triangle() -> Mesh { -/// let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); -/// mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]); -/// mesh.set_indices(Some(Indices::U32(vec![0,1,2]))); -/// mesh -/// } -/// ``` -impl Mesh { +#[derive(Debug, Clone, Default)] +pub struct MeshMetaInfo { + vertex_layout: VertexBufferLayout, + vertex_count: u32, + index: Option<(u32, IndexFormat)>, +} + +#[derive(Debug, Clone)] +enum MeshStorageState { + /// Only a local copy exists, but it's not intended for realtime modification. + ToBeOnGpu(MeshData), + /// Data only exists on the Gpu. + OnGpu(MeshMetaInfo), + /// A local copy exists and is available for realtime modification. + OnGpuAndLocal(MeshData), +} + +impl MeshMetaInfo { + pub fn get_vertex_buffer_layout(&self) -> VertexBufferLayout { + self.vertex_layout.clone() + } + + pub fn get_vertices_count(&self) -> usize { + self.vertex_count as usize + } + + pub fn get_index_count(&self) -> Option { + self.index.map(|x| x.0) + } + + pub fn get_index_format(&self) -> Option { + self.index.map(|x| x.1) + } +} + +impl MeshData { /// Per vertex coloring. Use in conjunction with [`Mesh::set_attribute`] pub const ATTRIBUTE_COLOR: &'static str = "Vertex_Color"; /// The direction the vertex normal is facing in. @@ -245,67 +270,26 @@ impl Mesh { /// Texture coordinates for the vertex. Use in conjunction with [`Mesh::set_attribute`] pub const ATTRIBUTE_UV_0: &'static str = "Vertex_Uv"; - /// Construct a new mesh. You need to provide a PrimitiveTopology so that the - /// renderer knows how to treat the vertex data. Most of the time this will be - /// `PrimitiveTopology::TriangleList`. - pub fn new(primitive_topology: PrimitiveTopology) -> Self { - Mesh { - primitive_topology, - attributes: Default::default(), - indices: None, - } - } - - pub fn primitive_topology(&self) -> PrimitiveTopology { - self.primitive_topology - } - /// Sets the data for a vertex attribute (position, normal etc.). The name will - /// often be one of the associated constants such as [`Mesh::ATTRIBUTE_POSITION`] + /// often be one of the associated constants such as [`MeshData::ATTRIBUTE_POSITION`] pub fn set_attribute( &mut self, name: impl Into>, values: impl Into, ) { let values: VertexAttributeValues = values.into(); - self.attributes.insert(name.into(), values); - } - - /// Retrieve the data currently set behind a vertex attribute. - pub fn attribute(&self, name: impl Into>) -> Option<&VertexAttributeValues> { - self.attributes.get(&name.into()) - } - - pub fn attribute_mut( - &mut self, - name: impl Into>, - ) -> Option<&mut VertexAttributeValues> { - self.attributes.get_mut(&name.into()) - } - - /// Indices describe how triangles are constructed out of the vertex attributes. - /// They are only useful for the [`crate::pipeline::PrimitiveTopology`] variants that use - /// triangles - pub fn set_indices(&mut self, indices: Option) { - self.indices = indices; - } - - pub fn indices(&self) -> Option<&Indices> { - self.indices.as_ref() - } + let name = name.into(); + // update or verify the vertex count + if self.meta.vertex_count == 0 { + self.meta.vertex_count = values.len() as u32; + } else { + assert_eq!(values.len() as u32, self.meta.vertex_count, + "Attribute {} has a different vertex count ({}) than other attributes ({}) in this mesh.", &name, values.len(), self.meta.vertex_count); + } - pub fn indices_mut(&mut self) -> Option<&mut Indices> { - self.indices.as_mut() - } + self.attributes.insert(name, values); - pub fn get_index_buffer_bytes(&self) -> Option> { - self.indices.as_ref().map(|indices| match &indices { - Indices::U16(indices) => indices.as_slice().as_bytes().to_vec(), - Indices::U32(indices) => indices.as_slice().as_bytes().to_vec(), - }) - } - - pub fn get_vertex_buffer_layout(&self) -> VertexBufferLayout { + // update the vertex layout let mut attributes = Vec::new(); let mut accumulated_offset = 0; for (attribute_name, attribute_values) in self.attributes.iter() { @@ -319,7 +303,7 @@ impl Mesh { accumulated_offset += vertex_format.get_size(); } - VertexBufferLayout { + self.meta.vertex_layout = VertexBufferLayout { name: Default::default(), stride: accumulated_offset, step_mode: InputStepMode::Vertex, @@ -327,18 +311,46 @@ impl Mesh { } } - pub fn count_vertices(&self) -> usize { - let mut vertex_count: Option = None; - for (attribute_name, attribute_data) in self.attributes.iter() { - let attribute_len = attribute_data.len(); - if let Some(previous_vertex_count) = vertex_count { - assert_eq!(previous_vertex_count, attribute_len, - "Attribute {} has a different vertex count ({}) than other attributes ({}) in this mesh.", attribute_name, attribute_len, previous_vertex_count); + /// Retrieve the data currently set behind a vertex attribute. + pub fn attribute(&self, name: impl Into>) -> Option<&VertexAttributeValues> { + self.attributes.get(&name.into()) + } + + pub fn attribute_mut( + &mut self, + name: impl Into>, + ) -> Option<&mut VertexAttributeValues> { + self.attributes.get_mut(&name.into()) + } + /// Indices describe how triangles are constructed out of the vertex attributes. + /// They are only useful for the [`crate::pipeline::PrimitiveTopology`] variants that use + /// triangles + pub fn set_indices(&mut self, new_indices: Option) { + self.indices = new_indices; + + // count indices and set indices format, if existing + self.meta.index = None; + if let Some(indices) = &self.indices { + match indices { + Indices::U16(indices) => { + self.meta.index = Some((indices.len() as u32, IndexFormat::Uint16)); + } + Indices::U32(indices) => { + self.meta.index = Some((indices.len() as u32, IndexFormat::Uint32)); + } } - vertex_count = Some(attribute_len); } + } + + pub fn indices(&self) -> Option<&Indices> { + self.indices.as_ref() + } - vertex_count.unwrap_or(0) + pub fn get_indices_buffer_bytes(&self) -> Option> { + self.indices.as_ref().map(|indices| match indices { + Indices::U16(indices) => indices.as_slice().as_bytes().to_vec(), + Indices::U32(indices) => indices.as_slice().as_bytes().to_vec(), + }) } pub fn get_vertex_buffer_data(&self) -> Vec { @@ -348,8 +360,9 @@ impl Mesh { vertex_size += vertex_format.get_size() as usize; } - let vertex_count = self.count_vertices(); + let vertex_count = self.meta.get_vertices_count(); let mut attributes_interleaved_buffer = vec![0; vertex_count * vertex_size]; + // bundle into interleaved buffers let mut attribute_offset = 0; for attribute_values in self.attributes.values() { @@ -366,9 +379,82 @@ impl Mesh { attribute_offset += attribute_size; } - attributes_interleaved_buffer } + + pub fn get_meta(&self) -> &MeshMetaInfo { + &self.meta + } +} + +/// Contains geometry in the form of a mesh. +/// +/// Often meshes are automatically generated by bevy's asset loaders or primitives, such as +/// [`crate::shape::Cube`] or [`crate::shape::Box`], but you can also construct +/// one yourself. +/// +/// Example of constructing a mesh: +/// ``` +/// # use bevy_render::mesh::{Mesh, Indices, MeshData}; +/// # use bevy_render::pipeline::PrimitiveTopology; +/// fn create_triangle() -> Mesh { +/// +/// let mut mesh_data = MeshData::default(); +/// mesh_data.set_attribute(MeshData::ATTRIBUTE_POSITION, vec![[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [1.0, 1.0, 0.0]]); +/// mesh_data.set_indices(Some(Indices::U32(vec![0,1,2]))); +/// Mesh::new_static(PrimitiveTopology::TriangleList, mesh_data) +/// } +/// ``` +impl Mesh { + /// Construct a new mesh. You need to provide a PrimitiveTopology so that the + /// renderer knows how to treat the vertex data. Most of the time this will be + /// `PrimitiveTopology::TriangleList`. The mesh will not be editable after creation. + /// If you want to edit the mesh further, use [`Mesh::new_dynamic`] + pub fn new_static(primitive_topology: PrimitiveTopology, mesh_data: MeshData) -> Self { + Mesh { + primitive_topology, + storage: MeshStorageState::ToBeOnGpu(mesh_data), + } + } + + /// Like [`Mesh::new`], but the data will not be locally unloaded and can be modified at runtime. Consumes additional RAM. + pub fn new_dynamic(primitive_topology: PrimitiveTopology, mesh_data: MeshData) -> Self { + Mesh { + primitive_topology, + storage: MeshStorageState::OnGpuAndLocal(mesh_data), + } + } + + pub fn primitive_topology(&self) -> PrimitiveTopology { + self.primitive_topology + } + + pub fn get_mesh_data(&self) -> Option<&MeshData> { + match &self.storage { + MeshStorageState::OnGpuAndLocal(mesh_data) => Some(&mesh_data), + _ => None, + } + } + + pub fn get_mesh_data_mut(&mut self) -> Option<&mut MeshData> { + match &mut self.storage { + MeshStorageState::OnGpuAndLocal(mesh_data) => Some(&mut *mesh_data), + _ => None, + } + } + + pub fn meta(&self) -> &MeshMetaInfo { + match &self.storage { + MeshStorageState::ToBeOnGpu(mesh_data) => &mesh_data.meta, + MeshStorageState::OnGpu(meta_data) => &meta_data, + MeshStorageState::OnGpuAndLocal(mesh_data) => &mesh_data.meta, + } + } + + /// Whether the mesh data is intend to be edited an runtime + pub fn is_dynamic(&self) -> bool { + matches!(&self.storage, MeshStorageState::OnGpuAndLocal(_)) + } } fn remove_resource_save( @@ -404,77 +490,94 @@ pub struct MeshResourceProviderState { pub fn mesh_resource_provider_system( mut state: Local, render_resource_context: Res>, - meshes: Res>, + mut meshes: ResMut>, mut mesh_events: EventReader>, mut queries: QuerySet<( Query<&mut RenderPipelines, With>>, Query<(Entity, &Handle, &mut RenderPipelines), Changed>>, )>, ) { - let mut changed_meshes = HashSet::default(); + let mut meshes_to_upload_to_gpu = HashSet::default(); + let mut meshes_to_locally_unload = HashSet::default(); let render_resource_context = &**render_resource_context; for event in mesh_events.iter() { match event { AssetEvent::Created { ref handle } => { - changed_meshes.insert(handle.clone_weak()); + meshes_to_upload_to_gpu.insert(handle.clone_weak()); } AssetEvent::Modified { ref handle } => { - changed_meshes.insert(handle.clone_weak()); - remove_current_mesh_resources(render_resource_context, handle); + if let Some(mesh) = meshes.get(handle) { + // don't unload buffers from meshes without a local copy + // since they won't be re added anymore. + if !matches!(mesh.storage, MeshStorageState::OnGpu(_)) { + meshes_to_upload_to_gpu.insert(handle.clone_weak()); + remove_current_mesh_resources(render_resource_context, handle); + } + } } AssetEvent::Removed { ref handle } => { remove_current_mesh_resources(render_resource_context, handle); // if mesh was modified and removed in the same update, ignore the modification // events are ordered so future modification events are ok - changed_meshes.remove(handle); + meshes_to_upload_to_gpu.remove(handle); } } } // update changed mesh data - for changed_mesh_handle in changed_meshes.iter() { + for changed_mesh_handle in meshes_to_upload_to_gpu.iter() { if let Some(mesh) = meshes.get(changed_mesh_handle) { - // TODO: check for individual buffer changes in non-interleaved mode - if let Some(data) = mesh.get_index_buffer_bytes() { - let index_buffer = render_resource_context.create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::INDEX, - ..Default::default() - }, - &data, - ); - - render_resource_context.set_asset_resource( - changed_mesh_handle, - RenderResourceId::Buffer(index_buffer), - INDEX_BUFFER_ASSET_INDEX, - ); + if matches!(mesh.storage, MeshStorageState::ToBeOnGpu(_)) { + meshes_to_locally_unload.insert(changed_mesh_handle.clone_weak()); } - let interleaved_buffer = mesh.get_vertex_buffer_data(); - - render_resource_context.set_asset_resource( - changed_mesh_handle, - RenderResourceId::Buffer(render_resource_context.create_buffer_with_data( - BufferInfo { - buffer_usage: BufferUsage::VERTEX, - ..Default::default() - }, - &interleaved_buffer, - )), - VERTEX_ATTRIBUTE_BUFFER_ID, - ); - - if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) { - for entity in mesh_entities.entities.iter() { - if let Ok(render_pipelines) = queries.q0_mut().get_mut(*entity) { - update_entity_mesh( - render_resource_context, - mesh, + match &mesh.storage { + MeshStorageState::ToBeOnGpu(mesh_data) + | MeshStorageState::OnGpuAndLocal(mesh_data) => { + // TODO: check for individual buffer changes in non-interleaved mode + if let Some(data) = mesh_data.get_indices_buffer_bytes() { + let index_buffer = render_resource_context.create_buffer_with_data( + BufferInfo { + buffer_usage: BufferUsage::INDEX, + ..Default::default() + }, + &data, + ); + + render_resource_context.set_asset_resource( changed_mesh_handle, - render_pipelines, + RenderResourceId::Buffer(index_buffer), + INDEX_BUFFER_ASSET_INDEX, ); } + + render_resource_context.set_asset_resource( + changed_mesh_handle, + RenderResourceId::Buffer(render_resource_context.create_buffer_with_data( + BufferInfo { + buffer_usage: BufferUsage::VERTEX, + ..Default::default() + }, + &mesh_data.get_vertex_buffer_data(), + )), + VERTEX_ATTRIBUTE_BUFFER_ID, + ); + + if let Some(mesh_entities) = state.mesh_entities.get_mut(changed_mesh_handle) { + for entity in mesh_entities.entities.iter() { + if let Ok(render_pipelines) = queries.q0_mut().get_mut(*entity) { + update_entity_pipelines( + render_resource_context, + mesh, + changed_mesh_handle, + render_pipelines, + ); + } + } + } + } + _ => { + error!("Attempted to upload a mesh without data. This is likely an internal bug. Please report an issue to the Bevy Team."); } } } @@ -488,12 +591,20 @@ pub fn mesh_resource_provider_system( .or_insert_with(MeshEntities::default); mesh_entities.entities.insert(entity); if let Some(mesh) = meshes.get(handle) { - update_entity_mesh(render_resource_context, mesh, handle, render_pipelines); + update_entity_pipelines(render_resource_context, mesh, handle, render_pipelines); + } + } + + // unload local copies of meshes + for mesh in meshes_to_locally_unload.iter() { + if let Some(mesh) = meshes.get_mut(mesh) { + let meta_info = mesh.meta().clone(); + mesh.storage = MeshStorageState::OnGpu(meta_info); } } } -fn update_entity_mesh( +fn update_entity_pipelines( render_resource_context: &dyn RenderResourceContext, mesh: &Mesh, handle: &Handle, @@ -501,19 +612,18 @@ fn update_entity_mesh( ) { for render_pipeline in render_pipelines.pipelines.iter_mut() { render_pipeline.specialization.primitive_topology = mesh.primitive_topology; - // TODO: don't allocate a new vertex buffer descriptor for every entity - render_pipeline.specialization.vertex_buffer_layout = mesh.get_vertex_buffer_layout(); + render_pipeline.specialization.vertex_buffer_layout = + mesh.meta().get_vertex_buffer_layout(); if let PrimitiveTopology::LineStrip | PrimitiveTopology::TriangleStrip = mesh.primitive_topology { - render_pipeline.specialization.strip_index_format = - mesh.indices().map(|indices| indices.into()); + render_pipeline.specialization.strip_index_format = mesh.meta().get_index_format(); } } if let Some(RenderResourceId::Buffer(index_buffer_resource)) = render_resource_context.get_asset_resource(handle, INDEX_BUFFER_ASSET_INDEX) { - let index_format: IndexFormat = mesh.indices().unwrap().into(); + let index_format: IndexFormat = mesh.meta().get_index_format().unwrap(); // set index buffer into binding render_pipelines .bindings diff --git a/crates/bevy_render/src/mesh/shape/capsule.rs b/crates/bevy_render/src/mesh/shape/capsule.rs index c4693adddc905..209da0531e80e 100644 --- a/crates/bevy_render/src/mesh/shape/capsule.rs +++ b/crates/bevy_render/src/mesh/shape/capsule.rs @@ -1,5 +1,5 @@ use crate::{ - mesh::{Indices, Mesh}, + mesh::{Indices, Mesh, MeshData}, pipeline::PrimitiveTopology, }; use bevy_math::{Vec2, Vec3}; @@ -371,11 +371,11 @@ impl From for Mesh { assert_eq!(vs.len(), vert_len); assert_eq!(tris.len(), fs_len); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, vs); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, vns); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, vts); - mesh.set_indices(Some(Indices::U32(tris))); - mesh + let mut data = MeshData::default(); + data.set_attribute(MeshData::ATTRIBUTE_POSITION, vs); + data.set_attribute(MeshData::ATTRIBUTE_NORMAL, vns); + data.set_attribute(MeshData::ATTRIBUTE_UV_0, vts); + data.set_indices(Some(Indices::U32(tris))); + Mesh::new_dynamic(PrimitiveTopology::TriangleList, data) } } diff --git a/crates/bevy_render/src/mesh/shape/icosphere.rs b/crates/bevy_render/src/mesh/shape/icosphere.rs index f3a4736b226f3..d0a8c177ddd68 100644 --- a/crates/bevy_render/src/mesh/shape/icosphere.rs +++ b/crates/bevy_render/src/mesh/shape/icosphere.rs @@ -1,5 +1,6 @@ use hexasphere::shapes::IcoSphere; +use crate::mesh::MeshData; use crate::{ mesh::{Indices, Mesh}, pipeline::PrimitiveTopology, @@ -96,11 +97,12 @@ impl From for Mesh { let indices = Indices::U32(indices); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, points); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + let mut data = MeshData::default(); + data.set_attribute(MeshData::ATTRIBUTE_POSITION, points); + data.set_attribute(MeshData::ATTRIBUTE_NORMAL, normals); + data.set_attribute(MeshData::ATTRIBUTE_UV_0, uvs); + data.set_indices(Some(indices)); + + Mesh::new_dynamic(PrimitiveTopology::TriangleList, data) } } diff --git a/crates/bevy_render/src/mesh/shape/mod.rs b/crates/bevy_render/src/mesh/shape/mod.rs index 84ff56098e28b..ce3e13fb9ee28 100644 --- a/crates/bevy_render/src/mesh/shape/mod.rs +++ b/crates/bevy_render/src/mesh/shape/mod.rs @@ -110,12 +110,13 @@ impl From for Mesh { 20, 21, 22, 22, 23, 20, // back ]); - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh.set_indices(Some(indices)); - mesh + let mut data = MeshData::default(); + data.set_attribute(MeshData::ATTRIBUTE_POSITION, positions); + data.set_attribute(MeshData::ATTRIBUTE_NORMAL, normals); + data.set_attribute(MeshData::ATTRIBUTE_UV_0, uvs); + data.set_indices(Some(indices)); + + Mesh::new_dynamic(PrimitiveTopology::TriangleList, data) } } @@ -212,12 +213,13 @@ impl From for Mesh { uvs.push(*uv); } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + let mut data = MeshData::default(); + data.set_attribute(MeshData::ATTRIBUTE_POSITION, positions); + data.set_attribute(MeshData::ATTRIBUTE_NORMAL, normals); + data.set_attribute(MeshData::ATTRIBUTE_UV_0, uvs); + data.set_indices(Some(indices)); + + Mesh::new_dynamic(PrimitiveTopology::TriangleList, data) } } @@ -256,12 +258,13 @@ impl From for Mesh { uvs.push(*uv); } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(indices)); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + let mut data = MeshData::default(); + data.set_attribute(MeshData::ATTRIBUTE_POSITION, positions); + data.set_attribute(MeshData::ATTRIBUTE_NORMAL, normals); + data.set_attribute(MeshData::ATTRIBUTE_UV_0, uvs); + data.set_indices(Some(indices)); + + Mesh::new_dynamic(PrimitiveTopology::TriangleList, data) } } @@ -269,6 +272,7 @@ mod capsule; mod icosphere; mod torus; +use crate::mesh::MeshData; pub use capsule::{Capsule, CapsuleUvProfile}; pub use icosphere::Icosphere; pub use torus::Torus; diff --git a/crates/bevy_render/src/mesh/shape/torus.rs b/crates/bevy_render/src/mesh/shape/torus.rs index 56039f3d1884d..84dbe002f11ee 100644 --- a/crates/bevy_render/src/mesh/shape/torus.rs +++ b/crates/bevy_render/src/mesh/shape/torus.rs @@ -1,3 +1,4 @@ +use crate::mesh::MeshData; use crate::{ mesh::{Indices, Mesh}, pipeline::PrimitiveTopology, @@ -84,11 +85,12 @@ impl From for Mesh { } } - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList); - mesh.set_indices(Some(Indices::U32(indices))); - mesh.set_attribute(Mesh::ATTRIBUTE_POSITION, positions); - mesh.set_attribute(Mesh::ATTRIBUTE_NORMAL, normals); - mesh.set_attribute(Mesh::ATTRIBUTE_UV_0, uvs); - mesh + let mut data = MeshData::default(); + data.set_attribute(MeshData::ATTRIBUTE_POSITION, positions); + data.set_attribute(MeshData::ATTRIBUTE_NORMAL, normals); + data.set_attribute(MeshData::ATTRIBUTE_UV_0, uvs); + data.set_indices(Some(Indices::U32(indices))); + + Mesh::new_dynamic(PrimitiveTopology::TriangleList, data) } } diff --git a/crates/bevy_render/src/pipeline/render_pipelines.rs b/crates/bevy_render/src/pipeline/render_pipelines.rs index ff3a041118501..26287bf0e590b 100644 --- a/crates/bevy_render/src/pipeline/render_pipelines.rs +++ b/crates/bevy_render/src/pipeline/render_pipelines.rs @@ -1,7 +1,7 @@ use super::{PipelineDescriptor, PipelineSpecialization}; use crate::{ draw::{Draw, DrawContext, OutsideFrustum}, - mesh::{Indices, Mesh}, + mesh::Mesh, prelude::{Msaa, Visible}, renderer::RenderResourceBindings, }; @@ -104,12 +104,6 @@ pub fn draw_render_pipelines_system( continue; }; - let index_range = match mesh.indices() { - Some(Indices::U32(indices)) => Some(0..indices.len() as u32), - Some(Indices::U16(indices)) => Some(0..indices.len() as u32), - None => None, - }; - let render_pipelines = &mut *render_pipelines; for pipeline in render_pipelines.pipelines.iter_mut() { pipeline.specialization.sample_count = msaa.samples; @@ -158,10 +152,10 @@ pub fn draw_render_pipelines_system( .set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings]) .unwrap(); - if let Some(indices) = index_range.clone() { - draw.draw_indexed(indices, 0, 0..1); + if let Some(indices_count) = mesh.meta().get_index_count() { + draw.draw_indexed(0..indices_count, 0, 0..1); } else { - draw.draw(0..mesh.count_vertices() as u32, 0..1) + draw.draw(0..mesh.meta().get_vertices_count() as u32, 0..1) } } } diff --git a/crates/bevy_render/src/wireframe/mod.rs b/crates/bevy_render/src/wireframe/mod.rs index b2ad852a4749c..8c747c0a0f3e2 100644 --- a/crates/bevy_render/src/wireframe/mod.rs +++ b/crates/bevy_render/src/wireframe/mod.rs @@ -1,6 +1,5 @@ use crate::{ draw::DrawContext, - mesh::Indices, pipeline::{PipelineDescriptor, PipelineSpecialization, RenderPipeline}, prelude::*, shader::Shader, @@ -94,7 +93,7 @@ pub fn draw_wireframes_system( .iter_dynamic_bindings() .map(|name| name.to_string()) .collect::>(), - vertex_buffer_layout: mesh.get_vertex_buffer_layout(), + vertex_buffer_layout: mesh.meta().get_vertex_buffer_layout(), }, ); render_pipeline.dynamic_bindings_generation = @@ -114,10 +113,9 @@ pub fn draw_wireframes_system( .set_vertex_buffers_from_bindings(&mut draw, &[&render_pipelines.bindings]) .unwrap(); - match mesh.indices() { - Some(Indices::U32(indices)) => draw.draw_indexed(0..indices.len() as u32, 0, 0..1), - Some(Indices::U16(indices)) => draw.draw_indexed(0..indices.len() as u32, 0, 0..1), - None => draw.draw(0..mesh.count_vertices() as u32, 0..1), + match mesh.meta().get_index_count() { + Some(index_count) => draw.draw_indexed(0..index_count, 0, 0..1), + None => draw.draw(0..mesh.meta().get_vertices_count() as u32, 0..1), }; }; diff --git a/crates/bevy_text/src/text2d.rs b/crates/bevy_text/src/text2d.rs index 112ce1cac0c11..6f327bd560196 100644 --- a/crates/bevy_text/src/text2d.rs +++ b/crates/bevy_text/src/text2d.rs @@ -76,7 +76,7 @@ pub fn draw_text2d_system( >, ) { let font_quad = meshes.get(&QUAD_HANDLE).unwrap(); - let font_quad_vertex_layout = font_quad.get_vertex_buffer_layout(); + let font_quad_vertex_layout = font_quad.meta().get_vertex_buffer_layout(); let scale_factor = if let Some(window) = windows.get_primary() { window.scale_factor() as f32 diff --git a/crates/bevy_ui/src/widget/text.rs b/crates/bevy_ui/src/widget/text.rs index a8dce7ea1a191..fbf958b6de44b 100644 --- a/crates/bevy_ui/src/widget/text.rs +++ b/crates/bevy_ui/src/widget/text.rs @@ -159,7 +159,7 @@ pub fn draw_text_system( }; let font_quad = meshes.get(&QUAD_HANDLE).unwrap(); - let vertex_buffer_layout = font_quad.get_vertex_buffer_layout(); + let vertex_buffer_layout = font_quad.meta().get_vertex_buffer_layout(); for (entity, mut draw, visible, text, node, global_transform) in query.iter_mut() { if !visible.is_visible { diff --git a/examples/shader/mesh_custom_attribute.rs b/examples/shader/mesh_custom_attribute.rs index 5fc22dc6e1732..ffb4c84643fd6 100644 --- a/examples/shader/mesh_custom_attribute.rs +++ b/examples/shader/mesh_custom_attribute.rs @@ -88,45 +88,48 @@ fn setup( let mut cube_with_vertex_colors = Mesh::from(shape::Cube { size: 2.0 }); // insert our custom color attribute with some nice colors! - cube_with_vertex_colors.set_attribute( - // name of the attribute - "Vertex_Color", - // the vertex attributes, represented by `VertexAttributeValues` - // NOTE: the attribute count has to be consistent across all attributes, otherwise bevy - // will panic. - VertexAttributeValues::from(vec![ - // top - [0.79, 0.73, 0.07], - [0.74, 0.14, 0.29], - [0.08, 0.55, 0.74], - [0.20, 0.27, 0.29], - // bottom - [0.79, 0.73, 0.07], - [0.74, 0.14, 0.29], - [0.08, 0.55, 0.74], - [0.20, 0.27, 0.29], - // right - [0.79, 0.73, 0.07], - [0.74, 0.14, 0.29], - [0.08, 0.55, 0.74], - [0.20, 0.27, 0.29], - // left - [0.79, 0.73, 0.07], - [0.74, 0.14, 0.29], - [0.08, 0.55, 0.74], - [0.20, 0.27, 0.29], - // front - [0.79, 0.73, 0.07], - [0.74, 0.14, 0.29], - [0.08, 0.55, 0.74], - [0.20, 0.27, 0.29], - // back - [0.79, 0.73, 0.07], - [0.74, 0.14, 0.29], - [0.08, 0.55, 0.74], - [0.20, 0.27, 0.29], - ]), - ); + cube_with_vertex_colors + .get_mesh_data_mut() + .unwrap() + .set_attribute( + // name of the attribute + "Vertex_Color", + // the vertex attributes, represented by `VertexAttributeValues` + // NOTE: the attribute count has to be consistent across all attributes, otherwise bevy + // will panic. + VertexAttributeValues::from(vec![ + // top + [0.79, 0.73, 0.07], + [0.74, 0.14, 0.29], + [0.08, 0.55, 0.74], + [0.20, 0.27, 0.29], + // bottom + [0.79, 0.73, 0.07], + [0.74, 0.14, 0.29], + [0.08, 0.55, 0.74], + [0.20, 0.27, 0.29], + // right + [0.79, 0.73, 0.07], + [0.74, 0.14, 0.29], + [0.08, 0.55, 0.74], + [0.20, 0.27, 0.29], + // left + [0.79, 0.73, 0.07], + [0.74, 0.14, 0.29], + [0.08, 0.55, 0.74], + [0.20, 0.27, 0.29], + // front + [0.79, 0.73, 0.07], + [0.74, 0.14, 0.29], + [0.08, 0.55, 0.74], + [0.20, 0.27, 0.29], + // back + [0.79, 0.73, 0.07], + [0.74, 0.14, 0.29], + [0.08, 0.55, 0.74], + [0.20, 0.27, 0.29], + ]), + ); // cube commands .spawn_bundle(MeshBundle {