Skip to main content

test_meshlet/
test_meshlet.rs

1//! Test meshlets by showing them next to the mesh they were created from.
2
3use bevy::{
4    asset::RenderAssetUsages,
5    camera_controller::free_camera::{FreeCamera, FreeCameraPlugin},
6    image::{ImageAddressMode, ImageFilterMode},
7    input::common_conditions::input_just_pressed,
8    pbr::experimental::meshlet::{
9        MeshletMesh, MeshletMesh3d, MeshletPlugin,
10        MESHLET_DEFAULT_VERTEX_POSITION_QUANTIZATION_FACTOR,
11    },
12    prelude::*,
13    render::render_resource::{AsBindGroup, Extent3d, TextureDimension, TextureFormat},
14};
15
16fn main() {
17    App::new()
18        .add_plugins((
19            DefaultPlugins,
20            MeshletPlugin {
21                cluster_buffer_slots: 1 << 14,
22            },
23            MaterialPlugin::<MeshletDebugMaterial>::default(),
24            FreeCameraPlugin,
25        ))
26        .add_systems(Startup, setup)
27        .add_systems(
28            Update,
29            toggle_materials.run_if(input_just_pressed(KeyCode::KeyM)),
30        )
31        .insert_resource(GlobalAmbientLight {
32            brightness: 500.0,
33            ..default()
34        })
35        .run();
36}
37
38fn setup(
39    mut commands: Commands,
40    mut standard_materials: ResMut<Assets<StandardMaterial>>,
41    mut debug_materials: ResMut<Assets<MeshletDebugMaterial>>,
42    mut meshes: ResMut<Assets<Mesh>>,
43    mut meshlets: ResMut<Assets<MeshletMesh>>,
44    mut images: ResMut<Assets<Image>>,
45) {
46    commands.spawn_scene_list(bsn_list! {
47        Camera3d
48        Transform::from_translation(Vec3::new(0.0, 1.5, -3.0))
49            .looking_at(vec3(0.0, 0.0, 1.5), Vec3::Y)
50        Msaa::Off
51        FreeCamera
52        --
53        DirectionalLight {
54            illuminance: light_consts::lux::FULL_DAYLIGHT
55        }
56        Transform::from_xyz(0.0, 1.0, -0.5).looking_at(Vec3::ZERO, Vec3::Z)
57        --
58        Text::new("M: toggle debug material")
59        Node {
60            position_type: PositionType::Absolute,
61            top: px(12),
62            left: px(12),
63        }
64        --
65        Text::new("meshlet")
66        Node {
67            position_type: PositionType::Absolute,
68            bottom: px(12),
69            left: percent(25),
70        }
71        --
72        Text::new("regular")
73        Node {
74            position_type: PositionType::Absolute,
75            bottom: px(12),
76            right: percent(25),
77        }
78    });
79
80    let sphere_mesh = Sphere::default().mesh().uv(48, 27);
81    let cube_mesh = Cuboid::from_length(0.8).mesh().build();
82
83    let sphere_meshlet = MeshletMesh::from_mesh(
84        &sphere_mesh,
85        MESHLET_DEFAULT_VERTEX_POSITION_QUANTIZATION_FACTOR,
86    )
87    .unwrap();
88
89    let cube_meshlet = MeshletMesh::from_mesh(
90        &cube_mesh,
91        MESHLET_DEFAULT_VERTEX_POSITION_QUANTIZATION_FACTOR,
92    )
93    .unwrap();
94
95    let sphere_mesh_asset = meshes.add(sphere_mesh);
96    let cube_mesh_asset = meshes.add(cube_mesh);
97
98    let sphere_meshlet_asset = meshlets.add(sphere_meshlet);
99    let cube_meshlet_asset = meshlets.add(cube_meshlet);
100
101    let mipmap_material = mipmap_material(&mut standard_materials, &mut images);
102    let debug_material = debug_materials.add(MeshletDebugMaterial::default());
103
104    for distance in [0.0, 8.0, 32.0, 128.0] {
105        commands.spawn_scene_list(bsn_list! {
106            Mesh3d({sphere_mesh_asset.clone()})
107            MeshMaterial3d::<StandardMaterial>({mipmap_material.clone()})
108            Transform::from_xyz(-0.5, 0.0, distance)
109            --
110            MeshletMesh3d({sphere_meshlet_asset.clone()})
111            MeshMaterial3d::<StandardMaterial>({mipmap_material.clone()})
112            Transform::from_xyz(0.5, 0.0, distance)
113            --
114            Mesh3d({cube_mesh_asset.clone()})
115            MeshMaterial3d::<StandardMaterial>({mipmap_material.clone()})
116            Transform::from_xyz(-1.5, 0.0, distance)
117            --
118            MeshletMesh3d({cube_meshlet_asset.clone()})
119            MeshMaterial3d::<StandardMaterial>({mipmap_material.clone()})
120            Transform::from_xyz(1.5, 0.0, distance)
121        });
122    }
123
124    commands.insert_resource(Materials {
125        mipmap: mipmap_material,
126        debug: debug_material,
127    });
128}
129
130fn toggle_materials(
131    mut commands: Commands,
132    materials: Res<Materials>,
133    meshlet_materials: Query<
134        (
135            Entity,
136            Option<&MeshMaterial3d<StandardMaterial>>,
137            Option<&MeshMaterial3d<MeshletDebugMaterial>>,
138        ),
139        With<MeshletMesh3d>,
140    >,
141) {
142    for (entity, mipmap, debug) in meshlet_materials {
143        if mipmap.is_some() {
144            commands
145                .entity(entity)
146                .remove::<MeshMaterial3d<StandardMaterial>>()
147                .insert(MeshMaterial3d(materials.debug.clone()));
148        } else if debug.is_some() {
149            commands
150                .entity(entity)
151                .remove::<MeshMaterial3d<MeshletDebugMaterial>>()
152                .insert(MeshMaterial3d(materials.mipmap.clone()));
153        }
154    }
155}
156
157#[derive(Resource)]
158struct Materials {
159    mipmap: Handle<StandardMaterial>,
160    debug: Handle<MeshletDebugMaterial>,
161}
162
163#[derive(Asset, TypePath, AsBindGroup, Clone, Default)]
164struct MeshletDebugMaterial {
165    _dummy: (),
166}
167
168impl Material for MeshletDebugMaterial {}
169
170fn checkerboard(size: usize, color: Srgba) -> Vec<u8> {
171    let color = color.to_u8_array();
172    let black = Srgba::BLACK.to_u8_array();
173    let shift = size.ilog2().saturating_sub(3);
174
175    (0..(size * size))
176        .into_iter()
177        .flat_map(|i| {
178            let x = i.rem_euclid(size) >> shift;
179            let y = (i / size) >> shift;
180            if ((x + y) & 1) == 0 {
181                color
182            } else {
183                black
184            }
185        })
186        .collect()
187}
188
189// Create a material where each mip has a different color.
190fn mipmap_material(
191    standard_materials: &mut Assets<StandardMaterial>,
192    images: &mut Assets<Image>,
193) -> Handle<StandardMaterial> {
194    let mip_colors = [
195        Srgba::rgb(1.0, 0.0, 0.0),
196        Srgba::rgb(0.0, 1.0, 0.0),
197        Srgba::rgb(0.0, 0.0, 1.0),
198        Srgba::rgb(1.0, 1.0, 0.0),
199        Srgba::rgb(0.0, 1.0, 1.0),
200        Srgba::rgb(1.0, 0.0, 1.0),
201    ];
202
203    let size = 256;
204
205    let mut image = Image::new_uninit(
206        Extent3d {
207            width: size as u32,
208            height: size as u32,
209            depth_or_array_layers: 1,
210        },
211        TextureDimension::D2,
212        TextureFormat::Rgba8UnormSrgb,
213        RenderAssetUsages::default(),
214    );
215
216    image.texture_descriptor.mip_level_count = mip_colors.len() as u32;
217
218    image
219        .sampler
220        .get_or_init_descriptor()
221        .set_filter(ImageFilterMode::Linear)
222        .set_address_mode(ImageAddressMode::Repeat);
223
224    image.data = Some(
225        mip_colors
226            .iter()
227            .enumerate()
228            .flat_map(|(level, color)| checkerboard(size / 2_usize.pow(level as u32), *color))
229            .collect(),
230    );
231
232    standard_materials.add(StandardMaterial {
233        base_color_texture: Some(images.add(image)),
234        perceptual_roughness: 1.0,
235        ..default()
236    })
237}