00001
00002
00003
00004
00005
00006
00007
00008
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
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
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
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
00350
00351
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
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 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
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