/***************************************************************************/
/*
*/
/* www.xbdev.net
*/
/* bkenwright@xbdev.net
*/
/* 3ds.cpp
*/
/*
*/
/***************************************************************************/
#include
"3ds.h"
C3DS::C3DS(void)
{
m_iNumMeshs = 0;
m_iNumMaterials = 0;
}
C3DS::~C3DS(void){}
/***************************************************************************/
/*
*/
/* Some user functions to make the reading of the .3ds file easier
*/
/*
*/
/***************************************************************************/
/* Helper Functions that make our parsing of the chunks easier
*/
/***************************************************************************/
void
C3DS::ReadChunk(stChunk *pChunk)
{
unsigned short
ID = 0;
unsigned int
bytesRead = 0;
unsigned int
bChunkLength = 0;
bytesRead = (unsigned
int)fread(&ID, 1, 2, m_fp);
bytesRead += (unsigned
int)fread(&bChunkLength, 1, 4, m_fp);
pChunk->ID = ID;
pChunk->length = bChunkLength;
pChunk->bytesRead = bytesRead;
}
void
C3DS::SkipChunk(stChunk *pChunk)
{
int buffer[50000] = {0};
fread(buffer, 1, pChunk->length - pChunk->bytesRead, m_fp);
}
/***************************************************************************/
/*
*/
/* Helper Fuction, simply reads in the string from the file pointer, until
*/
/* a null is reached, then returns how many bytes was read.
*/
/*
*/
/***************************************************************************/
int
C3DS::GetString(char* pBuffer)
{
int index = 0;
char buffer[100] = {0};
fread(buffer, 1, 1, m_fp);
while( *(buffer + index++) != 0)
{
fread(buffer + index, 1, 1, m_fp);
}
strcpy( pBuffer, buffer );
return (int)(strlen(buffer) + 1);
}
/***************************************************************************/
/*
*/
/* This little function reads the matrial data for our individual object,
*/
/* So it determines which face references which material, in our material
*/
/* list.
*/
/*
*/
/***************************************************************************/
void
C3DS::ReadMeshMaterials(stChunk* Chunk)
{
// Material Name Where Referencing
char str[256];
unsigned int
characterlen = GetString(str);
Chunk->bytesRead += characterlen;
unsigned short
iNumFaces = 0;
Chunk->bytesRead += (unsigned
int)fread(&iNumFaces, 1, 2, m_fp);
unsigned short
*FaceAssignedThisMaterial = new
unsigned short[iNumFaces];
Chunk->bytesRead += (unsigned
int)fread(FaceAssignedThisMaterial, 1,
iNumFaces*sizeof(unsigned
short), m_fp);
// Determine Which Material It Is In Our List
int MaterialID = 0;
for( int
cc=0; cc<m_iNumMaterials; cc++)
{
if( strcmp( str,
m_pMaterials[cc].szName ) == 0 )
MaterialID = cc;
}
stMesh* pMesh = &(m_pMeshs[m_iNumMeshs - 1]);
for(int i=0;
i<iNumFaces; i++)
{
int iIndex =
FaceAssignedThisMaterial[i];
pMesh->pFaces[iIndex].MaterialID = MaterialID;
}
return;
}
/***************************************************************************/
/*
*/
/* We get all the faces...e.g. Triangle Index's into our vertex array, so
*/
/* we can actually put our shape together.
*/
/*
*/
/***************************************************************************/
void
C3DS::ReadMeshFaces(stChunk* Chunk)
{
unsigned int
iNumberFaces = 0; //= Chunk->length - 6;
Chunk->bytesRead += (unsigned
int)fread(&iNumberFaces, 1, 2, m_fp);
// Each face is 3 points A TRIANGLE!..WOW
struct stXFace{
unsigned short p1, p2, p3,
visibityflag; };
stXFace *pFaces = new stXFace[iNumberFaces];
Chunk->bytesRead += (unsigned
int)fread(pFaces, 1, iNumberFaces*sizeof(stXFace),
m_fp);
stMesh* pMesh = &(m_pMeshs[m_iNumMeshs - 1]);
pMesh->pFaces = new stFace[iNumberFaces];
pMesh->iNumFaces = iNumberFaces;
for( unsigned
int i=0; i<iNumberFaces; i++ )
{
pMesh->pFaces[i].A = pFaces[i].p1;
pMesh->pFaces[i].B = pFaces[i].p2;
pMesh->pFaces[i].C = pFaces[i].p3;
}
delete[] pFaces;
// Our face material information is a sub-chunk.
ParseChunk(Chunk);
}
/***************************************************************************/
/*
*/
/* You know all those x,y,z things...yup I mean vertices...well this reads
*/
/* them all in.
*/
/*
*/
/***************************************************************************/
void
C3DS::ReadMeshVertices(stChunk* Chunk)
{
unsigned int
iNumberVertices = 0;
Chunk->bytesRead += (unsigned
int)fread(&iNumberVertices, 1, 2, m_fp);
// Allocate Memory and dump our vertices to the
screen.
stVert *pVerts = new stVert[iNumberVertices];
Chunk->bytesRead += (unsigned
int)fread( (void*)pVerts,
1, iNumberVertices*sizeof(stVert), m_fp);
stMesh* pMesh = &(m_pMeshs[m_iNumMeshs - 1]);
pMesh->pVerts = pVerts;
pMesh->iNumVerts = iNumberVertices;
SkipChunk(Chunk);
}
/***************************************************************************/
/*
*/
/* Well if we have a texture, e.g. coolimage.bmp, then we need to load in
*/
/* our texture coordinates...tu and tv.
*/
/*
*/
/***************************************************************************/
void
C3DS::ReadMeshTexCoords(stChunk* Chunk)
{
unsigned short
iNumberVertices = 0;
Chunk->bytesRead += (unsigned
int)fread(&iNumberVertices, 1, 2, m_fp);
// Allocate Memory and dump our texture for each
vertice to the screen.
stTex *pTex = new stTex[iNumberVertices];
Chunk->bytesRead += (unsigned
int)fread( (void*)pTex,
1, iNumberVertices*sizeof(stTex), m_fp);
stMesh* pMesh = &(m_pMeshs[m_iNumMeshs - 1]);
pMesh->pTexs = pTex;
SkipChunk(Chunk);
}
/***************************************************************************/
/*
*/
/* Read in our objects name...as each object in our 3D world has a name,
*/
/* for example Box1, HillMesh...whatever you called your object or
object's*/
/* in 3d max before you saved it.
*/
/*
*/
/***************************************************************************/
void
C3DS::GetMeshObjectName(stChunk* Chunk)
{
// The strange thing is, the next few parts of
this chunk represent
// the name of the object. Then we start chunking
again.
char str[256];
unsigned int
characterlen = GetString(str);
Chunk->bytesRead += characterlen;
stMesh* pMesh = &(m_pMeshs[m_iNumMeshs - 1]);
strcpy( pMesh->szMeshName, str );
ParseChunk(Chunk);
}
// Read in our texture's file name (e.g. coolpic.jpg)
void
C3DS::GetTexFileName(stChunk* Chunk)
{
char str[256];
Chunk->bytesRead += GetString(str);
stMaterial* pMat = &(m_pMaterials[m_iNumMaterials-1]);
strcpy( pMat->szTextureFile, str );
}
// Read in our diffuse colour (rgb)
void
C3DS::GetDiffuseColour(stChunk* Chunk)
{
struct stRGB{
unsigned char r, g, b; };
stRGB DiffColour;
char ChunkHeader[6];
Chunk->bytesRead += (unsigned
int)fread(ChunkHeader, 1, 6, m_fp);
Chunk->bytesRead += (unsigned
int)fread(&DiffColour, 1, 3, m_fp);
stMaterial* pM = &(m_pMaterials[m_iNumMaterials-1]);
pM->Colour.r
= DiffColour.r;
pM->Colour.g
= DiffColour.g;
pM->Colour.b
= DiffColour.b;
SkipChunk(Chunk);
}
// Get the materials name, e.g. default-2- etc
void
C3DS::GetMaterialName(stChunk* Chunk)
{
char str[256];
Chunk->bytesRead += GetString(str);
stMaterial* pM = &(m_pMaterials[m_iNumMaterials-1]);
strcpy(pM->szName, str);
}
/***************************************************************************/
/*
*/
/* If theres a nested sub-chunk, and we know its ID, e.g 0xA000 etc, then
*/
/* we can simply add its ID to the switch list, and add a calling sub
*/
/* functino which will deal with it. Else just skip over that Chunk...
*/
/* and carry on parsing the rest of our file.
*/
/*
*/
/***************************************************************************/
void
C3DS::ParseChunk(stChunk* Chunk)
{
while(Chunk->bytesRead < Chunk->length)
{
stChunk tempChunk = {0};
ReadChunk(&tempChunk);
switch( tempChunk.ID)
{
// HEADER OUR ENTRY POINT
case EDIT3DS:
//0x3D3D
ParseChunk(&tempChunk);
break;
// MATERIALS
case MATERIAL:
//0xAFFF
stMaterial newMaterial;
m_pMaterials.push_back(newMaterial);
m_iNumMaterials++;
ParseChunk(&tempChunk);
break;
case MAT_NAME:
//0xA000 - sz for hte material name "e.g. default
2"
GetMaterialName(&tempChunk);
break;
case MAT_DIFFUSE:
// Diffuse Colour //0xA020
GetDiffuseColour(&tempChunk);
break;
case MAT_TEXMAP:
//0xA200 - if there's a texture wrapped to it
where here
ParseChunk(&tempChunk);
break;
case MAT_TEXFLNM:
// 0xA300 - get filename of the material
GetTexFileName(&tempChunk);
break;
// OBJECT - MESH'S
case NAMED_OBJECT:
//0x4000
{
stMesh newMesh;
m_pMeshs.push_back(newMesh);
m_iNumMeshs++;
GetMeshObjectName(&tempChunk);
}
break;
case OBJ_MESH:
//0x4100
ParseChunk(&tempChunk);
break;
case MESH_VERTICES:
//0x4110
ReadMeshVertices(&tempChunk);
break;
case MESH_FACES:
//0x4120
ReadMeshFaces(&tempChunk);
break;
case MESH_TEX_VERT:
//0x4140
ReadMeshTexCoords(&tempChunk);
break;
case MESH_MATER:
//0x4130
ReadMeshMaterials(&tempChunk);
break;
default:
SkipChunk(&tempChunk);
}
Chunk->bytesRead += tempChunk.length;
}
}
/***************************************************************************/
/*
*/
/* Read in .3ds file.
*/
/*
*/
/***************************************************************************/
bool
C3DS::Create(char* szFileName)
{
m_fp = fopen(szFileName, "rb");
stChunk Chunk = {0};
ReadChunk(&Chunk);
ParseChunk(&Chunk );
fclose(m_fp);
return true;
}
void
C3DS::Release()
{
for(int i=0;
i<m_iNumMeshs; i++)
{
delete[] m_pMeshs[i].pVerts;
delete[] m_pMeshs[i].pFaces;
delete[] m_pMeshs[i].pTexs;
}
}
// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
// DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG DEBUG
// Debugging Function to dump raw valeus to a file
void
debug_op(char *s)
{
FILE *fp;
fp
= fopen("t.txt", "a+");
fprintf(fp, "%s", s);
fclose(fp);
}
// Debugging Function - Simply put it dumps our data to a file, just to
check
// that it has read it all in correctly.
void
DisplayRawData(C3DS* pObj)
{
char buf[500];
for( int i=0;
i<pObj->m_iNumMeshs; i++ )
{
C3DS::stMesh* pMesh = &(pObj->m_pMeshs[i]);
sprintf(buf, "Shape: %s\n", pMesh->szMeshName);
debug_op(buf);
sprintf(buf, "iNumFaces: %d\n", pMesh->iNumFaces);
debug_op(buf);
for(int
cc=0; cc<pMesh->iNumFaces; cc++)
{
sprintf(buf, "\t %d, \t %d \t %d\n",
pMesh->pFaces[cc].A, pMesh->pFaces[cc].B,
pMesh->pFaces[cc].C);
debug_op(buf);
}
sprintf(buf, "iNumVertices: %d\n", pMesh->iNumVerts);
debug_op(buf);
for( cc=0; cc<pMesh->iNumVerts;
cc++)
{
sprintf(buf, "\t %.2f, \t %.2f \t %.2f\n",
pMesh->pVerts[cc].x,pMesh->pVerts[cc].y,pMesh->pVerts[cc].z);
debug_op(buf);
}
if( pMesh->pTexs != NULL )
{
sprintf(buf, "iNumTex: %d\n", pMesh->iNumVerts);
debug_op(buf);
for( cc=0; cc<pMesh->iNumVerts;
cc++)
{
sprintf(buf, "\t %.2f, \t %.2f\n",
pMesh->pTexs[cc].tu, pMesh->pTexs[cc].tv );
debug_op(buf);
}
}
if( pObj->m_iNumMaterials > 0 )
{
sprintf(buf, "Material vs Faces: %d\n", pMesh->iNumFaces);
debug_op(buf);
for( cc=0; cc<pMesh->iNumFaces;
cc++)
{
sprintf(buf, "\t MaterialID: %d",
pMesh->pFaces[cc].MaterialID );
debug_op(buf);
int ID = pMesh->pFaces[cc].MaterialID;
sprintf(buf, "\t, Name: %s\n", pObj->m_pMaterials[ID].szName);
debug_op(buf);
}
}
}
} |