Add matrix.

Add quad generation from shape nodes.
master
Jeremy Hu 2016-12-22 22:33:32 +09:30
parent 5bf806f363
commit ef1836ab06
11 changed files with 294 additions and 6 deletions

View File

@ -7,7 +7,18 @@ Idea
I was inspired by Jimmy Gunawan's blogs of monster generation, here is the first one [INSPIRATION / Pixar Monster Factory Part One](http://blendersushi.blogspot.com.au/2013/06/inspiration-pixar-monster-factory-part.html), and by search the Skin Modifier of Blender, I found the theory of Skin Modifier:
[B-Mesh: A Fast Modeling System for Base Meshes
of 3D Articulated Shapes](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.357.7134&rep=rep1&type=pdf)(Authors: Zhongping Ji, Ligang Liu, Yigang Wang). I started to think of monster model generation for game development from years ago, thanks for this paper, Dust3D is achievable now.
From my initial thought, Dust3D should be a tool like [Makehuman](http://www.makehuman.org), with more versatile features, not only can make human, but also be able to **generate monsters automatically**.
From my initial thought, Dust3D should be a tool like [Makehuman](http://www.makehuman.org), with more versatile features, not only can make human, but also be able to **generate monsters automatically**.
Build
============
*Generate Xcode Project*
```
$ qmake -spec macx-xcode
```
*Generate Makefile*
```
$ qmake
```
TODO & Progress
==============
@ -35,6 +46,9 @@ I created the test nodes's geometry information from Blender. Here is the render
<img src="screenshot/dust3d_bmesh_nodes.png" width="206" height="164">
*Generate Inbetween Nodes*
<img src="screenshot/dust3d_bmesh_skeleton.png" width="124" height="128"> <img src="screenshot/dust3d_bmesh_inbetween.png" width="124" height="128">
*Generate Mesh*
<img src="screenshot/dust3d_generate_quad.png" width="124" height="128">
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.
- [ ] Export Wavefront .obj
- [ ] Render B-Mesh result
- [ ] Design UI for monster parts configuration
@ -43,3 +57,4 @@ I created the test nodes's geometry information from Blender. Here is the render
- [ ] png exporter for isometric 2.5D game
- [ ] Version 0.01 release
- [ ] Materials merge of different parts
- [ ] Implement [the Skeleton of a Closed 3D Shape](http://www1.idc.ac.il/icgf/GraphicsSeminar2006/DCGskeleton06.pdf)

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 KiB

View File

@ -2,8 +2,10 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "bmesh.h"
#include "array.h"
#include "matrix.h"
typedef struct bmeshNodeIndex {
int nodeIndex;
@ -14,6 +16,7 @@ struct bmesh {
array *nodeArray;
array *edgeArray;
array *indexArray;
array *quadArray;
int rootNodeIndex;
int roundColor;
};
@ -42,6 +45,12 @@ bmesh *bmeshCreate(void) {
bmeshDestroy(bm);
return 0;
}
bm->quadArray = arrayCreate(sizeof(quad));
if (!bm->quadArray) {
fprintf(stderr, "%s:arrayCreate quad failed.\n", __FUNCTION__);
bmeshDestroy(bm);
return 0;
}
bm->rootNodeIndex = -1;
bm->roundColor = 0;
return bm;
@ -51,6 +60,7 @@ void bmeshDestroy(bmesh *bm) {
arrayDestroy(bm->nodeArray);
arrayDestroy(bm->edgeArray);
arrayDestroy(bm->indexArray);
arrayDestroy(bm->quadArray);
free(bm);
}
@ -145,19 +155,95 @@ static int bmeshAddInbetweenNodeBetween(bmesh *bm,
return newNode.index;
}
static void floatsToQuad(float *floats, quad *q) {
int i;
int offset = 0;
for (i = 0; i < 4; ++i) {
q->pt[i].x = floats[offset++];
q->pt[i].y = floats[offset++];
q->pt[i].z = floats[offset++];
}
}
static int bmeshGenerateNodeQuad(bmesh *bm, bmeshNode *node,
matrix *matRotate, int connectWithQuad) {
quad q;
matrix matTranslate;
matrix matFinal;
int i;
float floats[4][3] = {
{-node->radius, +node->radius, 0},
{-node->radius, -node->radius, 0},
{+node->radius, -node->radius, 0},
{+node->radius, +node->radius, 0},
};
matrixTranslate(&matTranslate, node->position.x, node->position.y,
node->position.z);
matrixLoadIdentity(&matFinal);
matrixAppend(&matFinal, &matTranslate);
matrixAppend(&matFinal, matRotate);
matrixTransformVector(&matFinal, floats[0]);
matrixTransformVector(&matFinal, floats[1]);
matrixTransformVector(&matFinal, floats[2]);
matrixTransformVector(&matFinal, floats[3]);
floatsToQuad(&floats[0][0], &q);
if (-1 == bmeshAddQuad(bm, &q)) {
fprintf(stderr, "%s:meshAddQuad failed.\n", __FUNCTION__);
return -1;
}
if (connectWithQuad >= 0) {
for (i = 0; i < 4; ++i) {
quad face;
quad *lastQ = bmeshGetQuad(bm, connectWithQuad);
face.pt[0].x = lastQ->pt[(0 + i) % 4].x;
face.pt[0].y = lastQ->pt[(0 + i) % 4].y;
face.pt[0].z = lastQ->pt[(0 + i) % 4].z;
face.pt[1].x = q.pt[(0 + i) % 4].x;
face.pt[1].y = q.pt[(0 + i) % 4].y;
face.pt[1].z = q.pt[(0 + i) % 4].z;
face.pt[2].x = q.pt[(1 + i) % 4].x;
face.pt[2].y = q.pt[(1 + i) % 4].y;
face.pt[2].z = q.pt[(1 + i) % 4].z;
face.pt[3].x = lastQ->pt[(1 + i) % 4].x;
face.pt[3].y = lastQ->pt[(1 + i) % 4].y;
face.pt[3].z = lastQ->pt[(1 + i) % 4].z;
if (-1 == bmeshAddQuad(bm, &face)) {
fprintf(stderr, "%s:meshAddQuad failed.\n", __FUNCTION__);
return -1;
}
}
}
return 0;
}
static int bmeshGenerateInbetweenNodesBetween(bmesh *bm,
int firstNodeIndex, int secondNodeIndex) {
float step = 0.5;
float distance;
int parentNodeIndex = firstNodeIndex;
float rotateAngle = 0;
vec3 rotateAround = {0, 0, 0};
vec3 p;
vec3 zAxis = {0, 0, 1};
matrix matRotate;
int lastQuadIndex = -1;
bmeshNode *firstNode = bmeshGetNode(bm, firstNodeIndex);
bmeshNode *secondNode = bmeshGetNode(bm, secondNodeIndex);
bmeshNode *newNode;
if (secondNode->roundColor == bm->roundColor) {
return 0;
}
distance = vec3Distance(&firstNode->position, &secondNode->position);
vec3Sub(&firstNode->position, &secondNode->position, &p);
vec3CrossProduct(&zAxis, &p, &rotateAround);
vec3Normalize(&rotateAround);
distance = vec3Length(&p);
if (distance > 0) {
float offset = step;
rotateAngle = 180 / M_PI * acos(vec3DotProduct(&zAxis, &p) / distance);
matrixRotate(&matRotate,
rotateAngle, rotateAround.x, rotateAround.y, rotateAround.z);
if (offset + step <= distance) {
while (offset + step <= distance) {
float frac = offset / distance;
@ -166,14 +252,23 @@ static int bmeshGenerateInbetweenNodesBetween(bmesh *bm,
if (-1 == parentNodeIndex) {
return -1;
}
newNode = bmeshGetNode(bm, parentNodeIndex);
bmeshGenerateNodeQuad(bm, newNode, &matRotate,
lastQuadIndex);
lastQuadIndex = -1 == lastQuadIndex ? bmeshGetQuadNum(bm) - 1 :
bmeshGetQuadNum(bm) - 5;
offset += step;
}
} else if (distance > step) {
parentNodeIndex = bmeshAddInbetweenNodeBetween(bm, firstNode, secondNode, 0.5,
parentNodeIndex);
parentNodeIndex = bmeshAddInbetweenNodeBetween(bm, firstNode, secondNode,
0.5, parentNodeIndex);
if (-1 == parentNodeIndex) {
return -1;
}
newNode = bmeshGetNode(bm, parentNodeIndex);
bmeshGenerateNodeQuad(bm, newNode, &matRotate, lastQuadIndex);
lastQuadIndex = -1 == lastQuadIndex ? bmeshGetQuadNum(bm) - 1 :
bmeshGetQuadNum(bm) - 5;
}
}
if (-1 == bmeshAddChildNodeRelation(bm, parentNodeIndex, secondNodeIndex)) {
@ -256,3 +351,23 @@ int bmeshGenerateInbetweenNodes(bmesh *bm) {
bm->roundColor++;
return bmeshGenerateInbetweenNodesFrom(bm, bm->rootNodeIndex);
}
int bmeshGetQuadNum(bmesh *bm) {
return arrayGetLength(bm->quadArray);
}
quad *bmeshGetQuad(bmesh *bm, int index) {
return (quad *)arrayGetItem(bm->quadArray, index);
}
int bmeshAddQuad(bmesh *bm, quad *q) {
int index = arrayGetLength(bm->quadArray);
if (0 != arraySetLength(bm->quadArray, index + 1)) {
fprintf(stderr, "%s:arraySetLength failed.\n", __FUNCTION__);
return -1;
}
memcpy(arrayGetItem(bm->quadArray, index), q, sizeof(quad));
return index;
}

View File

@ -1,6 +1,7 @@
#ifndef B_MESH_H
#define B_MESH_H
#include "vector3d.h"
#include "draw.h"
#ifdef __cplusplus
extern "C" {
@ -41,6 +42,9 @@ int bmeshAddEdge(bmesh *bm, bmeshEdge *edge);
int bmeshGenerateInbetweenNodes(bmesh *bm);
int bmeshGetNodeNextChild(bmesh *bm, bmeshNode *node, int *childIndex);
bmeshNode *bmeshGetRootNode(bmesh *bm);
int bmeshGetQuadNum(bmesh *bm);
quad *bmeshGetQuad(bmesh *bm, int index);
int bmeshAddQuad(bmesh *bm, quad *q);
#ifdef __cplusplus
}

View File

@ -49,6 +49,7 @@ int drawCylinder(vec3 *topOrigin, vec3 *bottomOrigin, float radius, int slices,
vec3Sub(topOrigin, bottomOrigin, &p);
vec3CrossProduct(&zAxis, &p, &t);
vec3Normalize(&t);
height = vec3Length(&p);
if (height > 0) {
angle = 180 / M_PI * acos(vec3DotProduct(&zAxis, &p) / height);

View File

@ -17,7 +17,7 @@ typedef struct {
typedef struct {
vec3 pt[4];
} quard;
} quad;
typedef struct {
int npoly;

View File

@ -21,6 +21,42 @@ matrix *matrixTranslate(matrix *mat, float x, float y, float z) {
return mat;
}
//
// matrixRotate modified from http://www.gamedev.net/topic/600537-instead-of-glrotatef-build-a-matrix/
//
matrix *matrixRotate(matrix *mat, float degree, float x, float y, float z) {
float c;
float s;
float length;
matrixLoadIdentity(mat);
if (degree <= 0) {
return mat;
}
length = sqrt(x * x + y * y + z * z);
c = cos(degree * DEG2RAD);
s = sin(degree * DEG2RAD);
mat->data[0] = x * x * (1 - c) + c;
mat->data[4] = x * y * (1 - c) - z * s;
mat->data[8] = x * z * (1 - c) + y * s;
mat->data[1] = y * x * (1 - c) + z * s;
mat->data[5] = y * y * (1 - c) + c;
mat->data[9] = y * z * (1 - c) - x * s;
mat->data[2] = x * z * (1 - c) - y * s;
mat->data[6] = y * z * (1 - c) + x * s;
mat->data[10] = z * z * (1 - c) + c;
return mat;
}
matrix *matrixRotateX(matrix *mat, float degree) {
float c;
float s;

View File

@ -1,6 +1,10 @@
#ifndef __MATRIX_H__
#define __MATRIX_H__
#ifdef __cplusplus
extern "C" {
#endif
// Modified from http://wiki.unity3d.com/index.php?title=Matrix
typedef struct matrix {
@ -12,8 +16,13 @@ matrix *matrixTranslate(matrix *mat, float x, float y, float z);
matrix *matrixRotateX(matrix *mat, float degree);
matrix *matrixRotateY(matrix *mat, float degree);
matrix *matrixRotateZ(matrix *mat, float degree);
matrix *matrixRotate(matrix *mat, float degree, float x, float y, float z);
matrix *matrixScale(matrix *mat, float x, float y, float z);
float *matrixTransformVector(matrix *mat, float *vec);
matrix *matrixAppend(matrix *mat, matrix *matB);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -26,7 +26,6 @@ static int drawBmeshNode(bmesh *bm, bmeshNode *node) {
static void drawBmeshNodeRecursively(bmesh *bm, bmeshNode *node) {
int childIndex = node->firstChildIndex;
int childNodeIndex;
drawBmeshNode(bm, node);
while (-1 != childIndex) {
childNodeIndex = bmeshGetNodeNextChild(bm, node, &childIndex);
@ -37,6 +36,62 @@ static void drawBmeshNodeRecursively(bmesh *bm, bmeshNode *node) {
}
}
static void drawBmeshNodeQuardRecursively(bmesh *bm, bmeshNode *node) {
int childIndex = node->firstChildIndex;
int childNodeIndex;
/*
matrix matTmp;
matrix matCalc;
float quad[4][3] = {
{-node->radius, +node->radius, 0},
{-node->radius, -node->radius, 0},
{+node->radius, -node->radius, 0},
{+node->radius, +node->radius, 0},
};
matrixLoadIdentity(&matCalc);
matrixAppend(&matCalc,
matrixTranslate(&matTmp, node->position.x, node->position.y,
node->position.z));
matrixAppend(&matCalc,
matrixRotate(&matTmp,
node->rotateAngle, node->rotateAround.x, node->rotateAround.y,
node->rotateAround.z));
matrixTransformVector(&matCalc, quad[0]);
matrixTransformVector(&matCalc, quad[1]);
matrixTransformVector(&matCalc, quad[2]);
matrixTransformVector(&matCalc, quad[3]);
glVertex3fv(quad[0]);
glVertex3fv(quad[1]);
glVertex3fv(quad[2]);
glVertex3fv(quad[3]);
*/
/*
glPushMatrix();
glTranslatef(node->position.x, node->position.y,
node->position.z);
glRotatef(node->rotateAngle, node->rotateAround.x, node->rotateAround.y,
node->rotateAround.z);
glBegin(GL_QUADS);
glVertex3f(-node->radius, +node->radius, 0);
glVertex3f(-node->radius, -node->radius, 0);
glVertex3f(+node->radius, -node->radius, 0);
glVertex3f(+node->radius, +node->radius, 0);
glEnd();
glPopMatrix();
*/
while (-1 != childIndex) {
childNodeIndex = bmeshGetNodeNextChild(bm, node, &childIndex);
if (-1 == childNodeIndex) {
break;
}
drawBmeshNodeQuardRecursively(bm, bmeshGetNode(bm, childNodeIndex));
}
}
static int drawBmeshEdge(bmesh *bm, bmeshEdge *edge) {
glColor3fv(bmeshEdgeColor);
bmeshNode *firstNode = bmeshGetNode(bm, edge->firstNodeIndex);
@ -82,6 +137,8 @@ void Render::initializeGL() {
qglClearColor(QWidget::palette().color(QWidget::backgroundRole()));
glClearStencil(0);
glClearDepth(1.0f);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
GLfloat ambientLight[] = {0.0f, 0.0f, 0.0f, 1.0f};
GLfloat diffuseLight[] = {0.9f, 0.9f, 0.9f, 1.0f};
@ -156,6 +213,10 @@ void Render::paintGL() {
}
drawBmeshNodeRecursively(bm, bmeshGetRootNode(bm));
//glBegin(GL_QUADS);
//drawBmeshNodeQuardRecursively(bm, bmeshGetRootNode(bm));
//glEnd();
{
int index;
@ -168,6 +229,30 @@ void Render::paintGL() {
bmeshEdge *edge = bmeshGetEdge(bm, index);
drawBmeshEdge(bm, edge);
}
glColor4f(1.0f, 1.0f, 1.0f, 0.5);
glBegin(GL_QUADS);
for (index = 0; index < bmeshGetQuadNum(bm); ++index) {
quad *q = bmeshGetQuad(bm, index);
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);
for (index = 0; index < bmeshGetQuadNum(bm); ++index) {
quad *q = bmeshGetQuad(bm, index);
int j;
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();

View File

@ -49,3 +49,25 @@ float vec3Distance(vec3 *a, vec3 *b) {
vec3Sub(a, b, &p);
return vec3Length(&p);
}
void vec3Normal(vec3 *a, vec3 *b, vec3 *c, vec3 *normal) {
float v1[3], v2[3], vr[3], val;
v1[0] = a->x - b->x;
v1[1] = a->y - b->y;
v1[2] = a->z - b->z;
v2[0] = a->x - c->x;
v2[1] = a->y - c->y;
v2[2] = a->z - c->z;
vr[0] = v1[1] * v2[2] - v2[1] * v1[2];
vr[1] = v2[0] * v1[2] - v1[0] * v2[2];
vr[2] = v1[0] * v2[1] - v2[0] * v1[1];
val = sqrt(vr[0]*vr[0] + vr[1]*vr[1] + vr[2]*vr[2]);
normal->x = vr[0]/val;
normal->y = vr[1]/val;
normal->z = vr[2]/val;
}

View File

@ -19,6 +19,7 @@ void vec3Sub(vec3 *a, vec3 *b, vec3 *result);
float vec3DotProduct(vec3 *a, vec3 *b);
float vec3Length(vec3 *p);
float vec3Distance(vec3 *a, vec3 *b);
void vec3Normal(vec3 *a, vec3 *b, vec3 *c, vec3 *normal);
#ifdef __cplusplus
}