mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
634 lines
17 KiB
634 lines
17 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: nff.c
|
|
*
|
|
* Parse the Wavefront OBJ format.
|
|
*
|
|
* Documentation on OBJ available from:
|
|
*
|
|
* Wavefront Technologies, 530 E. Montecito St., Santa Barbara, CA 93103,
|
|
* Phone: 805-962-8117.
|
|
*
|
|
* Murray, J. D. and van Ryper, W., _Encyclopedia of Graphics File Formats_,
|
|
* O'Reilly & Associates, 1994, pp. 727-734.
|
|
*
|
|
* Created: 15-Mar-1995 23:27:08
|
|
* Author: Gilman Wong [gilmanw]
|
|
*
|
|
* Copyright (c) 1995 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "global.h"
|
|
#include "nff.h"
|
|
|
|
#define STATIC
|
|
|
|
typedef struct tagOBJPRIV
|
|
{
|
|
BOOL bInList; // TRUE while compiling display list
|
|
|
|
// Vertex arrays
|
|
|
|
MyXYZ *pxyzGeoVert; // array of geometric vertices
|
|
MyXYZ *pxyzTexVert; // array of texture vertices
|
|
MyXYZ *pxyzNorm; // array of vertex normals
|
|
|
|
// Number allocated for each array
|
|
|
|
GLuint numGeoVert; // total number of geometric vertices
|
|
GLuint numTexVert; // total number of texture vertices
|
|
GLuint numNorm; // total number of normals
|
|
|
|
// Number of vertices currently in each array
|
|
|
|
GLuint curGeoVert; // current number of geometric vertices
|
|
GLuint curTexVert; // current number of texture vertices
|
|
GLuint curNorm; // current number of normals
|
|
|
|
GLfloat rMax;
|
|
|
|
} OBJPRIV;
|
|
|
|
GLint maxLights;
|
|
|
|
STATIC void parseObjFile(SCENE *, LPTSTR);
|
|
STATIC void DoDisplayList(SCENE *);
|
|
STATIC void EndDisplayList(SCENE *);
|
|
STATIC void parseObjNormal(SCENE *scene, FILE *file, char *ach, int n);
|
|
STATIC void parseObjTextureVertex(SCENE *scene, FILE *file, char *ach, int n);
|
|
STATIC void parseObjVertex(SCENE *scene, FILE *file, char *ach, int n);
|
|
STATIC void parseObjFace(SCENE *scene, FILE *file, char *ach, int n);
|
|
STATIC void fakeObjLight(SCENE *scene, GLfloat x, GLfloat y, GLfloat z);
|
|
|
|
SCENE *ObjOpenScene(LPTSTR lpstr)
|
|
{
|
|
SCENE *scene;
|
|
|
|
scene = (SCENE *) LocalAlloc(LMEM_FIXED, sizeof(SCENE) + sizeof(OBJPRIV));
|
|
if (scene)
|
|
{
|
|
OBJPRIV *objPriv = (OBJPRIV *) (scene + 1);
|
|
|
|
glGetIntegerv(GL_MAX_LIGHTS, &maxLights);
|
|
|
|
scene->xyzFrom.x = 0.0;
|
|
scene->xyzFrom.y = 0.0;
|
|
//scene->xyzFrom.z = 5.0;
|
|
|
|
scene->xyzAt.x = 0.0;
|
|
scene->xyzAt.y = 0.0;
|
|
scene->xyzAt.z = 0.0;
|
|
|
|
scene->xyzUp.x = 0.0;
|
|
scene->xyzUp.y = 1.0;
|
|
scene->xyzUp.z = 0.0;
|
|
|
|
scene->ViewAngle = 45.0f;
|
|
scene->Hither = 0.1f;
|
|
//scene->Yon = 100.0f;
|
|
scene->AspectRatio = 1.0f;
|
|
|
|
scene->szWindow.cx = 400;
|
|
scene->szWindow.cy = 400;
|
|
|
|
scene->rgbaClear.r = (GLfloat) 0.0; // default is black
|
|
scene->rgbaClear.g = (GLfloat) 0.0;
|
|
scene->rgbaClear.b = (GLfloat) 0.0;
|
|
scene->rgbaClear.a = (GLfloat) 1.0;
|
|
|
|
scene->Lights.count = 0;
|
|
scene->Lights.listBase = 1;
|
|
|
|
scene->Objects.count = 1;
|
|
scene->Objects.listBase = maxLights + 1;
|
|
|
|
scene->pvData = (VOID *) objPriv;
|
|
objPriv->bInList = FALSE;
|
|
objPriv->pxyzGeoVert = (MyXYZ *) NULL;
|
|
objPriv->pxyzTexVert = (MyXYZ *) NULL;
|
|
objPriv->pxyzNorm = (MyXYZ *) NULL;
|
|
objPriv->numGeoVert = 0;
|
|
objPriv->numTexVert = 0;
|
|
objPriv->numNorm = 0;
|
|
objPriv->curGeoVert = 0;
|
|
objPriv->curTexVert = 0;
|
|
objPriv->curNorm = 0;
|
|
objPriv->rMax = (GLfloat) 0.0;
|
|
|
|
LBprintf("================================");
|
|
LBprintf("Parsing OBJ file, please wait...");
|
|
LBprintf("================================");
|
|
parseObjFile(scene, lpstr);
|
|
LBprintf("Here we go!");
|
|
|
|
scene->xyzFrom.z = 2.0f * objPriv->rMax * tan(90.0f - scene->ViewAngle);
|
|
scene->Yon = (scene->xyzAt.z - scene->xyzFrom.z) * (scene->xyzAt.z - scene->xyzFrom.z);
|
|
scene->Yon = sqrt(scene->Yon) * 2.5f;
|
|
|
|
fakeObjLight(scene, objPriv->rMax, objPriv->rMax, scene->xyzFrom.z);
|
|
}
|
|
else
|
|
{
|
|
LBprintf("ObjOpenScene: memory allocation failure");
|
|
}
|
|
|
|
return scene;
|
|
}
|
|
|
|
STATIC void parseObjFile(SCENE *scene, LPTSTR lpstr)
|
|
{
|
|
FILE *file;
|
|
char ach[512];
|
|
char *pch;
|
|
OBJPRIV *objPriv = (OBJPRIV *) scene->pvData;
|
|
|
|
file = fopen(lpstr, "rt");
|
|
|
|
if (file)
|
|
{
|
|
BOOL bKeepGoing = TRUE;
|
|
|
|
// Do a quick first pass through the file so we know how big to
|
|
// make the arrays.
|
|
|
|
do
|
|
{
|
|
if (fgets(ach, sizeof(ach), file) != NULL)
|
|
{
|
|
// Skip leading whitespace. Some input files have this.
|
|
|
|
pch = ach;
|
|
while (*pch && isspace(*pch)) pch++;
|
|
|
|
switch(pch[0])
|
|
{
|
|
case 'V':
|
|
case 'v':
|
|
switch(pch[1])
|
|
{
|
|
case 'N':
|
|
case 'n':
|
|
objPriv->numNorm++;
|
|
break;
|
|
case 'T':
|
|
case 't':
|
|
//objPriv->numTexVert++;
|
|
break;
|
|
case 'P':
|
|
case 'p':
|
|
break;
|
|
default:
|
|
objPriv->numGeoVert++;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bKeepGoing = FALSE;
|
|
//LBprintf("possible EOF");
|
|
}
|
|
|
|
} while (bKeepGoing || !feof(file));
|
|
rewind(file);
|
|
|
|
// Allocate the arrays.
|
|
|
|
objPriv->pxyzGeoVert = (MyXYZ *)
|
|
LocalAlloc(LMEM_FIXED, sizeof(MyXYZ) * (objPriv->numNorm +
|
|
objPriv->numTexVert +
|
|
objPriv->numGeoVert));
|
|
objPriv->pxyzTexVert = objPriv->pxyzGeoVert + objPriv->numGeoVert;
|
|
objPriv->pxyzNorm = objPriv->pxyzTexVert + objPriv->numTexVert;
|
|
|
|
// Parse the file for real.
|
|
|
|
bKeepGoing = TRUE;
|
|
do
|
|
{
|
|
if (fgets(ach, sizeof(ach), file) != NULL)
|
|
{
|
|
// Skip leading whitespace. Some input files have this.
|
|
|
|
pch = ach;
|
|
while (*pch && isspace(*pch)) pch++;
|
|
|
|
switch(pch[0])
|
|
{
|
|
case 'G':
|
|
case 'g':
|
|
//LBprintf(ach);
|
|
break;
|
|
|
|
case 'V':
|
|
case 'v':
|
|
switch(pch[1])
|
|
{
|
|
case 'N':
|
|
case 'n':
|
|
parseObjNormal(scene, file, pch, sizeof(ach) - (pch - ach));
|
|
break;
|
|
case 'T':
|
|
case 't':
|
|
//parseObjTextureVertex(scene, file, pch, sizeof(ach) - (pch - ach));
|
|
break;
|
|
case 'P':
|
|
case 'p':
|
|
break;
|
|
default:
|
|
parseObjVertex(scene, file, pch, sizeof(ach) - (pch - ach));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 'f':
|
|
case 'F':
|
|
switch(pch[1])
|
|
{
|
|
case 'O':
|
|
case 'o':
|
|
parseObjFace(scene, file, &pch[1], sizeof(ach) - (pch - ach) - 1);
|
|
break;
|
|
default:
|
|
parseObjFace(scene, file, pch, sizeof(ach) - (pch - ach));
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case '#':
|
|
LBprintf(pch);
|
|
break;
|
|
|
|
default:
|
|
//LBprintf("unknown: %s", ach);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bKeepGoing = FALSE;
|
|
//LBprintf("possible EOF");
|
|
}
|
|
|
|
} while (bKeepGoing || !feof(file));
|
|
fclose(file);
|
|
|
|
EndDisplayList(scene);
|
|
}
|
|
else
|
|
{
|
|
LBprintf("fopen failed %s", lpstr);
|
|
LBprintf("USAGE: viewer [OBJ filename].OBJ");
|
|
}
|
|
}
|
|
|
|
STATIC void DoDisplayList(SCENE *scene)
|
|
{
|
|
if (!((OBJPRIV *)scene->pvData)->bInList)
|
|
{
|
|
((OBJPRIV *)scene->pvData)->bInList = TRUE;
|
|
|
|
glNewList(scene->Objects.listBase, GL_COMPILE);
|
|
|
|
//LBprintf("BEGIN DISPLAY LIST");
|
|
}
|
|
}
|
|
|
|
STATIC void EndDisplayList(SCENE *scene)
|
|
{
|
|
if (((OBJPRIV *)scene->pvData)->bInList)
|
|
{
|
|
((OBJPRIV *)scene->pvData)->bInList = FALSE;
|
|
|
|
glEndList();
|
|
|
|
//LBprintf("END DISPLAY LIST");
|
|
}
|
|
}
|
|
|
|
STATIC void parseObjNormal(SCENE *scene, FILE *file, char *ach, int n)
|
|
{
|
|
OBJPRIV *objPriv = (OBJPRIV *) scene->pvData;
|
|
|
|
sscanf(ach, "vn %g %g %g", &objPriv->pxyzNorm[objPriv->curNorm].x,
|
|
&objPriv->pxyzNorm[objPriv->curNorm].y,
|
|
&objPriv->pxyzNorm[objPriv->curNorm].z);
|
|
|
|
objPriv->curNorm++;
|
|
}
|
|
|
|
STATIC void parseObjTextureVertex(SCENE *scene, FILE *file, char *ach, int n)
|
|
{
|
|
OBJPRIV *objPriv = (OBJPRIV *) scene->pvData;
|
|
|
|
sscanf(ach, "vt %g %g %g", &objPriv->pxyzTexVert[objPriv->curTexVert].x,
|
|
&objPriv->pxyzTexVert[objPriv->curTexVert].y,
|
|
&objPriv->pxyzTexVert[objPriv->curTexVert].z);
|
|
|
|
objPriv->curTexVert++;
|
|
}
|
|
|
|
STATIC void parseObjVertex(SCENE *scene, FILE *file, char *ach, int n)
|
|
{
|
|
OBJPRIV *objPriv = (OBJPRIV *) scene->pvData;
|
|
|
|
sscanf(ach, "v %g %g %g", &objPriv->pxyzGeoVert[objPriv->curGeoVert].x,
|
|
&objPriv->pxyzGeoVert[objPriv->curGeoVert].y,
|
|
&objPriv->pxyzGeoVert[objPriv->curGeoVert].z);
|
|
|
|
objPriv->curGeoVert++;
|
|
}
|
|
|
|
STATIC void parseObjFace(SCENE *scene, FILE *file, char *ach, int n)
|
|
{
|
|
OBJPRIV *objPriv = (OBJPRIV *) scene->pvData;
|
|
char *pch;
|
|
GLuint i, numVert = 0;
|
|
char achFormat[20];
|
|
BOOL bReadTex = FALSE, bReadNorm = FALSE;
|
|
GLuint v, vt, vn;
|
|
GLfloat rMax;
|
|
GLuint vv[3];
|
|
MyXYZ faceNorm;
|
|
|
|
DoDisplayList(scene);
|
|
|
|
// Determine number of vertices.
|
|
// Each vertex is represented by a field of non-whitespace characters
|
|
// which are numbers or '/' characters.
|
|
|
|
pch = &ach[1];
|
|
while ( *pch )
|
|
{
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
if (*pch)
|
|
numVert++;
|
|
|
|
while (*pch && !isspace(*pch))
|
|
pch++;
|
|
}
|
|
|
|
// Bail if there aren't enough vertices to define a face.
|
|
|
|
if (numVert < 3)
|
|
{
|
|
LBprintf("bad line: %s", ach);
|
|
return;
|
|
}
|
|
|
|
// Determine type of vertex info available. Each vertex has the form
|
|
// v[/[vt][/[vn]]]. Some examples include:
|
|
//
|
|
// f 1/2/3 ...
|
|
// f 1//3 ...
|
|
// f 1/2 ...
|
|
// f 1/2/ ...
|
|
// f 1 ...
|
|
// f 1// ...
|
|
// etc.
|
|
|
|
pch = &ach[1];
|
|
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
if (*pch && isdigit(*pch))
|
|
lstrcpy(achFormat, "%ld");
|
|
|
|
while (*pch && isdigit(*pch))
|
|
pch++;
|
|
|
|
if (*pch == '/')
|
|
{
|
|
lstrcat(achFormat, "/");
|
|
pch++;
|
|
}
|
|
|
|
if (*pch && isdigit(*pch))
|
|
{
|
|
bReadTex = TRUE;
|
|
lstrcat(achFormat, "%ld");
|
|
|
|
while (*pch && isdigit(*pch))
|
|
pch++;
|
|
}
|
|
|
|
if (*pch == '/')
|
|
{
|
|
lstrcat(achFormat, "/");
|
|
pch++;
|
|
}
|
|
|
|
if (*pch && isdigit(*pch))
|
|
{
|
|
bReadNorm = TRUE;
|
|
lstrcat(achFormat, "%ld");
|
|
|
|
while (*pch && isdigit(*pch))
|
|
pch++;
|
|
}
|
|
|
|
// If we need to compute our own normal, let's do it now.
|
|
|
|
pch = &ach[1];
|
|
|
|
if (!bReadNorm)
|
|
{
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
sscanf(pch, "%ld", &vv[i]);
|
|
if (vv[i] > 0)
|
|
vv[i] = vv[i] - 1;
|
|
else
|
|
vv[i] = objPriv->curGeoVert + vv[i];
|
|
|
|
while (*pch && !isspace(*pch))
|
|
pch++;
|
|
|
|
}
|
|
|
|
calcNorm((GLfloat *) &faceNorm,
|
|
(GLfloat *) &objPriv->pxyzGeoVert[vv[0]],
|
|
(GLfloat *) &objPriv->pxyzGeoVert[vv[1]],
|
|
(GLfloat *) &objPriv->pxyzGeoVert[vv[2]]);
|
|
}
|
|
|
|
// Finally, parse out the vertices.
|
|
|
|
pch = &ach[1];
|
|
|
|
glBegin(numVert == 3 ? GL_TRIANGLES :
|
|
numVert == 4 ? GL_QUADS :
|
|
GL_POLYGON);
|
|
if (!bReadTex && !bReadNorm)
|
|
{
|
|
glNormal3fv((GLfloat *) &faceNorm);
|
|
|
|
for ( i = 0; i < numVert; i++ )
|
|
{
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
sscanf(pch, achFormat, &v);
|
|
if (v > 0)
|
|
v = v - 1;
|
|
else
|
|
v = objPriv->curGeoVert + v;
|
|
|
|
glVertex3fv((GLfloat *)&objPriv->pxyzGeoVert[v]);
|
|
|
|
if (fabs(objPriv->pxyzGeoVert[v].x) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].x);
|
|
if (fabs(objPriv->pxyzGeoVert[v].y) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].y);
|
|
if (fabs(objPriv->pxyzGeoVert[v].z) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].z);
|
|
|
|
while (*pch && !isspace(*pch))
|
|
pch++;
|
|
|
|
}
|
|
}
|
|
else if (!bReadTex && bReadNorm)
|
|
{
|
|
for ( i = 0; i < numVert; i++ )
|
|
{
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
sscanf(pch, achFormat, &v, &vn);
|
|
if (v > 0)
|
|
v = v - 1;
|
|
else
|
|
v = objPriv->curGeoVert + v;
|
|
if (vn > 0)
|
|
vn = vn - 1;
|
|
else
|
|
vn = objPriv->curGeoVert + vn;
|
|
|
|
glNormal3fv((GLfloat *)&objPriv->pxyzNorm[vn]);
|
|
glVertex3fv((GLfloat *)&objPriv->pxyzGeoVert[v]);
|
|
|
|
if (fabs(objPriv->pxyzGeoVert[v].x) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].x);
|
|
if (fabs(objPriv->pxyzGeoVert[v].y) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].y);
|
|
if (fabs(objPriv->pxyzGeoVert[v].z) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].z);
|
|
|
|
while (*pch && !isspace(*pch))
|
|
pch++;
|
|
|
|
}
|
|
}
|
|
else if (bReadTex && !bReadNorm)
|
|
{
|
|
glNormal3fv((GLfloat *) &faceNorm);
|
|
|
|
for ( i = 0; i < numVert; i++ )
|
|
{
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
sscanf(pch, achFormat, &v, &vt);
|
|
if (v > 0)
|
|
v = v - 1;
|
|
else
|
|
v = objPriv->curGeoVert + v;
|
|
if (vt > 0)
|
|
vt = vt - 1;
|
|
else
|
|
vt = objPriv->curGeoVert + vt;
|
|
|
|
//!!! ignore texture vertex for now; just use the geometry
|
|
glVertex3fv((GLfloat *)&objPriv->pxyzGeoVert[v]);
|
|
|
|
if (fabs(objPriv->pxyzGeoVert[v].x) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].x);
|
|
if (fabs(objPriv->pxyzGeoVert[v].y) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].y);
|
|
if (fabs(objPriv->pxyzGeoVert[v].z) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].z);
|
|
|
|
while (*pch && !isspace(*pch))
|
|
pch++;
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for ( i = 0; i < numVert; i++ )
|
|
{
|
|
while (*pch && isspace(*pch))
|
|
pch++;
|
|
|
|
sscanf(pch, achFormat, &v, &vt, &vn);
|
|
if (v > 0)
|
|
v = v - 1;
|
|
else
|
|
v = objPriv->curGeoVert + v;
|
|
if (vt > 0)
|
|
vt = vt - 1;
|
|
else
|
|
vt = objPriv->curGeoVert + vt;
|
|
if (vn > 0)
|
|
vn = vn - 1;
|
|
else
|
|
vn = objPriv->curGeoVert + vn;
|
|
|
|
//!!! ignore texture vertex for now; just use the geometry
|
|
glNormal3fv((GLfloat *)&objPriv->pxyzNorm[vn]);
|
|
glVertex3fv((GLfloat *)&objPriv->pxyzGeoVert[v]);
|
|
|
|
if (fabs(objPriv->pxyzGeoVert[v].x) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].x);
|
|
if (fabs(objPriv->pxyzGeoVert[v].y) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].y);
|
|
if (fabs(objPriv->pxyzGeoVert[v].z) > objPriv->rMax)
|
|
objPriv->rMax = fabs(objPriv->pxyzGeoVert[v].z);
|
|
|
|
while (*pch && !isspace(*pch))
|
|
pch++;
|
|
|
|
}
|
|
}
|
|
glEnd();
|
|
}
|
|
|
|
STATIC void fakeObjLight(SCENE *scene, GLfloat x, GLfloat y, GLfloat z)
|
|
{
|
|
MyXYZ xyz;
|
|
MyRGBA rgb = {1.0f, 1.0f, 1.0f, 1.0f};
|
|
|
|
xyz.x = x;
|
|
xyz.y = y;
|
|
xyz.z = z;
|
|
|
|
if (scene->Lights.count < (maxLights + 1))
|
|
{
|
|
glNewList(scene->Lights.listBase + scene->Lights.count, GL_COMPILE);
|
|
|
|
glLightfv(GL_LIGHT0 + scene->Lights.count, GL_POSITION, (GLfloat *) &xyz);
|
|
glLightfv(GL_LIGHT0 + scene->Lights.count, GL_DIFFUSE, (GLfloat *) &rgb);
|
|
glLightfv(GL_LIGHT0 + scene->Lights.count, GL_SPECULAR, (GLfloat *) &rgb);
|
|
|
|
glEndList();
|
|
|
|
scene->Lights.count++;
|
|
}
|
|
}
|