Hull builder can now build hulls including various validations.
parent
d8a67db2a9
commit
8aa06fb3d6
161
src/sconvcol.h
161
src/sconvcol.h
|
@ -9,6 +9,10 @@ extern "C" {
|
|||
#include <stdbool.h>
|
||||
|
||||
#include "vectorial/simd4x4f.h"
|
||||
8
|
||||
inline bool sch_simd4f_equal(simd4f a, simd4f b) {
|
||||
return (simd4f_get_x(simd4f_length4_squared(simd4f_sub(a, b))) == 0.f);
|
||||
}
|
||||
|
||||
typedef struct sch_edge sch_edge;
|
||||
typedef struct sch_vert sch_vert;
|
||||
|
@ -35,10 +39,12 @@ struct sch_face {
|
|||
};
|
||||
|
||||
struct sch_hull {
|
||||
int num_faces;
|
||||
sch_face* faces;
|
||||
sch_edge* edges;
|
||||
sch_vert* vertices;
|
||||
int num_faces;
|
||||
int num_edges;
|
||||
int num_vertices;
|
||||
};
|
||||
|
||||
struct sch_plane {
|
||||
|
@ -64,7 +70,10 @@ struct sch_face_query {
|
|||
|
||||
enum SchHullResult {
|
||||
SchHullResultOK = 0,
|
||||
SchHullResultConcaveVertex = -1
|
||||
SchHullResultConcaveVertex = -1,
|
||||
SchHullResultWrongWinding = -2,
|
||||
SchHullResultInvalidTwinEdges = -3,
|
||||
SchHullResultOpenHull = -4
|
||||
};
|
||||
|
||||
typedef enum SchHullResult SchHullResult;
|
||||
|
@ -98,21 +107,20 @@ void sch_edge_get_dir(sch_edge* edge, simd4f* dir);
|
|||
|
||||
void sch_hull_free_memory (sch_hull* hull);
|
||||
|
||||
void sch_hull_get_plane(const sch_hull* hull, int index, sch_plane* out_plane);
|
||||
void sch_hull_get_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, simd4f n, simd4f* out_vert);
|
||||
|
||||
int sch_hull_is_vertex_concave(const sch_hull* hull, simd4f p);
|
||||
int sch_hull_is_vertex_concave(const sch_hull* hull, const simd4f p);
|
||||
|
||||
int sch_hull_is_closed (const sch_hull* hull);
|
||||
|
||||
int 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);
|
||||
|
||||
SchHullResult sch_create_hull(int num_faces, sch_face* faces, sch_hull* out_hull);
|
||||
|
||||
void sch_query_face_dir(
|
||||
const sch_hull* hull_A,
|
||||
const sch_hull* hull_B,
|
||||
sch_face_query* out_query);
|
||||
|
||||
//
|
||||
// sconvcol Implementation
|
||||
//
|
||||
|
@ -162,7 +170,12 @@ void sch_builder_reset(sch_hull_builder* builder) {
|
|||
|
||||
void sch_builder_face_begin(sch_hull_builder* builder) {
|
||||
int face_index = builder->num_faces;
|
||||
builder->face_vert_idx_start[face_index] = builder->num_vertices;
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -171,37 +184,41 @@ 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 = 1;
|
||||
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;
|
||||
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_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;
|
||||
|
||||
out_hull->num_faces = 0;
|
||||
|
||||
for (int face_index = 0; face_index < builder->num_faces; face_index++) {
|
||||
sch_face* face = &out_hull->faces[face_index];
|
||||
|
||||
|
@ -242,10 +259,18 @@ SchHullResult sch_builder_create_hull(sch_hull_builder* builder, sch_hull* out_h
|
|||
prev_edge = edge;
|
||||
}
|
||||
|
||||
int 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;
|
||||
}
|
||||
|
||||
return SchHullResultOK;
|
||||
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) {
|
||||
|
@ -277,7 +302,7 @@ void sch_builder_face_vertex(sch_hull_builder* builder, simd4f vertex) {
|
|||
builder->face_vert_idx[face_end_idx] = vert_index;
|
||||
}
|
||||
|
||||
void sch_hull_get_plane(const sch_hull* hull, int index, sch_plane* out_plane) {
|
||||
void sch_hull_get_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);
|
||||
|
@ -295,7 +320,24 @@ void sch_hull_get_plane(const sch_hull* hull, int index, sch_plane* out_plane) {
|
|||
out_plane->n = simd4f_cross3(dir0, dir1);
|
||||
}
|
||||
|
||||
int sch_hull_is_vertex_concave(const sch_hull* hull, simd4f v) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_get_plane(hull, i, &plane);
|
||||
|
@ -306,40 +348,63 @@ int sch_hull_is_vertex_concave(const sch_hull* hull, simd4f v) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
sch_hull* sch_create_box() {
|
||||
simd4f vertices_pos_z[4] = {
|
||||
simd4f_create(-0.5f, -0.5f, 0.5f, 0.f),
|
||||
simd4f_create(0.5f, -0.5f, 0.5f, 0.f),
|
||||
simd4f_create(0.5f, 0.5f, 0.5f, 0.f),
|
||||
simd4f_create(-0.5f, 0.5f, 0.5f, 0.f)};
|
||||
sch_face face_pos_z = {0};
|
||||
sch_create_face(4, vertices_pos_z, &face_pos_z);
|
||||
int sch_hull_connect_face_edges(const sch_hull* hull, int new_face_index) {
|
||||
sch_face* new_face = &hull->faces[new_face_index];
|
||||
|
||||
simd4f vertices_neg_z[4] = {
|
||||
simd4f_create(-0.5f, -0.5f, -0.5f, 0.f),
|
||||
simd4f_create(0.5f, -0.5f, -0.5f, 0.f),
|
||||
simd4f_create(0.5f, 0.5f, -0.5f, 0.f),
|
||||
simd4f_create(-0.5f, 0.5f, -0.5f, 0.f)};
|
||||
sch_face face_neg_z = {0};
|
||||
sch_create_face(4, vertices_neg_z, &face_neg_z);
|
||||
sch_edge* new_face_edge0 = new_face->edge;
|
||||
sch_edge* new_face_edge = new_face_edge0;
|
||||
|
||||
simd4f vertices_pos_x[4] = {
|
||||
simd4f_create(0.5f, -0.5f, 0.5f, 0.f),
|
||||
simd4f_create(0.5f, -0.5f, -0.5f, 0.f),
|
||||
simd4f_create(0.5f, 0.5f, -0.5f, 0.f),
|
||||
simd4f_create(0.5f, 0.5f, 0.5f, 0.f)};
|
||||
do {
|
||||
sch_vert* new_edge_v0 = new_face_edge->vert;
|
||||
sch_vert* new_edge_v1 = new_face_edge->next->vert;
|
||||
|
||||
sch_face face_pos_x = {0};
|
||||
sch_create_face(4, vertices_pos_x, &face_pos_x);
|
||||
for (int fi = 0; fi < hull->num_faces; fi++) {
|
||||
if (fi == new_face_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
simd4f vertices_neg_x[4] = {
|
||||
simd4f_create(-0.5f, -0.5f, 0.5f, 0.f),
|
||||
simd4f_create(-0.5f, -0.5f, -0.5f, 0.f),
|
||||
simd4f_create(-0.5f, 0.5f, -0.5f, 0.f),
|
||||
simd4f_create(-0.5f, 0.5f, 0.5f, 0.f)};
|
||||
sch_face* face = &hull->faces[fi];
|
||||
sch_edge* face_edge0 = face->edge;
|
||||
sch_edge* face_edge = face_edge0;
|
||||
|
||||
sch_face face_neg_x = {0};
|
||||
sch_create_face(4, vertices_neg_x, &face_neg_x);
|
||||
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;
|
||||
}
|
||||
|
||||
int 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;
|
||||
}
|
||||
|
||||
#endif /* SCONVCOL_IMPLEMENTATION */
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
#include "catch.hpp"
|
||||
#include "sconvcol.h"
|
||||
|
||||
bool simd4f_equal(simd4f a, simd4f b) {
|
||||
return (simd4f_get_x(simd4f_length4_squared(simd4f_sub(a, b))) == 0.f);
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
TEST_CASE("Plane Distance", "[sconvcol]") {
|
||||
|
@ -70,12 +66,13 @@ TEST_CASE("HullBuilder", "[sconvcol]") {
|
|||
GIVEN("An empty builder") {
|
||||
sch_builder_reset(&builder);
|
||||
|
||||
WHEN("adding two vertices ") {
|
||||
simd4f vertex0 = simd4f_create(0.f, 0.f, 0.f, 1.f);
|
||||
simd4f vertex1 = simd4f_create(1.f, 0.f, 0.f, 1.f);
|
||||
simd4f vertex2 = simd4f_create(0.5f, 1.f, 0.f, 1.f);
|
||||
|
||||
WHEN("adding two vertices ") {
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex0);
|
||||
|
||||
simd4f vertex1 = simd4f_create(1.f, 0.f, 0.f, 1.f);
|
||||
sch_builder_face_vertex(&builder, vertex1);
|
||||
|
||||
WHEN("ending the face") {
|
||||
|
@ -89,7 +86,6 @@ TEST_CASE("HullBuilder", "[sconvcol]") {
|
|||
}
|
||||
|
||||
WHEN("creating a triangle") {
|
||||
simd4f vertex2 = simd4f_create(0.5f, 1.f, 0.f, 1.f);
|
||||
sch_builder_face_vertex(&builder, vertex2);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
|
@ -105,7 +101,7 @@ TEST_CASE("HullBuilder", "[sconvcol]") {
|
|||
SchHullResult result = sch_builder_create_hull(&builder, &hull);
|
||||
|
||||
THEN("verify triangle hull") {
|
||||
REQUIRE(result == SchHullResultOK);
|
||||
REQUIRE(result == SchHullResultOpenHull);
|
||||
REQUIRE(hull.num_faces == 1);
|
||||
sch_face* face = &hull.faces[0];
|
||||
|
||||
|
@ -113,9 +109,9 @@ TEST_CASE("HullBuilder", "[sconvcol]") {
|
|||
sch_vert* hull_vert1 = face->edge->next->vert;
|
||||
sch_vert* hull_vert2 = face->edge->next->next->vert;
|
||||
|
||||
REQUIRE(simd4f_equal(hull_vert0->p, vertex0));
|
||||
REQUIRE(simd4f_equal(hull_vert1->p, vertex1));
|
||||
REQUIRE(simd4f_equal(hull_vert2->p, vertex2));
|
||||
REQUIRE(sch_simd4f_equal(hull_vert0->p, vertex0));
|
||||
REQUIRE(sch_simd4f_equal(hull_vert1->p, vertex1));
|
||||
REQUIRE(sch_simd4f_equal(hull_vert2->p, vertex2));
|
||||
|
||||
REQUIRE(face->edge->next->next->next == face->edge);
|
||||
}
|
||||
|
@ -145,12 +141,99 @@ TEST_CASE("HullBuilder", "[sconvcol]") {
|
|||
}
|
||||
|
||||
THEN("hull creation fails") {
|
||||
SchHullResult result = sch_builder_create_hull(&builder, &hull);
|
||||
sch_hull concave_hull;
|
||||
SchHullResult result = sch_builder_create_hull(&builder, &concave_hull);
|
||||
|
||||
REQUIRE(result == SchHullResultConcaveVertex);
|
||||
|
||||
sch_hull_free_memory(&concave_hull);
|
||||
}
|
||||
|
||||
sch_hull_free_memory(&hull);
|
||||
}
|
||||
|
||||
WHEN("adding adjacent clockwise winded face") {
|
||||
simd4f vertex4 = simd4f_create(0.5f, -0.25f, 0, 1.f);
|
||||
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex0);
|
||||
sch_builder_face_vertex(&builder, vertex1);
|
||||
sch_builder_face_vertex(&builder, vertex4);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
THEN("hull creation fails") {
|
||||
sch_hull hull;
|
||||
SchHullResult result = sch_builder_create_hull(&builder, &hull);
|
||||
|
||||
REQUIRE(result == SchHullResultWrongWinding);
|
||||
|
||||
sch_hull_free_memory(&hull);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("adding adjacent counter clockwise winded face") {
|
||||
simd4f vertex4 = simd4f_create(0.5f, -0.25f, 0, 1.f);
|
||||
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex0);
|
||||
sch_builder_face_vertex(&builder, vertex4);
|
||||
sch_builder_face_vertex(&builder, vertex1);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
THEN("edge v0-v1 has twin edge v1-v0") {
|
||||
sch_hull hull;
|
||||
SchHullResult result = sch_builder_create_hull(&builder, &hull);
|
||||
|
||||
REQUIRE(result == SchHullResultOpenHull);
|
||||
|
||||
sch_edge* edge_v0_v1 = sch_hull_find_edge(&hull, vertex0, vertex1);
|
||||
sch_edge* edge_v1_v0 = sch_hull_find_edge(&hull, vertex1, vertex0);
|
||||
|
||||
REQUIRE (edge_v0_v1 != NULL);
|
||||
REQUIRE (edge_v1_v0->twin == edge_v0_v1);
|
||||
|
||||
sch_hull_free_memory(&hull);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("creating a tetrahedron ") {
|
||||
simd4f vertex3 = simd4f_create (0.5f, 0.f, -0.5f, 1.f);
|
||||
|
||||
// +z facing triangle
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex0);
|
||||
sch_builder_face_vertex(&builder, vertex1);
|
||||
sch_builder_face_vertex(&builder, vertex2);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
// +x,-z facing triangle
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex1);
|
||||
sch_builder_face_vertex(&builder, vertex3);
|
||||
sch_builder_face_vertex(&builder, vertex2);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
// -x,-z facing triangle
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex0);
|
||||
sch_builder_face_vertex(&builder, vertex2);
|
||||
sch_builder_face_vertex(&builder, vertex3);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
// -y facing triangle
|
||||
sch_builder_face_begin(&builder);
|
||||
sch_builder_face_vertex(&builder, vertex0);
|
||||
sch_builder_face_vertex(&builder, vertex3);
|
||||
sch_builder_face_vertex(&builder, vertex1);
|
||||
sch_builder_face_end(&builder);
|
||||
|
||||
THEN("results in a closed hull") {
|
||||
sch_hull hull;
|
||||
SchHullResult result = sch_builder_create_hull(&builder, &hull);
|
||||
REQUIRE (result == SchHullResultOK);
|
||||
sch_hull_free_memory(&hull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue