From e9e9a170e379cb58732d18bf20c7bc15fea211e0 Mon Sep 17 00:00:00 2001 From: Jeremy Hu Date: Wed, 28 Dec 2016 22:16:25 +0930 Subject: [PATCH] Add triangle to quad conversion, not finish yet. Fix convex hull algorithm. Unify generated faces'normal. Add hashtable. Fix incorrect realloc in array. --- build/dust3d.pro | 8 +- src/array.c | 5 + src/bmesh.c | 130 +++++++++++++++++-- src/convexhull.c | 283 +++++++++++++++++++++++++---------------- src/convexhull.h | 2 + src/hashtable.c | 117 +++++++++++++++++ src/hashtable.h | 15 +++ src/render.cpp | 17 ++- src/tri2quad.c | 324 +++++++++++++++++++++++++++++++++++++++++++++++ src/tri2quad.h | 17 +++ src/vector3d.c | 9 ++ src/vector3d.h | 1 + 12 files changed, 801 insertions(+), 127 deletions(-) create mode 100644 src/hashtable.c create mode 100644 src/hashtable.h create mode 100644 src/tri2quad.c create mode 100644 src/tri2quad.h diff --git a/build/dust3d.pro b/build/dust3d.pro index 6c9faa7c..06f8a778 100644 --- a/build/dust3d.pro +++ b/build/dust3d.pro @@ -15,7 +15,9 @@ SOURCES += main.cpp \ array.c \ bmesh.c \ matrix.c \ - convexhull.c + convexhull.c \ + tri2quad.c \ + hashtable.c HEADERS += mainwindow.h \ render.h \ @@ -24,4 +26,6 @@ HEADERS += mainwindow.h \ array.h \ bmesh.h \ matrix.h \ - convexhull.h \ No newline at end of file + convexhull.h \ + tri2quad.h \ + hashtable.h \ No newline at end of file diff --git a/src/array.c b/src/array.c index d678c9ce..2654d40b 100644 --- a/src/array.c +++ b/src/array.c @@ -23,11 +23,16 @@ array *arrayCreate(int nodeSize) { int arraySetLength(array *arr, int length) { if (length > arr->capacity) { int newCapacity = (arr->capacity + 1) * 2; + if (newCapacity < length) { + newCapacity = length; + } char *newNodes = (char *)realloc(arr->nodes, arr->nodeSize * newCapacity); if (!newNodes) { fprintf(stderr, "%s:Insufficient memory.\n", __FUNCTION__); return -1; } + memset(newNodes + arr->nodeSize * arr->capacity, 0, + arr->nodeSize * (newCapacity - arr->capacity)); arr->capacity = newCapacity; arr->nodes = newNodes; } diff --git a/src/bmesh.c b/src/bmesh.c index 1938c9c2..fd3e2d8b 100644 --- a/src/bmesh.c +++ b/src/bmesh.c @@ -3,10 +3,12 @@ #include #include #include +#include #include "bmesh.h" #include "array.h" #include "matrix.h" #include "convexhull.h" +#include "tri2quad.h" #include "draw.h" #define BMESH_STEP_DISTANCE 0.4 @@ -518,9 +520,12 @@ static bmeshBall *bmeshFindBallForConvexHull(bmesh *bm, bmeshBall *root, return bmeshFindBallForConvexHull(bm, root, child); } -static void addBallToHull(convexHull *hull, bmeshBall *ballForConvexHull) { +static void addBallToHull(convexHull *hull, bmeshBall *ballForConvexHull, + bmeshBall **outmostBall, int *outmostBallFirstVertexIndex) { vec3 z, y; quad q; + int vertexIndex[4]; + int needUpdateOutmost = 0; vec3Scale(&ballForConvexHull->localYaxis, ballForConvexHull->radius, &y); vec3Scale(&ballForConvexHull->localZaxis, ballForConvexHull->radius, &z); @@ -533,10 +538,22 @@ static void addBallToHull(convexHull *hull, bmeshBall *ballForConvexHull) { vec3Add(&ballForConvexHull->position, &y, &q.pt[3]); vec3Add(&q.pt[3], &z, &q.pt[3]); - convexHullAddVertex(hull, &q.pt[0], ballForConvexHull->index, 0); - convexHullAddVertex(hull, &q.pt[1], ballForConvexHull->index, 1); - convexHullAddVertex(hull, &q.pt[2], ballForConvexHull->index, 2); - convexHullAddVertex(hull, &q.pt[3], ballForConvexHull->index, 3); + vertexIndex[0] = convexHullAddVertex(hull, &q.pt[0], ballForConvexHull->index, 0); + vertexIndex[1] = convexHullAddVertex(hull, &q.pt[1], ballForConvexHull->index, 1); + vertexIndex[2] = convexHullAddVertex(hull, &q.pt[2], ballForConvexHull->index, 2); + vertexIndex[3] = convexHullAddVertex(hull, &q.pt[3], ballForConvexHull->index, 3); + + if (*outmostBall) { + if (ballForConvexHull->radius > (*outmostBall)->radius) { + needUpdateOutmost = 1; + } + } else { + needUpdateOutmost = 1; + } + if (needUpdateOutmost) { + *outmostBall = ballForConvexHull; + *outmostBallFirstVertexIndex = vertexIndex[0]; + } } static int bmeshStichFrom(bmesh *bm, bmeshBall *parent, bmeshBall *ball) { @@ -548,12 +565,22 @@ static int bmeshStichFrom(bmesh *bm, bmeshBall *parent, bmeshBall *ball) { return 0; } ball->roundColor = bm->roundColor; - if (BMESH_BALL_TYPE_ROOT == ball->type) { - convexHull *hull = convexHullCreate(); + if (BMESH_BALL_TYPE_ROOT == ball->type && 4 == ball->index) { + tri2QuadContext *t2q; + convexHull *hull; + bmeshBall *outmostBall = 0; + int outmostBallFirstVertexIndex = 0; + hull = convexHullCreate(); if (!hull) { fprintf(stderr, "%s:convexHullCreate failed.\n", __FUNCTION__); return -1; } + t2q = tri2QuadContextCreate(); + if (!t2q) { + fprintf(stderr, "%s:tri2QuadContextCreate failed.\n", __FUNCTION__); + convexHullDestroy(hull); + return -1; + } glColor3f(0.0, 0.0, 0.0); drawDebugPrintf("root <%f,%f,%f>", ball->position.x, ball->position.y, ball->position.z); @@ -561,19 +588,40 @@ static int bmeshStichFrom(bmesh *bm, bmeshBall *parent, bmeshBall *ball) { child; child = bmeshGetBallNextChild(bm, ball, &iterator)) { ballForConvexHull = bmeshFindBallForConvexHull(bm, ball, child); - addBallToHull(hull, ballForConvexHull); + addBallToHull(hull, ballForConvexHull, + &outmostBall, &outmostBallFirstVertexIndex); } if (parent) { - addBallToHull(hull, parent); + addBallToHull(hull, parent, &outmostBall, &outmostBallFirstVertexIndex); + } + if (outmostBall) { + convexHullAddTodo(hull, outmostBallFirstVertexIndex + 0, + outmostBallFirstVertexIndex + 1, outmostBallFirstVertexIndex + 2); } convexHullGenerate(hull); - glPushMatrix(); + convexHullUnifyNormals(hull, &ball->position); { int triIndex; - glColor3f(1.0f, 1.0f, 1.0f); for (triIndex = 0; triIndex < convexHullGetTriangleNum(hull); ++triIndex) { triangle *tri = (triangle *)convexHullGetTriangle(hull, triIndex); + tri2QuadAddTriangle(t2q, tri); + } + } + tri2QuadConvert(t2q); + + glPushMatrix(); + + /* + glColor3f(1.0f, 1.0f, 1.0f); + { + int triIndex; + for (triIndex = 0; triIndex < convexHullGetTriangleNum(hull); + ++triIndex) { + triangle *tri = (triangle *)convexHullGetTriangle(hull, triIndex); + //if (triIndex > displayTriangleFaceIndex) { + // continue; + //} drawTriangle(tri); } } @@ -593,8 +641,68 @@ static int bmeshStichFrom(bmesh *bm, bmeshBall *parent, bmeshBall *ball) { glEnd(); } } + */ + glColor3f(1.0f, 1.0f, 1.0f); + { + int triIndex; + for (triIndex = 0; triIndex < tri2QuadGetTriangleNum(t2q); + ++triIndex) { + triangle *tri = (triangle *)tri2QuadGetTriangle(t2q, triIndex); + drawTriangle(tri); + } + } + glColor3f(0.0f, 0.0f, 0.0f); + { + int triIndex; + int j; + for (triIndex = 0; triIndex < tri2QuadGetTriangleNum(t2q); + ++triIndex) { + triangle *tri = (triangle *)tri2QuadGetTriangle(t2q, triIndex); + glBegin(GL_LINE_STRIP); + for (j = 0; j < 3; ++j) { + glVertex3f(tri->pt[j].x, tri->pt[j].y, tri->pt[j].z); + } + glVertex3f(tri->pt[0].x, tri->pt[0].y, tri->pt[0].z); + glEnd(); + } + } + + glColor3f(1.0f, 1.0f, 1.0f); + { + int quadIndex; + glBegin(GL_QUADS); + for (quadIndex = 0; quadIndex < tri2QuadGetQuadNum(t2q); + ++quadIndex) { + quad *q = (quad *)tri2QuadGetQuad(t2q, quadIndex); + vec3 normal; + int j; + vec3Normal(&q->pt[0], &q->pt[1], &q->pt[2], &normal); + for (j = 0; j < 4; ++j) { + glNormal3f(normal.x, normal.y, normal.z); + glVertex3f(q->pt[j].x, q->pt[j].y, q->pt[j].z); + } + } + glEnd(); + } + glColor3f(0.0f, 0.0f, 0.0f); + { + int quadIndex; + int j; + for (quadIndex = 0; quadIndex < tri2QuadGetQuadNum(t2q); + ++quadIndex) { + quad *q = (quad *)tri2QuadGetQuad(t2q, quadIndex); + glBegin(GL_LINE_STRIP); + for (j = 0; j < 4; ++j) { + glVertex3f(q->pt[j].x, q->pt[j].y, q->pt[j].z); + } + glVertex3f(q->pt[0].x, q->pt[0].y, q->pt[0].z); + glEnd(); + } + } + glPopMatrix(); convexHullDestroy(hull); + tri2QuadContextDestroy(t2q); } for (child = bmeshGetBallFirstChild(bm, ball, &iterator); child; diff --git a/src/convexhull.c b/src/convexhull.c index e6dcc1ad..a0156143 100644 --- a/src/convexhull.c +++ b/src/convexhull.c @@ -3,14 +3,14 @@ #include #include "convexhull.h" #include "array.h" +#include "hashtable.h" #include "draw.h" // // Implement Gift wrapping method which describled in http://dccg.upc.edu/people/vera/wp-content/uploads/2014/11/GA2014-ConvexHulls3D-Roger-Hernando.pdf // -// -// Translate from Danielhst's lua version https://github.com/danielhst/3d-Hull-gift-wrap/blob/master/giftWrap.lua -// + +#define TRIANGLE_INDEX_HASHTABLE_SIZE 100 typedef struct { vec3 pt; @@ -18,18 +18,50 @@ typedef struct { int orderOnPlane; } converHullVertex; +typedef struct face { + int indices[3]; +} face; + struct convexHull { array *vertexArray; - array *openEdgeArray; - array *triangleArray; - int nextEdgeIndex; + array *todoArray; + array *faceArray; + int nextTodoIndex; unsigned int *openEdgeExistMap; + hashtable *faceHashtable; + face findFace; + triangle returnTriangle; }; typedef struct { int firstVertex; int secondVertex; -} edge; + int thirdVertex; +} todo; + +face *convexHullGetFaceByHashtableParam(convexHull *hull, + const void *param) { + int index = (char *)param - (char *)0; + if (0 == index) { + return &hull->findFace; + } + return (face *)arrayGetItem(hull->faceArray, index - 1); +} + +static int faceHash(void *userData, const void *node) { + face *triIdx = convexHullGetFaceByHashtableParam( + (convexHull *)userData, node); + return triIdx->indices[0] * triIdx->indices[1] * triIdx->indices[2]; +} + +static int faceCompare(void *userData, const void *node1, + const void *node2) { + face *triIdx1 = convexHullGetFaceByHashtableParam( + (convexHull *)userData, node1); + face *triIdx2 = convexHullGetFaceByHashtableParam( + (convexHull *)userData, node2); + return memcmp(triIdx1, triIdx2, sizeof(face)); +} convexHull *convexHullCreate(void) { convexHull *hull = (convexHull *)calloc(1, sizeof(convexHull)); @@ -40,16 +72,26 @@ convexHull *convexHullCreate(void) { hull->vertexArray = arrayCreate(sizeof(converHullVertex)); if (!hull->vertexArray) { fprintf(stderr, "%s:arrayCreate failed.\n", __FUNCTION__); + convexHullDestroy(hull); return 0; } - hull->openEdgeArray = arrayCreate(sizeof(edge)); - if (!hull->openEdgeArray) { + hull->todoArray = arrayCreate(sizeof(todo)); + if (!hull->todoArray) { fprintf(stderr, "%s:arrayCreate failed.\n", __FUNCTION__); + convexHullDestroy(hull); return 0; } - hull->triangleArray = arrayCreate(sizeof(triangle)); - if (!hull->triangleArray) { + hull->faceArray = arrayCreate(sizeof(face)); + if (!hull->faceArray) { fprintf(stderr, "%s:arrayCreate failed.\n", __FUNCTION__); + convexHullDestroy(hull); + return 0; + } + hull->faceHashtable = hashtableCreate(TRIANGLE_INDEX_HASHTABLE_SIZE, + faceHash, faceCompare, hull); + if (!hull->faceHashtable) { + fprintf(stderr, "%s:hashtableCreate failed.\n", __FUNCTION__); + convexHullDestroy(hull); return 0; } return hull; @@ -65,10 +107,13 @@ void convexHullMarkEdgeAsExsits(convexHull *hull, int firstVertex, int convexHullEdgeExsits(convexHull *hull, int firstVertex, int secondVertex) { - int mapIndex = firstVertex * arrayGetLength(hull->vertexArray) + secondVertex; - int unitIndex = mapIndex / (sizeof(unsigned int) * 8); - int unitOffset = mapIndex % (sizeof(unsigned int) * 8); - return hull->openEdgeExistMap[unitIndex] & (0x00000001 << unitOffset); + if (hull->openEdgeExistMap) { + int mapIndex = firstVertex * arrayGetLength(hull->vertexArray) + secondVertex; + int unitIndex = mapIndex / (sizeof(unsigned int) * 8); + int unitOffset = mapIndex % (sizeof(unsigned int) * 8); + return hull->openEdgeExistMap[unitIndex] & (0x00000001 << unitOffset); + } + return 0; } int convexHullAddVertex(convexHull *hull, vec3 *vertex, int plane, @@ -86,31 +131,24 @@ int convexHullAddVertex(convexHull *hull, vec3 *vertex, int plane, return newVertex; } -int convexHullAddOpenEdgeNoCheck(convexHull *hull, int firstVertex, - int secondVertex) { - edge *e; - int newEdge = arrayGetLength(hull->openEdgeArray); +int convexHullAddTodoNoCheck(convexHull *hull, int firstVertex, + int secondVertex, int thirdVertex) { + todo *t; + int newEdge = arrayGetLength(hull->todoArray); if (firstVertex < 0 || secondVertex < 0) { fprintf(stderr, "%s:Invalid params(firstVertex:%d secondVertex:%d).\n", __FUNCTION__, firstVertex, secondVertex); return -1; } - if (0 != arraySetLength(hull->openEdgeArray, newEdge + 1)) { + if (0 != arraySetLength(hull->todoArray, newEdge + 1)) { fprintf(stderr, "%s:arraySetLength failed.\n", __FUNCTION__); return -1; } - e = (edge *)arrayGetItem(hull->openEdgeArray, newEdge); - memset(e, 0, sizeof(edge)); - e->firstVertex = firstVertex; - e->secondVertex = secondVertex; - return 0; -} - -int convexHullAddEdge(convexHull *hull, int p1, int p2) { - convexHullMarkEdgeAsExsits(hull, p1, p2); - if (!convexHullEdgeExsits(hull, p2, p1)) { - return convexHullAddOpenEdgeNoCheck(hull, p2, p1); - } + t = (todo *)arrayGetItem(hull->todoArray, newEdge); + memset(t, 0, sizeof(todo)); + t->firstVertex = firstVertex; + t->secondVertex = secondVertex; + t->thirdVertex = thirdVertex; return 0; } @@ -119,9 +157,15 @@ static int isInAdjacentOrder(int order1, int order2) { (order2 + 1) % 4 == order1); } -int convexHullAddTriangle(convexHull *hull, int firstVertex, int secondVertex, +static int sortface(const void *first, const void *second) { + const int *firstIndex = (const void *)first; + const int *secondIndex = (const void *)second; + return *firstIndex - *secondIndex; +} + +int convexHullAddFace(convexHull *hull, int firstVertex, int secondVertex, int thirdVertex) { - triangle *tri; + face *tri; converHullVertex *vtx1; converHullVertex *vtx2; converHullVertex *vtx3; @@ -147,16 +191,26 @@ int convexHullAddTriangle(convexHull *hull, int firstVertex, int secondVertex, return 0; } } - newTri = arrayGetLength(hull->triangleArray); - if (0 != arraySetLength(hull->triangleArray, newTri + 1)) { - fprintf(stderr, "%s:arraySetLength failed.\n", __FUNCTION__); - return -1; + memset(&hull->findFace, 0, sizeof(hull->findFace)); + hull->findFace.indices[0] = firstVertex; + hull->findFace.indices[1] = secondVertex; + hull->findFace.indices[2] = thirdVertex; + if (0 == hashtableGet(hull->faceHashtable, 0)) { + qsort(hull->findFace.indices, 3, + sizeof(hull->findFace.indices[0]), sortface); + newTri = arrayGetLength(hull->faceArray); + if (0 != arraySetLength(hull->faceArray, newTri + 1)) { + fprintf(stderr, "%s:arraySetLength failed.\n", __FUNCTION__); + return -1; + } + tri = (face *)arrayGetItem(hull->faceArray, newTri); + *tri = hull->findFace; + if (0 != hashtableInsert(hull->faceHashtable, + (char *)0 + newTri + 1)) { + fprintf(stderr, "%s:hashtableInsert failed.\n", __FUNCTION__); + return -1; + } } - tri = (triangle *)arrayGetItem(hull->triangleArray, newTri); - memset(tri, 0, sizeof(triangle)); - tri->pt[0] = vtx1->pt; - tri->pt[1] = vtx2->pt; - tri->pt[2] = vtx3->pt; return 0; } @@ -167,8 +221,9 @@ static void convexHullReleaseForGenerate(convexHull *hull) { void convexHullDestroy(convexHull *hull) { arrayDestroy(hull->vertexArray); - arrayDestroy(hull->openEdgeArray); - arrayDestroy(hull->triangleArray); + arrayDestroy(hull->todoArray); + arrayDestroy(hull->faceArray); + hashtableDestroy(hull->faceHashtable); convexHullReleaseForGenerate(hull); free(hull); } @@ -183,60 +238,45 @@ static int convexHullPrepareForGenerate(convexHull *hull) { fprintf(stderr, "%s:Insufficient memory.\n", __FUNCTION__); return -1; } - hull->nextEdgeIndex = 0; + hull->nextTodoIndex = 0; return 0; } -int convexHullGetLower(convexHull *hull) { - int index = 0; +int convexHullGetNextVertex(convexHull *hull, int p1Index, int p2Index, + int p3Index) { + vec3 *p1 = (vec3 *)arrayGetItem(hull->vertexArray, p1Index); + vec3 *p2 = (vec3 *)arrayGetItem(hull->vertexArray, p2Index); + vec3 *p3 = (vec3 *)arrayGetItem(hull->vertexArray, p3Index); + vec3 beginNormal; + vec3 endNormal; int i; - for (i = 1; i < arrayGetLength(hull->vertexArray); ++i) { - vec3 *pI = (vec3 *)arrayGetItem(hull->vertexArray, i); - vec3 *pIndex = (vec3 *)arrayGetItem(hull->vertexArray, index); - if (pI->z < pIndex->z) { - index = i; - } else if (pI->z == pIndex->z) { - if (pI->y < pIndex->y) { - index = i; - } else if (pI->x < pIndex->x) { - index = i; + float angle; + float maxAngle = 0; + int candidateIndex = -1; + + vec3Normal(p1, p2, p3, &beginNormal); + + for (i = 0; i < arrayGetLength(hull->vertexArray); ++i) { + if (i != p1Index && i != p2Index && i != p3Index) { + vec3Normal(p1, p2, (vec3 *)arrayGetItem(hull->vertexArray, i), + &endNormal); + angle = vec3Angle(&beginNormal, &endNormal); + if (angle > maxAngle) { + candidateIndex = i; + maxAngle = angle; } } } - return index; + + return candidateIndex; } -int convexHullGetNextVertex(convexHull *hull, int p1Index, int p2Index) { - vec3 pCorner = {1, 1, 0}; - vec3 *p1 = (vec3 *)arrayGetItem(hull->vertexArray, p1Index); - vec3 *p2 = p2Index < 0 ? &pCorner : (vec3 *)arrayGetItem(hull->vertexArray, - p2Index); - vec3 edge; - int candidateIndex = -1; - int i; - vec3Sub(p2, p1, &edge); - vec3Normalize(&edge); - for (i = 0; i < arrayGetLength(hull->vertexArray); ++i) { - if (i != p1Index && i != p2Index) { - if (-1 == candidateIndex) { - candidateIndex = 0; - } else { - vec3 v, proj, candidate, canProj, cross; - vec3Sub((vec3 *)arrayGetItem(hull->vertexArray, i), p1, &v); - vec3ProjectOver(&v, &edge, &proj); - vec3Sub(&v, &proj, &v); - vec3Sub((vec3 *)arrayGetItem(hull->vertexArray, - candidateIndex), p1, &candidate); - vec3ProjectOver(&candidate, &edge, &canProj); - vec3Sub(&candidate, &canProj, &candidate); - vec3CrossProduct(&candidate, &v, &cross); - if (vec3DotProduct(&cross, &edge) > 0) { - candidateIndex = i; - } - } - } +int convexHullAddTodo(convexHull *hull, int vertex1, int vertex2, + int vertex3) { + if (!convexHullEdgeExsits(hull, vertex1, vertex2)) { + return convexHullAddTodoNoCheck(hull, vertex1, vertex2, vertex3); } - return candidateIndex; + return 0; } int convexHullGenerate(convexHull *hull) { @@ -246,34 +286,63 @@ int convexHullGenerate(convexHull *hull) { fprintf(stderr, "%s:convexHullPrepareForGenerate failed.\n", __FUNCTION__); return -1; } - index1 = convexHullGetLower(hull); - index2 = convexHullGetNextVertex(hull, index1, -1); - convexHullAddEdge(hull, index2, index1); - //glColor3f(0.0, 0.0, 0.0); - //drawDebugPrintf("edgeLength:%d", arrayGetLength(hull->openEdgeArray)); - while (hull->nextEdgeIndex < arrayGetLength(hull->openEdgeArray)) { - edge *e = (edge *)arrayGetItem(hull->openEdgeArray, hull->nextEdgeIndex++); - index1 = e->firstVertex; - index2 = e->secondVertex; + while (hull->nextTodoIndex < arrayGetLength(hull->todoArray)) { + todo *t = (todo *)arrayGetItem(hull->todoArray, hull->nextTodoIndex++); + index1 = t->firstVertex; + index2 = t->secondVertex; if (convexHullEdgeExsits(hull, index1, index2)) { continue; } convexHullMarkEdgeAsExsits(hull, index1, index2); - index3 = convexHullGetNextVertex(hull, index1, index2); - //drawDebugPrintf("%d,%d,%d", index1, index2, index3); - convexHullAddTriangle(hull, index1, index2, index3); - convexHullAddEdge(hull, index1, index2); - convexHullAddEdge(hull, index2, index3); - convexHullAddEdge(hull, index3, index1); + index3 = convexHullGetNextVertex(hull, index1, index2, t->thirdVertex); + if (-1 == index3) { + continue; + } + convexHullAddFace(hull, index1, index2, index3); + convexHullAddTodo(hull, index1, index2, index3); + convexHullAddTodo(hull, index2, index3, index1); + convexHullAddTodo(hull, index3, index1, index2); + } + return 0; +} + +int convexHullUnifyNormals(convexHull *hull, vec3 *origin) { + int i; + for (i = 0; i < arrayGetLength(hull->faceArray); ++i) { + face *triIdx = (face *)arrayGetItem( + hull->faceArray, i); + converHullVertex *p1 = (converHullVertex *)arrayGetItem( + hull->vertexArray, triIdx->indices[0]); + converHullVertex *p2 = (converHullVertex *)arrayGetItem( + hull->vertexArray, triIdx->indices[1]); + converHullVertex *p3 = (converHullVertex *)arrayGetItem( + hull->vertexArray, triIdx->indices[2]); + vec3 normal; + vec3 o2v; + vec3Normal(&p1->pt, &p2->pt, &p3->pt, &normal); + vec3Sub(&p1->pt, origin, &o2v); + if (vec3DotProduct(&o2v, &normal) < 0) { + int index = triIdx->indices[0]; + triIdx->indices[0] = triIdx->indices[2]; + triIdx->indices[2] = index; + } } return 0; } int convexHullGetTriangleNum(convexHull *hull) { - return arrayGetLength(hull->triangleArray); + return arrayGetLength(hull->faceArray); } triangle *convexHullGetTriangle(convexHull *hull, int index) { - triangle *tri = (triangle *)arrayGetItem(hull->triangleArray, index); - return tri; + int i; + face *triIdx = (face *)arrayGetItem( + hull->faceArray, index); + memset(&hull->returnTriangle, 0, sizeof(hull->returnTriangle)); + for (i = 0; i < 3; ++i) { + converHullVertex *vertex = (converHullVertex *)arrayGetItem( + hull->vertexArray, triIdx->indices[i]); + hull->returnTriangle.pt[i] = vertex->pt; + } + return &hull->returnTriangle; } diff --git a/src/convexhull.h b/src/convexhull.h index 84285270..067d83dc 100644 --- a/src/convexhull.h +++ b/src/convexhull.h @@ -9,7 +9,9 @@ int convexHullAddVertex(convexHull *hull, vec3 *vertex, int plane, int orderOnPlane); void convexHullDestroy(convexHull *hull); int convexHullGenerate(convexHull *hull); +int convexHullUnifyNormals(convexHull *hull, vec3 *origin); int convexHullGetTriangleNum(convexHull *hull); triangle *convexHullGetTriangle(convexHull *hull, int index); +int convexHullAddTodo(convexHull *hull, int vertex1, int vertex2, int vertex3); #endif diff --git a/src/hashtable.c b/src/hashtable.c new file mode 100644 index 00000000..d6ad7c45 --- /dev/null +++ b/src/hashtable.c @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include "hashtable.h" +#include "array.h" + +typedef struct hashtableEntry { + const void *node; + int nextEntryIndex; +} hashtableEntry; + +typedef struct hashtableKey { + int entryNum; + int firstEntryIndex; +} hashtableKey; + +struct hashtable { + array *keyArray; + array *entryArray; + int bucketSize; + int (*hashCallback)(void *userData, const void *node); + int (*compareCallback)(void *userData, const void *node1, const void *node2); + void *userData; +}; + +hashtable *hashtableCreate(int bucketSize, + int (*hashCallback)(void *userData, const void *node), + int (*compareCallback)(void *userData, const void *node1, + const void *node2), + void *userData) { + hashtable *ht = (hashtable *)calloc(1, sizeof(hashtable)); + if (!ht) { + fprintf(stderr, "%s:Insufficient memory.", __FUNCTION__); + return 0; + } + ht->keyArray = arrayCreate(sizeof(hashtableKey)); + if (!ht->keyArray) { + fprintf(stderr, "%s:arrayCreate failed.", __FUNCTION__); + hashtableDestroy(ht); + return 0; + } + ht->entryArray = arrayCreate(sizeof(hashtableEntry)); + if (!ht->entryArray) { + fprintf(stderr, "%s:arrayCreate failed.", __FUNCTION__); + hashtableDestroy(ht); + return 0; + } + ht->bucketSize = bucketSize; + ht->hashCallback = hashCallback; + ht->compareCallback = compareCallback; + ht->userData = userData; + if (0 != arraySetLength(ht->keyArray, bucketSize)) { + fprintf(stderr, "%s:arraySetLength failed(bucketSize:%d).", __FUNCTION__, + bucketSize); + hashtableDestroy(ht); + return 0; + } + return ht; +} + +void hashtableDestroy(hashtable *ht) { + if (ht) { + arrayDestroy(ht->keyArray); + arrayDestroy(ht->entryArray); + free(ht); + } +} + +static int hashtableGetNodeHash(hashtable *ht, const void *node) { + return (int)((unsigned int)ht->hashCallback(ht->userData, + node) % ht->bucketSize); +} + +static hashtableEntry *findEntry(hashtable *ht, hashtableKey *key, + const void *node) { + int i; + int nextEntryIndex = key->firstEntryIndex; + for (i = 0; i < key->entryNum; ++i) { + hashtableEntry *entry; + assert(-1 != nextEntryIndex); + entry = (hashtableEntry *)arrayGetItem(ht->entryArray, nextEntryIndex); + if (0 == ht->compareCallback(ht->userData, entry->node, node)) { + return (void *)entry->node; + } + nextEntryIndex = entry->nextEntryIndex; + } + return 0; +} + +int hashtableInsert(hashtable *ht, const void *node) { + int newEntryIndex; + int hash = hashtableGetNodeHash(ht, node); + hashtableKey *key = (hashtableKey *)arrayGetItem(ht->keyArray, hash); + hashtableEntry *entry = findEntry(ht, key, node); + if (entry) { + return -1; + } + newEntryIndex = arrayGetLength(ht->entryArray); + if (0 != arraySetLength(ht->entryArray, newEntryIndex + 1)) { + fprintf(stderr, "%s:arraySetLength failed(newEntryIndex:%d).", + __FUNCTION__, newEntryIndex); + return -1; + } + entry = (hashtableEntry *)arrayGetItem(ht->entryArray, newEntryIndex); + entry->node = node; + entry->nextEntryIndex = 0 == key->entryNum ? -1 : key->firstEntryIndex; + key->firstEntryIndex = newEntryIndex; + key->entryNum++; + return 0; +} + +void *hashtableGet(hashtable *ht, const void *node) { + int hash = hashtableGetNodeHash(ht, node); + hashtableKey *key = (hashtableKey *)arrayGetItem(ht->keyArray, hash); + return findEntry(ht, key, node); +} diff --git a/src/hashtable.h b/src/hashtable.h new file mode 100644 index 00000000..8915162d --- /dev/null +++ b/src/hashtable.h @@ -0,0 +1,15 @@ +#ifndef HASHTABLE_H +#define HASHTABLE_H + +typedef struct hashtable hashtable; + +hashtable *hashtableCreate(int bucketSize, + int (*hashCallback)(void *userData, const void *node), + int (*compareCallback)(void *userData, const void *node1, const void *node2), + void *userData); +void hashtableDestroy(hashtable *ht); +int hashtableInsert(hashtable *ht, const void *node); +void *hashtableGet(hashtable *ht, const void *node); + +#endif + diff --git a/src/render.cpp b/src/render.cpp index 2bd765be..3b17f992 100644 --- a/src/render.cpp +++ b/src/render.cpp @@ -8,10 +8,10 @@ #include "matrix.h" #include "vector3d.h" -static const float bmeshBallColors[][3] { - {0, 0.78, 1}, - {1, 0, 0}, - {1, 1, 1} +static const float bmeshBallColors[][4] { + {0.00, 0.78, 1.00, 0.5}, + {1.00, 0.00, 0.00, 0.5}, + {1.00, 1.00, 1.00, 0.5} }; static const float bmeshBoneColor[3] = {1, 1, 0}; @@ -27,11 +27,14 @@ int drawDebugPrintf(const char *fmt, ...) { va_start(args, fmt); vsnprintf(text, sizeof(text), fmt, args); debugOutputTop += 9; + if (debugOutputTop > 200) { + debugOutputTop = 0; + } return drawText(x, y, text); } static int drawBmeshBall(bmesh *bm, bmeshBall *ball) { - glColor3fv(bmeshBallColors[ball->type]); + glColor4fv(bmeshBallColors[ball->type]); drawSphere(&ball->position, ball->radius, 36, 24); return 0; } @@ -129,7 +132,7 @@ static void drawBmeshBallQuadRecursively(bmesh *bm, bmeshBall *ball) { glPopMatrix(); */ - drawBmeshBallQuad(ball); + //drawBmeshBallQuad(ball); for (child = bmeshGetBallFirstChild(bm, ball, &iterator); child; @@ -277,7 +280,7 @@ void Render::paintGL() { for (index = 0; index < bmeshGetBoneNum(bm); ++index) { bmeshBone *bone = bmeshGetBone(bm, index); - drawBmeshBone(bm, bone); + //drawBmeshBone(bm, bone); } /* glColor4f(1.0f, 1.0f, 1.0f, 0.5); diff --git a/src/tri2quad.c b/src/tri2quad.c new file mode 100644 index 00000000..e01c5bde --- /dev/null +++ b/src/tri2quad.c @@ -0,0 +1,324 @@ +#include +#include +#include +#include +#include "tri2quad.h" +#include "vector3d.h" +#include "array.h" +#include "hashtable.h" + +#define EDGE_HASHTABLE_SIZE 100 + +typedef struct edge { + vec3 p1; + vec3 p2; + int inputA; + int inputB; + int score; +} edge; + +typedef struct inputTriangle { + triangle tri; + float area; + vec3 normal; + int merged; +} inputTriangle; + +struct tri2QuadContext { + array *inputArray; + array *triangleArray; + array *quadArray; + array *edgeArray; + hashtable *edgeHashtable; + edge findEdge; +}; + +static edge *tri2QuadGetEdgeByHashtableParam(tri2QuadContext *ctx, + const void *param) { + int edgeIndex = (char *)param - (char *)0; + if (0 == edgeIndex) { + return &ctx->findEdge; + } + return (edge *)arrayGetItem(ctx->edgeArray, edgeIndex - 1); +} + +static int edgeHash(void *userData, const void *node) { + edge *e = tri2QuadGetEdgeByHashtableParam((tri2QuadContext *)userData, node); + return (int)(e->p1.x + 1) * (e->p1.y + 1) * (e->p1.z + 1) * + (e->p2.x + 1) * (e->p2.y + 1) * (e->p2.z + 1); +} + +static int edgeCompare(void *userData, const void *node1, const void *node2) { + const edge *e1 = (const edge *)tri2QuadGetEdgeByHashtableParam( + (tri2QuadContext *)userData, node1); + const edge *e2 = (const edge *)tri2QuadGetEdgeByHashtableParam( + (tri2QuadContext *)userData, node2); + if ((0 == memcmp(&e1->p1, &e2->p1, sizeof(e1->p1)) && + 0 == memcmp(&e1->p2, &e2->p2, sizeof(e1->p1))) || + (0 == memcmp(&e1->p1, &e2->p2, sizeof(e1->p1)) && + 0 == memcmp(&e1->p2, &e2->p1, sizeof(e1->p1)))) { + return 0; + } + return -1; +} + +tri2QuadContext *tri2QuadContextCreate(void) { + tri2QuadContext *ctx = (tri2QuadContext *)calloc(1, sizeof(tri2QuadContext)); + if (!ctx) { + fprintf(stderr, "%s:Insufficient memory.", __FUNCTION__); + return 0; + } + ctx->inputArray = arrayCreate(sizeof(inputTriangle)); + if (!ctx->inputArray) { + fprintf(stderr, "%s:arrayCreate failed.", __FUNCTION__); + tri2QuadContextDestroy(ctx); + return 0; + } + ctx->triangleArray = arrayCreate(sizeof(triangle)); + if (!ctx->triangleArray) { + fprintf(stderr, "%s:arrayCreate failed.", __FUNCTION__); + tri2QuadContextDestroy(ctx); + return 0; + } + ctx->quadArray = arrayCreate(sizeof(quad)); + if (!ctx->quadArray) { + fprintf(stderr, "%s:arrayCreate failed.", __FUNCTION__); + tri2QuadContextDestroy(ctx); + return 0; + } + ctx->edgeHashtable = hashtableCreate(EDGE_HASHTABLE_SIZE, edgeHash, + edgeCompare, ctx); + if (!ctx->edgeHashtable) { + fprintf(stderr, "%s:hashtableCreate failed.", __FUNCTION__); + tri2QuadContextDestroy(ctx); + return 0; + } + ctx->edgeArray = arrayCreate(sizeof(edge)); + if (!ctx->edgeArray) { + fprintf(stderr, "%s:arrayCreate failed.", __FUNCTION__); + tri2QuadContextDestroy(ctx); + return 0; + } + return ctx; +} + +int tri2QuadAddTriangle(tri2QuadContext *ctx, triangle *tri) { + int newInputIndex; + inputTriangle *newInput; + newInputIndex = arrayGetLength(ctx->inputArray); + if (0 != arraySetLength(ctx->inputArray, newInputIndex + 1)) { + fprintf(stderr, "%s:arraySetLength failed.", __FUNCTION__); + return -1; + } + newInput = (inputTriangle *)arrayGetItem(ctx->inputArray, newInputIndex); + newInput->merged = 0; + newInput->tri = *tri; + newInput->area = vec3TriangleArea(&tri->pt[0], &tri->pt[1], &tri->pt[2]); + vec3Normal(&tri->pt[0], &tri->pt[1], &tri->pt[2], &newInput->normal); + return newInputIndex; +} + +static edge *tri2QuadFindEdge(tri2QuadContext *ctx, + vec3 *p1, vec3 *p2) { + int edgeIndex; + ctx->findEdge.p1 = *p1; + ctx->findEdge.p2 = *p2; + edgeIndex = (char *)hashtableGet(ctx->edgeHashtable, (char *)0) - (char *)0; + if (0 == edgeIndex) { + return 0; + } + return (edge *)arrayGetItem(ctx->edgeArray, edgeIndex - 1); +} + +static int tri2QuadAddInitialEdge(tri2QuadContext *ctx, + vec3 *p1, vec3 *p2, int inputIndex) { + edge *existedEdge = tri2QuadFindEdge(ctx, p1, p2); + if (!existedEdge) { + edge *newEdge; + int newEdgeIndex = arrayGetLength(ctx->edgeArray); + if (0 != arraySetLength(ctx->edgeArray, newEdgeIndex + 1)) { + fprintf(stderr, "%s:arraySetLength failed(newEdgeIndex:%d).", + __FUNCTION__, newEdgeIndex); + return -1; + } + newEdge = (edge *)arrayGetItem(ctx->edgeArray, newEdgeIndex); + newEdge->p1 = *p1; + newEdge->p2 = *p2; + if (0 != hashtableInsert(ctx->edgeHashtable, + (char *)0 + newEdgeIndex + 1)) { + fprintf(stderr, "%s:hashtableInsert failed.", __FUNCTION__); + return -1; + } + newEdge->inputA = inputIndex; + newEdge->inputB = -1; + newEdge->score = 0; + } else { + existedEdge->inputB = inputIndex; + } + return 0; +} + +static int tri2QuadAddOutputQuad(tri2QuadContext *ctx, vec3 *first, + vec3 *second, vec3 *third, vec3 *fourth) { + quad *q; + int newQuadIndex = arrayGetLength(ctx->quadArray); + if (0 != arraySetLength(ctx->quadArray, newQuadIndex + 1)) { + fprintf(stderr, "%s:arraySetLength failed(newQuadIndex:%d).", __FUNCTION__, + newQuadIndex); + return -1; + } + q = (quad *)arrayGetItem(ctx->quadArray, newQuadIndex); + memset(q, 0, sizeof(quad)); + q->pt[0] = *first; + q->pt[1] = *second; + q->pt[2] = *third; + q->pt[3] = *fourth; + return newQuadIndex; +} + +static int tri2QuadAddOutputTriangle(tri2QuadContext *ctx, vec3 *first, + vec3 *second, vec3 *third) { + triangle *tri; + int newTriangleIndex = arrayGetLength(ctx->triangleArray); + if (0 != arraySetLength(ctx->triangleArray, newTriangleIndex + 1)) { + fprintf(stderr, "%s:arraySetLength failed(newTriangleIndex:%d).", + __FUNCTION__, newTriangleIndex); + return -1; + } + tri = (triangle *)arrayGetItem(ctx->triangleArray, newTriangleIndex); + memset(tri, 0, sizeof(triangle)); + tri->pt[0] = *first; + tri->pt[1] = *second; + tri->pt[2] = *third; + return newTriangleIndex; +} + +static int tri2QuadAddOutputQuadByEdgeAndInput(tri2QuadContext *ctx, edge *e, + inputTriangle *input1, inputTriangle *input2) { + vec3 outputPt[4]; + int i; + int nextSaveToIndex; + memset(outputPt, 0, sizeof(outputPt)); + nextSaveToIndex = 0; + for (i = 0; i < 3; ++i) { + vec3 *pt = &input1->tri.pt[i]; + if (0 == memcmp(&e->p1, pt, sizeof(e->p1))) { + outputPt[nextSaveToIndex] = *pt; + nextSaveToIndex += 2; + } else if (0 == memcmp(&e->p2, pt, sizeof(e->p2))) { + outputPt[nextSaveToIndex] = *pt; + nextSaveToIndex += 2; + } else { + outputPt[1] = *pt; + } + } + for (i = 0; i < 3; ++i) { + vec3 *pt = &input2->tri.pt[i]; + if (0 != memcmp(&e->p1, pt, sizeof(e->p1)) && + 0 != memcmp(&e->p2, pt, sizeof(e->p2))) { + outputPt[3] = *pt; + break; + } + } + return tri2QuadAddOutputQuad(ctx, &outputPt[0], &outputPt[1], + &outputPt[2], &outputPt[3]); +} + +static int sortEdgeByScoreDesc(const void *first, const void *second) { + const edge *e1 = (const edge *)first; + const edge *e2 = (const edge *)second; + return e2->score - e1->score; +} + +int tri2QuadConvert(tri2QuadContext *ctx) { + int inputIndex; + int edgeIndex; + + for (inputIndex = 0; inputIndex < arrayGetLength(ctx->inputArray); + ++inputIndex) { + inputTriangle *inputItem = (inputTriangle *)arrayGetItem(ctx->inputArray, + inputIndex); + tri2QuadAddInitialEdge(ctx, &inputItem->tri.pt[0], &inputItem->tri.pt[1], + inputIndex); + tri2QuadAddInitialEdge(ctx, &inputItem->tri.pt[1], &inputItem->tri.pt[2], + inputIndex); + tri2QuadAddInitialEdge(ctx, &inputItem->tri.pt[2], &inputItem->tri.pt[0], + inputIndex); + } + for (edgeIndex = 0; edgeIndex < arrayGetLength(ctx->edgeArray); ++edgeIndex) { + edge *e = (edge *)arrayGetItem(ctx->edgeArray, edgeIndex); + inputTriangle *input1; + inputTriangle *input2; + if (-1 != e->inputA && -1 != e->inputB) { + input1 = (inputTriangle *)arrayGetItem(ctx->inputArray, + e->inputA); + input2 = (inputTriangle *)arrayGetItem(ctx->inputArray, + e->inputB); + e->score = (int)((input1->area + input2->area) * + vec3DotProduct(&input1->normal, &input2->normal) * 100); + } + } + qsort(arrayGetItem(ctx->edgeArray, 0), arrayGetLength(ctx->edgeArray), + sizeof(edge), sortEdgeByScoreDesc); + + // + // After qsort, the edge indices inside the hashtable are no longer right. + // + + hashtableDestroy(ctx->edgeHashtable); + ctx->edgeHashtable = 0; + + for (edgeIndex = 0; edgeIndex < arrayGetLength(ctx->edgeArray); ++edgeIndex) { + edge *e = (edge *)arrayGetItem(ctx->edgeArray, edgeIndex); + inputTriangle *input1; + inputTriangle *input2; + if (-1 != e->inputA && -1 != e->inputB) { + input1 = (inputTriangle *)arrayGetItem(ctx->inputArray, + e->inputA); + input2 = (inputTriangle *)arrayGetItem(ctx->inputArray, + e->inputB); + if (!input1->merged && !input2->merged) { + //input1->merged = 1; + //input2->merged = 1; + //tri2QuadAddOutputQuadByEdgeAndInput(ctx, e, input1, input2); + } + } + } + + for (inputIndex = 0; inputIndex < arrayGetLength(ctx->inputArray); + ++inputIndex) { + inputTriangle *inputItem = (inputTriangle *)arrayGetItem(ctx->inputArray, + inputIndex); + if (!inputItem->merged) { + tri2QuadAddOutputTriangle(ctx, &inputItem->tri.pt[0], + &inputItem->tri.pt[1], &inputItem->tri.pt[2]); + } + } + + return 0; +} + +int tri2QuadGetTriangleNum(tri2QuadContext *ctx) { + return arrayGetLength(ctx->triangleArray); +} + +triangle *tri2QuadGetTriangle(tri2QuadContext *ctx, int index) { + return (triangle *)arrayGetItem(ctx->triangleArray, index); +} + +int tri2QuadGetQuadNum(tri2QuadContext *ctx) { + return arrayGetLength(ctx->quadArray); +} + +quad *tri2QuadGetQuad(tri2QuadContext *ctx, int index) { + return (quad *)arrayGetItem(ctx->quadArray, index); +} + +void tri2QuadContextDestroy(tri2QuadContext *ctx) { + arrayDestroy(ctx->inputArray); + arrayDestroy(ctx->triangleArray); + arrayDestroy(ctx->quadArray); + hashtableDestroy(ctx->edgeHashtable); + arrayDestroy(ctx->edgeArray); + free(ctx); +} diff --git a/src/tri2quad.h b/src/tri2quad.h new file mode 100644 index 00000000..f7053b9c --- /dev/null +++ b/src/tri2quad.h @@ -0,0 +1,17 @@ +#ifndef TRI2QUAD_H +#define TRI2QUAD_H +#include "draw.h" + +typedef struct tri2QuadContext tri2QuadContext; + +tri2QuadContext *tri2QuadContextCreate(void); +int tri2QuadAddTriangle(tri2QuadContext *ctx, triangle *tri); +int tri2QuadConvert(tri2QuadContext *ctx); +int tri2QuadGetTriangleNum(tri2QuadContext *ctx); +triangle *tri2QuadGetTriangle(tri2QuadContext *ctx, int index); +int tri2QuadGetQuadNum(tri2QuadContext *ctx); +quad *tri2QuadGetQuad(tri2QuadContext *ctx, int index); +void tri2QuadContextDestroy(tri2QuadContext *ctx); + +#endif + diff --git a/src/vector3d.c b/src/vector3d.c index 450d6f7a..f784725a 100644 --- a/src/vector3d.c +++ b/src/vector3d.c @@ -129,3 +129,12 @@ void vec3ProjectOver(vec3 *a, vec3 *over, vec3 *result) { result->z = length * over->z; } +float vec3TriangleArea(vec3 *a, vec3 *b, vec3 *c) { + vec3 ab; + vec3 ac; + vec3 cross; + vec3Sub(a, b, &ab); + vec3Sub(a, c, &ac); + vec3CrossProduct(&ab, &ac, &cross); + return vec3Length(&cross) * 0.5; +} diff --git a/src/vector3d.h b/src/vector3d.h index e35ea8a2..0b23356b 100644 --- a/src/vector3d.h +++ b/src/vector3d.h @@ -25,6 +25,7 @@ void vec3Normal(vec3 *a, vec3 *b, vec3 *c, vec3 *normal); void vec3RotateAlong(vec3 *a, float angle, vec3 *axis, vec3 *result); float vec3Angle(vec3 *a, vec3 *b); void vec3ProjectOver(vec3 *a, vec3 *over, vec3 *result); +float vec3TriangleArea(vec3 *a, vec3 *b, vec3 *c); #ifdef __cplusplus }