www.xbdev.net
xbdev - software development
Tuesday January 21, 2025
Home | Contact | Support | 3D Formats Where else are you going to keep your 3D data?...
     
 

3D Formats

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

 

3DS Part 5- "storing xyz!" - Working Loader

by bkenwright@xbdev.net

<code v8>

Well we can now load all our 3D data in, and it will be all nice and safe in our data containers.  Data containers?...let me show you how and why....

 

Also note, I put our definition for our data structures in a file called data.h, so it doesn't make our code all messy :)

 

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

/*                                                                         */

/* File:  data.h                                                           */

/* Author bkenwright@xbdev.net                                             */

/*                                                                         */

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

 

// Well we need to load in and store our 3D file information, so we need to

// define some structures to put them in.

 

// Now the tricky thing to remember is that we could have multiple objects,

// so our data structure could grow dynamically as where loading.

 

// Our only solution is linked lists... now we could write our own, or use

// the STL libraries.  I've chosen to use the STL libraries...but you could

// always work around them.

 

#include <vector>

using namespace std ;

 

// Its a beautiful thing the <vector> STL...its really easy to use and understand

// <1> you declare it at the top ..then

/*

Its like using an array...

so for example you'd do

vector<int> pOurVector;

 

When you want to add a new int to your array of int's you'd do

int pp = 2;

pOurVector->push_back(pp);

To use our array vector, we use it like do an array

pOurVector[0] = 2;

When we've finished with it, we can just delete it.

delete[] pOurVector

*/

struct stMaterial

{

      char szName[256];

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

      char szTextureFile[256];

};

 

struct stVert

{

      float x, y, z;

};

struct stFace

{   // 3 Sides of a triangle make a face.

      unsigned int A, B, C;

      int MaterialID;

};

struct stTex

{

      float tu, tv;

};

 

struct stMesh

{

      char         szMeshName[256];

      int          iNumVerts;

      stVert*      pVerts;

      int          iNumFaces;

      stFace*      pFaces;

      stTex*       pTexs;

 

      stMesh()

      {

            iNumVerts  = 0;

            pVerts     = NULL;

            iNumFaces  = 0;

            pFaces     = NULL;

            pTexs      = NULL;

      }

     

};

 

struct stObject

{

      int                iNumMeshs;

      vector<stMesh>     pMeshs;

 

      int                iNumMaterials;

      vector<stMaterial> pMaterials;

 

      stObject()

      {

            iNumMeshs     = 0;

            iNumMaterials = 0;

      };

};

 

 

Well the code is C - function depended at present...which means that we havn't used any fancy C++ objects or classes yet...just incase your not to good with object orientated programming.  But hold on...we will be going down that road eventually.

 

 

// 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;

}

 

 

 

And of course the we have created an output file, which verifies our data.

 

================================================================

Shape: Box03

iNumFaces: 12

             0,         2          3

             3,         1          0

             4,         5          7

             7,         6          4

             8,         9          10

             11,       12        13

             14,       15        16

             17,       18        19

             20,       21        22

             23,       24        25

             26,       27        28

             29,       30        31

iNumVertices: 32

             -6.79,  -2.30    0.00

             -2.93,  -2.30    0.00

             -6.79,  5.29     0.00

             -2.93,  5.29     0.00

             -6.79,  -2.30    4.13

             -2.93,  -2.30    4.13

             -6.79,  5.29     4.13

             -2.93,  5.29     4.13

             -6.79,  -2.30    0.00

             -2.93,  -2.30    0.00

             -2.93,  -2.30    4.13

             -2.93,  -2.30    4.13

             -6.79,  -2.30    4.13

             -6.79,  -2.30    0.00

             -2.93,  -2.30    0.00

             -2.93,  5.29     0.00

             -2.93,  5.29     4.13

             -2.93,  5.29     4.13

             -2.93,  -2.30    4.13

             -2.93,  -2.30    0.00

             -2.93,  5.29     0.00

             -6.79,  5.29     0.00

             -6.79,  5.29     4.13

             -6.79,  5.29     4.13

             -2.93,  5.29     4.13

             -2.93,  5.29     0.00

             -6.79,  5.29     0.00

             -6.79,  -2.30    0.00

             -6.79,  -2.30    4.13

             -6.79,  -2.30    4.13

             -6.79,  5.29     4.13

             -6.79,  5.29     0.00

iNumTex: 32

             1.00,    0.00

             0.00,    0.00

             1.00,    1.00

             0.00,    1.00

             0.00,    0.00

             1.00,    0.00

             0.00,    1.00

             1.00,    1.00

             0.00,    0.00

             1.00,    0.00

             1.00,    1.00

             1.00,    1.00

             0.00,    1.00

             0.00,    0.00

             0.00,    0.00

             1.00,    0.00

             1.00,    1.00

             1.00,    1.00

             0.00,    1.00

             0.00,    0.00

             0.00,    0.00

             1.00,    0.00

             1.00,    1.00

             1.00,    1.00

             0.00,    1.00

             0.00,    0.00

             0.00,    0.00

             1.00,    0.00

             1.00,    1.00

             1.00,    1.00

             0.00,    1.00

             0.00,    0.00

Material vs Faces: 12

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

             MaterialID: 0   , Name: 8 - Default

 

 

 

 

 

 

 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.