4
4
//! `Handle<Material>` and `Handle<Mesh>` for all of your instances.
5
5
//!
6
6
//! This example is intended for advanced users and shows how to make a custom instancing
7
- //! implementation using bevy 's low level rendering api .
7
+ //! implementation using Bevy 's low level rendering API .
8
8
//! It's generally recommended to try the built-in instancing before going with this approach.
9
9
10
10
use bevy:: {
@@ -36,11 +36,13 @@ use bevy::{
36
36
} ;
37
37
use bytemuck:: { Pod , Zeroable } ;
38
38
39
- /// This example uses a shader source file from the assets subdirectory
39
+ /// Path to the WGSL shader asset used for custom instancing rendering.
40
40
const SHADER_ASSET_PATH : & str = "shaders/instancing.wgsl" ;
41
41
42
+ /// Component holding per-instance data for custom rendering.
42
43
#[ derive( Component ) ]
43
44
pub struct InstanceMaterialData {
45
+ /// A list of per-instance transform and color data.
44
46
pub instances : Vec < InstanceData > ,
45
47
}
46
48
@@ -56,6 +58,7 @@ impl ExtractComponent for InstanceMaterialData {
56
58
}
57
59
}
58
60
61
+ /// Plugin that sets up the custom voxel material pipeline.
59
62
pub struct VoxelMaterialPlugin ;
60
63
61
64
impl Plugin for VoxelMaterialPlugin {
@@ -78,13 +81,17 @@ impl Plugin for VoxelMaterialPlugin {
78
81
}
79
82
}
80
83
84
+ /// Single instance data containing position, scale and color.
81
85
#[ derive( Clone , Copy , Pod , Zeroable ) ]
82
86
#[ repr( C ) ]
83
87
pub struct InstanceData {
84
- pub pos_scale : [ f32 ; 4 ] , // x, y, z, scale
88
+ /// (x, y, z) position and uniform scale.
89
+ pub pos_scale : [ f32 ; 4 ] ,
90
+ /// RGBA color.
85
91
pub color : [ f32 ; 4 ] ,
86
92
}
87
93
94
+ /// Queues custom rendering commands for entities with `InstanceMaterialData`.
88
95
#[ allow( clippy:: too_many_arguments) ]
89
96
fn queue_custom (
90
97
transparent_3d_draw_functions : Res < DrawFunctions < Transparent3d > > ,
@@ -105,9 +112,9 @@ fn queue_custom(
105
112
} ;
106
113
107
114
let msaa_key = MeshPipelineKey :: from_msaa_samples ( msaa. samples ( ) ) ;
108
-
109
115
let view_key = msaa_key | MeshPipelineKey :: from_hdr ( view. hdr ) ;
110
116
let rangefinder = view. rangefinder3d ( ) ;
117
+
111
118
for ( entity, main_entity) in & material_meshes {
112
119
let Some ( mesh_instance) = render_mesh_instances. render_mesh_queue_data ( * main_entity)
113
120
else {
@@ -116,6 +123,7 @@ fn queue_custom(
116
123
let Some ( mesh) = meshes. get ( mesh_instance. mesh_asset_id ) else {
117
124
continue ;
118
125
} ;
126
+
119
127
let key =
120
128
view_key | MeshPipelineKey :: from_primitive_topology ( mesh. primitive_topology ( ) ) ;
121
129
let pipeline = pipelines
@@ -133,12 +141,14 @@ fn queue_custom(
133
141
}
134
142
}
135
143
144
+ /// GPU buffer holding instance data ready for rendering.
136
145
#[ derive( Component ) ]
137
146
struct InstanceBuffer {
138
147
buffer : Buffer ,
139
148
length : usize ,
140
149
}
141
150
151
+ /// Prepares instance buffers each frame, sorting instances by distance to camera.
142
152
fn prepare_instance_buffers (
143
153
mut commands : Commands ,
144
154
query : Query < ( Entity , & InstanceMaterialData ) > ,
@@ -148,23 +158,20 @@ fn prepare_instance_buffers(
148
158
let Some ( camera) = cameras. iter ( ) . next ( ) else {
149
159
return ;
150
160
} ;
151
-
152
161
let cam_pos = camera. world_from_view . transform_point ( Vec3 :: ZERO ) ;
153
162
154
163
for ( entity, instance_data) in & query {
155
164
let mut sorted_instances = instance_data. instances . clone ( ) ;
156
165
157
166
if sorted_instances. is_empty ( ) {
158
- // No instances, remove any existing buffer or do nothing
159
167
commands. entity ( entity) . remove :: < InstanceBuffer > ( ) ;
160
168
continue ;
161
169
}
162
170
163
- // Sort instances by distance from camera ( back-to-front)
171
+ // Sort back-to-front for proper alpha blending
164
172
sorted_instances. sort_by ( |a, b| {
165
173
let a_pos = Vec3 :: new ( a. pos_scale [ 0 ] , a. pos_scale [ 1 ] , a. pos_scale [ 2 ] ) ;
166
174
let b_pos = Vec3 :: new ( b. pos_scale [ 0 ] , b. pos_scale [ 1 ] , b. pos_scale [ 2 ] ) ;
167
-
168
175
let a_dist = cam_pos. distance_squared ( a_pos) ;
169
176
let b_dist = cam_pos. distance_squared ( b_pos) ;
170
177
@@ -186,9 +193,12 @@ fn prepare_instance_buffers(
186
193
}
187
194
}
188
195
196
+ /// Custom pipeline for instanced mesh rendering.
189
197
#[ derive( Resource ) ]
190
198
struct CustomPipeline {
199
+ /// The custom shader handle.
191
200
shader : Handle < Shader > ,
201
+ /// Reference to Bevy's default mesh pipeline.
192
202
mesh_pipeline : MeshPipeline ,
193
203
}
194
204
@@ -215,22 +225,12 @@ impl SpecializedMeshPipeline for CustomPipeline {
215
225
216
226
let color_format = TextureFormat :: Rgba8UnormSrgb ;
217
227
218
- // Custom depth stencil settings
219
228
descriptor. depth_stencil = Some ( DepthStencilState {
220
229
format : TextureFormat :: Depth32Float ,
221
230
depth_compare : CompareFunction :: Always ,
222
- stencil : StencilState {
223
- front : Default :: default ( ) ,
224
- back : Default :: default ( ) ,
225
- read_mask : 0 ,
226
- write_mask : 0 ,
227
- } , // Use default stencil state
231
+ stencil : StencilState :: default ( ) ,
228
232
depth_write_enabled : false ,
229
- bias : DepthBiasState {
230
- constant : 0 ,
231
- slope_scale : 0.0 ,
232
- clamp : 0.0 ,
233
- } ,
233
+ bias : DepthBiasState :: default ( ) ,
234
234
} ) ;
235
235
236
236
descriptor. fragment . as_mut ( ) . unwrap ( ) . targets [ 0 ] = Some ( ColorTargetState {
@@ -247,7 +247,7 @@ impl SpecializedMeshPipeline for CustomPipeline {
247
247
VertexAttribute {
248
248
format: VertexFormat :: Float32x4 ,
249
249
offset: 0 ,
250
- shader_location: 3 , // shader locations 0-2 are taken up by Position, Normal and UV attributes
250
+ shader_location: 3 ,
251
251
} ,
252
252
VertexAttribute {
253
253
format: VertexFormat :: Float32x4 ,
@@ -262,13 +262,15 @@ impl SpecializedMeshPipeline for CustomPipeline {
262
262
}
263
263
}
264
264
265
+ /// The custom draw command for rendering instances.
265
266
type DrawCustom = (
266
267
SetItemPipeline ,
267
268
SetMeshViewBindGroup < 0 > ,
268
269
SetMeshBindGroup < 1 > ,
269
270
DrawMeshInstanced ,
270
271
) ;
271
272
273
+ /// Draws a mesh multiple times using instance buffers.
272
274
struct DrawMeshInstanced ;
273
275
274
276
impl < P : PhaseItem > RenderCommand < P > for DrawMeshInstanced {
@@ -288,7 +290,6 @@ impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
288
290
( meshes, render_mesh_instances, mesh_allocator) : SystemParamItem < ' w , ' _ , Self :: Param > ,
289
291
pass : & mut TrackedRenderPass < ' w > ,
290
292
) -> RenderCommandResult {
291
- // A borrow check workaround.
292
293
let mesh_allocator = mesh_allocator. into_inner ( ) ;
293
294
294
295
let Some ( mesh_instance) = render_mesh_instances. render_mesh_queue_data ( item. main_entity ( ) )
0 commit comments