diff --git a/crates/skate-core/src/animation/playback_tree/selection_space.rs b/crates/skate-core/src/animation/playback_tree/selection_space.rs index af36818c..0f0ea7b3 100644 --- a/crates/skate-core/src/animation/playback_tree/selection_space.rs +++ b/crates/skate-core/src/animation/playback_tree/selection_space.rs @@ -23,8 +23,6 @@ pub struct SelectionSpace { pub parameters: Vec, pub candidates: Vec, pub selected: Option, - /// Host GetAnimTree callback has supplied the selected child's wrappers. - pub child_constructed: bool, values: Vec>, speed: f32, requested_time: f32, @@ -44,7 +42,6 @@ impl SelectionSpace { parameters, candidates, selected: None, - child_constructed: false, values, speed: 1.0, requested_time: 0.0, diff --git a/crates/skate-game/src/graph_host/motion_animation/selection_space_host.rs b/crates/skate-game/src/graph_host/motion_animation/selection_space_host.rs index 585a39af..ab92367d 100644 --- a/crates/skate-game/src/graph_host/motion_animation/selection_space_host.rs +++ b/crates/skate-game/src/graph_host/motion_animation/selection_space_host.rs @@ -1,4 +1,4 @@ -//! Deferred native GetAnimTree callback in SelectionSpace::SetAttributes. +//! Deferred raw-tree callback in SelectionSpace::SetAttributes. use super::*; impl MotionAnimation { @@ -10,22 +10,11 @@ impl MotionAnimation { match tree { PlaybackTree::SelectionSpace(space) => { space.select(attributes)?; - if !space.child_constructed { - let motion = space - .current() - .ok_or("SelectionSpace has no selected child")? - .clone(); - let (motion, posture) = if self.posture_bank_valid { - self.posture - .apply::<_, String>((motion, None), |(motion, _), pose| { - Ok((motion, Some(pose))) - })? - } else { - (motion, None) - }; - *space.current_mut().unwrap() = self.add_bind_pose(motion, posture)?; - space.child_constructed = true; - } + //82D26F88 calls IAnimatable+76, not SkaterAnim::GetAnimTree. + //TU3 vtable8231E050+76 ->82E32328 ->82D19648 constructs a + //raw child. The enclosing main/channel tree already owns its + //bind pose and mirroring; applying those here doubles bone + //translations and rotations (visible on offboard jump clips). self.prepare_selection_spaces(space.current_mut().unwrap(), attributes)?; } PlaybackTree::BlendSpace(space) => { diff --git a/docs/OFFBOARD_FALL_INVESTIGATION.md b/docs/OFFBOARD_FALL_INVESTIGATION.md index 89bb5b74..b48527b1 100644 --- a/docs/OFFBOARD_FALL_INVESTIGATION.md +++ b/docs/OFFBOARD_FALL_INVESTIGATION.md @@ -32,4 +32,22 @@ established defect. MatchAirTime (82BBA070) seeks the current sequence to the fraction of elapsed flight. Generated native instruction extracts are in `logs/biped-*.txt`; authoritative TU3 sources remain in Skate3Research. -No gameplay fix is claimed by these diagnostic commits. +## Located defect + +The next user capture, `logs/game-20260906-225525.stderr.log`, identifies +`SS_BR_JUMP_RUN_INTO`. Before IK, part 19 moves from animation Y=0.541 +at tick 3 to Y=1.794 at tick 4 and Y=2.363 at tick 5. Original joint positions +already contain the stretch, while the mapped rotation bases have unit length. + +The selection-space adapter incorrectly wrapped its selected raw child in a +second BindPose node. The main playback tree already had that wrapper. This +composed rest-pose translations and rotations twice as the jump blended in. + +Native evidence: SelectionSpace::SetAttributes82D26CC0, call at82D26F88, +dispatches through IAnimatable+76. TU3 vtable8231E050+76 contains82E32328. +That three-instruction thunk passes the construction map and calls82D19648 +directly; it does not call posture or AddBindPose. Main GetAnimTree82B980A0 +instead invokes vtable208 (posture) and164 (AddBindPose82B98118) after the +raw builder. The adapter now preserves this distinction and leaves selected +children raw, including nested selection spaces. Bail thresholds and physics +are unchanged. Compilation is checked; gameplay validation belongs to the user.