www.pudn.com > 3dsMFCRender.rar > 3dsReader.cpp, change:1998-05-23,size:16095b
//////////////////////////////////////////////////////////////////////
//
// 3dsReader.cpp: implementation of the C3dsReader class.
// C3dsReader class version 1.5
// (C) 1998 - Sivert L. Nielsen (sivni@qeocities.com)
// This code may be freely distributed and used in programs so long
// as this notice appears in the code.
//
// One world, one mind.
//
// Along with the docs present about the 3ds-fileformat on the Internet
// Keith Rule supplied a great starting perspective in his
// book "3D Graphics File Formats" ISBN:0-201-48835. Much of
// the reader code is based on his work.
// Spline classes used in TriObject by Kyle E. Lussier
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "3dsReader.h"
#include <stdlib.h>
#ifndef DSREADER_CPP
#define DSREADER_CPP
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
C3dsReader::C3dsReader()
{
}
C3dsReader::~C3dsReader()
{
}
// Read Chunk into Chunk structure
int C3dsReader::Read3DSChunk(FILE* fp, Chunk3DS& chunk)
{
if (!ReadUShort(fp, chunk.id)) return FALSE;
if (!ReadLong(fp, chunk.len)) return FALSE;
return TRUE;
}
// Read string and truncate if buffer is shorter than actual string.
int C3dsReader::Read3DSString(FILE* fp, char* name, int len /*= 256*/)
{
int c;
for (int i=0; (c = fgetc(fp)) != EOF && c != '\0'; i++) {
if (i len) {
name[i] = c;
}
}
if (i len) {
name[i] = '\0';
} else {
name[len-1] = '\0';
}
return (c != EOF);
}
// Read a Percent Chunk
int C3dsReader::ReadPercentage(FILE* fp, float& value)
{
Chunk3DS chunk;
long chunkStart = ftell(fp);
if (!Read3DSChunk(fp, chunk)) return FALSE;
if (chunk.id == INT_PERCENTAGE) {
short svalue;
if (!ReadShort(fp, svalue)) return FALSE;
value = (float) svalue/ (float) 100.0;
return TRUE;
} else if (chunk.id == FLOAT_PERCENTAGE) {
if (!ReadFloat(fp, value)) return FALSE;
return TRUE;
}
fseek(fp, chunkStart + chunk.len, SEEK_SET);
return FALSE;
}
// Read a color definition
int C3dsReader::ReadColor(FILE* fp, float& red, float& green, float& blue)
{
Chunk3DS chunk;
long chunkStart = ftell(fp);
unsigned char tmp;
if (!Read3DSChunk(fp, chunk)) return FALSE;
switch (chunk.id) {
case COLOR_F:
if (!ReadFloat(fp, red)) return FALSE;
if (!ReadFloat(fp, green)) return FALSE;
if (!ReadFloat(fp, blue)) return FALSE;
break;
case COLOR_24:
if (!ReadUByte(fp, tmp)) return FALSE;
red = (float) tmp / (float) 255.0;
if (!ReadUByte(fp, tmp)) return FALSE;
green = (float) tmp / (float) 255.0;
if (!ReadUByte(fp, tmp)) return FALSE;
blue = (float) tmp / (float) 255.0;
break;
default:
fseek(fp, chunkStart + chunk.len, SEEK_SET);
return FALSE;
}
return TRUE;
}
// Read vertices
int C3dsReader::ReadPointArray (TriObject* newchild, long fileSize, FILE *fp)
{
unsigned short count;
float value;
if (!ReadUShort(fp, count)) return FALSE;
float* x = new float[count];
float* y = new float[count];
float* z = new float[count];
if (x == NULL || y == NULL || z == NULL) return FALSE;
for (int i = 0; i<count; i++) {
if (!ReadFloat(fp, value)) { //X
delete [] x;
delete [] y;
delete [] z;
return FALSE;
}
x[i] = value;
if (!ReadFloat(fp, value)) { //Y
delete [] x;
delete [] y;
delete [] z;
return FALSE;
}
y[i] = value;
if (!ReadFloat(fp, value)) { //Z
delete [] x;
delete [] y;
delete [] z;
return FALSE;
}
z[i] = value;
}
newchild->setX(x, count);
newchild->setY(y, count);
newchild->setZ(z, count);
return count;
}
// Read polygons
int C3dsReader::ReadFaceArray (TriObject* newchild, long unsigned fileSize, FILE* fp)
{
unsigned short count = 0;
unsigned short value = 0;
BOOL error = FALSE;
// Read the count
int* fac;
if (!ReadUShort(fp, count)) return FALSE;
fac = new int[count*3];
if (fac == NULL) return FALSE;
// Read the faces
for (int i=0; i<count;i++) {
// Read the triangles facenumbers
if (!ReadUShort(fp, value)) error = TRUE;
fac[3*i +0] = value;
if (!ReadUShort(fp, value)) error = TRUE;
fac[3*i +1] = value;
if (!ReadUShort(fp, value)) error = TRUE;
fac[3*i +2] = value;
if (!ReadUShort(fp, value)) error = TRUE; // Read the visible edges
if(error) {
delete [] fac;
fac = NULL;
return FALSE;
}
}
newchild->setFaces(fac, count*3);
return count*3;
}
// Read the materials used by the group
int C3dsReader::ReadMeshMatGroup(TriObject* newchild, MaterialDict* matdict, long fileSize, FILE* fp)
{
unsigned short count, face;
char name[256];
tMaterial* lookup;
long index = 0;
// Read the material name
if (!Read3DSString(fp, name, 256)) return FALSE;
// Find the material in my material list
if ((lookup = matdict->Lookup(name)) != NULL)
{
index = newchild->addMaterial(lookup);
}
// Read the number of faces to map
if (!ReadUShort(fp, count)) return FALSE;
// Map each of the faces
while (count-- > 0) {
if (!ReadUShort(fp, face)) return FALSE;
if (index != -1) newchild->addMaterialFace(face, index);
}
return TRUE;
}
// Read the Mesh
int C3dsReader::ReadTriObject(MaterialDict* matdict, long fileSize, FILE* fp, long triStart, long triSize, char* groupName)
{
Chunk3DS chunk;
long chunkStart = ftell(fp);
int verticecount = 0;
int facecount = 0;
int matcount = 0;
static int id = 1;
TriObject* newchild = new TriObject();
//read and fill data
while (chunkStart triStart + triSize && Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case POINT_ARRAY:
verticecount = ReadPointArray(newchild, fileSize, fp);
if (verticecount == FALSE) return FALSE;
break;
case FACE_ARRAY:
facecount = ReadFaceArray(newchild, fileSize, fp);
if (facecount == FALSE) return FALSE;
break;
case MSH_MAT_GROUP:
if (!ReadMeshMatGroup(newchild, matdict, fileSize, fp)) return FALSE;
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
// set name and add this dude to our list
newchild->setName(groupName);
newchild->setId(id);
DaList->add(newchild);
id++;
return TRUE;
}
// Read the Named Mesh (other named objects are ignored).
int C3dsReader::ReadNamedObject(MaterialDict* matdict, long fileSize, long namedStart, long namedSize, FILE* fp)
{
char groupName[256];
Chunk3DS chunk;
long chunkStart;
if (!Read3DSString(fp, groupName, 256)) return FALSE;
chunkStart = ftell(fp);
while (chunkStart namedStart+namedSize && Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case N_TRI_OBJECT:
if (!ReadTriObject(matdict, fileSize, fp, chunkStart, chunk.len, groupName)) return FALSE;
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
return TRUE;
}
// Read a material definition, add it to the material dictionary.
int C3dsReader::ReadMatEntry(MaterialDict* matdict, long fileSize, long matStart, long matSize, FILE* fp)
{
long chunkStart = ftell(fp);
Chunk3DS chunk;
char name[256];
float red, green, blue;
float percentage;
tMaterial material;
while (chunkStart matStart + matSize &&
Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case MAT_NAME:
if (!Read3DSString(fp, name, 256)) return FALSE;
break;
case MAT_AMBIENT:
if (!ReadColor(fp, red, green, blue)) return FALSE;
material.ambientColor[0] = red;
material.ambientColor[1] = green;
material.ambientColor[2] = blue;
break;
case MAT_DIFFUSE:
if (!ReadColor(fp, red, green, blue)) return FALSE;
material.diffuseColor[0] = red;
material.diffuseColor[1] = green;
material.diffuseColor[2] = blue;
break;
case MAT_SPECULAR:
if (!ReadColor(fp, red, green, blue)) return FALSE;
material.specularColor[0] = red;
material.specularColor[1] = green;
material.specularColor[2] = blue;
break;
case MAT_SHININESS:
if (!ReadPercentage(fp, percentage)) return FALSE;
material.shininess = ((float)percentage)/100.0f;
break;
case MAT_TRANSPARENCY:
if (!ReadPercentage(fp, percentage)) return FALSE;
material.transparency = ((float)percentage)/100.0f;
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
matdict->Add(name, material);
return TRUE;
}
// Read toplevel objects
int C3dsReader::ReadMDATA(MaterialDict* matdict, long fileSize, long mdataStart, long mdataSize, FILE* fp)
{
long chunkStart = ftell(fp);
Chunk3DS chunk;
unsigned long version;
float scale;
while (chunkStart mdataStart + mdataSize && Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case MESH_VERSION:
if (!ReadULong(fp, version)) {
return FALSE;
}
break;
case MAT_ENTRY:
if (!ReadMatEntry(matdict, fileSize, chunkStart, chunk.len, fp)) {
return FALSE;
}
break;
case MASTER_SCALE:
if (!ReadFloat(fp, scale)) {
return FALSE;
}
break;
case NAMED_OBJECT:
if (!ReadNamedObject(matdict, fileSize, chunkStart, chunk.len, fp)) {
return FALSE;
}
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
return TRUE;
}
// Read 3ds file
int C3dsReader::Read3DSFile(long fileSize, long fileStart, long fileLen, FILE* fp)
{
long chunkStart = ftell(fp);
Chunk3DS chunk;
MaterialDict *matdict = new MaterialDict();
unsigned long version;
while (chunkStart fileStart + fileLen && Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case M3D_VERSION:
if (!ReadULong(fp, version)) goto error;
break;
case MDATA:
if (!ReadMDATA(matdict, fileSize, chunkStart, chunk.len, fp)) goto error;
break;
case KFDATA:
if (!ReadKFDATA(fileSize, chunkStart, chunk.len, fp)) goto error;
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
if (matdict != NULL) delete matdict;
return TRUE;
error:
if (matdict != NULL) delete matdict;
return FALSE;
}
// Verifies that the current file is a 3ds file.
int C3dsReader::Is3DSFile(FILE* fp)
{
Chunk3DS chunk;
long pos = ftell(fp);
if (!Read3DSChunk(fp, chunk)) {
fseek(fp, pos, SEEK_SET);
return FALSE;
}
fseek(fp, pos, SEEK_SET);
return (chunk.id == M3DMAGIC);
}
// External entry for reading 3ds file.
BOOL C3dsReader::Reader( char* filename, TriList* _list)
{
FILE* fp;
long fileSize;
Chunk3DS chunk;
DaList = _list;
// Open 3DS file in a "binary" way :)
if ((fp = fopen(filename, "rb")) != NULL) {
long chunkStart = ftell(fp);
// Get File Size
fseek(fp, 0, SEEK_END);
fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
// Verify file type
if (!Is3DSFile(fp)) {
return FALSE;
}
// Loop through the chunks
while (chunkStart fileSize &&
Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case M3DMAGIC:
if (!Read3DSFile(fileSize, chunkStart, chunk.len, fp)) {
fclose(fp);
return FALSE;
}
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
fclose(fp);
}
else
return FALSE;
return TRUE;
}
#endif
int C3dsReader::ReadKFDATA(long fileSize, long kfdataStart, long kfdataSize, FILE * fp)
{
long chunkStart = ftell(fp);
Chunk3DS chunk;
short version;
long kflength;
long kfstart;
long kfend;
char name[256];
while (chunkStart kfdataStart + kfdataSize && Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case KFHDR:
if (!ReadShort(fp,version)) return FALSE;
if (!Read3DSString(fp, name, 256)) return FALSE;
if (!ReadLong(fp, kflength)) return FALSE;
break;
case KFSEG:
if (!ReadLong(fp, kfstart)) return FALSE;
if (!ReadLong(fp, kfend)) return FALSE;
DaList->setAnim(kfstart, kfend);
break;
case OBJECT_NODE_TAG:
ReadKFObjectNode(fileSize, chunkStart, chunk.len, fp);
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
return TRUE;
}
int C3dsReader::ReadKFObjectNode(long fileSize, long nodeStart, long nodeSize, FILE * fp)
{
long chunkStart = ftell(fp);
Chunk3DS chunk;
short nodeid;
char nodeName[256];
tVector pivot;
while (chunkStart nodeStart + nodeSize && Read3DSChunk(fp, chunk)) {
switch (chunk.id) {
case NODE_ID:
if(!ReadShort(fp, nodeid)) return false;
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
case NODE_HDR:
if (!Read3DSString(fp, nodeName, 256)) return FALSE;
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
case PIVOT:
if (!ReadFloat(fp, pivot.x)) return FALSE;
if (!ReadFloat(fp, pivot.y)) return FALSE;
if (!ReadFloat(fp, pivot.z)) return FALSE;
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
case POS_TRACK_TAG:
if(!ReadKFTrackTag(fileSize, chunkStart, chunk.len, fp, nodeName, &pivot, chunk)) return FALSE;
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
case ROT_TRACK_TAG:
if(!ReadKFTrackTag(fileSize, chunkStart, chunk.len, fp, nodeName, &pivot, chunk)) return FALSE;
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
case SCL_TRACK_TAG:
if(!ReadKFTrackTag(fileSize, chunkStart, chunk.len, fp, nodeName, &pivot, chunk)) return FALSE;
fseek(fp, chunkStart + chunk.len, SEEK_SET);
break;
default:
// Skip past unexpected chunks
fseek(fp, chunkStart + chunk.len, SEEK_SET);
}
chunkStart = ftell(fp);
}
return TRUE;
}
int C3dsReader::ReadKFTrackTag(long fileSize, long tagStart, long tagSize, FILE * fp, char * nodeName, tVector* pivot, Chunk3DS chunk)
{
long numkeys;
short rflags;
short trflags;
long trtmin,trtmax;
TriObject* current;
Key key;
//getObject to work with
current = DaList->getObjectByName(nodeName);
if(current == NULL) return FALSE;
//setPivotPoint
current->setPivotPoint(pivot);
ReadShort(fp, trflags);
ReadLong(fp, trtmin);
ReadLong(fp, trtmax);
ReadLong(fp, numkeys);
for (int i=0; i<numkeys; i++) {
memset(&key,0,sizeof(Key));
ReadLong(fp, key.time);
ReadShort(fp, rflags);
if (rflags&W_TENS) ReadFloat(fp, key.tension);
if (rflags&W_CONT) ReadFloat(fp, key.continuity);
if (rflags&W_BIAS) ReadFloat(fp, key.bias);
if (rflags&W_EASETO) ReadFloat(fp, key.easeto);
if (rflags&W_EASEFROM)ReadFloat(fp, key.easefrom);
switch (chunk.id) {
case POS_TRACK_TAG:
Poskey pkey;
memset(&pkey,0,sizeof(Poskey));
memcpy(&pkey,&key,sizeof(Poskey));
ReadFloat(fp, pkey.pos[0]);
ReadFloat(fp, pkey.pos[1]);
ReadFloat(fp, pkey.pos[2]);
current->addPosKey(pkey);
break;
case SCL_TRACK_TAG:
Poskey skey;
memset(&skey,0,sizeof(Poskey));
memcpy(&skey,&key,sizeof(Poskey));
ReadFloat(fp, skey.pos[0]);
ReadFloat(fp, skey.pos[1]);
ReadFloat(fp, skey.pos[2]);
current->addSclKey(skey);
break;
case ROT_TRACK_TAG:
Rotkey rkey;
memset(&rkey,0,sizeof(Rotkey));
memcpy(&rkey,&key,sizeof(Rotkey));
ReadFloat(fp, rkey.angle);
ReadFloat(fp, rkey.axis[0]);
ReadFloat(fp, rkey.axis[1]);
ReadFloat(fp, rkey.axis[2]);
current->addRotKey(rkey);
break;
}
}
return TRUE;
}