897 lines
27 KiB
C
897 lines
27 KiB
C
#ifndef SCONVCOL_H
|
|
#define SCONVCOL_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <float.h>
|
|
#include <stdbool.h>
|
|
|
|
#include "vectorial/simd4x4f.h"
|
|
|
|
#define SCH_EPS 0.001
|
|
|
|
inline bool sch_simd4f_equal(simd4f a, simd4f b) {
|
|
return (simd4f_get_x(simd4f_length4_squared(simd4f_sub(a, b))) == 0.f);
|
|
}
|
|
|
|
inline bool sch_simd4f_close(simd4f a, simd4f b, float tol) {
|
|
return (simd4f_get_x(simd4f_length4_squared(simd4f_sub(a, b))) < tol * tol);
|
|
}
|
|
|
|
inline void sch_simd4x4f_transform_inv(simd4x4f* trans, simd4x4f* out) {
|
|
*out = *trans;
|
|
simd4f w = simd4f_create(
|
|
-simd4f_get_x(trans->w),
|
|
-simd4f_get_y(trans->w),
|
|
-simd4f_get_z(trans->w),
|
|
1.f);
|
|
|
|
out->w = simd4f_create(0.f, 0.f, 0.f, 1.f);
|
|
simd4x4f_transpose_inplace(out);
|
|
|
|
simd4f out_w;
|
|
simd4x4f_matrix_vector_mul(out, &w, &out_w);
|
|
out->w = out_w;
|
|
}
|
|
|
|
typedef struct sch_edge sch_edge;
|
|
typedef struct sch_vert sch_vert;
|
|
typedef struct sch_face sch_face;
|
|
typedef struct sch_hull sch_hull;
|
|
typedef struct sch_plane sch_plane;
|
|
typedef struct sch_hull_builder sch_hull_builder;
|
|
typedef struct sch_face_query sch_face_query;
|
|
typedef struct sch_edge_query sch_edge_query;
|
|
typedef struct sch_manifold sch_manifold;
|
|
|
|
struct sch_edge {
|
|
sch_vert* vert;
|
|
sch_edge* twin;
|
|
sch_face* face;
|
|
sch_edge* next;
|
|
};
|
|
|
|
struct sch_vert {
|
|
simd4f p;
|
|
sch_edge* edge;
|
|
};
|
|
|
|
struct sch_face {
|
|
sch_edge* edge;
|
|
};
|
|
|
|
struct sch_hull {
|
|
sch_face* faces;
|
|
sch_edge* edges;
|
|
sch_vert* vertices;
|
|
simd4f center;
|
|
int num_faces;
|
|
int num_edges;
|
|
int num_vertices;
|
|
};
|
|
|
|
struct sch_plane {
|
|
simd4f p;
|
|
simd4f n;
|
|
};
|
|
|
|
struct sch_face_query {
|
|
float dist;
|
|
int face_idx;
|
|
sch_plane plane;
|
|
simd4f vert;
|
|
};
|
|
|
|
struct sch_edge_query {
|
|
float dist;
|
|
sch_plane plane;
|
|
int edge_A_idx;
|
|
int edge_B_idx;
|
|
};
|
|
|
|
struct sch_manifold {
|
|
int num_points;
|
|
int num_vertices;
|
|
simd4f* vertices;
|
|
simd4f normal;
|
|
};
|
|
|
|
//
|
|
// Hull Builder
|
|
//
|
|
|
|
#define SCH_BUILDER_MAX_NUM_VERTICES 1024
|
|
#define SCH_BUILDER_MAX_NUM_FACES 256
|
|
#define SCH_BUILDER_MAX_NUM_FACE_VERTICES 32
|
|
|
|
enum SchHullResult {
|
|
SchHullResultOK = 0,
|
|
SchHullResultConcaveVertex = -1,
|
|
SchHullResultWrongWinding = -2,
|
|
SchHullResultInvalidTwinEdges = -3,
|
|
SchHullResultOpenHull = -4
|
|
};
|
|
|
|
typedef enum SchHullResult SchHullResult;
|
|
|
|
struct sch_hull_builder {
|
|
int num_vertices;
|
|
int num_faces;
|
|
simd4f vertices[SCH_BUILDER_MAX_NUM_VERTICES];
|
|
int face_vert_idx[SCH_BUILDER_MAX_NUM_FACES * SCH_BUILDER_MAX_NUM_VERTICES];
|
|
int face_vert_idx_start[SCH_BUILDER_MAX_NUM_FACES];
|
|
int face_vert_idx_end[SCH_BUILDER_MAX_NUM_FACES];
|
|
};
|
|
|
|
void sch_builder_reset(sch_hull_builder* builder);
|
|
|
|
void sch_builder_face_begin(sch_hull_builder* builder);
|
|
|
|
void sch_builder_face_vertex(sch_hull_builder* builder, simd4f vertex);
|
|
|
|
void sch_builder_face_end(sch_hull_builder* builder);
|
|
|
|
SchHullResult sch_builder_create_hull(sch_hull_builder* builder, sch_hull* out_hull);
|
|
|
|
//
|
|
// Helper data structures
|
|
//
|
|
void sch_manifold_alloc (sch_manifold* manifold, int num_vertices);
|
|
void sch_manifold_add_point (sch_manifold* manifold, simd4f p);
|
|
void sch_manifold_reset (sch_manifold* manifold);
|
|
void sch_manifold_free_memory (sch_manifold* manifold);
|
|
|
|
//
|
|
// Calculations
|
|
//
|
|
|
|
float sch_plane_distance(const sch_plane* plane, const simd4f* v);
|
|
|
|
bool sch_plane_intersection(const sch_plane* plane, const simd4f* p0, const simd4f* p1, simd4f* result);
|
|
|
|
void sch_plane_point_project(const sch_plane* plane, simd4f* p);
|
|
|
|
void sch_edge_get_dir(const sch_edge* edge, simd4f* out_dir);
|
|
|
|
void sch_hull_free_memory (sch_hull* hull);
|
|
|
|
void sch_hull_translate (sch_hull* hull, float x, float y, float z);
|
|
|
|
void sch_hull_rotate (sch_hull* hull, const float radians, const simd4f axis);
|
|
|
|
void sch_hull_transform (sch_hull* hull, simd4x4f mat);
|
|
|
|
void sch_hull_calc_plane(const sch_hull* hull, const int index, sch_plane* out_plane);
|
|
|
|
sch_edge* sch_hull_find_edge (const sch_hull* hull, const simd4f v0, const simd4f v1);
|
|
|
|
void sch_hull_get_support(const sch_hull* hull, simd4x4f* trans, simd4f n, simd4f* out_vert);
|
|
|
|
float sch_query_face_directions(
|
|
const sch_hull* hull_A,
|
|
simd4x4f* trans_AtoB,
|
|
const sch_hull* hull_B,
|
|
sch_face_query* result);
|
|
|
|
void sch_clip_faces (const sch_face* ref_face, const sch_face* inc_face, sch_manifold* manifold);
|
|
|
|
void sch_create_face_contact (const sch_face_query* query_A_B, const sch_hull* hull_A, const sch_face_query* query_B_A, const sch_hull* hull_B, sch_manifold *result);
|
|
|
|
void sch_create_edge_contact (const sch_edge_query* query_edge, const sch_hull* hull_A, const sch_hull* hull_B, sch_manifold* result);
|
|
|
|
bool sch_hull_sat(const sch_hull* hull_A, simd4x4f* trans_A, const sch_hull* hull_B, simd4x4f* trans_B, sch_manifold* result);
|
|
|
|
int sch_hull_is_vertex_concave(const sch_hull* hull, const simd4f p);
|
|
|
|
SchHullResult sch_hull_is_closed (const sch_hull* hull);
|
|
|
|
SchHullResult sch_hull_connect_face_edges(const sch_hull* hull, int face_index);
|
|
|
|
void sch_create_face(int num_vert, simd4f* vertices, sch_face* out_face);
|
|
|
|
void sch_create_unitbox(sch_hull* out_hull);
|
|
|
|
//
|
|
// sconvcol Implementation
|
|
//
|
|
|
|
#ifdef SCONVCOL_IMPLEMENTATION
|
|
|
|
void sch_manifold_alloc (sch_manifold* manifold, int num_vertices) {
|
|
manifold->vertices = (simd4f*) aligned_alloc(16, sizeof(simd4f) * num_vertices);
|
|
assert (manifold->vertices != NULL);
|
|
manifold->num_vertices = num_vertices;
|
|
sch_manifold_reset(manifold);
|
|
}
|
|
|
|
void sch_manifold_add_point (sch_manifold* manifold, simd4f p) {
|
|
assert (manifold->num_vertices > manifold->num_points);
|
|
manifold->vertices[manifold->num_points] = p;
|
|
manifold->num_points++;
|
|
}
|
|
|
|
void sch_manifold_reset (sch_manifold* manifold) {
|
|
manifold->num_points = 0;
|
|
}
|
|
|
|
void sch_manifold_free_memory (sch_manifold* manifold) {
|
|
free (manifold->vertices);
|
|
manifold->vertices = NULL;
|
|
manifold->num_vertices = 0;
|
|
}
|
|
|
|
float sch_plane_distance(const sch_plane* plane, const simd4f* v) {
|
|
return simd4f_dot3_scalar(simd4f_sub(*v, plane->p), plane->n);
|
|
}
|
|
|
|
bool sch_plane_intersection(const sch_plane* plane, const simd4f* p0, const simd4f* p1, simd4f* result) {
|
|
simd4f line = simd4f_sub (*p1, *p0);
|
|
float line_dot_n = simd4f_dot3_scalar(plane->n, line);
|
|
if (fabs(line_dot_n) < SCH_EPS) {
|
|
return false;
|
|
}
|
|
|
|
float s = -simd4f_dot3_scalar(simd4f_sub(*p0, plane->p), plane->n) / line_dot_n;
|
|
*result = simd4f_add(*p0, simd4f_mul(line, simd4f_splat(s)));
|
|
if (fabs (sch_plane_distance(plane, result)) <= SCH_EPS) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void sch_plane_point_project(const sch_plane* plane, simd4f* p) {
|
|
float s = -simd4f_dot3_scalar(simd4f_sub(*p, plane->p), plane->n);
|
|
*p = simd4f_add (*p, simd4f_mul(plane->n, simd4f_splat(s)));
|
|
}
|
|
|
|
void sch_create_face(int num_vert, simd4f* vertices, sch_face* out_face) {
|
|
assert(out_face != NULL);
|
|
assert(out_face->edge == NULL);
|
|
|
|
int i = 0;
|
|
sch_edge* f_edges = (sch_edge*) malloc(sizeof(sch_edge) * num_vert);
|
|
sch_vert* f_verts = (sch_vert*) malloc(sizeof(sch_vert) * num_vert);
|
|
|
|
while (i < num_vert) {
|
|
sch_vert* vert = &f_verts[i];
|
|
sch_edge* edge = &f_edges[i];
|
|
|
|
edge->twin = NULL;
|
|
edge->vert = vert;
|
|
edge->face = out_face;
|
|
edge->next = &f_edges[(i + 1) % num_vert];
|
|
|
|
vert->edge = edge;
|
|
vert->p = vertices[i];
|
|
|
|
i++;
|
|
}
|
|
|
|
out_face->edge = &f_edges[0];
|
|
}
|
|
|
|
void sch_edge_get_dir(const sch_edge* edge, simd4f* out_dir) {
|
|
*out_dir = simd4f_sub(edge->next->vert->p, edge->vert->p);
|
|
float recip_len = 1.f / sqrtf(simd4f_dot3_scalar(*out_dir, *out_dir));
|
|
*out_dir = simd4f_mul(*out_dir, simd4f_splat(recip_len));
|
|
}
|
|
|
|
void sch_builder_reset(sch_hull_builder* builder) {
|
|
builder->num_vertices = 0;
|
|
builder->num_faces = 0;
|
|
}
|
|
|
|
void sch_builder_face_begin(sch_hull_builder* builder) {
|
|
int face_index = builder->num_faces;
|
|
if (builder->num_faces == 0) {
|
|
builder->face_vert_idx_start[face_index] = 0;
|
|
} else {
|
|
builder->face_vert_idx_start[face_index] = builder->face_vert_idx_end[face_index - 1] + 1;
|
|
}
|
|
|
|
builder->face_vert_idx_end[face_index] = -1;
|
|
}
|
|
|
|
void sch_builder_face_end(sch_hull_builder* builder) { builder->num_faces++; }
|
|
|
|
void sch_builder_allocate_memory (sch_hull_builder* builder, sch_hull* out_hull) {
|
|
out_hull->faces = (sch_face*)malloc(sizeof(sch_face) * builder->num_faces);
|
|
|
|
out_hull->num_vertices = builder->num_vertices;
|
|
out_hull->vertices =
|
|
(sch_vert*)malloc(sizeof(sch_vert) * builder->num_vertices);
|
|
|
|
int num_edges = 0;
|
|
for (int face_index = 0; face_index < builder->num_faces; face_index++) {
|
|
int start_idx = builder->face_vert_idx_start[face_index];
|
|
int end_idx = builder->face_vert_idx_end[face_index];
|
|
int edge_count = end_idx - start_idx;
|
|
|
|
num_edges += edge_count + 1;
|
|
}
|
|
|
|
out_hull->num_edges = num_edges;
|
|
out_hull->edges = (sch_edge*)malloc(sizeof(sch_edge) * num_edges);
|
|
}
|
|
|
|
void sch_hull_translate (sch_hull* hull, float x, float y, float z) {
|
|
simd4f r = simd4f_create (x, y, z, 0.f);
|
|
hull->center = simd4f_add (hull->center, r);
|
|
|
|
for (int vi = 0; vi < hull->num_vertices; vi++) {
|
|
hull->vertices[vi].p = simd4f_add (hull->vertices[vi].p, r);
|
|
}
|
|
}
|
|
|
|
void sch_hull_rotate (sch_hull* hull, const float radians, const simd4f axis) {
|
|
simd4x4f rot_mat;
|
|
simd4x4f_axis_rotation(&rot_mat, radians, axis);
|
|
simd4x4f_matrix_vector_mul(&rot_mat, &hull->center, &hull->center);
|
|
|
|
for (int vi = 0; vi < hull->num_vertices; vi++) {
|
|
simd4x4f_matrix_vector_mul(&rot_mat, &hull->vertices[vi].p, &hull->vertices[vi].p);
|
|
}
|
|
}
|
|
|
|
void sch_hull_transform (sch_hull* hull, simd4x4f mat) {
|
|
simd4f v_temp = hull->center;
|
|
simd4x4f_matrix_vector_mul(&mat, &v_temp, &hull->center);
|
|
|
|
for (int vi = 0; vi < hull->num_vertices; vi++) {
|
|
v_temp = hull->vertices[vi].p;
|
|
simd4x4f_matrix_vector_mul(&mat, &v_temp, &hull->vertices[vi].p);
|
|
}
|
|
}
|
|
|
|
void sch_hull_free_memory (sch_hull* hull) {
|
|
free (hull->faces);
|
|
hull->faces = NULL;
|
|
hull->num_faces = 0;
|
|
free (hull->vertices);
|
|
hull->vertices = NULL;
|
|
hull->num_vertices = 0;
|
|
free (hull->edges);
|
|
hull->edges = NULL;
|
|
hull->num_edges = 0;
|
|
}
|
|
|
|
SchHullResult sch_builder_create_hull(sch_hull_builder* builder, sch_hull* out_hull) {
|
|
sch_builder_allocate_memory(builder, out_hull);
|
|
out_hull->num_faces = 0;
|
|
|
|
int hull_edge_idx = 0;
|
|
|
|
for (int face_index = 0; face_index < builder->num_faces; face_index++) {
|
|
sch_face* face = &out_hull->faces[face_index];
|
|
|
|
int start_idx = builder->face_vert_idx_start[face_index];
|
|
int end_idx = builder->face_vert_idx_end[face_index];
|
|
|
|
sch_edge* prev_edge = NULL;
|
|
|
|
for (int face_vert_idx = start_idx; face_vert_idx <= end_idx;
|
|
face_vert_idx++) {
|
|
sch_edge* edge = &out_hull->edges[hull_edge_idx];
|
|
hull_edge_idx++;
|
|
if (face_vert_idx == start_idx) {
|
|
face->edge = edge;
|
|
}
|
|
|
|
int vert_idx = builder->face_vert_idx[face_vert_idx];
|
|
sch_vert* vert = &out_hull->vertices[vert_idx];
|
|
|
|
vert->p = builder->vertices[vert_idx];
|
|
vert->edge = edge;
|
|
edge->vert = vert;
|
|
edge->twin = NULL;
|
|
edge->face = face;
|
|
|
|
if (sch_hull_is_vertex_concave(out_hull, vert->p)) {
|
|
sch_hull_free_memory(out_hull);
|
|
return SchHullResultConcaveVertex;
|
|
}
|
|
|
|
if (face_vert_idx == end_idx) {
|
|
edge->next = face->edge;
|
|
}
|
|
if (prev_edge != NULL) {
|
|
prev_edge->next = edge;
|
|
}
|
|
|
|
prev_edge = edge;
|
|
}
|
|
|
|
SchHullResult edge_add_result = sch_hull_connect_face_edges (out_hull, face_index);
|
|
if (edge_add_result != SchHullResultOK) {
|
|
sch_hull_free_memory(out_hull);
|
|
return edge_add_result;
|
|
}
|
|
|
|
out_hull->num_faces = face_index + 1;
|
|
}
|
|
|
|
assert (hull_edge_idx == out_hull->num_edges);
|
|
|
|
return sch_hull_is_closed (out_hull);
|
|
}
|
|
|
|
void sch_builder_face_vertex(sch_hull_builder* builder, simd4f vertex) {
|
|
int face_index = builder->num_faces;
|
|
int vert_index = -1;
|
|
for (int i = 0; i < builder->num_vertices; i++) {
|
|
if (simd4f_get_x(
|
|
simd4f_length4_squared(simd4f_sub(builder->vertices[i], vertex)))
|
|
< 1.0e-4) {
|
|
vert_index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vert_index == -1) {
|
|
vert_index = builder->num_vertices;
|
|
builder->vertices[vert_index] = vertex;
|
|
builder->num_vertices++;
|
|
}
|
|
|
|
if (builder->face_vert_idx_end[face_index] == -1) {
|
|
builder->face_vert_idx_end[face_index] =
|
|
builder->face_vert_idx_start[face_index];
|
|
} else {
|
|
builder->face_vert_idx_end[face_index]++;
|
|
}
|
|
|
|
int face_end_idx = builder->face_vert_idx_end[face_index];
|
|
builder->face_vert_idx[face_end_idx] = vert_index;
|
|
}
|
|
|
|
void sch_face_calc_plane(const sch_face* face, sch_plane* out_plane) {
|
|
assert (face != NULL);
|
|
assert (out_plane != NULL);
|
|
|
|
sch_edge* edge0 = face->edge;
|
|
sch_edge* edge1 = edge0->next;
|
|
simd4f dir0;
|
|
simd4f dir1;
|
|
sch_edge_get_dir(edge0, &dir0);
|
|
sch_edge_get_dir(edge1, &dir1);
|
|
|
|
out_plane->p = edge0->vert->p;
|
|
out_plane->n = simd4f_cross3(dir0, dir1);
|
|
}
|
|
|
|
void sch_hull_calc_plane(const sch_hull* hull, const int index, sch_plane* out_plane) {
|
|
assert(hull != NULL);
|
|
assert(index >= 0 && index < hull->num_faces);
|
|
assert(out_plane != NULL);
|
|
|
|
// TODO move plane calculation to create hull?
|
|
sch_face_calc_plane(&hull->faces[index], out_plane);
|
|
}
|
|
|
|
sch_edge* sch_hull_find_edge (const sch_hull* hull, const simd4f v0, const simd4f v1) {
|
|
for (int fi = 0; fi < hull->num_faces; fi++) {
|
|
sch_face* face = &hull->faces[fi];
|
|
sch_edge* edge0 = face->edge;
|
|
sch_edge* edge = edge0;
|
|
do {
|
|
if (sch_simd4f_equal(edge->vert->p, v0)
|
|
&& sch_simd4f_equal(edge->next->vert->p, v1)) {
|
|
return edge;
|
|
}
|
|
edge = edge->next;
|
|
} while (edge != edge0);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void sch_hull_get_support(const sch_hull* hull, simd4x4f* trans, simd4f n, simd4f* out_vert) {
|
|
sch_edge* edge = hull->faces[0].edge;
|
|
sch_edge* last_edge = edge;
|
|
float normal_dot_edge = -1.;
|
|
|
|
simd4f n_local;
|
|
simd4x4f_inv_ortho_matrix_vector3_mul(trans, &n, &n_local);
|
|
|
|
do {
|
|
simd4f dir = simd4f_normalize3(simd4f_sub (edge->next->vert->p, edge->vert->p));
|
|
normal_dot_edge = simd4f_dot3_scalar(dir, n_local);
|
|
|
|
if (normal_dot_edge <= 0.) {
|
|
edge = edge->twin->next;
|
|
} else {
|
|
edge = edge->next;
|
|
last_edge = edge;
|
|
}
|
|
} while (last_edge != edge || normal_dot_edge > 0);
|
|
|
|
simd4x4f_matrix_point3_mul(trans, &edge->vert->p, out_vert);
|
|
}
|
|
|
|
float sch_query_face_directions(
|
|
const sch_hull* hull_A,
|
|
simd4x4f* trans_BtoA,
|
|
const sch_hull* hull_B,
|
|
sch_face_query* result) {
|
|
result->dist = -FLT_MAX;
|
|
for (int fi = 0; fi < hull_A->num_faces; fi++) {
|
|
sch_plane plane;
|
|
sch_hull_calc_plane(hull_A, fi, &plane);
|
|
simd4f vert_B;
|
|
sch_hull_get_support(hull_B, trans_BtoA, simd4f_sub(simd4f_zero(), plane.n), &vert_B);
|
|
|
|
float distance = sch_plane_distance(&plane, &vert_B);
|
|
if (distance > result->dist) {
|
|
result->dist = distance;
|
|
result->face_idx = fi;
|
|
result->plane = plane;
|
|
result->vert = vert_B;
|
|
}
|
|
}
|
|
|
|
return result->dist;
|
|
}
|
|
|
|
float sch_query_edge_directions(
|
|
const sch_hull* hull_A,
|
|
simd4x4f *trans_BtoA,
|
|
const sch_hull* hull_B,
|
|
sch_edge_query* result) {
|
|
result->dist = -FLT_MAX;
|
|
for (int iAe = 0; iAe < hull_A->num_edges; iAe++) {
|
|
for (int iBe = 0; iBe < hull_B->num_edges; iBe++) {
|
|
sch_edge* edge_A = &hull_A->edges[iAe];
|
|
sch_edge* edge_B = &hull_B->edges[iBe];
|
|
simd4f dir_A;
|
|
sch_edge_get_dir(edge_A, &dir_A);
|
|
|
|
simd4f dir_B_B;
|
|
sch_edge_get_dir(edge_B, &dir_B_B);
|
|
simd4f dir_B;
|
|
simd4x4f_inv_ortho_matrix_vector3_mul(trans_BtoA, &dir_B_B, &dir_B);
|
|
|
|
simd4f axis = simd4f_cross3 (dir_A, dir_B);
|
|
if (simd4f_dot3_scalar(dir_A, simd4f_sub(edge_A->vert->p, hull_A->center) ) < 0.f) {
|
|
axis = simd4f_sub(simd4f_zero(), axis);
|
|
}
|
|
|
|
// TODO: properly handle touching parallel edges.
|
|
if (simd4f_get_x(simd4f_length3_squared(axis)) < SCH_EPS) {
|
|
continue;
|
|
}
|
|
|
|
axis = simd4f_normalize3(axis);
|
|
|
|
sch_plane plane_A;
|
|
plane_A.n = axis;
|
|
// Note: in Gregorius talk he uses the origin of edge_A. This is wrong as
|
|
// there are likely points in hull_A that are further out along the plane
|
|
// normal. We therefore use the support point of hull_A along the axis.
|
|
simd4x4f identity;
|
|
simd4x4f_identity(&identity);
|
|
sch_hull_get_support(hull_A, &identity, plane_A.n, &plane_A.p);
|
|
|
|
simd4f vert_B_B;
|
|
sch_hull_get_support(hull_B, trans_BtoA, simd4f_sub(simd4f_zero(), plane_A.n), &vert_B_B);
|
|
simd4f vert_B;
|
|
simd4x4f_matrix_point3_mul(trans_BtoA, &vert_B_B, &vert_B);
|
|
|
|
|
|
float distance = sch_plane_distance(&plane_A, &vert_B);
|
|
if (distance > result->dist) {
|
|
result->dist = distance;
|
|
result->plane = plane_A;
|
|
result->edge_A_idx = iAe;
|
|
result->edge_B_idx = iBe;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result->dist;
|
|
}
|
|
|
|
void sch_clip_faces (const sch_face* ref_face, const sch_face* inc_face, sch_manifold* manifold) {
|
|
simd4f* input_vertices = (simd4f*) malloc(sizeof(simd4f) * manifold->num_vertices);
|
|
assert (input_vertices != NULL);
|
|
|
|
sch_edge* inc_start_edge = inc_face->edge;
|
|
sch_edge* inc_cur_edge = inc_face->edge;
|
|
|
|
sch_manifold_reset(manifold);
|
|
int num_input_vertices = 0;
|
|
do {
|
|
sch_manifold_add_point(manifold, inc_cur_edge->vert->p);
|
|
inc_cur_edge = inc_cur_edge->next;
|
|
} while (inc_cur_edge != inc_start_edge);
|
|
|
|
sch_edge* ref_start_edge = ref_face->edge;
|
|
sch_edge* ref_cur_edge = ref_face->edge;
|
|
|
|
sch_plane ref_face_plane;
|
|
sch_face_calc_plane(ref_face, &ref_face_plane);
|
|
|
|
do {
|
|
memcpy(input_vertices, manifold->vertices, sizeof(simd4f) * manifold->num_points);
|
|
num_input_vertices = manifold->num_points;
|
|
sch_manifold_reset(manifold);
|
|
|
|
// construct plane orthogonal to reference face that contains current edge.
|
|
sch_plane edge_plane;
|
|
edge_plane.n = simd4f_normalize3(simd4f_cross3(ref_face_plane.n, simd4f_sub(ref_cur_edge->next->vert->p, ref_cur_edge->vert->p)));
|
|
edge_plane.p = ref_cur_edge->vert->p;
|
|
|
|
for (int i = 0; i < num_input_vertices; i++) {
|
|
simd4f current = input_vertices[i];
|
|
simd4f prev = input_vertices[(i + num_input_vertices - 1) % num_input_vertices];
|
|
simd4f intersection;
|
|
bool is_intersecting = sch_plane_intersection(&edge_plane, ¤t, &prev, &intersection);
|
|
|
|
if (sch_plane_distance(&edge_plane, ¤t) >= SCH_EPS) {
|
|
if (sch_plane_distance(&edge_plane, &prev) <= SCH_EPS) {
|
|
sch_manifold_add_point(manifold, intersection);
|
|
}
|
|
sch_manifold_add_point(manifold, current);
|
|
} else if (sch_plane_distance(&edge_plane, &prev) >= SCH_EPS) {
|
|
sch_manifold_add_point(manifold, intersection);
|
|
}
|
|
}
|
|
|
|
ref_cur_edge = ref_cur_edge->next;
|
|
} while (ref_cur_edge != ref_start_edge);
|
|
|
|
free(input_vertices);
|
|
}
|
|
|
|
void sch_create_face_contact (const sch_face_query* query_A_B, const sch_hull* hull_A, const sch_face_query* query_B_A, const sch_hull* hull_B, sch_manifold *result) {
|
|
const sch_plane* ref_plane = &query_A_B->plane;
|
|
int ref_face_idx = query_A_B->face_idx;
|
|
const sch_hull* ref_hull = hull_A;
|
|
const sch_hull* inc_hull = hull_B;
|
|
const sch_face* ref_face = &hull_A->faces[query_A_B->face_idx];
|
|
|
|
// normalize input
|
|
if (query_A_B->dist < query_B_A->dist) {
|
|
ref_plane = &query_B_A->plane;
|
|
ref_face_idx = query_B_A->face_idx;
|
|
ref_face = &hull_B->faces[query_B_A->face_idx];
|
|
ref_hull = hull_B;
|
|
inc_hull = hull_A;
|
|
}
|
|
|
|
result->normal = ref_plane->n;
|
|
|
|
// find most antiparallel face
|
|
float dot_min = 1.0f;
|
|
int inc_face_idx = -1;
|
|
for (int i = 0; i < inc_hull->num_faces; i++) {
|
|
sch_plane face_plane;
|
|
sch_hull_calc_plane(inc_hull, i, &face_plane);
|
|
float normal_dot = simd4f_dot3_scalar(result->normal, face_plane.n);
|
|
if (normal_dot < dot_min) {
|
|
dot_min = normal_dot;
|
|
inc_face_idx = i;
|
|
}
|
|
}
|
|
assert (inc_face_idx >= 0);
|
|
|
|
const sch_face* inc_face = &inc_hull->faces[inc_face_idx];
|
|
sch_clip_faces(ref_face, inc_face, result);
|
|
|
|
// project points onto plane of reference face
|
|
for (int i = 0; i < result->num_points; i++) {
|
|
sch_plane_point_project(ref_plane, &result->vertices[i]);
|
|
}
|
|
}
|
|
|
|
void sch_create_edge_contact (const sch_edge_query* query_edge, const sch_hull* hull_A, const sch_hull* hull_B, sch_manifold* result) {
|
|
sch_edge edge_A = hull_A->edges[query_edge->edge_A_idx];
|
|
sch_edge edge_B = hull_B->edges[query_edge->edge_B_idx];
|
|
|
|
sch_plane plane_A_n;
|
|
plane_A_n.p = edge_A.vert->p;
|
|
plane_A_n.n = simd4f_normalize3(simd4f_cross3(simd4f_sub(edge_A.vert->p, edge_A.next->vert->p), query_edge->plane.n));
|
|
simd4f edge_B_projected;
|
|
bool is_edge_B_intersecting;
|
|
is_edge_B_intersecting = sch_plane_intersection(&plane_A_n, &edge_B.vert->p, &edge_B.next->vert->p, &edge_B_projected);
|
|
assert (is_edge_B_intersecting);
|
|
|
|
sch_plane plane_B_n;
|
|
plane_B_n.p = edge_B.vert->p;
|
|
plane_B_n.n = simd4f_normalize3(simd4f_cross3(simd4f_sub(edge_B.vert->p, edge_B.next->vert->p), query_edge->plane.n));
|
|
simd4f edge_A_projected;
|
|
bool is_edge_A_intersecting;
|
|
is_edge_A_intersecting = sch_plane_intersection(&plane_B_n, &edge_A.vert->p, &edge_A.next->vert->p, &edge_A_projected);
|
|
assert (is_edge_A_intersecting);
|
|
|
|
sch_manifold_add_point(result,simd4f_add(simd4f_mul(edge_B_projected, simd4f_splat(0.5f)),
|
|
simd4f_mul(edge_B_projected, simd4f_splat(0.5f))));
|
|
result->normal = query_edge->plane.n;
|
|
|
|
#ifndef NDEBUG
|
|
simd4f closest_points_dir = simd4f_normalize3(simd4f_sub (edge_B_projected, edge_A_projected));
|
|
float normal_error = 1.0f - simd4f_dot3_scalar(closest_points_dir, result->normal);
|
|
assert (fabs(normal_error) < SCH_EPS);
|
|
#endif
|
|
}
|
|
|
|
bool sch_hull_sat(
|
|
const sch_hull* hull_A,
|
|
simd4x4f* trans_A,
|
|
const sch_hull* hull_B,
|
|
simd4x4f* trans_B,
|
|
sch_manifold* result) {
|
|
simd4x4f trans_Binv;
|
|
sch_simd4x4f_transform_inv(trans_B, &trans_Binv);
|
|
simd4x4f trans_BtoA;
|
|
simd4x4f_matrix_mul(&trans_Binv, trans_A, &trans_BtoA);
|
|
|
|
sch_face_query query_A_B;
|
|
sch_query_face_directions(hull_A, &trans_BtoA, hull_B, &query_A_B);
|
|
if (query_A_B.dist > 0.f) {
|
|
return true;
|
|
}
|
|
|
|
sch_face_query query_B_A;
|
|
simd4x4f trans_AtoB;
|
|
sch_simd4x4f_transform_inv(&trans_BtoA, &trans_AtoB);
|
|
sch_query_face_directions(hull_B, &trans_AtoB, hull_A, &query_B_A);
|
|
if (query_B_A.dist > 0.f) {
|
|
return true;
|
|
}
|
|
|
|
sch_edge_query query_edge;
|
|
sch_query_edge_directions(hull_A, &trans_BtoA, hull_B, &query_edge);
|
|
if (query_edge.dist > 0.f) {
|
|
return true;
|
|
}
|
|
|
|
bool is_face_contact_A = query_A_B.dist > query_edge.dist;
|
|
bool is_face_contact_B = query_B_A.dist > query_edge.dist;
|
|
if (is_face_contact_A && is_face_contact_B) {
|
|
sch_create_face_contact (&query_A_B, hull_A, &query_B_A, hull_B, result);
|
|
} else {
|
|
sch_create_edge_contact (&query_edge, hull_A, hull_B, result);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int sch_hull_is_vertex_concave(const sch_hull* hull, const simd4f v) {
|
|
sch_plane plane;
|
|
for (int i = 0; i < hull->num_faces; i++) {
|
|
sch_hull_calc_plane(hull, i, &plane);
|
|
float distance = sch_plane_distance(&plane, &v);
|
|
return (distance > 0.);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
SchHullResult sch_hull_connect_face_edges(const sch_hull* hull, int new_face_index) {
|
|
sch_face* new_face = &hull->faces[new_face_index];
|
|
|
|
sch_edge* new_face_edge0 = new_face->edge;
|
|
sch_edge* new_face_edge = new_face_edge0;
|
|
|
|
do {
|
|
sch_vert* new_edge_v0 = new_face_edge->vert;
|
|
sch_vert* new_edge_v1 = new_face_edge->next->vert;
|
|
|
|
for (int fi = 0; fi < hull->num_faces; fi++) {
|
|
if (fi == new_face_index) {
|
|
continue;
|
|
}
|
|
|
|
sch_face* face = &hull->faces[fi];
|
|
sch_edge* face_edge0 = face->edge;
|
|
sch_edge* face_edge = face_edge0;
|
|
|
|
do {
|
|
sch_vert* edge_v0 = face_edge->vert;
|
|
sch_vert* edge_v1 = face_edge->next->vert;
|
|
|
|
if (sch_simd4f_equal(new_edge_v0->p, edge_v0->p)
|
|
&& sch_simd4f_equal(new_edge_v1->p, edge_v1->p)) {
|
|
return SchHullResultWrongWinding;
|
|
} else if (sch_simd4f_equal(new_edge_v0->p, edge_v1->p)
|
|
&& sch_simd4f_equal(new_edge_v1->p, edge_v0->p)) {
|
|
if (new_face_edge->twin == NULL && face_edge->twin == NULL) {
|
|
new_face_edge->twin = face_edge;
|
|
face_edge->twin = new_face_edge;
|
|
} else if (new_face_edge->twin == face_edge->twin) {
|
|
// nothing to do
|
|
} else if ((new_face_edge->twin == NULL && face_edge->twin != NULL)
|
|
|| (new_face_edge->twin != NULL && face_edge->twin == NULL)) {
|
|
return SchHullResultInvalidTwinEdges;
|
|
}
|
|
}
|
|
|
|
face_edge = face_edge->next;
|
|
} while (face_edge != face_edge0);
|
|
}
|
|
|
|
new_face_edge = new_face_edge->next;
|
|
} while (new_face_edge != new_face_edge0);
|
|
|
|
return SchHullResultOK;
|
|
}
|
|
|
|
SchHullResult sch_hull_is_closed (const sch_hull* hull) {
|
|
for (int ei = 0; ei < hull->num_edges; ei++) {
|
|
if (hull->edges[ei].twin == NULL) {
|
|
return SchHullResultOpenHull;
|
|
}
|
|
}
|
|
|
|
return SchHullResultOK;
|
|
}
|
|
|
|
void sch_create_unitbox(sch_hull* out_hull) {
|
|
sch_hull_builder builder;
|
|
sch_builder_reset(&builder);
|
|
|
|
// +x
|
|
sch_builder_face_begin(&builder);
|
|
sch_builder_face_vertex(&builder, simd4f_create (0.5f, -0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, -0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, 0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create (0.5f, 0.5f, 0.5f, 1.f));
|
|
sch_builder_face_end(&builder);
|
|
|
|
// -x
|
|
sch_builder_face_begin(&builder);
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, -0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( -0.5f, -0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( -0.5f, 0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, 0.5f, -0.5f, 1.f));
|
|
sch_builder_face_end(&builder);
|
|
|
|
// +y
|
|
sch_builder_face_begin(&builder);
|
|
sch_builder_face_vertex(&builder, simd4f_create (0.5f, 0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, 0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( -0.5f, 0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, 0.5f, 0.5f, 1.f));
|
|
sch_builder_face_end(&builder);
|
|
|
|
// -y
|
|
sch_builder_face_begin(&builder);
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, -0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, -0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, -0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, -0.5f, 0.5f, 1.f));
|
|
sch_builder_face_end(&builder);
|
|
|
|
// +z
|
|
sch_builder_face_begin(&builder);
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, -0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, -0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create ( 0.5f, 0.5f, 0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create (-0.5f, 0.5f, 0.5f, 1.f));
|
|
sch_builder_face_end(&builder);
|
|
|
|
// -z
|
|
sch_builder_face_begin(&builder);
|
|
sch_builder_face_vertex(&builder, simd4f_create(0.5f, -0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create(-0.5f, -0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create(-0.5f, 0.5f, -0.5f, 1.f));
|
|
sch_builder_face_vertex(&builder, simd4f_create(0.5f, 0.5f, -0.5f, 1.f));
|
|
sch_builder_face_end(&builder);
|
|
|
|
int hull_result = sch_builder_create_hull(&builder, out_hull);
|
|
out_hull->center = simd4f_create(0.f, 0.f, 0.f, 1.f);
|
|
assert (hull_result == SchHullResultOK);
|
|
}
|
|
|
|
#endif /* SCONVCOL_IMPLEMENTATION */
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* SCONVCOL_H */
|