BT410 Phase 5.3.4: Mech::ResolveJoint + skeleton-joint binding (torso twist)
Reconstructs the shared skeleton-joint resolver and binds the first subsystem (Torso) to the live skeleton. - Mech::ResolveJoint(name) (mech.cpp @00424b60): GetSegment(name) -> segment joint index -> GetJointSubsystem()->GetJoint(idx). - Verified the JointedMover base streams the full skeleton headlessly: jointCount=19, 40 named segments (gun joints jointlgun/jointrgun, shoulders, hip, leg joints jointlthigh..jointrankle). BT_MECH_LOG [skel] summary; BT_SKEL_DUMP lists every segment/jointIdx. - Torso resolves + binds its twist joint (ResolveJoint(torsoHorizontalJoint)) and pushes currentTwist onto it via Joint::SetRotation (hinge->Radian, ball->EulerAngles yaw). The TEST.EGG mech has a fixed torso (horizJoint='', enabled=0) so its twist path is inert -- correct + guarded, zero Fail. Next joint piece: elevation -> gun joints (jointlgun/jointrgun, the fixed-torso aim mechanism) via weapon-joint binding; then gait leg animation. Both are VISIBLE only under the renderer; headlessly the joint transforms are loggable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -50,6 +50,8 @@
|
||||
#include <mislanch.hpp> // MissileLauncher
|
||||
#include <ammobin.hpp> // AmmoBin
|
||||
#include <mechmppr.hpp> // MechControlsMapper -- the drive reads its demands
|
||||
#include <joint.hpp> // Joint / JointSubsystem -- ResolveJoint
|
||||
#include <segment.hpp> // EntitySegment -- the skeleton segment table
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
@@ -327,6 +329,29 @@ Mech::Mech(
|
||||
{
|
||||
DEBUG_STREAM << "[mech] segment walk done: subsystemCount=" << subsystemCount
|
||||
<< " weaponCount=" << weaponCount << endl << flush;
|
||||
|
||||
//
|
||||
// Skeleton summary: confirm the JointedMover base streamed the segment /
|
||||
// joint tables (so joint-driven aim / animation / damage has something to
|
||||
// bind to). BT_SKEL_DUMP additionally lists every segment name + joint
|
||||
// index (used to identify the twist / gun / leg joints).
|
||||
//
|
||||
JointSubsystem *joints = GetJointSubsystem();
|
||||
DEBUG_STREAM << "[skel] jointSubsystem=" << (void *)joints
|
||||
<< " jointCount=" << (joints ? joints->GetJointCount() : -1) << endl << flush;
|
||||
if (getenv("BT_SKEL_DUMP"))
|
||||
{
|
||||
EntitySegment::SegmentTableIterator it(segmentTable);
|
||||
EntitySegment *seg;
|
||||
int i = 0;
|
||||
while ((seg = it.ReadAndNext()) != NULL && i < 60)
|
||||
{
|
||||
DEBUG_STREAM << "[skel] seg[" << i << "] name=" << seg->GetName()
|
||||
<< " jointIdx=" << seg->GetJointIndex() << endl;
|
||||
++i;
|
||||
}
|
||||
DEBUG_STREAM << "[skel] segments=" << i << endl << flush;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -416,6 +441,40 @@ void
|
||||
subsystemArray[0] = subsystem;
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// ResolveJoint -- the shared skeleton-joint resolver (mech.cpp @00424b60).
|
||||
// A subsystem hands us the joint NAME from its resource; we look up the
|
||||
// skeleton segment of that name, read its joint index, and fetch the animated
|
||||
// Joint from the JointSubsystem. NULL for an empty/unknown name or a mech with
|
||||
// no skeleton/joint subsystem.
|
||||
//#############################################################################
|
||||
//
|
||||
Joint*
|
||||
Mech::ResolveJoint(const char *joint_name)
|
||||
{
|
||||
Check(this);
|
||||
|
||||
if (joint_name == NULL || joint_name[0] == '\0')
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EntitySegment *segment = GetSegment(CString(joint_name));
|
||||
if (segment == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JointSubsystem *joints = GetJointSubsystem();
|
||||
if (joints == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return joints->GetJoint(segment->GetJointIndex());
|
||||
}
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// Simulate -- the mech's per-frame body Performance (the Mover locomotion tick).
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
class PlatformTool;
|
||||
class MechControlsMapper;
|
||||
class Mech__DamageZone;
|
||||
class Joint;
|
||||
|
||||
//###########################################################################
|
||||
//######################### Mech Model Resource #########################
|
||||
@@ -187,6 +188,14 @@
|
||||
void
|
||||
SetMappingSubsystem(Subsystem *mapper);
|
||||
|
||||
//
|
||||
// Resolve a skeleton joint by name (the shared resolver the subsystems
|
||||
// use to bind their animated joints -- Torso twist, etc.):
|
||||
// GetSegment(name) -> segment jointIndex -> JointSubsystem::GetJoint.
|
||||
//
|
||||
Joint*
|
||||
ResolveJoint(const char *joint_name);
|
||||
|
||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// Per-frame simulation (the mech's body Performance; installed by the ctor
|
||||
// and dispatched each frame from Simulation::PerformAndWatch).
|
||||
|
||||
@@ -237,3 +237,30 @@ animation clips), a later wave.
|
||||
Still deferred: gait-clip-exact advance + leg animation (needs the animation
|
||||
subsystem + renderer), model-resource/LoadLocomotionClips sourcing, terrain
|
||||
drop, torso/free-look aiming (Torso/HUD analog axes), telemetry filters.
|
||||
|
||||
## PHASE 5.3 INCREMENT 3+: skeleton-joint resolver + torso aim wiring (2026-07-21)
|
||||
|
||||
- Torso weapon-elevation aim reconstructed (`Torso::TorsoSimulation`, wired into
|
||||
`InterpretControls`): stick pitch → elevation, slew + clamp to the resource
|
||||
limit (verified: clamps at the authentic 20°). See TORSO.NOTES.md.
|
||||
- **`Mech::ResolveJoint(name)`** reconstructed (mech.cpp @00424b60): `GetSegment
|
||||
(name)` → segment joint index → `GetJointSubsystem()->GetJoint(idx)`. The
|
||||
shared skeleton-joint resolver the subsystems use to bind their animated
|
||||
joints.
|
||||
- **The skeleton is live headlessly** (verified, `BT_MECH_LOG` `[skel]` summary +
|
||||
`BT_SKEL_DUMP` per-segment list): the JointedMover base streams the full
|
||||
segment/joint tables — `jointCount=19`, 40 named segments including the gun
|
||||
joints (`jointlgun`/`jointrgun`), shoulders, hip, and all leg joints
|
||||
(`jointlthigh`…`jointrankle`). So joint-driven aim / gait / damage has real
|
||||
joints to bind to.
|
||||
- The Torso now resolves + binds its twist joint (`ResolveJoint(torsoHorizontal
|
||||
Joint)`) and pushes `currentTwist` onto it via `Joint::SetRotation` (hinge →
|
||||
Radian, ball → EulerAngles yaw). The bring-up TEST.EGG mech has a FIXED torso
|
||||
(`horizJoint=''`, `enabled=0`) so its twist path is inert — correct and
|
||||
guarded; a torso-twist mech would drive the joint.
|
||||
|
||||
Next joint piece: elevation → the gun joints (`jointlgun`/`jointrgun`) — the
|
||||
aim mechanism for fixed-torso mechs — which needs the weapon-joint binding
|
||||
(resolved from the weapon subsystems, not the torso resource). Then the gait
|
||||
leg-clip animation. Both become VISIBLE only under the renderer; headlessly the
|
||||
joint transforms are computable/loggable.
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
# include <mech.hpp>
|
||||
#endif
|
||||
|
||||
#if !defined(JOINT_HPP)
|
||||
# include <joint.hpp>
|
||||
#endif
|
||||
|
||||
Derivation
|
||||
Torso::ClassDerivations(
|
||||
PowerWatcher::ClassDerivations,
|
||||
@@ -67,11 +71,24 @@ Torso::Torso(
|
||||
horizontalEnabled = r->torsoHorizontalEnabled;
|
||||
|
||||
//
|
||||
// The named skeleton joints are resolved once the Mech skeleton link is
|
||||
// live (per-frame sim / phase-4 wiring); leave the handles null here.
|
||||
// Resolve the named torso-twist skeleton joints (twist body + shadow) from
|
||||
// the mech skeleton (now live). A mech with a fixed torso, or one whose
|
||||
// joint name is absent from the skeleton, resolves NULL and simply never
|
||||
// twists.
|
||||
//
|
||||
horizontalJointNode = NULL;
|
||||
horizontalShadowJointNode = NULL;
|
||||
horizontalJointNode = owner->ResolveJoint(r->torsoHorizontalJoint);
|
||||
horizontalShadowJointNode = owner->ResolveJoint(r->torsoHorizontalShadowJoint);
|
||||
|
||||
if (getenv("BT_MECH_LOG"))
|
||||
{
|
||||
DEBUG_STREAM << "[torso] horizJoint '" << r->torsoHorizontalJoint
|
||||
<< "' -> " << (void *)horizontalJointNode;
|
||||
if (horizontalJointNode != NULL)
|
||||
{
|
||||
DEBUG_STREAM << " type=" << (int)horizontalJointNode->GetJointType();
|
||||
}
|
||||
DEBUG_STREAM << " enabled=" << (int)horizontalEnabled << endl << flush;
|
||||
}
|
||||
|
||||
currentTwist = 0.0f;
|
||||
currentElevation = 0.0f;
|
||||
@@ -153,6 +170,26 @@ void
|
||||
? horizontalLimitRight : horizontalLimitLeft;
|
||||
if (currentTwist < lo) currentTwist = lo;
|
||||
if (currentTwist > hi) currentTwist = hi;
|
||||
|
||||
//
|
||||
// Push the twist onto the skeleton torso joint so the upper body rotates
|
||||
// on the model. Hinge joints take the scalar angle about their axis;
|
||||
// ball joints take it as the yaw component.
|
||||
//
|
||||
if (horizontalJointNode != NULL)
|
||||
{
|
||||
Joint::JointType jt = horizontalJointNode->GetJointType();
|
||||
if (jt == Joint::HingeXJointType
|
||||
|| jt == Joint::HingeYJointType
|
||||
|| jt == Joint::HingeZJointType)
|
||||
{
|
||||
horizontalJointNode->SetRotation(Radian(currentTwist));
|
||||
}
|
||||
else if (jt == Joint::BallJointType)
|
||||
{
|
||||
horizontalJointNode->SetRotation(EulerAngles(0.0f, currentTwist, 0.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Check_Fpu();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
//##################### Forward Class Declarations #######################
|
||||
class Mech;
|
||||
class Joint;
|
||||
|
||||
//###########################################################################
|
||||
//##################### Torso Model Resource ###########################
|
||||
@@ -117,8 +118,8 @@
|
||||
Scalar buttonAccelerationStart;
|
||||
int buttonRampActive;
|
||||
Scalar buttonRamp;
|
||||
void *horizontalJointNode;
|
||||
void *horizontalShadowJointNode;
|
||||
Joint *horizontalJointNode;
|
||||
Joint *horizontalShadowJointNode;
|
||||
Scalar currentTwist;
|
||||
Scalar currentElevation;
|
||||
Scalar analogElevationAxis;
|
||||
|
||||
@@ -24,11 +24,25 @@
|
||||
stick pitch 0.8 slews the elevation up and clamps at the authentic
|
||||
`verticalLimitTop` = 0.349 rad = **20°**. Zero Fail.
|
||||
|
||||
## Deferred (skeleton-link + render wave)
|
||||
## Skeleton-joint binding (2026-07-21)
|
||||
|
||||
`TorsoSimulation` advances the SCALAR aim state (elevation/twist), which the HUD
|
||||
reticle / weapon-aim read. APPLYING those angles onto the skeleton — rotating
|
||||
the `horizontalJointNode` (torso twist) and the elevation joint so the model's
|
||||
torso/gun barrels visibly aim — is deferred with the skeleton-joint-link +
|
||||
renderer wave (the joint handles are resolved null for now). `TorsoCopySimulation`
|
||||
(replicant console-driven aim) also stays staged.
|
||||
The twist joints are now RESOLVED and BOUND: the ctor calls
|
||||
`owner->ResolveJoint(torsoHorizontalJoint / torsoHorizontalShadowJoint)` (the
|
||||
skeleton is live — see MECH.NOTES.md), stores them as `Joint*`, and
|
||||
`TorsoSimulation` pushes `currentTwist` onto `horizontalJointNode` via
|
||||
`Joint::SetRotation` (hinge → `Radian`; ball → `EulerAngles` yaw).
|
||||
|
||||
The bring-up TEST.EGG mech has a FIXED torso (`horizJoint=''`, `enabled=0`), so
|
||||
its twist path is inert — correctly guarded (null joint → no apply, no crash). A
|
||||
torso-twist mech drives the joint. Verified headlessly; the VISUAL rotation
|
||||
needs the renderer.
|
||||
|
||||
## Deferred
|
||||
|
||||
- **Elevation → gun joints.** This mech aims by pitching the guns
|
||||
(`jointlgun`/`jointrgun`), not twisting the torso. `currentElevation` still
|
||||
only advances the scalar state; applying it to the gun joints needs the
|
||||
weapon-joint binding (resolved from the weapon subsystems, not the torso
|
||||
resource) — the next joint piece.
|
||||
- HUD free-aim slew, look/eyepoint commit, `TorsoCopySimulation` (replicant
|
||||
console-driven aim) — still staged.
|
||||
|
||||
Reference in New Issue
Block a user