Skip to content

Animation blending and root motion#1837

Open
Sezzary wants to merge 372 commits intodevelopfrom
sezz/anim-blend
Open

Animation blending and root motion#1837
Sezzary wants to merge 372 commits intodevelopfrom
sezz/anim-blend

Conversation

@Sezzary
Copy link
Copy Markdown
Member

@Sezzary Sezzary commented Dec 13, 2025

Checklist

Links to issue(s) this pull request concerns (if applicable)

N/A

Description of pull request

Introduces the following new features:

  • Animation blending.
  • Root motion.

TODO:

  • Investigate crash when unholstering weapons.
  • Check if extra code is required for root motion at 60FPS.
  • Fix hair teleport.
  • Add more 2 or 3 frame blends for certain hardcoded animation dispatches.
  • Check if any more merge errors made it in.
  • Remove debug code when it's no longer needed.
  • Make TE write relevant data to the level.

Related TE PR: TombEngine/Tomb-Editor#1095

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds animation blending and root motion support to TombEngine’s animation, item, and renderer pipelines, including new curve math utilities and updated level serialization for the extra animation data.

Changes:

  • Introduces per-animation blend settings (frame count + Bezier curve) and per-item runtime blend state used by the renderer.
  • Implements root motion extraction/counteraction and applies it across rendering transforms and various gameplay queries/bounds.
  • Replaces legacy “keyframe interpolation” access patterns with direct Frames[...] / GetFrame(...) usage and renames/reshapes related structs/constants.

Reviewed changes

Copilot reviewed 48 out of 49 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
TombEngine/TombEngine.vcxproj Adds BezierCurve2 sources to the build.
TombEngine/Specific/level.cpp Reads new animation data (blend curves, fixed motion curves, per-frame root position).
TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.h Extends Lua API to allow specifying blend frames when setting animation.
TombEngine/Scripting/Internal/TEN/Objects/Moveable/MoveableObject.cpp Wires scripting animation changes to new blending-aware SetAnimation.
TombEngine/Scripting/Internal/TEN/Objects/Lara/LaraObject.cpp Minor cleanup to TestLaraPosition call.
TombEngine/Renderer/Structures/RendererMirror.h Formatting/whitespace normalization.
TombEngine/Renderer/Structures/RendererItem.h Renames bone arrays and interpolation buffers to new constants/names.
TombEngine/Renderer/RendererLara.cpp Applies root motion counteraction to Lara world matrix and updates animation update calls for new frame/blend path.
TombEngine/Renderer/RendererHelper.cpp Updates animation evaluation to use FrameData + optional blend data.
TombEngine/Renderer/RendererFrame.cpp Applies root motion counteraction to renderer item transforms and updates interpolation buffer handling.
TombEngine/Renderer/RendererEnums.h Renames MAX_BONES/MAX_BONE_WEIGHTS to BONE_COUNT_MAX/BONE_WEIGHT_COUNT_MAX.
TombEngine/Renderer/RendererDrawMenu.cpp Updates preview animation path to use Frames rather than keyframe interpolation data.
TombEngine/Renderer/RendererDrawEffect.cpp Updates effect attachment/root offsets to new Frames/RootPosition fields and renamed bone buffers.
TombEngine/Renderer/RendererDraw.cpp Updates skinning transforms/bone loops to new bone constants and renamed buffers.
TombEngine/Renderer/Renderer.h Updates UpdateAnimation signature to new frame/blend types.
TombEngine/Renderer/ConstantBuffers/ItemBuffer.h Updates constant buffer arrays to use BONE_COUNT_MAX.
TombEngine/Objects/Utils/object_helper.cpp Uses new Frames container for “has > 1 frame” checks.
TombEngine/Objects/TR5/Entity/tr5_guard.cpp Updates bounds query to GetFrame.
TombEngine/Objects/TR4/Trap/tr4_teethspike.cpp Updates bounds query to GetFrame.
TombEngine/Objects/TR4/Trap/tr4_joby_spikes.cpp Refactors flag locals and switches player height calc to OBB-based approach.
TombEngine/Objects/TR2/Entity/tr2_mercenary.cpp Minor comment formatting for deterministic shooting rate logic.
TombEngine/Objects/Generic/Object/rope.cpp Updates root offset access to GetFrame(...).RootPosition.
TombEngine/Objects/Generic/Object/Pushable/PushableStates.cpp Updates last-frame bounds query helper.
TombEngine/Objects/Generic/Object/Pushable/PushableCollision.cpp Updates root offset access to GetFrame(...).RootPosition.
TombEngine/Objects/Generic/Object/BridgeObject.cpp Updates local AABB access to Frames[].LocalAabb.
TombEngine/Math/Objects/GameBoundingBox.cpp Switches to GetFrame and applies root-motion counteraction translation in deprecated ctor.
TombEngine/Math/Objects/BezierCurve2.h Adds new 2D Bezier curve utility type used for blending and fixed motion.
TombEngine/Math/Objects/BezierCurve2.cpp Implements Bezier curve presets + evaluation (GetY via Newton-Raphson).
TombEngine/Math/Math.h Exposes BezierCurve2 through the umbrella math header.
TombEngine/Game/pickup/pickup.cpp Updates plinth bounds queries to Frames[...] and leaves a commented-out debug return.
TombEngine/Game/people.cpp Updates target bounds queries to GetFrame.
TombEngine/Game/items.h Renames/reshapes ItemInfo sub-structs; adds blending state + helpers; pulls in renderer bone constants.
TombEngine/Game/items.cpp Implements blending state capture (SetAnimBlend/DisableAnimBlend) and updates OBB calculation.
TombEngine/Game/effects/tomb4fx.cpp Updates bounds query to GetFrame.
TombEngine/Game/effects/hair.cpp Updates bounds query to Frames[...] / GetFrame.
TombEngine/Game/control/box.cpp Updates various bounds/frame queries to Frames/GetFrame.
TombEngine/Game/collision/collide_room.cpp Updates collision AABB bounds query to GetFrame.
TombEngine/Game/collision/collide_item.cpp Updates collision bounds queries to GetFrame/Frames[...] and formatting.
TombEngine/Game/Lara/lara_initialise.cpp Renames EntityAnimationData to MoveableAnimData in backup globals/copy.
TombEngine/Game/Lara/lara_helpers.cpp Updates many SetAnimation calls to include blend parameters/curves.
TombEngine/Game/Lara/lara_flare.cpp Refactors flare object/anim assignment (currently introduces an AnimObjectID mismatch).
TombEngine/Game/Lara/lara_fire.cpp Updates bounds query to GetFrame.
TombEngine/Game/Lara/lara_collide.cpp Updates several SetAnimation calls to blend-aware overloads and tweaks comments.
TombEngine/Game/Lara/lara.h Adds default player animation blend frame count constant.
TombEngine/Game/Lara/lara.cpp Adds a debug block forcing root-motion flags on Lara animations; updates a fall transition to blend.
TombEngine/Game/Animation/Commands.h Renames command base type and “MoveOrigin” -> “MoveRoot”.
TombEngine/Game/Animation/Commands.cpp Updates move-root execution and uses OBB-based height for Lara room update.
TombEngine/Game/Animation/Animation.h Reworks animation data model (frames, dispatch ranges, curves, flags) and updates API signatures.
TombEngine/Game/Animation/Animation.cpp Implements fixed motion + root motion extraction/counteraction and blending integration into animation updates.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +88 to +96
{
auto interpData = GetKeyframeInterpolationData(frameNumber);
return ((interpData.Alpha <= 0.5f) ? interpData.Keyframe0 : interpData.Keyframe1);
const auto& rootOrient = Frames[frameNumber].BoneOrientations.front();
const auto& prevRootOrient = Frames[frameNumber - 1].BoneOrientations.front();
auto rootRot = rootOrient - prevRootOrient;

rot = EulerAngles(
(Flags & (int)AnimFlags::RootMotionRotationX) ? rootRot.x : ANGLE(0.0f),
(Flags & (int)AnimFlags::RootMotionRotationY) ? rootRot.y : ANGLE(0.0f),
(Flags & (int)AnimFlags::RootMotionRotationZ) ? rootRot.z : ANGLE(0.0f));
Copy link

Copilot AI Mar 28, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AnimData::GetRootMotion() computes rootRot via rootOrient - prevRootOrient and then treats the quaternion components as Euler angles. Quaternion subtraction is not a valid way to derive relative rotation and will yield incorrect/root-motion rotations. Compute a delta rotation (e.g., delta = rootOrient * Inverse(prevRootOrient)), convert that delta to EulerAngles, then mask axes via the flags.

Copilot uses AI. Check for mistakes.
@Lwmte Lwmte added Renderer Renderer Module Ready to Test labels Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants