// Copyright (c) 2000. Aanand Narayanan.P.P // For questions email digicrush_ii@rediff.com // Reads a .3ds file and create a linked list of objects #include #define D3DOVERLOADS #include typedef struct _map_list { char filename[256]; // Mapping filename (Texture) float u; // U scale float v; // V scale float uoff; // U Offset float voff; // V Offset float rot; // Rotation angle _map_list *next; }map_list; typedef struct _mat_list { char name[200]; // Material name DWORD ambient; // Ambient color (RGBA) DWORD diffuse; // Diffuse color (RGBA) DWORD specular; // Specular color (RGBA) _map_list *htex, *ttex; // Texture maps (presently only 1 is used. diffused map) _mat_list *next; }mat_list; typedef struct _face_mat { char name[200];// Material Name WORD ne; // No. of entries WORD *faces; // Faces assigned to this material _face_mat *next; }face_mat; typedef struct _mesh_object { char name[200]; // Object name float *vlst; // Vertex list WORD *flst; // Face list WORD nv; // No. of vertices WORD nf; // No. of faces WORD mnv; // No. of vertices having mapping coords. float *mc; // Mapping coords. as U,V pairs (actual texture coordinates) float lmat[4][4]; // Local transformation matrix _mesh_object *next; // Pointer to the next object face_mat *fhead, *ftail; }mesh_object; char temp_name [100]; float trans_mat [4][4]; // translation matrix for objects FILE *bin3ds; mesh_object *head=NULL, *tail=NULL; mat_list *mathead=NULL, *mattail=NULL; map_list *maphead=NULL, *maptail=NULL; void ReadObject(); unsigned char ReadChar (void) { return (fgetc (bin3ds)); } unsigned int ReadInt (void) { unsigned int temp = ReadChar(); return ( temp | (ReadChar () << 8)); // I really don't know Y I'm do'n this, too lazy to change } unsigned long ReadLong (void) { unsigned long temp1,temp2; temp1=ReadInt (); temp2=ReadInt (); return (temp1 | (temp2 << 16)); // same as above } void read_mat(DWORD len) { unsigned long count=ftell(bin3ds) + (len - 6); WORD id; DWORD llen; int done = 0; BOOL is_ambient = FALSE; BOOL is_diffuse = FALSE; BOOL is_specular = FALSE; // Allocate a new material if(mathead == NULL) { mathead = new mat_list; mattail = mathead; } else { mattail->next = new mat_list; mattail = mattail->next; } mattail->next = NULL; mattail->htex = NULL; while(!done) { id = ReadInt(); if(feof(bin3ds)) // OOPS! EOF { done = 1; break; } llen = ReadLong(); switch(id) { case 0xA000: { int i=0; char ch; mattail->next = NULL; // Read material name while((ch = fgetc(bin3ds)) != 0) { mattail->name[i] = ch; i++; } mattail->name[i] = '\0'; }break; case 0xA010: { // Hey! AMBIENT is_diffuse = FALSE; is_specular = FALSE; is_ambient = TRUE; mattail->ambient = 0; }break; case 0xA020: { // Hey! DIFFUSE is_diffuse = TRUE; is_specular = FALSE; is_ambient = FALSE; mattail->diffuse = 0; }break; case 0xA030: { // OH! SPECULAR is_diffuse = FALSE; is_specular = TRUE; is_ambient = FALSE; mattail->specular = 0; }break; case 0xA200: { // Texture if(mattail->htex == NULL) { mattail->htex = new _map_list; mattail->htex->next = NULL; mattail->ttex = mattail->htex; } else { mattail->ttex->next = new _map_list; mattail->ttex = mattail->ttex->next; mattail->ttex->next = NULL; } mattail->ttex->u = mattail->ttex->v = mattail->ttex->uoff = mattail->ttex->voff = 0.0; mattail->ttex->rot = 0.0; }break; case 0xA300: { // Texture name (filename with out path) char ch; int i=0; while((ch = fgetc(bin3ds)) != 0) { mattail->ttex->filename[i] = ch; i++; } mattail->ttex->filename[i] = '\0'; }break; case 0xA354: { // V coords fread(&(mattail->ttex->v), sizeof(float), 1, bin3ds); }break; case 0xA356: { // U coords fread(&(mattail->ttex->u), sizeof(float), 1, bin3ds); }break; case 0xA358: { // U offset fread(&(mattail->ttex->uoff), sizeof(float), 1, bin3ds); }break; case 0xA35A: { // V offset fread(&(mattail->ttex->voff), sizeof(float), 1, bin3ds); }break; case 0xA35C: { // Texture rotation angle fread(&(mattail->ttex->rot), sizeof(float), 1, bin3ds); }break; case 0x0011: { char r, g, b; // Read colors if(is_diffuse) { fread(&r, 1, 1, bin3ds); // Red component 1 byte fread(&g, 1, 1, bin3ds); // Green component 1 byte fread(&b, 1, 1, bin3ds); // Blue component 1 byte mattail->diffuse = long((r&0xFF)<<16) | long((g&0xFF)<<8) | long(b&0xFF); } else if(is_ambient) { fread(&r, 1, 1, bin3ds); // Red component 1 byte fread(&g, 1, 1, bin3ds); // Green component 1 byte fread(&b, 1, 1, bin3ds); // Blue component 1 byte mattail->ambient = long((r&0xFF)<<16) | long((g&0xFF)<<8) | long(b&0xFF); } if(is_specular) { fread(&r, 1, 1, bin3ds); // Red component 1 byte fread(&g, 1, 1, bin3ds); // Green component 1 byte fread(&b, 1, 1, bin3ds); // Blue component 1 byte mattail->specular = long((r&0xFF)<<16) | long((g&0xFF)<<8) | long(b&0xFF); } }break; default: { unsigned long pos; pos = ftell(bin3ds); if((pos - 6) >= count) // Check if v've crossed the chunk bound. { fseek(bin3ds, -6, SEEK_CUR); done = 1; break; } // Unknow CHUNK ID pos += (llen - 6); if(fseek(bin3ds, pos, SEEK_SET)) done = 1; } } } } void read_mesh(DWORD len) { unsigned long count=ftell(bin3ds) + (len - 6); WORD id; DWORD llen; int done = 0; while(!done) { id = ReadInt(); if(feof(bin3ds)) { done = 1; break; } llen = ReadLong(); switch(id) { case 0x4100: { if(tail == NULL) break; }break; case 0x4110: { int i; tail->nv = ReadInt(); // No. of vertices tail->vlst = new float[tail->nv * 3]; // Read vertices for(i=0; inv; i++) { fread(&(tail->vlst[i*3]), sizeof(float), 1, bin3ds); fread(&(tail->vlst[i*3+2]), sizeof(float), 1, bin3ds); // Swap z and y fread(&(tail->vlst[i*3+1]), sizeof(float), 1, bin3ds); } }break; case 0x4120: { int i; unsigned int nf; nf = ReadInt(); // No. of faces tail->nf = nf; tail->flst = new WORD[tail->nf * 4]; // Read all the faces for(i=0; inf; i++) { fread(&(tail->flst[i*4+2]), sizeof(WORD), 1, bin3ds); // clock wise order fread(&(tail->flst[i*4+1]), sizeof(WORD), 1, bin3ds); // worth experiment'n fread(&(tail->flst[i*4+0]), sizeof(WORD), 1, bin3ds); fread(&(tail->flst[i*4+3]), sizeof(WORD), 1, bin3ds); // face order } }break; case 0x4130: { // Material mapping Info. int i=0; char ch; if(tail == NULL) break; if(tail->fhead == NULL) { tail->fhead = new face_mat; tail->ftail = tail->fhead; } else { tail->ftail->next = new face_mat; tail->ftail = tail->ftail->next; } tail->ftail->next = NULL; // Read material name while((ch = fgetc(bin3ds)) != 0) { tail->ftail->name[i] = ch; i++; } tail->ftail->name[i] = '\0'; // Read no. of faces for this material tail->ftail->ne = ReadInt(); tail->ftail->faces = new WORD[tail->ftail->ne]; // read all faces (actually indices to the face list) fread(tail->ftail->faces, sizeof(WORD)*tail->ftail->ne, 1, bin3ds); }break; case 0x4140: { // Material mapping coords if(tail == NULL) break; int i; tail->mnv = ReadInt(); // No. of mapping coords tail->mc = new float[tail->mnv * 2]; // Read mapping coords // These are actually texture coords for vertices. for(i=0; imnv; i++) { fread(&(tail->mc[i*2]), sizeof(float), 1, bin3ds); // U fread(&(tail->mc[i*2+1]), sizeof(float), 1, bin3ds); // V } }break; case 0x4160: { // Local transformation matrix int i, j; if(tail == NULL) break; for (j=0;j<4;j++) { for (i=0;i<3;i++) { fread(&(tail->lmat[j][i]),sizeof (float),1,bin3ds); } } tail->lmat[0][3]=0; tail->lmat[1][3]=0; tail->lmat[2][3]=0; tail->lmat[3][3]=1; }break; case 0x4000: { // Object fseek(bin3ds, -6, SEEK_CUR); done = 1; break; } default: // Unknown CHUNK { unsigned long pos; pos = ftell(bin3ds); if((pos - 6) >= count) { fseek(bin3ds, -6, SEEK_CUR); done = 1; break; } pos += (llen - 6); if(fseek(bin3ds, pos, SEEK_SET)) done = 1; } } } } void read_object(DWORD len) { unsigned long count=ftell(bin3ds) + (len - 6); WORD id; DWORD llen; int done = 0; head = tail = NULL; maphead = maptail = NULL; mathead = mattail = NULL; while(!done) { id = ReadInt(); if(feof(bin3ds)) { break; done = 1; } llen = ReadLong(); // length of chunk switch(id) { case 0x4000: { // He He! MESH! int u=0; char ch; if(head == NULL) { head = new mesh_object; tail = head; } else { tail->next = new mesh_object; tail = tail->next; } // Initialize tail->next = NULL; tail->flst = NULL; tail->vlst = NULL; tail->nf = 0; tail->nv = 0; tail->mnv = 0; tail->mc = NULL; tail->fhead = NULL; tail->ftail = NULL; // Read mesh name (object name) while((ch = fgetc(bin3ds)) != 0) { tail->name[u] = ch; u++; } tail->name[u] = '\0'; read_mesh(llen); if(tail->nv == 0) // Object was not a mesh (might be a light etc) { // So free up the previously allocated mem (if any) if(tail->flst != NULL) delete tail->flst; if(tail->vlst != NULL) delete tail->vlst; if(tail == head) // check if first mesh { delete tail; tail = head = NULL; } else // No, then detach last and relocate tail { mesh_object *t, *p; t = p = head; while(t != NULL) { if(t == tail) break; else { p = t; t = t->next; } } delete tail; tail = p; tail->next = NULL; } } }break; case 0xAFFF: read_mat(llen); break; // Read materials default: // Unknown { unsigned long pos; pos = ftell(bin3ds); if((pos - 6) >= count) { fseek(bin3ds, -6, SEEK_CUR); done = 1; break; } pos += (llen - 6); if(fseek(bin3ds, pos, SEEK_SET)) done = 1; } } } } int read_3ds() { WORD id; DWORD len; int done = 0; while(!done) { id = ReadInt(); if(feof(bin3ds)) { break; done = 1; } len = ReadLong(); switch(id) { case 0xFFFF: done = 1; break; case 0x3D3D: read_object(len); break; // Read Objects default: // Unknown { unsigned long pos; pos = ftell(bin3ds); pos += (len - 6); if(fseek(bin3ds, pos, SEEK_SET)) done = 1; } break; } if(feof(bin3ds)) done = 1; } return 1; } int read_primary_chunk (void) { unsigned char version; if (ReadInt ()==0x4D4D) { fseek (bin3ds,28L,SEEK_SET); version=ReadChar (); if (version<3) { // Invalid version return 1; } fseek (bin3ds, 16, SEEK_SET); // Relocate to chunk start read_3ds(); } else return (1); return (0); } int load_3ds(FILE *fp) { char buf[6] = ".PMF"; WORD ver = 0x0002; unsigned long sz; int st; if(fp == NULL) return -1; bin3ds = fp; fseek(bin3ds, 0, SEEK_SET); // Just to make sure while (read_primary_chunk ()==0); // TODO: IMPORTANT!!!!!! // Free all the linked list here after u do watever u want to do // with the obj data (i.e u could write the data to a custom format file. // I use my own mesh format) return (0); } // Code end here