diff --git a/README.md b/README.md
index 566e8d89..c5410bb8 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ Two caps and many strips composites a cylinder.
Almost all 3D editor have a infinite grid ground, I just made a finite one, in the future, I should expand the grid outside of the screen to make it infinite.
Now, for just beginning, I think it's a not bad start.
-- [ ] Implement B-Mesh algorithm (Dec 18, 2016 ~ Dec 25, 2016)
+- [ ] Implement B-Mesh algorithm (Dec 18, 2016 ~ Dec 26, 2016)
*Drawing Skeletal Shape Balls*
Draw shape ball is easy, no need to rotate, I just need scale it along the ball's radius.
Draw the cylinder which connects two shape balls is more difficult, I need do some math to rotate it. [Here](http://www.thjsmith.com/40/cylinder-between-two-points-opengl-c) described it.
@@ -54,8 +54,8 @@ I created the test nodes's geometry information from Blender. Here is the render
When I am implementing the B-Mesh algorithm, I am also think in the future, how to create a library of bunch of initial base models. There is a paper [the Skeleton of a Closed 3D Shape](http://www1.idc.ac.il/icgf/GraphicsSeminar2006/DCGskeleton06.pdf) described how to generate skeleton from mesh, this is the reverse progress of what I am doing, I think it can resolve the problem of insufficient initial base models, I can generate from tons of existed models.
*Convex Hull*
-After finish the rotation at the two connected bones, I need implement [3D Convex Hull](https://en.wikipedia.org/wiki/Convex_hull) algorithm at joint ball.
-
+After finish the rotation at the two connected bones, I need implement 3D Convex Hull algorithm at the joint ball, there are so many methods to get the convex hull, I found the [Gift wrapping](http://dccg.upc.edu/people/vera/wp-content/uploads/2014/11/GA2014-ConvexHulls3D-Roger-Hernando.pdf) is the most strait-forward one, though is not the most efficient one.
+
- [ ] Export Wavefront .obj
- [ ] Render B-Mesh result
- [ ] Design UI for monster parts configuration
diff --git a/build/dust3d.pro b/build/dust3d.pro
index 75f7809d..6c9faa7c 100644
--- a/build/dust3d.pro
+++ b/build/dust3d.pro
@@ -14,7 +14,8 @@ SOURCES += main.cpp \
draw.cpp \
array.c \
bmesh.c \
- matrix.c
+ matrix.c \
+ convexhull.c
HEADERS += mainwindow.h \
render.h \
@@ -22,4 +23,5 @@ HEADERS += mainwindow.h \
draw.h \
array.h \
bmesh.h \
- matrix.h
\ No newline at end of file
+ matrix.h \
+ convexhull.h
\ No newline at end of file
diff --git a/screenshot/dust3d_convex_hull.png b/screenshot/dust3d_convex_hull.png
new file mode 100644
index 00000000..ca9ce992
Binary files /dev/null and b/screenshot/dust3d_convex_hull.png differ
diff --git a/src/bmesh.c b/src/bmesh.c
index b81ad1df..b66ad86e 100644
--- a/src/bmesh.c
+++ b/src/bmesh.c
@@ -6,6 +6,7 @@
#include "bmesh.h"
#include "array.h"
#include "matrix.h"
+#include "convexhull.h"
#include "draw.h"
#define BMESH_STEP_DISTANCE 0.4
@@ -245,6 +246,7 @@ static int bmeshGenerateInbetweenBallsBetween(bmesh *bm,
generateYZfromBoneDirection(&boneDirection,
&localYaxis, &localZaxis);
+ /*
glColor3f(0.0, 0.0, 0.0);
drawDebugPrintf("<%f,%f,%f> <%f,%f,%f> <%f,%f,%f>",
localYaxis.x,
@@ -256,7 +258,8 @@ static int bmeshGenerateInbetweenBallsBetween(bmesh *bm,
boneDirection.x,
boneDirection.y,
boneDirection.z);
-
+ */
+
distance = vec3Length(&boneDirection);
if (distance > BMESH_STEP_DISTANCE) {
float offset;
@@ -485,7 +488,7 @@ static bmeshBall *bmeshFindBallForConvexHull(bmesh *bm, bmeshBall *root,
bmeshBallIterator iterator;
bmeshBall *child;
float distance = vec3Distance(&root->position, &ball->position);
- if (distance >= root->radius) {
+ if (distance > root->radius) {
return ball;
}
child = bmeshGetBallFirstChild(bm, ball, &iterator);
@@ -502,11 +505,51 @@ static int bmeshStichFrom(bmesh *bm, bmeshBall *ball) {
bmeshBall *child;
bmeshBall *ballForConvexHull;
if (BMESH_BALL_TYPE_ROOT == ball->type) {
+ convexHull *hull = convexHullCreate();
+ if (!hull) {
+ fprintf(stderr, "%s:convexHullCreate failed.\n", __FUNCTION__);
+ return -1;
+ }
for (child = bmeshGetBallFirstChild(bm, ball, &iterator);
child;
child = bmeshGetBallNextChild(bm, ball, &iterator)) {
+ vec3 z, y;
+ quad q;
+ int vertexIndices[4];
+
ballForConvexHull = bmeshFindBallForConvexHull(bm, ball, child);
+
+ vec3Scale(&ballForConvexHull->localYaxis, ballForConvexHull->radius, &y);
+ vec3Scale(&ballForConvexHull->localZaxis, ballForConvexHull->radius, &z);
+ vec3Sub(&ballForConvexHull->position, &y, &q.pt[0]);
+ vec3Add(&q.pt[0], &z, &q.pt[0]);
+ vec3Sub(&ballForConvexHull->position, &y, &q.pt[1]);
+ vec3Sub(&q.pt[1], &z, &q.pt[1]);
+ vec3Add(&ballForConvexHull->position, &y, &q.pt[2]);
+ vec3Sub(&q.pt[2], &z, &q.pt[2]);
+ vec3Add(&ballForConvexHull->position, &y, &q.pt[3]);
+ vec3Add(&q.pt[3], &z, &q.pt[3]);
+
+ vertexIndices[0] = convexHullAddVertex(hull, &q.pt[0]);
+ vertexIndices[1] = convexHullAddVertex(hull, &q.pt[1]);
+ vertexIndices[2] = convexHullAddVertex(hull, &q.pt[2]);
+ vertexIndices[3] = convexHullAddVertex(hull, &q.pt[3]);
}
+ convexHullGenerate(hull);
+
+ glPushMatrix();
+ {
+ int triIndex;
+ glColor3f(1.0, 1.0, 1.0);
+ for (triIndex = 0; triIndex < convexHullGetTriangleNum(hull);
+ ++triIndex) {
+ triangle *tri = (triangle *)convexHullGetTriangle(hull, triIndex);
+ drawTriangle(tri);
+ }
+ }
+ glPopMatrix();
+
+ convexHullDestroy(hull);
}
for (child = bmeshGetBallFirstChild(bm, ball, &iterator);
child;
diff --git a/src/convexhull.c b/src/convexhull.c
new file mode 100644
index 00000000..b2a46c51
--- /dev/null
+++ b/src/convexhull.c
@@ -0,0 +1,238 @@
+#include
+#include
+#include
+#include "convexhull.h"
+#include "array.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
+//
+
+struct convexHull {
+ array *vertexArray;
+ array *openEdgeArray;
+ array *triangleArray;
+ int nextEdgeIndex;
+ unsigned int *openEdgeExistMap;
+};
+
+typedef struct {
+ int firstVertex;
+ int secondVertex;
+} edge;
+
+convexHull *convexHullCreate(void) {
+ convexHull *hull = (convexHull *)calloc(1, sizeof(convexHull));
+ if (!hull) {
+ fprintf(stderr, "%s:Insufficient memory.\n", __FUNCTION__);
+ return 0;
+ }
+ hull->vertexArray = arrayCreate(sizeof(vec3));
+ if (!hull->vertexArray) {
+ fprintf(stderr, "%s:arrayCreate failed.\n", __FUNCTION__);
+ return 0;
+ }
+ hull->openEdgeArray = arrayCreate(sizeof(edge));
+ if (!hull->openEdgeArray) {
+ fprintf(stderr, "%s:arrayCreate failed.\n", __FUNCTION__);
+ return 0;
+ }
+ hull->triangleArray = arrayCreate(sizeof(triangle));
+ if (!hull->triangleArray) {
+ fprintf(stderr, "%s:arrayCreate failed.\n", __FUNCTION__);
+ return 0;
+ }
+ return hull;
+}
+
+void convexHullMarkEdgeAsExsits(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);
+ hull->openEdgeExistMap[unitIndex] |= (0x00000001 << unitOffset);
+}
+
+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);
+}
+
+int convexHullAddVertex(convexHull *hull, vec3 *vertex) {
+ int newVertex = arrayGetLength(hull->vertexArray);
+ if (0 != arraySetLength(hull->vertexArray, newVertex + 1)) {
+ fprintf(stderr, "%s:arraySetLength failed.\n", __FUNCTION__);
+ return -1;
+ }
+ *((vec3 *)arrayGetItem(hull->vertexArray, newVertex)) = *vertex;
+ return newVertex;
+}
+
+int convexHullAddOpenEdgeNoCheck(convexHull *hull, int firstVertex,
+ int secondVertex) {
+ edge *e;
+ int newEdge = arrayGetLength(hull->openEdgeArray);
+ 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)) {
+ 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);
+ }
+ return 0;
+}
+
+int convexHullAddTriangle(convexHull *hull, int firstVertex, int secondVertex,
+ int thirdVertex) {
+ triangle *tri;
+ int newTri = arrayGetLength(hull->triangleArray);
+ if (0 != arraySetLength(hull->triangleArray, newTri + 1)) {
+ fprintf(stderr, "%s:arraySetLength failed.\n", __FUNCTION__);
+ return -1;
+ }
+ tri = (triangle *)arrayGetItem(hull->triangleArray, newTri);
+ memset(tri, 0, sizeof(triangle));
+ tri->pt[0] = *((vec3 *)arrayGetItem(hull->vertexArray, firstVertex));
+ tri->pt[1] = *((vec3 *)arrayGetItem(hull->vertexArray, secondVertex));
+ tri->pt[2] = *((vec3 *)arrayGetItem(hull->vertexArray, thirdVertex));
+ return 0;
+}
+
+static void convexHullReleaseForGenerate(convexHull *hull) {
+ free(hull->openEdgeExistMap);
+ hull->openEdgeExistMap = 0;
+}
+
+void convexHullDestroy(convexHull *hull) {
+ arrayDestroy(hull->vertexArray);
+ arrayDestroy(hull->openEdgeArray);
+ arrayDestroy(hull->triangleArray);
+ convexHullReleaseForGenerate(hull);
+ free(hull);
+}
+
+static int convexHullPrepareForGenerate(convexHull *hull) {
+ free(hull->openEdgeExistMap);
+ hull->openEdgeExistMap = (unsigned int *)calloc(
+ arrayGetLength(hull->vertexArray) * arrayGetLength(hull->vertexArray) /
+ (sizeof(unsigned int) * 8) + 1,
+ sizeof(unsigned int));
+ if (!hull->openEdgeExistMap) {
+ fprintf(stderr, "%s:Insufficient memory.\n", __FUNCTION__);
+ return -1;
+ }
+ hull->nextEdgeIndex = 0;
+ return 0;
+}
+
+int convexHullGetLower(convexHull *hull) {
+ int index = 0;
+ 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;
+ }
+ }
+ }
+ return index;
+}
+
+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;
+ }
+ }
+ }
+ }
+ return candidateIndex;
+}
+
+int convexHullGenerate(convexHull *hull) {
+ int index1, index2, index3;
+ convexHullReleaseForGenerate(hull);
+ if (0 != convexHullPrepareForGenerate(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;
+ 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);
+ }
+ return 0;
+}
+
+int convexHullGetTriangleNum(convexHull *hull) {
+ return arrayGetLength(hull->triangleArray);
+}
+
+triangle *convexHullGetTriangle(convexHull *hull, int index) {
+ triangle *tri = (triangle *)arrayGetItem(hull->triangleArray, index);
+ return tri;
+}
diff --git a/src/convexhull.h b/src/convexhull.h
new file mode 100644
index 00000000..ae31c37b
--- /dev/null
+++ b/src/convexhull.h
@@ -0,0 +1,14 @@
+#ifndef CONVEX_HULL_H
+#define CONVEX_HULL_H
+#include "vector3d.h"
+#include "draw.h"
+
+typedef struct convexHull convexHull;
+convexHull *convexHullCreate(void);
+int convexHullAddVertex(convexHull *hull, vec3 *vertex);
+void convexHullDestroy(convexHull *hull);
+int convexHullGenerate(convexHull *hull);
+int convexHullGetTriangleNum(convexHull *hull);
+triangle *convexHullGetTriangle(convexHull *hull, int index);
+
+#endif
diff --git a/src/vector3d.c b/src/vector3d.c
index 35d4ea49..450d6f7a 100644
--- a/src/vector3d.c
+++ b/src/vector3d.c
@@ -105,23 +105,27 @@ float vec3Angle(vec3 *a, vec3 *b) {
float angle;
vec3 p;
float distance;
- float dot;
float acosParam;
- float acosVal;
vec3Sub(a, b, &p);
distance = vec3Length(&p);
if (0 == distance) {
return 0;
}
- dot = vec3DotProduct(a, b);
- acosParam = dot / distance;
+ acosParam = vec3DotProduct(a, b) / distance;
if (acosParam < -1) {
acosParam = -1;
}
if (acosParam > 1) {
acosParam = 1;
}
- acosVal = acos(acosParam);
- angle = 180 / M_PI * acosVal;
+ angle = 180 / M_PI * acos(acosParam);
return angle;
}
+
+void vec3ProjectOver(vec3 *a, vec3 *over, vec3 *result) {
+ float length = vec3DotProduct(a, over);
+ result->x = length * over->x;
+ result->y = length * over->y;
+ result->z = length * over->z;
+}
+
diff --git a/src/vector3d.h b/src/vector3d.h
index 45cd8c39..e35ea8a2 100644
--- a/src/vector3d.h
+++ b/src/vector3d.h
@@ -24,6 +24,7 @@ float vec3Distance(vec3 *a, vec3 *b);
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);
#ifdef __cplusplus
}