#![allow(clippy::needless_range_loop)]
use super::integrate::{finalize_bodies, integrate_positions, integrate_velocities};
use super::StepContext;
use crate::constraint_graph::OVERFLOW_INDEX;
use crate::contact::contact_flags;
use crate::contact_solver::wide_kernels::{
apply_restitution_wide, prepare_contacts_wide, solve_contacts_wide, store_impulses_wide,
warm_start_contacts_wide, wide_block_count, ContactConstraintWide,
};
use crate::contact_solver::{
apply_restitution, prepare_contacts, solve_contacts, store_impulses, warm_start_contacts,
ContactConstraint,
};
use crate::core::NULL_INDEX;
use crate::events::{BodyMoveEvent, ContactHitEvent, JointEvent};
use crate::id::{BodyId, ContactId, JointId, ShapeId};
use crate::joint::{get_joint_reaction, prepare_joint, solve_joint, warm_start_joint};
use crate::math_functions::{make_world_transform, offset_pos, TRANSFORM_IDENTITY};
use crate::solver_set::AWAKE_SET;
use crate::timer::{get_milliseconds, get_milliseconds_and_reset, get_ticks};
use crate::types::BodyType;
use crate::world::World;
const ITERATIONS: i32 = 1;
const RELAX_ITERATIONS: i32 = 1;
fn prepare_color_joints(world: &mut World, color_index: i32, context: &StepContext) {
let count = world.constraint_graph.colors[color_index as usize]
.joint_sims
.len();
for i in 0..count {
let mut sim = world.constraint_graph.colors[color_index as usize].joint_sims[i];
prepare_joint(world, &mut sim, context);
world.constraint_graph.colors[color_index as usize].joint_sims[i] = sim;
}
}
pub fn solve(world: &mut World, context: &StepContext) {
world.step_index += 1;
let awake_body_count = world.solver_sets[AWAKE_SET as usize].body_sims.len();
if awake_body_count == 0 {
world.broad_phase.validate_no_enlarged();
return;
}
let mut setup_ticks = get_ticks();
let mut bullet_bodies: Vec<i32> = Vec::with_capacity(awake_body_count);
world.body_move_events.resize(
awake_body_count,
BodyMoveEvent {
user_data: 0,
transform: make_world_transform(TRANSFORM_IDENTITY),
body_id: BodyId::default(),
fell_asleep: false,
},
);
let joint_id_capacity = world.joint_id_pool.id_capacity();
let contact_id_capacity = world.contact_id_pool.id_capacity();
{
let task_context = &mut world.task_contexts[0];
task_context
.joint_state_bit_set
.set_bit_count_and_clear(joint_id_capacity as u32);
task_context
.hit_event_bit_set
.set_bit_count_and_clear(contact_id_capacity as u32);
task_context.has_hit_events = false;
}
if world.split_island_id != NULL_INDEX {
let split_ticks = get_ticks();
let split_id = world.split_island_id;
crate::island::split_island(world, split_id);
world.split_island_id = NULL_INDEX;
world.profile.split_islands += get_milliseconds(split_ticks);
}
world.profile.solver_setup = get_milliseconds_and_reset(&mut setup_ticks);
let mut constraint_ticks = get_ticks();
let mut ticks = get_ticks();
for color_index in 0..OVERFLOW_INDEX {
prepare_color_joints(world, color_index, context);
}
let mut wide_constraints: Vec<Vec<ContactConstraintWide>> =
(0..OVERFLOW_INDEX).map(|_| Vec::new()).collect();
for color_index in 0..OVERFLOW_INDEX as usize {
let count = world.constraint_graph.colors[color_index]
.contact_sims
.len();
wide_constraints[color_index]
.resize(wide_block_count(count), ContactConstraintWide::default());
}
let mut overflow_constraints: Vec<ContactConstraint> = Vec::new();
overflow_constraints.resize(
world.constraint_graph.colors[OVERFLOW_INDEX as usize]
.contact_sims
.len(),
ContactConstraint::default(),
);
let enable_softening = world.enable_contact_softening;
let contact_hertz = world.contact_hertz;
let contact_damping_ratio = world.contact_damping_ratio;
for color_index in 0..OVERFLOW_INDEX as usize {
let contacts = &world.constraint_graph.colors[color_index].contact_sims;
let states = &world.solver_sets[AWAKE_SET as usize].body_states;
prepare_contacts_wide(
&mut wide_constraints[color_index],
contacts,
states,
context,
enable_softening,
contact_hertz,
contact_damping_ratio,
);
}
prepare_color_joints(world, OVERFLOW_INDEX, context);
{
let contacts = &world.constraint_graph.colors[OVERFLOW_INDEX as usize].contact_sims;
let states = &world.solver_sets[AWAKE_SET as usize].body_states;
prepare_contacts(&mut overflow_constraints, contacts, states, context);
}
world.profile.prepare_constraints += get_milliseconds_and_reset(&mut ticks);
let sub_step_count = context.sub_step_count;
for _sub_step_index in 0..sub_step_count {
integrate_velocities(world, context);
world.profile.integrate_velocities += get_milliseconds_and_reset(&mut ticks);
{
let world_parts = &mut *world;
let graph = &mut world_parts.constraint_graph;
let states = &mut world_parts.solver_sets[AWAKE_SET as usize].body_states;
for joint in graph.colors[OVERFLOW_INDEX as usize].joint_sims.iter_mut() {
warm_start_joint(joint, states);
}
warm_start_contacts(&mut overflow_constraints, states);
for color_index in 0..OVERFLOW_INDEX as usize {
for joint in graph.colors[color_index].joint_sims.iter_mut() {
warm_start_joint(joint, states);
}
warm_start_contacts_wide(&mut wide_constraints[color_index], states);
}
}
world.profile.warm_start += get_milliseconds_and_reset(&mut ticks);
for _ in 0..ITERATIONS {
let use_bias = true;
let world_parts = &mut *world;
let graph = &mut world_parts.constraint_graph;
let states = &mut world_parts.solver_sets[AWAKE_SET as usize].body_states;
let joint_state_bit_set = &mut world_parts.task_contexts[0].joint_state_bit_set;
for joint in graph.colors[OVERFLOW_INDEX as usize].joint_sims.iter_mut() {
solve_joint(joint, context, states, use_bias);
}
solve_contacts(&mut overflow_constraints, states, context, use_bias);
for color_index in 0..OVERFLOW_INDEX as usize {
for joint in graph.colors[color_index].joint_sims.iter_mut() {
solve_joint(joint, context, states, use_bias);
if (joint.force_threshold < f32::MAX || joint.torque_threshold < f32::MAX)
&& !joint_state_bit_set.get_bit(joint.joint_id as u32)
{
let (force, torque) = get_joint_reaction(joint, context.inv_h);
if force >= joint.force_threshold || torque >= joint.torque_threshold {
joint_state_bit_set.set_bit(joint.joint_id as u32);
}
}
}
solve_contacts_wide(
&mut wide_constraints[color_index],
states,
context,
use_bias,
);
}
}
world.profile.solve_impulses += get_milliseconds_and_reset(&mut ticks);
integrate_positions(world, context);
world.profile.integrate_positions += get_milliseconds_and_reset(&mut ticks);
for _ in 0..RELAX_ITERATIONS {
let use_bias = false;
let world_parts = &mut *world;
let graph = &mut world_parts.constraint_graph;
let states = &mut world_parts.solver_sets[AWAKE_SET as usize].body_states;
for joint in graph.colors[OVERFLOW_INDEX as usize].joint_sims.iter_mut() {
solve_joint(joint, context, states, use_bias);
}
solve_contacts(&mut overflow_constraints, states, context, use_bias);
for color_index in 0..OVERFLOW_INDEX as usize {
for joint in graph.colors[color_index].joint_sims.iter_mut() {
solve_joint(joint, context, states, use_bias);
}
solve_contacts_wide(
&mut wide_constraints[color_index],
states,
context,
use_bias,
);
}
}
world.profile.relax_impulses += get_milliseconds_and_reset(&mut ticks);
}
{
let world_parts = &mut *world;
let states = &mut world_parts.solver_sets[AWAKE_SET as usize].body_states;
apply_restitution(&mut overflow_constraints, states, context);
for color_index in 0..OVERFLOW_INDEX as usize {
apply_restitution_wide(&mut wide_constraints[color_index], states, context);
}
}
world.profile.apply_restitution += get_milliseconds_and_reset(&mut ticks);
{
let world_parts = &mut *world;
let graph = &mut world_parts.constraint_graph;
let task_context = &mut world_parts.task_contexts[0];
let neg_hit_threshold = -world_parts.hit_event_threshold;
store_impulses(
&overflow_constraints,
&mut graph.colors[OVERFLOW_INDEX as usize].contact_sims,
);
for color_index in 0..OVERFLOW_INDEX as usize {
store_impulses_wide(
&wide_constraints[color_index],
&mut graph.colors[color_index].contact_sims,
);
for contact_sim in &graph.colors[color_index].contact_sims {
if contact_sim.sim_flags & contact_flags::SIM_ENABLE_HIT_EVENT != 0 {
for k in 0..contact_sim.manifold.point_count as usize {
let mp = &contact_sim.manifold.points[k];
if mp.normal_velocity < neg_hit_threshold && mp.total_normal_impulse > 0.0 {
task_context
.hit_event_bit_set
.set_bit(contact_sim.contact_id as u32);
task_context.has_hit_events = true;
break;
}
}
}
}
}
}
world.profile.store_impulses += get_milliseconds_and_reset(&mut ticks);
world.profile.constraints = get_milliseconds_and_reset(&mut constraint_ticks);
let transform_ticks = get_ticks();
{
let awake_island_count = world.solver_sets[AWAKE_SET as usize].island_sims.len();
let task_context = &mut world.task_contexts[0];
task_context.sensor_hits.clear();
task_context
.enlarged_sim_bit_set
.set_bit_count_and_clear(awake_body_count as u32);
task_context
.awake_island_bit_set
.set_bit_count_and_clear(awake_island_count as u32);
task_context.split_island_id = NULL_INDEX;
task_context.split_sleep_time = 0.0;
}
finalize_bodies(world, context, &mut bullet_bodies);
world.profile.transforms = get_milliseconds(transform_ticks);
{
let joint_event_ticks = get_ticks();
let world_id = world.world_id;
let word_count = world.task_contexts[0].joint_state_bit_set.block_count();
for k in 0..word_count {
let mut word = world.task_contexts[0].joint_state_bit_set.block(k);
while word != 0 {
let ctz = word.trailing_zeros();
let joint_id = (64 * k + ctz) as i32;
let joint = &world.joints[joint_id as usize];
debug_assert!(joint.set_index == AWAKE_SET);
let event = JointEvent {
joint_id: JointId {
index1: joint_id + 1,
world0: world_id,
generation: joint.generation,
},
user_data: joint.user_data,
};
world.joint_events.push(event);
word &= word - 1;
}
}
world.profile.joint_events = get_milliseconds(joint_event_ticks);
}
{
let hit_ticks = get_ticks();
debug_assert!(world.contact_hit_events.is_empty());
if world.task_contexts[0].has_hit_events {
let threshold = world.hit_event_threshold;
let world_id = world.world_id;
let word_count = world.task_contexts[0].hit_event_bit_set.block_count();
for k in 0..word_count {
let mut word = world.task_contexts[0].hit_event_bit_set.block(k);
while word != 0 {
let ctz = word.trailing_zeros();
let contact_id = (64 * k + ctz) as i32;
let contact = world.contacts[contact_id as usize];
debug_assert!(
contact.set_index == AWAKE_SET && contact.color_index != NULL_INDEX
);
let contact_sim = &world.constraint_graph.colors[contact.color_index as usize]
.contact_sims[contact.local_index as usize];
let mut approach_speed = threshold;
let mut best_point: Option<usize> = None;
for p in 0..contact_sim.manifold.point_count as usize {
let mp = &contact_sim.manifold.points[p];
let point_approach_speed = -mp.normal_velocity;
if point_approach_speed > approach_speed && mp.total_normal_impulse > 0.0 {
approach_speed = point_approach_speed;
best_point = Some(p);
}
}
if let Some(p) = best_point {
let best = contact_sim.manifold.points[p];
let normal = contact_sim.manifold.normal;
let shape_a = &world.shapes[contact_sim.shape_id_a as usize];
let shape_b = &world.shapes[contact_sim.shape_id_b as usize];
let body_a = &world.bodies[shape_a.body_id as usize];
let body_b = &world.bodies[shape_b.body_id as usize];
let point = if body_a.type_ != BodyType::Static
&& body_b.type_ == BodyType::Static
{
let body_sim_b = &world.solver_sets[body_b.set_index as usize]
.body_sims[body_b.local_index as usize];
offset_pos(body_sim_b.center, best.anchor_b)
} else {
let body_sim_a = &world.solver_sets[body_a.set_index as usize]
.body_sims[body_a.local_index as usize];
offset_pos(body_sim_a.center, best.anchor_a)
};
let event = ContactHitEvent {
shape_id_a: ShapeId {
index1: shape_a.id + 1,
world0: world_id,
generation: shape_a.generation,
},
shape_id_b: ShapeId {
index1: shape_b.id + 1,
world0: world_id,
generation: shape_b.generation,
},
contact_id: ContactId {
index1: contact.contact_id + 1,
world0: world_id,
padding: 0,
generation: contact.generation,
},
point,
normal,
approach_speed,
};
world.contact_hit_events.push(event);
}
word &= word - 1;
}
}
}
world.profile.hit_events = get_milliseconds(hit_ticks);
}
{
let refit_ticks = get_ticks();
world.broad_phase.validate_no_enlarged();
let word_count = world.task_contexts[0].enlarged_sim_bit_set.block_count();
for k in 0..word_count {
let mut word = world.task_contexts[0].enlarged_sim_bit_set.block(k);
while word != 0 {
let ctz = word.trailing_zeros();
let body_sim_index = (64 * k + ctz) as usize;
let (body_id, sim_flags) = {
let body_sim = &world.solver_sets[AWAKE_SET as usize].body_sims[body_sim_index];
(body_sim.body_id, body_sim.flags)
};
let mut shape_id = world.bodies[body_id as usize].head_shape_id;
if sim_flags
& (crate::body::body_flags::IS_BULLET | crate::body::body_flags::IS_FAST)
== (crate::body::body_flags::IS_BULLET | crate::body::body_flags::IS_FAST)
{
while shape_id != NULL_INDEX {
let proxy_key = world.shapes[shape_id as usize].proxy_key;
world.broad_phase.buffer_move(proxy_key);
shape_id = world.shapes[shape_id as usize].next_shape_id;
}
} else {
while shape_id != NULL_INDEX {
if world.shapes[shape_id as usize].enlarged_aabb {
let proxy_key = world.shapes[shape_id as usize].proxy_key;
let fat_aabb = world.shapes[shape_id as usize].fat_aabb;
world.broad_phase.enlarge_proxy(proxy_key, fat_aabb);
world.shapes[shape_id as usize].enlarged_aabb = false;
}
shape_id = world.shapes[shape_id as usize].next_shape_id;
}
}
word &= word - 1;
}
}
world.broad_phase.validate();
world.profile.refit = get_milliseconds(refit_ticks);
}
if !bullet_bodies.is_empty() {
let bullet_ticks = get_ticks();
for &sim_index in &bullet_bodies {
super::continuous::solve_continuous(world, sim_index);
}
for &sim_index in &bullet_bodies {
let (body_id, enlarge) = {
let bullet_body_sim =
&world.solver_sets[AWAKE_SET as usize].body_sims[sim_index as usize];
(
bullet_body_sim.body_id,
bullet_body_sim.flags & crate::body::body_flags::ENLARGE_BOUNDS != 0,
)
};
if !enlarge {
continue;
}
world.solver_sets[AWAKE_SET as usize].body_sims[sim_index as usize].flags &=
!crate::body::body_flags::ENLARGE_BOUNDS;
let mut shape_id = world.bodies[body_id as usize].head_shape_id;
while shape_id != NULL_INDEX {
if !world.shapes[shape_id as usize].enlarged_aabb {
shape_id = world.shapes[shape_id as usize].next_shape_id;
continue;
}
world.shapes[shape_id as usize].enlarged_aabb = false;
let proxy_key = world.shapes[shape_id as usize].proxy_key;
let proxy_id = crate::broad_phase::proxy_id(proxy_key);
debug_assert!(crate::broad_phase::proxy_type(proxy_key) == BodyType::Dynamic);
debug_assert!(world.broad_phase.moved_proxies[BodyType::Dynamic as usize]
.get_bit(proxy_id as u32));
let fat_aabb = world.shapes[shape_id as usize].fat_aabb;
world.broad_phase.trees[BodyType::Dynamic as usize]
.enlarge_proxy(proxy_id, fat_aabb);
shape_id = world.shapes[shape_id as usize].next_shape_id;
}
}
world.profile.bullets = get_milliseconds(bullet_ticks);
}
{
let sensor_hit_ticks = get_ticks();
let hits = std::mem::take(&mut world.task_contexts[0].sensor_hits);
for hit in hits {
let sensor_index = world.shapes[hit.sensor_id as usize].sensor_index;
let generation = world.shapes[hit.visitor_id as usize].generation;
let shape_ref = crate::sensor::Visitor {
shape_id: hit.visitor_id,
generation,
};
world.sensors[sensor_index as usize].hits.push(shape_ref);
}
world.profile.sensor_hits = get_milliseconds(sensor_hit_ticks);
}
if world.enable_sleep {
let sleep_ticks = get_ticks();
debug_assert!(world.split_island_id == NULL_INDEX);
{
let task_context = &world.task_contexts[0];
if task_context.split_island_id != NULL_INDEX && task_context.split_sleep_time >= 0.0 {
debug_assert!(task_context.split_sleep_time > 0.0);
world.split_island_id = task_context.split_island_id;
}
}
let count = world.solver_sets[AWAKE_SET as usize].island_sims.len();
for island_index in (0..count).rev() {
if world.task_contexts[0]
.awake_island_bit_set
.get_bit(island_index as u32)
{
continue;
}
let island_id =
world.solver_sets[AWAKE_SET as usize].island_sims[island_index].island_id;
crate::solver_set::try_sleep_island(world, island_id);
}
world.validate_solver_sets();
world.profile.sleep_islands = get_milliseconds(sleep_ticks);
}
}