trying to fix the clipping bugs
[meshfrac] / src / frac.c
index 071b5c2..4263c65 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdlib.h>
 #include <string.h>
+#include <assert.h>
 #include "frac.h"
 #include "dynarr.h"
 
@@ -31,12 +32,20 @@ void frac_destroy(struct fracture *frac)
        }
 }
 
+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)
@@ -49,17 +58,10 @@ int frac_point(struct fracture *frac, float x, float y, float z)
        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;
@@ -113,39 +115,251 @@ int frac_build_cells(struct fracture *frac)
                }
        }
 
-       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 *= 8;
 
        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))) {
-                       return -1;
+               midplane(&plane, &frac->cells[i].pt, &cell->pt, frac->cell_gap);
+               if(plane_sdist(&plane, &cell->pt) > 0.0f) {
+                       plane_poly(&plane, &poly, bsize);
+                       if(!(pptr = dynarr_push(polys, &poly))) {
+                               return -1;
+                       }
+                       polys = pptr;
                }
-               cell->planes = 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_poly...
+                       /* 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 *tmpvert, vert = {0};
+       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];
+               if(narr) {
+                       vert.norm.x = narr[vidx * nsz];
+                       vert.norm.y = narr[vidx * nsz + 1];
+                       vert.norm.z = narr[vidx * nsz + 2];
+               }
+               if(uvarr) {
+                       vert.uv.x = uvarr[vidx * uvsz];
+                       vert.uv.y = uvarr[vidx * uvsz + 1];
+               }
+
+               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);
+               /* flip the plane normal towards the inside of the cell, to clip everything
+                * outside the cell
+                */
+               cgm_vcons(&cplanes[i].norm, -cplanes[i].norm.x, -cplanes[i].norm.y, -cplanes[i].norm.z);
+       }
+
+       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;
+                                       destroy_poly(&wallclipped);
+                               } 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)