Skip to main content

bevy_mesh/
lib.rs

1#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
2
3extern crate alloc;
4extern crate core;
5
6mod components;
7mod conversions;
8mod index;
9mod mesh;
10#[cfg(feature = "bevy_mikktspace")]
11mod mikktspace;
12#[cfg(feature = "morph")]
13pub mod morph;
14pub mod primitives;
15pub mod skinning;
16mod vertex;
17use bevy_app::{App, Plugin, PostUpdate};
18use bevy_asset::{AssetApp, AssetEventSystems};
19use bevy_ecs::schedule::IntoScheduleConfigs;
20use bitflags::bitflags;
21pub use components::*;
22pub use index::*;
23pub use mesh::*;
24#[cfg(feature = "bevy_mikktspace")]
25pub use mikktspace::*;
26pub use primitives::*;
27pub use vertex::*;
28use wgpu_types::IndexFormat;
29pub use wgpu_types::VertexFormat;
30
31/// The mesh prelude.
32///
33/// This includes the most common types in this crate, re-exported for your convenience.
34pub mod prelude {
35    #[cfg(feature = "morph")]
36    pub use crate::morph::MorphWeights;
37    #[doc(hidden)]
38    pub use crate::{primitives::MeshBuilder, primitives::Meshable, Mesh, Mesh2d, Mesh3d};
39}
40
41bitflags! {
42    /// Our base mesh pipeline key bits start from the highest bit and go
43    /// downward. The PBR mesh pipeline key bits start from the lowest bit and
44    /// go upward. This allows the PBR bits in the downstream crate `bevy_pbr`
45    /// to coexist in the same field without any shifts.
46    #[derive(Clone, Debug)]
47    pub struct BaseMeshPipelineKey: u64 {
48        const MORPH_TARGETS = 1 << Self::MORPH_TARGETS_SHIFT_BITS;
49
50        const PRIMITIVE_TOPOLOGY_RESERVED_BITS  = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
51
52        const STRIP_INDEX_FORMAT_RESERVED_BITS = Self::STRIP_INDEX_FORMAT_MASK_BITS << Self::STRIP_INDEX_FORMAT_SHIFT_BITS;
53        const STRIP_INDEX_FORMAT_NONE = 0 << Self::STRIP_INDEX_FORMAT_SHIFT_BITS;
54        const STRIP_INDEX_FORMAT_U32  = 1 << Self::STRIP_INDEX_FORMAT_SHIFT_BITS;
55        const STRIP_INDEX_FORMAT_U16  = 2 << Self::STRIP_INDEX_FORMAT_SHIFT_BITS;
56    }
57}
58
59/// Adds [`Mesh`] as an asset.
60#[derive(Default)]
61pub struct MeshPlugin;
62
63impl Plugin for MeshPlugin {
64    fn build(&self, app: &mut App) {
65        app.init_asset::<Mesh>()
66            .init_asset::<skinning::SkinnedMeshInverseBindposes>()
67            .register_asset_reflect::<Mesh>()
68            .add_systems(
69                PostUpdate,
70                (
71                    mark_2d_meshes_as_changed_if_their_assets_changed,
72                    mark_3d_meshes_as_changed_if_their_assets_changed,
73                )
74                    .after(AssetEventSystems),
75            );
76    }
77}
78
79impl BaseMeshPipelineKey {
80    pub const MORPH_TARGETS_SHIFT_BITS: u64 = (u64::BITS - 1) as u64;
81
82    pub const PRIMITIVE_TOPOLOGY_MASK_BITS: u64 = 0b111;
83    pub const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u64 =
84        Self::MORPH_TARGETS_SHIFT_BITS - Self::PRIMITIVE_TOPOLOGY_MASK_BITS.count_ones() as u64;
85
86    pub const STRIP_INDEX_FORMAT_MASK_BITS: u64 = 0b11;
87    pub const STRIP_INDEX_FORMAT_SHIFT_BITS: u64 = Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS
88        - Self::STRIP_INDEX_FORMAT_MASK_BITS.count_ones() as u64;
89
90    /// Create a [`BaseMeshPipelineKey`] from mesh primitive topology and index format.
91    ///
92    /// For non-strip topologies, [`Self::STRIP_INDEX_FORMAT_NONE`] is set regardless of the `strip_index_format` argument.
93    pub fn from_primitive_topology_and_strip_index(
94        primitive_topology: PrimitiveTopology,
95        strip_index_format: Option<IndexFormat>,
96    ) -> Self {
97        let index_bits = if primitive_topology.is_strip() {
98            match strip_index_format {
99                None => BaseMeshPipelineKey::STRIP_INDEX_FORMAT_NONE,
100                Some(index_format) => match index_format {
101                    IndexFormat::Uint16 => BaseMeshPipelineKey::STRIP_INDEX_FORMAT_U16,
102                    IndexFormat::Uint32 => BaseMeshPipelineKey::STRIP_INDEX_FORMAT_U32,
103                },
104            }
105        } else {
106            BaseMeshPipelineKey::STRIP_INDEX_FORMAT_NONE
107        }
108        .bits();
109        let primitive_topology_bits = ((primitive_topology as u64)
110            & Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
111            << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
112        Self::from_bits_retain(primitive_topology_bits | index_bits)
113    }
114
115    pub fn primitive_topology(&self) -> PrimitiveTopology {
116        let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
117            & Self::PRIMITIVE_TOPOLOGY_MASK_BITS;
118        match primitive_topology_bits {
119            x if x == PrimitiveTopology::PointList as u64 => PrimitiveTopology::PointList,
120            x if x == PrimitiveTopology::LineList as u64 => PrimitiveTopology::LineList,
121            x if x == PrimitiveTopology::LineStrip as u64 => PrimitiveTopology::LineStrip,
122            x if x == PrimitiveTopology::TriangleList as u64 => PrimitiveTopology::TriangleList,
123            x if x == PrimitiveTopology::TriangleStrip as u64 => PrimitiveTopology::TriangleStrip,
124            _ => PrimitiveTopology::default(),
125        }
126    }
127
128    /// Returns the [`IndexFormat`] that this mesh pipeline key specifies.
129    pub fn strip_index_format(&self) -> Option<IndexFormat> {
130        let index_bits = self.bits() & Self::STRIP_INDEX_FORMAT_RESERVED_BITS.bits();
131        match index_bits {
132            x if x == Self::STRIP_INDEX_FORMAT_U16.bits() => Some(IndexFormat::Uint16),
133            x if x == Self::STRIP_INDEX_FORMAT_U32.bits() => Some(IndexFormat::Uint32),
134            x if x == Self::STRIP_INDEX_FORMAT_NONE.bits() => None,
135            _ => unreachable!(),
136        }
137    }
138}