use super::api::reset_proxy;
use super::{compute_shape_margin, get_shape_index};
use crate::body::wake_body;
use crate::collision::{Capsule, ChainSegment, Circle, Polygon, Segment, ShapeGeometry, ShapeType};
use crate::core::NULL_INDEX;
use crate::id::{ChainId, ShapeId};
use crate::math_functions::{
abs_float, add, cross, cross_sv, distance_squared, dot, get_length_and_normalize, left_perp,
lerp, mul_add, mul_sub, mul_sv, neg, normalize, right_perp, rotate_vector, sub, Vec2,
VEC2_ZERO,
};
use crate::solver_set::{AWAKE_SET, FIRST_SLEEPING_SET};
use crate::types::BodyType;
use crate::world::World;
pub fn shape_get_circle(world: &World, shape_id: ShapeId) -> Circle {
let shape_index = get_shape_index(world, shape_id);
match &world.shapes[shape_index as usize].geometry {
ShapeGeometry::Circle(circle) => *circle,
_ => unreachable!("shape is not a circle"),
}
}
pub fn shape_get_segment(world: &World, shape_id: ShapeId) -> Segment {
let shape_index = get_shape_index(world, shape_id);
match &world.shapes[shape_index as usize].geometry {
ShapeGeometry::Segment(segment) => *segment,
_ => unreachable!("shape is not a segment"),
}
}
pub fn shape_get_chain_segment(world: &World, shape_id: ShapeId) -> ChainSegment {
let shape_index = get_shape_index(world, shape_id);
match &world.shapes[shape_index as usize].geometry {
ShapeGeometry::ChainSegment(chain_segment) => *chain_segment,
_ => unreachable!("shape is not a chain segment"),
}
}
pub fn shape_get_capsule(world: &World, shape_id: ShapeId) -> Capsule {
let shape_index = get_shape_index(world, shape_id);
match &world.shapes[shape_index as usize].geometry {
ShapeGeometry::Capsule(capsule) => *capsule,
_ => unreachable!("shape is not a capsule"),
}
}
pub fn shape_get_polygon(world: &World, shape_id: ShapeId) -> Polygon {
let shape_index = get_shape_index(world, shape_id);
match &world.shapes[shape_index as usize].geometry {
ShapeGeometry::Polygon(polygon) => *polygon,
_ => unreachable!("shape is not a polygon"),
}
}
fn set_geometry(world: &mut World, shape_index: i32, geometry: ShapeGeometry) {
{
let shape = &mut world.shapes[shape_index as usize];
shape.geometry = geometry;
shape.aabb_margin = compute_shape_margin(shape);
}
let wake_bodies = true;
let destroy_proxy = true;
reset_proxy(world, shape_index, wake_bodies, destroy_proxy);
}
pub fn shape_set_circle(world: &mut World, shape_id: ShapeId, circle: &Circle) {
crate::recording::record_op(world, |rec, _| {
crate::recording::write_shape_geometry(
rec,
crate::recording::OP_SHAPE_SET_CIRCLE,
shape_id,
|buf| crate::recording::rec_w_circle(buf, *circle),
)
});
debug_assert!(!world.locked);
if world.locked {
return;
}
let shape_index = get_shape_index(world, shape_id);
set_geometry(world, shape_index, ShapeGeometry::Circle(*circle));
}
pub fn shape_set_capsule(world: &mut World, shape_id: ShapeId, capsule: &Capsule) {
crate::recording::record_op(world, |rec, _| {
crate::recording::write_shape_geometry(
rec,
crate::recording::OP_SHAPE_SET_CAPSULE,
shape_id,
|buf| crate::recording::rec_w_capsule(buf, *capsule),
)
});
debug_assert!(!world.locked);
if world.locked {
return;
}
let length_sqr = distance_squared(capsule.center1, capsule.center2);
if length_sqr <= crate::constants::linear_slop() * crate::constants::linear_slop() {
return;
}
let shape_index = get_shape_index(world, shape_id);
set_geometry(world, shape_index, ShapeGeometry::Capsule(*capsule));
}
pub fn shape_set_segment(world: &mut World, shape_id: ShapeId, segment: &Segment) {
crate::recording::record_op(world, |rec, _| {
crate::recording::write_shape_geometry(
rec,
crate::recording::OP_SHAPE_SET_SEGMENT,
shape_id,
|buf| crate::recording::rec_w_segment(buf, *segment),
)
});
debug_assert!(!world.locked);
if world.locked {
return;
}
let shape_index = get_shape_index(world, shape_id);
set_geometry(world, shape_index, ShapeGeometry::Segment(*segment));
}
pub fn shape_set_polygon(world: &mut World, shape_id: ShapeId, polygon: &Polygon) {
crate::recording::record_op(world, |rec, _| {
crate::recording::write_shape_geometry(
rec,
crate::recording::OP_SHAPE_SET_POLYGON,
shape_id,
|buf| crate::recording::rec_w_polygon(buf, polygon),
)
});
debug_assert!(!world.locked);
if world.locked {
return;
}
let shape_index = get_shape_index(world, shape_id);
set_geometry(world, shape_index, ShapeGeometry::Polygon(*polygon));
}
pub fn shape_set_chain_segment(world: &mut World, shape_id: ShapeId, chain_segment: &ChainSegment) {
crate::recording::record_op(world, |rec, _| {
crate::recording::write_shape_geometry(
rec,
crate::recording::OP_SHAPE_SET_CHAIN_SEGMENT,
shape_id,
|buf| crate::recording::rec_w_chainseg(buf, *chain_segment),
)
});
debug_assert!(!world.locked);
if world.locked {
return;
}
let shape_index = get_shape_index(world, shape_id);
if let ShapeGeometry::ChainSegment(existing) = &world.shapes[shape_index as usize].geometry {
if existing.chain_id != NULL_INDEX {
debug_assert!(false, "cannot modify a chain segment owned by a chain");
return;
}
}
let length_sqr = distance_squared(chain_segment.segment.point1, chain_segment.segment.point2);
if length_sqr <= crate::constants::linear_slop() * crate::constants::linear_slop() {
return;
}
let mut local = *chain_segment;
local.chain_id = NULL_INDEX;
set_geometry(world, shape_index, ShapeGeometry::ChainSegment(local));
}
pub fn shape_get_parent_chain(world: &World, shape_id: ShapeId) -> ChainId {
let shape_index = get_shape_index(world, shape_id);
if let ShapeGeometry::ChainSegment(chain_segment) = &world.shapes[shape_index as usize].geometry
{
let chain_id = chain_segment.chain_id;
if chain_id != NULL_INDEX {
let chain = &world.chain_shapes[chain_id as usize];
return ChainId {
index1: chain_id + 1,
world0: world.world_id,
generation: chain.generation,
};
}
}
ChainId::default()
}
pub fn shape_apply_wind(
world: &mut World,
shape_id: ShapeId,
wind: Vec2,
drag: f32,
lift: f32,
wake: bool,
) {
crate::recording::record_op(world, |rec, _| {
crate::recording::write_shape_apply_wind(rec, shape_id, wind, drag, lift, wake)
});
let shape_index = get_shape_index(world, shape_id);
let shape_type = world.shapes[shape_index as usize].shape_type();
if shape_type != ShapeType::Circle
&& shape_type != ShapeType::Capsule
&& shape_type != ShapeType::Polygon
{
return;
}
let body_id = world.shapes[shape_index as usize].body_id;
if world.bodies[body_id as usize].type_ != BodyType::Dynamic {
return;
}
if world.bodies[body_id as usize].set_index >= FIRST_SLEEPING_SET && !wake {
return;
}
if world.bodies[body_id as usize].set_index != AWAKE_SET {
wake_body(world, body_id);
}
debug_assert!(world.bodies[body_id as usize].set_index == AWAKE_SET);
let local_index = world.bodies[body_id as usize].local_index;
let (transform, local_center) = {
let sim = &world.solver_sets[AWAKE_SET as usize].body_sims[local_index as usize];
(sim.transform, sim.local_center)
};
let (linear_velocity, angular_velocity) = {
let state = &world.solver_sets[AWAKE_SET as usize].body_states[local_index as usize];
(state.linear_velocity, state.angular_velocity)
};
let shape = &world.shapes[shape_index as usize];
let length_units = crate::core::get_length_units_per_meter();
let volume_units = length_units * length_units * length_units;
let air_density = 1.2250 / volume_units;
let mut force = VEC2_ZERO;
let mut torque = 0.0f32;
match &shape.geometry {
ShapeGeometry::Circle(circle) => {
let radius = circle.radius;
let centroid = shape.local_centroid;
let lever = rotate_vector(transform.q, sub(centroid, local_center));
let shape_velocity = add(linear_velocity, cross_sv(angular_velocity, lever));
let relative_velocity = mul_sub(wind, drag, shape_velocity);
let mut speed = 0.0;
let direction = get_length_and_normalize(&mut speed, relative_velocity);
let projected_area = 2.0 * radius;
force = mul_sv(
0.5 * air_density * projected_area * speed * speed,
direction,
);
torque = cross(lever, force);
}
ShapeGeometry::Capsule(capsule) => {
let centroid = shape.local_centroid;
let lever = rotate_vector(transform.q, sub(centroid, local_center));
let shape_velocity = add(linear_velocity, cross_sv(angular_velocity, lever));
let relative_velocity = mul_sub(wind, drag, shape_velocity);
let mut speed = 0.0;
let direction = get_length_and_normalize(&mut speed, relative_velocity);
let mut d = sub(capsule.center2, capsule.center1);
d = rotate_vector(transform.q, d);
let radius = capsule.radius;
let projected_area = 2.0 * radius + abs_float(cross(d, direction));
let mut normal = left_perp(normalize(d));
if dot(normal, direction) > 0.0 {
normal = neg(normal);
}
let lift_direction = cross_sv(cross(normal, direction), direction);
let force_magnitude = 0.5 * air_density * projected_area * speed * speed;
force = mul_sv(force_magnitude, mul_add(direction, lift, lift_direction));
let edge_lever = mul_add(lever, radius, normal);
torque = cross(edge_lever, force);
}
ShapeGeometry::Polygon(polygon) => {
let centroid = shape.local_centroid;
let lever = rotate_vector(transform.q, sub(centroid, local_center));
let shape_velocity = add(linear_velocity, cross_sv(angular_velocity, lever));
let relative_velocity = mul_sub(wind, drag, shape_velocity);
let mut speed = 0.0;
let direction = get_length_and_normalize(&mut speed, relative_velocity);
let count = polygon.count as usize;
let vertices = &polygon.vertices;
let mut v1 = vertices[count - 1];
for vertex in vertices[..count].iter() {
let v2 = *vertex;
let mut d = sub(v2, v1);
let edge_center = lerp(v1, v2, 0.5);
v1 = v2;
d = rotate_vector(transform.q, d);
let projected_area = cross(d, direction);
if projected_area <= 0.0 {
continue;
}
let normal = right_perp(normalize(d));
let lift_direction = cross_sv(cross(normal, direction), direction);
let force_magnitude = 0.5 * air_density * projected_area * speed * speed;
let f = mul_sv(force_magnitude, mul_add(direction, lift, lift_direction));
let edge_lever = rotate_vector(transform.q, sub(edge_center, local_center));
force = add(force, f);
torque += cross(edge_lever, f);
}
}
_ => {}
}
let sim = &mut world.solver_sets[AWAKE_SET as usize].body_sims[local_index as usize];
sim.force = add(sim.force, force);
sim.torque += torque;
}