#include <stdlib.h>
#include <string.h>
+#include <assert.h>
#include "frac.h"
#include "dynarr.h"
}
}
+static int init_cell(struct frac_cell *cell)
+{
+ memset(cell, 0, sizeof *cell);
+ if(!(cell->mesh = cmesh_alloc())) {
+ return -1;
+ }
+ return 0;
+}
+
static void destroy_cell(struct frac_cell *cell)
{
if(!cell) return;
-
+ free(cell->polys);
cmesh_free(cell->mesh);
- dynarr_free(cell->planes);
}
void frac_mesh(struct fracture *frac, const struct cmesh *m)
struct frac_cell cell;
struct frac_cell *tmp;
+ init_cell(&cell);
cgm_vcons(&cell.pt, x, y, z);
- if(!(cell.mesh = cmesh_alloc())) {
- return -1;
- }
- if(!(cell.planes = dynarr_alloc(0, sizeof *cell.planes))) {
- cmesh_free(cell.mesh);
- return -1;
- }
if(!(tmp = dynarr_push(frac->cells, &cell))) {
- cmesh_free(cell.mesh);
- dynarr_free(cell.planes);
+ destroy_cell(&cell);
return -1;
}
frac->cells = tmp;
}
}
- return -1;
+ return 0;
}
static int build_cell(struct fracture *frac, int cellidx)
{
- int i, j, num;
- struct plane plane, *pptr;
+ int i, j, num, clipres;
+ int *valid_planes;
+ struct plane plane;
struct frac_cell *cell = frac->cells + cellidx;
+ struct poly poly, clipped, *polys, *pptr;
+ float bsize;
+
+ if(!(polys = dynarr_alloc(0, sizeof *polys))) {
+ return -1;
+ }
+
+ cmesh_bsphere(frac->mesh, 0, &bsize);
+ bsize *= 2;
num = dynarr_size(frac->cells);
for(i=0; i<num; i++) {
if(i == cellidx) continue;
midplane(&plane, &cell->pt, &frac->cells[i].pt);
- if(!(pptr = dynarr_push(cell->planes, &plane))) {
+ plane_poly(&plane, &poly, bsize);
+ if(!(pptr = dynarr_push(polys, &poly))) {
return -1;
}
- cell->planes = pptr;
+ polys = pptr;
}
+ num = dynarr_size(polys);
+ valid_planes = alloca(num * sizeof *valid_planes);
+ memset(valid_planes, 0xff, num * sizeof *valid_planes);
+
/* clip all planes against each other to end up with a convex cell */
- num = dynarr_size(cell->planes);
+ cell->num_polys = num;
for(i=0; i<num; i++) {
for(j=0; j<num; j++) {
- if(i == j) continue;
+ if(i == j || !valid_planes[j]) {
+ continue;
+ }
+
+ /* clip plane polygon i with plane j */
+ poly_plane(polys + j, &plane);
+ init_poly(&clipped);
+ if((clipres = clip_poly(&clipped, polys + i, &plane)) < 0) {
+ /* completely clipped, mark it for removal */
+ valid_planes[i] = 0;
+ cell->num_polys--;
+ destroy_poly(&clipped);
+ break;
+ }
+ destroy_poly(polys + i);
+ polys[i] = clipped;
+ }
+ }
+
+ if(!(cell->polys = malloc(cell->num_polys * sizeof *cell->polys))) {
+ return -1;
+ }
+ pptr = cell->polys;
+ for(i=0; i<num; i++) {
+ if(valid_planes[i]) {
+ assert(pptr - cell->polys < cell->num_polys);
+ *pptr++ = polys[i];
+ } else {
+ destroy_poly(polys + i);
+ }
+ }
+ dynarr_free(polys);
+ return 0;
+}
+
+static int mesh_poly(struct poly *poly, const struct cmesh *mesh, int faceidx)
+{
+ int i, vsz, nsz, uvsz;
+ struct vertex vert, *tmpvert;
+ const float *varr, *narr, *uvarr;
+ unsigned int vidx;
+
+ if(init_poly(poly) == -1) {
+ return -1;
+ }
+
+ varr = cmesh_attrib_ro(mesh, CMESH_ATTR_VERTEX);
+ narr = cmesh_attrib_ro(mesh, CMESH_ATTR_NORMAL);
+ uvarr = cmesh_attrib_ro(mesh, CMESH_ATTR_TEXCOORD);
+ vsz = cmesh_attrib_nelem(mesh, CMESH_ATTR_VERTEX);
+ nsz = cmesh_attrib_nelem(mesh, CMESH_ATTR_NORMAL);
+ uvsz = cmesh_attrib_nelem(mesh, CMESH_ATTR_TEXCOORD);
+
+ for(i=0; i<3; i++) {
+ if(cmesh_indexed(mesh)) {
+ vidx = cmesh_index_ro(mesh)[faceidx * 3 + i];
+ } else {
+ vidx = faceidx * 3 + i;
+ }
+ vert.pos.x = varr[vidx * vsz];
+ vert.pos.y = varr[vidx * vsz + 1];
+ vert.pos.z = varr[vidx * vsz + 2];
+ vert.norm.x = narr[vidx * nsz];
+ vert.norm.y = narr[vidx * nsz + 1];
+ vert.norm.z = narr[vidx * nsz + 2];
+ vert.uv.x = uvarr[vidx * uvsz];
+ vert.uv.y = uvarr[vidx * uvsz + 1];
- clip_poly...
+ if(!(tmpvert = dynarr_push(poly->verts, &vert))) {
+ destroy_poly(poly);
+ return -1;
}
+ poly->verts = tmpvert;
}
+ return 0;
+}
+
+#define ADD_VERTEX(mesh, vert) \
+ do { \
+ cmesh_normal(mesh, (vert)->norm.x, (vert)->norm.y, (vert)->norm.z); \
+ cmesh_texcoord(mesh, (vert)->uv.x, (vert)->uv.y, 0); \
+ if(cmesh_vertex(mesh, (vert)->pos.x, (vert)->pos.y, (vert)->pos.z) == -1) { \
+ fprintf(stderr, "SKATA\n"); \
+ abort(); \
+ } \
+ } while(0)
+
+static int build_cell_shell(struct cmesh *mesh, const struct cmesh *orig,
+ struct frac_cell *cell)
+{
+ int i, j, nfaces, clipres;
+ struct plane plane;
+ struct poly poly, clipped, wallclipped;
+ struct vertex *vert;
+ int *delwall;
+ struct plane *cplanes;
+
+ /* array for marking wall polygons for deletion when they get clipped entirely */
+ delwall = alloca(cell->num_polys * sizeof *delwall);
+ memset(delwall, 0, cell->num_polys * sizeof *delwall);
+
+ /* array for pre-constructing the voronoi clipping planes */
+ cplanes = alloca(cell->num_polys * sizeof *cplanes);
+ for(i=0; i<cell->num_polys; i++) {
+ poly_plane(cell->polys + i, cplanes + i);
+ }
+
+ nfaces = cmesh_poly_count(orig);
+ for(i=0; i<nfaces; i++) {
+ if(mesh_poly(&poly, orig, i) == -1) {
+ cmesh_destroy(mesh);
+ return -1;
+ }
+
+ for(j=0; j<cell->num_polys; j++) {
+ init_poly(&clipped);
+ if((clipres = clip_poly(&clipped, &poly, cplanes + j)) < 0) {
+ destroy_poly(&clipped);
+ break;
+ }
+
+ /* if the plane clipped the polygon, and the two polygons intersect
+ * within their bounds, also clip the cell polygon by the original
+ * mesh polygon.
+ *
+ * TODO clipping with the polygon's plane is incorrect, and will lead
+ * to gaps in the cell walls when the surface is concave. We'll need
+ * to clip by the polygon itself, which can make the wall polygon
+ * concave, and will need to be split into multiple convex ones.
+ */
+ if(clipres == 0 && poly_poly(&poly, cell->polys + j)) {
+ poly_plane(&poly, &plane);
+ init_poly(&wallclipped);
+
+ if((clipres = clip_poly(&wallclipped, cell->polys + j, &plane)) < 0) {
+ /* mark for deletion */
+ delwall[j] = 1;
+ } else if(clipres == 0) {
+ destroy_poly(cell->polys + j);
+ cell->polys[j] = wallclipped;
+ } else {
+ destroy_poly(&wallclipped);
+ }
+ }
+
+ destroy_poly(&poly);
+ poly = clipped;
+ }
+
+ if(j < cell->num_polys) {
+ continue;
+ }
+
+ vert = poly.verts + 1;
+ for(j=0; j<dynarr_size(poly.verts)-2; j++) {
+ ADD_VERTEX(mesh, poly.verts);
+ ADD_VERTEX(mesh, vert); vert++;
+ ADD_VERTEX(mesh, vert);
+ }
+ destroy_poly(&poly);
+ }
+
+ /* remove deleted wall polygons */
+ for(i=0; i<cell->num_polys; i++) {
+ if(delwall[i]) {
+ struct poly tmp = cell->polys[i];
+ cell->polys[i] = cell->polys[--cell->num_polys];
+ destroy_poly(&tmp);
+ }
+ }
+
+ /* add wall polygons to the mesh */
+ for(i=0; i<cell->num_polys; i++) {
+ vert = cell->polys[i].verts + 1;
+ for(j=0; j<dynarr_size(cell->polys[i].verts)-2; j++) {
+ ADD_VERTEX(mesh, cell->polys[i].verts);
+ ADD_VERTEX(mesh, vert); vert++;
+ ADD_VERTEX(mesh, vert);
+ }
+ }
+
+ return 0;
}
int frac_build_shell(struct fracture *frac)
{
- return -1;
+ int i, num_cells;
+
+ num_cells = dynarr_size(frac->cells);
+ for(i=0; i<num_cells; i++) {
+ if(build_cell_shell(frac->cells[i].mesh, frac->mesh, frac->cells + i) == -1) {
+ return -1;
+ }
+ }
+
+ return 0;
}
int frac_build_walls(struct fracture *frac)