00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include "Keos3DSLoader.h"
00022 #include "KeosRenderSystem.h"
00023 #include "KeosLogger.h"
00024 #include "KeosMesh.h"
00025 #include "KeosString.h"
00026 #include "KeosUtil.h"
00027 #include <fstream>
00028
00029 namespace Keos
00030 {
00031
00032
00033
00034
00035
00036 C3DSLoader::C3DSLoader()
00037 {
00038 m_pTexCoords = NULL;
00039 m_pVertices = NULL;
00040
00041 m_nNumTexVertex = 0;
00042 m_nObjectWithMultMaterial = 0;
00043 }
00044
00045
00046 CMesh* C3DSLoader::LoadFromFile(const String& strFilename)
00047 {
00048 m_CurrentChunk = new TChunk;
00049 m_TempChunk = new TChunk;
00050
00051
00052 m_FilePointer = fopen(strFilename.c_str(), "rb");
00053
00054 ILogger::Log("Load of the mesh : %s", strFilename.c_str());
00055
00056
00057 if (!m_FilePointer)
00058 LOADINGFAILED_EXCEPT(strFilename, "Unable to find the file !", "C3DSLoader::LoadFromFile");
00059
00060
00061
00062
00063
00064
00065 ReadChunk(m_CurrentChunk);
00066
00067
00068 if (m_CurrentChunk->ID != CHUNK_PRIMARY)
00069 LOADINGFAILED_EXCEPT(strFilename, "The file is not a valid model 3DS !", "C3DSLoader::LoadFromFile");
00070
00071 CMesh* pMesh = new CMesh();
00072
00073
00074 ProcessNextChunk(pMesh, m_CurrentChunk);
00075
00076
00077 CleanUp();
00078
00079
00080 pMesh->ComputeNormals();
00081
00082 return pMesh;
00083 }
00084
00085
00086 void C3DSLoader::CleanUp()
00087 {
00088 fclose(m_FilePointer);
00089 delete m_CurrentChunk;
00090 delete m_TempChunk;
00091 }
00092
00093
00094 void C3DSLoader::ProcessNextChunk(CMesh *pMesh, TChunk *pPreviousChunk)
00095 {
00096 unsigned int version = 0;
00097 int buffer[50000] = {0};
00098 char strBuffer[256] = {0};
00099
00100 m_CurrentChunk = new TChunk;
00101
00102
00103
00104
00105
00106
00107
00108
00109 while (pPreviousChunk->bytesRead < pPreviousChunk->length)
00110 {
00111
00112 ReadChunk(m_CurrentChunk);
00113
00114
00115 switch (m_CurrentChunk->ID)
00116 {
00117 case CHUNK_VERSION:
00118
00119
00120
00121
00122
00123
00124 m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00125
00126
00127 if (version > 0x03)
00128 ILogger::Log("WARNING - This 3DS file is over version 3 so it may load incorrectly");
00129 break;
00130
00131 case CHUNK_OBJECTINFO:
00132
00133
00134
00135
00136
00137 ReadChunk(m_TempChunk);
00138
00139
00140 m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
00141
00142
00143 m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
00144
00145
00146 ProcessNextChunk(pMesh, m_CurrentChunk);
00147 break;
00148
00149 case CHUNK_MATERIAL:
00150 {
00151
00152
00153
00154 CMaterial* pMaterial = new CMaterial();
00155 pMesh->GetMaterialList().push_back(pMaterial);
00156
00157
00158 ProcessNextMaterialChunk(pMesh, m_CurrentChunk);
00159 }
00160 break;
00161
00162 case CHUNK_OBJECT:
00163
00164
00165
00166
00167
00168 m_CurrentChunk->bytesRead += GetString(strBuffer);
00169
00170
00171 ProcessNextObjectChunk(pMesh, strBuffer, m_CurrentChunk);
00172
00173 break;
00174
00175 case CHUNK_EDITKEYFRAME:
00176
00177
00178
00179
00180
00181 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00182 break;
00183
00184 default:
00185
00186
00187
00188 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00189 break;
00190 }
00191
00192
00193 pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
00194 }
00195
00196
00197 delete m_CurrentChunk;
00198 m_CurrentChunk = pPreviousChunk;
00199 }
00200
00201
00202 void C3DSLoader::ProcessNextObjectChunk(CMesh *pMesh, char* strObjectName, TChunk *pPreviousChunk)
00203 {
00204 int buffer[50000] = {0};
00205
00206
00207 m_CurrentChunk = new TChunk;
00208
00209
00210 while (pPreviousChunk->bytesRead < pPreviousChunk->length)
00211 {
00212
00213 ReadChunk(m_CurrentChunk);
00214
00215
00216 switch (m_CurrentChunk->ID)
00217 {
00218 case CHUNK_OBJECT_MESH:
00219 {
00220
00221 ProcessNextObjectChunk(pMesh, strObjectName, m_CurrentChunk);
00222
00223
00224
00225 std::vector<TGeom*> GeomList;
00226 TGeom* CurrentGeom;
00227 std::map<int, uint> MaterialSet;
00228 std::map<int, uint>::const_iterator SetIter;
00229 uint nNumDiffMat = 0;
00230 for (uint i = 0; i < m_Faces.size(); ++i)
00231 {
00232
00233 SetIter = MaterialSet.find(m_Faces[i].materialID);
00234 if (SetIter == MaterialSet.end())
00235 {
00236
00237 MaterialSet[m_Faces[i].materialID] = nNumDiffMat;
00238 GeomList.push_back(new TGeom);
00239 CurrentGeom = GeomList[nNumDiffMat];
00240 nNumDiffMat++;
00241 }
00242
00243 else CurrentGeom = GeomList[SetIter->second];
00244
00245 CurrentGeom->materialID = m_Faces[i].materialID;
00246
00247
00248 CurrentGeom->Indicies.push_back(m_Faces[i].vertIndex[0]);
00249 CurrentGeom->Indicies.push_back(m_Faces[i].vertIndex[1]);
00250 CurrentGeom->Indicies.push_back(m_Faces[i].vertIndex[2]);
00251 }
00252
00253
00254 TVertexList Vertices;
00255 Vertices.reserve(m_nNumVerts);
00256 for (int i = 0; i < m_nNumVerts; ++i)
00257 {
00258
00259 CMeshGeom::TVertex v;
00260 v.Position.x = m_pVertices[i].x;
00261 v.Position.y = m_pVertices[i].y;
00262 v.Position.z = m_pVertices[i].z;
00263
00264 v.Normal = TVector3F(0.0, 0.0, 0.0);
00265 if (GeomList[0]->materialID != -1)
00266 v.Color = Renderer->ConvertColor(pMesh->GetMaterial(GeomList[0]->materialID)->GetDiffuseColor());
00267 else v.Color = Renderer->ConvertColor(CColor::White);
00268
00269 if (i < m_nNumTexVertex)
00270 v.TexCoords = m_pTexCoords[i];
00271 else v.TexCoords = TVector2F(0, 0);
00272
00273
00274 Vertices.push_back(v);
00275 }
00276
00277 KEOS_DELETE_ARRAY(m_pTexCoords);
00278 KEOS_DELETE_ARRAY(m_pVertices);
00279 m_nNumTexVertex = 0;
00280
00281 #if KEOS_MESH_SHARED_VERTEX_BUFFER
00282
00283 if (GeomList.size() > 1)
00284 {
00285
00286 if (m_nObjectWithMultMaterial >= 1)
00287 ILogger::Log("WARNING - This 3DS model has more than one object with multiple materials, the loader may not support this correctly !");
00288 m_nObjectWithMultMaterial++;
00289
00290
00291
00292
00293 if (m_nObjectWithMultMaterial == 1)
00294 pMesh->CreateSharedVertexBuffer(&Vertices[0], (int)Vertices.size());
00295 }
00296 #endif
00297
00298
00299 for (uint i = 0; i < GeomList.size(); i++)
00300 {
00301 CurrentGeom = GeomList[i];
00302
00303
00304 CMeshGeom *pMeshGeom;
00305 #if KEOS_MESH_SHARED_VERTEX_BUFFER
00306 if (m_nObjectWithMultMaterial == 1)
00307 pMeshGeom = new CMeshGeom(&(CurrentGeom->Indicies[0]), (int)CurrentGeom->Indicies.size());
00308 else
00309 #endif
00310 pMeshGeom = new CMeshGeom(&Vertices[0], (int)Vertices.size(), &(CurrentGeom->Indicies[0]), (int)CurrentGeom->Indicies.size());
00311
00312
00313 pMesh->AddMeshGeom(pMeshGeom, String(strObjectName) + CStringConverter::ToString((int)i));
00314
00315
00316 pMeshGeom->SetMaterialIndex(CurrentGeom->materialID);
00317 }
00318 std::for_each(GeomList.begin(), GeomList.end(), Delete());
00319
00320 }
00321 break;
00322
00323 case CHUNK_OBJECT_VERTICES:
00324 ReadVertices(m_CurrentChunk);
00325 break;
00326
00327 case CHUNK_OBJECT_FACES:
00328 ReadVertexIndices(m_CurrentChunk);
00329 break;
00330
00331 case CHUNK_OBJECT_MATERIAL:
00332 ReadObjectMaterial(pMesh, m_CurrentChunk);
00333 break;
00334
00335 case CHUNK_OBJECT_UV:
00336
00337
00338 ReadUVCoordinates(m_CurrentChunk);
00339 break;
00340
00341 default:
00342
00343
00344 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00345 break;
00346 }
00347
00348
00349 pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
00350 }
00351
00352
00353 delete m_CurrentChunk;
00354 m_CurrentChunk = pPreviousChunk;
00355 }
00356
00357
00358 void C3DSLoader::ProcessNextMaterialChunk(CMesh *pMesh, TChunk *pPreviousChunk)
00359 {
00360 int buffer[50000] = {0};
00361 char strBuffer[255];
00362
00363
00364 m_CurrentChunk = new TChunk;
00365
00366
00367 while (pPreviousChunk->bytesRead < pPreviousChunk->length)
00368 {
00369
00370 ReadChunk(m_CurrentChunk);
00371
00372 CMaterial* pMaterial = pMesh->GetMaterialList()[pMesh->GetLastAddedMaterialIndex()];
00373
00374
00375 switch (m_CurrentChunk->ID)
00376 {
00377 case CHUNK_MATNAME:
00378
00379
00380 m_CurrentChunk->bytesRead += fread(strBuffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00381 pMaterial->SetName(strBuffer);
00382 break;
00383
00384 case CHUNK_MATDIFFUSE:
00385 ReadColorChunk(pMaterial, m_CurrentChunk);
00386 break;
00387
00388 case CHUNK_MATMAP:
00389
00390
00391 ProcessNextMaterialChunk(pMesh, m_CurrentChunk);
00392 break;
00393
00394 case CHUNK_MATMAPFILE:
00395
00396
00397 m_CurrentChunk->bytesRead += fread(strBuffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00398 pMaterial->CreateTexture(String(strBuffer));
00399
00400
00401
00402 pMaterial->GetTexture()->GetPixels().Flip();
00403 pMaterial->GetTexture()->Update();
00404 break;
00405
00406 default:
00407
00408
00409 m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
00410 break;
00411 }
00412
00413
00414 pPreviousChunk->bytesRead += m_CurrentChunk->bytesRead;
00415 }
00416
00417
00418 delete m_CurrentChunk;
00419 m_CurrentChunk = pPreviousChunk;
00420 }
00421
00422
00423 void C3DSLoader::ReadChunk(TChunk *pChunk)
00424 {
00425
00426
00427
00428 pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
00429
00430
00431
00432 pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
00433 }
00434
00435
00436 size_t C3DSLoader::GetString(char *pBuffer)
00437 {
00438 int index = 0;
00439
00440
00441 fread(pBuffer, 1, 1, m_FilePointer);
00442
00443
00444 while (*(pBuffer + index++) != 0)
00445 {
00446
00447
00448 fread(pBuffer + index, 1, 1, m_FilePointer);
00449 }
00450
00451
00452 return strlen(pBuffer) + 1;
00453 }
00454
00455
00456 void C3DSLoader::ReadColorChunk(CMaterial *pMaterial, TChunk *pChunk)
00457 {
00458
00459 ReadChunk(m_TempChunk);
00460
00461 unsigned char color[4];
00462
00463 m_TempChunk->bytesRead += fread(color, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
00464 pMaterial->SetDiffuseColor(CColor(color[0], color[1], color[2]));
00465
00466
00467 pChunk->bytesRead += m_TempChunk->bytesRead;
00468 }
00469
00470
00471 void C3DSLoader::ReadVertexIndices(TChunk *pPreviousChunk)
00472 {
00473 unsigned short index = 0;
00474
00475
00476
00477
00478
00479
00480
00481 int nNumOfFaces = 0;
00482 pPreviousChunk->bytesRead += fread(&nNumOfFaces, 1, 2, m_FilePointer);
00483
00484
00485 m_Faces.clear();
00486 TFace Face = {0};
00487 Face.materialID = -1;
00488
00489
00490 for (int i = 0; i < nNumOfFaces; i++)
00491 {
00492
00493
00494 for (int j = 0; j < 4; j++)
00495 {
00496
00497 pPreviousChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
00498
00499 if (j < 3)
00500 {
00501
00502 Face.vertIndex[j] = index;
00503 }
00504 }
00505 m_Faces.push_back(Face);
00506 }
00507 }
00508
00509
00510 void C3DSLoader::ReadUVCoordinates(TChunk *pPreviousChunk)
00511 {
00512
00513
00514
00515
00516 m_nNumTexVertex = 0;
00517 pPreviousChunk->bytesRead += fread(&m_nNumTexVertex, 1, 2, m_FilePointer);
00518
00519
00520 m_pTexCoords = new TVector2F [m_nNumTexVertex];
00521
00522
00523 pPreviousChunk->bytesRead += fread(m_pTexCoords, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
00524 }
00525
00526
00527 void C3DSLoader::ReadVertices(TChunk *pPreviousChunk)
00528 {
00529
00530
00531
00532
00533
00534 m_nNumVerts = 0;
00535 pPreviousChunk->bytesRead += fread(&m_nNumVerts, 1, 2, m_FilePointer);
00536
00537
00538 m_pVertices = new TVector3F [m_nNumVerts];
00539 memset(m_pVertices, 0, sizeof(TVector3F) * m_nNumVerts);
00540
00541
00542 pPreviousChunk->bytesRead += fread(m_pVertices, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564 }
00565
00566
00567 void C3DSLoader::ReadObjectMaterial(CMesh *pMesh, TChunk *pPreviousChunk)
00568 {
00569 char strMaterial[255] = {0};
00570 int buffer[50000] = {0};
00571 int nMaterialIndex = -1;
00572
00573
00574
00575
00576
00577
00578
00579 pPreviousChunk->bytesRead += GetString(strMaterial);
00580
00581
00582
00583
00584
00585
00586
00587
00588 for (uint i = 0; i < pMesh->GetMaterialsCount(); i++)
00589 {
00590
00591 if (strcmp(strMaterial, pMesh->GetMaterialList()[i]->GetName().c_str()) == 0)
00592 {
00593
00594 nMaterialIndex = i;
00595 break;
00596 }
00597 else
00598 {
00599
00600 nMaterialIndex = -1;
00601 }
00602 }
00603
00604
00605 ushort iNumFaces = 0;
00606 pPreviousChunk->bytesRead += fread(&iNumFaces, 1, 2, m_FilePointer);
00607
00608 ushort* pFaceAssignedThisMaterial = new ushort[iNumFaces];
00609 memset(pFaceAssignedThisMaterial, 0, sizeof(ushort) * iNumFaces);
00610
00611
00612 pPreviousChunk->bytesRead += fread(pFaceAssignedThisMaterial, 1, iNumFaces * sizeof(ushort), m_FilePointer);
00613
00614
00615 for (int i = 0; i < iNumFaces; i++)
00616 {
00617 int iIndex = pFaceAssignedThisMaterial[i];
00618 m_Faces[iIndex].materialID = nMaterialIndex;
00619 }
00620
00621 KEOS_DELETE_ARRAY(pFaceAssignedThisMaterial);
00622
00623
00624
00625 pPreviousChunk->bytesRead += fread(buffer, 1, pPreviousChunk->length - pPreviousChunk->bytesRead, m_FilePointer);
00626 }
00627 }