Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/render/morph.rs
6600 views
1
use core::{iter, mem};
2
3
use bevy_camera::visibility::ViewVisibility;
4
use bevy_ecs::prelude::*;
5
use bevy_mesh::morph::{MeshMorphWeights, MAX_MORPH_WEIGHTS};
6
use bevy_render::sync_world::MainEntityHashMap;
7
use bevy_render::{
8
batching::NoAutomaticBatching,
9
render_resource::{BufferUsages, RawBufferVec},
10
renderer::{RenderDevice, RenderQueue},
11
Extract,
12
};
13
use bytemuck::NoUninit;
14
15
#[derive(Component)]
16
pub struct MorphIndex {
17
pub index: u32,
18
}
19
20
/// Maps each mesh affected by morph targets to the applicable offset within the
21
/// [`MorphUniforms`] buffer.
22
///
23
/// We store both the current frame's mapping and the previous frame's mapping
24
/// for the purposes of motion vector calculation.
25
#[derive(Default, Resource)]
26
pub struct MorphIndices {
27
/// Maps each entity with a morphed mesh to the appropriate offset within
28
/// [`MorphUniforms::current_buffer`].
29
pub current: MainEntityHashMap<MorphIndex>,
30
31
/// Maps each entity with a morphed mesh to the appropriate offset within
32
/// [`MorphUniforms::prev_buffer`].
33
pub prev: MainEntityHashMap<MorphIndex>,
34
}
35
36
/// The GPU buffers containing morph weights for all meshes with morph targets.
37
///
38
/// This is double-buffered: we store the weights of the previous frame in
39
/// addition to those of the current frame. This is for motion vector
40
/// calculation. Every frame, we swap buffers and reuse the morph target weight
41
/// buffer from two frames ago for the current frame.
42
#[derive(Resource)]
43
pub struct MorphUniforms {
44
/// The morph weights for the current frame.
45
pub current_buffer: RawBufferVec<f32>,
46
/// The morph weights for the previous frame.
47
pub prev_buffer: RawBufferVec<f32>,
48
}
49
50
impl Default for MorphUniforms {
51
fn default() -> Self {
52
Self {
53
current_buffer: RawBufferVec::new(BufferUsages::UNIFORM),
54
prev_buffer: RawBufferVec::new(BufferUsages::UNIFORM),
55
}
56
}
57
}
58
59
pub fn prepare_morphs(
60
render_device: Res<RenderDevice>,
61
render_queue: Res<RenderQueue>,
62
mut uniform: ResMut<MorphUniforms>,
63
) {
64
if uniform.current_buffer.is_empty() {
65
return;
66
}
67
let len = uniform.current_buffer.len();
68
uniform.current_buffer.reserve(len, &render_device);
69
uniform
70
.current_buffer
71
.write_buffer(&render_device, &render_queue);
72
73
// We don't need to write `uniform.prev_buffer` because we already wrote it
74
// last frame, and the data should still be on the GPU.
75
}
76
77
const fn can_align(step: usize, target: usize) -> bool {
78
step.is_multiple_of(target) || target.is_multiple_of(step)
79
}
80
81
const WGPU_MIN_ALIGN: usize = 256;
82
83
/// Align a [`RawBufferVec`] to `N` bytes by padding the end with `T::default()` values.
84
fn add_to_alignment<T: NoUninit + Default>(buffer: &mut RawBufferVec<T>) {
85
let n = WGPU_MIN_ALIGN;
86
let t_size = size_of::<T>();
87
if !can_align(n, t_size) {
88
// This panic is stripped at compile time, due to n, t_size and can_align being const
89
panic!(
90
"RawBufferVec should contain only types with a size multiple or divisible by {n}, \
91
{} has a size of {t_size}, which is neither multiple or divisible by {n}",
92
core::any::type_name::<T>()
93
);
94
}
95
96
let buffer_size = buffer.len();
97
let byte_size = t_size * buffer_size;
98
let bytes_over_n = byte_size % n;
99
if bytes_over_n == 0 {
100
return;
101
}
102
let bytes_to_add = n - bytes_over_n;
103
let ts_to_add = bytes_to_add / t_size;
104
buffer.extend(iter::repeat_with(T::default).take(ts_to_add));
105
}
106
107
// Notes on implementation: see comment on top of the extract_skins system in skin module.
108
// This works similarly, but for `f32` instead of `Mat4`
109
pub fn extract_morphs(
110
morph_indices: ResMut<MorphIndices>,
111
uniform: ResMut<MorphUniforms>,
112
query: Extract<Query<(Entity, &ViewVisibility, &MeshMorphWeights)>>,
113
) {
114
// Borrow check workaround.
115
let (morph_indices, uniform) = (morph_indices.into_inner(), uniform.into_inner());
116
117
// Swap buffers. We need to keep the previous frame's buffer around for the
118
// purposes of motion vector computation.
119
mem::swap(&mut morph_indices.current, &mut morph_indices.prev);
120
mem::swap(&mut uniform.current_buffer, &mut uniform.prev_buffer);
121
morph_indices.current.clear();
122
uniform.current_buffer.clear();
123
124
for (entity, view_visibility, morph_weights) in &query {
125
if !view_visibility.get() {
126
continue;
127
}
128
let start = uniform.current_buffer.len();
129
let weights = morph_weights.weights();
130
let legal_weights = weights.iter().take(MAX_MORPH_WEIGHTS).copied();
131
uniform.current_buffer.extend(legal_weights);
132
add_to_alignment::<f32>(&mut uniform.current_buffer);
133
134
let index = (start * size_of::<f32>()) as u32;
135
morph_indices
136
.current
137
.insert(entity.into(), MorphIndex { index });
138
}
139
}
140
141
// NOTE: Because morph targets require per-morph target texture bindings, they cannot
142
// currently be batched.
143
pub fn no_automatic_morph_batching(
144
mut commands: Commands,
145
query: Query<Entity, (With<MeshMorphWeights>, Without<NoAutomaticBatching>)>,
146
) {
147
for entity in &query {
148
commands.entity(entity).try_insert(NoAutomaticBatching);
149
}
150
}
151
152