Audio Phase 4d (AUDIO_FIDELITY F5): footStep is the authored CONTACT LEVEL
The binary's per-frame perf (disasm @0x4a9e80-0x4a9eb6 leg / @0x4aba86- 0x4abab9 body) computes footStep@0x394 = (jointlocal.y <= *footStepThreshold) with the threshold pointer = SequenceController+0x20 = &ANI hdr[2] -- the header word the engine's own AnimationInstance captures (JMOVER.cpp:1415) and our SelectSequence skipped. States 0/1 retain the value. Port: SelectSequence captures hdr[2]; PerformAndWatch evaluates the contact level per frame from the cached jointlocal root joint (leg channel drives the pose); the 150 ms clip-transition pulse + decay are RETIRED (steps fired at clip boundaries with a fixed width; clips whose root crosses twice or never counted wrong). Live verification (30s walk): the authored threshold decodes real (-0.232 root height); rootY oscillates across it per stride (-0.26 contact / -0.19 swing) with clean 0->1->0 transitions per leg state (5/6/7); 34 footfall deliveries at stride rate with per-stride varying gains. Also closes F23(2): the 17 authored AnimationState trigger states fire empirically -- the EngineShiftFwd/Rev heard during the gait dead-band hunt WERE states 10/11/14/15; the runtime clip numbering is correct. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
9dcb4752de
commit
0e2401fb52
@@ -930,7 +930,8 @@ Mech::Mech(
|
||||
collisionState.SetState(0); // NoCollisionState
|
||||
collisionTemporaryState = 0;
|
||||
footStep = 0; // FootStep pulse (audio trigger)
|
||||
footStepDecay = 0;
|
||||
footStepRootJoint = 0; // (F5) resolved on first contact eval
|
||||
footStepRootResolved = 0;
|
||||
fwdSpeedFiltered = 0.0f; // smoothed published velocity (see mech4 derive)
|
||||
vertSpeedFiltered = 0.0f;
|
||||
{ extern void *g_btFootStepAddr; g_btFootStepAddr = &footStep; } // DIAG: watcher-poll tracer target
|
||||
|
||||
@@ -854,11 +854,15 @@ protected:
|
||||
Scalar vertSpeedFiltered; // per-frame position derivative pulses per stride and
|
||||
// flapped the audio's speed triggers -- engine-hum
|
||||
// restarts on every footfall)
|
||||
int footStepDecay; // MILLISECONDS until the pulse clears (time-based:
|
||||
// the watcher polls ~10Hz and strides re-pulse ~2Hz,
|
||||
// so the pulse must be one poll-period wide but end
|
||||
// well before the next stride -- match=1 fires on the
|
||||
// CHANGE to 1, so footStep must return to 0 in between)
|
||||
// (AUDIO_FIDELITY F5) footStep is the authored CONTACT LEVEL, not a
|
||||
// clip-transition pulse: per frame (leg gait state not 0/1),
|
||||
// footStep = (jointlocal.y <= *clip footStepThreshold) -- binary
|
||||
// disasm @0x4a9e80-0x4a9eb6 (leg) / @0x4aba86-0x4abab9 (body); the
|
||||
// threshold is the ANI header word[2] SelectSequence now captures.
|
||||
// States 0/1 RETAIN the previous value (binary skip). The old 150 ms
|
||||
// pulse + decay members are retired.
|
||||
Joint *footStepRootJoint; // cached "jointlocal" (binary mech+0x5C8)
|
||||
int footStepRootResolved;
|
||||
// Radar/map gauge attributes (binary @0x404/0x408/0x40c/0x3f8). The map
|
||||
// widget reads position/angle as POINTERS into the mech's live origin, so
|
||||
// radarLinearPosition/radarAngularPosition point at localOrigin.{linear,
|
||||
|
||||
@@ -262,24 +262,20 @@ void
|
||||
// knockdown/death (those get collision/fall audio, not a step). Raise the
|
||||
// Logical the FootStep AudioLogicalTrigger polls; IntegrateMotion decays it
|
||||
// back to 0 a few frames later so each step is a clean rising edge. [T2]
|
||||
if (state >= 1 && state <= 0x17)
|
||||
{
|
||||
footStep = 1;
|
||||
footStepDecay = 150; // ms: ~1.5 poll periods ON, OFF well before the next stride
|
||||
if (getenv("BT_AUDIO_LOG")) { static int s_fs=0; if (s_fs++<40)
|
||||
DEBUG_STREAM << "[audio] footstep pulse (clip " << state << ")\n" << std::flush; }
|
||||
|
||||
// (AUDIO_FIDELITY F19) the old STEP-INTENSITY SEND is GONE. Its
|
||||
// premise ("the mixer feed was lost game code") was wrong: the feed is
|
||||
// WHOLLY AUTHORED -- LocalAcceleration |linear| [0,10] -> ctl100 and
|
||||
// LocalVelocity |linear| [0,0.6] -> ctl101 scale watchers drive the
|
||||
// footstep volume mixer (0.4 base while moving + the per-stride
|
||||
// acceleration kick). The port's gap was never publishing
|
||||
// localAcceleration; mech4.cpp now derives it exactly as the binary
|
||||
// does (d(averaged velocity)/dt, part_012.c:15186-15195), and the
|
||||
// invented broadcast fought that live chain (it zeroed the sibling
|
||||
// input the authored scale had just written).
|
||||
}
|
||||
// (AUDIO_FIDELITY F5) the old per-transition footStep PULSE is GONE:
|
||||
// footStep is the authored CONTACT LEVEL, evaluated per frame in
|
||||
// Mech::PerformAndWatch from jointlocal.y vs the clip's authored
|
||||
// threshold (ANI hdr[2]) -- steps now fire at actual root-height
|
||||
// contact crossings, not at clip boundaries with a fixed width.
|
||||
// (F19) the old STEP-INTENSITY SEND is also GONE. Its premise ("the
|
||||
// mixer feed was lost game code") was wrong: the feed is WHOLLY
|
||||
// AUTHORED -- LocalAcceleration |linear| [0,10] -> ctl100 and
|
||||
// LocalVelocity |linear| [0,0.6] -> ctl101 scale watchers drive the
|
||||
// footstep volume mixer (0.4 base while moving + the per-stride
|
||||
// acceleration kick). The port's gap was never publishing
|
||||
// localAcceleration; mech4.cpp derives it exactly as the binary does
|
||||
// (d(averaged velocity)/dt, part_012.c:15186-15195), and the invented
|
||||
// broadcast fought that live chain.
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -5274,18 +5274,32 @@ void
|
||||
// (AudioLogicalTrigger FootStep, AudioMotionScale/Trigger LocalVelocity, ...)
|
||||
// never executed -- only the PUSHED StateIndicator watchers fired. That's
|
||||
// why footsteps/motion-scaled sounds were silent while state sounds worked. [T0]
|
||||
// FootStep pulse decay (see SetBodyAnimation): clear the contact flag ~150ms
|
||||
// after the plant so the FootStep matcher (fires on the CHANGE to 1) sees a 0
|
||||
// before the next stride re-pulses. Lives HERE (not IntegrateMotion, which the
|
||||
// current gait path never calls) so it provably ticks every mech frame, right
|
||||
// before the watcher poll below reads it. Time-based -- tick-rate immune.
|
||||
if (footStepDecay > 0)
|
||||
// (AUDIO_FIDELITY F5) footStep is the authored CONTACT LEVEL (binary
|
||||
// disasm @0x4a9e80-0x4a9eb6): while the leg channel's gait state is not
|
||||
// Standing(0)/1, footStep = (jointlocal.y <= *clip threshold); states
|
||||
// 0/1 RETAIN the previous value. The FootStep matcher fires on the
|
||||
// CHANGE to 1 -- one clean rising edge per actual foot plant, timed by
|
||||
// the clip's authored root-height crossing (replaces the 150 ms
|
||||
// transition pulse, which fired at clip boundaries with a fixed width).
|
||||
if (legAnimationState != 0 && legAnimationState != 1)
|
||||
{
|
||||
footStepDecay -= (int)(dt * 1000.0f);
|
||||
if (footStepDecay <= 0)
|
||||
if (!footStepRootResolved)
|
||||
{
|
||||
footStepDecay = 0;
|
||||
footStep = 0;
|
||||
footStepRootResolved = 1;
|
||||
footStepRootJoint = ResolveJoint("jointlocal");
|
||||
}
|
||||
Scalar *fs_thresh = legAnimation.footStepThreshold;
|
||||
if (footStepRootJoint != 0 && fs_thresh != 0)
|
||||
{
|
||||
int contact =
|
||||
(footStepRootJoint->GetTranslation().y <= *fs_thresh) ? 1 : 0;
|
||||
if (getenv("BT_AUDIO_LOG")) { static int s_fc=0;
|
||||
if (contact != footStep && s_fc++ < 40)
|
||||
DEBUG_STREAM << "[audio] footstep contact " << footStep << "->" << contact
|
||||
<< " rootY=" << footStepRootJoint->GetTranslation().y
|
||||
<< " thresh=" << *fs_thresh << " legState=" << legAnimationState
|
||||
<< "\n" << std::flush; }
|
||||
footStep = contact;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -270,6 +270,11 @@ struct SequenceController
|
||||
// --- clip data parsed by SelectSequence (keyframe accessors read by mech3) ---
|
||||
int keyframeCount; // frameCount (binary +0x14)
|
||||
int jointCount; // animated joints (binary +0x18)
|
||||
// (AUDIO_FIDELITY F5) the ANI header's footstep CONTACT threshold -- the
|
||||
// clip-authored root height below which the foot is planted. The engine's
|
||||
// own AnimationInstance captures the same word (JMOVER.cpp:1415); the
|
||||
// binary's per-frame perf compares jointlocal.y against it (+0x20).
|
||||
Scalar *footStepThreshold; // &hdr[2] (binary +0x20)
|
||||
int *jointIndices; // keyframe->skeleton map (binary +0x1c)
|
||||
Scalar *keyframeTimes; // frameTimes[frameCount] (binary +0x24)
|
||||
void *keyframeBase; // keyframe pose data base (binary +0x28)
|
||||
@@ -286,10 +291,10 @@ struct SequenceController
|
||||
unsigned cbArg2, cbArg3; // finished-callback context words (binary +0x4c/0x50)
|
||||
|
||||
SequenceController()
|
||||
: keyframeCount(0), jointCount(0), jointIndices(0), keyframeTimes(0),
|
||||
keyframeBase(0), keyframeCursor(0), keyframeData(0), currentFrame(0),
|
||||
currentTime(0.0f), jointSubsystem(0), owner(0), clipResource(0),
|
||||
cbCode(0), cbArg2(0), cbArg3(0) {}
|
||||
: keyframeCount(0), jointCount(0), footStepThreshold(0), jointIndices(0),
|
||||
keyframeTimes(0), keyframeBase(0), keyframeCursor(0), keyframeData(0),
|
||||
currentFrame(0), currentTime(0.0f), jointSubsystem(0), owner(0),
|
||||
clipResource(0), cbCode(0), cbArg2(0), cbArg3(0) {}
|
||||
|
||||
void Init(Mech *owner_mech); // @00427768 (ctor logic)
|
||||
void SelectSequence(int clip_id, void *cb, unsigned a2, unsigned a3); // @004277a8
|
||||
|
||||
@@ -149,6 +149,10 @@ void
|
||||
int *hdr = (int *)desc->resourceAddress; // resource data (binary: *(desc+0x3c))
|
||||
keyframeCount = hdr[0]; // @0x14 frameCount
|
||||
jointCount = hdr[1]; // @0x18 jointCount
|
||||
// (AUDIO_FIDELITY F5) hdr[2] = the clip's authored footstep contact
|
||||
// threshold (root height); previously skipped. Same capture as the
|
||||
// engine's AnimationInstance (JMOVER.cpp:1415).
|
||||
footStepThreshold = (Scalar *)(hdr + 2); // @0x20
|
||||
|
||||
int *p = hdr + 3; // jointIndices follow hdr[0..2]
|
||||
jointIndices = p; // @0x1c (jointCount ints)
|
||||
|
||||
Reference in New Issue
Block a user