use crate::{
mesh::Mesh,
render_resource::{Extent3d, TextureDimension, TextureFormat},
texture::Image,
};
use bevy_app::{Plugin, PostUpdate};
use bevy_asset::Handle;
use bevy_ecs::prelude::*;
use bevy_hierarchy::Children;
use bevy_math::Vec3;
use bevy_reflect::Reflect;
use bytemuck::{Pod, Zeroable};
use std::{iter, mem};
use thiserror::Error;
const MAX_TEXTURE_WIDTH: u32 = 2048;
const MAX_COMPONENTS: u32 = MAX_TEXTURE_WIDTH * MAX_TEXTURE_WIDTH;
pub const MAX_MORPH_WEIGHTS: usize = 64;
pub struct MorphPlugin;
impl Plugin for MorphPlugin {
fn build(&self, app: &mut bevy_app::App) {
app.register_type::<MorphWeights>()
.register_type::<MeshMorphWeights>()
.add_systems(PostUpdate, inherit_weights);
}
}
#[derive(Error, Clone, Debug)]
pub enum MorphBuildError {
#[error(
"Too many vertexĂ—components in morph target, max is {MAX_COMPONENTS}, \
got {vertex_count}Ă—{component_count} = {}",
*vertex_count * *component_count as usize
)]
TooManyAttributes {
vertex_count: usize,
component_count: u32,
},
#[error(
"Bevy only supports up to {} morph targets (individual poses), tried to \
create a model with {target_count} morph targets",
MAX_MORPH_WEIGHTS
)]
TooManyTargets { target_count: usize },
}
#[derive(Debug)]
pub struct MorphTargetImage(pub Image);
impl MorphTargetImage {
pub fn new(
targets: impl ExactSizeIterator<Item = impl Iterator<Item = MorphAttributes>>,
vertex_count: usize,
) -> Result<Self, MorphBuildError> {
let max = MAX_TEXTURE_WIDTH;
let target_count = targets.len();
if target_count > MAX_MORPH_WEIGHTS {
return Err(MorphBuildError::TooManyTargets { target_count });
}
let component_count = (vertex_count * MorphAttributes::COMPONENT_COUNT) as u32;
let Some((Rect(width, height), padding)) = lowest_2d(component_count, max) else {
return Err(MorphBuildError::TooManyAttributes {
vertex_count,
component_count,
});
};
let data = targets
.flat_map(|mut attributes| {
let layer_byte_count = (padding + component_count) as usize * mem::size_of::<f32>();
let mut buffer = Vec::with_capacity(layer_byte_count);
for _ in 0..vertex_count {
let Some(to_add) = attributes.next() else {
break;
};
buffer.extend_from_slice(bytemuck::bytes_of(&to_add));
}
buffer.extend(iter::repeat(0).take(padding as usize * mem::size_of::<f32>()));
debug_assert_eq!(buffer.len(), layer_byte_count);
buffer
})
.collect();
let extents = Extent3d {
width,
height,
depth_or_array_layers: target_count as u32,
};
let image = Image::new(extents, TextureDimension::D3, data, TextureFormat::R32Float);
Ok(MorphTargetImage(image))
}
}
#[derive(Reflect, Default, Debug, Clone, Component)]
#[reflect(Debug, Component)]
pub struct MorphWeights {
weights: Vec<f32>,
first_mesh: Option<Handle<Mesh>>,
}
impl MorphWeights {
pub fn new(
weights: Vec<f32>,
first_mesh: Option<Handle<Mesh>>,
) -> Result<Self, MorphBuildError> {
if weights.len() > MAX_MORPH_WEIGHTS {
let target_count = weights.len();
return Err(MorphBuildError::TooManyTargets { target_count });
}
Ok(MorphWeights {
weights,
first_mesh,
})
}
pub fn first_mesh(&self) -> Option<&Handle<Mesh>> {
self.first_mesh.as_ref()
}
pub fn weights(&self) -> &[f32] {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut [f32] {
&mut self.weights
}
}
#[derive(Reflect, Default, Debug, Clone, Component)]
#[reflect(Debug, Component)]
pub struct MeshMorphWeights {
weights: Vec<f32>,
}
impl MeshMorphWeights {
pub fn new(weights: Vec<f32>) -> Result<Self, MorphBuildError> {
if weights.len() > MAX_MORPH_WEIGHTS {
let target_count = weights.len();
return Err(MorphBuildError::TooManyTargets { target_count });
}
Ok(MeshMorphWeights { weights })
}
pub fn weights(&self) -> &[f32] {
&self.weights
}
pub fn weights_mut(&mut self) -> &mut [f32] {
&mut self.weights
}
}
pub fn inherit_weights(
morph_nodes: Query<(&Children, &MorphWeights), (Without<Handle<Mesh>>, Changed<MorphWeights>)>,
mut morph_primitives: Query<&mut MeshMorphWeights, With<Handle<Mesh>>>,
) {
for (children, parent_weights) in &morph_nodes {
let mut iter = morph_primitives.iter_many_mut(children);
while let Some(mut child_weight) = iter.fetch_next() {
child_weight.weights.clear();
child_weight.weights.extend(&parent_weights.weights);
}
}
}
#[derive(Copy, Clone, PartialEq, Pod, Zeroable, Default)]
#[repr(C)]
pub struct MorphAttributes {
pub position: Vec3,
pub normal: Vec3,
pub tangent: Vec3,
}
impl From<[Vec3; 3]> for MorphAttributes {
fn from([position, normal, tangent]: [Vec3; 3]) -> Self {
MorphAttributes {
position,
normal,
tangent,
}
}
}
impl MorphAttributes {
pub const COMPONENT_COUNT: usize = 9;
pub fn new(position: Vec3, normal: Vec3, tangent: Vec3) -> Self {
MorphAttributes {
position,
normal,
tangent,
}
}
}
const fn div_ceil(lhf: u32, rhs: u32) -> u32 {
(lhf + rhs - 1) / rhs
}
struct Rect(u32, u32);
fn lowest_2d(min_includes: u32, max_edge: u32) -> Option<(Rect, u32)> {
(1..=max_edge)
.filter_map(|a| {
let b = div_ceil(min_includes, a);
let diff = (a * b).checked_sub(min_includes)?;
Some((Rect(a, b), diff))
})
.filter_map(|(rect, diff)| (rect.1 <= max_edge).then_some((rect, diff)))
.min_by_key(|(_, diff)| *diff)
}