mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-27 19:54:40 +00:00
127 lines
4.6 KiB
Rust
127 lines
4.6 KiB
Rust
//! One physical solve for the board, skater and original animation targets.
|
|
//! The caller publishes forces and drive targets before entering this phase.
|
|
mod assembly_contacts;
|
|
mod diagnostics;
|
|
use super::{GamePhysics, SkaterRuntime, colliders, skeleton_colliders};
|
|
use skate_core::physics::{
|
|
board::BodyId,
|
|
board_step::{ATTACHED_REACTION_BASE, AttachedStep},
|
|
skeleton_animation_record::{AnimationPartTransform, IDENTITY},
|
|
skeleton_body::PART_COUNT,
|
|
};
|
|
|
|
pub(super) fn advance(
|
|
physics: &mut GamePhysics,
|
|
skater: &mut SkaterRuntime,
|
|
truck_targets: [f32; 2],
|
|
) -> Result<(), String> {
|
|
let before = diagnostics::snapshot(physics, skater);
|
|
diagnostics::validate(&before, "before shared solve")?;
|
|
let board_volumes = if skater.offboard.board_policy.volumes_enabled {
|
|
colliders::world_volumes(&physics.board, &physics.settings)
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
let skeleton_volumes =
|
|
skeleton_colliders::enabled_volumes(&skater.skeleton, &skater.skeleton_collision)?;
|
|
// Each native assembly has its own query record and retention buffer.
|
|
// Skeleton82BE5094 passes false to82768728: its edge threshold is -1,
|
|
// whereas the board requests .999. GroundPipeline supplies the remaining
|
|
// shared values. Do not let the second query overwrite the first's rows.
|
|
let board_timer = crate::performance::Scope::new("board_world_contacts");
|
|
let mut contacts = physics
|
|
.world
|
|
.query_primitives(&board_volumes, physics.query, physics.retention)
|
|
.to_vec();
|
|
drop(board_timer);
|
|
let skeleton_timer = crate::performance::Scope::new("skeleton_world_contacts");
|
|
let mut skeleton_query = physics.query;
|
|
skeleton_query.edge_cos_bend_normal_threshold = -1.0;
|
|
let mut skeleton_world_volumes = skeleton_volumes.clone();
|
|
skeleton_colliders::retain_world_volumes(&mut skeleton_world_volumes, &skater.skeleton_collision);
|
|
contacts.extend_from_slice(physics.world.query_primitives(
|
|
&skeleton_world_volumes,
|
|
skeleton_query,
|
|
physics.retention,
|
|
));
|
|
drop(skeleton_timer);
|
|
assembly_contacts::append(
|
|
&mut contacts,
|
|
&board_volumes,
|
|
&skeleton_volumes,
|
|
physics.board.collision_group(),
|
|
&skater.skeleton_collision,
|
|
)?;
|
|
physics.contact_count = contacts.len();
|
|
let dt = physics.settings.step.simulation.time_step;
|
|
let mut joints =
|
|
skater
|
|
.skeleton_joints
|
|
.build(skater.skeleton.bodies(), ATTACHED_REACTION_BASE, dt);
|
|
let mut drives = skater.skeleton_drives.build(
|
|
skater.skeleton.bodies(),
|
|
ATTACHED_REACTION_BASE,
|
|
ATTACHED_REACTION_BASE + PART_COUNT,
|
|
dt,
|
|
);
|
|
let skeleton_drive_count = drives.rows.len();
|
|
if skater.skateboard_controller.fields.system_on_452 {
|
|
super::offboard::hand_drives::append(
|
|
&mut drives.rows,
|
|
&skater.offboard.possession,
|
|
physics.board.bodies(),
|
|
skater.skeleton.bodies(),
|
|
dt,
|
|
);
|
|
}
|
|
let bodies = skater
|
|
.skeleton
|
|
.bodies_mut()
|
|
.iter_mut()
|
|
.chain(skater.skeleton_drives.targets.bodies.iter_mut())
|
|
.collect();
|
|
physics.board.advance_attached(
|
|
&contacts,
|
|
truck_targets,
|
|
physics.settings.step,
|
|
AttachedStep {
|
|
bodies,
|
|
contacts: &mut [],
|
|
joints: &mut joints,
|
|
drives: &mut drives.rows,
|
|
},
|
|
);
|
|
if let Err(error) = diagnostics::validate(
|
|
&diagnostics::snapshot(physics, skater), "after shared solve",
|
|
) {
|
|
return Err(format!("{error}; input_bodies={before:?}; contacts={contacts:?}; joints={joints:?}; drives={:?}", drives.rows));
|
|
}
|
|
skater
|
|
.skeleton
|
|
.publish_physical_record(deck_frame(&physics.board));
|
|
// These solved rows are consumed by the actual collision/drive feedback
|
|
// phase; keep their identity and impulses after the shared solve.
|
|
//Controller drive descriptors have flags28=0: they are not Skeleton's
|
|
//spy drives and must not be paired with SkeletonDriveIdentity feedback.
|
|
drives.rows.truncate(skeleton_drive_count);
|
|
skater.solved_drives = Some(drives);
|
|
Ok(())
|
|
}
|
|
|
|
pub(super) fn deck_frame(
|
|
board: &skate_core::physics::board_runtime::BoardRuntime,
|
|
) -> AnimationPartTransform {
|
|
let deck = board.part_transforms()[BodyId::Deck.index()];
|
|
let mut frame = IDENTITY;
|
|
for (axis, column) in deck.basis.columns.iter().enumerate() {
|
|
frame[axis][..3].copy_from_slice(column);
|
|
}
|
|
frame[3] = [
|
|
deck.translation.x,
|
|
deck.translation.y,
|
|
deck.translation.z,
|
|
0.0,
|
|
];
|
|
frame
|
|
}
|