// Our .3DS file that we'll use for testing.
#define
FILENAME "cube.3ds"
//#define FILENAME "multishape.3ds"
/***************************************************************************/
// These few lines are compiler directives that include the windows librarys
// during linking. You could instead inside visual studio goto Project->
// settings" menu and under the "Link" tab add the necessary librarys
instead.
#pragma
comment(lib,
"kernel32.lib")
#pragma
comment(lib,
"user32.lib")
#pragma
comment(lib,
"gdi32.lib")
#pragma
comment(lib,
"comctl32.lib")
#pragma
comment(lib,
"Advapi32.lib")
#pragma
comment(lib,
"Winmm.lib")
#include
<windows.h>
#include
<stdio.h>
// I've put the chunk defenitions in a seperate file on there own, called
// chunkdetails.h, which we'll include here.
#include
"chunkdetails.h"
#include
"data.h"
/***************************************************************************/
/*
*/
/* Write feedback information to a file - so we can understand what is
*/
/* really happening inside that .3ds file.
*/
/*
*/
/***************************************************************************/
void
abc(char *s)
{
FILE *fp;
fp
= fopen("t.txt", "a+");
fprintf(fp, "%s", s);
fclose(fp);
}
/***************************************************************************/
/*
*/
/* Some user functions to make the reading of the .3ds file easier
*/
/*
*/
/***************************************************************************/
/* Helper Functions that make our parsing of the chunks easier
*/
/***************************************************************************/
struct
stChunk
{
unsigned short
ID;
unsigned int
length;
unsigned int
bytesRead;
};
void
ReadChunk(FILE*fp, stChunk *pChunk)
{
unsigned short
ID = 0;
unsigned int
bytesRead = 0;
unsigned int
bChunkLength = 0;
bytesRead = (unsigned
int)fread(&ID, 1, 2, fp);
bytesRead += (unsigned
int)fread(&bChunkLength, 1, 4, fp);
pChunk->ID = ID;
pChunk->length = bChunkLength;
pChunk->bytesRead = bytesRead;
}
void
SkipChunk(FILE*fp, stChunk *pChunk)
{
int buffer[50000] = {0};
fread(buffer, 1, pChunk->length - pChunk->bytesRead, fp);
}
void
DisplayChunkInfo(stChunk* pChunk)
{
char buf[1000] = {0};
sprintf(buf, "Chunk ID: 0x %04x Size of Chunk: %u\n",
pChunk->ID, pChunk->length);
abc(buf);
}
/***************************************************************************/
/*
*/
/* Helper Fuction, simply reads in the string from the file pointer, until
*/
/* a null is reached, then returns how many bytes was read.
*/
/*
*/
/***************************************************************************/
int
GetString(char* pBuffer, FILE *fp)
{
int index = 0;
char buffer[100] = {0};
fread(buffer, 1, 1, fp);
while( *(buffer + index++) != 0)
{
fread(buffer + index, 1, 1, fp);
}
strcpy( pBuffer, buffer );
return (int)(strlen(buffer) + 1);
}
void
ParseChunk(stObject* pObject, FILE *fp, stChunk* Chunk);
// forward declaration
/***************************************************************************/
/*
*/
/* This little function reads the matrial data for our individual object,
*/
/* So it determines which face references which material, in our material
*/
/* list.
*/
/*
*/
/***************************************************************************/
void
ReadMeshMaterials(stObject* pObject, FILE*fp, stChunk* Chunk)
{
// Material Name Where Referencing
char str[256];
unsigned int
characterlen = GetString(str, fp);
Chunk->bytesRead += characterlen;
unsigned short
iNumFaces = 0;
Chunk->bytesRead += (unsigned
int)fread(&iNumFaces, 1, 2, fp);
unsigned short
*FaceAssignedThisMaterial = new
unsigned short[iNumFaces];
Chunk->bytesRead += (unsigned
int)fread(FaceAssignedThisMaterial, 1,
iNumFaces*sizeof(unsigned
short), fp);
// Determine Which Material It Is In Our List
int MaterialID = 0;
for( int
cc=0; cc<pObject->iNumMaterials; cc++)
{
if( strcmp( str, pObject->pMaterials[cc].szName
) == 0 )
MaterialID = cc;
}
stMesh* pMesh = &(pObject->pMeshs[pObject->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
ReadMeshFaces(stObject* pObject, FILE *fp, stChunk* Chunk)
{
unsigned int
iNumberFaces = 0; //= Chunk->length - 6;
Chunk->bytesRead += (unsigned
int)fread(&iNumberFaces, 1, 2, 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),
fp);
stMesh* pMesh = &(pObject->pMeshs[pObject->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(pObject, fp, Chunk);
}
/***************************************************************************/
/*
*/
/* You know all those x,y,z things...yup I mean vertices...well this reads
*/
/* them all in.
*/
/*
*/
/***************************************************************************/
void
ReadMeshVertices(stObject* pObject, FILE *fp, stChunk* Chunk)
{
unsigned int
iNumberVertices = 0;
Chunk->bytesRead += (unsigned
int)fread(&iNumberVertices, 1, 2, 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), fp);
stMesh* pMesh = &(pObject->pMeshs[pObject->iNumMeshs - 1]);
pMesh->pVerts = pVerts;
pMesh->iNumVerts = iNumberVertices;
SkipChunk(fp, Chunk);
}
/***************************************************************************/
/*
*/
/* Well if we have a texture, e.g. coolimage.bmp, then we need to load in
*/
/* our texture coordinates...tu and tv.
*/
/*
*/
/***************************************************************************/
void
ReadMeshTexCoords(stObject* pObject, FILE *fp, stChunk* Chunk)
{
unsigned short
iNumberVertices = 0;
Chunk->bytesRead += (unsigned
int)fread(&iNumberVertices, 1, 2, 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), fp);
stMesh* pMesh = &(pObject->pMeshs[pObject->iNumMeshs - 1]);
pMesh->pTexs = pTex;
SkipChunk(fp, 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
GetMeshObjectName(stObject* pObject, FILE *fp, 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, fp);
Chunk->bytesRead += characterlen;
stMesh* pMesh = &(pObject->pMeshs[pObject->iNumMeshs - 1]);
strcpy( pMesh->szMeshName, str );
ParseChunk(pObject, fp, Chunk);
}
// Read in our texture's file name (e.g. coolpic.jpg)
void
GetTexFileName(stObject* pObject, FILE* fp, stChunk* Chunk)
{
char str[256];
Chunk->bytesRead += GetString(str, fp);
stMaterial* pMat = &(pObject->pMaterials[pObject->iNumMaterials-1]);
strcpy( pMat->szTextureFile, str );
}
// Read in our diffuse colour (rgb)
void
GetDiffuseColour(stObject* pObject, FILE *fp, stChunk* Chunk)
{
struct stRGB{
unsigned char r, g, b; };
stRGB DiffColour;
char ChunkHeader[6];
Chunk->bytesRead += (unsigned
int)fread(ChunkHeader, 1, 6, fp);
Chunk->bytesRead += (unsigned
int)fread(&DiffColour, 1, 3, fp);
stMaterial* pM = &(pObject->pMaterials[pObject->iNumMaterials-1]);
pM->Colour.r
= DiffColour.r;
pM->Colour.g
= DiffColour.g;
pM->Colour.b
= DiffColour.b;
SkipChunk(fp, Chunk);
}
// Get the materials name, e.g. default-2- etc
void
GetMaterialName(stObject* pObject, FILE *fp, stChunk* Chunk)
{
char str[256];
Chunk->bytesRead += GetString(str, fp);
stMaterial* pM = &(pObject->pMaterials[pObject->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
ParseChunk(stObject* pObject, FILE *fp, stChunk* Chunk)
{
while(Chunk->bytesRead < Chunk->length)
{
stChunk tempChunk = {0};
ReadChunk(fp, &tempChunk);
switch( tempChunk.ID)
{
// HEADER OUR ENTRY POINT
case EDIT3DS:
//0x3D3D
ParseChunk(pObject, fp, &tempChunk);
break;
// MATERIALS
case MATERIAL:
//0xAFFF
stMaterial newMaterial;
pObject->pMaterials.push_back(newMaterial);
pObject->iNumMaterials++;
ParseChunk(pObject, fp, &tempChunk);
break;
case MAT_NAME:
//0xA000 - sz for hte material name "e.g. default
2"
GetMaterialName(pObject, fp, &tempChunk);
break;
case MAT_DIFFUSE:
// Diffuse Colour //0xA020
GetDiffuseColour(pObject, fp, &tempChunk);
break;
case MAT_TEXMAP:
//0xA200 - if there's a texture wrapped to it
where here
ParseChunk(pObject, fp, &tempChunk);
break;
case MAT_TEXFLNM:
// 0xA300 - get filename of the material
GetTexFileName(pObject, fp, &tempChunk);
break;
// OBJECT - MESH'S
case NAMED_OBJECT:
//0x4000
{
stMesh newMesh;
pObject->pMeshs.push_back(newMesh);
pObject->iNumMeshs++;
GetMeshObjectName(pObject, fp, &tempChunk);
}
break;
case OBJ_MESH:
//0x4100
ParseChunk(pObject, fp, &tempChunk);
break;
case MESH_VERTICES:
//0x4110
ReadMeshVertices(pObject, fp, &tempChunk);
break;
case MESH_FACES:
//0x4120
ReadMeshFaces(pObject, fp, &tempChunk);
break;
case MESH_TEX_VERT:
//0x4140
ReadMeshTexCoords(pObject, fp, &tempChunk);
break;
case MESH_MATER:
//0x4130
ReadMeshMaterials(pObject, fp, &tempChunk);
break;
default:
SkipChunk(fp, &tempChunk);
}
Chunk->bytesRead += tempChunk.length;
}
}
/***************************************************************************/
/*
*/
/* Read in .3ds file.
*/
/*
*/
/***************************************************************************/
void
read3ds(stObject* pObject)
{
FILE* pFile;
pFile = fopen(FILENAME, "rb");
stChunk Chunk = {0};
ReadChunk(pFile, &Chunk);
ParseChunk(pObject, pFile, &Chunk );
fclose(pFile);
}
// Debugging Function - Simply put it dumps our data to a file, just to
check
// that it has read it all in correctly.
void
display_raw_data(stObject* pObj)
{
char buf[500];
for( int i=0;
i<pObj->iNumMeshs; i++ )
{
stMesh* pMesh = &(pObj->pMeshs[i]);
sprintf(buf, "Shape: %s\n", pMesh->szMeshName);
abc(buf);
sprintf(buf, "iNumFaces: %d\n", pMesh->iNumFaces);
abc(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);
abc(buf);
}
sprintf(buf, "iNumVertices: %d\n", pMesh->iNumVerts);
abc(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);
abc(buf);
}
if( pMesh->pTexs != NULL )
{
sprintf(buf, "iNumTex: %d\n", pMesh->iNumVerts);
abc(buf);
for( cc=0; cc<pMesh->iNumVerts;
cc++)
{
sprintf(buf, "\t %.2f, \t %.2f\n",
pMesh->pTexs[cc].tu, pMesh->pTexs[cc].tv );
abc(buf);
}
}
if( pObj->iNumMaterials > 0 )
{
sprintf(buf, "Material vs Faces: %d\n", pMesh->iNumFaces);
abc(buf);
for( cc=0; cc<pMesh->iNumFaces;
cc++)
{
sprintf(buf, "\t MaterialID: %d",
pMesh->pFaces[cc].MaterialID );
abc(buf);
int ID = pMesh->pFaces[cc].MaterialID;
sprintf(buf, "\t, Name: %s\n", pObj->pMaterials[ID].szName);
abc(buf);
}
}
}
}
/***************************************************************************/
/*
*/
/* The program entry point, this is where we start and end.
*/
/*
*/
/***************************************************************************/
int
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR,
int)
{
stObject myobj;
read3ds( &myobj );
abc("================================================================\n");
display_raw_data( &myobj );
for(int i=0;
i<myobj.iNumMeshs; i++)
{
delete[] myobj.pMeshs[i].pVerts;
delete[] myobj.pMeshs[i].pFaces;
delete[] myobj.pMeshs[i].pTexs;
}
myobj.pMaterials.empty();
return 0;
} |