parent
5bf806f363
commit
ef1836ab06
17
README.md
17
README.md
|
@ -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 |
121
src/bmesh.c
121
src/bmesh.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -17,7 +17,7 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
vec3 pt[4];
|
||||
} quard;
|
||||
} quad;
|
||||
|
||||
typedef struct {
|
||||
int npoly;
|
||||
|
|
36
src/matrix.c
36
src/matrix.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue