bevy/crates/bevy_pbr/src/render/morph.rs
Nicola Papale db1e3d36bc
Move skin code to a separate module (#9899)
# Objective

mesh.rs is infamously large. We could split off unrelated code.

## Solution

Morph targets are very similar to skinning and have their own module. We
move skinned meshes to an independent module like morph targets and give
the systems similar names.

### Open questions

Should the skinning systems and structs stay public?

---

## Migration Guide

Renamed skinning systems, resources and components:

- extract_skinned_meshes -> extract_skins
- prepare_skinned_meshes -> prepare_skins
- SkinnedMeshUniform -> SkinUniform
- SkinnedMeshJoints -> SkinIndex

---------

Co-authored-by: François <mockersf@gmail.com>
Co-authored-by: vero <email@atlasdostal.com>
2023-09-25 18:40:22 +00:00

102 lines
3.1 KiB
Rust

use std::{iter, mem};
use bevy_ecs::prelude::*;
use bevy_render::{
batching::NoAutomaticBatching,
mesh::morph::{MeshMorphWeights, MAX_MORPH_WEIGHTS},
render_resource::{BufferUsages, BufferVec},
renderer::{RenderDevice, RenderQueue},
view::ViewVisibility,
Extract,
};
use bytemuck::Pod;
#[derive(Component)]
pub struct MorphIndex {
pub(super) index: u32,
}
#[derive(Resource)]
pub struct MorphUniform {
pub buffer: BufferVec<f32>,
}
impl Default for MorphUniform {
fn default() -> Self {
Self {
buffer: BufferVec::new(BufferUsages::UNIFORM),
}
}
}
pub fn prepare_morphs(
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
mut uniform: ResMut<MorphUniform>,
) {
if uniform.buffer.is_empty() {
return;
}
let len = uniform.buffer.len();
uniform.buffer.reserve(len, &render_device);
uniform.buffer.write_buffer(&render_device, &render_queue);
}
const fn can_align(step: usize, target: usize) -> bool {
step % target == 0 || target % step == 0
}
const WGPU_MIN_ALIGN: usize = 256;
/// Align a [`BufferVec`] to `N` bytes by padding the end with `T::default()` values.
fn add_to_alignment<T: Pod + Default>(buffer: &mut BufferVec<T>) {
let n = WGPU_MIN_ALIGN;
let t_size = mem::size_of::<T>();
if !can_align(n, t_size) {
// This panic is stripped at compile time, due to n, t_size and can_align being const
panic!(
"BufferVec should contain only types with a size multiple or divisible by {n}, \
{} has a size of {t_size}, which is neither multiple or divisible by {n}",
std::any::type_name::<T>()
);
}
let buffer_size = buffer.len();
let byte_size = t_size * buffer_size;
let bytes_over_n = byte_size % n;
if bytes_over_n == 0 {
return;
}
let bytes_to_add = n - bytes_over_n;
let ts_to_add = bytes_to_add / t_size;
buffer.extend(iter::repeat_with(T::default).take(ts_to_add));
}
// Notes on implementation: see comment on top of the extract_skins system in skin module.
// This works similarly, but for `f32` instead of `Mat4`
pub fn extract_morphs(
mut commands: Commands,
mut previous_len: Local<usize>,
mut uniform: ResMut<MorphUniform>,
query: Extract<Query<(Entity, &ViewVisibility, &MeshMorphWeights)>>,
) {
uniform.buffer.clear();
let mut values = Vec::with_capacity(*previous_len);
for (entity, view_visibility, morph_weights) in &query {
if !view_visibility.get() {
continue;
}
let start = uniform.buffer.len();
let weights = morph_weights.weights();
let legal_weights = weights.iter().take(MAX_MORPH_WEIGHTS).copied();
uniform.buffer.extend(legal_weights);
add_to_alignment::<f32>(&mut uniform.buffer);
let index = (start * mem::size_of::<f32>()) as u32;
// NOTE: Because morph targets require per-morph target texture bindings, they cannot
// currently be batched.
values.push((entity, (MorphIndex { index }, NoAutomaticBatching)));
}
*previous_len = values.len();
commands.insert_or_spawn_batch(values);
}