windows-nt/Source/XPSP1/NT/multimedia/opengl/test/demos/viewer/obj.c

635 lines
17 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/******************************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++;
}
}