Publish native grind direction and conditioned camera contact

This commit is contained in:
chazmm committed 2026-09-07 20:30:09 +01:00
1 parent 685d55601e
commit d39703e48c
4 files changed
+121 -8

No files matched your search

+10 -8
View File
@@ -2,7 +2,7 @@
//! Reset82DE53F0 runs before the actual producers82DB6EC0/82D3A388.
use super::{GamePhysics, SkaterRuntime};
use crate::camera::{
CameraAirOutput, CameraAnimationOutput, CameraEventsOutput, CameraGrindOutput,
CameraAirOutput, CameraAnimationOutput, CameraEventsOutput,
CameraOffboardOutput, CameraPreferences, CameraPublicationInputs, CameraStateOutput,
};
use skate_core::{
@@ -15,11 +15,16 @@ use skate_core::{
/// The user's selected normal High camera is graph type1. This custom world
/// has no road/ledge/camera-volume annotations or other moving actors.
pub(crate) fn advance(
physics: &GamePhysics,
physics: &mut GamePhysics,
skater: &SkaterRuntime,
feedback: &PhysicalFeedback,
camera: &mut crate::camera::CameraRuntime,
) -> Result<(), String> {
//Once per physical publication, preserving conditioner history across
//direct grind-type changes and invalidating it on the first non-grind tick.
physics.grind.advance_camera(
skater.player_input.processed.vectors_400_416[0].map(f32::from_bits),
)?;
let inputs = publish(
physics,
skater,
@@ -137,12 +142,9 @@ pub(crate) fn publish(
use_trajectory_331: 0,
dropping_in_334: 0,
},
grinds: CameraGrindOutput {
//Reset82DE3518 loads original world-X literal82139A10.
direction_0: [1.0, 0.0, 0.0, 0.0],
camera_target_96: [0.0; 4],
grinding_316: u8::from(matches!(physical.grinds.words_136_140[1], 1 | 2)),
},
//Shared grind Fill82D40EA0 publishes the directed rail tangent;
//conditioner82DF0640 supplies the continuous contact anchor.
grinds: physics.grind.camera_output(),
events: CameraEventsOutput {
intent_51: bit(intents, 28),
preparing_52: bit(intents, 27),
+21
View File
@@ -17,7 +17,10 @@ use skate_core::{
};
use skate_data::collections::Collections;
type V = [f32; 4];
#[path = "grind_camera.rs"]
mod camera;
pub(crate) struct Runtime {
camera: camera::GrindCamera,
primitives: Vec<Primitive>,
pub candidate: Option<FiftyFiftyCandidate>,
pub name: String,
@@ -50,6 +53,7 @@ impl Runtime {
.words::<8>("physics_grinds", "default", "PinVsSlope")?
.map(f32::from_bits);
Ok(Self {
camera: camera::GrindCamera::default(),
primitives: crate::grind_world::primitives(map)?,
candidate: None,
name: String::new(),
@@ -98,6 +102,23 @@ impl Runtime {
diagnostic_tick: 0,
})
}
pub fn advance_camera(&mut self, velocity: V) -> Result<(), String> {
if self.active {
let contact = self.candidate.ok_or("Grind camera needs active contact")?;
self.camera.update(contact, self.primitives[contact.primitive], self.kind, velocity);
} else {
self.camera.exit();
}
Ok(())
}
pub fn camera_output(&self) -> crate::camera::CameraGrindOutput {
crate::camera::CameraGrindOutput {
//Reset82DE3518 defaults still apply outside the active state.
direction_0: if self.active { self.camera.direction } else { [1.0, 0.0, 0.0, 0.0] },
camera_target_96: if self.active { self.camera.target } else { [0.0; 4] },
grinding_316: u8::from(self.active),
}
}
pub fn output(&self) -> GrindState {
if !self.active {
return GrindState::default();
@@ -0,0 +1,58 @@
//! TU3 grind direction82D87460 and contact-point conditioner82DF0640.
use skate_core::physics::grind_contact::{FiftyFiftyCandidate, Primitive};
use skate_core::riding::ground_correction_math::dot_product;
type V = [f32; 4];
#[derive(Default)]
pub(super) struct GrindCamera {
pub direction: V,
pub target: V,
previous_target: V,
offset: V,
midpoint: V,
kind: u32,
active: bool,
}
impl GrindCamera {
pub fn exit(&mut self) {
//82DF0610/82DF07EC invalidate history without erasing the rail tangent.
self.active = false;
}
pub fn update(&mut self, contact: FiftyFiftyCandidate, edge: Primitive, kind: u32, velocity: V) {
//82D37048 supplies the normalized primitive tangent, independently of
//the deck's facing direction (including backwards50-50s/boardslides).
let delta: V = core::array::from_fn(|i| edge.end[i] - edge.start[i]);
let length = dot_product(delta, delta).sqrt();
let mut direction = if length > 0.0 { delta.map(|v| v / length) } else { self.direction };
let along = dot_product(direction, velocity);
if along < 0.0 {
direction = direction.map(|v| -v);
}
//82D874F0: native literals820641A8=.1 and822F8E94=-.9.
if along.abs() < 0.1 && dot_product(self.direction, direction) < -0.9 {
direction = direction.map(|v| -v);
}
self.direction = direction;
let midpoint: V = core::array::from_fn(|i| (edge.start[i] + edge.end[i]) * 0.5);
if !self.active {
self.offset = [0.0; 4];
self.target = contact.centre;
self.previous_target = contact.centre;
} else if kind != self.kind || midpoint[..3] != self.midpoint[..3] {
//Preserve extrapolated motion across a change of contact owner.
self.offset = core::array::from_fn(|i| {
self.target[i] + (self.target[i] - self.previous_target[i]) - contact.centre[i]
});
}
//Constructor82DF24CC loads literal82072818, .95 per physical tick.
//This decays a contact discontinuity, not ordinary camera movement.
self.offset = self.offset.map(|v| v * 0.95);
self.previous_target = self.target;
self.target = core::array::from_fn(|i| contact.centre[i] + self.offset[i]);
self.midpoint = midpoint;
self.kind = kind;
self.active = true;
}
}