Skip to content

Commit

Permalink
Root Motion (#799)
Browse files Browse the repository at this point in the history
  • Loading branch information
Almahmudrony authored Feb 10, 2024
1 parent 70a467c commit 77e7edd
Show file tree
Hide file tree
Showing 10 changed files with 308 additions and 4 deletions.
88 changes: 88 additions & 0 deletions Editor/AnimationWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -927,11 +927,68 @@ void AnimationWindow::Create(EditorComponent* _editor)
});
AddWidget(&retargetCombo);

// Root Motion Tick
rootMotionCheckBox.Create("RootMotion: ");
rootMotionCheckBox.SetTooltip("Toggle root bone animation.");
rootMotionCheckBox.SetSize(XMFLOAT2(hei, hei));
//rootMotionCheckBox.SetPos(XMFLOAT2(x, y += step));
rootMotionCheckBox.OnClick([&](wi::gui::EventArgs args) {
AnimationComponent* animation = editor->GetCurrentScene().animations.GetComponent(entity);
if (animation != nullptr)
{
if (args.bValue) {
animation->RootMotionOn();
}
else {
animation->RootMotionOff();
}
}
});
rootMotionCheckBox.SetCheckText(ICON_CHECK);
AddWidget(&rootMotionCheckBox);
// Root Bone selector
rootBoneComboBox.Create("Root Bone: ");
rootBoneComboBox.SetSize(XMFLOAT2(wid, hei));
rootBoneComboBox.SetPos(XMFLOAT2(x, y));
rootBoneComboBox.SetEnabled(false);
rootBoneComboBox.OnSelect([&](wi::gui::EventArgs args) {
AnimationComponent* animation = editor->GetCurrentScene().animations.GetComponent(entity);
if (animation != nullptr)
{
Entity ent = (Entity)args.userdata;
if (ent != wi::ecs::INVALID_ENTITY)
{
animation->rootMotionBone = ent;
}
else
{
animation->rootMotionBone = wi::ecs::INVALID_ENTITY;
}
}
});
rootBoneComboBox.SetTooltip("Choose the root bone to evaluate root motion from.");
AddWidget(&rootBoneComboBox);


SetMinimized(true);
SetVisible(false);

}
// Example function to check if an entity already exists in the list
static bool EntityExistsInList(Entity entity, std::vector<Entity> entityList)
{
// Iterate through the list of entities
for (Entity& e : entityList)
{
if (e == entity)
{
// Entity found in the list
return true;
}
}
// Entity not found in the list
return false;
}

void AnimationWindow::SetEntity(Entity entity)
{
Expand All @@ -944,6 +1001,32 @@ void AnimationWindow::SetEntity(Entity entity)
{
this->entity = entity;
RefreshKeyframesList();

rootBoneComboBox.ClearItems();
rootBoneComboBox.AddItem("NO ROOT BONE " ICON_DISABLED, wi::ecs::INVALID_ENTITY);
if (animation != nullptr)
{
// Define a list of entities
std::vector<Entity> bone_list;
// Add items to root bone name combo box.
for (const AnimationComponent::AnimationChannel& channel : animation->channels)
{
if (channel.path == AnimationComponent::AnimationChannel::Path::TRANSLATION ||
channel.path == AnimationComponent::AnimationChannel::Path::ROTATION) {

if (!EntityExistsInList(channel.target, bone_list))
{
bone_list.push_back(channel.target);
const NameComponent* name = scene.names.GetComponent(channel.target);
if (name != nullptr)
{
rootBoneComboBox.AddItem(name->name, channel.target);
}
}
}
}
bone_list.clear();
}
}
}
}
Expand Down Expand Up @@ -986,6 +1069,9 @@ void AnimationWindow::Update()

loopedCheckBox.SetCheck(animation.IsLooped());

rootMotionCheckBox.SetCheck(animation.IsRootMotion());
rootBoneComboBox.SetSelectedByUserdataWithoutCallback(animation.rootMotionBone);

timerSlider.SetRange(animation.start, animation.end);
timerSlider.SetValue(animation.timer);
amountSlider.SetValue(animation.amount);
Expand Down Expand Up @@ -1213,5 +1299,7 @@ void AnimationWindow::ResizeLayout()
add(endInput);
add(recordCombo);
add(retargetCombo);
add(rootMotionCheckBox);
add(rootBoneComboBox);
add_fullwidth(keyframesList);
}
3 changes: 3 additions & 0 deletions Editor/AnimationWindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class AnimationWindow : public wi::gui::Window

wi::gui::ComboBox retargetCombo;

wi::gui::CheckBox rootMotionCheckBox;
wi::gui::ComboBox rootBoneComboBox;

void Update();

void RefreshKeyframesList();
Expand Down
71 changes: 70 additions & 1 deletion WickedEngine/wiMath_BindLua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,6 +622,10 @@ namespace wi::lua
lunamethod(Matrix_BindLua, Multiply),
lunamethod(Matrix_BindLua, Transpose),
lunamethod(Matrix_BindLua, Inverse),

lunamethod(Matrix_BindLua, GetForward),
lunamethod(Matrix_BindLua, GetUp),
lunamethod(Matrix_BindLua, GetRight),
{ NULL, NULL }
};
Luna<Matrix_BindLua>::PropertyType Matrix_BindLua::properties[] = {
Expand Down Expand Up @@ -910,7 +914,72 @@ namespace wi::lua
wi::lua::SError(L, "Inverse(Matrix m) not enough arguments!");
return 0;
}

static XMVECTOR GetForward(XMMATRIX* _m)
{
XMVECTOR V = XMVectorSet(XMVectorGetX(_m->r[2]), XMVectorGetY(_m->r[2]), XMVectorGetZ(_m->r[2]), 0.0f);
return V;
}
int Matrix_BindLua::GetForward(lua_State* L)
{
int argc = wi::lua::SGetArgCount(L);
if (argc > 0)
{
Matrix_BindLua* m1 = Luna<Matrix_BindLua>::lightcheck(L, 1);
if (m1)
{
XMMATRIX matrix = XMLoadFloat4x4(&m1->data);
XMVECTOR V = XMVectorSet(XMVectorGetX(matrix.r[2]), XMVectorGetY(matrix.r[2]), XMVectorGetZ(matrix.r[2]), 0.0f);
Luna<Vector_BindLua>::push(L, V);
return 1;
}
}
wi::lua::SError(L, "GetForward(Matrix m) not enough arguments!");
return 0;
}
static XMVECTOR GetUp(XMMATRIX* _m)
{
XMVECTOR V = XMVectorSet(XMVectorGetX(_m->r[1]), XMVectorGetY(_m->r[1]), XMVectorGetZ(_m->r[1]), 0.0f);
return V;
}
int Matrix_BindLua::GetUp(lua_State* L)
{
int argc = wi::lua::SGetArgCount(L);
if (argc > 0)
{
Matrix_BindLua* m1 = Luna<Matrix_BindLua>::lightcheck(L, 1);
if (m1)
{
XMMATRIX matrix = XMLoadFloat4x4(&m1->data);
XMVECTOR V = XMVectorSet(XMVectorGetX(matrix.r[1]), XMVectorGetY(matrix.r[1]), XMVectorGetZ(matrix.r[1]), 0.0f);
Luna<Vector_BindLua>::push(L, V);
return 1;
}
}
wi::lua::SError(L, "GetUp(Matrix m) not enough arguments!");
return 0;
}
static XMVECTOR GetRight(XMMATRIX* _m)
{
XMVECTOR V = XMVectorSet(XMVectorGetX(_m->r[0]), XMVectorGetY(_m->r[0]), XMVectorGetZ(_m->r[0]), 0.0f);
return V;
}
int Matrix_BindLua::GetRight(lua_State* L)
{
int argc = wi::lua::SGetArgCount(L);
if (argc > 0)
{
Matrix_BindLua* m1 = Luna<Matrix_BindLua>::lightcheck(L, 1);
if (m1)
{
XMMATRIX matrix = XMLoadFloat4x4(&m1->data);
XMVECTOR V = XMVectorSet(XMVectorGetX(matrix.r[0]), XMVectorGetY(matrix.r[0]), XMVectorGetZ(matrix.r[0]), 0.0f);
Luna<Vector_BindLua>::push(L, V);
return 1;
}
}
wi::lua::SError(L, "GetRight(Matrix m) not enough arguments!");
return 0;
}

void Matrix_BindLua::Bind()
{
Expand Down
4 changes: 4 additions & 0 deletions WickedEngine/wiMath_BindLua.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ namespace wi::lua
int Transpose(lua_State* L);
int Inverse(lua_State* L);

int GetForward(lua_State* L);
int GetUp(lua_State* L);
int GetRight(lua_State* L);

static void Bind();
};
struct MatrixProperty
Expand Down
74 changes: 72 additions & 2 deletions WickedEngine/wiScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,9 @@ namespace wi::scene
// The interpolated raw values will be blended on top of component values:
const float t = animation.amount;

// CheckIf this channel is the root motion bone or not.
const bool isRootBone = (animation.IsRootMotion() && animation.rootMotionBone != wi::ecs::INVALID_ENTITY && (target_transform == transforms.GetComponent(animation.rootMotionBone)));

if (target_transform != nullptr)
{
target_transform->SetDirty();
Expand Down Expand Up @@ -2117,7 +2120,40 @@ namespace wi::scene
}
}
const XMVECTOR T = XMVectorLerp(aT, bT, t);
XMStoreFloat3(&target_transform->translation_local, T);
if (!isRootBone)
{
// Not root motion bone.
XMStoreFloat3(&target_transform->translation_local, T);
}
else
{
if (XMVector4Equal(animation.rootPrevTranslation, animation.INVALID_VECTOR) || animation.end < animation.prevLocTimer)
{
// If root motion bone.
animation.rootPrevTranslation = T;
}

XMVECTOR rotation_quat = animation.rootPrevRotation;

if (XMVector4Equal(animation.rootPrevRotation, animation.INVALID_VECTOR) || animation.end < animation.prevRotTimer)
{
// If root motion bone.
rotation_quat = XMLoadFloat4(&target_transform->rotation_local);
}

const XMVECTOR root_trans = XMVectorSubtract(T, animation.rootPrevTranslation);
XMVECTOR inverseQuaternion = XMQuaternionInverse(rotation_quat);
XMVECTOR rotatedDirectionVector = XMVector3Rotate(root_trans, inverseQuaternion);

XMMATRIX mat = XMLoadFloat4x4(&target_transform->world);
rotatedDirectionVector = XMVector4Transform(rotatedDirectionVector, mat);

// Store root motion offset
XMStoreFloat3(&animation.rootTranslationOffset, rotatedDirectionVector);
// If root motion bone.
animation.rootPrevTranslation = T;
animation.prevLocTimer = animation.timer;
}
}
break;
case AnimationComponent::AnimationChannel::Path::ROTATION:
Expand All @@ -2141,7 +2177,41 @@ namespace wi::scene
}
}
const XMVECTOR R = XMQuaternionSlerp(aR, bR, t);
XMStoreFloat4(&target_transform->rotation_local, R);
if (!isRootBone)
{
// Not root motion bone.
XMStoreFloat4(&target_transform->rotation_local, R);
}
else
{
if (XMVector4Equal(animation.rootPrevRotation, animation.INVALID_VECTOR) || animation.end < animation.prevRotTimer)
{
// If root motion bone.
animation.rootPrevRotation = R;
}

// Assuming q1 and q2 are the two quaternions you want to subtract
// // Let's say you want to find the relative rotation from q1 to q2
XMMATRIX mat1 = XMMatrixRotationQuaternion(animation.rootPrevRotation);
XMMATRIX mat2 = XMMatrixRotationQuaternion(R);
// Compute the relative rotation matrix by multiplying the inverse of the first rotation
// by the second rotation
XMMATRIX relativeRotationMatrix = XMMatrixMultiply(XMMatrixTranspose(mat1), mat2);
// Extract the quaternion representing the relative rotation
XMVECTOR relativeRotationQuaternion = XMQuaternionRotationMatrix(relativeRotationMatrix);

// Store root motion offset
XMStoreFloat4(&animation.rootRotationOffset, relativeRotationQuaternion);
// Swap Y and Z Axis for Unknown reason
const float Y = animation.rootRotationOffset.y;
animation.rootRotationOffset.y = animation.rootRotationOffset.z;
animation.rootRotationOffset.z = Y;

// If root motion bone.
animation.rootPrevRotation = R;
animation.prevRotTimer = animation.timer;
}

}
break;
case AnimationComponent::AnimationChannel::Path::SCALE:
Expand Down
2 changes: 1 addition & 1 deletion WickedEngine/wiScene.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ namespace wi::scene
wi::ecs::ComponentManager<EnvironmentProbeComponent>& probes = componentLibrary.Register<EnvironmentProbeComponent>("wi::scene::Scene::probes", 1); // version = 1
wi::ecs::ComponentManager<ForceFieldComponent>& forces = componentLibrary.Register<ForceFieldComponent>("wi::scene::Scene::forces", 1); // version = 1
wi::ecs::ComponentManager<DecalComponent>& decals = componentLibrary.Register<DecalComponent>("wi::scene::Scene::decals", 1); // version = 1
wi::ecs::ComponentManager<AnimationComponent>& animations = componentLibrary.Register<AnimationComponent>("wi::scene::Scene::animations", 1); // version = 1
wi::ecs::ComponentManager<AnimationComponent>& animations = componentLibrary.Register<AnimationComponent>("wi::scene::Scene::animations", 2); // version = 2
wi::ecs::ComponentManager<AnimationDataComponent>& animation_datas = componentLibrary.Register<AnimationDataComponent>("wi::scene::Scene::animation_datas");
wi::ecs::ComponentManager<EmittedParticleSystem>& emitters = componentLibrary.Register<EmittedParticleSystem>("wi::scene::Scene::emitters");
wi::ecs::ComponentManager<HairParticleSystem>& hairs = componentLibrary.Register<HairParticleSystem>("wi::scene::Scene::hairs");
Expand Down
35 changes: 35 additions & 0 deletions WickedEngine/wiScene_BindLua.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3685,6 +3685,11 @@ Luna<AnimationComponent_BindLua>::FunctionType AnimationComponent_BindLua::metho
lunamethod(AnimationComponent_BindLua, SetStart),
lunamethod(AnimationComponent_BindLua, GetEnd),
lunamethod(AnimationComponent_BindLua, SetEnd),
lunamethod(AnimationComponent_BindLua, IsRootMotion),
lunamethod(AnimationComponent_BindLua, RootMotionOn),
lunamethod(AnimationComponent_BindLua, RootMotionOff),
lunamethod(AnimationComponent_BindLua, GetRootTranslation),
lunamethod(AnimationComponent_BindLua, GetRootRotation),
{ NULL, NULL }
};
Luna<AnimationComponent_BindLua>::PropertyType AnimationComponent_BindLua::properties[] = {
Expand Down Expand Up @@ -3814,6 +3819,36 @@ int AnimationComponent_BindLua::SetEnd(lua_State* L)
return 0;
}

int AnimationComponent_BindLua::IsRootMotion(lua_State* L)
{
wi::lua::SSetBool(L, component->IsRootMotion());
return 1;
}

int AnimationComponent_BindLua::RootMotionOn(lua_State* L)
{
component->RootMotionOn();
return 0;
}

int AnimationComponent_BindLua::RootMotionOff(lua_State* L)
{
component->RootMotionOff();
return 0;
}

int AnimationComponent_BindLua::GetRootTranslation(lua_State* L)
{
Luna<Vector_BindLua>::push(L, component->rootTranslationOffset);
return 1;
}

int AnimationComponent_BindLua::GetRootRotation(lua_State* L)
{
Luna<Vector_BindLua>::push(L, component->rootRotationOffset);
return 1;
}




Expand Down
7 changes: 7 additions & 0 deletions WickedEngine/wiScene_BindLua.h
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,13 @@ namespace wi::lua::scene
int SetStart(lua_State* L);
int GetEnd(lua_State* L);
int SetEnd(lua_State* L);

// For Rootmotion
int IsRootMotion(lua_State* L);
int RootMotionOn(lua_State* L);
int RootMotionOff(lua_State* L);
int GetRootTranslation(lua_State* L);
int GetRootRotation(lua_State* L);
};

class MaterialComponent_BindLua
Expand Down
Loading

0 comments on commit 77e7edd

Please sign in to comment.