iDevGames Forums
3ds Loader Woes - Printable Version

+- iDevGames Forums (http://www.idevgames.com/forums)
+-- Forum: Development Zone (/forum-3.html)
+--- Forum: Game Programming Fundamentals (/forum-7.html)
+--- Thread: 3ds Loader Woes (/thread-5701.html)



3ds Loader Woes - Nick - Mar 28, 2005 05:20 PM

I'm trying my hand at .3ds loading so I can get some animations as well as increasing my knowledge and what have you. For some reason, the program never reads the vertices. Here's all my code for loading it. Is there something obviously wrong or is it a corrupted .3ds file or what?

BTW, the ConvertEndian functions and FileLength function are from a tutorial showing how to read the file. I'm not sure if they are necessary but I don't know yet how to read binary files. Please let me know.

Code:
float ConvertEndianf(float infloat)
{
    union floatunion { unsigned char part[4]; float float_value; } Converter;
    char swap_char;
    
    Converter.float_value = infloat;
    swap_char = Converter.part[0]; Converter.part[0] = Converter.part[3]; Converter.part[3] = swap_char;
    swap_char = Converter.part[1]; Converter.part[1] = Converter.part[2]; Converter.part[2] = swap_char;
    
    return Converter.float_value;
}

unsigned long ConvertEndianul(unsigned long inlong)
{
    union longunion { unsigned char part[4]; unsigned long long_value; } Converter;
    char swap_char;
    
    Converter.long_value = inlong;
    swap_char = Converter.part[0]; Converter.part[0] = Converter.part[3]; Converter.part[3] = swap_char;
    swap_char = Converter.part[1]; Converter.part[1] = Converter.part[2]; Converter.part[2] = swap_char;
    
    return Converter.long_value;
}

unsigned short ConvertEndianus(unsigned short inshort)
{
    union shortunion { unsigned char part[2]; unsigned short short_value; } Converter;
    char swap_char;
    
    Converter.short_value = inshort;
    swap_char = Converter.part[0]; Converter.part[0] = Converter.part[1]; Converter.part[1] = swap_char;
    
    return Converter.short_value;
}

long FileLength(int file)
{
    struct stat file_buffer;
    fstat(file, &file_buffer);
    return(file_buffer.st_size);
}

bool Load3dsMesh(Mesh &theMesh, const char *filename, TextureImage &ti)
{
    FILE                *modelFile;
    char                tempchar[255] = {};
    Vector                tempVertex;
    Face                tempFace;
    TextureCoordinates    tempTC;
    GLfloat                x,y,z;
    int                    j,k,l;
    int                    i;
    unsigned short        chunkId;
    unsigned long        chunkLength;
    unsigned char        character;
    unsigned short        qty;
    unsigned short        faceFlags;
    
    strcat(tempchar,"The City.app/Contents/Resources/Models/3ds/");
    strcat(tempchar,filename);
    
    if((modelFile = fopen(tempchar, "rb"))==NULL)        //if the file did not load
    {
        cout << "Failed to open " << filename << ". Model not loaded." << endl;
        return false;
    }
    
    cout << "Loading model " << filename << "..." << endl;
    
    theMesh.SetFilename(filename);
    
    while(ftell(modelFile)<FileLength(fileno(modelFile)))
    {
        fread(&chunkId, 2, 1, modelFile);
        chunkId = ConvertEndianus(chunkId);
        printf("ChunkId: %x\n",chunkId);
        fread(&chunkLength, 4, 1, modelFile);
        chunkLength = ConvertEndianul(chunkLength);
        
        switch(chunkId)
        {
            case 0x4d4d :
                break;
                
            case 0x3d3d :
                break;
                
            case 0x4000 :
                i = 0;
                do
                {
                    fread(&character, 1, 1, modelFile);
                    tempchar[i] = character;
                    i++;
                } while(character!='\0' && i<20);
                    break;
                
            case 0x4110 :
                fread(&qty, sizeof(unsigned short), 1, modelFile);
                qty = ConvertEndianus(qty);
                theMesh.SetNV(qty);
                for(i=0; i<qty; i++)
                {
                    fread(&x, sizeof(float), 1, modelFile);
                    fread(&y, sizeof(float), 1, modelFile);
                    fread(&z, sizeof(float), 1, modelFile);
                    x = ConvertEndianf(x);
                    y = ConvertEndianf(y);
                    z = ConvertEndianf(z);
                    tempVertex.Init(x,y,z);
                    tempVertex.Output();
                    theMesh.SetV(i,tempVertex);
                }
                    break;
                
            case 0x4120 :
                fread(&qty, sizeof(unsigned short), 1, modelFile);
                qty = ConvertEndianus(qty);
                theMesh.SetNF(qty);
                for(i=0; i<qty; i++)
                {
                    fread(&j, sizeof(unsigned short), 1, modelFile);
                    fread(&k, sizeof(unsigned short), 1, modelFile);
                    fread(&l, sizeof(unsigned short), 1, modelFile);
                    fread(&faceFlags, sizeof(unsigned short), 1, modelFile);
                    j = ConvertEndianus(j);
                    k = ConvertEndianus(k);
                    l = ConvertEndianus(l);
                    tempFace.SetVI(0,j-1);
                    tempFace.SetVI(1,k-1);
                    tempFace.SetVI(2,l-1);
                    tempFace.SetNV(3);
                    theMesh.SetF(i,tempFace);
                }
                    break;
                
            case 0x4140 :
                fread(&qty, sizeof(unsigned short), 1, modelFile);
                qty = ConvertEndianus(qty);
                for(i=0; i<qty; i++)
                {
                    fread(&x, sizeof(float), 1, modelFile);
                    fread(&y, sizeof(float), 1, modelFile);
                    x = ConvertEndianf(x);
                    y = ConvertEndianf(y);
                    tempTC.Init(x,y);
                    theMesh.SetTC(i,tempTC);
                }
                    break;
                
            default :
                fseek(modelFile, chunkLength-6, SEEK_CUR);
        }
    }
    
    fclose(modelFile);                
    
    Vector bCenter(0,0,0);
    for(int i=0; i<theMesh.GetNV(); i++)
    {
        bCenter = bCenter + theMesh.GetV(i);
    }
    bCenter = bCenter / theMesh.GetNV();
    
    theMesh.SetTranslate(bCenter);
    
    bCenter = bCenter - (bCenter * 2);
    
    for(int i=0; i<theMesh.GetNV(); i++)
    {
        theMesh.AddV(i,bCenter);
    }
    
    return true;
}



3ds Loader Woes - kberg - Mar 28, 2005 11:58 PM

The whole thing seems to be structured a little differently then most of the 3ds tutorials I've looked over myself. The endian swap functions are necessary, but the file length is probably not...

The portion of that code responsible for loading vertices is of course case 0x4110, and the logic there looks ok to me. One thing I notice is that vertex indices for 3ds files with multiple object chunks might be incorrect. There is quite a bit of code involved, which makes it difficult to go over everything in detail, but I'll post some rough code I have for loading vertex chunks out of a 3ds file, and you can take what you want from it.

Code:
#define Endian32_Swap(value) \
      ( ((((UInt32)value)<<24) & 0xFF000000) | \
        ((((UInt32)value)<< 8) & 0x00FF0000) | \
        ((((UInt32)value)>> 8) & 0x0000FF00) | \
        ((((UInt32)value)>>24) & 0x000000FF) )

#define Endian16_Swap(value) \
      ( ((((UInt16)value)<< 8) & 0xFF00) | \
        ((((UInt16)value)>> 8) & 0x00FF) )

struct Max_Chunk
{
   unsigned short type;
   unsigned int   leng;
   unsigned int   read;
};

void max_read_chunk(FILE *in_file, Max_Chunk *dst)
{
   dst->read  = fread(&dst->type, 1, 2, in_file);
   dst->type  = Endian16_Swap(dst->type);
   dst->read += fread(&dst->leng, 1, 4, in_file);
   dst->leng  = Endian32_Swap(dst->leng);
}

void max_read_verts(TriMesh *theMesh, FILE *in_file, unsigned char *buf, Max_Chunk *prev_chunk)
{
   vec3 posn;
   unsigned short num_verts;
   prev_chunk->read += fread(&num_verts, 1, 2, in_file);
   num_verts = Endian16_Swap(num_verts);
   prev_chunk->read += fread(buf, 1, prev_chunk->leng - prev_chunk->read, in_file);

   unsigned int *buff = (unsigned int*)buf;
   unsigned int *swap = (unsigned int*)malloc(sizeof(float) * num_verts * 3);
   for (int i = 0; i < num_verts; i++)
   {
      int id = i * 3;
      swap[id + 0] = Endian32_Swap(buff[id + 0]);
      swap[id + 1] = Endian32_Swap(buff[id + 1]);
      swap[id + 2] = Endian32_Swap(buff[id + 2]);
   }

   float *verts = (float*)swap;
   for (int i = 0; i < num_verts; i++)
   {
      int id = i * 3;
      Vec3_set(posn, verts[id + 0], verts[id + 1], verts[id + 2]);
      theMesh->AddVert(posn, 0.0f, 0.0f);  // (vec3: position, float: u value, float: v value)
   }

   free(swap);
}

bool max_proc_obj(TriMesh *theMesh, FILE *in_file, unsigned char *buf, Max_Chunk *prev_chunk)
{
   Max_Chunk *next_chunk = (Max_Chunk*)malloc(sizeof(Max_Chunk));

   while (prev_chunk->read < prev_chunk->leng)
   {
      max_read_chunk(in_file, next_chunk);
      if (next_chunk->read == 0)
      {
         free(next_chunk);
         printf("Error!  Unexpected end of file.\n");
         return false;
      }

      switch (next_chunk->type)
      {
         case OBJTRIMESH:
            if (!max_proc_obj(theMesh, in_file, buf, next_chunk))
            {
               free(next_chunk);
               return false;
            }
            break;
         case TRIVERT:
            max_read_verts(theMesh, in_file, buf, next_chunk);
            break;
         case TRIUVWP:
            max_read_uvwps(theMesh, in_file, buf, next_chunk);
            break;
         case TRIFACE:
            max_read_faces(theMesh, in_file, buf, next_chunk);
            break;
         default:
            next_chunk->read += fread(buf, 1, next_chunk->leng - next_chunk->read, in_file);
            break;
      }
      prev_chunk->read += next_chunk->read;
   }

   free(next_chunk);
   next_chunk = prev_chunk;

   return true;
}

bool max_proc_chunk(Scene& theScene, FILE *in_file, unsigned char *buf, Max_Chunk *prev_chunk)
{
   Max_Chunk *next_chunk = (Max_Chunk*)malloc(sizeof(Max_Chunk));
   unsigned short vers = 0;
   TriMesh  *theMesh = NULL;

   while (prev_chunk->read < prev_chunk->leng)
   {
      max_read_chunk(in_file, next_chunk);
      if (next_chunk->read == 0)
      {
         free(next_chunk);
         printf("Error!  Unexpected end of file.\n");
         return false;
      }

      switch (next_chunk->type)
      {
         case VERSION:
            next_chunk->read += fread(buf, 1, next_chunk->leng - next_chunk->read, in_file);
            vers = Endian16_Swap(*(unsigned short*)buf);
            if (vers > 3)
            {
               free(next_chunk);
               printf("Error!  File version incompatibility.\n");
               return false;
            }
            break;
//         case EDITMATERIAL:
//            if (!max_proc_mat(theScene, in_file, buf, next_chunk))
//            {
//               free(next_chunk);
//               return false;
//            }
//            break;
         case MESHVERSION:
            next_chunk->read += fread(buf, 1, next_chunk->leng - next_chunk->read, in_file);
            vers = Endian16_Swap(*(unsigned short*)buf);
            if (vers > 3)
            {
               free(next_chunk);
               printf("Error!  File version incompatibility.\n");
               return false;
            }
            break;
         case EDIT3DS:
            if (!max_proc_chunk(theScene, in_file, buf, next_chunk))
            {
               free(next_chunk);
               printf("embedded chunk call failed!\n");
               return false;
            }
            break;
         case EDITOBJECT:
            next_chunk->read += max_read_string(in_file, buf);
            theMesh = new(TriMesh);
            strncpy(theMesh->GetNamePtr(), (const char*)buf, strlen((const char*)buf) + 1);

            if (!max_proc_obj(theMesh, in_file, buf, next_chunk))
            {
               free(next_chunk);
               printf("embedded object chunk call failed!\n");
               return false;
            }
            theMesh->CalcNorms();
            theScene.AddShape(theMesh);
            break;
         case EDITKEYFRAME:
            if (!max_proc_key(theScene, in_file, buf, next_chunk))
            {
               free(next_chunk);
               return false;
            }
            break;
         default:
            next_chunk->read += fread(buf, 1, next_chunk->leng - next_chunk->read, in_file);
            break;
      }
      prev_chunk->read += next_chunk->read;
   }
   free(next_chunk);
   next_chunk = prev_chunk;

   return true;
}

bool max_ParseFile(Scene& theScene, char *filename)
{
   Max_Chunk *next_chunk;
   FILE      *in_file;
   unsigned char *buf = NULL;

   next_chunk = (Max_Chunk*)malloc(sizeof(Max_Chunk));
   max_init_chunk(next_chunk);

   printf("Loading model %s: ", filename);
   in_file = fopen(filename, "rt");
   if (in_file == NULL)
   {
      printf("error, file not found!\n");
      return false;
   }
   else
      printf("done.\n");

   max_read_chunk(in_file, next_chunk);
   if (next_chunk->type != PRIMARY)
   {
      printf("error, file is damaged!\n");
      fclose(in_file);
      return false;
   }

   buf = (unsigned char*)malloc(sizeof(float) * 196608);
   if (!max_proc_chunk(theScene, in_file, buf, next_chunk))
   {
      printf("error, unknown error encountered while loading file!\n");
      fclose(in_file);
      free(buf);
      return false;
   }

   fclose(in_file);
   free(next_chunk);
   free(buf);

   return true;
}



3ds Loader Woes - elerion - Jul 12, 2005 05:01 AM

Your problem lies probably here:

"fread(&x, sizeof(float), 1, modelFile);
fread(&y, sizeof(float), 1, modelFile);
fread(&z, sizeof(float), 1, modelFile);
x = ConvertEndianf(x);
y = ConvertEndianf(y);
z = ConvertEndianf(z);"

3D Studio Max models have the z-axis pointing up, so you should have something like this:

"fread(&x, sizeof(float), 1, modelFile);
fread(&z, sizeof(float), 1, modelFile); // first z
fread(&y, sizeof(float), 1, modelFile); // then y
x = ConvertEndianf(x);
y = ConvertEndianf(y);
z = -ConvertEndianf(z);" // and invert z so all is correct again

otherwise the normals might be reversed


3ds Loader Woes - demonpants - Apr 1, 2009 02:22 PM

Any chance you had any progress with this / would you be willing to share it?