Runtime animation retargeting (#627)
* runtime animation retargeting * improvements, fixes * character controller script: separate animation file from character * editor fix * script improvements * foot positioning fixes
This commit is contained in:
+8
-181
@@ -5,65 +5,6 @@
|
||||
using namespace wi::ecs;
|
||||
using namespace wi::scene;
|
||||
|
||||
XMMATRIX ComputeWorldMatrixRecursive(Scene& scene, Entity entity, XMMATRIX localMatrix)
|
||||
{
|
||||
HierarchyComponent* hier = scene.hierarchy.GetComponent(entity);
|
||||
if (hier != nullptr)
|
||||
{
|
||||
Entity parentID = hier->parentID;
|
||||
while (parentID != INVALID_ENTITY)
|
||||
{
|
||||
TransformComponent* transform_parent = scene.transforms.GetComponent(parentID);
|
||||
if (transform_parent == nullptr)
|
||||
break;
|
||||
|
||||
localMatrix *= transform_parent->GetLocalMatrix();
|
||||
|
||||
const HierarchyComponent* hier_recursive = scene.hierarchy.GetComponent(parentID);
|
||||
if (hier_recursive != nullptr)
|
||||
{
|
||||
parentID = hier_recursive->parentID;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentID = INVALID_ENTITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return localMatrix;
|
||||
}
|
||||
XMMATRIX ComputeInverseParentMatrixRecursive(Scene& scene, Entity entity)
|
||||
{
|
||||
XMMATRIX inverseParentMatrix = XMMatrixIdentity();
|
||||
|
||||
HierarchyComponent* hier = scene.hierarchy.GetComponent(entity);
|
||||
if (hier != nullptr)
|
||||
{
|
||||
Entity parentID = hier->parentID;
|
||||
while (parentID != INVALID_ENTITY)
|
||||
{
|
||||
TransformComponent* transform_parent = scene.transforms.GetComponent(parentID);
|
||||
if (transform_parent == nullptr)
|
||||
break;
|
||||
|
||||
inverseParentMatrix *= transform_parent->GetLocalMatrix();
|
||||
|
||||
const HierarchyComponent* hier_recursive = scene.hierarchy.GetComponent(parentID);
|
||||
if (hier_recursive != nullptr)
|
||||
{
|
||||
parentID = hier_recursive->parentID;
|
||||
}
|
||||
else
|
||||
{
|
||||
parentID = INVALID_ENTITY;
|
||||
}
|
||||
}
|
||||
|
||||
inverseParentMatrix = XMMatrixInverse(nullptr, inverseParentMatrix);
|
||||
}
|
||||
return inverseParentMatrix;
|
||||
}
|
||||
|
||||
void AnimationWindow::Create(EditorComponent* _editor)
|
||||
{
|
||||
editor = _editor;
|
||||
@@ -970,132 +911,18 @@ void AnimationWindow::Create(EditorComponent* _editor)
|
||||
retargetCombo.OnSelect([=](wi::gui::EventArgs args) {
|
||||
retargetCombo.SetSelectedWithoutCallback(-1);
|
||||
wi::scene::Scene& scene = editor->GetCurrentScene();
|
||||
const AnimationComponent* animation_source = scene.animations.GetComponent(entity);
|
||||
if (animation_source == nullptr)
|
||||
return;
|
||||
const HumanoidComponent* humanoid_dest = scene.humanoids.GetComponent((Entity)args.userdata);
|
||||
if (humanoid_dest == nullptr)
|
||||
return;
|
||||
|
||||
bool retarget_valid = false;
|
||||
Scene retarget_scene;
|
||||
Entity retarget_entity = CreateEntity();
|
||||
AnimationComponent& animation = retarget_scene.animations.Create(retarget_entity);
|
||||
animation = *animation_source;
|
||||
animation.channels.clear();
|
||||
animation.samplers.clear();
|
||||
|
||||
NameComponent name;
|
||||
const NameComponent* name_source = scene.names.GetComponent(entity);
|
||||
if (name_source != nullptr)
|
||||
Entity retarget_entity = scene.RetargetAnimation((Entity)args.userdata, entity, true);
|
||||
if (retarget_entity != INVALID_ENTITY)
|
||||
{
|
||||
name.name += name_source->name;
|
||||
}
|
||||
scene.names.Create(retarget_entity) = name;
|
||||
|
||||
TransformComponent transform;
|
||||
const TransformComponent* transform_source = scene.transforms.GetComponent(entity);
|
||||
if (transform_source != nullptr)
|
||||
{
|
||||
transform = *transform_source;
|
||||
}
|
||||
scene.transforms.Create(retarget_entity) = transform;
|
||||
|
||||
scene.Component_Attach(retarget_entity, (Entity)args.userdata);
|
||||
|
||||
for (auto& channel : animation_source->channels)
|
||||
{
|
||||
bool found = false;
|
||||
for (size_t i = 0; (i < scene.humanoids.GetCount()) && !found; ++i)
|
||||
NameComponent name;
|
||||
const NameComponent* name_source = scene.names.GetComponent(entity);
|
||||
if (name_source != nullptr)
|
||||
{
|
||||
const HumanoidComponent& humanoid_source = scene.humanoids[i];
|
||||
for (size_t humanoidBoneIndex = 0; humanoidBoneIndex < arraysize(humanoid_source.bones); ++humanoidBoneIndex)
|
||||
{
|
||||
Entity bone_source = humanoid_source.bones[humanoidBoneIndex];
|
||||
if (bone_source == channel.target)
|
||||
{
|
||||
retarget_valid = true;
|
||||
found = true;
|
||||
Entity bone_dest = humanoid_dest->bones[humanoidBoneIndex];
|
||||
|
||||
auto& retarget_channel = animation.channels.emplace_back();
|
||||
retarget_channel = channel;
|
||||
retarget_channel.target = bone_dest;
|
||||
retarget_channel.samplerIndex = (int)animation.samplers.size();
|
||||
|
||||
auto& sampler = animation_source->samplers[channel.samplerIndex];
|
||||
|
||||
auto& retarget_sampler = animation.samplers.emplace_back();
|
||||
retarget_sampler = sampler;
|
||||
retarget_sampler.backwards_compatibility_data = {};
|
||||
|
||||
Entity retarget_animation_data_entity = CreateEntity();
|
||||
auto& retarget_animation_data = retarget_scene.animation_datas.Create(retarget_animation_data_entity);
|
||||
retarget_sampler.data = retarget_animation_data_entity;
|
||||
|
||||
auto& animation_data = scene.animation_datas.Contains(sampler.data) ? *scene.animation_datas.GetComponent(sampler.data) : sampler.backwards_compatibility_data;
|
||||
retarget_animation_data = animation_data;
|
||||
|
||||
TransformComponent* transform_source = scene.transforms.GetComponent(bone_source);
|
||||
TransformComponent* transform_dest = scene.transforms.GetComponent(bone_dest);
|
||||
if (transform_source != nullptr && transform_dest != nullptr)
|
||||
{
|
||||
XMMATRIX bindMatrix = ComputeWorldMatrixRecursive(scene, bone_source, transform_source->GetLocalMatrix());
|
||||
XMMATRIX inverseBindMatrix = XMMatrixInverse(nullptr, bindMatrix);
|
||||
XMMATRIX targetMatrix = ComputeWorldMatrixRecursive(scene, bone_dest, transform_dest->GetLocalMatrix());
|
||||
XMMATRIX inverseParentMatrix = ComputeInverseParentMatrixRecursive(scene, bone_dest);
|
||||
XMVECTOR S, R, T; // matrix decompose destinations
|
||||
|
||||
switch (channel.path)
|
||||
{
|
||||
case AnimationComponent::AnimationChannel::Path::SCALE:
|
||||
for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 3)
|
||||
{
|
||||
XMFLOAT3* data = (XMFLOAT3*)&retarget_animation_data.keyframe_data[offset];
|
||||
TransformComponent transform = *transform_source;
|
||||
transform.scale_local = *data;
|
||||
XMMATRIX localMatrix = inverseBindMatrix * ComputeWorldMatrixRecursive(scene, bone_source, transform.GetLocalMatrix());
|
||||
localMatrix = targetMatrix * localMatrix * inverseParentMatrix;
|
||||
XMMatrixDecompose(&S, &R, &T, localMatrix);
|
||||
XMStoreFloat3(data, S);
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::ROTATION:
|
||||
for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 4)
|
||||
{
|
||||
XMFLOAT4* data = (XMFLOAT4*)&retarget_animation_data.keyframe_data[offset];
|
||||
TransformComponent transform = *transform_source;
|
||||
transform.rotation_local = *data;
|
||||
XMMATRIX localMatrix = inverseBindMatrix * ComputeWorldMatrixRecursive(scene, bone_source, transform.GetLocalMatrix());
|
||||
localMatrix = targetMatrix * localMatrix * inverseParentMatrix;
|
||||
XMMatrixDecompose(&S, &R, &T, localMatrix);
|
||||
XMStoreFloat4(data, R);
|
||||
}
|
||||
break;
|
||||
case AnimationComponent::AnimationChannel::Path::TRANSLATION:
|
||||
for (size_t offset = 0; offset < retarget_animation_data.keyframe_data.size(); offset += 3)
|
||||
{
|
||||
XMFLOAT3* data = (XMFLOAT3*)&retarget_animation_data.keyframe_data[offset];
|
||||
TransformComponent transform = *transform_source;
|
||||
transform.translation_local = *data;
|
||||
XMMATRIX localMatrix = inverseBindMatrix * ComputeWorldMatrixRecursive(scene, bone_source, transform.GetLocalMatrix());
|
||||
localMatrix = targetMatrix * localMatrix * inverseParentMatrix;
|
||||
XMMatrixDecompose(&S, &R, &T, localMatrix);
|
||||
XMStoreFloat3(data, T);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
name.name += name_source->name;
|
||||
}
|
||||
}
|
||||
if (retarget_valid)
|
||||
{
|
||||
scene.Merge(retarget_scene);
|
||||
scene.names.Create(retarget_entity) = name;
|
||||
|
||||
editor->optionsWnd.RefreshEntityTree();
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user