765 lines
23 KiB
C
765 lines
23 KiB
C
//
|
|
// LICENCE:
|
|
// The MIT License (MIT)
|
|
//
|
|
// Copyright (c) 2016 Karim Naaji, karim.naaji@gmail.com
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE
|
|
//
|
|
// REFERENCES:
|
|
// http://matthias-mueller-fischer.ch/publications/tetraederCollision.pdf
|
|
// http://fileadmin.cs.lth.se/cs/Personal/Tomas_Akenine-Moller/code/tribox2.txt
|
|
//
|
|
// HOWTO:
|
|
// #define VOXELIZER_IMPLEMENTATION
|
|
// #define VOXELIZER_DEBUG // Only if assertions need to be checked
|
|
// #include "voxelizer.h"
|
|
//
|
|
// HISTORY:
|
|
// - version 0.9.0: Initial
|
|
//
|
|
// TODO:
|
|
// - Triangle face merging
|
|
// - Add colors from input mesh
|
|
// - Potential issue with voxel bigger than triangle
|
|
//
|
|
|
|
#ifndef VOXELIZER_H
|
|
#define VOXELIZER_H
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
// VOXELIZER PUBLIC API
|
|
//
|
|
|
|
#ifndef VOXELIZER_HELPERS
|
|
#include <stdlib.h> // malloc, calloc, free
|
|
#endif
|
|
|
|
typedef struct vx_vertex {
|
|
union {
|
|
float v[3];
|
|
struct {
|
|
float x;
|
|
float y;
|
|
float z;
|
|
};
|
|
};
|
|
} vx_vertex_t;
|
|
|
|
typedef struct vx_mesh {
|
|
vx_vertex_t* vertices; // Contiguous mesh vertices
|
|
vx_vertex_t* normals; // Contiguous mesh normals
|
|
unsigned int* indices; // Mesh indices
|
|
unsigned int* normalindices; // Mesh normal indices
|
|
size_t nindices; // The number of normal indices
|
|
size_t nvertices; // The number of vertices
|
|
size_t nnormals; // The number of normals
|
|
} vx_mesh_t;
|
|
|
|
vx_mesh_t* vx_voxelize(vx_mesh_t* _mesh, // The input mesh
|
|
float voxelsizex, // Voxel size on X-axis
|
|
float voxelsizey, // Voxel size on Y-axis
|
|
float voxelsizez, // Voxel size on Z-axis
|
|
float precision); // A precision factor that reduces "holes" artifact
|
|
// usually a precision = voxelsize / 10. works ok.
|
|
|
|
void vx_mesh_free(vx_mesh_t* _mesh);
|
|
vx_mesh_t* vx_mesh_alloc(int nindices, int nvertices);
|
|
|
|
// Voxelizer Helpers, define your own if needed
|
|
#ifndef VOXELIZER_HELPERS
|
|
#define VOXELIZER_HELPERS 1
|
|
#define VX_MIN(a, b) (a > b ? b : a)
|
|
#define VX_MAX(a, b) (a > b ? a : b)
|
|
#define VX_FINDMINMAX(x0, x1, x2, min, max) \
|
|
min = max = x0; \
|
|
if (x1 < min) min = x1; \
|
|
if (x1 > max) max = x1; \
|
|
if (x2 < min) min = x2; \
|
|
if (x2 > max) max = x2;
|
|
#define VX_CLAMP(v, lo, hi) VX_MAX(lo, VX_MIN(hi, v))
|
|
#define VX_MALLOC(T, N) ((T*) malloc(N * sizeof(T)))
|
|
#define VX_FREE(T) free(T)
|
|
#define VX_CALLOC(T, N) ((T*) calloc(N * sizeof(T), 1))
|
|
#define VX_SWAP(T, A, B) { T tmp = B; B = A; A = tmp; }
|
|
#ifdef VOXELIZER_DEBUG
|
|
#define VX_ASSERT(STMT) if (!(STMT)) { *(int *)0 = 0; }
|
|
#else
|
|
#define VX_ASSERT(STMT)
|
|
#endif // VOXELIZER_DEBUG
|
|
#endif // VOXELIZER_HELPERS
|
|
|
|
//
|
|
// END VOXELIZER PUBLIC API
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
#endif // VOXELIZER_H
|
|
|
|
#ifdef VOXELIZER_IMPLEMENTATION
|
|
|
|
#include <math.h> // ceil, fabs & al.
|
|
#include <stdbool.h> // hughh
|
|
#include <string.h> // memcpy
|
|
|
|
#define VOXELIZER_EPSILON (0.0000001)
|
|
#define VOXELIZER_NORMAL_INDICES_SIZE (6)
|
|
#define VOXELIZER_INDICES_SIZE (36)
|
|
#define VOXELIZER_HASH_TABLE_SIZE (4096)
|
|
|
|
unsigned int vx_voxel_indices[VOXELIZER_INDICES_SIZE] = {
|
|
0, 1, 2,
|
|
0, 2, 3,
|
|
3, 2, 6,
|
|
3, 6, 7,
|
|
0, 7, 4,
|
|
0, 3, 7,
|
|
4, 7, 5,
|
|
7, 6, 5,
|
|
0, 4, 5,
|
|
0, 5, 1,
|
|
1, 5, 6,
|
|
1, 6, 2,
|
|
};
|
|
|
|
float vx_normals[18] = {
|
|
0.0, -1.0, 0.0,
|
|
0.0, 1.0, 0.0,
|
|
1.0, 0.0, 0.0,
|
|
0.0, 0.0, 1.0,
|
|
-1.0, 0.0, 0.0,
|
|
0.0, 0.0, -1.0,
|
|
};
|
|
|
|
unsigned int vx_normal_indices[VOXELIZER_NORMAL_INDICES_SIZE] = {
|
|
3, 2, 1, 5, 4, 0,
|
|
};
|
|
|
|
typedef struct vx_aabb {
|
|
vx_vertex_t min;
|
|
vx_vertex_t max;
|
|
} vx_aabb_t;
|
|
|
|
typedef struct vx_edge {
|
|
vx_vertex_t p1;
|
|
vx_vertex_t p2;
|
|
} vx_edge_t;
|
|
|
|
typedef struct vx_triangle {
|
|
vx_vertex_t p1;
|
|
vx_vertex_t p2;
|
|
vx_vertex_t p3;
|
|
} vx_triangle_t;
|
|
|
|
typedef struct vx_hash_table_node {
|
|
struct vx_hash_table_node* next;
|
|
struct vx_hash_table_node* prev;
|
|
void* data;
|
|
} vx_hash_table_node_t;
|
|
|
|
typedef struct vx_hash_table {
|
|
vx_hash_table_node_t** elements;
|
|
size_t size;
|
|
} vx_hash_table_t;
|
|
|
|
vx_hash_table_t* vx__hash_table_alloc(size_t size)
|
|
{
|
|
vx_hash_table_t* table = VX_MALLOC(vx_hash_table_t, 1);
|
|
if (!table) { return NULL; }
|
|
table->size = size;
|
|
table->elements = VX_MALLOC(vx_hash_table_node_t*, size);
|
|
if (!table->elements) { return NULL; }
|
|
for (size_t i = 0; i < table->size; ++i) {
|
|
table->elements[i] = NULL;
|
|
}
|
|
return table;
|
|
}
|
|
|
|
void vx__hash_table_free(vx_hash_table_t* table, bool freedata)
|
|
{
|
|
for (size_t i = 0; i < table->size; ++i) {
|
|
vx_hash_table_node_t* node = table->elements[i];
|
|
|
|
if (node) {
|
|
if (node->next) {
|
|
while (node->next) {
|
|
node = node->next;
|
|
if (freedata) {
|
|
VX_FREE(node->prev->data);
|
|
}
|
|
VX_FREE(node->prev);
|
|
}
|
|
VX_FREE(node);
|
|
} else {
|
|
VX_FREE(node->data);
|
|
VX_FREE(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
VX_FREE(table->elements);
|
|
VX_FREE(table);
|
|
}
|
|
|
|
bool vx__hash_table_insert(vx_hash_table_t* table,
|
|
size_t hash,
|
|
void* data,
|
|
bool (*compfunc)(void* d1, void* d2))
|
|
{
|
|
if (!table->elements[hash]) {
|
|
table->elements[hash] = VX_MALLOC(vx_hash_table_node_t, 1);
|
|
table->elements[hash]->prev = NULL;
|
|
table->elements[hash]->next = NULL;
|
|
table->elements[hash]->data = data;
|
|
} else {
|
|
vx_hash_table_node_t* node = table->elements[hash];
|
|
|
|
if (compfunc && compfunc(node->data, data)) {
|
|
return false;
|
|
}
|
|
|
|
while (node->next) {
|
|
node = node->next;
|
|
if (compfunc && compfunc(node->data, data)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
vx_hash_table_node_t* nnode = VX_MALLOC(vx_hash_table_node_t, 1);
|
|
|
|
nnode->prev = node;
|
|
nnode->next = NULL;
|
|
nnode->data = data;
|
|
node->next = nnode;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void vx_mesh_free(vx_mesh_t* m)
|
|
{
|
|
VX_FREE(m->vertices);
|
|
m->vertices = NULL;
|
|
m->nvertices = 0;
|
|
VX_FREE(m->indices);
|
|
m->indices = NULL;
|
|
m->nindices = 0;
|
|
if (m->normals) { VX_FREE(m->normals); }
|
|
VX_FREE(m);
|
|
}
|
|
|
|
vx_mesh_t* vx_mesh_alloc(int nvertices, int nindices)
|
|
{
|
|
vx_mesh_t* m = VX_MALLOC(vx_mesh_t, 1);
|
|
if (!m) { return NULL; }
|
|
m->indices = VX_CALLOC(unsigned int, nindices);
|
|
if (!m->indices) { return NULL; }
|
|
m->vertices = VX_CALLOC(vx_vertex_t, nvertices);
|
|
if (!m->vertices) { return NULL; }
|
|
m->normals = NULL;
|
|
m->nindices = nindices;
|
|
m->nvertices = nvertices;
|
|
return m;
|
|
}
|
|
|
|
float vx__map_to_voxel(float position, float voxelSize, bool min)
|
|
{
|
|
float vox = (position + (position < 0.f ? -1.f : 1.f) * voxelSize * 0.5f) / voxelSize;
|
|
return (min ? floor(vox) : ceil(vox)) * voxelSize;
|
|
}
|
|
|
|
vx_vertex_t vx__vertex_cross(vx_vertex_t* v1, vx_vertex_t* v2)
|
|
{
|
|
vx_vertex_t cross;
|
|
cross.x = v1->y * v2->z - v1->z * v2->y;
|
|
cross.y = v1->z * v2->x - v1->x * v2->z;
|
|
cross.z = v1->x * v2->y - v1->y * v2->x;
|
|
return cross;
|
|
}
|
|
|
|
bool vx__vertex_equals(vx_vertex_t* v1, vx_vertex_t* v2) {
|
|
return fabs(v1->x - v2->x) < VOXELIZER_EPSILON &&
|
|
fabs(v1->y - v2->y) < VOXELIZER_EPSILON &&
|
|
fabs(v1->z - v2->z) < VOXELIZER_EPSILON;
|
|
}
|
|
|
|
bool vx__vertex_comp_func(void* a, void* b)
|
|
{
|
|
return vx__vertex_equals((vx_vertex_t*) a, (vx_vertex_t*) b);
|
|
}
|
|
|
|
void vx__vertex_sub(vx_vertex_t* a, vx_vertex_t* b)
|
|
{
|
|
a->x -= b->x;
|
|
a->y -= b->y;
|
|
a->z -= b->z;
|
|
}
|
|
|
|
void vx__vertex_add(vx_vertex_t* a, vx_vertex_t* b)
|
|
{
|
|
a->x += b->x;
|
|
a->y += b->y;
|
|
a->z += b->z;
|
|
}
|
|
|
|
void vx__vertex_multiply(vx_vertex_t* a, float v)
|
|
{
|
|
a->x *= v;
|
|
a->y *= v;
|
|
a->z *= v;
|
|
}
|
|
|
|
float vx__vertex_dot(vx_vertex_t* v1, vx_vertex_t* v2)
|
|
{
|
|
return v1->x * v2->x + v1->y * v2->y + v1->z * v2->z;
|
|
}
|
|
|
|
int vx__plane_box_overlap(vx_vertex_t* normal,
|
|
float d,
|
|
vx_vertex_t* halfboxsize)
|
|
{
|
|
vx_vertex_t vmin, vmax;
|
|
|
|
for (int dim = 0; dim <= 2; dim++) {
|
|
if (normal->v[dim] > 0.0f) {
|
|
vmin.v[dim] = -halfboxsize->v[dim];
|
|
vmax.v[dim] = halfboxsize->v[dim];
|
|
} else {
|
|
vmin.v[dim] = halfboxsize->v[dim];
|
|
vmax.v[dim] = -halfboxsize->v[dim];
|
|
}
|
|
}
|
|
|
|
if (vx__vertex_dot(normal, &vmin) + d > 0.0f) {
|
|
return false;
|
|
}
|
|
|
|
if (vx__vertex_dot(normal, &vmax) + d >= 0.0f) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#define AXISTEST_X01(a, b, fa, fb) \
|
|
p1 = a * v1.y - b * v1.z; \
|
|
p3 = a * v3.y - b * v3.z; \
|
|
if (p1 < p3) { \
|
|
min = p1; max = p3; \
|
|
} else { \
|
|
min = p3; max = p1; \
|
|
} \
|
|
rad = fa * halfboxsize.y + fb * halfboxsize.z; \
|
|
if (min > rad || max < -rad) { \
|
|
return false; \
|
|
} \
|
|
|
|
#define AXISTEST_X2(a, b, fa, fb) \
|
|
p1 = a * v1.y - b * v1.z; \
|
|
p2 = a * v2.y - b * v2.z; \
|
|
if (p1 < p2) { \
|
|
min = p1; max = p2; \
|
|
} else { \
|
|
min = p2; max = p1; \
|
|
} \
|
|
rad = fa * halfboxsize.y + fb * halfboxsize.z; \
|
|
if (min > rad || max < -rad) { \
|
|
return false; \
|
|
} \
|
|
|
|
#define AXISTEST_Y02(a, b, fa, fb) \
|
|
p1 = -a * v1.x + b * v1.z; \
|
|
p3 = -a * v3.x + b * v3.z; \
|
|
if (p1 < p3) { \
|
|
min = p1; max = p3; \
|
|
} else { \
|
|
min = p3; max = p1; \
|
|
} \
|
|
rad = fa * halfboxsize.x + fb * halfboxsize.z; \
|
|
if (min > rad || max < -rad) { \
|
|
return false; \
|
|
} \
|
|
|
|
#define AXISTEST_Y1(a, b, fa, fb) \
|
|
p1 = -a * v1.x + b * v1.z; \
|
|
p2 = -a * v2.x + b * v2.z; \
|
|
if (p1 < p2) { \
|
|
min = p1; max = p2; \
|
|
} else { \
|
|
min = p2; max = p1; \
|
|
} \
|
|
rad = fa * halfboxsize.x + fb * halfboxsize.z; \
|
|
if (min > rad || max < -rad) { \
|
|
return false; \
|
|
}
|
|
|
|
#define AXISTEST_Z12(a, b, fa, fb) \
|
|
p2 = a * v2.x - b * v2.y; \
|
|
p3 = a * v3.x - b * v3.y; \
|
|
if (p3 < p2) { \
|
|
min = p3; max = p2; \
|
|
} else { \
|
|
min = p2; max = p3; \
|
|
} \
|
|
rad = fa * halfboxsize.x + fb * halfboxsize.y; \
|
|
if (min > rad || max < -rad) { \
|
|
return false; \
|
|
}
|
|
|
|
#define AXISTEST_Z0(a, b, fa, fb) \
|
|
p1 = a * v1.x - b * v1.y; \
|
|
p2 = a * v2.x - b * v2.y; \
|
|
if (p1 < p2) { \
|
|
min = p1; max = p2; \
|
|
} else { \
|
|
min = p2; max = p1; \
|
|
} \
|
|
rad = fa * halfboxsize.x + fb * halfboxsize.y; \
|
|
if (min > rad || max < -rad) { \
|
|
return false; \
|
|
}
|
|
|
|
int vx__triangle_box_overlap(vx_vertex_t boxcenter,
|
|
vx_vertex_t halfboxsize,
|
|
vx_triangle_t triangle)
|
|
{
|
|
vx_vertex_t v1, v2, v3, normal, e1, e2, e3;
|
|
float min, max, d, p1, p2, p3, rad, fex, fey, fez;
|
|
|
|
v1 = triangle.p1;
|
|
v2 = triangle.p2;
|
|
v3 = triangle.p3;
|
|
|
|
vx__vertex_sub(&v1, &boxcenter);
|
|
vx__vertex_sub(&v2, &boxcenter);
|
|
vx__vertex_sub(&v3, &boxcenter);
|
|
|
|
e1 = v2;
|
|
e2 = v3;
|
|
e3 = v1;
|
|
|
|
vx__vertex_sub(&e1, &v1);
|
|
vx__vertex_sub(&e2, &v2);
|
|
vx__vertex_sub(&e3, &v3);
|
|
|
|
fex = fabs(e1.x);
|
|
fey = fabs(e1.y);
|
|
fez = fabs(e1.z);
|
|
|
|
AXISTEST_X01(e1.z, e1.y, fez, fey);
|
|
AXISTEST_Y02(e1.z, e1.x, fez, fex);
|
|
AXISTEST_Z12(e1.y, e1.x, fey, fex);
|
|
|
|
fex = fabs(e2.x);
|
|
fey = fabs(e2.y);
|
|
fez = fabs(e2.z);
|
|
|
|
AXISTEST_X01(e2.z, e2.y, fez, fey);
|
|
AXISTEST_Y02(e2.z, e2.x, fez, fex);
|
|
AXISTEST_Z0(e2.y, e2.x, fey, fex);
|
|
|
|
fex = fabs(e3.x);
|
|
fey = fabs(e3.y);
|
|
fez = fabs(e3.z);
|
|
|
|
AXISTEST_X2(e3.z, e3.y, fez, fey);
|
|
AXISTEST_Y1(e3.z, e3.x, fez, fex);
|
|
AXISTEST_Z12(e3.y, e3.x, fey, fex);
|
|
|
|
VX_FINDMINMAX(v1.x, v2.x, v3.x, min, max);
|
|
if (min > halfboxsize.x || max < -halfboxsize.x) {
|
|
return false;
|
|
}
|
|
|
|
VX_FINDMINMAX(v1.y, v2.y, v3.y, min, max);
|
|
if (min > halfboxsize.y || max < -halfboxsize.y) {
|
|
return false;
|
|
}
|
|
|
|
VX_FINDMINMAX(v1.z, v2.z, v3.z, min, max);
|
|
if (min > halfboxsize.z || max < -halfboxsize.z) {
|
|
return false;
|
|
}
|
|
|
|
normal = vx__vertex_cross(&e1, &e2);
|
|
d = -vx__vertex_dot(&normal, &v1);
|
|
|
|
if (!vx__plane_box_overlap(&normal, d, &halfboxsize)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#undef AXISTEST_X2
|
|
#undef AXISTEST_X01
|
|
#undef AXISTEST_Y1
|
|
#undef AXISTEST_Y02
|
|
#undef AXISTEST_Z0
|
|
#undef AXISTEST_Z12
|
|
|
|
float vx__triangle_area(vx_triangle_t* triangle) {
|
|
vx_vertex_t ab = triangle->p2;
|
|
vx_vertex_t ac = triangle->p3;
|
|
|
|
vx__vertex_sub(&ab, &triangle->p1);
|
|
vx__vertex_sub(&ac, &triangle->p1);
|
|
|
|
float a0 = ab.y * ac.z - ab.z * ac.y;
|
|
float a1 = ab.z * ac.x - ab.x * ac.z;
|
|
float a2 = ab.x * ac.y - ab.y * ac.x;
|
|
|
|
return sqrtf(powf(a0, 2.f) + powf(a1, 2.f) + powf(a2, 2.f)) * 0.5f;
|
|
}
|
|
|
|
void vx__aabb_init(vx_aabb_t* aabb)
|
|
{
|
|
aabb->max.x = aabb->max.y = aabb->max.z = -INFINITY;
|
|
aabb->min.x = aabb->min.y = aabb->min.z = INFINITY;
|
|
}
|
|
|
|
vx_aabb_t vx__triangle_aabb(vx_triangle_t* triangle)
|
|
{
|
|
vx_aabb_t aabb;
|
|
|
|
vx__aabb_init(&aabb);
|
|
|
|
aabb.max.x = VX_MAX(aabb.max.x, triangle->p1.x); aabb.max.x = VX_MAX(aabb.max.x,
|
|
triangle->p2.x); aabb.max.x = VX_MAX(aabb.max.x, triangle->p3.x);
|
|
aabb.max.y = VX_MAX(aabb.max.y, triangle->p1.y); aabb.max.y = VX_MAX(aabb.max.y,
|
|
triangle->p2.y); aabb.max.y = VX_MAX(aabb.max.y, triangle->p3.y);
|
|
aabb.max.z = VX_MAX(aabb.max.z, triangle->p1.z); aabb.max.z = VX_MAX(aabb.max.z,
|
|
triangle->p2.z); aabb.max.z = VX_MAX(aabb.max.z, triangle->p3.z);
|
|
|
|
aabb.min.x = VX_MIN(aabb.min.x, triangle->p1.x); aabb.min.x = VX_MIN(aabb.min.x,
|
|
triangle->p2.x); aabb.min.x = VX_MIN(aabb.min.x, triangle->p3.x);
|
|
aabb.min.y = VX_MIN(aabb.min.y, triangle->p1.y); aabb.min.y = VX_MIN(aabb.min.y,
|
|
triangle->p2.y); aabb.min.y = VX_MIN(aabb.min.y, triangle->p3.y);
|
|
aabb.min.z = VX_MIN(aabb.min.z, triangle->p1.z); aabb.min.z = VX_MIN(aabb.min.z,
|
|
triangle->p2.z); aabb.min.z = VX_MIN(aabb.min.z, triangle->p3.z);
|
|
|
|
return aabb;
|
|
}
|
|
|
|
vx_vertex_t vx__aabb_center(vx_aabb_t* a)
|
|
{
|
|
vx_vertex_t boxcenter = a->min;
|
|
vx__vertex_add(&boxcenter, &a->max);
|
|
vx__vertex_multiply(&boxcenter, 0.5f);
|
|
|
|
return boxcenter;
|
|
}
|
|
|
|
vx_vertex_t vx__aabb_half_size(vx_aabb_t* a)
|
|
{
|
|
vx_vertex_t size;
|
|
|
|
size.x = fabs(a->max.x - a->min.x) * 0.5f;
|
|
size.y = fabs(a->max.y - a->min.y) * 0.5f;
|
|
size.z = fabs(a->max.z - a->min.z) * 0.5f;
|
|
|
|
return size;
|
|
}
|
|
|
|
vx_aabb_t vx__aabb_merge(vx_aabb_t* a, vx_aabb_t* b)
|
|
{
|
|
vx_aabb_t merge;
|
|
|
|
merge.min.x = VX_MIN(a->min.x, b->min.x);
|
|
merge.min.y = VX_MIN(a->min.y, b->min.y);
|
|
merge.min.z = VX_MIN(a->min.z, b->min.z);
|
|
|
|
merge.max.x = VX_MAX(a->max.x, b->max.x);
|
|
merge.max.y = VX_MAX(a->max.y, b->max.y);
|
|
merge.max.z = VX_MAX(a->max.z, b->max.z);
|
|
|
|
return merge;
|
|
}
|
|
|
|
size_t vx__vertex_hash(vx_vertex_t pos, size_t n)
|
|
{
|
|
size_t a = (size_t)(pos.x * 73856093);
|
|
size_t b = (size_t)(pos.y * 19349663);
|
|
size_t c = (size_t)(pos.z * 83492791);
|
|
|
|
return (a ^ b ^ c) % n;
|
|
}
|
|
|
|
void vx__add_voxel(vx_mesh_t* mesh,
|
|
vx_vertex_t* pos,
|
|
float* vertices)
|
|
{
|
|
for (size_t i = 0; i < 8; ++i) {
|
|
size_t index = i+mesh->nvertices;
|
|
|
|
mesh->vertices[index].x = vertices[i*3+0] + pos->x;
|
|
mesh->vertices[index].y = vertices[i*3+1] + pos->y;
|
|
mesh->vertices[index].z = vertices[i*3+2] + pos->z;
|
|
}
|
|
|
|
int j = -1;
|
|
for (size_t i = 0; i < VOXELIZER_INDICES_SIZE; ++i) {
|
|
if (i % 6 == 0) {
|
|
j++;
|
|
}
|
|
mesh->normalindices[i+mesh->nindices] = vx_normal_indices[j];
|
|
}
|
|
|
|
for (size_t i = 0; i < VOXELIZER_INDICES_SIZE; ++i) {
|
|
mesh->indices[i+mesh->nindices] = vx_voxel_indices[i] + mesh->nvertices;
|
|
}
|
|
|
|
mesh->nindices += VOXELIZER_INDICES_SIZE;
|
|
mesh->nvertices += 8;
|
|
}
|
|
|
|
vx_mesh_t* vx_voxelize(vx_mesh_t* m,
|
|
float voxelsizex,
|
|
float voxelsizey,
|
|
float voxelsizez,
|
|
float precision)
|
|
{
|
|
vx_mesh_t* outmesh = NULL;
|
|
vx_hash_table_t* table = NULL;
|
|
size_t voxels = 0;
|
|
|
|
float halfsizex = voxelsizex * 0.5f;
|
|
float halfsizey = voxelsizey * 0.5f;
|
|
float halfsizez = voxelsizez * 0.5f;
|
|
|
|
table = vx__hash_table_alloc(VOXELIZER_HASH_TABLE_SIZE);
|
|
|
|
for (int i = 0; i < m->nindices; i += 3) {
|
|
vx_triangle_t triangle;
|
|
|
|
VX_ASSERT(m->indices[i+0] < m->nvertices);
|
|
VX_ASSERT(m->indices[i+1] < m->nvertices);
|
|
VX_ASSERT(m->indices[i+2] < m->nvertices);
|
|
|
|
triangle.p1 = m->vertices[m->indices[i+0]];
|
|
triangle.p2 = m->vertices[m->indices[i+1]];
|
|
triangle.p3 = m->vertices[m->indices[i+2]];
|
|
|
|
if (vx__triangle_area(&triangle) < VOXELIZER_EPSILON) {
|
|
// triangle with 0 area
|
|
continue;
|
|
}
|
|
|
|
vx_aabb_t aabb = vx__triangle_aabb(&triangle);
|
|
|
|
aabb.min.x = vx__map_to_voxel(aabb.min.x, voxelsizex, true);
|
|
aabb.min.y = vx__map_to_voxel(aabb.min.y, voxelsizey, true);
|
|
aabb.min.z = vx__map_to_voxel(aabb.min.z, voxelsizez, true);
|
|
|
|
aabb.max.x = vx__map_to_voxel(aabb.max.x, voxelsizex, false);
|
|
aabb.max.y = vx__map_to_voxel(aabb.max.y, voxelsizey, false);
|
|
aabb.max.z = vx__map_to_voxel(aabb.max.z, voxelsizez, false);
|
|
|
|
for (float x = aabb.min.x; x < aabb.max.x; x += voxelsizex) {
|
|
for (float y = aabb.min.y; y < aabb.max.y; y += voxelsizey) {
|
|
for (float z = aabb.min.z; z < aabb.max.z; z += voxelsizez) {
|
|
vx_aabb_t saabb;
|
|
|
|
saabb.min.x = x - halfsizex;
|
|
saabb.min.y = y - halfsizey;
|
|
saabb.min.z = z - halfsizez;
|
|
saabb.max.x = x + halfsizex;
|
|
saabb.max.y = y + halfsizey;
|
|
saabb.max.z = z + halfsizez;
|
|
|
|
vx_vertex_t boxcenter = vx__aabb_center(&saabb);
|
|
vx_vertex_t halfsize = vx__aabb_half_size(&saabb);
|
|
|
|
// HACK: some holes might appear, this
|
|
// precision factor reduces the artifact
|
|
halfsize.x += precision;
|
|
halfsize.y += precision;
|
|
halfsize.z += precision;
|
|
|
|
if (vx__triangle_box_overlap(boxcenter, halfsize, triangle)) {
|
|
vx_vertex_t* nodedata = VX_MALLOC(vx_vertex_t, 1);
|
|
*nodedata = boxcenter;
|
|
|
|
size_t hash = vx__vertex_hash(boxcenter, VOXELIZER_HASH_TABLE_SIZE);
|
|
|
|
bool insert = vx__hash_table_insert(table,
|
|
hash,
|
|
nodedata,
|
|
vx__vertex_comp_func);
|
|
|
|
if (insert) {
|
|
voxels++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
outmesh = VX_MALLOC(vx_mesh_t, 1);
|
|
size_t nvertices = voxels * 8;
|
|
size_t nindices = voxels * VOXELIZER_INDICES_SIZE;
|
|
outmesh->nnormals = VOXELIZER_NORMAL_INDICES_SIZE;
|
|
outmesh->vertices = VX_CALLOC(vx_vertex_t, nvertices);
|
|
outmesh->normals = VX_CALLOC(vx_vertex_t, 6);
|
|
outmesh->indices = VX_CALLOC(unsigned int, nindices);
|
|
outmesh->normalindices = VX_CALLOC(unsigned int, nindices);
|
|
outmesh->nindices = 0;
|
|
outmesh->nvertices = 0;
|
|
|
|
memcpy(outmesh->normals, vx_normals, 18 * sizeof(float));
|
|
|
|
float vertices[24] = {
|
|
-halfsizex, halfsizey, halfsizez,
|
|
-halfsizex, -halfsizey, halfsizez,
|
|
halfsizex, -halfsizey, halfsizez,
|
|
halfsizex, halfsizey, halfsizez,
|
|
-halfsizex, halfsizey, -halfsizez,
|
|
-halfsizex, -halfsizey, -halfsizez,
|
|
halfsizex, -halfsizey, -halfsizez,
|
|
halfsizex, halfsizey, -halfsizez,
|
|
};
|
|
|
|
for (size_t i = 0; i < table->size; ++i) {
|
|
if (table->elements[i] != NULL) {
|
|
vx_hash_table_node_t* node = table->elements[i];
|
|
|
|
if (!node) {
|
|
continue;
|
|
}
|
|
|
|
vx_vertex_t* p = (vx_vertex_t*) node->data;
|
|
vx__add_voxel(outmesh, p, vertices);
|
|
|
|
while (node->next) {
|
|
node = node->next;
|
|
p = (vx_vertex_t*) node->data;
|
|
vx__add_voxel(outmesh, p, vertices);
|
|
}
|
|
}
|
|
}
|
|
|
|
vx__hash_table_free(table, true);
|
|
|
|
return outmesh;
|
|
}
|
|
|
|
#undef VOXELIZER_EPSILON
|
|
#undef VOXELIZER_INDICES_SIZE
|
|
#undef VOXELIZER_HASH_TABLE_SIZE
|
|
|
|
#endif // VX_VOXELIZER_IMPLEMENTATION
|