use super::continuous::solve_continuous;
use super::StepContext;
use crate::body::body_flags;
use crate::constants::{speculative_distance, MAX_ROTATION, TIME_TO_SLEEP};
use crate::core::NULL_INDEX;
use crate::events::BodyMoveEvent;
use crate::id::BodyId;
use crate::math_functions::{
aabb_contains, abs_float, add, dot, integrate_rotation, is_valid_float, is_valid_vec2, length,
max_float, mul_add, mul_rot, mul_sv, neg, normalize_rot, offset_pos, rotate_vector, Aabb,
ROT_IDENTITY, VEC2_ZERO,
};
use crate::solver_set::AWAKE_SET;
use crate::types::BodyType;
use crate::world::World;
pub(super) fn integrate_velocities(world: &mut World, context: &StepContext) {
let gravity = world.gravity;
let h = context.h;
let set = &mut world.solver_sets[AWAKE_SET as usize];
let sims = &set.body_sims;
let states = &mut set.body_states;
for (state, sim) in states.iter_mut().zip(sims.iter()) {
let v = state.linear_velocity;
let w = state.angular_velocity;
let linear_damping = 1.0 / (1.0 + h * sim.linear_damping);
let angular_damping = 1.0 / (1.0 + h * sim.angular_damping);
let gravity_scale = if sim.inv_mass > 0.0 {
sim.gravity_scale
} else {
0.0
};
let linear_velocity_delta = add(
mul_sv(h * sim.inv_mass, sim.force),
mul_sv(h * gravity_scale, gravity),
);
let angular_velocity_delta = h * sim.inv_inertia * sim.torque;
state.linear_velocity = mul_add(linear_velocity_delta, linear_damping, v);
state.angular_velocity = angular_velocity_delta + angular_damping * w;
}
}
pub(super) fn integrate_positions(world: &mut World, context: &StepContext) {
let h = context.h;
let max_linear_speed = context.max_linear_velocity;
let max_angular_speed = MAX_ROTATION * context.inv_dt;
let max_linear_speed_squared = max_linear_speed * max_linear_speed;
let max_angular_speed_squared = max_angular_speed * max_angular_speed;
let states = &mut world.solver_sets[AWAKE_SET as usize].body_states;
for state in states.iter_mut() {
let mut v = state.linear_velocity;
let mut w = state.angular_velocity;
if state.flags & body_flags::LOCK_LINEAR_X != 0 {
v.x = 0.0;
}
if state.flags & body_flags::LOCK_LINEAR_Y != 0 {
v.y = 0.0;
}
if state.flags & body_flags::LOCK_ANGULAR_Z != 0 {
w = 0.0;
}
if dot(v, v) > max_linear_speed_squared {
let ratio = max_linear_speed / length(v);
v = mul_sv(ratio, v);
state.flags |= body_flags::IS_SPEED_CAPPED;
}
if w * w > max_angular_speed_squared && state.flags & body_flags::ALLOW_FAST_ROTATION == 0 {
let ratio = max_angular_speed / abs_float(w);
w *= ratio;
state.flags |= body_flags::IS_SPEED_CAPPED;
}
state.linear_velocity = v;
state.angular_velocity = w;
state.delta_position = mul_add(state.delta_position, h, state.linear_velocity);
state.delta_rotation = integrate_rotation(state.delta_rotation, h * state.angular_velocity);
}
}
pub(super) fn finalize_bodies(
world: &mut World,
context: &StepContext,
bullet_bodies: &mut Vec<i32>,
) {
let enable_sleep = world.enable_sleep;
let enable_continuous = world.enable_continuous;
let time_step = context.dt;
let inv_time_step = context.inv_dt;
let world_id = world.world_id;
let speculative_distance_ = speculative_distance();
let awake_body_count = world.solver_sets[AWAKE_SET as usize].body_sims.len();
debug_assert!(awake_body_count <= world.body_move_events.len());
for sim_index in 0..awake_body_count {
let mut state = world.solver_sets[AWAKE_SET as usize].body_states[sim_index];
let mut sim = world.solver_sets[AWAKE_SET as usize].body_sims[sim_index];
let v = state.linear_velocity;
let w = state.angular_velocity;
debug_assert!(is_valid_vec2(v));
debug_assert!(is_valid_float(w));
sim.center = offset_pos(sim.center, state.delta_position);
sim.transform.q = normalize_rot(mul_rot(state.delta_rotation, sim.transform.q));
let max_velocity = length(v) + abs_float(w) * sim.max_extent;
let max_delta_position =
length(state.delta_position) + abs_float(state.delta_rotation.s) * sim.max_extent;
let position_sleep_factor = 0.5;
let sleep_velocity = max_float(
max_velocity,
position_sleep_factor * inv_time_step * max_delta_position,
);
state.delta_position = VEC2_ZERO;
state.delta_rotation = ROT_IDENTITY;
sim.transform.p = offset_pos(
sim.center,
neg(rotate_vector(sim.transform.q, sim.local_center)),
);
let body_id = sim.body_id;
{
let body = &mut world.bodies[body_id as usize];
body.body_move_index = sim_index as i32;
world.body_move_events[sim_index] = BodyMoveEvent {
transform: sim.transform,
body_id: BodyId {
index1: body_id + 1,
world0: world_id,
generation: body.generation,
},
user_data: body.user_data,
fell_asleep: false,
};
}
sim.force = VEC2_ZERO;
sim.torque = 0.0;
debug_assert!(world.bodies[body_id as usize].flags & body_flags::DIRTY_MASS == 0);
{
let body = &mut world.bodies[body_id as usize];
body.flags &= !body_flags::BODY_TRANSIENT_FLAGS;
body.flags |=
sim.flags & (body_flags::IS_SPEED_CAPPED | body_flags::HAD_TIME_OF_IMPACT);
body.flags |=
state.flags & (body_flags::IS_SPEED_CAPPED | body_flags::HAD_TIME_OF_IMPACT);
}
sim.flags &= !body_flags::BODY_TRANSIENT_FLAGS;
state.flags &= !body_flags::BODY_TRANSIENT_FLAGS;
world.solver_sets[AWAKE_SET as usize].body_states[sim_index] = state;
let body_flags_now = world.bodies[body_id as usize].flags;
let body_type = world.bodies[body_id as usize].type_;
let sleep_threshold = world.bodies[body_id as usize].sleep_threshold;
if !enable_sleep
|| body_flags_now & body_flags::ENABLE_SLEEP == 0
|| sleep_velocity > sleep_threshold
{
world.bodies[body_id as usize].sleep_time = 0.0;
let safety_factor = 0.5;
let max_motion = max_float(max_delta_position, max_velocity * time_step);
if body_type == BodyType::Dynamic
&& enable_continuous
&& max_motion > safety_factor * sim.min_extent
{
sim.flags |= body_flags::IS_FAST;
if sim.flags & body_flags::IS_BULLET != 0 {
bullet_bodies.push(sim_index as i32);
world.solver_sets[AWAKE_SET as usize].body_sims[sim_index] = sim;
} else {
world.solver_sets[AWAKE_SET as usize].body_sims[sim_index] = sim;
solve_continuous(world, sim_index as i32);
}
} else {
sim.center0 = sim.center;
sim.rotation0 = sim.transform.q;
world.solver_sets[AWAKE_SET as usize].body_sims[sim_index] = sim;
}
} else {
sim.center0 = sim.center;
sim.rotation0 = sim.transform.q;
world.bodies[body_id as usize].sleep_time += time_step;
world.solver_sets[AWAKE_SET as usize].body_sims[sim_index] = sim;
}
let sleep_time = world.bodies[body_id as usize].sleep_time;
let island_id = world.bodies[body_id as usize].island_id;
if sleep_time < TIME_TO_SLEEP {
let island_index = world.islands[island_id as usize].local_index;
world.task_contexts[0]
.awake_island_bit_set
.set_bit(island_index as u32);
} else if world.islands[island_id as usize].constraint_remove_count > 0 {
let task_context = &mut world.task_contexts[0];
if sleep_time > task_context.split_sleep_time
|| (sleep_time == task_context.split_sleep_time
&& island_id > task_context.split_island_id)
{
task_context.split_island_id = island_id;
task_context.split_sleep_time = sleep_time;
}
}
let sim_now = world.solver_sets[AWAKE_SET as usize].body_sims[sim_index];
let transform = sim_now.transform;
let is_fast = sim_now.flags & body_flags::IS_FAST != 0;
let mut shape_id = world.bodies[body_id as usize].head_shape_id;
while shape_id != NULL_INDEX {
if is_fast {
world.task_contexts[0]
.enlarged_sim_bit_set
.set_bit(sim_index as u32);
} else {
let shape = &mut world.shapes[shape_id as usize];
let aabb = crate::geometry::compute_fat_shape_aabb(
&shape.geometry,
transform,
speculative_distance_,
);
shape.aabb = aabb;
debug_assert!(!shape.enlarged_aabb);
if !aabb_contains(shape.fat_aabb, aabb) {
let margin = shape.aabb_margin;
let fat_aabb = Aabb {
lower_bound: crate::math_functions::Vec2 {
x: aabb.lower_bound.x - margin,
y: aabb.lower_bound.y - margin,
},
upper_bound: crate::math_functions::Vec2 {
x: aabb.upper_bound.x + margin,
y: aabb.upper_bound.y + margin,
},
};
shape.fat_aabb = fat_aabb;
shape.enlarged_aabb = true;
world.task_contexts[0]
.enlarged_sim_bit_set
.set_bit(sim_index as u32);
}
}
shape_id = world.shapes[shape_id as usize].next_shape_id;
}
}
}