www.xbdev.net xbdev - software development
Friday December 14, 2018
home | about | contact | Donations

     
 

3D Formats

Where else are you going to keep your 3D data?...

 

3DS Part 4- "wrapping it all up" - Tidying it all up

by bkenwright@xbdev.net

<code v7>

Well we've gone over how it all works, and created a pretty nice 3ds file dumper program, which allows us to see the data in our 3d file.  We could even modify it to convert our 3ds file to another format!  But what we want now, is to load all our data in, and keep it saved, so we can use it somewhere....in a game or a plugin for some app etc.

 

Code tidy up time!.. hopefully you should have grasped the 3ds file format by now?  As its made up of chunks, and each chunk, even sub-chunks has a unique id number.... SOOoooooo.. we could do a chunkparser(..) function.... so instead of parsing the chunks all over the place, we can do it in one place.

So if we know a chunk has a sub-chunk containing data, we just call the ParseChunk(..) function, and add another case statement to the switch statement in the parser function, and then add in a function that extracts the data.

This means with practically little effort we could keep adding to our code for further updates with little problem :)

Of course it does use Recursion.... a function calling itself...but as long as it doens't got to deep we should be okay.

 

 

// Our .3DS file that we'll use for testing.

#define FILENAME "cube.3ds"

  

/***************************************************************************/

 

#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"

 

/***************************************************************************/

/*                                                                         */

/* 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 = fread(&ID, 1, 2, fp);

 

      bytesRead += 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(FILE *fp)

{

      int index = 0;

 

      char buffer[100] = {0};

 

      fread(buffer, 1, 1, fp);

 

      while( *(buffer + index++) != 0)

      {

            fread(buffer + index, 1, 1, fp);

      }

      abc(buffer);

      abc("\n");

 

      return (int)(strlen(buffer) + 1);

}

 

 

void ParseChunk(FILE *fp, stChunk* Chunk);  // forward declaration

int GetString(FILE* fp);                    // 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(FILE*fp, stChunk* Chunk)

{

      abc("\n");

      // Material Name Where Referencing

      unsigned int characterlen = GetString(fp);

      Chunk->bytesRead += characterlen;

 

      unsigned short iNumFaces = 0;

      Chunk->bytesRead += fread(&iNumFaces, 1, 2, fp);

 

      unsigned short *FaceAssignedThisMaterial = new unsigned short[iNumFaces];

      Chunk->bytesRead += fread(FaceAssignedThisMaterial, 1,

                                                      iNumFaces*sizeof(unsigned short), fp);

 

      char buf[1000] = {0};

      sprintf(buf, "Material: FaceCount: %d\n",iNumFaces);

      abc(buf);

 

      for( int i=0; i<iNumFaces; i++ )

      {

            sprintf(buf, "\tFaceAssignedThisMaterial: %d\n",FaceAssignedThisMaterial[i]);

            abc(buf);

      }

 

      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(FILE *fp, stChunk* Chunk)

{

      unsigned int iNumberFaces = 0; //= Chunk->length - 6;

      Chunk->bytesRead += fread(&iNumberFaces, 1, 2, fp);  

 

      char buff[100];

      sprintf(buff, "Number of Faces %u\n", iNumberFaces);

      abc(buff);

 

      // Each face is 3 points A TRIANGLE!..WOW

      struct stFace{ unsigned short p1, p2, p3, visibityflag; };

      stFace *pFaces = new stFace[iNumberFaces];

 

      Chunk->bytesRead += fread(pFaces, 1, iNumberFaces*sizeof(stFace), fp);

 

      for( int i=0; i<iNumberFaces; i++ )

      {

            sprintf(buff, "\t Face:  Index1: %d, Index2: %d, Index3 %d\n",

                                                    pFaces[i].p1, pFaces[i].p2, pFaces[i].p3);

            abc(buff);

      }

 

      delete pFaces;

 

      // Our face material information is a sub-chunk.

      ParseChunk(fp, Chunk);

}

 

/***************************************************************************/

/*                                                                         */

/* You know all those x,y,z things...yup I mean vertices...well this reads */

/* them all in.                                                            */

/*                                                                         */

/***************************************************************************/

void ReadMeshVertices(FILE *fp, stChunk* Chunk)

{

      unsigned int iNumberVertices = 0;

      Chunk->bytesRead += fread(&iNumberVertices, 1, 2, fp);     

 

      // e.g.  8 Vertices make up our simple box!

      char buff[300];

      sprintf(buff, "Number of Vertices %d\n", iNumberVertices);

      abc(buff);

 

      // Allocate Memory and dump our vertices to the screen.

      struct stVect{ float x, y, z; };

      stVect *pVerts = new stVect[iNumberVertices];

 

      Chunk->bytesRead += fread( (void*)pVerts, 1, iNumberVertices*sizeof(stVect), fp);

 

      for(int i=0; i<iNumberVertices; i++)

      {

            sprintf(buff, "\t Vertices Point: x: %.2f, \ty: %.2f, \tz: %.2f\n",

                                                       pVerts[i].x, pVerts[i].y, pVerts[i].z);

            abc(buff);

      }

 

      delete[] pVerts;

     

      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(FILE *fp, stChunk* Chunk)

{

      unsigned short iNumberVertices = 0;

      Chunk->bytesRead += fread(&iNumberVertices, 1, 2, fp);     

 

      // e.g.  8 Vertices make up our simple box!

      char buff[300];

      sprintf(buff, "Number of Vertices %d\n", iNumberVertices);

      abc(buff);

 

      // Allocate Memory and dump our texture for each vertice to the screen.

      struct stTex{ float tu, tv; };

      stTex *pTex = new stTex[iNumberVertices];

 

      Chunk->bytesRead += fread( (void*)pTex, 1, iNumberVertices*sizeof(stTex), fp);

 

      for(int i=0; i<iNumberVertices; i++)

      {

            sprintf(buff, "\t Tex Coord: tu: %.2f,  \t tv: %.2f\n",

                                                       pTex[i].tu, pTex[i].tv);

            abc(buff);

      }

 

      delete[] 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(FILE *fp, stChunk* Chunk)

{

      abc("\n");

      // The strange thing is, the next few parts of this chunk represent

      // the name of the object.  Then we start chunking again.

      unsigned int characterlen = GetString(fp);

      Chunk->bytesRead += characterlen;

 

      ParseChunk(fp, Chunk);

}

 

// Read in our texture's file name (e.g. coolpic.jpg)

void GetTexFileName(FILE* fp, stChunk* Chunk)

{

      Chunk->bytesRead += GetString(fp);

}

// Read in our diffuse colour (rgb)

void GetDiffuseColour(FILE *fp, stChunk* Chunk)

{

      abc("\n");

     

      struct stRGB{ unsigned char r, g, b; };

      stRGB DiffColour;

 

      char ChunkHeader[6];

      Chunk->bytesRead += fread(ChunkHeader, 1, 6, fp);

 

      Chunk->bytesRead += fread(&DiffColour, 1, 3, fp);    

 

      // e.g.  Display our diffuse colour we read in

      char buff[300];

      sprintf(buff, "Diffuse Colour\t r: %x   g: %x    b: %x\n",

                                                DiffColour.r, DiffColour.g, DiffColour.b);

      abc(buff);

 

      SkipChunk(fp, Chunk);

}

// Get the materials name, e.g. default-2- etc

void GetMaterialName(FILE *fp, stChunk* Chunk)

{

      Chunk->bytesRead += GetString(fp);

}

/***************************************************************************/

/*                                                                         */

/* 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(FILE *fp, stChunk* Chunk)

{

      abc("\n");

      while(Chunk->bytesRead < Chunk->length)

      {

            stChunk tempChunk = {0};

            ReadChunk(fp, &tempChunk);

            DisplayChunkInfo(&tempChunk);

           

            switch( tempChunk.ID)

            {

            // HEADER OUR ENTRY POINT

            case EDIT3DS:

                  ParseChunk(fp, &tempChunk);

                  break;

            // MATERIALS

            case MATERIAL: //0xAFFF

                  ParseChunk(fp, &tempChunk);

                  break;

            case MAT_NAME: //0xA000 - sz for hte material name "e.g. default 2"

                  GetMaterialName(fp, &tempChunk);

                  break;

            case MAT_DIFFUSE:  // Diffuse Colour  //0xA020

                  GetDiffuseColour(fp, &tempChunk);

                  break;

            case MAT_TEXMAP:  //0xA200 - if there's a texture wrapped to it where here

                  ParseChunk(fp, &tempChunk);

                  break;

            case MAT_TEXFLNM: // 0xA300 -  get filename of the material

                  GetTexFileName(fp, &tempChunk);

                  break;

 

            // OBJECT - MESH'S

            case NAMED_OBJECT: //0x4000

                  GetMeshObjectName(fp, &tempChunk);

                  break;

            case OBJ_MESH:     //0x4100

                  ParseChunk(fp, &tempChunk);

                  break;

            case MESH_VERTICES: //0x4110

                  ReadMeshVertices(fp, &tempChunk);

                  break;

            case MESH_FACES: //0x4120

                  ReadMeshFaces(fp, &tempChunk);

                  break;

            case MESH_TEX_VERT: //0x4140

                  ReadMeshTexCoords(fp, &tempChunk);

                  break;

            case MESH_MATER: //0x4130

                  ReadMeshMaterials(fp, &tempChunk);

                  break;

 

            default:

                  SkipChunk(fp, &tempChunk);

            }

           

            Chunk->bytesRead += tempChunk.length;

      }

}

 

/***************************************************************************/

/*                                                                         */

/* Our 3DS File Reader Entry Point - Lets read that baby in and show it    */

/* who's boss".                                                            */

/*                                                                         */

/***************************************************************************/

 

void read3ds()

{

      FILE* pFile;

      pFile = fopen(FILENAME, "rb");

     

      stChunk Chunk = {0};

      ReadChunk(pFile, &Chunk);

 

      ParseChunk( pFile, &Chunk );

     

 

      fclose(pFile);

}

 

/***************************************************************************/

/*                                                                         */

/* The program entry point, this is where we start and end.                */

/*                                                                         */

/***************************************************************************/

 

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)

{

      read3ds();

 

      return 0;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 Visitor: 9534626  { 209.237.238.175 } Copyright (c) 2002-2017 xbdev.net - All rights reserved.
Designated tutorial and software are the property of their respective owners.