bevy_pbr/render/
mesh_bindings.rs

1//! Bind group layout related definitions for the mesh pipeline.
2
3use bevy_math::Mat4;
4use bevy_render::{
5    mesh::morph::MAX_MORPH_WEIGHTS, render_resource::*, renderer::RenderDevice, texture::GpuImage,
6};
7
8use crate::render::skin::MAX_JOINTS;
9
10const MORPH_WEIGHT_SIZE: usize = size_of::<f32>();
11pub const MORPH_BUFFER_SIZE: usize = MAX_MORPH_WEIGHTS * MORPH_WEIGHT_SIZE;
12
13const JOINT_SIZE: usize = size_of::<Mat4>();
14pub(crate) const JOINT_BUFFER_SIZE: usize = MAX_JOINTS * JOINT_SIZE;
15
16/// Individual layout entries.
17mod layout_entry {
18    use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE};
19    use crate::MeshUniform;
20    use bevy_render::{
21        render_resource::{
22            binding_types::{sampler, texture_2d, texture_3d, uniform_buffer_sized},
23            BindGroupLayoutEntryBuilder, BufferSize, GpuArrayBuffer, SamplerBindingType,
24            ShaderStages, TextureSampleType,
25        },
26        renderer::RenderDevice,
27    };
28
29    pub(super) fn model(render_device: &RenderDevice) -> BindGroupLayoutEntryBuilder {
30        GpuArrayBuffer::<MeshUniform>::binding_layout(render_device)
31            .visibility(ShaderStages::VERTEX_FRAGMENT)
32    }
33    pub(super) fn skinning() -> BindGroupLayoutEntryBuilder {
34        uniform_buffer_sized(true, BufferSize::new(JOINT_BUFFER_SIZE as u64))
35    }
36    pub(super) fn weights() -> BindGroupLayoutEntryBuilder {
37        uniform_buffer_sized(true, BufferSize::new(MORPH_BUFFER_SIZE as u64))
38    }
39    pub(super) fn targets() -> BindGroupLayoutEntryBuilder {
40        texture_3d(TextureSampleType::Float { filterable: false })
41    }
42    pub(super) fn lightmaps_texture_view() -> BindGroupLayoutEntryBuilder {
43        texture_2d(TextureSampleType::Float { filterable: true }).visibility(ShaderStages::FRAGMENT)
44    }
45    pub(super) fn lightmaps_sampler() -> BindGroupLayoutEntryBuilder {
46        sampler(SamplerBindingType::Filtering).visibility(ShaderStages::FRAGMENT)
47    }
48}
49
50/// Individual [`BindGroupEntry`]
51/// for bind groups.
52mod entry {
53    use super::{JOINT_BUFFER_SIZE, MORPH_BUFFER_SIZE};
54    use bevy_render::render_resource::{
55        BindGroupEntry, BindingResource, Buffer, BufferBinding, BufferSize, Sampler, TextureView,
56    };
57
58    fn entry(binding: u32, size: u64, buffer: &Buffer) -> BindGroupEntry {
59        BindGroupEntry {
60            binding,
61            resource: BindingResource::Buffer(BufferBinding {
62                buffer,
63                offset: 0,
64                size: Some(BufferSize::new(size).unwrap()),
65            }),
66        }
67    }
68    pub(super) fn model(binding: u32, resource: BindingResource) -> BindGroupEntry {
69        BindGroupEntry { binding, resource }
70    }
71    pub(super) fn skinning(binding: u32, buffer: &Buffer) -> BindGroupEntry {
72        entry(binding, JOINT_BUFFER_SIZE as u64, buffer)
73    }
74    pub(super) fn weights(binding: u32, buffer: &Buffer) -> BindGroupEntry {
75        entry(binding, MORPH_BUFFER_SIZE as u64, buffer)
76    }
77    pub(super) fn targets(binding: u32, texture: &TextureView) -> BindGroupEntry {
78        BindGroupEntry {
79            binding,
80            resource: BindingResource::TextureView(texture),
81        }
82    }
83    pub(super) fn lightmaps_texture_view(binding: u32, texture: &TextureView) -> BindGroupEntry {
84        BindGroupEntry {
85            binding,
86            resource: BindingResource::TextureView(texture),
87        }
88    }
89    pub(super) fn lightmaps_sampler(binding: u32, sampler: &Sampler) -> BindGroupEntry {
90        BindGroupEntry {
91            binding,
92            resource: BindingResource::Sampler(sampler),
93        }
94    }
95}
96
97/// All possible [`BindGroupLayout`]s in bevy's default mesh shader (`mesh.wgsl`).
98#[derive(Clone)]
99pub struct MeshLayouts {
100    /// The mesh model uniform (transform) and nothing else.
101    pub model_only: BindGroupLayout,
102
103    /// Includes the lightmap texture and uniform.
104    pub lightmapped: BindGroupLayout,
105
106    /// Also includes the uniform for skinning
107    pub skinned: BindGroupLayout,
108
109    /// Like [`MeshLayouts::skinned`], but includes slots for the previous
110    /// frame's joint matrices, so that we can compute motion vectors.
111    pub skinned_motion: BindGroupLayout,
112
113    /// Also includes the uniform and [`MorphAttributes`] for morph targets.
114    ///
115    /// [`MorphAttributes`]: bevy_render::mesh::morph::MorphAttributes
116    pub morphed: BindGroupLayout,
117
118    /// Like [`MeshLayouts::morphed`], but includes a slot for the previous
119    /// frame's morph weights, so that we can compute motion vectors.
120    pub morphed_motion: BindGroupLayout,
121
122    /// Also includes both uniforms for skinning and morph targets, also the
123    /// morph target [`MorphAttributes`] binding.
124    ///
125    /// [`MorphAttributes`]: bevy_render::mesh::morph::MorphAttributes
126    pub morphed_skinned: BindGroupLayout,
127
128    /// Like [`MeshLayouts::morphed_skinned`], but includes slots for the
129    /// previous frame's joint matrices and morph weights, so that we can
130    /// compute motion vectors.
131    pub morphed_skinned_motion: BindGroupLayout,
132}
133
134impl MeshLayouts {
135    /// Prepare the layouts used by the default bevy [`Mesh`].
136    ///
137    /// [`Mesh`]: bevy_render::prelude::Mesh
138    pub fn new(render_device: &RenderDevice) -> Self {
139        MeshLayouts {
140            model_only: Self::model_only_layout(render_device),
141            lightmapped: Self::lightmapped_layout(render_device),
142            skinned: Self::skinned_layout(render_device),
143            skinned_motion: Self::skinned_motion_layout(render_device),
144            morphed: Self::morphed_layout(render_device),
145            morphed_motion: Self::morphed_motion_layout(render_device),
146            morphed_skinned: Self::morphed_skinned_layout(render_device),
147            morphed_skinned_motion: Self::morphed_skinned_motion_layout(render_device),
148        }
149    }
150
151    // ---------- create individual BindGroupLayouts ----------
152
153    fn model_only_layout(render_device: &RenderDevice) -> BindGroupLayout {
154        render_device.create_bind_group_layout(
155            "mesh_layout",
156            &BindGroupLayoutEntries::single(
157                ShaderStages::empty(),
158                layout_entry::model(render_device),
159            ),
160        )
161    }
162
163    /// Creates the layout for skinned meshes.
164    fn skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
165        render_device.create_bind_group_layout(
166            "skinned_mesh_layout",
167            &BindGroupLayoutEntries::with_indices(
168                ShaderStages::VERTEX,
169                (
170                    (0, layout_entry::model(render_device)),
171                    // The current frame's joint matrix buffer.
172                    (1, layout_entry::skinning()),
173                ),
174            ),
175        )
176    }
177
178    /// Creates the layout for skinned meshes with the infrastructure to compute
179    /// motion vectors.
180    fn skinned_motion_layout(render_device: &RenderDevice) -> BindGroupLayout {
181        render_device.create_bind_group_layout(
182            "skinned_motion_mesh_layout",
183            &BindGroupLayoutEntries::with_indices(
184                ShaderStages::VERTEX,
185                (
186                    (0, layout_entry::model(render_device)),
187                    // The current frame's joint matrix buffer.
188                    (1, layout_entry::skinning()),
189                    // The previous frame's joint matrix buffer.
190                    (6, layout_entry::skinning()),
191                ),
192            ),
193        )
194    }
195
196    /// Creates the layout for meshes with morph targets.
197    fn morphed_layout(render_device: &RenderDevice) -> BindGroupLayout {
198        render_device.create_bind_group_layout(
199            "morphed_mesh_layout",
200            &BindGroupLayoutEntries::with_indices(
201                ShaderStages::VERTEX,
202                (
203                    (0, layout_entry::model(render_device)),
204                    // The current frame's morph weight buffer.
205                    (2, layout_entry::weights()),
206                    (3, layout_entry::targets()),
207                ),
208            ),
209        )
210    }
211
212    /// Creates the layout for meshes with morph targets and the infrastructure
213    /// to compute motion vectors.
214    fn morphed_motion_layout(render_device: &RenderDevice) -> BindGroupLayout {
215        render_device.create_bind_group_layout(
216            "morphed_mesh_layout",
217            &BindGroupLayoutEntries::with_indices(
218                ShaderStages::VERTEX,
219                (
220                    (0, layout_entry::model(render_device)),
221                    // The current frame's morph weight buffer.
222                    (2, layout_entry::weights()),
223                    (3, layout_entry::targets()),
224                    // The previous frame's morph weight buffer.
225                    (7, layout_entry::weights()),
226                ),
227            ),
228        )
229    }
230
231    /// Creates the bind group layout for meshes with both skins and morph
232    /// targets.
233    fn morphed_skinned_layout(render_device: &RenderDevice) -> BindGroupLayout {
234        render_device.create_bind_group_layout(
235            "morphed_skinned_mesh_layout",
236            &BindGroupLayoutEntries::with_indices(
237                ShaderStages::VERTEX,
238                (
239                    (0, layout_entry::model(render_device)),
240                    // The current frame's joint matrix buffer.
241                    (1, layout_entry::skinning()),
242                    // The current frame's morph weight buffer.
243                    (2, layout_entry::weights()),
244                    (3, layout_entry::targets()),
245                ),
246            ),
247        )
248    }
249
250    /// Creates the bind group layout for meshes with both skins and morph
251    /// targets, in addition to the infrastructure to compute motion vectors.
252    fn morphed_skinned_motion_layout(render_device: &RenderDevice) -> BindGroupLayout {
253        render_device.create_bind_group_layout(
254            "morphed_skinned_motion_mesh_layout",
255            &BindGroupLayoutEntries::with_indices(
256                ShaderStages::VERTEX,
257                (
258                    (0, layout_entry::model(render_device)),
259                    // The current frame's joint matrix buffer.
260                    (1, layout_entry::skinning()),
261                    // The current frame's morph weight buffer.
262                    (2, layout_entry::weights()),
263                    (3, layout_entry::targets()),
264                    // The previous frame's joint matrix buffer.
265                    (6, layout_entry::skinning()),
266                    // The previous frame's morph weight buffer.
267                    (7, layout_entry::weights()),
268                ),
269            ),
270        )
271    }
272
273    fn lightmapped_layout(render_device: &RenderDevice) -> BindGroupLayout {
274        render_device.create_bind_group_layout(
275            "lightmapped_mesh_layout",
276            &BindGroupLayoutEntries::with_indices(
277                ShaderStages::VERTEX,
278                (
279                    (0, layout_entry::model(render_device)),
280                    (4, layout_entry::lightmaps_texture_view()),
281                    (5, layout_entry::lightmaps_sampler()),
282                ),
283            ),
284        )
285    }
286
287    // ---------- BindGroup methods ----------
288
289    pub fn model_only(&self, render_device: &RenderDevice, model: &BindingResource) -> BindGroup {
290        render_device.create_bind_group(
291            "model_only_mesh_bind_group",
292            &self.model_only,
293            &[entry::model(0, model.clone())],
294        )
295    }
296
297    pub fn lightmapped(
298        &self,
299        render_device: &RenderDevice,
300        model: &BindingResource,
301        lightmap: &GpuImage,
302    ) -> BindGroup {
303        render_device.create_bind_group(
304            "lightmapped_mesh_bind_group",
305            &self.lightmapped,
306            &[
307                entry::model(0, model.clone()),
308                entry::lightmaps_texture_view(4, &lightmap.texture_view),
309                entry::lightmaps_sampler(5, &lightmap.sampler),
310            ],
311        )
312    }
313
314    /// Creates the bind group for skinned meshes with no morph targets.
315    pub fn skinned(
316        &self,
317        render_device: &RenderDevice,
318        model: &BindingResource,
319        current_skin: &Buffer,
320    ) -> BindGroup {
321        render_device.create_bind_group(
322            "skinned_mesh_bind_group",
323            &self.skinned,
324            &[
325                entry::model(0, model.clone()),
326                entry::skinning(1, current_skin),
327            ],
328        )
329    }
330
331    /// Creates the bind group for skinned meshes with no morph targets, with
332    /// the infrastructure to compute motion vectors.
333    ///
334    /// `current_skin` is the buffer of joint matrices for this frame;
335    /// `prev_skin` is the buffer for the previous frame. The latter is used for
336    /// motion vector computation. If there is no such applicable buffer,
337    /// `current_skin` and `prev_skin` will reference the same buffer.
338    pub fn skinned_motion(
339        &self,
340        render_device: &RenderDevice,
341        model: &BindingResource,
342        current_skin: &Buffer,
343        prev_skin: &Buffer,
344    ) -> BindGroup {
345        render_device.create_bind_group(
346            "skinned_motion_mesh_bind_group",
347            &self.skinned_motion,
348            &[
349                entry::model(0, model.clone()),
350                entry::skinning(1, current_skin),
351                entry::skinning(6, prev_skin),
352            ],
353        )
354    }
355
356    /// Creates the bind group for meshes with no skins but morph targets.
357    pub fn morphed(
358        &self,
359        render_device: &RenderDevice,
360        model: &BindingResource,
361        current_weights: &Buffer,
362        targets: &TextureView,
363    ) -> BindGroup {
364        render_device.create_bind_group(
365            "morphed_mesh_bind_group",
366            &self.morphed,
367            &[
368                entry::model(0, model.clone()),
369                entry::weights(2, current_weights),
370                entry::targets(3, targets),
371            ],
372        )
373    }
374
375    /// Creates the bind group for meshes with no skins but morph targets, in
376    /// addition to the infrastructure to compute motion vectors.
377    ///
378    /// `current_weights` is the buffer of morph weights for this frame;
379    /// `prev_weights` is the buffer for the previous frame. The latter is used
380    /// for motion vector computation. If there is no such applicable buffer,
381    /// `current_weights` and `prev_weights` will reference the same buffer.
382    pub fn morphed_motion(
383        &self,
384        render_device: &RenderDevice,
385        model: &BindingResource,
386        current_weights: &Buffer,
387        targets: &TextureView,
388        prev_weights: &Buffer,
389    ) -> BindGroup {
390        render_device.create_bind_group(
391            "morphed_motion_mesh_bind_group",
392            &self.morphed_motion,
393            &[
394                entry::model(0, model.clone()),
395                entry::weights(2, current_weights),
396                entry::targets(3, targets),
397                entry::weights(7, prev_weights),
398            ],
399        )
400    }
401
402    /// Creates the bind group for meshes with skins and morph targets.
403    #[allow(clippy::too_many_arguments)]
404    pub fn morphed_skinned(
405        &self,
406        render_device: &RenderDevice,
407        model: &BindingResource,
408        current_skin: &Buffer,
409        current_weights: &Buffer,
410        targets: &TextureView,
411    ) -> BindGroup {
412        render_device.create_bind_group(
413            "morphed_skinned_mesh_bind_group",
414            &self.morphed_skinned,
415            &[
416                entry::model(0, model.clone()),
417                entry::skinning(1, current_skin),
418                entry::weights(2, current_weights),
419                entry::targets(3, targets),
420            ],
421        )
422    }
423
424    /// Creates the bind group for meshes with skins and morph targets, in
425    /// addition to the infrastructure to compute motion vectors.
426    ///
427    /// See the documentation for [`MeshLayouts::skinned_motion`] and
428    /// [`MeshLayouts::morphed_motion`] above for more information about the
429    /// `current_skin`, `prev_skin`, `current_weights`, and `prev_weights`
430    /// buffers.
431    #[allow(clippy::too_many_arguments)]
432    pub fn morphed_skinned_motion(
433        &self,
434        render_device: &RenderDevice,
435        model: &BindingResource,
436        current_skin: &Buffer,
437        current_weights: &Buffer,
438        targets: &TextureView,
439        prev_skin: &Buffer,
440        prev_weights: &Buffer,
441    ) -> BindGroup {
442        render_device.create_bind_group(
443            "morphed_skinned_motion_mesh_bind_group",
444            &self.morphed_skinned_motion,
445            &[
446                entry::model(0, model.clone()),
447                entry::skinning(1, current_skin),
448                entry::weights(2, current_weights),
449                entry::targets(3, targets),
450                entry::skinning(6, prev_skin),
451                entry::weights(7, prev_weights),
452            ],
453        )
454    }
455}