Faster MeshletMesh deserialization (#14193)
# Objective - Using bincode to deserialize binary into a MeshletMesh is expensive (~77ms for a 5mb file). ## Solution - Write a custom deserializer using bytemuck's Pod types and slice casting. - Total asset load time has gone from ~102ms to ~12ms. - Change some types I never meant to be public to private and other misc cleanup. ## Testing - Ran the meshlet example and added timing spans to the asset loader. --- ## Changelog - Improved `MeshletMesh` loading speed - The `MeshletMesh` disk format has changed, and `MESHLET_MESH_ASSET_VERSION` has been bumped - `MeshletMesh` fields are now private - Renamed `MeshletMeshSaverLoad` to `MeshletMeshSaverLoader` - The `Meshlet`, `MeshletBoundingSpheres`, and `MeshletBoundingSphere` types are now private - Removed `MeshletMeshSaveOrLoadError::SerializationOrDeserialization` - Added `MeshletMeshSaveOrLoadError::WrongFileType` ## Migration Guide - Regenerate your `MeshletMesh` assets, as the disk format has changed, and `MESHLET_MESH_ASSET_VERSION` has been bumped - `MeshletMesh` fields are now private - `MeshletMeshSaverLoad` is now named `MeshletMeshSaverLoader` - The `Meshlet`, `MeshletBoundingSpheres`, and `MeshletBoundingSphere` types are now private - `MeshletMeshSaveOrLoadError::SerializationOrDeserialization` has been removed - Added `MeshletMeshSaveOrLoadError::WrongFileType`, match on this variant if you match on `MeshletMeshSaveOrLoadError`
This commit is contained in:
parent
5f3a529920
commit
6e8d43a037
@ -18,13 +18,7 @@ shader_format_glsl = ["bevy_render/shader_format_glsl"]
|
|||||||
trace = ["bevy_render/trace"]
|
trace = ["bevy_render/trace"]
|
||||||
ios_simulator = ["bevy_render/ios_simulator"]
|
ios_simulator = ["bevy_render/ios_simulator"]
|
||||||
# Enables the meshlet renderer for dense high-poly scenes (experimental)
|
# Enables the meshlet renderer for dense high-poly scenes (experimental)
|
||||||
meshlet = [
|
meshlet = ["dep:lz4_flex", "dep:thiserror", "dep:range-alloc", "dep:bevy_tasks"]
|
||||||
"dep:lz4_flex",
|
|
||||||
"dep:serde",
|
|
||||||
"dep:bincode",
|
|
||||||
"dep:thiserror",
|
|
||||||
"dep:range-alloc",
|
|
||||||
]
|
|
||||||
# Enables processing meshes into meshlet meshes
|
# Enables processing meshes into meshlet meshes
|
||||||
meshlet_processor = ["meshlet", "dep:meshopt", "dep:metis", "dep:itertools"]
|
meshlet_processor = ["meshlet", "dep:meshopt", "dep:metis", "dep:itertools"]
|
||||||
|
|
||||||
@ -34,16 +28,17 @@ bevy_app = { path = "../bevy_app", version = "0.15.0-dev" }
|
|||||||
bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" }
|
bevy_asset = { path = "../bevy_asset", version = "0.15.0-dev" }
|
||||||
bevy_color = { path = "../bevy_color", version = "0.15.0-dev" }
|
bevy_color = { path = "../bevy_color", version = "0.15.0-dev" }
|
||||||
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" }
|
bevy_core_pipeline = { path = "../bevy_core_pipeline", version = "0.15.0-dev" }
|
||||||
|
bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
|
||||||
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
|
bevy_ecs = { path = "../bevy_ecs", version = "0.15.0-dev" }
|
||||||
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
|
bevy_math = { path = "../bevy_math", version = "0.15.0-dev" }
|
||||||
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
|
bevy_reflect = { path = "../bevy_reflect", version = "0.15.0-dev", features = [
|
||||||
"bevy",
|
"bevy",
|
||||||
] }
|
] }
|
||||||
bevy_render = { path = "../bevy_render", version = "0.15.0-dev" }
|
bevy_render = { path = "../bevy_render", version = "0.15.0-dev" }
|
||||||
|
bevy_tasks = { path = "../bevy_tasks", version = "0.15.0-dev", optional = true }
|
||||||
bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" }
|
bevy_transform = { path = "../bevy_transform", version = "0.15.0-dev" }
|
||||||
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
|
bevy_utils = { path = "../bevy_utils", version = "0.15.0-dev" }
|
||||||
bevy_window = { path = "../bevy_window", version = "0.15.0-dev" }
|
bevy_window = { path = "../bevy_window", version = "0.15.0-dev" }
|
||||||
bevy_derive = { path = "../bevy_derive", version = "0.15.0-dev" }
|
|
||||||
|
|
||||||
|
|
||||||
# other
|
# other
|
||||||
@ -53,8 +48,6 @@ fixedbitset = "0.5"
|
|||||||
lz4_flex = { version = "0.11", default-features = false, features = [
|
lz4_flex = { version = "0.11", default-features = false, features = [
|
||||||
"frame",
|
"frame",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
serde = { version = "1", features = ["derive", "rc"], optional = true }
|
|
||||||
bincode = { version = "1", optional = true }
|
|
||||||
thiserror = { version = "1", optional = true }
|
thiserror = { version = "1", optional = true }
|
||||||
range-alloc = { version = "0.1", optional = true }
|
range-alloc = { version = "0.1", optional = true }
|
||||||
meshopt = { version = "0.3.0", optional = true }
|
meshopt = { version = "0.3.0", optional = true }
|
||||||
|
@ -5,13 +5,19 @@ use bevy_asset::{
|
|||||||
};
|
};
|
||||||
use bevy_math::Vec3;
|
use bevy_math::Vec3;
|
||||||
use bevy_reflect::TypePath;
|
use bevy_reflect::TypePath;
|
||||||
|
use bevy_tasks::block_on;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use lz4_flex::frame::{FrameDecoder, FrameEncoder};
|
use lz4_flex::frame::{FrameDecoder, FrameEncoder};
|
||||||
use serde::{Deserialize, Serialize};
|
use std::{
|
||||||
use std::{io::Cursor, sync::Arc};
|
io::{Read, Write},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Unique identifier for the [`MeshletMesh`] asset format.
|
||||||
|
const MESHLET_MESH_ASSET_MAGIC: u64 = 1717551717668;
|
||||||
|
|
||||||
/// The current version of the [`MeshletMesh`] asset format.
|
/// The current version of the [`MeshletMesh`] asset format.
|
||||||
pub const MESHLET_MESH_ASSET_VERSION: u64 = 0;
|
pub const MESHLET_MESH_ASSET_VERSION: u64 = 1;
|
||||||
|
|
||||||
/// A mesh that has been pre-processed into multiple small clusters of triangles called meshlets.
|
/// A mesh that has been pre-processed into multiple small clusters of triangles called meshlets.
|
||||||
///
|
///
|
||||||
@ -27,24 +33,24 @@ pub const MESHLET_MESH_ASSET_VERSION: u64 = 0;
|
|||||||
/// * Limited control over [`bevy_render::render_resource::RenderPipelineDescriptor`] attributes.
|
/// * Limited control over [`bevy_render::render_resource::RenderPipelineDescriptor`] attributes.
|
||||||
///
|
///
|
||||||
/// See also [`super::MaterialMeshletMeshBundle`] and [`super::MeshletPlugin`].
|
/// See also [`super::MaterialMeshletMeshBundle`] and [`super::MeshletPlugin`].
|
||||||
#[derive(Asset, TypePath, Serialize, Deserialize, Clone)]
|
#[derive(Asset, TypePath, Clone)]
|
||||||
pub struct MeshletMesh {
|
pub struct MeshletMesh {
|
||||||
/// The total amount of triangles summed across all LOD 0 meshlets in the mesh.
|
/// The total amount of triangles summed across all LOD 0 meshlets in the mesh.
|
||||||
pub worst_case_meshlet_triangles: u64,
|
pub(crate) worst_case_meshlet_triangles: u64,
|
||||||
/// Raw vertex data bytes for the overall mesh.
|
/// Raw vertex data bytes for the overall mesh.
|
||||||
pub vertex_data: Arc<[u8]>,
|
pub(crate) vertex_data: Arc<[u8]>,
|
||||||
/// Indices into `vertex_data`.
|
/// Indices into `vertex_data`.
|
||||||
pub vertex_ids: Arc<[u32]>,
|
pub(crate) vertex_ids: Arc<[u32]>,
|
||||||
/// Indices into `vertex_ids`.
|
/// Indices into `vertex_ids`.
|
||||||
pub indices: Arc<[u8]>,
|
pub(crate) indices: Arc<[u8]>,
|
||||||
/// The list of meshlets making up this mesh.
|
/// The list of meshlets making up this mesh.
|
||||||
pub meshlets: Arc<[Meshlet]>,
|
pub(crate) meshlets: Arc<[Meshlet]>,
|
||||||
/// Spherical bounding volumes.
|
/// Spherical bounding volumes.
|
||||||
pub bounding_spheres: Arc<[MeshletBoundingSpheres]>,
|
pub(crate) bounding_spheres: Arc<[MeshletBoundingSpheres]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single meshlet within a [`MeshletMesh`].
|
/// A single meshlet within a [`MeshletMesh`].
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Pod, Zeroable)]
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Meshlet {
|
pub struct Meshlet {
|
||||||
/// The offset within the parent mesh's [`MeshletMesh::vertex_ids`] buffer where the indices for this meshlet begin.
|
/// The offset within the parent mesh's [`MeshletMesh::vertex_ids`] buffer where the indices for this meshlet begin.
|
||||||
@ -56,7 +62,7 @@ pub struct Meshlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Bounding spheres used for culling and choosing level of detail for a [`Meshlet`].
|
/// Bounding spheres used for culling and choosing level of detail for a [`Meshlet`].
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Pod, Zeroable)]
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct MeshletBoundingSpheres {
|
pub struct MeshletBoundingSpheres {
|
||||||
/// The bounding sphere used for frustum and occlusion culling for this meshlet.
|
/// The bounding sphere used for frustum and occlusion culling for this meshlet.
|
||||||
@ -68,7 +74,7 @@ pub struct MeshletBoundingSpheres {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A spherical bounding volume used for a [`Meshlet`].
|
/// A spherical bounding volume used for a [`Meshlet`].
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Pod, Zeroable)]
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct MeshletBoundingSphere {
|
pub struct MeshletBoundingSphere {
|
||||||
pub center: Vec3,
|
pub center: Vec3,
|
||||||
@ -76,37 +82,9 @@ pub struct MeshletBoundingSphere {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An [`AssetLoader`] and [`AssetSaver`] for `.meshlet_mesh` [`MeshletMesh`] assets.
|
/// An [`AssetLoader`] and [`AssetSaver`] for `.meshlet_mesh` [`MeshletMesh`] assets.
|
||||||
pub struct MeshletMeshSaverLoad;
|
pub struct MeshletMeshSaverLoader;
|
||||||
|
|
||||||
impl AssetLoader for MeshletMeshSaverLoad {
|
impl AssetSaver for MeshletMeshSaverLoader {
|
||||||
type Asset = MeshletMesh;
|
|
||||||
type Settings = ();
|
|
||||||
type Error = MeshletMeshSaveOrLoadError;
|
|
||||||
|
|
||||||
async fn load<'a>(
|
|
||||||
&'a self,
|
|
||||||
reader: &'a mut dyn Reader,
|
|
||||||
_settings: &'a Self::Settings,
|
|
||||||
_load_context: &'a mut LoadContext<'_>,
|
|
||||||
) -> Result<Self::Asset, Self::Error> {
|
|
||||||
let version = read_u64(reader).await?;
|
|
||||||
if version != MESHLET_MESH_ASSET_VERSION {
|
|
||||||
return Err(MeshletMeshSaveOrLoadError::WrongVersion { found: version });
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut bytes = Vec::new();
|
|
||||||
reader.read_to_end(&mut bytes).await?;
|
|
||||||
let asset = bincode::deserialize_from(FrameDecoder::new(Cursor::new(bytes)))?;
|
|
||||||
|
|
||||||
Ok(asset)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extensions(&self) -> &[&str] {
|
|
||||||
&["meshlet_mesh"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AssetSaver for MeshletMeshSaverLoad {
|
|
||||||
type Asset = MeshletMesh;
|
type Asset = MeshletMesh;
|
||||||
type Settings = ();
|
type Settings = ();
|
||||||
type OutputLoader = Self;
|
type OutputLoader = Self;
|
||||||
@ -115,37 +93,143 @@ impl AssetSaver for MeshletMeshSaverLoad {
|
|||||||
async fn save<'a>(
|
async fn save<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
writer: &'a mut Writer,
|
writer: &'a mut Writer,
|
||||||
asset: SavedAsset<'a, Self::Asset>,
|
asset: SavedAsset<'a, MeshletMesh>,
|
||||||
_settings: &'a Self::Settings,
|
_settings: &'a (),
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), MeshletMeshSaveOrLoadError> {
|
||||||
|
// Write asset magic number
|
||||||
|
writer
|
||||||
|
.write_all(&MESHLET_MESH_ASSET_MAGIC.to_le_bytes())
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Write asset version
|
||||||
writer
|
writer
|
||||||
.write_all(&MESHLET_MESH_ASSET_VERSION.to_le_bytes())
|
.write_all(&MESHLET_MESH_ASSET_VERSION.to_le_bytes())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut bytes = Vec::new();
|
// Compress and write asset data
|
||||||
let mut sync_writer = FrameEncoder::new(&mut bytes);
|
writer
|
||||||
bincode::serialize_into(&mut sync_writer, asset.get())?;
|
.write_all(&asset.worst_case_meshlet_triangles.to_le_bytes())
|
||||||
sync_writer.finish()?;
|
.await?;
|
||||||
writer.write_all(&bytes).await?;
|
let mut writer = FrameEncoder::new(AsyncWriteSyncAdapter(writer));
|
||||||
|
write_slice(&asset.vertex_data, &mut writer)?;
|
||||||
|
write_slice(&asset.vertex_ids, &mut writer)?;
|
||||||
|
write_slice(&asset.indices, &mut writer)?;
|
||||||
|
write_slice(&asset.meshlets, &mut writer)?;
|
||||||
|
write_slice(&asset.bounding_spheres, &mut writer)?;
|
||||||
|
writer.finish()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AssetLoader for MeshletMeshSaverLoader {
|
||||||
|
type Asset = MeshletMesh;
|
||||||
|
type Settings = ();
|
||||||
|
type Error = MeshletMeshSaveOrLoadError;
|
||||||
|
|
||||||
|
async fn load<'a>(
|
||||||
|
&'a self,
|
||||||
|
reader: &'a mut dyn Reader,
|
||||||
|
_settings: &'a (),
|
||||||
|
_load_context: &'a mut LoadContext<'_>,
|
||||||
|
) -> Result<MeshletMesh, MeshletMeshSaveOrLoadError> {
|
||||||
|
// Load and check magic number
|
||||||
|
let magic = async_read_u64(reader).await?;
|
||||||
|
if magic != MESHLET_MESH_ASSET_MAGIC {
|
||||||
|
return Err(MeshletMeshSaveOrLoadError::WrongFileType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and check asset version
|
||||||
|
let version = async_read_u64(reader).await?;
|
||||||
|
if version != MESHLET_MESH_ASSET_VERSION {
|
||||||
|
return Err(MeshletMeshSaveOrLoadError::WrongVersion { found: version });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load and decompress asset data
|
||||||
|
let worst_case_meshlet_triangles = async_read_u64(reader).await?;
|
||||||
|
let reader = &mut FrameDecoder::new(AsyncReadSyncAdapter(reader));
|
||||||
|
let vertex_data = read_slice(reader)?;
|
||||||
|
let vertex_ids = read_slice(reader)?;
|
||||||
|
let indices = read_slice(reader)?;
|
||||||
|
let meshlets = read_slice(reader)?;
|
||||||
|
let bounding_spheres = read_slice(reader)?;
|
||||||
|
|
||||||
|
Ok(MeshletMesh {
|
||||||
|
worst_case_meshlet_triangles,
|
||||||
|
vertex_data,
|
||||||
|
vertex_ids,
|
||||||
|
indices,
|
||||||
|
meshlets,
|
||||||
|
bounding_spheres,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extensions(&self) -> &[&str] {
|
||||||
|
&["meshlet_mesh"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
pub enum MeshletMeshSaveOrLoadError {
|
pub enum MeshletMeshSaveOrLoadError {
|
||||||
|
#[error("file was not a MeshletMesh asset")]
|
||||||
|
WrongFileType,
|
||||||
#[error("expected asset version {MESHLET_MESH_ASSET_VERSION} but found version {found}")]
|
#[error("expected asset version {MESHLET_MESH_ASSET_VERSION} but found version {found}")]
|
||||||
WrongVersion { found: u64 },
|
WrongVersion { found: u64 },
|
||||||
#[error("failed to serialize or deserialize asset data")]
|
|
||||||
SerializationOrDeserialization(#[from] bincode::Error),
|
|
||||||
#[error("failed to compress or decompress asset data")]
|
#[error("failed to compress or decompress asset data")]
|
||||||
CompressionOrDecompression(#[from] lz4_flex::frame::Error),
|
CompressionOrDecompression(#[from] lz4_flex::frame::Error),
|
||||||
#[error("failed to read or write asset data")]
|
#[error("failed to read or write asset data")]
|
||||||
Io(#[from] std::io::Error),
|
Io(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_u64(reader: &mut dyn Reader) -> Result<u64, bincode::Error> {
|
async fn async_read_u64(reader: &mut dyn Reader) -> Result<u64, std::io::Error> {
|
||||||
let mut bytes = [0u8; 8];
|
let mut bytes = [0u8; 8];
|
||||||
reader.read_exact(&mut bytes).await?;
|
reader.read_exact(&mut bytes).await?;
|
||||||
Ok(u64::from_le_bytes(bytes))
|
Ok(u64::from_le_bytes(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_u64(reader: &mut dyn Read) -> Result<u64, std::io::Error> {
|
||||||
|
let mut bytes = [0u8; 8];
|
||||||
|
reader.read_exact(&mut bytes)?;
|
||||||
|
Ok(u64::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_slice<T: Pod>(
|
||||||
|
field: &[T],
|
||||||
|
writer: &mut dyn Write,
|
||||||
|
) -> Result<(), MeshletMeshSaveOrLoadError> {
|
||||||
|
writer.write_all(&(field.len() as u64).to_le_bytes())?;
|
||||||
|
writer.write_all(bytemuck::cast_slice(field))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_slice<T: Pod>(reader: &mut dyn Read) -> Result<Arc<[T]>, std::io::Error> {
|
||||||
|
let len = read_u64(reader)? as usize;
|
||||||
|
|
||||||
|
let mut data: Arc<[T]> = std::iter::repeat_with(T::zeroed).take(len).collect();
|
||||||
|
let slice = Arc::get_mut(&mut data).unwrap();
|
||||||
|
reader.read_exact(bytemuck::cast_slice_mut(slice))?;
|
||||||
|
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use async for everything and get rid of this adapter
|
||||||
|
struct AsyncWriteSyncAdapter<'a>(&'a mut Writer);
|
||||||
|
|
||||||
|
impl Write for AsyncWriteSyncAdapter<'_> {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||||
|
block_on(self.0.write(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> std::io::Result<()> {
|
||||||
|
block_on(self.0.flush())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Use async for everything and get rid of this adapter
|
||||||
|
struct AsyncReadSyncAdapter<'a>(&'a mut dyn Reader);
|
||||||
|
|
||||||
|
impl Read for AsyncReadSyncAdapter<'_> {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||||
|
block_on(self.0.read(buf))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -294,7 +294,6 @@ fn simplify_meshlet_groups(
|
|||||||
let target_error = target_error_relative * mesh_scale;
|
let target_error = target_error_relative * mesh_scale;
|
||||||
|
|
||||||
// Simplify the group to ~50% triangle count
|
// Simplify the group to ~50% triangle count
|
||||||
// TODO: Use simplify_with_locks()
|
|
||||||
let mut error = 0.0;
|
let mut error = 0.0;
|
||||||
let simplified_group_indices = simplify(
|
let simplified_group_indices = simplify(
|
||||||
&group_indices,
|
&group_indices,
|
||||||
|
@ -30,7 +30,7 @@ pub(crate) use self::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::asset::*;
|
pub use self::asset::{MeshletMesh, MeshletMeshSaverLoader};
|
||||||
#[cfg(feature = "meshlet_processor")]
|
#[cfg(feature = "meshlet_processor")]
|
||||||
pub use self::from_mesh::MeshToMeshletMeshConversionError;
|
pub use self::from_mesh::MeshToMeshletMeshConversionError;
|
||||||
|
|
||||||
@ -118,6 +118,9 @@ pub struct MeshletPlugin;
|
|||||||
|
|
||||||
impl Plugin for MeshletPlugin {
|
impl Plugin for MeshletPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
|
#[cfg(target_endian = "big")]
|
||||||
|
compile_error!("MeshletPlugin is only supported on little-endian processors.");
|
||||||
|
|
||||||
load_internal_asset!(
|
load_internal_asset!(
|
||||||
app,
|
app,
|
||||||
MESHLET_BINDINGS_SHADER_HANDLE,
|
MESHLET_BINDINGS_SHADER_HANDLE,
|
||||||
@ -168,7 +171,7 @@ impl Plugin for MeshletPlugin {
|
|||||||
);
|
);
|
||||||
|
|
||||||
app.init_asset::<MeshletMesh>()
|
app.init_asset::<MeshletMesh>()
|
||||||
.register_asset_loader(MeshletMeshSaverLoad)
|
.register_asset_loader(MeshletMeshSaverLoader)
|
||||||
.insert_resource(Msaa::Off)
|
.insert_resource(Msaa::Off)
|
||||||
.add_systems(
|
.add_systems(
|
||||||
PostUpdate,
|
PostUpdate,
|
||||||
|
@ -16,7 +16,8 @@ use bevy::{
|
|||||||
use camera_controller::{CameraController, CameraControllerPlugin};
|
use camera_controller::{CameraController, CameraControllerPlugin};
|
||||||
use std::{f32::consts::PI, path::Path, process::ExitCode};
|
use std::{f32::consts::PI, path::Path, process::ExitCode};
|
||||||
|
|
||||||
const ASSET_URL: &str = "https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/bd869887bc5c9c6e74e353f657d342bef84bacd8/bunny.meshlet_mesh";
|
const ASSET_URL: &str =
|
||||||
|
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/b6c712cfc87c65de419f856845401aba336a7bcd/bunny.meshlet_mesh";
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
if !Path::new("./assets/models/bunny.meshlet_mesh").exists() {
|
if !Path::new("./assets/models/bunny.meshlet_mesh").exists() {
|
||||||
|
Loading…
Reference in New Issue
Block a user