MESH.C

/******************************Module*Header*******************************\ 
* Module Name: mesh.c
*
* Routines to create a mesh representation of a 3D object and to turn it
* into an OpenGL description.
*
* Copyright (c) 1994 Microsoft Corporation
*
\**************************************************************************/
#include <stdlib.h>
#include <windows.h>
#include <GL\gl.h>
#include <string.h>
#include <math.h>
#include "ss3dfo.h"
#include "mesh.h"

#define ZERO_EPS 0.00000001

/******************************Public*Routine******************************\
* newMesh
*
* Allocate memory for the mesh structure to accomodate the specified number
* of points and faces.
*
\**************************************************************************/

void newMesh(MESH *mesh, int numFaces, int numPts)
{
mesh->numFaces = 0;
mesh->numPoints = 0;

if (numPts) {
mesh->pts = SaverAlloc((LONG)numPts * (LONG)sizeof(POINT3D));
mesh->norms = SaverAlloc((LONG)numPts * (LONG)sizeof(POINT3D));
}
mesh->faces = SaverAlloc((LONG)numFaces * (LONG)sizeof(MFACE));
}

/******************************Public*Routine******************************\
* delMesh
*
* Delete the allocated portions of the MESH structure.
*
\**************************************************************************/

void delMesh(MESH *mesh)
{
SaverFree(mesh->pts);
SaverFree(mesh->norms);
SaverFree(mesh->faces);
}

/******************************Public*Routine******************************\
* iPtInList
*
* Add a vertex and its normal to the mesh. If the vertex already exists,
* add in the normal to the existing normal (we to accumulate the average
* normal at each vertex). Normalization of the normals is the
* responsibility of the caller.
*
\**************************************************************************/

static int iPtInList(MESH *mesh, POINT3D *p, POINT3D *norm, int start)
{
int i;
POINT3D *pts = mesh->pts + start;

for (i = start; i < mesh->numPoints; i++, pts++)
{
// If the vertices are within ZERO_EPS of each other, then its the same
// vertex.

if ( fabs(pts->x - p->x) < ZERO_EPS &&
fabs(pts->y - p->y) < ZERO_EPS &&
fabs(pts->z - p->z) < ZERO_EPS )
{
mesh->norms[i].x += norm->x;
mesh->norms[i].y += norm->y;
mesh->norms[i].z += norm->z;
return i;
}
}

mesh->pts[i] = *p;
mesh->norms[i] = *norm;
mesh->numPoints++;
return i;
}


/******************************Public*Routine******************************\
* revolveSurface
*
* Takes the set of points in curve and fills the mesh structure with a
* surface of revolution. The surface consists of quads made up of the
* points in curve rotated about the y-axis. The number of increments
* in the revolution is determined by the steps parameter.
*
\**************************************************************************/

#define MAXPREC 40

void revolveSurface(MESH *mesh, POINT3D *curve, int steps)
{
int i;
int j;
int facecount = 0;
double rotation = 0.0;
double rotInc;
double cosVal;
double sinVal;
int stepsSqr;
POINT3D norm;
POINT3D a[MAXPREC + 1];
POINT3D b[MAXPREC + 1];

if (steps > MAXPREC)
steps = MAXPREC;
rotInc = (2.0 * PI) / (double)(steps - 1);
stepsSqr = steps * steps;
newMesh(mesh, stepsSqr, 4 * stepsSqr);

for (j = 0; j < steps; j++, rotation += rotInc) {
cosVal = cos(rotation);
sinVal = sin(rotation);
for (i = 0; i < steps; i++) {
a[i].x = (float) (curve[i].x * cosVal + curve[i].z * sinVal);
a[i].y = (float) (curve[i].y);
a[i].z = (float) (curve[i].z * cosVal - curve[i].x * sinVal);
}

cosVal = cos(rotation + rotInc);
sinVal = sin(rotation + rotInc);
for (i = 0; i < steps; i++) {
b[i].x = (float) (curve[i].x * cosVal + curve[i].z * sinVal);
b[i].y = (float) (curve[i].y);
b[i].z = (float) (curve[i].z * cosVal - curve[i].x * sinVal);
}

for (i = 0; i < (steps - 1); i++) {
ss_calcNorm(&norm, &b[i + 1], &b[i], &a[i]);
if ((norm.x * norm.x) + (norm.y * norm.y) + (norm.z * norm.z) < 0.9)
ss_calcNorm(&norm, &a[i], &a[i+1], &b[i + 1]);
mesh->faces[facecount].material = j & 7;
mesh->faces[facecount].norm = norm;
mesh->faces[facecount].p[0] = iPtInList(mesh, &b[i], &norm, 0);
mesh->faces[facecount].p[1] = iPtInList(mesh, &a[i], &norm, 0);
mesh->faces[facecount].p[2] = iPtInList(mesh, &b[i + 1], &norm, 0);
mesh->faces[facecount].p[3] = iPtInList(mesh, &a[i + 1], &norm, 0);
mesh->numFaces++;
facecount++;
}
}

ss_normalizeNorms(mesh->norms, mesh->numPoints);
}


/******************************Public*Routine******************************\
* updateObject
*
* Takes the mesh structure and converts the data into OpenGL immediate
* mode commands.
*
\**************************************************************************/

void updateObject(MESH *mesh, BOOL bSmooth)
{
int i;
int a, b;
int aOffs, bOffs, cOffs, dOffs;
MFACE *faces;
POINT3D *pp;
POINT3D *pn;
int lastC, lastD;

pp = mesh->pts;
pn = mesh->norms;

glBegin(GL_QUAD_STRIP);
for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
i < mesh->numFaces; i++, faces++) {

a = faces->p[0];
b = faces->p[1];

if (!bSmooth) {
if ((a != lastC) || (b != lastD)) {
glNormal3fv((GLfloat *)&(faces - 1)->norm);

glVertex3fv((GLfloat *)((char *)pp +
(lastC << 3) + (lastC << 2)));
glVertex3fv((GLfloat *)((char *)pp +
(lastD << 3) + (lastD << 2)));
glEnd();
glBegin(GL_QUAD_STRIP);
}

glNormal3fv((GLfloat *)&faces->norm);
glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
} else {
if ((a != lastC) || (b != lastD)) {
cOffs = (lastC << 3) + (lastC << 2);
dOffs = (lastD << 3) + (lastD << 2);

glNormal3fv((GLfloat *)((char *)pn + cOffs));
glVertex3fv((GLfloat *)((char *)pp + cOffs));
glNormal3fv((GLfloat *)((char *)pn + dOffs));
glVertex3fv((GLfloat *)((char *)pp + dOffs));
glEnd();
glBegin(GL_QUAD_STRIP);
}

aOffs = (a << 3) + (a << 2);
bOffs = (b << 3) + (b << 2);

glNormal3fv((GLfloat *)((char *)pn + aOffs));
glVertex3fv((GLfloat *)((char *)pp + aOffs));
glNormal3fv((GLfloat *)((char *)pn + bOffs));
glVertex3fv((GLfloat *)((char *)pp + bOffs));
}

lastC = faces->p[2];
lastD = faces->p[3];
}

if (!bSmooth) {
glNormal3fv((GLfloat *)&(faces - 1)->norm);
glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
} else {
cOffs = (lastC << 3) + (lastC << 2);
dOffs = (lastD << 3) + (lastD << 2);

glNormal3fv((GLfloat *)((char *)pn + cOffs));
glVertex3fv((GLfloat *)((char *)pp + cOffs));
glNormal3fv((GLfloat *)((char *)pn + dOffs));
glVertex3fv((GLfloat *)((char *)pp + dOffs));
}

glEnd();
}


/******************************Public*Routine******************************\
* updateObject
*
* This is a special case that handles a mesh structure that represents
* a strip that is a 1 high loop.
*
* Takes the mesh structure and converts the data into OpenGL immediate
* mode commands.
*
\**************************************************************************/

void updateObject2(MESH *mesh, BOOL bSmooth)
{
int i;
int a, b;
int aOffs, bOffs, cOffs, dOffs;
MFACE *faces;
POINT3D *pp;
POINT3D *pn;
int lastC, lastD;

pp = mesh->pts;
pn = mesh->norms;

glBegin(GL_QUAD_STRIP);
for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
i < mesh->numFaces; i++, faces++) {

a = faces->p[0];
b = faces->p[1];

if (!bSmooth) {
glNormal3fv((GLfloat *)&faces->norm);
glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
} else {
aOffs = (a << 3) + (a << 2);
bOffs = (b << 3) + (b << 2);

glNormal3fv((GLfloat *)((char *)pn + aOffs));
glVertex3fv((GLfloat *)((char *)pp + aOffs));
glNormal3fv((GLfloat *)((char *)pn + bOffs));
glVertex3fv((GLfloat *)((char *)pp + bOffs));
}

lastC = faces->p[2];
lastD = faces->p[3];
}

if (!bSmooth) {
glNormal3fv((GLfloat *)&(mesh->faces)->norm);
glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
} else {
cOffs = (lastC << 3) + (lastC << 2);
dOffs = (lastD << 3) + (lastD << 2);

glNormal3fv((GLfloat *)((char *)pn + cOffs));
glVertex3fv((GLfloat *)((char *)pp + cOffs));
glNormal3fv((GLfloat *)((char *)pn + dOffs));
glVertex3fv((GLfloat *)((char *)pp + dOffs));
}

glEnd();
}


/******************************Public*Routine******************************\
* MakeList
*
* Takes the mesh structure and converts the data into OpenGL display
* list.
*
\**************************************************************************/

void MakeList(GLuint listID, MESH *mesh)
{
int i;
int a, b;
int aOffs, bOffs, cOffs, dOffs;
MFACE *faces;
BOOL bSmooth;
POINT3D *pp;
POINT3D *pn;
GLint shadeModel;
int lastC, lastD;

glGetIntegerv(GL_SHADE_MODEL, &shadeModel);

bSmooth = (shadeModel == GL_SMOOTH);

glNewList(listID, GL_COMPILE);

pp = mesh->pts;
pn = mesh->norms;

glBegin(GL_QUAD_STRIP);
for (i = 0, faces = mesh->faces, lastC = faces->p[0], lastD = faces->p[1];
i < mesh->numFaces; i++, faces++) {

a = faces->p[0];
b = faces->p[1];

if (!bSmooth) {

if ((a != lastC) || (b != lastD)) {
glNormal3fv((GLfloat *)&((faces - 1)->norm));

glVertex3fv((GLfloat *)((char *)pp +
(lastC << 3) + (lastC << 2)));
glVertex3fv((GLfloat *)((char *)pp +
(lastD << 3) + (lastD << 2)));
glEnd();
glBegin(GL_QUAD_STRIP);
}

glNormal3fv((GLfloat *)&faces->norm);
glVertex3fv((GLfloat *)((char *)pp + (a << 3) + (a << 2)));
glVertex3fv((GLfloat *)((char *)pp + (b << 3) + (b << 2)));
} else {
if ((a != lastC) || (b != lastD)) {
cOffs = (lastC << 3) + (lastC << 2);
dOffs = (lastD << 3) + (lastD << 2);

glNormal3fv((GLfloat *)((char *)pn + cOffs));
glVertex3fv((GLfloat *)((char *)pp + cOffs));
glNormal3fv((GLfloat *)((char *)pn + dOffs));
glVertex3fv((GLfloat *)((char *)pp + dOffs));
glEnd();
glBegin(GL_QUAD_STRIP);
}

aOffs = (a << 3) + (a << 2);
bOffs = (b << 3) + (b << 2);

glNormal3fv((GLfloat *)((char *)pn + aOffs));
glVertex3fv((GLfloat *)((char *)pp + aOffs));
glNormal3fv((GLfloat *)((char *)pn + bOffs));
glVertex3fv((GLfloat *)((char *)pp + bOffs));
}

lastC = faces->p[2];
lastD = faces->p[3];
}

if (!bSmooth) {
glNormal3fv((GLfloat *)&((faces - 1)->norm));
glVertex3fv((GLfloat *)((char *)pp + (lastC << 3) + (lastC << 2)));
glVertex3fv((GLfloat *)((char *)pp + (lastD << 3) + (lastD << 2)));
} else {
cOffs = (lastC << 3) + (lastC << 2);
dOffs = (lastD << 3) + (lastD << 2);

glNormal3fv((GLfloat *)((char *)pn + cOffs));
glVertex3fv((GLfloat *)((char *)pp + cOffs));
glNormal3fv((GLfloat *)((char *)pn + dOffs));
glVertex3fv((GLfloat *)((char *)pp + dOffs));
}

glEnd();

glEndList();
}