Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

3dsread.cpp

Go to the documentation of this file.
00001 // 3dsread.cpp
00002 // 
00003 // Copyright (C) 2000, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <iomanip>
00011 
00012 #include "3dschunk.h"
00013 #include "3dsmodel.h"
00014 #include "3dsread.h"
00015 #include <celutil/bytes.h>
00016 #include <celutil/debug.h>
00017 
00018 using namespace std;
00019 
00020 typedef bool (*ProcessChunkFunc)(ifstream& in,
00021                                  unsigned short chunkType,
00022                                  int contentSize,
00023                                  void*);
00024 
00025 static int read3DSChunk(ifstream& in,
00026                         ProcessChunkFunc chunkFunc,
00027                         void* obj);
00028 
00029 // For pretty printing debug info
00030 static int logIndent = 0;
00031 
00032 
00033 static int32 readInt(ifstream& in)
00034 {
00035     int32 ret;
00036     in.read((char *) &ret, sizeof(int32));
00037     LE_TO_CPU_INT32(ret, ret);
00038     return ret;
00039 }
00040 
00041 static int16 readShort(ifstream& in)
00042 {
00043     int16 ret;
00044     in.read((char *) &ret, sizeof(int16));
00045     LE_TO_CPU_INT16(ret, ret);
00046     return ret;
00047 }
00048 
00049 static uint16 readUshort(ifstream& in)
00050 {
00051     uint16 ret;
00052     in.read((char *) &ret, sizeof(uint16));
00053     LE_TO_CPU_INT16(ret, ret);
00054     return ret;
00055 }
00056  
00057 static float readFloat(ifstream& in)
00058 {
00059     float f;
00060     in.read((char*) &f, sizeof(float));
00061     LE_TO_CPU_FLOAT(f, f);
00062     return f;
00063 }
00064 
00065 
00066 static char readChar(ifstream& in)
00067 {
00068     char c;
00069     in.read(&c, 1);
00070     return c;
00071 }
00072 
00073 
00074 /* Not currently used
00075 static int readString(ifstream& in, char* s, int maxLength)
00076 {
00077     int count;
00078     for (count = 0; count < maxLength; count++)
00079     {
00080         in.read(s + count, 1);
00081         if (s[count] == '\0')
00082             break;
00083     }
00084 
00085     return count;
00086 }*/
00087 
00088 static string readString(ifstream& in)
00089 {
00090     char s[1024];
00091     int maxLength = sizeof(s);
00092 
00093     for (int count = 0; count < maxLength; count++)
00094     {
00095         in.read(s + count, 1);
00096         if (s[count] == '\0')
00097             break;
00098     }
00099 
00100     return string(s);
00101 }
00102 
00103 
00104 static void skipBytes(ifstream& in, int count)
00105 {
00106     char c;
00107     while (count-- > 0)
00108         in.get(c);
00109 }
00110 
00111 
00112 void indent()
00113 {
00114     for (int i = 0; i < logIndent; i++)
00115         cout << "  ";
00116 }
00117 
00118 void logChunk(uint16 chunkType, int chunkSize)
00119 {
00120     const char* name = NULL;
00121 
00122     switch (chunkType)
00123     {
00124     case M3DCHUNK_NULL:
00125         name = "M3DCHUNK_NULL"; break;
00126     case M3DCHUNK_VERSION:
00127         name = "M3DCHUNK_VERSION"; break;
00128     case M3DCHUNK_COLOR_FLOAT:
00129         name = "M3DCHUNK_COLOR_FLOAT"; break;
00130     case M3DCHUNK_COLOR_24:
00131         name = "M3DCHUNK_COLOR_24"; break;
00132     case M3DCHUNK_LIN_COLOR_F:
00133         name = "M3DCHUNK_LIN_COLOR_F"; break;
00134     case M3DCHUNK_INT_PERCENTAGE:
00135         name = "M3DCHUNK_INT_PERCENTAGE"; break;
00136     case M3DCHUNK_FLOAT_PERCENTAGE:
00137         name = "M3DCHUNK_FLOAT_PERCENTAGE"; break;
00138     case M3DCHUNK_MASTER_SCALE:
00139         name = "M3DCHUNK_MASTER_SCALE"; break;
00140     case M3DCHUNK_BACKGROUND_COLOR:
00141         name = "M3DCHUNK_BACKGROUND_COLOR"; break;
00142     case M3DCHUNK_MESHDATA:
00143         name = "M3DCHUNK_MESHDATA"; break;
00144     case M3DCHUNK_MESH_VERSION:
00145         name = "M3DCHUNK_MESHVERSION"; break;
00146     case M3DCHUNK_NAMED_OBJECT:
00147         name = "M3DCHUNK_NAMED_OBJECT"; break;
00148     case M3DCHUNK_TRIANGLE_MESH:
00149         name = "M3DCHUNK_TRIANGLE_MESH"; break;
00150     case M3DCHUNK_POINT_ARRAY:
00151         name = "M3DCHUNK_POINT_ARRAY"; break;
00152     case M3DCHUNK_POINT_FLAG_ARRAY:
00153         name = "M3DCHUNK_POINT_FLAG_ARRAY"; break;
00154     case M3DCHUNK_FACE_ARRAY:
00155         name = "M3DCHUNK_FACE_ARRAY"; break;
00156     case M3DCHUNK_MESH_MATERIAL_GROUP:
00157         name = "M3DCHUNK_MESH_MATERIAL_GROUP"; break;
00158     case M3DCHUNK_MESH_TEXTURE_COORDS:
00159         name = "M3DCHUNK_MESH_TEXTURE_COORDS"; break;
00160     case M3DCHUNK_MESH_SMOOTH_GROUP:
00161         name = "M3DCHUNK_MESH_SMOOTH_GROUP"; break;
00162     case M3DCHUNK_MESH_MATRIX:
00163         name = "M3DCHUNK_MESH_MATRIX"; break;
00164     case M3DCHUNK_MAGIC:
00165         name = "M3DCHUNK_MAGIC"; break;
00166     case M3DCHUNK_MATERIAL_NAME:
00167         name = "M3DCHUNK_MATERIAL_NAME"; break;
00168     case M3DCHUNK_MATERIAL_AMBIENT:
00169         name = "M3DCHUNK_MATERIAL_AMBIENT"; break;
00170     case M3DCHUNK_MATERIAL_DIFFUSE:
00171         name = "M3DCHUNK_MATERIAL_DIFFUSE"; break;
00172     case M3DCHUNK_MATERIAL_SPECULAR:
00173         name = "M3DCHUNK_MATERIAL_SPECULAR"; break;
00174     case M3DCHUNK_MATERIAL_SHININESS:
00175         name = "M3DCHUNK_MATERIAL_SHININESS"; break;
00176     case M3DCHUNK_MATERIAL_SHIN2PCT:
00177         name = "M3DCHUNK_MATERIAL_SHIN2PCT"; break;
00178     case M3DCHUNK_MATERIAL_TRANSPARENCY:
00179         name = "M3DCHUNK_MATERIAL_TRANSPARENCY"; break;
00180     case M3DCHUNK_MATERIAL_XPFALL:
00181         name = "M3DCHUNK_MATERIAL_XPFALL"; break;
00182     case M3DCHUNK_MATERIAL_REFBLUR:
00183         name = "M3DCHUNK_MATERIAL_REFBLUR"; break;
00184     case M3DCHUNK_MATERIAL_TEXMAP:
00185         name = "M3DCHUNK_MATERIAL_TEXMAP"; break;
00186     case M3DCHUNK_MATERIAL_MAPNAME:
00187         name = "M3DCHUNK_MATERIAL_MAPNAME"; break;
00188     case M3DCHUNK_MATERIAL_ENTRY:
00189         name = "M3DCHUNK_MATERIAL_ENTRY"; break;
00190     case M3DCHUNK_KFDATA:
00191         name = "M3DCHUNK_KFDATA";
00192     default:
00193         break;
00194     }
00195 #if 0
00196     indent();
00197 
00198     if (name == NULL)
00199     {
00200         cout << "Chunk ID " << setw(4) << hex << setfill('0') << chunkType;
00201         cout << setw(0) << dec << ", size = " << chunkSize << '\n';
00202     }
00203     else
00204     {
00205         cout << name << ", size = " << chunkSize << '\n';
00206     }
00207 
00208     cout.flush();
00209 #endif
00210 }
00211 
00212 
00213 int read3DSChunk(ifstream& in,
00214                  ProcessChunkFunc chunkFunc,
00215                  void* obj)
00216 {
00217     unsigned short chunkType = readUshort(in);
00218     int32 chunkSize = readInt(in);
00219     int contentSize = chunkSize - 6;
00220         
00221     logChunk(chunkType, chunkSize);
00222     bool chunkWasRead = chunkFunc(in, chunkType, contentSize, obj);
00223 
00224     if (!chunkWasRead)
00225     {
00226         skipBytes(in, contentSize);
00227     }
00228 
00229     return chunkSize;
00230 }
00231 
00232 
00233 int read3DSChunks(ifstream& in,
00234                   int nBytes,
00235                   ProcessChunkFunc chunkFunc,
00236                   void* obj)
00237 {
00238     int bytesRead = 0;
00239 
00240     logIndent++;
00241     while (bytesRead < nBytes)
00242         bytesRead += read3DSChunk(in, chunkFunc, obj);
00243     logIndent--;
00244 
00245     if (bytesRead != nBytes)
00246         cout << "Expected " << nBytes << " bytes but read " << bytesRead << '\n'; 
00247     return bytesRead;
00248 }
00249 
00250 
00251 M3DColor readColor(ifstream& in, int nBytes)
00252 {
00253     unsigned char r = (unsigned char) readChar(in);
00254     unsigned char g = (unsigned char) readChar(in);
00255     unsigned char b = (unsigned char) readChar(in);
00256 
00257     return M3DColor((float) r / 255.0f,
00258                     (float) g / 255.0f,
00259                     (float) b / 255.0f);
00260 }
00261 
00262 
00263 M3DColor readFloatColor(ifstream& in, int nBytes)
00264 {
00265     float r = readFloat(in);
00266     float g = readFloat(in);
00267     float b = readFloat(in);
00268 
00269     return M3DColor((float) r / 255.0f,
00270                     (float) g / 255.0f,
00271                     (float) b / 255.0f);
00272 }
00273 
00274 
00275 Mat4f readMeshMatrix(ifstream& in, int nBytes)
00276 {
00277     float m00 = readFloat(in);
00278     float m01 = readFloat(in);
00279     float m02 = readFloat(in);
00280     float m10 = readFloat(in);
00281     float m11 = readFloat(in);
00282     float m12 = readFloat(in);
00283     float m20 = readFloat(in);
00284     float m21 = readFloat(in);
00285     float m22 = readFloat(in);
00286     float m30 = readFloat(in);
00287     float m31 = readFloat(in);
00288     float m32 = readFloat(in);
00289 
00290 #if 0
00291     cout << m00 << "   " << m01 << "   " << m02 << '\n';
00292     cout << m10 << "   " << m11 << "   " << m12 << '\n';
00293     cout << m20 << "   " << m21 << "   " << m22 << '\n';
00294     cout << m30 << "   " << m31 << "   " << m32 << '\n';
00295 #endif
00296 
00297     return Mat4f(Vec4f(m00, m01, m02, 0),
00298                  Vec4f(m10, m11, m12, 0),
00299                  Vec4f(m20, m21, m22, 0),
00300                  Vec4f(m30, m31, m32, 1));
00301 }
00302 
00303 
00304 bool stubProcessChunk(ifstream& in,
00305                       unsigned short chunkType,
00306                       int contentSize,
00307                       void* obj)
00308 {
00309     return false;
00310 }
00311 
00312 
00313 void readPointArray(ifstream& in, M3DTriangleMesh* triMesh)
00314 {
00315     uint16 nPoints = readUshort(in);
00316 
00317     for (int i = 0; i < (int) nPoints; i++)
00318     {
00319         float x = readFloat(in);
00320         float y = readFloat(in);
00321         float z = readFloat(in);
00322         triMesh->addVertex(Point3f(x, y, z));
00323     }
00324 }
00325 
00326 
00327 void readTextureCoordArray(ifstream& in, M3DTriangleMesh* triMesh)
00328 {
00329     uint16 nPoints = readUshort(in);
00330 
00331     for (int i = 0; i < (int) nPoints; i++)
00332     {
00333         float u = readFloat(in);
00334         float v = readFloat(in);
00335         triMesh->addTexCoord(Point2f(u, -v));
00336     }
00337 }
00338 
00339 
00340 bool processFaceArrayChunk(ifstream& in,
00341                            unsigned short chunkType,
00342                            int contentSize,
00343                            void* obj)
00344 {
00345     M3DTriangleMesh* triMesh = (M3DTriangleMesh*) obj;
00346 
00347     if (chunkType == M3DCHUNK_MESH_MATERIAL_GROUP)
00348     {
00349         // For now, we just assume that there is only one material group
00350         // per triangle mesh, and that the material applies to all faces in
00351         // the mesh.
00352         string materialName = readString(in);
00353         uint16 nFaces = readUshort(in);
00354         
00355         for (uint16 i = 0; i < nFaces; i++)
00356         {
00357             readUshort(in);
00358         }
00359 
00360         triMesh->setMaterialName(materialName);
00361 
00362         return true;
00363     }
00364     else if (chunkType == M3DCHUNK_MESH_SMOOTH_GROUP)
00365     {
00366         uint16 nFaces = triMesh->getFaceCount();
00367         
00368         for (uint16 i = 0; i < nFaces; i++)
00369         {
00370             uint32 groups = (uint32) readInt(in);
00371             //            cout << setw(8) << hex << setfill('0') << groups << dec << setw(0) << '\n';
00372             triMesh->addSmoothingGroups(groups);
00373         }
00374         return true;
00375     }
00376     else
00377     {
00378         return false;
00379     }
00380 }
00381 
00382 
00383 void readFaceArray(ifstream& in, M3DTriangleMesh* triMesh, int contentSize)
00384 {
00385     uint16 nFaces = readUshort(in);
00386 
00387     for (int i = 0; i < (int) nFaces; i++)
00388     {
00389         uint16 v0 = readUshort(in);
00390         uint16 v1 = readUshort(in);
00391         uint16 v2 = readUshort(in);
00392         /*uint16 flags = */ readUshort(in);
00393         triMesh->addFace(v0, v1, v2);
00394     }
00395 
00396     int bytesLeft = contentSize - (8 * nFaces + 2);
00397     if (bytesLeft > 0)
00398     {
00399         read3DSChunks(in,
00400                       bytesLeft,
00401                       processFaceArrayChunk,
00402                       (void*) triMesh);
00403     }        
00404 }
00405 
00406 
00407 bool processTriMeshChunk(ifstream& in,
00408                          unsigned short chunkType,
00409                          int contentSize,
00410                          void* obj)
00411 {
00412     M3DTriangleMesh* triMesh = (M3DTriangleMesh*) obj;
00413 
00414     if (chunkType == M3DCHUNK_POINT_ARRAY)
00415     {
00416         readPointArray(in, triMesh);
00417         return true;
00418     }
00419     else if (chunkType == M3DCHUNK_MESH_TEXTURE_COORDS)
00420     {
00421         readTextureCoordArray(in, triMesh);
00422         return true;
00423     }
00424     else if (chunkType == M3DCHUNK_FACE_ARRAY)
00425     {
00426         readFaceArray(in, triMesh, contentSize);
00427         return true;
00428     }
00429     else if (chunkType == M3DCHUNK_MESH_MATRIX)
00430     {
00431         triMesh->setMatrix(readMeshMatrix(in, contentSize));
00432         return true;
00433     }
00434     else
00435     {
00436         return false;
00437     }
00438 }
00439 
00440 
00441 bool processModelChunk(ifstream& in,
00442                        unsigned short chunkType,
00443                        int contentSize,
00444                        void* obj)
00445 {
00446     M3DModel* model = (M3DModel*) obj;
00447 
00448     if (chunkType == M3DCHUNK_TRIANGLE_MESH)
00449     {
00450         M3DTriangleMesh* triMesh = new M3DTriangleMesh();
00451         read3DSChunks(in, contentSize, processTriMeshChunk, (void*) triMesh);
00452         model->addTriMesh(triMesh);
00453         return true;
00454     }
00455     else
00456     {
00457         return false;
00458     }
00459 }
00460 
00461 
00462 bool processColorChunk(ifstream& in,
00463                        unsigned short chunkType,
00464                        int contentSize,
00465                        void* obj)
00466 {
00467     M3DColor* color = (M3DColor*) obj;
00468 
00469     if (chunkType == M3DCHUNK_COLOR_24)
00470     {
00471         *color = readColor(in, contentSize);
00472         return true;
00473     }
00474     else if (chunkType == (M3DCHUNK_COLOR_FLOAT))
00475     {
00476         *color = readFloatColor(in, contentSize);
00477         return true;
00478     }
00479     else
00480     {
00481         return false;
00482     }
00483 }
00484 
00485 
00486 static bool processPercentageChunk(ifstream& in,
00487                                    unsigned short chunkType,
00488                                    int contentSize,
00489                                    void* obj)
00490 {
00491     float* percent = (float*) obj;
00492 
00493     if (chunkType == M3DCHUNK_INT_PERCENTAGE)
00494     {
00495         *percent = readShort(in);
00496         return true;
00497     }
00498     else if (chunkType == M3DCHUNK_FLOAT_PERCENTAGE)
00499     {
00500         *percent = readFloat(in);
00501         return true;
00502     }
00503     else
00504     {
00505         return false;
00506     }
00507 }
00508 
00509 
00510 static bool processTexmapChunk(ifstream& in,
00511                                unsigned short chunkType,
00512                                int contentSize,
00513                                void* obj)
00514 {
00515     M3DMaterial* material = (M3DMaterial*) obj;
00516 
00517     if (chunkType == M3DCHUNK_MATERIAL_MAPNAME)
00518     {
00519         string name = readString(in);
00520         material->setTextureMap(name);
00521         return true;
00522     }
00523     else
00524     {
00525         return false;
00526     }
00527 }
00528 
00529 
00530 bool processMaterialChunk(ifstream& in,
00531                           unsigned short chunkType,
00532                           int contentSize,
00533                           void* obj)
00534 {
00535     M3DMaterial* material = (M3DMaterial*) obj;
00536 
00537     if (chunkType == M3DCHUNK_MATERIAL_NAME)
00538     {
00539         string name = readString(in);
00540         material->setName(name);
00541         return true;
00542     }
00543     else if (chunkType == M3DCHUNK_MATERIAL_AMBIENT)
00544     {
00545         M3DColor ambient;
00546         read3DSChunks(in, contentSize, processColorChunk, (void*) &ambient);
00547         material->setAmbientColor(ambient);
00548         return true;
00549     }
00550     else if (chunkType == M3DCHUNK_MATERIAL_DIFFUSE)
00551     {
00552         M3DColor diffuse;
00553         read3DSChunks(in, contentSize, processColorChunk, (void*) &diffuse);
00554         material->setDiffuseColor(diffuse);
00555         return true;
00556     }
00557     else if (chunkType == M3DCHUNK_MATERIAL_SPECULAR)
00558     {
00559         M3DColor specular;
00560         read3DSChunks(in, contentSize, processColorChunk, (void*) &specular);
00561         material->setSpecularColor(specular);
00562         return true;
00563     }
00564     else if (chunkType == M3DCHUNK_MATERIAL_SHININESS)
00565     {
00566         float shininess;
00567         read3DSChunks(in, contentSize, processPercentageChunk,
00568                       (void*) &shininess);
00569         material->setShininess(shininess);
00570         return true;
00571     }
00572     else if (chunkType == M3DCHUNK_MATERIAL_TRANSPARENCY)
00573     {
00574         float transparency;
00575         read3DSChunks(in, contentSize, processPercentageChunk,
00576                       (void*) &transparency);
00577         material->setOpacity(1.0f - transparency / 100.0f);
00578         return true;
00579     }
00580 
00581     else if (chunkType == M3DCHUNK_MATERIAL_TEXMAP)
00582     {
00583         read3DSChunks(in, contentSize, processTexmapChunk, (void*) material);
00584         return true;
00585     }
00586     else
00587     {
00588         return false;
00589     }
00590 }
00591 
00592 
00593 bool processSceneChunk(ifstream& in,
00594                        unsigned short chunkType,
00595                        int contentSize,
00596                        void* obj)
00597 {
00598     M3DScene* scene = (M3DScene*) obj;
00599 
00600     if (chunkType == M3DCHUNK_NAMED_OBJECT)
00601     {
00602         string name = readString(in);
00603 
00604         M3DModel* model = new M3DModel();
00605         model->setName(name);
00606         // indent(); cout << "  [" << name << "]\n";
00607         read3DSChunks(in,
00608                       contentSize - (name.length() + 1),
00609                       processModelChunk,
00610                       (void*) model);
00611         scene->addModel(model);
00612 
00613         return true;
00614     }
00615     else if (chunkType == M3DCHUNK_MATERIAL_ENTRY)
00616     {
00617         M3DMaterial* material = new M3DMaterial();
00618         read3DSChunks(in,
00619                       contentSize,
00620                       processMaterialChunk,
00621                       (void*) material);
00622         scene->addMaterial(material);
00623 
00624         return true;
00625     }
00626     else if (chunkType == M3DCHUNK_BACKGROUND_COLOR)
00627     {
00628         M3DColor color;
00629         read3DSChunks(in, contentSize, processColorChunk, (void*) &color);
00630         scene->setBackgroundColor(color);
00631         return true;
00632     }
00633     else
00634     {
00635         return false;
00636     }
00637 }
00638 
00639 
00640 bool processTopLevelChunk(ifstream& in,
00641                           unsigned short chunkType,
00642                           int contentSize,
00643                           void* obj)
00644 {
00645     M3DScene* scene = (M3DScene*) obj;
00646 
00647     if (chunkType == M3DCHUNK_MESHDATA)
00648     {
00649         read3DSChunks(in, contentSize, processSceneChunk, (void*) scene);
00650         return true;
00651     }
00652     else
00653     {
00654         return false;
00655     }
00656 }
00657 
00658 
00659 M3DScene* Read3DSFile(ifstream& in)
00660 {
00661     unsigned short chunkType = readUshort(in);
00662     if (chunkType != M3DCHUNK_MAGIC)
00663     {
00664         DPRINTF(0, "Read3DSFile: Wrong magic number in header\n");
00665         return NULL;
00666     }
00667 
00668     int32 chunkSize = readInt(in);
00669     if (in.bad())
00670     {
00671         DPRINTF(0, "Read3DSFile: Error reading 3DS file.\n");
00672         return NULL;
00673     }
00674     
00675     DPRINTF(1, "3DS file, %d bytes\n", chunkSize);
00676 
00677     M3DScene* scene = new M3DScene();
00678     int contentSize = chunkSize - 6;
00679 
00680     read3DSChunks(in, contentSize, processTopLevelChunk, (void*) scene);
00681 
00682     return scene;
00683 }
00684 
00685 
00686 M3DScene* Read3DSFile(const string& filename)
00687 {
00688     ifstream in(filename.c_str(), ios::in | ios::binary);
00689     if (!in.good())
00690     {
00691         DPRINTF(0, "Read3DSFile: Error opening %s\n", filename.c_str());
00692         return NULL;
00693     }
00694     else
00695     {
00696         M3DScene* scene = Read3DSFile(in);
00697         in.close();
00698         return scene;
00699     }
00700 }
00701 
00702 
00703 #if 0
00704 int main(int argc, char* argv[])
00705 {
00706     if (argc != 2)
00707     {
00708         cerr << "Usage: 3dsread <filename>\n";
00709         exit(1);
00710     }
00711 
00712     ifstream in(argv[1], ios::in | ios::binary);
00713     if (!in.good())
00714     {
00715         cerr << "Error opening " << argv[1] << '\n';
00716         exit(1);
00717     }
00718     else
00719     {
00720         read3DSFile(in);
00721         in.close();
00722     }
00723 
00724     return 0;
00725 }
00726 #endif

Generated on Sat Jan 14 22:30:26 2006 for Celestia by  doxygen 1.4.1