use std::collections::HashMap;
use awsm_materials::MaterialShaderId;
use awsm_renderer::animation::scene_loader::{lower_stored_clip, lower_stored_mixer};
use awsm_renderer::animation::{
AnimationClipGroup, AnimationClipKey, AnimationTarget, BuiltinMaterialParam, CameraParam,
LightParam, TargetMask,
};
use awsm_renderer::cameras::CameraKey;
use awsm_renderer::lights::LightKey;
use awsm_renderer::materials::MaterialKey;
use awsm_renderer::meshes::MeshKey;
use awsm_renderer::transforms::TransformKey;
use awsm_renderer::AwsmRenderer;
use awsm_scene::animation::{BuiltinParamKind, CameraParamKind, LightParamKind, TrackTarget};
use awsm_scene::{AssetId, EditorNode, NodeId, Scene};
#[derive(Default)]
pub struct AnimResolveMaps {
pub transforms: HashMap<NodeId, TransformKey>,
pub lights: HashMap<NodeId, LightKey>,
pub cameras: HashMap<NodeId, CameraKey>,
pub meshes: HashMap<NodeId, MeshKey>,
pub skin_joints: HashMap<NodeId, TransformKey>,
pub node_materials: HashMap<NodeId, MaterialKey>,
pub custom_shaders: HashMap<AssetId, MaterialShaderId>,
pub custom_materials: HashMap<AssetId, MaterialKey>,
}
pub fn load_animations(
renderer: &mut AwsmRenderer,
scene: &Scene,
maps: &AnimResolveMaps,
) -> Vec<AnimationClipKey> {
if scene.animations.is_empty() && scene.mixer.layers.is_empty() {
return Vec::new();
}
let groups: Vec<(AssetId, AnimationClipGroup)> = scene
.animations
.iter()
.map(|clip| {
(
clip.id,
lower_stored_clip(clip, |t| resolve_target(renderer, t, maps)),
)
})
.collect();
let mut clip_keys: Vec<(AssetId, AnimationClipKey)> = Vec::with_capacity(groups.len());
let mut loaded: Vec<AnimationClipKey> = Vec::with_capacity(groups.len());
for (id, group) in groups {
let key = renderer.animations.insert_clip(group);
clip_keys.push((id, key));
loaded.push(key);
}
renderer.animations.mixer = lower_stored_mixer(
&scene.mixer,
|id| clip_keys.iter().find(|(a, _)| *a == id).map(|(_, k)| *k),
|nodes, include_descendants| {
let mut mask = TargetMask::default();
let expanded;
let set: &[NodeId] = if include_descendants {
expanded = expand_descendants(scene, nodes);
&expanded
} else {
nodes
};
for nid in set {
if let Some(tk) = maps.transforms.get(nid) {
mask.transforms.insert(*tk);
}
}
mask
},
);
loaded
}
fn resolve_target(
renderer: &AwsmRenderer,
target: &TrackTarget,
maps: &AnimResolveMaps,
) -> Option<AnimationTarget> {
match target {
TrackTarget::Transform { node, .. } => maps
.skin_joints
.get(node)
.or_else(|| maps.transforms.get(node))
.copied()
.map(AnimationTarget::Transform),
TrackTarget::BuiltinParam { node, param } => {
maps.node_materials
.get(node)
.copied()
.map(|material| AnimationTarget::BuiltinParam {
material,
param: builtin_param(*param),
})
}
TrackTarget::Light { node, param } => {
maps.lights
.get(node)
.copied()
.map(|light| AnimationTarget::Light {
light,
param: light_param(*param),
})
}
TrackTarget::Camera { node, param } => {
maps.cameras
.get(node)
.copied()
.map(|camera| AnimationTarget::Camera {
camera,
param: camera_param(*param),
})
}
TrackTarget::Morph { node, .. } => {
let mesh = maps.meshes.get(node).copied()?;
renderer
.meshes
.geometry_morph_key_for_mesh(mesh)
.map(|k| AnimationTarget::Morph(k.into()))
}
TrackTarget::Uniform { material, name } => {
let shader_id = maps.custom_shaders.get(material).copied()?;
let slot = renderer
.dynamic_material_registration(shader_id)?
.layout
.uniforms
.iter()
.position(|u| u.name == *name)?;
let material = maps.custom_materials.get(material).copied()?;
Some(AnimationTarget::Uniform { material, slot })
}
}
}
fn expand_descendants(scene: &Scene, roots: &[NodeId]) -> Vec<NodeId> {
fn find(nodes: &[EditorNode], id: NodeId) -> Option<&EditorNode> {
for n in nodes {
if n.id == id {
return Some(n);
}
if let Some(found) = find(&n.children, id) {
return Some(found);
}
}
None
}
fn collect(node: &EditorNode, out: &mut Vec<NodeId>) {
for child in &node.children {
out.push(child.id);
collect(child, out);
}
}
let mut out = Vec::new();
for root in roots {
out.push(*root);
if let Some(n) = find(&scene.nodes, *root) {
collect(n, &mut out);
}
}
out
}
fn builtin_param(p: BuiltinParamKind) -> BuiltinMaterialParam {
match p {
BuiltinParamKind::BaseColor => BuiltinMaterialParam::BaseColor,
BuiltinParamKind::Metallic => BuiltinMaterialParam::Metallic,
BuiltinParamKind::Roughness => BuiltinMaterialParam::Roughness,
BuiltinParamKind::Emissive => BuiltinMaterialParam::Emissive,
}
}
fn light_param(p: LightParamKind) -> LightParam {
match p {
LightParamKind::Intensity => LightParam::Intensity,
LightParamKind::Color => LightParam::Color,
LightParamKind::Range => LightParam::Range,
LightParamKind::InnerAngle => LightParam::InnerAngle,
LightParamKind::OuterAngle => LightParam::OuterAngle,
}
}
fn camera_param(p: CameraParamKind) -> CameraParam {
match p {
CameraParamKind::FovY => CameraParam::FovY,
CameraParamKind::Near => CameraParam::Near,
CameraParamKind::Far => CameraParam::Far,
CameraParamKind::Aperture => CameraParam::Aperture,
CameraParamKind::FocusDistance => CameraParam::FocusDistance,
}
}
#[cfg(test)]
mod tests {
use super::expand_descendants;
use awsm_scene::{EditorNode, NodeId, NodeKind, Scene};
fn node(id: NodeId, children: Vec<EditorNode>) -> EditorNode {
EditorNode {
id,
name: String::new(),
transform: Default::default(),
kind: NodeKind::Group,
locked: false,
visible: true,
prefab: false,
children,
}
}
fn scene_with(nodes: Vec<EditorNode>) -> Scene {
Scene {
nodes,
..Default::default()
}
}
#[test]
fn expands_root_and_all_descendants_depth_first() {
let (root, child1, grandchild, child2) =
(NodeId::new(), NodeId::new(), NodeId::new(), NodeId::new());
let scene = scene_with(vec![node(
root,
vec![
node(child1, vec![node(grandchild, vec![])]),
node(child2, vec![]),
],
)]);
assert_eq!(
expand_descendants(&scene, &[root]),
vec![root, child1, grandchild, child2],
"root first, then depth-first descendants"
);
}
#[test]
fn unknown_root_yields_only_itself() {
let ghost = NodeId::new();
let scene = scene_with(vec![node(NodeId::new(), vec![])]);
assert_eq!(expand_descendants(&scene, &[ghost]), vec![ghost]);
}
#[test]
fn mid_tree_root_expands_only_its_subtree() {
let (root, child1, grandchild, child2) =
(NodeId::new(), NodeId::new(), NodeId::new(), NodeId::new());
let scene = scene_with(vec![node(
root,
vec![
node(child1, vec![node(grandchild, vec![])]),
node(child2, vec![]),
],
)]);
assert_eq!(
expand_descendants(&scene, &[child1]),
vec![child1, grandchild],
"child1's subtree only"
);
}
#[test]
fn multiple_roots_expand_in_order() {
let (a, a_kid, b) = (NodeId::new(), NodeId::new(), NodeId::new());
let scene = scene_with(vec![node(a, vec![node(a_kid, vec![])]), node(b, vec![])]);
assert_eq!(expand_descendants(&scene, &[a, b]), vec![a, a_kid, b]);
}
}