205 lines
3.9 KiB
Odin
205 lines
3.9 KiB
Odin
package collision
|
|
|
|
import "core:math"
|
|
import lg "core:math/linalg"
|
|
import "game:halfedge"
|
|
|
|
Convex :: distinct halfedge.Half_Edge_Mesh
|
|
|
|
BOX_CORNERS_NORM :: [8]Vec3 {
|
|
Vec3{-1, -1, -1},
|
|
Vec3{-1, -1, 1},
|
|
Vec3{-1, 1, -1},
|
|
Vec3{-1, 1, 1},
|
|
Vec3{1, -1, -1},
|
|
Vec3{1, -1, 1},
|
|
Vec3{1, 1, -1},
|
|
Vec3{1, 1, 1},
|
|
}
|
|
|
|
box_indices := [6 * 4]u16 {
|
|
// near
|
|
0,
|
|
4,
|
|
6,
|
|
2,
|
|
// right
|
|
4,
|
|
5,
|
|
7,
|
|
6,
|
|
// far
|
|
5,
|
|
1,
|
|
3,
|
|
7,
|
|
// left
|
|
1,
|
|
0,
|
|
2,
|
|
3,
|
|
// top
|
|
2,
|
|
6,
|
|
7,
|
|
3,
|
|
// bottom
|
|
0,
|
|
4,
|
|
5,
|
|
1,
|
|
}
|
|
|
|
box_to_convex :: proc(box: Box, allocator := context.allocator) -> (convex: Convex) {
|
|
vertices := make([]Vec3, 8, context.temp_allocator)
|
|
|
|
for corner, i in BOX_CORNERS_NORM {
|
|
vertices[i] = corner * box.rad
|
|
}
|
|
|
|
convex = Convex(halfedge.mesh_from_vertex_index_list(vertices, box_indices[:], 4, allocator))
|
|
|
|
return
|
|
}
|
|
|
|
Contact_Manifold :: struct {
|
|
points: []Vec3,
|
|
normal: Vec3,
|
|
}
|
|
|
|
convex_vs_convex_sat :: proc(a, b: Convex) -> (manifold: Contact_Manifold, collision: bool) {
|
|
face_query_a := query_separation_face_directions(a, b)
|
|
if face_query_a.separation > 0 {
|
|
return
|
|
}
|
|
face_query_b := query_separation_face_directions(b, a)
|
|
if face_query_b.separation > 0 {
|
|
return
|
|
}
|
|
|
|
edge_separation, edge_a, edge_b := query_separation_edges(a, b)
|
|
if edge_separation > 0 {
|
|
return
|
|
}
|
|
|
|
is_face_a_contact := face_query_a.separation > edge_separation
|
|
is_face_b_contact := face_query_b.separation > edge_separation
|
|
|
|
collision = true
|
|
if is_face_a_contact && is_face_b_contact {
|
|
manifold = create_face_contact_manifold(face_query_a, a, face_query_b, b)
|
|
} else {
|
|
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
Face_Query :: struct {
|
|
separation: f32,
|
|
face: halfedge.Face_Index,
|
|
}
|
|
|
|
query_separation_face_directions :: proc(a, b: Convex) -> (result: Face_Query) {
|
|
result.separation = min(f32)
|
|
for face, f in a.faces {
|
|
index := a.edges[face.edge].origin
|
|
|
|
pos := a.vertices[index].pos
|
|
normal := face.normal
|
|
|
|
support_point := find_support_point(b, -normal)
|
|
|
|
plane := plane_from_point_normal(pos, normal)
|
|
|
|
distance := signed_distance_plane(support_point, plane)
|
|
if distance > result.separation {
|
|
result.face = halfedge.Face_Index(f)
|
|
result.separation = distance
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
find_support_point :: proc(convex: Convex, normal: Vec3) -> Vec3 {
|
|
p: Vec3
|
|
max_proj := min(f32)
|
|
for vert in convex.vertices {
|
|
proj := lg.dot(vert.pos, normal)
|
|
if proj > max_proj {
|
|
max_proj = proj
|
|
p = vert.pos
|
|
}
|
|
}
|
|
|
|
return p
|
|
}
|
|
|
|
query_separation_edges :: proc(
|
|
a, b: Convex,
|
|
) -> (
|
|
separation: f32,
|
|
a_edge: halfedge.Edge_Index,
|
|
b_edge: halfedge.Edge_Index,
|
|
) {
|
|
separation = min(f32)
|
|
a_edge = -1
|
|
b_edge = -1
|
|
|
|
for edge_a, edge_a_idx in a.edges {
|
|
for edge_b, edge_b_idx in b.edges {
|
|
edge_a_dir := halfedge.get_edge_direction_normalized(
|
|
halfedge.Half_Edge_Mesh(a),
|
|
edge_a,
|
|
)
|
|
edge_b_dir := halfedge.get_edge_direction_normalized(
|
|
halfedge.Half_Edge_Mesh(b),
|
|
edge_b,
|
|
)
|
|
|
|
axis := lg.cross(edge_a_dir, edge_b_dir)
|
|
|
|
edge_a_origin := a.vertices[edge_a.origin].pos
|
|
if lg.dot(axis, edge_a_origin - a.center) < 0 {
|
|
axis = -axis
|
|
}
|
|
|
|
plane_a := plane_from_point_normal(edge_a_origin, axis)
|
|
vert_b := find_support_point(b, -plane_a.normal)
|
|
distance := signed_distance_plane(vert_b, plane_a)
|
|
if distance > separation {
|
|
separation = distance
|
|
a_edge = halfedge.Edge_Index(edge_a_idx)
|
|
b_edge = halfedge.Edge_Index(edge_b_idx)
|
|
}
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
create_face_contact_manifold :: proc(
|
|
face_query_a: Face_Query,
|
|
a: Convex,
|
|
face_query_b: Face_Query,
|
|
b: Convex,
|
|
) -> (
|
|
manifold: Contact_Manifold,
|
|
) {
|
|
is_ref_a := face_query_a.separation > face_query_b.separation
|
|
ref_face_query := is_ref_a ? face_query_a : face_query_b
|
|
ref_convex := is_ref_a ? a : b
|
|
|
|
inc_face_query := is_ref_a ? face_query_b : face_query_a
|
|
inc_convex := is_ref_a ? b : a
|
|
|
|
it := halfedge.iterator_face_edges(halfedge.Half_Edge_Mesh(ref_convex), ref_face_query.face)
|
|
|
|
for edge in halfedge.iterate_next_edge(&it) {
|
|
clipping_face := halfedge.get_adjacent_face(halfedge.Half_Edge_Mesh(ref_convex), edge)
|
|
}
|
|
|
|
return
|
|
}
|