Initial commit
This commit is contained in:
363
physics/gjk.jai
Normal file
363
physics/gjk.jai
Normal file
@@ -0,0 +1,363 @@
|
||||
Simplex :: struct {
|
||||
points: [4] Vector3;
|
||||
size: s32;
|
||||
}
|
||||
|
||||
Edge :: struct {
|
||||
i1: s64;
|
||||
i2: s64;
|
||||
}
|
||||
|
||||
Collision_Point :: struct {
|
||||
normal: Vector3;
|
||||
penetration_depth: float;
|
||||
has_collision: bool;
|
||||
}
|
||||
|
||||
get_face_normals :: (polytope: [] Vector3, faces: [] s64) -> [..] Vector4, s64 {
|
||||
normals : [..] Vector4;
|
||||
normals.allocator = temp;
|
||||
|
||||
min_triangle : s64 = 0;
|
||||
min_distance := FLOAT32_MAX;
|
||||
|
||||
i := 0;
|
||||
while i < faces.count {
|
||||
defer i += 3;
|
||||
|
||||
a := polytope[faces[i+0]];
|
||||
b := polytope[faces[i+1]];
|
||||
c := polytope[faces[i+2]];
|
||||
|
||||
normal := normalize(cross(b - a, c - a));
|
||||
distance := dot(normal, a);
|
||||
|
||||
if distance < 0 {
|
||||
normal *= -1.0;
|
||||
distance *= -1.0;
|
||||
}
|
||||
|
||||
data : Vector4;
|
||||
data.x = normal.x;
|
||||
data.y = normal.y;
|
||||
data.z = normal.z;
|
||||
data.w = distance;
|
||||
array_add(*normals, data);
|
||||
|
||||
if distance < min_distance {
|
||||
min_triangle = i / 3;
|
||||
min_distance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return normals, min_triangle;
|
||||
}
|
||||
|
||||
add_if_unique_edge :: (edges: *[..] Edge, faces: [] s64, a: s64, b: s64) {
|
||||
i := 0;
|
||||
found := false;
|
||||
while i < edges.count {
|
||||
defer i += 1;
|
||||
|
||||
edge := edges.*[i];
|
||||
if edge.i1 == faces[b] && edge.i2 == faces[a] {
|
||||
array_unordered_remove_by_index(edges, i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
array_add(edges, .{faces[a], faces[b]});
|
||||
}
|
||||
}
|
||||
|
||||
nearly_equal :: (a: float, b: float) -> bool {
|
||||
return abs(a - b) < 0.00001;
|
||||
}
|
||||
|
||||
epa :: (simplex: Simplex, collider_a: Collider, collider_b: Collider) -> Collision_Point {
|
||||
polytope : [..] Vector3;
|
||||
polytope.allocator = temp;
|
||||
|
||||
array_reserve(*polytope, simplex.size);
|
||||
for simplex.points {
|
||||
array_add(*polytope, it);
|
||||
}
|
||||
|
||||
faces : [..] s64;
|
||||
faces.allocator = temp;
|
||||
array_resize(*faces, 12);
|
||||
|
||||
faces[0] = 0;
|
||||
faces[1] = 1;
|
||||
faces[2] = 2;
|
||||
faces[3] = 0;
|
||||
faces[4] = 3;
|
||||
faces[5] = 1;
|
||||
faces[6] = 0;
|
||||
faces[7] = 2;
|
||||
faces[8] = 3;
|
||||
faces[9] = 1;
|
||||
faces[10] = 3;
|
||||
faces[11] = 2;
|
||||
|
||||
normals, min_face := get_face_normals(polytope, faces);
|
||||
|
||||
min_normal : Vector3;
|
||||
min_distance := FLOAT32_MAX;
|
||||
|
||||
while nearly_equal(min_distance, FLOAT32_MAX) {
|
||||
min_normal = to_v3(normals[min_face]);
|
||||
min_distance = normals[min_face].w;
|
||||
|
||||
s := support(collider_a, collider_b, min_normal);
|
||||
s_distance := dot(min_normal, s);
|
||||
|
||||
if abs(s_distance - min_distance) > 0.01 {
|
||||
min_distance = FLOAT32_MAX;
|
||||
|
||||
unique_edges : [..] Edge;
|
||||
unique_edges.allocator = temp;
|
||||
|
||||
i := 0;
|
||||
while i < normals.count {
|
||||
defer i += 1;
|
||||
if dot(to_v3(normals[i]), s) > dot(to_v3(normals[i]), polytope[faces[i*3]]) { //same_direction(to_v3(normals[i]), s) {
|
||||
f := i * 3;
|
||||
add_if_unique_edge(*unique_edges, faces, f, f + 1);
|
||||
add_if_unique_edge(*unique_edges, faces, f + 1, f + 2);
|
||||
add_if_unique_edge(*unique_edges, faces, f + 2, f);
|
||||
|
||||
faces[f+2] = faces[faces.count-1];
|
||||
faces[f+1] = faces[faces.count-2];
|
||||
faces[f+0] = faces[faces.count-3];
|
||||
|
||||
faces.count -= 3;
|
||||
|
||||
normals[i] = normals[normals.count-1];
|
||||
normals.count -= 1;
|
||||
i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
new_faces : [..] s64;
|
||||
new_faces.allocator = temp;
|
||||
for e: unique_edges {
|
||||
array_add(*new_faces, e.i1);
|
||||
array_add(*new_faces, e.i2);
|
||||
array_add(*new_faces, polytope.count);
|
||||
}
|
||||
|
||||
array_add(*polytope, s);
|
||||
|
||||
new_normals, new_min_face := get_face_normals(polytope, new_faces);
|
||||
|
||||
old_min_distance := FLOAT32_MAX;
|
||||
|
||||
for n: normals {
|
||||
if n.w < old_min_distance {
|
||||
old_min_distance = n.w;
|
||||
min_face = it_index;
|
||||
}
|
||||
}
|
||||
|
||||
if new_normals[new_min_face].w < old_min_distance {
|
||||
min_face = new_min_face + normals.count;
|
||||
}
|
||||
|
||||
|
||||
for f: new_faces {
|
||||
array_add(*faces, f);
|
||||
}
|
||||
|
||||
for n: new_normals {
|
||||
array_add(*normals, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
point : Collision_Point;
|
||||
point.normal = min_normal;
|
||||
point.penetration_depth = min_distance + 0.001;
|
||||
point.has_collision = true;
|
||||
|
||||
return point;
|
||||
}
|
||||
|
||||
find_furthest_point :: (collider: Collider, direction: Vector3) -> Vector3 {
|
||||
max_point : Vector3;
|
||||
max_distance := -FLOAT32_MAX;
|
||||
|
||||
if collider.type == {
|
||||
case .MESH; {
|
||||
for collider.mesh.vertices {
|
||||
distance := dot(it, direction);
|
||||
if distance > max_distance {
|
||||
max_distance = distance;
|
||||
max_point = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max_point;
|
||||
}
|
||||
|
||||
push_front :: (simplex: *Simplex, p: Vector3) {
|
||||
simplex.points[3] = simplex.points[2];
|
||||
simplex.points[2] = simplex.points[1];
|
||||
simplex.points[1] = simplex.points[0];
|
||||
simplex.points[0] = p;
|
||||
|
||||
simplex.size = min(simplex.size + 1, 4);
|
||||
}
|
||||
|
||||
tetrahedron :: (simplex: *Simplex, direction: *Vector3) -> bool {
|
||||
a := simplex.points[0];
|
||||
b := simplex.points[1];
|
||||
c := simplex.points[2];
|
||||
d := simplex.points[3];
|
||||
|
||||
ab := b - a;
|
||||
ac := c - a;
|
||||
ad := d - a;
|
||||
ao := - a;
|
||||
|
||||
abc := cross(ab, ac);
|
||||
acd := cross(ac, ad);
|
||||
adb := cross(ad, ab);
|
||||
|
||||
if same_direction(abc, ao) {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = b;
|
||||
simplex.points[2] = c;
|
||||
simplex.size = 3;
|
||||
return triangle(simplex, direction);
|
||||
}
|
||||
|
||||
if same_direction(acd, ao) {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = c;
|
||||
simplex.points[2] = d;
|
||||
simplex.size = 3;
|
||||
return triangle(simplex, direction);
|
||||
}
|
||||
|
||||
if same_direction(adb, ao) {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = d;
|
||||
simplex.points[2] = b;
|
||||
simplex.size = 3;
|
||||
return triangle(simplex, direction);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
triangle :: (simplex: *Simplex, direction: *Vector3) -> bool {
|
||||
a := simplex.points[0];
|
||||
b := simplex.points[1];
|
||||
c := simplex.points[2];
|
||||
|
||||
ab := b - a;
|
||||
ac := c - a;
|
||||
ao := - a;
|
||||
|
||||
abc := cross(ab, ac);
|
||||
|
||||
if same_direction(cross(abc, ac), ao) {
|
||||
if same_direction(ac, ao) {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = c;
|
||||
simplex.size = 2;
|
||||
direction.* = cross(cross(ac, ao), ac);
|
||||
} else {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = b;
|
||||
simplex.size = 2;
|
||||
return line(simplex, direction);
|
||||
}
|
||||
} else {
|
||||
if same_direction(cross(ab, abc), ao) {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = b;
|
||||
simplex.size = 2;
|
||||
return line(simplex, direction);
|
||||
} else {
|
||||
if same_direction(abc, ao) {
|
||||
direction.* = abc;
|
||||
} else {
|
||||
simplex.points[0] = a;
|
||||
simplex.points[1] = c;
|
||||
simplex.points[2] = b;
|
||||
simplex.size = 3;
|
||||
direction.* = -abc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
same_direction :: (direction: Vector3, ao: Vector3) -> bool {
|
||||
return dot(direction, ao) > 0;
|
||||
}
|
||||
|
||||
line :: (simplex: *Simplex, direction: *Vector3) -> bool {
|
||||
a := simplex.points[0];
|
||||
b := simplex.points[1];
|
||||
|
||||
ab := b - a;
|
||||
ao := - a;
|
||||
|
||||
if same_direction(ab, ao) {
|
||||
direction.* = cross(cross(ab, ao), ab);
|
||||
} else {
|
||||
simplex.points[0] = a;
|
||||
simplex.size = 1;
|
||||
direction.* = ao;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
next_simplex :: (simplex: *Simplex, direction: *Vector3) -> bool {
|
||||
if simplex.size == {
|
||||
case 2; return line(simplex, direction);
|
||||
case 3; return triangle(simplex, direction);
|
||||
case 4; return tetrahedron(simplex, direction);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
support :: (a: Collider, b: Collider, direction: Vector3) -> Vector3 {
|
||||
return find_furthest_point(a, direction) - find_furthest_point(b, -direction);
|
||||
}
|
||||
|
||||
gjk :: (a: Collider, b: Collider) -> Collision_Point {
|
||||
sup := support(a, b, .{1,0,0});
|
||||
|
||||
points : Simplex;
|
||||
push_front(*points, sup);
|
||||
|
||||
direction := -sup;
|
||||
|
||||
while true {
|
||||
sup = support(a, b, direction);
|
||||
|
||||
if dot(sup, direction) <= 0 {
|
||||
return .{};
|
||||
}
|
||||
|
||||
push_front(*points, sup);
|
||||
|
||||
if next_simplex(*points, *direction) {
|
||||
collision_point := epa(points, a, b);
|
||||
return collision_point;
|
||||
}
|
||||
}
|
||||
|
||||
return .{};
|
||||
}
|
||||
421
physics/physics.jai
Normal file
421
physics/physics.jai
Normal file
@@ -0,0 +1,421 @@
|
||||
#load "gjk.jai";
|
||||
|
||||
WORLD_UP :: Vector3.{0,1,0};
|
||||
GRAVITY :: -20.8;
|
||||
|
||||
Collider_Type :: enum {
|
||||
AABB;
|
||||
SPHERE;
|
||||
MESH;
|
||||
BOX;
|
||||
}
|
||||
|
||||
Sphere :: struct {
|
||||
radius: float;
|
||||
}
|
||||
|
||||
Mesh_Collider :: struct {
|
||||
vertices : [..] Vector3;
|
||||
is_baked: bool;
|
||||
}
|
||||
|
||||
Trigger_Overlap :: struct {
|
||||
entity: *Entity;
|
||||
frame_index: u64;
|
||||
}
|
||||
|
||||
MAX_TRIGGER_OVERLAPS :: 16;
|
||||
|
||||
Collider :: struct {
|
||||
type : Collider_Type;
|
||||
|
||||
aabb: AABB;
|
||||
override_aabb: bool;
|
||||
union {
|
||||
sphere: Sphere;
|
||||
mesh : Mesh_Collider;
|
||||
}
|
||||
|
||||
overlaps: [MAX_TRIGGER_OVERLAPS] Trigger_Overlap;
|
||||
num_overlaps: s64;
|
||||
|
||||
ignore: bool;
|
||||
}
|
||||
|
||||
Physics_Body :: struct {
|
||||
velocity: Vector3;
|
||||
|
||||
friction : float = 0.0;
|
||||
bounciness : float = 0.0;
|
||||
linear_damping : float = 0.0;
|
||||
|
||||
check_for_grounded: bool;
|
||||
grounded: bool;
|
||||
}
|
||||
|
||||
update_mesh_collider :: (e: *Entity) {
|
||||
if e.collider.mesh.vertices.count == 0 {
|
||||
array_resize(*e.collider.mesh.vertices, 8);
|
||||
}
|
||||
|
||||
m := e.transform.model_matrix;
|
||||
e.collider.mesh.vertices[0] = transform_position(e.collider.aabb.min, m);
|
||||
e.collider.mesh.vertices[1] = transform_position(e.collider.aabb.max, m);
|
||||
e.collider.mesh.vertices[2] = transform_position(.{e.collider.aabb.min.x, e.collider.aabb.min.y, e.collider.aabb.max.z}, m);
|
||||
e.collider.mesh.vertices[3] = transform_position(.{e.collider.aabb.max.x, e.collider.aabb.min.y, e.collider.aabb.max.z}, m);
|
||||
e.collider.mesh.vertices[4] = transform_position(.{e.collider.aabb.max.x, e.collider.aabb.min.y, e.collider.aabb.min.z}, m);
|
||||
e.collider.mesh.vertices[5] = transform_position(.{e.collider.aabb.min.x, e.collider.aabb.max.y, e.collider.aabb.max.z}, m);
|
||||
e.collider.mesh.vertices[6] = transform_position(.{e.collider.aabb.max.x, e.collider.aabb.max.y, e.collider.aabb.min.z}, m);
|
||||
e.collider.mesh.vertices[7] = transform_position(.{e.collider.aabb.min.x, e.collider.aabb.max.y, e.collider.aabb.min.z}, m);
|
||||
|
||||
e.collider.mesh.is_baked = true;
|
||||
}
|
||||
|
||||
update_mesh_colliders :: (scene: *Scene) {
|
||||
for e: scene.entities {
|
||||
if e.flags & .COLLISION {
|
||||
if e.collider.type == .MESH {
|
||||
if e.flags & .STATIC && e.collider.mesh.is_baked continue;
|
||||
|
||||
update_mesh_collider(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_sure_entity_does_not_collide :: (e: *Entity, scene: *Scene) {
|
||||
if e.flags & .PHYSICS {
|
||||
aabb := e.collider.aabb;
|
||||
aabb.min += e.transform.position;
|
||||
aabb.max += e.transform.position;
|
||||
|
||||
for other_e: scene.entities {
|
||||
if e == other_e continue;
|
||||
|
||||
if other_e.flags & .COLLISION {
|
||||
other_aabb := other_e.collider.aabb;
|
||||
other_aabb.min += other_e.transform.position;
|
||||
other_aabb.max += other_e.transform.position;
|
||||
if aabb_vs_aabb(aabb, other_aabb) {
|
||||
offset := resolve_aabb_vs_aabb(aabb, other_aabb);
|
||||
set_position(*e.transform, e.transform.position + offset * 1.0001);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
make_sure_nothing_collides :: (scene: *Scene) {
|
||||
for e: scene.entities {
|
||||
make_sure_entity_does_not_collide(e, scene);
|
||||
}
|
||||
}
|
||||
|
||||
update_gravity :: (scene: *Scene, dt: float) {
|
||||
for e: scene.entities {
|
||||
if !e.enabled continue;
|
||||
|
||||
if e.flags & .PHYSICS {
|
||||
if e.is_proxy continue;
|
||||
if e.collider.ignore continue;
|
||||
e.body.velocity.y += GRAVITY * dt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update_positions :: (scene: *Scene, dt: float) {
|
||||
for e: scene.entities {
|
||||
if !e.enabled continue;
|
||||
if e.is_proxy continue;
|
||||
if e.collider.ignore continue;
|
||||
|
||||
if e.flags & .PHYSICS {
|
||||
delta := e.body.velocity * dt;
|
||||
set_position(*e.transform, e.transform.position + delta);
|
||||
|
||||
// @Speed: Only do this, if we actually moved
|
||||
m := e.transform.model_matrix;
|
||||
if e.collider.mesh.vertices.count < 8 continue;
|
||||
|
||||
e.collider.mesh.vertices[0] = transform_position(e.collider.aabb.min, m);
|
||||
e.collider.mesh.vertices[1] = transform_position(e.collider.aabb.max, m);
|
||||
e.collider.mesh.vertices[2] = transform_position(.{e.collider.aabb.min.x, e.collider.aabb.min.y, e.collider.aabb.max.z}, m);
|
||||
e.collider.mesh.vertices[3] = transform_position(.{e.collider.aabb.max.x, e.collider.aabb.min.y, e.collider.aabb.max.z}, m);
|
||||
e.collider.mesh.vertices[4] = transform_position(.{e.collider.aabb.max.x, e.collider.aabb.min.y, e.collider.aabb.min.z}, m);
|
||||
e.collider.mesh.vertices[5] = transform_position(.{e.collider.aabb.min.x, e.collider.aabb.max.y, e.collider.aabb.max.z}, m);
|
||||
e.collider.mesh.vertices[6] = transform_position(.{e.collider.aabb.max.x, e.collider.aabb.max.y, e.collider.aabb.min.z}, m);
|
||||
e.collider.mesh.vertices[7] = transform_position(.{e.collider.aabb.min.x, e.collider.aabb.max.y, e.collider.aabb.min.z}, m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add_trigger_overlap_if_new :: (e: *Entity, other_e: *Entity) {
|
||||
for 0..e.collider.num_overlaps-1 {
|
||||
overlap := *e.collider.overlaps[it];
|
||||
if overlap.entity == other_e {
|
||||
overlap.frame_index = frame_index;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
on_trigger_enter(e, other_e);
|
||||
e.collider.overlaps[e.collider.num_overlaps] = .{ other_e, frame_index };
|
||||
e.collider.num_overlaps += 1;
|
||||
}
|
||||
|
||||
physics_step :: (scene: *Scene, timestep: float) {
|
||||
update_gravity(scene, timestep);
|
||||
update_positions(scene, timestep);
|
||||
|
||||
for e: scene.entities {
|
||||
if !e.enabled continue;
|
||||
if e.is_proxy continue;
|
||||
if e.collider.ignore continue;
|
||||
|
||||
if e.flags & .PHYSICS {
|
||||
for other_e: scene.entities {
|
||||
if e == other_e continue;
|
||||
if other_e.collider.ignore continue;
|
||||
|
||||
if other_e.flags & .COLLISION {
|
||||
point := gjk(e.collider, other_e.collider);
|
||||
|
||||
if point.has_collision {
|
||||
if other_e.flags & .TRIGGER {
|
||||
// TRIGGER CALLBACK
|
||||
add_trigger_overlap_if_new(e, other_e);
|
||||
} else {
|
||||
n := -point.normal;
|
||||
speed_along_normal := dot(e.body.velocity, n);
|
||||
|
||||
restitution := e.body.bounciness;
|
||||
impulse := n * (-(1.0 + restitution) * speed_along_normal);
|
||||
e.body.velocity += impulse;
|
||||
|
||||
percent := 0.1;
|
||||
slop := 0.005;
|
||||
correction := n * max(point.penetration_depth - slop, 0.0) / (1.0 / percent);
|
||||
set_position(*e.transform, e.transform.position + correction);
|
||||
|
||||
if e.body.check_for_grounded {
|
||||
e.body.grounded = dot(n, WORLD_UP) > 0.6; // @Incomplete: Add allowed angle variable at some point?
|
||||
}
|
||||
|
||||
// @Incomplete: This shouldn't be in here
|
||||
//if e.type == Diamond && length(impulse) > 2.0 {
|
||||
// play_audio_event(sfx_diamond_hit);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
on_trigger_enter :: (e: *Entity, other_e: *Entity) {
|
||||
//if (e.type == Player || e.type == Npc) && other_e.type == Door {
|
||||
// other_e.enabled = false;
|
||||
//}
|
||||
}
|
||||
|
||||
on_trigger_exit :: (e: *Entity, other_e: *Entity) {
|
||||
//if other_e.type == Door {
|
||||
// other_e.enabled = true;
|
||||
//}
|
||||
}
|
||||
|
||||
update_physics :: (scene: *Scene, dt: float) {
|
||||
for scene.entities {
|
||||
if it.collider.type == .MESH {
|
||||
if !it.collider.mesh.is_baked {
|
||||
update_mesh_collider(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iterations := 4;
|
||||
step_time := dt / cast(float)iterations;
|
||||
for 0..iterations-1 {
|
||||
physics_step(scene, step_time);
|
||||
|
||||
for e: scene.entities {
|
||||
if e.flags & .PHYSICS {
|
||||
//if e.body.friction > 0.0 {
|
||||
// e.body.velocity *= 1.0 - (e.body.friction / cast(float)iterations);
|
||||
//}
|
||||
|
||||
e.body.velocity *= (1.0 - e.body.linear_damping / cast(float)iterations);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for e: scene.entities {
|
||||
index := 0;
|
||||
while index < e.collider.num_overlaps {
|
||||
defer index += 1;
|
||||
|
||||
if e.collider.overlaps[index].frame_index < frame_index {
|
||||
on_trigger_exit(e, e.collider.overlaps[index].entity);
|
||||
|
||||
if e.collider.num_overlaps > 1 {
|
||||
e.collider.overlaps[index] = e.collider.overlaps[e.collider.num_overlaps-1];
|
||||
}
|
||||
|
||||
e.collider.num_overlaps -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DISCRETE
|
||||
aabb_vs_aabb :: (box1: AABB, box2: AABB) -> bool {
|
||||
// Check for no overlap along any axis
|
||||
if box1.max.x < box2.min.x || box1.min.x > box2.max.x return false;
|
||||
if box1.max.y < box2.min.y || box1.min.y > box2.max.y return false;
|
||||
if box1.max.z < box2.min.z || box1.min.z > box2.max.z return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
resolve_aabb_vs_aabb :: (moving_box: AABB, static_box: AABB) -> Vector3 {
|
||||
overlap_x := min(moving_box.max.x, static_box.max.x) - max(moving_box.min.x, static_box.min.x);
|
||||
overlap_y := min(moving_box.max.y, static_box.max.y) - max(moving_box.min.y, static_box.min.y);
|
||||
overlap_z := min(moving_box.max.z, static_box.max.z) - max(moving_box.min.z, static_box.min.z);
|
||||
|
||||
offset : Vector3;
|
||||
|
||||
// Resolve overlap on each axis
|
||||
if (overlap_x > 0 && overlap_y > 0 && overlap_z > 0) {
|
||||
// Determine which axis has the smallest overlap
|
||||
if (overlap_x <= overlap_y && overlap_x <= overlap_z) {
|
||||
// Resolve overlap on X-axis
|
||||
if (moving_box.max.x < static_box.max.x) {
|
||||
offset.x -= overlap_x;
|
||||
} else {
|
||||
offset.x += overlap_x;
|
||||
}
|
||||
} else if (overlap_y <= overlap_x && overlap_y <= overlap_z) {
|
||||
// Resolve overlap on Y-axis
|
||||
if (moving_box.max.y < static_box.max.y) {
|
||||
offset.y -= overlap_y;
|
||||
} else {
|
||||
offset.y += overlap_y;
|
||||
}
|
||||
} else {
|
||||
// Resolve overlap on Z-axis
|
||||
if (moving_box.max.z < static_box.max.z) {
|
||||
offset.z -= overlap_z;
|
||||
} else {
|
||||
offset.z += overlap_z;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
// SWEPT
|
||||
// Check for collision between two AABBs over a specified time interval
|
||||
swept_aabb_collision :: (box1: AABB, box2: AABB, delta: Vector3) -> bool, t_enter: float, t_exit: float {
|
||||
d_inv := 1.0 / delta;
|
||||
|
||||
tx_enter, tx_exit, ty_enter, ty_exit, tz_enter, tz_exit : float;
|
||||
|
||||
if (delta.x >= 0) {
|
||||
tx_enter = (box2.min.x - box1.max.x) * d_inv.x;
|
||||
tx_exit = (box2.max.x - box1.min.x) * d_inv.x;
|
||||
} else {
|
||||
tx_enter = (box2.max.x - box1.min.x) * d_inv.x;
|
||||
tx_exit = (box2.min.x - box1.max.x) * d_inv.x;
|
||||
}
|
||||
|
||||
if (delta.y >= 0) {
|
||||
ty_enter = (box2.min.y - box1.max.y) * d_inv.y;
|
||||
ty_exit = (box2.max.y - box1.min.y) * d_inv.y;
|
||||
} else {
|
||||
ty_enter = (box2.max.y - box1.min.y) * d_inv.y;
|
||||
ty_exit = (box2.min.y - box1.max.y) * d_inv.y;
|
||||
}
|
||||
|
||||
if (delta.z >= 0) {
|
||||
tz_enter = (box2.min.z - box1.max.z) * d_inv.z;
|
||||
tz_exit = (box2.max.z - box1.min.z) * d_inv.z;
|
||||
} else {
|
||||
tz_enter = (box2.max.z - box1.min.z) * d_inv.z;
|
||||
tz_exit = (box2.min.z - box1.max.z) * d_inv.z;
|
||||
}
|
||||
|
||||
t_enter := max(max(tx_enter, ty_enter), tz_enter);
|
||||
t_exit := min(min(tx_exit, ty_exit), tz_exit);
|
||||
|
||||
return t_enter <= t_exit && t_exit >= 0 && t_enter <= 1, t_enter, t_exit;
|
||||
}
|
||||
|
||||
calculate_aabbs :: (scene: *Scene) {
|
||||
for e: scene.entities {
|
||||
if e.flags & .COLLISION && e.flags & .RENDERABLE {
|
||||
if e.collider.override_aabb continue;
|
||||
|
||||
aabb : AABB;
|
||||
|
||||
for n : e.renderable.model.nodes {
|
||||
if n.parent == 0 {
|
||||
bake_aabb(*aabb, Matrix4_Identity, e, n);
|
||||
}
|
||||
}
|
||||
|
||||
e.collider.aabb = aabb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bake_aabb :: (aabb: *AABB, parent_matrix: Matrix4, e: *Entity, n: Node) {
|
||||
update_matrix(*n.transform);
|
||||
node_matrix := parent_matrix * n.transform.model_matrix;
|
||||
|
||||
for handle : n.meshes {
|
||||
index := 0;
|
||||
m := parray_get(*renderer.meshes, handle);
|
||||
if m.indices.count > 0 {
|
||||
while index < m.indices.count {
|
||||
i1 := m.indices[index];
|
||||
i2 := m.indices[index + 1];
|
||||
i3 := m.indices[index + 2];
|
||||
p0 := to_v3(node_matrix * to_v4(m.positions[i1]));
|
||||
p1 := to_v3(node_matrix * to_v4(m.positions[i2]));
|
||||
p2 := to_v3(node_matrix * to_v4(m.positions[i3]));
|
||||
|
||||
apply_min_max(*aabb.min, *aabb.max, p0);
|
||||
apply_min_max(*aabb.min, *aabb.max, p1);
|
||||
apply_min_max(*aabb.min, *aabb.max, p2);
|
||||
|
||||
index += 3;
|
||||
}
|
||||
// assert("Meshes with indices currently for aabb collision baking." && false);
|
||||
} else {
|
||||
while index < m.positions.count - 1 {
|
||||
p0 := to_v3(node_matrix * to_v4(m.positions[index]));
|
||||
p1 := to_v3(node_matrix * to_v4(m.positions[index + 1]));
|
||||
p2 := to_v3(node_matrix * to_v4(m.positions[index + 2]));
|
||||
|
||||
apply_min_max(*aabb.min, *aabb.max, p0);
|
||||
apply_min_max(*aabb.min, *aabb.max, p1);
|
||||
apply_min_max(*aabb.min, *aabb.max, p2);
|
||||
|
||||
index += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for n.children {
|
||||
child := *e.renderable.model.nodes[it - 1];
|
||||
bake_aabb(aabb, node_matrix, e, child);
|
||||
}
|
||||
|
||||
if abs(aabb.min.y - aabb.max.y) < 0.00001 {
|
||||
aabb.min.y -= 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user