mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
Meshlet new error projection (#15846)
* New error projection code taken from @zeux's meshoptimizer nanite.cpp demo for determining LOD (thanks zeux!) * Builder: `compute_lod_group_data()` * Runtime: `lod_error_is_imperceptible()`
This commit is contained in:
parent
9930df83ed
commit
9d54fe0370
11 changed files with 233 additions and 137 deletions
|
@ -1213,7 +1213,7 @@ setup = [
|
||||||
"curl",
|
"curl",
|
||||||
"-o",
|
"-o",
|
||||||
"assets/models/bunny.meshlet_mesh",
|
"assets/models/bunny.meshlet_mesh",
|
||||||
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/8443bbdee0bf517e6c297dede7f6a46ab712ee4c/bunny.meshlet_mesh",
|
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/167cdaf0b08f89fb747b83b94c27755f116cd408/bunny.meshlet_mesh",
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -18,7 +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 = ["dep:lz4_flex", "dep:range-alloc", "dep:bevy_tasks"]
|
meshlet = ["dep:lz4_flex", "dep:range-alloc", "dep:half", "dep:bevy_tasks"]
|
||||||
# Enables processing meshes into meshlet meshes
|
# Enables processing meshes into meshlet meshes
|
||||||
meshlet_processor = [
|
meshlet_processor = [
|
||||||
"meshlet",
|
"meshlet",
|
||||||
|
@ -50,16 +50,17 @@ bevy_window = { path = "../bevy_window", version = "0.15.0-dev" }
|
||||||
# other
|
# other
|
||||||
bitflags = "2.3"
|
bitflags = "2.3"
|
||||||
fixedbitset = "0.5"
|
fixedbitset = "0.5"
|
||||||
# meshlet
|
|
||||||
lz4_flex = { version = "0.11", default-features = false, features = [
|
|
||||||
"frame",
|
|
||||||
], optional = true }
|
|
||||||
derive_more = { version = "1", default-features = false, features = [
|
derive_more = { version = "1", default-features = false, features = [
|
||||||
"error",
|
"error",
|
||||||
"from",
|
"from",
|
||||||
"display",
|
"display",
|
||||||
] }
|
] }
|
||||||
|
# meshlet
|
||||||
|
lz4_flex = { version = "0.11", default-features = false, features = [
|
||||||
|
"frame",
|
||||||
|
], optional = true }
|
||||||
range-alloc = { version = "0.1.3", optional = true }
|
range-alloc = { version = "0.1.3", optional = true }
|
||||||
|
half = { version = "2", features = ["bytemuck"], optional = true }
|
||||||
meshopt = { version = "0.3.0", optional = true }
|
meshopt = { version = "0.3.0", optional = true }
|
||||||
metis = { version = "0.2", optional = true }
|
metis = { version = "0.2", optional = true }
|
||||||
itertools = { version = "0.13", optional = true }
|
itertools = { version = "0.13", optional = true }
|
||||||
|
|
|
@ -9,6 +9,7 @@ use bevy_reflect::TypePath;
|
||||||
use bevy_tasks::block_on;
|
use bevy_tasks::block_on;
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use derive_more::derive::{Display, Error, From};
|
use derive_more::derive::{Display, Error, From};
|
||||||
|
use half::f16;
|
||||||
use lz4_flex::frame::{FrameDecoder, FrameEncoder};
|
use lz4_flex::frame::{FrameDecoder, FrameEncoder};
|
||||||
use std::io::{Read, Write};
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
@ -51,6 +52,8 @@ pub struct MeshletMesh {
|
||||||
pub(crate) meshlets: Arc<[Meshlet]>,
|
pub(crate) meshlets: Arc<[Meshlet]>,
|
||||||
/// Spherical bounding volumes.
|
/// Spherical bounding volumes.
|
||||||
pub(crate) meshlet_bounding_spheres: Arc<[MeshletBoundingSpheres]>,
|
pub(crate) meshlet_bounding_spheres: Arc<[MeshletBoundingSpheres]>,
|
||||||
|
/// Meshlet group and parent group simplification errors.
|
||||||
|
pub(crate) meshlet_simplification_errors: Arc<[MeshletSimplificationError]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A single meshlet within a [`MeshletMesh`].
|
/// A single meshlet within a [`MeshletMesh`].
|
||||||
|
@ -90,12 +93,12 @@ pub struct Meshlet {
|
||||||
#[derive(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.
|
/// Bounding sphere used for frustum and occlusion culling for this meshlet.
|
||||||
pub self_culling: MeshletBoundingSphere,
|
pub culling_sphere: MeshletBoundingSphere,
|
||||||
/// The bounding sphere used for determining if this meshlet is at the correct level of detail for a given view.
|
/// Bounding sphere used for determining if this meshlet's group is at the correct level of detail for a given view.
|
||||||
pub self_lod: MeshletBoundingSphere,
|
pub lod_group_sphere: MeshletBoundingSphere,
|
||||||
/// The bounding sphere used for determining if this meshlet's parent is at the correct level of detail for a given view.
|
/// Bounding sphere used for determining if this meshlet's parent group is at the correct level of detail for a given view.
|
||||||
pub parent_lod: MeshletBoundingSphere,
|
pub lod_parent_group_sphere: MeshletBoundingSphere,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A spherical bounding volume used for a [`Meshlet`].
|
/// A spherical bounding volume used for a [`Meshlet`].
|
||||||
|
@ -106,6 +109,16 @@ pub struct MeshletBoundingSphere {
|
||||||
pub radius: f32,
|
pub radius: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Simplification error used for choosing level of detail for a [`Meshlet`].
|
||||||
|
#[derive(Copy, Clone, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct MeshletSimplificationError {
|
||||||
|
/// Simplification error used for determining if this meshlet's group is at the correct level of detail for a given view.
|
||||||
|
pub group_error: f16,
|
||||||
|
/// Simplification error used for determining if this meshlet's parent group is at the correct level of detail for a given view.
|
||||||
|
pub parent_group_error: f16,
|
||||||
|
}
|
||||||
|
|
||||||
/// An [`AssetSaver`] for `.meshlet_mesh` [`MeshletMesh`] assets.
|
/// An [`AssetSaver`] for `.meshlet_mesh` [`MeshletMesh`] assets.
|
||||||
pub struct MeshletMeshSaver;
|
pub struct MeshletMeshSaver;
|
||||||
|
|
||||||
|
@ -139,6 +152,7 @@ impl AssetSaver for MeshletMeshSaver {
|
||||||
write_slice(&asset.indices, &mut writer)?;
|
write_slice(&asset.indices, &mut writer)?;
|
||||||
write_slice(&asset.meshlets, &mut writer)?;
|
write_slice(&asset.meshlets, &mut writer)?;
|
||||||
write_slice(&asset.meshlet_bounding_spheres, &mut writer)?;
|
write_slice(&asset.meshlet_bounding_spheres, &mut writer)?;
|
||||||
|
write_slice(&asset.meshlet_simplification_errors, &mut writer)?;
|
||||||
writer.finish()?;
|
writer.finish()?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -179,6 +193,7 @@ impl AssetLoader for MeshletMeshLoader {
|
||||||
let indices = read_slice(reader)?;
|
let indices = read_slice(reader)?;
|
||||||
let meshlets = read_slice(reader)?;
|
let meshlets = read_slice(reader)?;
|
||||||
let meshlet_bounding_spheres = read_slice(reader)?;
|
let meshlet_bounding_spheres = read_slice(reader)?;
|
||||||
|
let meshlet_simplification_errors = read_slice(reader)?;
|
||||||
|
|
||||||
Ok(MeshletMesh {
|
Ok(MeshletMesh {
|
||||||
vertex_positions,
|
vertex_positions,
|
||||||
|
@ -187,6 +202,7 @@ impl AssetLoader for MeshletMeshLoader {
|
||||||
indices,
|
indices,
|
||||||
meshlets,
|
meshlets,
|
||||||
meshlet_bounding_spheres,
|
meshlet_bounding_spheres,
|
||||||
|
meshlet_simplification_errors,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#import bevy_pbr::meshlet_bindings::{
|
#import bevy_pbr::meshlet_bindings::{
|
||||||
meshlet_cluster_meshlet_ids,
|
meshlet_cluster_meshlet_ids,
|
||||||
meshlet_bounding_spheres,
|
meshlet_bounding_spheres,
|
||||||
|
meshlet_simplification_errors,
|
||||||
meshlet_cluster_instance_ids,
|
meshlet_cluster_instance_ids,
|
||||||
meshlet_instance_uniforms,
|
meshlet_instance_uniforms,
|
||||||
meshlet_second_pass_candidates,
|
meshlet_second_pass_candidates,
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
meshlet_hardware_raster_indirect_args,
|
meshlet_hardware_raster_indirect_args,
|
||||||
meshlet_raster_clusters,
|
meshlet_raster_clusters,
|
||||||
meshlet_raster_cluster_rightmost_slot,
|
meshlet_raster_cluster_rightmost_slot,
|
||||||
|
MeshletBoundingSphere,
|
||||||
}
|
}
|
||||||
#import bevy_render::maths::affine3_to_square
|
#import bevy_render::maths::affine3_to_square
|
||||||
|
|
||||||
|
@ -48,8 +50,8 @@ fn cull_clusters(
|
||||||
let world_from_local = affine3_to_square(instance_uniform.world_from_local);
|
let world_from_local = affine3_to_square(instance_uniform.world_from_local);
|
||||||
let world_scale = max(length(world_from_local[0]), max(length(world_from_local[1]), length(world_from_local[2])));
|
let world_scale = max(length(world_from_local[0]), max(length(world_from_local[1]), length(world_from_local[2])));
|
||||||
let bounding_spheres = meshlet_bounding_spheres[meshlet_id];
|
let bounding_spheres = meshlet_bounding_spheres[meshlet_id];
|
||||||
let culling_bounding_sphere_center = world_from_local * vec4(bounding_spheres.self_culling.center, 1.0);
|
let culling_bounding_sphere_center = world_from_local * vec4(bounding_spheres.culling_sphere.center, 1.0);
|
||||||
let culling_bounding_sphere_radius = world_scale * bounding_spheres.self_culling.radius;
|
let culling_bounding_sphere_radius = world_scale * bounding_spheres.culling_sphere.radius;
|
||||||
|
|
||||||
#ifdef MESHLET_FIRST_CULLING_PASS
|
#ifdef MESHLET_FIRST_CULLING_PASS
|
||||||
// Frustum culling
|
// Frustum culling
|
||||||
|
@ -60,19 +62,10 @@ fn cull_clusters(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate view-space LOD bounding sphere for the cluster
|
// Check LOD cut (cluster group error imperceptible, and parent group error not imperceptible)
|
||||||
let lod_bounding_sphere_center = world_from_local * vec4(bounding_spheres.self_lod.center, 1.0);
|
let simplification_errors = unpack2x16float(meshlet_simplification_errors[meshlet_id]);
|
||||||
let lod_bounding_sphere_radius = world_scale * bounding_spheres.self_lod.radius;
|
let lod_is_ok = lod_error_is_imperceptible(bounding_spheres.lod_group_sphere, simplification_errors.x, world_from_local, world_scale);
|
||||||
let lod_bounding_sphere_center_view_space = (view.view_from_world * vec4(lod_bounding_sphere_center.xyz, 1.0)).xyz;
|
let parent_lod_is_ok = lod_error_is_imperceptible(bounding_spheres.lod_parent_group_sphere, simplification_errors.y, world_from_local, world_scale);
|
||||||
|
|
||||||
// Calculate view-space LOD bounding sphere for the cluster's parent
|
|
||||||
let parent_lod_bounding_sphere_center = world_from_local * vec4(bounding_spheres.parent_lod.center, 1.0);
|
|
||||||
let parent_lod_bounding_sphere_radius = world_scale * bounding_spheres.parent_lod.radius;
|
|
||||||
let parent_lod_bounding_sphere_center_view_space = (view.view_from_world * vec4(parent_lod_bounding_sphere_center.xyz, 1.0)).xyz;
|
|
||||||
|
|
||||||
// Check LOD cut (cluster error imperceptible, and parent error not imperceptible)
|
|
||||||
let lod_is_ok = lod_error_is_imperceptible(lod_bounding_sphere_center_view_space, lod_bounding_sphere_radius);
|
|
||||||
let parent_lod_is_ok = lod_error_is_imperceptible(parent_lod_bounding_sphere_center_view_space, parent_lod_bounding_sphere_radius);
|
|
||||||
if !lod_is_ok || parent_lod_is_ok { return; }
|
if !lod_is_ok || parent_lod_is_ok { return; }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -80,8 +73,8 @@ fn cull_clusters(
|
||||||
#ifdef MESHLET_FIRST_CULLING_PASS
|
#ifdef MESHLET_FIRST_CULLING_PASS
|
||||||
let previous_world_from_local = affine3_to_square(instance_uniform.previous_world_from_local);
|
let previous_world_from_local = affine3_to_square(instance_uniform.previous_world_from_local);
|
||||||
let previous_world_from_local_scale = max(length(previous_world_from_local[0]), max(length(previous_world_from_local[1]), length(previous_world_from_local[2])));
|
let previous_world_from_local_scale = max(length(previous_world_from_local[0]), max(length(previous_world_from_local[1]), length(previous_world_from_local[2])));
|
||||||
let occlusion_culling_bounding_sphere_center = previous_world_from_local * vec4(bounding_spheres.self_culling.center, 1.0);
|
let occlusion_culling_bounding_sphere_center = previous_world_from_local * vec4(bounding_spheres.culling_sphere.center, 1.0);
|
||||||
let occlusion_culling_bounding_sphere_radius = previous_world_from_local_scale * bounding_spheres.self_culling.radius;
|
let occlusion_culling_bounding_sphere_radius = previous_world_from_local_scale * bounding_spheres.culling_sphere.radius;
|
||||||
let occlusion_culling_bounding_sphere_center_view_space = (previous_view.view_from_world * vec4(occlusion_culling_bounding_sphere_center.xyz, 1.0)).xyz;
|
let occlusion_culling_bounding_sphere_center_view_space = (previous_view.view_from_world * vec4(occlusion_culling_bounding_sphere_center.xyz, 1.0)).xyz;
|
||||||
#else
|
#else
|
||||||
let occlusion_culling_bounding_sphere_center = culling_bounding_sphere_center;
|
let occlusion_culling_bounding_sphere_center = culling_bounding_sphere_center;
|
||||||
|
@ -148,14 +141,23 @@ fn cull_clusters(
|
||||||
meshlet_raster_clusters[buffer_slot] = cluster_id;
|
meshlet_raster_clusters[buffer_slot] = cluster_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/21648630/radius-of-projected-sphere-in-screen-space/21649403#21649403
|
// https://github.com/zeux/meshoptimizer/blob/1e48e96c7e8059321de492865165e9ef071bffba/demo/nanite.cpp#L115
|
||||||
fn lod_error_is_imperceptible(cp: vec3<f32>, r: f32) -> bool {
|
fn lod_error_is_imperceptible(lod_sphere: MeshletBoundingSphere, simplification_error: f32, world_from_local: mat4x4<f32>, world_scale: f32) -> bool {
|
||||||
let d2 = dot(cp, cp);
|
let sphere_world_space = (world_from_local * vec4(lod_sphere.center, 1.0)).xyz;
|
||||||
let r2 = r * r;
|
let radius_world_space = world_scale * lod_sphere.radius;
|
||||||
let sphere_diameter_uv = view.clip_from_view[0][0] * r / sqrt(d2 - r2);
|
let error_world_space = world_scale * simplification_error;
|
||||||
let view_size = f32(max(view.viewport.z, view.viewport.w));
|
|
||||||
let sphere_diameter_pixels = sphere_diameter_uv * view_size;
|
var projected_error = error_world_space;
|
||||||
return sphere_diameter_pixels < 1.0;
|
if view.clip_from_view[3][3] != 1.0 {
|
||||||
|
// Perspective
|
||||||
|
let distance_to_closest_point_on_sphere = distance(sphere_world_space, view.world_position) - radius_world_space;
|
||||||
|
let distance_to_closest_point_on_sphere_clamped_to_znear = max(distance_to_closest_point_on_sphere, view.clip_from_view[3][2]);
|
||||||
|
projected_error /= distance_to_closest_point_on_sphere_clamped_to_znear;
|
||||||
|
}
|
||||||
|
projected_error *= view.clip_from_view[1][1] * 0.5;
|
||||||
|
projected_error *= view.viewport.w;
|
||||||
|
|
||||||
|
return projected_error < 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://zeux.io/2023/01/12/approximate-projected-bounds
|
// https://zeux.io/2023/01/12/approximate-projected-bounds
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
use super::asset::{Meshlet, MeshletBoundingSphere, MeshletBoundingSpheres, MeshletMesh};
|
use super::asset::{
|
||||||
|
Meshlet, MeshletBoundingSphere, MeshletBoundingSpheres, MeshletMesh, MeshletSimplificationError,
|
||||||
|
};
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use bevy_math::{ops::log2, IVec3, Vec2, Vec3, Vec3Swizzles};
|
use bevy_math::{ops::log2, IVec3, Vec2, Vec3, Vec3Swizzles};
|
||||||
use bevy_render::{
|
use bevy_render::{
|
||||||
|
@ -7,13 +9,12 @@ use bevy_render::{
|
||||||
};
|
};
|
||||||
use bevy_utils::HashMap;
|
use bevy_utils::HashMap;
|
||||||
use bitvec::{order::Lsb0, vec::BitVec, view::BitView};
|
use bitvec::{order::Lsb0, vec::BitVec, view::BitView};
|
||||||
use core::ops::Range;
|
use core::{iter, ops::Range};
|
||||||
use derive_more::derive::{Display, Error};
|
use derive_more::derive::{Display, Error};
|
||||||
|
use half::f16;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use meshopt::{
|
use meshopt::{
|
||||||
build_meshlets, compute_cluster_bounds, compute_meshlet_bounds,
|
build_meshlets, ffi::meshopt_Meshlet, simplify, Meshlets, SimplifyOptions, VertexDataAdapter,
|
||||||
ffi::{meshopt_Bounds, meshopt_Meshlet},
|
|
||||||
simplify, Meshlets, SimplifyOptions, VertexDataAdapter,
|
|
||||||
};
|
};
|
||||||
use metis::Graph;
|
use metis::Graph;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -69,19 +70,21 @@ impl MeshletMesh {
|
||||||
let mut bounding_spheres = meshlets
|
let mut bounding_spheres = meshlets
|
||||||
.iter()
|
.iter()
|
||||||
.map(|meshlet| compute_meshlet_bounds(meshlet, &vertices))
|
.map(|meshlet| compute_meshlet_bounds(meshlet, &vertices))
|
||||||
.map(convert_meshlet_bounds)
|
|
||||||
.map(|bounding_sphere| MeshletBoundingSpheres {
|
.map(|bounding_sphere| MeshletBoundingSpheres {
|
||||||
self_culling: bounding_sphere,
|
culling_sphere: bounding_sphere,
|
||||||
self_lod: MeshletBoundingSphere {
|
lod_group_sphere: bounding_sphere,
|
||||||
center: bounding_sphere.center,
|
lod_parent_group_sphere: MeshletBoundingSphere {
|
||||||
|
center: Vec3::ZERO,
|
||||||
radius: 0.0,
|
radius: 0.0,
|
||||||
},
|
},
|
||||||
parent_lod: MeshletBoundingSphere {
|
|
||||||
center: bounding_sphere.center,
|
|
||||||
radius: f32::MAX,
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
let mut simplification_errors = iter::repeat(MeshletSimplificationError {
|
||||||
|
group_error: f16::ZERO,
|
||||||
|
parent_group_error: f16::MAX,
|
||||||
|
})
|
||||||
|
.take(meshlets.len())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
// Build further LODs
|
// Build further LODs
|
||||||
let mut simplification_queue = 0..meshlets.len();
|
let mut simplification_queue = 0..meshlets.len();
|
||||||
|
@ -107,22 +110,13 @@ impl MeshletMesh {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Force parent error to be >= child error (we're currently building the parent from its children)
|
// Compute LOD data for the group
|
||||||
group_error = group_meshlets.iter().fold(group_error, |acc, meshlet_id| {
|
let group_bounding_sphere = compute_lod_group_data(
|
||||||
acc.max(bounding_spheres[*meshlet_id].self_lod.radius)
|
&group_meshlets,
|
||||||
});
|
&mut group_error,
|
||||||
|
&mut bounding_spheres,
|
||||||
// Build a new LOD bounding sphere for the simplified group as a whole
|
&mut simplification_errors,
|
||||||
let mut group_bounding_sphere = convert_meshlet_bounds(compute_cluster_bounds(
|
);
|
||||||
&simplified_group_indices,
|
|
||||||
&vertices,
|
|
||||||
));
|
|
||||||
group_bounding_sphere.radius = group_error;
|
|
||||||
|
|
||||||
// For each meshlet in the group set their parent LOD bounding sphere to that of the simplified group
|
|
||||||
for meshlet_id in group_meshlets {
|
|
||||||
bounding_spheres[meshlet_id].parent_lod = group_bounding_sphere;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build new meshlets using the simplified group
|
// Build new meshlets using the simplified group
|
||||||
let new_meshlets_count = split_simplified_group_into_new_meshlets(
|
let new_meshlets_count = split_simplified_group_into_new_meshlets(
|
||||||
|
@ -131,22 +125,24 @@ impl MeshletMesh {
|
||||||
&mut meshlets,
|
&mut meshlets,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Calculate the culling bounding sphere for the new meshlets and set their LOD bounding spheres
|
// Calculate the culling bounding sphere for the new meshlets and set their LOD group data
|
||||||
let new_meshlet_ids = (meshlets.len() - new_meshlets_count)..meshlets.len();
|
let new_meshlet_ids = (meshlets.len() - new_meshlets_count)..meshlets.len();
|
||||||
bounding_spheres.extend(
|
bounding_spheres.extend(new_meshlet_ids.clone().map(|meshlet_id| {
|
||||||
new_meshlet_ids
|
MeshletBoundingSpheres {
|
||||||
.map(|meshlet_id| {
|
culling_sphere: compute_meshlet_bounds(meshlets.get(meshlet_id), &vertices),
|
||||||
compute_meshlet_bounds(meshlets.get(meshlet_id), &vertices)
|
lod_group_sphere: group_bounding_sphere,
|
||||||
})
|
lod_parent_group_sphere: MeshletBoundingSphere {
|
||||||
.map(convert_meshlet_bounds)
|
center: Vec3::ZERO,
|
||||||
.map(|bounding_sphere| MeshletBoundingSpheres {
|
radius: 0.0,
|
||||||
self_culling: bounding_sphere,
|
},
|
||||||
self_lod: group_bounding_sphere,
|
}
|
||||||
parent_lod: MeshletBoundingSphere {
|
}));
|
||||||
center: group_bounding_sphere.center,
|
simplification_errors.extend(
|
||||||
radius: f32::MAX,
|
iter::repeat(MeshletSimplificationError {
|
||||||
},
|
group_error,
|
||||||
}),
|
parent_group_error: f16::MAX,
|
||||||
|
})
|
||||||
|
.take(new_meshlet_ids.len()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,6 +175,7 @@ impl MeshletMesh {
|
||||||
indices: meshlets.triangles.into(),
|
indices: meshlets.triangles.into(),
|
||||||
meshlets: bevy_meshlets.into(),
|
meshlets: bevy_meshlets.into(),
|
||||||
meshlet_bounding_spheres: bounding_spheres.into(),
|
meshlet_bounding_spheres: bounding_spheres.into(),
|
||||||
|
meshlet_simplification_errors: simplification_errors.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -303,7 +300,7 @@ fn simplify_meshlet_group(
|
||||||
group_meshlets: &[usize],
|
group_meshlets: &[usize],
|
||||||
meshlets: &Meshlets,
|
meshlets: &Meshlets,
|
||||||
vertices: &VertexDataAdapter<'_>,
|
vertices: &VertexDataAdapter<'_>,
|
||||||
) -> Option<(Vec<u32>, f32)> {
|
) -> Option<(Vec<u32>, f16)> {
|
||||||
// Build a new index buffer into the mesh vertex data by combining all meshlet data in the group
|
// Build a new index buffer into the mesh vertex data by combining all meshlet data in the group
|
||||||
let mut group_indices = Vec::new();
|
let mut group_indices = Vec::new();
|
||||||
for meshlet_id in group_meshlets {
|
for meshlet_id in group_meshlets {
|
||||||
|
@ -330,10 +327,55 @@ fn simplify_meshlet_group(
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert error from diameter to radius
|
Some((simplified_group_indices, f16::from_f32(error)))
|
||||||
error *= 0.5;
|
}
|
||||||
|
|
||||||
Some((simplified_group_indices, error))
|
fn compute_lod_group_data(
|
||||||
|
group_meshlets: &[usize],
|
||||||
|
group_error: &mut f16,
|
||||||
|
bounding_spheres: &mut [MeshletBoundingSpheres],
|
||||||
|
simplification_errors: &mut [MeshletSimplificationError],
|
||||||
|
) -> MeshletBoundingSphere {
|
||||||
|
let mut group_bounding_sphere = MeshletBoundingSphere {
|
||||||
|
center: Vec3::ZERO,
|
||||||
|
radius: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compute the lod group sphere center as a weighted average of the children spheres
|
||||||
|
let mut weight = 0.0;
|
||||||
|
for meshlet_id in group_meshlets {
|
||||||
|
let meshlet_lod_bounding_sphere = bounding_spheres[*meshlet_id].lod_group_sphere;
|
||||||
|
group_bounding_sphere.center +=
|
||||||
|
meshlet_lod_bounding_sphere.center * meshlet_lod_bounding_sphere.radius;
|
||||||
|
weight += meshlet_lod_bounding_sphere.radius;
|
||||||
|
}
|
||||||
|
group_bounding_sphere.center /= weight;
|
||||||
|
|
||||||
|
// Force parent group sphere to contain all child group spheres (we're currently building the parent from its children)
|
||||||
|
// TODO: This does not produce the absolute minimal bounding sphere. Doing so is non-trivial.
|
||||||
|
// "Smallest enclosing balls of balls" http://www.inf.ethz.ch/personal/emo/DoctThesisFiles/fischer05.pdf
|
||||||
|
for meshlet_id in group_meshlets {
|
||||||
|
let meshlet_lod_bounding_sphere = bounding_spheres[*meshlet_id].lod_group_sphere;
|
||||||
|
let d = meshlet_lod_bounding_sphere
|
||||||
|
.center
|
||||||
|
.distance(group_bounding_sphere.center);
|
||||||
|
group_bounding_sphere.radius = group_bounding_sphere
|
||||||
|
.radius
|
||||||
|
.max(meshlet_lod_bounding_sphere.radius + d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force parent error to be >= child error (we're currently building the parent from its children)
|
||||||
|
for meshlet_id in group_meshlets {
|
||||||
|
*group_error = group_error.max(simplification_errors[*meshlet_id].group_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the children's lod parent group data to the new lod group we just made
|
||||||
|
for meshlet_id in group_meshlets {
|
||||||
|
bounding_spheres[*meshlet_id].lod_parent_group_sphere = group_bounding_sphere;
|
||||||
|
simplification_errors[*meshlet_id].parent_group_error = *group_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
group_bounding_sphere
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_simplified_group_into_new_meshlets(
|
fn split_simplified_group_into_new_meshlets(
|
||||||
|
@ -449,7 +491,11 @@ fn build_and_compress_meshlet_vertex_data(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_meshlet_bounds(bounds: meshopt_Bounds) -> MeshletBoundingSphere {
|
fn compute_meshlet_bounds(
|
||||||
|
meshlet: meshopt::Meshlet<'_>,
|
||||||
|
vertices: &VertexDataAdapter<'_>,
|
||||||
|
) -> MeshletBoundingSphere {
|
||||||
|
let bounds = meshopt::compute_meshlet_bounds(meshlet, vertices);
|
||||||
MeshletBoundingSphere {
|
MeshletBoundingSphere {
|
||||||
center: bounds.center.into(),
|
center: bounds.center.into(),
|
||||||
radius: bounds.radius,
|
radius: bounds.radius,
|
||||||
|
|
|
@ -25,9 +25,9 @@ fn get_meshlet_triangle_count(meshlet: ptr<function, Meshlet>) -> u32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MeshletBoundingSpheres {
|
struct MeshletBoundingSpheres {
|
||||||
self_culling: MeshletBoundingSphere,
|
culling_sphere: MeshletBoundingSphere,
|
||||||
self_lod: MeshletBoundingSphere,
|
lod_group_sphere: MeshletBoundingSphere,
|
||||||
parent_lod: MeshletBoundingSphere,
|
lod_parent_group_sphere: MeshletBoundingSphere,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MeshletBoundingSphere {
|
struct MeshletBoundingSphere {
|
||||||
|
@ -62,16 +62,17 @@ var<push_constant> cluster_count: u32;
|
||||||
var<push_constant> meshlet_raster_cluster_rightmost_slot: u32;
|
var<push_constant> meshlet_raster_cluster_rightmost_slot: u32;
|
||||||
@group(0) @binding(0) var<storage, read> meshlet_cluster_meshlet_ids: array<u32>; // Per cluster
|
@group(0) @binding(0) var<storage, read> meshlet_cluster_meshlet_ids: array<u32>; // Per cluster
|
||||||
@group(0) @binding(1) var<storage, read> meshlet_bounding_spheres: array<MeshletBoundingSpheres>; // Per meshlet
|
@group(0) @binding(1) var<storage, read> meshlet_bounding_spheres: array<MeshletBoundingSpheres>; // Per meshlet
|
||||||
@group(0) @binding(2) var<storage, read> meshlet_cluster_instance_ids: array<u32>; // Per cluster
|
@group(0) @binding(2) var<storage, read> meshlet_simplification_errors: array<u32>; // Per meshlet
|
||||||
@group(0) @binding(3) var<storage, read> meshlet_instance_uniforms: array<Mesh>; // Per entity instance
|
@group(0) @binding(3) var<storage, read> meshlet_cluster_instance_ids: array<u32>; // Per cluster
|
||||||
@group(0) @binding(4) var<storage, read> meshlet_view_instance_visibility: array<u32>; // 1 bit per entity instance, packed as a bitmask
|
@group(0) @binding(4) var<storage, read> meshlet_instance_uniforms: array<Mesh>; // Per entity instance
|
||||||
@group(0) @binding(5) var<storage, read_write> meshlet_second_pass_candidates: array<atomic<u32>>; // 1 bit per cluster , packed as a bitmask
|
@group(0) @binding(5) var<storage, read> meshlet_view_instance_visibility: array<u32>; // 1 bit per entity instance, packed as a bitmask
|
||||||
@group(0) @binding(6) var<storage, read_write> meshlet_software_raster_indirect_args: DispatchIndirectArgs; // Single object shared between all workgroups/clusters/triangles
|
@group(0) @binding(6) var<storage, read_write> meshlet_second_pass_candidates: array<atomic<u32>>; // 1 bit per cluster , packed as a bitmask
|
||||||
@group(0) @binding(7) var<storage, read_write> meshlet_hardware_raster_indirect_args: DrawIndirectArgs; // Single object shared between all workgroups/clusters/triangles
|
@group(0) @binding(7) var<storage, read_write> meshlet_software_raster_indirect_args: DispatchIndirectArgs; // Single object shared between all workgroups/clusters/triangles
|
||||||
@group(0) @binding(8) var<storage, read_write> meshlet_raster_clusters: array<u32>; // Single object shared between all workgroups/clusters/triangles
|
@group(0) @binding(8) var<storage, read_write> meshlet_hardware_raster_indirect_args: DrawIndirectArgs; // Single object shared between all workgroups/clusters/triangles
|
||||||
@group(0) @binding(9) var depth_pyramid: texture_2d<f32>; // From the end of the last frame for the first culling pass, and from the first raster pass for the second culling pass
|
@group(0) @binding(9) var<storage, read_write> meshlet_raster_clusters: array<u32>; // Single object shared between all workgroups/clusters/triangles
|
||||||
@group(0) @binding(10) var<uniform> view: View;
|
@group(0) @binding(10) var depth_pyramid: texture_2d<f32>; // From the end of the last frame for the first culling pass, and from the first raster pass for the second culling pass
|
||||||
@group(0) @binding(11) var<uniform> previous_view: PreviousViewUniforms;
|
@group(0) @binding(11) var<uniform> view: View;
|
||||||
|
@group(0) @binding(12) var<uniform> previous_view: PreviousViewUniforms;
|
||||||
|
|
||||||
fn should_cull_instance(instance_id: u32) -> bool {
|
fn should_cull_instance(instance_id: u32) -> bool {
|
||||||
let bit_offset = instance_id % 32u;
|
let bit_offset = instance_id % 32u;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{
|
use super::{
|
||||||
asset::{Meshlet, MeshletBoundingSpheres},
|
asset::{Meshlet, MeshletBoundingSpheres, MeshletSimplificationError},
|
||||||
persistent_buffer::PersistentGpuBuffer,
|
persistent_buffer::PersistentGpuBuffer,
|
||||||
MeshletMesh,
|
MeshletMesh,
|
||||||
};
|
};
|
||||||
|
@ -26,7 +26,8 @@ pub struct MeshletMeshManager {
|
||||||
pub indices: PersistentGpuBuffer<Arc<[u8]>>,
|
pub indices: PersistentGpuBuffer<Arc<[u8]>>,
|
||||||
pub meshlets: PersistentGpuBuffer<Arc<[Meshlet]>>,
|
pub meshlets: PersistentGpuBuffer<Arc<[Meshlet]>>,
|
||||||
pub meshlet_bounding_spheres: PersistentGpuBuffer<Arc<[MeshletBoundingSpheres]>>,
|
pub meshlet_bounding_spheres: PersistentGpuBuffer<Arc<[MeshletBoundingSpheres]>>,
|
||||||
meshlet_mesh_slices: HashMap<AssetId<MeshletMesh>, [Range<BufferAddress>; 6]>,
|
pub meshlet_simplification_errors: PersistentGpuBuffer<Arc<[MeshletSimplificationError]>>,
|
||||||
|
meshlet_mesh_slices: HashMap<AssetId<MeshletMesh>, [Range<BufferAddress>; 7]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromWorld for MeshletMeshManager {
|
impl FromWorld for MeshletMeshManager {
|
||||||
|
@ -42,6 +43,10 @@ impl FromWorld for MeshletMeshManager {
|
||||||
"meshlet_bounding_spheres",
|
"meshlet_bounding_spheres",
|
||||||
render_device,
|
render_device,
|
||||||
),
|
),
|
||||||
|
meshlet_simplification_errors: PersistentGpuBuffer::new(
|
||||||
|
"meshlet_simplification_errors",
|
||||||
|
render_device,
|
||||||
|
),
|
||||||
meshlet_mesh_slices: HashMap::new(),
|
meshlet_mesh_slices: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,6 +86,9 @@ impl MeshletMeshManager {
|
||||||
let meshlet_bounding_spheres_slice = self
|
let meshlet_bounding_spheres_slice = self
|
||||||
.meshlet_bounding_spheres
|
.meshlet_bounding_spheres
|
||||||
.queue_write(Arc::clone(&meshlet_mesh.meshlet_bounding_spheres), ());
|
.queue_write(Arc::clone(&meshlet_mesh.meshlet_bounding_spheres), ());
|
||||||
|
let meshlet_simplification_errors_slice = self
|
||||||
|
.meshlet_simplification_errors
|
||||||
|
.queue_write(Arc::clone(&meshlet_mesh.meshlet_simplification_errors), ());
|
||||||
|
|
||||||
[
|
[
|
||||||
vertex_positions_slice,
|
vertex_positions_slice,
|
||||||
|
@ -89,11 +97,12 @@ impl MeshletMeshManager {
|
||||||
indices_slice,
|
indices_slice,
|
||||||
meshlets_slice,
|
meshlets_slice,
|
||||||
meshlet_bounding_spheres_slice,
|
meshlet_bounding_spheres_slice,
|
||||||
|
meshlet_simplification_errors_slice,
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
// If the MeshletMesh asset has not been uploaded to the GPU yet, queue it for uploading
|
// If the MeshletMesh asset has not been uploaded to the GPU yet, queue it for uploading
|
||||||
let [_, _, _, _, meshlets_slice, _] = self
|
let [_, _, _, _, meshlets_slice, _, _] = self
|
||||||
.meshlet_mesh_slices
|
.meshlet_mesh_slices
|
||||||
.entry(asset_id)
|
.entry(asset_id)
|
||||||
.or_insert_with_key(queue_meshlet_mesh)
|
.or_insert_with_key(queue_meshlet_mesh)
|
||||||
|
@ -106,7 +115,7 @@ impl MeshletMeshManager {
|
||||||
|
|
||||||
pub fn remove(&mut self, asset_id: &AssetId<MeshletMesh>) {
|
pub fn remove(&mut self, asset_id: &AssetId<MeshletMesh>) {
|
||||||
if let Some(
|
if let Some(
|
||||||
[vertex_positions_slice, vertex_normals_slice, vertex_uvs_slice, indices_slice, meshlets_slice, meshlet_bounding_spheres_slice],
|
[vertex_positions_slice, vertex_normals_slice, vertex_uvs_slice, indices_slice, meshlets_slice, meshlet_bounding_spheres_slice, meshlet_simplification_errors_slice],
|
||||||
) = self.meshlet_mesh_slices.remove(asset_id)
|
) = self.meshlet_mesh_slices.remove(asset_id)
|
||||||
{
|
{
|
||||||
self.vertex_positions
|
self.vertex_positions
|
||||||
|
@ -117,6 +126,8 @@ impl MeshletMeshManager {
|
||||||
self.meshlets.mark_slice_unused(meshlets_slice);
|
self.meshlets.mark_slice_unused(meshlets_slice);
|
||||||
self.meshlet_bounding_spheres
|
self.meshlet_bounding_spheres
|
||||||
.mark_slice_unused(meshlet_bounding_spheres_slice);
|
.mark_slice_unused(meshlet_bounding_spheres_slice);
|
||||||
|
self.meshlet_simplification_errors
|
||||||
|
.mark_slice_unused(meshlet_simplification_errors_slice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,4 +156,7 @@ pub fn perform_pending_meshlet_mesh_writes(
|
||||||
meshlet_mesh_manager
|
meshlet_mesh_manager
|
||||||
.meshlet_bounding_spheres
|
.meshlet_bounding_spheres
|
||||||
.perform_writes(&render_queue, &render_device);
|
.perform_writes(&render_queue, &render_device);
|
||||||
|
meshlet_mesh_manager
|
||||||
|
.meshlet_simplification_errors
|
||||||
|
.perform_writes(&render_queue, &render_device);
|
||||||
}
|
}
|
||||||
|
|
|
@ -290,6 +290,7 @@ impl Plugin for MeshletPlugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The meshlet mesh equivalent of [`bevy_render::mesh::Mesh3d`].
|
||||||
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]
|
||||||
#[reflect(Component, Default)]
|
#[reflect(Component, Default)]
|
||||||
#[require(Transform, Visibility)]
|
#[require(Transform, Visibility)]
|
||||||
|
|
|
@ -1,10 +1,42 @@
|
||||||
use super::{
|
use super::{
|
||||||
asset::{Meshlet, MeshletBoundingSpheres},
|
asset::{Meshlet, MeshletBoundingSpheres, MeshletSimplificationError},
|
||||||
persistent_buffer::PersistentGpuBufferable,
|
persistent_buffer::PersistentGpuBufferable,
|
||||||
};
|
};
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use bevy_math::Vec2;
|
use bevy_math::Vec2;
|
||||||
|
|
||||||
|
impl PersistentGpuBufferable for Arc<[Meshlet]> {
|
||||||
|
type Metadata = (u64, u64, u64);
|
||||||
|
|
||||||
|
fn size_in_bytes(&self) -> usize {
|
||||||
|
self.len() * size_of::<Meshlet>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes_le(
|
||||||
|
&self,
|
||||||
|
(vertex_position_offset, vertex_attribute_offset, index_offset): Self::Metadata,
|
||||||
|
buffer_slice: &mut [u8],
|
||||||
|
) {
|
||||||
|
let vertex_position_offset = (vertex_position_offset * 8) as u32;
|
||||||
|
let vertex_attribute_offset = (vertex_attribute_offset as usize / size_of::<u32>()) as u32;
|
||||||
|
let index_offset = index_offset as u32;
|
||||||
|
|
||||||
|
for (i, meshlet) in self.iter().enumerate() {
|
||||||
|
let size = size_of::<Meshlet>();
|
||||||
|
let i = i * size;
|
||||||
|
let bytes = bytemuck::cast::<_, [u8; size_of::<Meshlet>()]>(Meshlet {
|
||||||
|
start_vertex_position_bit: meshlet.start_vertex_position_bit
|
||||||
|
+ vertex_position_offset,
|
||||||
|
start_vertex_attribute_id: meshlet.start_vertex_attribute_id
|
||||||
|
+ vertex_attribute_offset,
|
||||||
|
start_index_id: meshlet.start_index_id + index_offset,
|
||||||
|
..*meshlet
|
||||||
|
});
|
||||||
|
buffer_slice[i..(i + size)].clone_from_slice(&bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PersistentGpuBufferable for Arc<[u8]> {
|
impl PersistentGpuBufferable for Arc<[u8]> {
|
||||||
type Metadata = ();
|
type Metadata = ();
|
||||||
|
|
||||||
|
@ -41,38 +73,6 @@ impl PersistentGpuBufferable for Arc<[Vec2]> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PersistentGpuBufferable for Arc<[Meshlet]> {
|
|
||||||
type Metadata = (u64, u64, u64);
|
|
||||||
|
|
||||||
fn size_in_bytes(&self) -> usize {
|
|
||||||
self.len() * size_of::<Meshlet>()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_bytes_le(
|
|
||||||
&self,
|
|
||||||
(vertex_position_offset, vertex_attribute_offset, index_offset): Self::Metadata,
|
|
||||||
buffer_slice: &mut [u8],
|
|
||||||
) {
|
|
||||||
let vertex_position_offset = (vertex_position_offset * 8) as u32;
|
|
||||||
let vertex_attribute_offset = (vertex_attribute_offset as usize / size_of::<u32>()) as u32;
|
|
||||||
let index_offset = index_offset as u32;
|
|
||||||
|
|
||||||
for (i, meshlet) in self.iter().enumerate() {
|
|
||||||
let size = size_of::<Meshlet>();
|
|
||||||
let i = i * size;
|
|
||||||
let bytes = bytemuck::cast::<_, [u8; size_of::<Meshlet>()]>(Meshlet {
|
|
||||||
start_vertex_position_bit: meshlet.start_vertex_position_bit
|
|
||||||
+ vertex_position_offset,
|
|
||||||
start_vertex_attribute_id: meshlet.start_vertex_attribute_id
|
|
||||||
+ vertex_attribute_offset,
|
|
||||||
start_index_id: meshlet.start_index_id + index_offset,
|
|
||||||
..*meshlet
|
|
||||||
});
|
|
||||||
buffer_slice[i..(i + size)].clone_from_slice(&bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PersistentGpuBufferable for Arc<[MeshletBoundingSpheres]> {
|
impl PersistentGpuBufferable for Arc<[MeshletBoundingSpheres]> {
|
||||||
type Metadata = ();
|
type Metadata = ();
|
||||||
|
|
||||||
|
@ -84,3 +84,15 @@ impl PersistentGpuBufferable for Arc<[MeshletBoundingSpheres]> {
|
||||||
buffer_slice.clone_from_slice(bytemuck::cast_slice(self));
|
buffer_slice.clone_from_slice(bytemuck::cast_slice(self));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PersistentGpuBufferable for Arc<[MeshletSimplificationError]> {
|
||||||
|
type Metadata = ();
|
||||||
|
|
||||||
|
fn size_in_bytes(&self) -> usize {
|
||||||
|
self.len() * size_of::<MeshletSimplificationError>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_bytes_le(&self, _: Self::Metadata, buffer_slice: &mut [u8]) {
|
||||||
|
buffer_slice.clone_from_slice(bytemuck::cast_slice(self));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -135,6 +135,7 @@ impl ResourceManager {
|
||||||
storage_buffer_read_only_sized(false, None),
|
storage_buffer_read_only_sized(false, None),
|
||||||
storage_buffer_read_only_sized(false, None),
|
storage_buffer_read_only_sized(false, None),
|
||||||
storage_buffer_read_only_sized(false, None),
|
storage_buffer_read_only_sized(false, None),
|
||||||
|
storage_buffer_read_only_sized(false, None),
|
||||||
storage_buffer_sized(false, None),
|
storage_buffer_sized(false, None),
|
||||||
storage_buffer_sized(false, None),
|
storage_buffer_sized(false, None),
|
||||||
storage_buffer_sized(false, None),
|
storage_buffer_sized(false, None),
|
||||||
|
@ -624,6 +625,7 @@ pub fn prepare_meshlet_view_bind_groups(
|
||||||
let entries = BindGroupEntries::sequential((
|
let entries = BindGroupEntries::sequential((
|
||||||
cluster_meshlet_ids.as_entire_binding(),
|
cluster_meshlet_ids.as_entire_binding(),
|
||||||
meshlet_mesh_manager.meshlet_bounding_spheres.binding(),
|
meshlet_mesh_manager.meshlet_bounding_spheres.binding(),
|
||||||
|
meshlet_mesh_manager.meshlet_simplification_errors.binding(),
|
||||||
cluster_instance_ids.as_entire_binding(),
|
cluster_instance_ids.as_entire_binding(),
|
||||||
instance_manager.instance_uniforms.binding().unwrap(),
|
instance_manager.instance_uniforms.binding().unwrap(),
|
||||||
view_resources.instance_visibility.as_entire_binding(),
|
view_resources.instance_visibility.as_entire_binding(),
|
||||||
|
@ -652,6 +654,7 @@ pub fn prepare_meshlet_view_bind_groups(
|
||||||
let entries = BindGroupEntries::sequential((
|
let entries = BindGroupEntries::sequential((
|
||||||
cluster_meshlet_ids.as_entire_binding(),
|
cluster_meshlet_ids.as_entire_binding(),
|
||||||
meshlet_mesh_manager.meshlet_bounding_spheres.binding(),
|
meshlet_mesh_manager.meshlet_bounding_spheres.binding(),
|
||||||
|
meshlet_mesh_manager.meshlet_simplification_errors.binding(),
|
||||||
cluster_instance_ids.as_entire_binding(),
|
cluster_instance_ids.as_entire_binding(),
|
||||||
instance_manager.instance_uniforms.binding().unwrap(),
|
instance_manager.instance_uniforms.binding().unwrap(),
|
||||||
view_resources.instance_visibility.as_entire_binding(),
|
view_resources.instance_visibility.as_entire_binding(),
|
||||||
|
|
|
@ -17,7 +17,7 @@ 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 =
|
const ASSET_URL: &str =
|
||||||
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/8443bbdee0bf517e6c297dede7f6a46ab712ee4c/bunny.meshlet_mesh";
|
"https://raw.githubusercontent.com/JMS55/bevy_meshlet_asset/167cdaf0b08f89fb747b83b94c27755f116cd408/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 a new issue