use rinecs::{
Entities, IntoUniqueIterator, Entity, Filter, Has, IntoStorages, Not, Read, ReadNot, ReadOption,
ReadOr, ReadRef, Res, Resources, ResourcesThreadLocal, Sto, StorageRef, StreamingIterator,
StreamingIteratorMut, UniqueEntities, Write, WriteOption, entity::EntityStorages,
storage::SliceView, system, system_storages, system_thread_local, system_thread_local_storages
};
#[cfg(feature="rayon")]
use rinecs::IntoUniqueParallelIterator;
use super::{
actions::{ActionClock, Action, ActionClockExt, RootMotionRemove},
components::{Rotation, RotModeToOrder},
SceneIndex, components::ShapeKey, Vertex,
skinning::{
Skeleton, BoneBase, RootMotionBone, BoneName, SkeletonRef,
},
};
use rin_scene::{
transformation::{Bone, BoneParts, RotMode},
geometry::{Geometry, GeometryRef, AnimatedGeometry},
components::{Name, Visible},
time::Clock,
};
use rin_graphics::{Node, NodeParts};
use rin_math::{UnitQuat, vec3, vec4};
#[cfg(feature="rayon")]
use rayon::prelude::*;
use super::actions::{ActionExt, ActionUpdate};
pub struct ActionClockUpdated;
pub struct ActionUpdated;
#[system(name = "animation update")]
#[needs("Action", "ActionClock")]
#[updates("ActionClockUpdated", "ActionUpdated", "ShapeKey")]
#[writes("ActionClock", "Action")]
#[reads(Clock)]
pub fn animation_update(mut entities: Entities, resources: Resources){
let clock = resources.get::<Clock>().unwrap();
let clock: &Clock = &clock;
let reset_clock = |action_clock: &mut ActionClock| {
action_clock.update(clock);
while let Some(_) = action_clock.try_recv_time(){}
};
let reset_actions = |action: &mut Action| {
action.update();
};
let reset_shape_keys = |shapekey: &mut ShapeKey| {
if let Some(action) = shapekey.action.as_mut() {
action.update();
}
};
entities.iter_for_mut::<Write<ActionClock>>().for_each(reset_clock);
entities.iter_for_mut::<Write<Action>>().for_each(reset_actions);
entities.iter_for_mut::<Write<ShapeKey>>().for_each(reset_shape_keys);
}
fn animate_skeleton(
entities: &EntityStorages,
skeleton: &mut Skeleton,
skeleton_trafo: &Node,
base_bones: SliceView<BoneBase>,
mut root_motion_bone: Option<&mut RootMotionBone>,
action_clock: &ActionClock,
action: &Action)
{
let mut root_motion_delta = vec3!(0.);
let mut root_motion_orientation_delta = UnitQuat::identity();
if !action.root_motion_remove(None).is_empty() {
if let Some(root_motion_bone) = root_motion_bone.as_mut(){
root_motion_bone.changed = false;
}
}
if let Some(now) = action_clock.last_recv_time() {
let t = now as f32;
let skeleton_scale = skeleton_trafo.global_scale();
base_bones.iter().for_each(|base_bone| {
let bones = entities.tree_node_for_mut::<Bone>(base_bone).unwrap();
let bones = unsafe{ bones.into_node().descendants_mut() };
let names = entities.tree_node_for::<BoneName>(base_bone).unwrap();
let names = names.descendants_ref();
for (mut bone, name) in bones.zip(names).filter(|(bone, _)| bone.used) {
let is_root_motion_bone = root_motion_bone
.as_ref()
.map(|b| b.name == name.0)
.unwrap_or(false);
let q = match bone.rot_mode{
RotMode::Quat => match action.root_motion_remove(Some(t)) {
root_motion_remove if is_root_motion_bone &&
root_motion_remove.intersects(RootMotionRemove::ORIENTATIONS) =>
{
let root_motion_bone = root_motion_bone.as_mut().unwrap();
action.root_motion_orientation_at(
&name, t,
&bone.animated.orientation(),
&mut root_motion_bone.prev_blend_orientation).map(|(q, delta)|
{
root_motion_bone.changed = true;
root_motion_bone.orientation = q;
root_motion_orientation_delta = delta;
let inv_original = bone.original.global_orientation().inverse();
let qq = inv_original * q;
let (mut roll, mut pitch, mut yaw) = qq.euler_angles();
if root_motion_remove.contains(RootMotionRemove::ROLL){
roll = 0.;
}
if root_motion_remove.contains(RootMotionRemove::PITCH){
pitch = 0.;
}
if root_motion_remove.contains(RootMotionRemove::YAW){
yaw = 0.;
}
let qq = UnitQuat::from_euler_angles(roll, pitch, yaw);
qq * bone.original.global_orientation()
})
},
_ => action.orientation_at(&name, t, &bone.animated.orientation()),
},
RotMode::AxisAngle =>
action.orientation_from_axis_angle_at(&name, t, &bone.animated.orientation()),
euler_rotation =>
action.orientation_from_euler_at(&name, t, &bone.animated.orientation(), euler_rotation.to_rot_order()),
};
if let Some(q) = q{
bone.animated.set_orientation(q);
}
match action.root_motion_remove(Some(t)) {
root_motion_remove if is_root_motion_bone &&
root_motion_remove.intersects(RootMotionRemove::TRANSLATIONS) =>
{
let root_motion_bone = root_motion_bone.as_mut().unwrap();
if let Some((p, delta)) = action.root_motion_location_at(
&name, t,
&bone.animated.position(),
&mut root_motion_bone.prev_blend_pos)
{
root_motion_bone.changed = true;
root_motion_bone.position = p;
root_motion_delta += bone.rest.global_orientation() * delta.component_mul(&skeleton_scale);
let original_inverse = bone.rest
.global_orientation()
.inverse();
let mut pp = original_inverse * p;
let prev_pp = original_inverse * bone.animated.position();
if root_motion_remove.contains(RootMotionRemove::TRANSLATION_X){
pp.x = prev_pp.x;
}
if root_motion_remove.contains(RootMotionRemove::TRANSLATION_Y){
pp.y = prev_pp.y;
}
if root_motion_remove.contains(RootMotionRemove::TRANSLATION_Z){
pp.z = prev_pp.z;
}
let p = bone.rest.global_orientation() * pp;
bone.animated.set_position(p);
}
}
_ => if let Some(p) = action.location_at(&name, t, &bone.animated.position()) {
bone.animated.set_position(p);
}
}
let s = action.scale_at(&name, t, &bone.animated.scale());
if let Some(s) = s{
bone.animated.set_scale(s);
}
}
});
skeleton.changed = true;
}
if !action.root_motion_remove(None).is_empty() {
if let Some(root_motion_bone) = root_motion_bone.as_mut(){
if root_motion_bone.changed {
root_motion_bone.delta = root_motion_delta;
root_motion_bone.orientation_delta = root_motion_orientation_delta;
}
}
}
}
#[system(name = "animation skeletons")]
#[needs("ActionClockUpdated", "ActionUpdated", "BoneBase")]
#[updates("Skeleton", "BoneParts", "RootMotionBone", "Bone")]
#[reads("Node", "ActionClock", "Action", "BoneName")]
pub fn animation_skeletons(mut entities: Entities, _resources: Resources){
let entities = entities.as_storages();
let mut storage = entities.storage_for::<(
Write<Skeleton>,
Read<Node>,
Read<BoneBase>,
WriteOption<RootMotionBone>,
Read<ActionClock>,
Read<Action>
)>().unwrap();
for (skeleton, skeleton_trafo, base_bones, root_motion_bone, action_clock, action) in storage.iter_mut() {
animate_skeleton(
&entities,
skeleton,
skeleton_trafo,
base_bones,
root_motion_bone,
action_clock,
action)
}
}
#[system(name = "animation scenes")]
#[needs("ActionClockUpdated", "ActionUpdated")]
#[updates("NodeParts", "Rotation", "Visible", "AnimatedGeometry<Vertex>", "RootMotionBone")]
#[writes("Node", "Skeleton", "Bone")]
#[reads(
Action,
Geometry<Vertex>,
ShapeKey,
SceneIndex,
ActionClock,
BoneBase,
BoneName,
GeometryRef,
Name,
SkeletonRef,
super::ApplyBlendShaoes
)]
pub fn animation_scenes(mut entities: Entities, resources: Resources){
let apply_blend_shapes = *resources.get::<super::ApplyBlendShaoes>()
.unwrap()
.apply_blend_shapes
.get();
let entities = entities.as_storages();
let scenes = entities.storage_for::<(
Read<SceneIndex>,
Read<ActionClock>
)>().unwrap();
for (scene, action_clock) in scenes.iter() {
if let Some(now) = action_clock.last_recv_time() {
let t = now as f32;
{
let mut trafos = entities.storage_for::<(
Write<Node>,
Write<Rotation>,
ReadNot<Action, ActionClock>)>();
#[cfg(feature="rayon")]
let models_and_empties = unsafe {
scene.models.par_iter()
.chain(scene.empties.par_iter())
.into_unique()
};
#[cfg(not(feature="rayon"))]
let models_and_empties = unsafe {
scene.models.iter()
.chain(scene.empties.iter())
.into_unique()
};
#[cfg(feature="rayon")]
let trafos = trafos.par_iter_for_entities_mut(models_and_empties);
#[cfg(not(feature="rayon"))]
let trafos = trafos.iter_for_entities_mut(models_and_empties);
trafos.for_each(|(trafo, rot, action)| {
let new_rot = action.rotation_at("", t as f32, &rot);
if let Some(new_rot) = new_rot{
*rot = new_rot;
trafo.set_orientation(new_rot.into_quat());
}
let p = action.location_at("", t as f32, &trafo.position());
if let Some(p) = p{
trafo.set_position(p);
}
let s = action.scale_at("", t as f32, &trafo.scale());
if let Some(s) = s{
trafo.set_scale(s);
}
});
}
{
let mut visibles = entities.storage_for::<(
Write<Visible>,
ReadNot<Action, ActionClock>,
)>();
#[cfg(feature="rayon")]
let visibles = visibles
.par_iter_for_entities_mut(&scene.models);
#[cfg(not(feature="rayon"))]
let visibles = visibles.iter_for_entities_mut(&scene.models);
visibles.for_each(|(visible, action)| {
let v = action.hide_render_at("", t as f32);
if let Some(v) = v{
if v {
visible.hide()
}else{
visible.show()
}
}
});
}
{
let mut skeletons = entities.storage_for::<(
Write<Skeleton>,
Read<Node>,
Read<BoneBase>,
WriteOption<RootMotionBone>,
ReadNot<Action, ActionClock>,
)>();
#[cfg(feature="rayon")]
let skeletons = skeletons
.par_iter_for_entities_mut(&scene.skeletons);
#[cfg(not(feature="rayon"))]
let skeletons = skeletons.iter_for_entities_mut(&scene.skeletons);
skeletons.for_each(|(skeleton, skeleton_trafo, base_bones, root_motion_bone, action)| {
animate_skeleton(&entities, skeleton, skeleton_trafo, base_bones, root_motion_bone, action_clock, action)
});
}
if !apply_blend_shapes {
continue;
}
let mut shape_keys = entities.storage_for::<(
ReadRef<GeometryRef, Geometry<Vertex>>,
Write<AnimatedGeometry<Vertex>>,
ReadNot<ShapeKey, ActionClock>
)>();
#[cfg(feature="rayon")]
let shape_keys = shape_keys
.par_iter_for_entities_mut(&scene.models);
#[cfg(not(feature="rayon"))]
let shape_keys = shape_keys.iter_for_entities_mut(&scene.models);
shape_keys.for_each(|(geom, animated, shapekey)| {
if shapekey.action.is_some() || !shapekey.key.drivers().is_empty() {
for ((animated, basis), geom) in animated.geom.iter_mut()
.zip(shapekey.key.basis().unwrap().data())
.zip(geom.vertices())
{
animated.position = vec4!(*basis, 1.);
animated.normal = geom.normal;
}
}
if let Some(action) = shapekey.action.as_ref() {
for keyblock in shapekey.key.block_names() {
let key_pct = action.keyblock_at(keyblock, t as f32);
if let Some(key_pct) = key_pct{
shapekey.key.apply4(keyblock, &mut animated.geom, key_pct);
animated.changed = true;
}
}
}else if !shapekey.key.drivers().is_empty() {
let r = regex::Regex::new(r#"key_blocks\["([^"]*)"\]"#).unwrap();
for driver in shapekey.key.drivers()
.iter()
.filter(|driver| driver.is_enabled())
{
let keyblock_path = driver.rna_path();
let keyblock = r.captures(keyblock_path).unwrap().get(1).unwrap().as_str();
let target_trafo = driver.driver().unwrap().variables().iter().map(|var| {
match var.ty() {
rinblender::curves::DriverVarType::TransformChannel => {
let target = var.driver_target_object().unwrap();
let pchan_name = var.pchan_name();
let skeleton_entities = entities
.storage_for::<(Entity, Read<Name>, Has<Skeleton>)>();
let local_bones = entities
.storage_for::<(Read<Bone>, Read<SkeletonRef>, Read<BoneName>)>();
let global_bones = entities
.storage_for::<(Read<Node>, Read<SkeletonRef>, Read<BoneName>)>();
let trafo = if pchan_name != "" {
let skeleton = skeleton_entities
.iter_for_entities(&scene.skeletons)
.find(|(_, name, _)| name.ends_with(target))
.unwrap().0;
if var.target_flags()
.contains(rinblender::curves::DriverTargetFlags::LOCAL_SPACE)
{
&local_bones
.iter()
.find(|(_,skeleton_ref,name)| skeleton_ref.0 == skeleton && name.0 == pchan_name)
.unwrap().0.animated
}else{
global_bones
.iter()
.find(|(_,skeleton_ref,name)| skeleton_ref.0 == skeleton && name.0 == pchan_name)
.unwrap().0
}
}else{
todo!("No channel, find object instead")
};
let (roll, pitch, yaw) = trafo.orientation().euler_angles();
let rot = vec3!(roll, pitch, yaw);
let trafo = rinblender::curves::DriverTransformation {
loc: trafo.position(),
rot,
scale: trafo.scale(),
};
trafo
}
rinblender::curves::DriverVarType::SingleProp =>
todo!("Single property not implemented yet"),
rinblender::curves::DriverVarType::RotDiff =>
todo!("Rotation diff not implemented yet"),
rinblender::curves::DriverVarType::LocDiff =>
todo!("Location diff not implemented yet"),
}
}).collect::<Vec<_>>();
let key_pct = driver.driver_value_at(&target_trafo, None, None).unwrap();
animated.changed = true;
shapekey.key.apply4(keyblock, &mut animated.geom, key_pct);
}
}
});
}
}
}
#[system_storages(name = "animation scenes trafos")]
#[needs("ActionClockUpdated", "ActionUpdated")]
#[updates("NodeParts", "Rotation")]
pub fn animation_scenes_trafos(
scenes: Sto<(Read<SceneIndex>, Read<ActionClock>)>,
mut trafos: Sto<(
Write<Node>,
Write<Rotation>,
ReadNot<Action, ActionClock>
)>,
){
for (scene, action_clock) in scenes.iter() {
if let Some(now) = action_clock.last_recv_time() {
let t = now as f32;
#[cfg(feature="rayon")]
let models_and_empties = unsafe {
scene.models.par_iter()
.chain(scene.empties.par_iter())
.into_unique()
};
#[cfg(not(feature="rayon"))]
let models_and_empties = unsafe {
scene.models.iter()
.chain(scene.empties.iter())
.into_unique()
};
#[cfg(feature="rayon")]
let trafos = trafos.par_iter_for_entities_mut(models_and_empties);
#[cfg(not(feature="rayon"))]
let trafos = trafos.iter_for_entities_mut(models_and_empties);
let apply_trafos = |(trafo, rot, action): (&mut Node, &mut Rotation, &Action)| {
let new_rot = action.rotation_at("", t as f32, &rot);
if let Some(new_rot) = new_rot{
*rot = new_rot;
trafo.set_orientation(new_rot.into_quat());
}
let p = action.location_at("", t as f32, &trafo.position());
if let Some(p) = p{
trafo.set_position(p);
}
let s = action.scale_at("", t as f32, &trafo.scale());
if let Some(s) = s{
trafo.set_scale(s);
}
};
trafos.for_each(apply_trafos);
}
}
}
#[system_storages(name = "animation scenes visibles")]
#[needs("ActionClockUpdated", "ActionUpdated")]
#[updates("Visible")]
pub fn animation_scenes_visibles(
scenes: Sto<(Read<SceneIndex>, Read<ActionClock>)>,
mut visibles: Sto<(Write<Visible>, ReadNot<Action, ActionClock>)>,
){
for (scene, action_clock) in scenes.iter() {
if let Some(now) = action_clock.last_recv_time() {
#[cfg(feature="rayon")]
let visibles = visibles.par_iter_for_entities_mut(&scene.models);
#[cfg(not(feature="rayon"))]
let visibles = visibles.iter_for_entities_mut(&scene.models);
let apply_visible = |(visible, action): (&mut Visible, &Action)| {
let v = action.hide_render_at("", now as f32);
if let Some(v) = v{
if v {
visible.hide()
}else{
visible.show()
}
}
};
visibles.for_each(apply_visible);
}
}
}
#[system(name = "animation scenes skeletons")]
#[needs("ActionClockUpdated", "ActionUpdated")]
#[updates(Skeleton, BoneParts, RootMotionBone, Bone)]
#[reads(SceneIndex, Node, BoneBase, ActionClock, Action, BoneName)]
pub fn animation_scenes_skeletons(mut entities: Entities, resources: Resources) {
let entities = entities.as_storages();
let scenes = entities
.storage_for::<(Read<SceneIndex>, Read<ActionClock>)>()
.unwrap();
let mut skeletons = entities
.storage_for::<(
Write<Skeleton>,
Read<Node>,
Read<BoneBase>,
WriteOption<RootMotionBone>,
ReadNot<Action, ActionClock>
)>().unwrap();
for (scene, action_clock) in scenes.iter() {
if let Some(now) = action_clock.last_recv_time() {
#[cfg(feature="rayon")]
let skeletons = skeletons.par_iter_for_entities_mut(&scene.skeletons);
#[cfg(not(feature="rayon"))]
let skeletons = skeletons.iter_for_entities_mut(&scene.skeletons);
skeletons.for_each(|(skeleton, skeleton_trafo, base_bones, root_motion_bone, action)|{
animate_skeleton(&entities, skeleton, skeleton_trafo, base_bones, root_motion_bone, action_clock, action)
});
}
}
}
#[system_storages(name = "animation scenes shapekeys")]
#[needs("ActionClockUpdated", "ActionUpdated")]
#[updates(AnimatedGeometry<Vertex>)]
pub fn animation_scenes_shapekeys(
scenes: Sto<(Read<SceneIndex>, Read<ActionClock>)>,
mut shape_keys: Sto<(
ReadRef<GeometryRef, Geometry<Vertex>>,
Write<AnimatedGeometry<Vertex>>,
ReadNot<ShapeKey, ActionClock>,
)>,
skeletons: Sto<(Entity, Read<Name>, Has<Skeleton>)>,
local_bones: Sto<(Read<Bone>, Read<SkeletonRef>, Read<BoneName>)>,
global_bones: Sto<(Read<Node>, Read<SkeletonRef>, Read<BoneName>)>,
parameters: Res<super::ApplyBlendShaoes>,
){
let apply_blend_shapes = *parameters
.apply_blend_shapes
.get();
if !apply_blend_shapes {
return;
}
let skeletons = skeletons.as_send_storage();
let local_bones = local_bones.as_send_storage();
let global_bones = global_bones.as_send_storage();
for (scene, action_clock) in scenes.iter() {
if let Some(now) = action_clock.last_recv_time() {
let t = now as f32;
#[cfg(feature="rayon")]
let shape_keys = shape_keys.par_iter_for_entities_mut(&scene.models);
#[cfg(not(feature="rayon"))]
let shape_keys = shape_keys.iter_for_entities_mut(&scene.models);
shape_keys.for_each(|(geom, animated, shapekey)| {
if shapekey.action.is_some() || !shapekey.key.drivers().is_empty() {
for ((animated, basis), geom) in animated.geom.iter_mut()
.zip(shapekey.key.basis().unwrap().data())
.zip(geom.vertices())
{
animated.position = vec4!(*basis, 1.);
animated.normal = geom.normal;
}
}
if let Some(action) = shapekey.action.as_ref() {
for keyblock in shapekey.key.block_names() {
let key_pct = action.keyblock_at(keyblock, t as f32);
if let Some(key_pct) = key_pct{
shapekey.key.apply4(keyblock, &mut animated.geom, key_pct);
animated.changed = true;
}
}
}else if !shapekey.key.drivers().is_empty(){
let r = regex::Regex::new(r#"key_blocks\["([^"]*)"\]"#).unwrap();
for driver in shapekey.key.drivers()
.iter()
.filter(|driver| driver.is_enabled())
{
let keyblock_path = driver.rna_path();
let keyblock = r.captures(keyblock_path).unwrap().get(1).unwrap().as_str();
let target_trafo = driver.driver().unwrap().variables().iter().map(|var| {
match var.ty() {
rinblender::curves::DriverVarType::TransformChannel => {
let target = var.driver_target_object().unwrap();
let pchan_name = var.pchan_name();
let trafo = if pchan_name != "" {
let skeleton = skeletons
.iter_for_entities(scene.skeletons.iter())
.find(|(_, name, _)| name.ends_with(target))
.unwrap().0;
if var.target_flags()
.contains(rinblender::curves::DriverTargetFlags::LOCAL_SPACE)
{
&local_bones
.iter()
.find(|(_,skeleton_ref,name)| skeleton_ref.0 == skeleton && name.0 == pchan_name)
.unwrap().0.animated
}else{
global_bones
.iter()
.find(|(_,skeleton_ref,name)| skeleton_ref.0 == skeleton && name.0 == pchan_name)
.unwrap().0
}
}else{
todo!("No channel, find object instead")
};
let (roll, pitch, yaw) = trafo.orientation().euler_angles();
let rot = vec3!(roll, pitch, yaw);
let trafo = rinblender::curves::DriverTransformation {
loc: trafo.position(),
rot,
scale: trafo.scale(),
};
trafo
}
rinblender::curves::DriverVarType::SingleProp =>
todo!("Single property not implemented yet"),
rinblender::curves::DriverVarType::RotDiff =>
todo!("Rotation diff not implemented yet"),
rinblender::curves::DriverVarType::LocDiff =>
todo!("Location diff not implemented yet"),
}
}).collect::<Vec<_>>();
let key_pct = driver.driver_value_at(&target_trafo, None, None).unwrap();
animated.changed = true;
shapekey.key.apply4(keyblock, &mut animated.geom, key_pct);
}
}
});
}
}
}
#[derive(Filter)]
struct IndividualEntity<'a>{
action_or_shape_key: ReadOr<'a, (Action, ShapeKey)>,
action_clock: ReadNot<'a, ActionClock, SceneIndex>,
trafo: WriteOption<'a, Node>,
rot: WriteOption<'a, Rotation>,
geom: ReadOption<'a, GeometryRef>,
animated_geom: WriteOption<'a, AnimatedGeometry<Vertex>>,
visible: WriteOption<'a, Visible>,
}
#[system_storages(name = "animation entities")]
#[needs("ActionClockUpdated", "ActionUpdated", "ShapeKey")]
#[updates("NodeParts", "Rotation", "AnimatedGeometry<Vertex>", "Visible")]
#[writes("Node")]
#[reads("Action", "ShapeKey", "ActionClock", "GeometryRef", "Geometry<Vertex>")]
pub fn animation_individual_entities(
geometries: Sto<Read<Geometry<Vertex>>>,
mut entities: Sto<(IndividualEntity, Not<Skeleton>)>)
{
for (mut e, _) in entities.iter_mut() {
if let Some(now) = e.action_clock.last_recv_time() {
let t = now as f32;
let (action, shape_key) = &*e.action_or_shape_key;
if let Some(action) = action.as_ref() {
if let Some(visible) = e.visible.as_mut() {
let v = action.hide_render_at("", t as f32);
if let Some(v) = v{
if v {
visible.hide()
}else{
visible.show()
}
}
}
if let (Some(trafo), Some(rot)) = (e.trafo.as_mut(), e.rot.as_mut()) {
let new_rot = action.rotation_at("", t as f32, rot);
if let Some(new_rot) = new_rot{
**rot = new_rot;
trafo.set_orientation(new_rot.into_quat());
}
let p = action.location_at("", t as f32, &trafo.position());
if let Some(p) = p{
trafo.set_position(p);
}
let s = action.scale_at("", t as f32, &trafo.scale());
if let Some(s) = s{
trafo.set_scale(s);
}
}
}
if let (Some(geomref), Some(animated), Some(shapekey)) =
(e.geom.as_ref(), e.animated_geom.as_mut(), shape_key.as_ref())
{
let geom = geometries.get(&geomref).unwrap();
if let Some(action) = shapekey.action.as_ref() {
if shapekey.action.is_some() || !shapekey.key.drivers().is_empty() {
animated.geom.clear();
animated.geom.extend_from_slice(geom.vertices());
}
for keyblock in shapekey.key.block_names() {
let key_pct = action.keyblock_at(keyblock, t as f32);
if let Some(key_pct) = key_pct {
shapekey.key.apply4(keyblock, &mut animated.geom, key_pct);
animated.changed = true;
}
}
}else{
println!("Shape key without action {}", shapekey.key.name());
}
}
}
}
}