vrshoot

view libs/assimp/MDCLoader.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
line source
1 /*
2 ---------------------------------------------------------------------------
3 Open Asset Import Library (assimp)
4 ---------------------------------------------------------------------------
6 Copyright (c) 2006-2012, assimp team
8 All rights reserved.
10 Redistribution and use of this software in source and binary forms,
11 with or without modification, are permitted provided that the following
12 conditions are met:
14 * Redistributions of source code must retain the above
15 copyright notice, this list of conditions and the
16 following disclaimer.
18 * Redistributions in binary form must reproduce the above
19 copyright notice, this list of conditions and the
20 following disclaimer in the documentation and/or other
21 materials provided with the distribution.
23 * Neither the name of the assimp team, nor the names of its
24 contributors may be used to endorse or promote products
25 derived from this software without specific prior
26 written permission of the assimp team.
28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 ---------------------------------------------------------------------------
40 */
42 /** @file Implementation of the MDC importer class */
44 #include "AssimpPCH.h"
45 #ifndef ASSIMP_BUILD_NO_MDC_IMPORTER
47 // internal headers
48 #include "MDCLoader.h"
49 #include "MD3FileData.h"
50 #include "MDCNormalTable.h" // shouldn't be included by other units
52 using namespace Assimp;
53 using namespace Assimp::MDC;
55 static const aiImporterDesc desc = {
56 "Return To Castle Wolfenstein Mesh Importer",
57 "",
58 "",
59 "",
60 aiImporterFlags_SupportBinaryFlavour,
61 0,
62 0,
63 0,
64 0,
65 "mdc"
66 };
68 // ------------------------------------------------------------------------------------------------
69 void MDC::BuildVertex(const Frame& frame,
70 const BaseVertex& bvert,
71 const CompressedVertex& cvert,
72 aiVector3D& vXYZOut,
73 aiVector3D& vNorOut)
74 {
75 // compute the position
76 const float xd = (cvert.xd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
77 const float yd = (cvert.yd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
78 const float zd = (cvert.zd - AI_MDC_CVERT_BIAS) * AI_MDC_DELTA_SCALING;
79 vXYZOut.x = frame.localOrigin.x + AI_MDC_BASE_SCALING * (bvert.x + xd);
80 vXYZOut.y = frame.localOrigin.y + AI_MDC_BASE_SCALING * (bvert.y + yd);
81 vXYZOut.z = frame.localOrigin.z + AI_MDC_BASE_SCALING * (bvert.z + zd);
83 // compute the normal vector .. ehm ... lookup it in the table :-)
84 vNorOut.x = mdcNormals[cvert.nd][0];
85 vNorOut.y = mdcNormals[cvert.nd][1];
86 vNorOut.z = mdcNormals[cvert.nd][2];
87 }
89 // ------------------------------------------------------------------------------------------------
90 // Constructor to be privately used by Importer
91 MDCImporter::MDCImporter()
92 {
93 }
95 // ------------------------------------------------------------------------------------------------
96 // Destructor, private as well
97 MDCImporter::~MDCImporter()
98 {
99 }
100 // ------------------------------------------------------------------------------------------------
101 // Returns whether the class can handle the format of the given file.
102 bool MDCImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
103 {
104 const std::string extension = GetExtension(pFile);
105 if (extension == "mdc")
106 return true;
108 // if check for extension is not enough, check for the magic tokens
109 if (!extension.length() || checkSig) {
110 uint32_t tokens[1];
111 tokens[0] = AI_MDC_MAGIC_NUMBER_LE;
112 return CheckMagicToken(pIOHandler,pFile,tokens,1);
113 }
114 return false;
115 }
117 // ------------------------------------------------------------------------------------------------
118 const aiImporterDesc* MDCImporter::GetInfo () const
119 {
120 return &desc;
121 }
123 // ------------------------------------------------------------------------------------------------
124 // Validate the header of the given MDC file
125 void MDCImporter::ValidateHeader()
126 {
127 AI_SWAP4( this->pcHeader->ulVersion );
128 AI_SWAP4( this->pcHeader->ulFlags );
129 AI_SWAP4( this->pcHeader->ulNumFrames );
130 AI_SWAP4( this->pcHeader->ulNumTags );
131 AI_SWAP4( this->pcHeader->ulNumSurfaces );
132 AI_SWAP4( this->pcHeader->ulNumSkins );
133 AI_SWAP4( this->pcHeader->ulOffsetBorderFrames );
135 if (pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_BE &&
136 pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_LE)
137 {
138 char szBuffer[5];
139 szBuffer[0] = ((char*)&pcHeader->ulIdent)[0];
140 szBuffer[1] = ((char*)&pcHeader->ulIdent)[1];
141 szBuffer[2] = ((char*)&pcHeader->ulIdent)[2];
142 szBuffer[3] = ((char*)&pcHeader->ulIdent)[3];
143 szBuffer[4] = '\0';
145 throw DeadlyImportError("Invalid MDC magic word: should be IDPC, the "
146 "magic word found is " + std::string( szBuffer ));
147 }
149 if (pcHeader->ulVersion != AI_MDC_VERSION)
150 DefaultLogger::get()->warn("Unsupported MDC file version (2 (AI_MDC_VERSION) was expected)");
152 if (pcHeader->ulOffsetBorderFrames + pcHeader->ulNumFrames * sizeof(MDC::Frame) > this->fileSize ||
153 pcHeader->ulOffsetSurfaces + pcHeader->ulNumSurfaces * sizeof(MDC::Surface) > this->fileSize)
154 {
155 throw DeadlyImportError("Some of the offset values in the MDC header are invalid "
156 "and point to something behind the file.");
157 }
159 if (this->configFrameID >= this->pcHeader->ulNumFrames)
160 throw DeadlyImportError("The requested frame is not available");
161 }
163 // ------------------------------------------------------------------------------------------------
164 // Validate the header of a given MDC file surface
165 void MDCImporter::ValidateSurfaceHeader(BE_NCONST MDC::Surface* pcSurf)
166 {
167 AI_SWAP4(pcSurf->ulFlags);
168 AI_SWAP4(pcSurf->ulNumCompFrames);
169 AI_SWAP4(pcSurf->ulNumBaseFrames);
170 AI_SWAP4(pcSurf->ulNumShaders);
171 AI_SWAP4(pcSurf->ulNumVertices);
172 AI_SWAP4(pcSurf->ulNumTriangles);
173 AI_SWAP4(pcSurf->ulOffsetTriangles);
174 AI_SWAP4(pcSurf->ulOffsetTexCoords);
175 AI_SWAP4(pcSurf->ulOffsetBaseVerts);
176 AI_SWAP4(pcSurf->ulOffsetCompVerts);
177 AI_SWAP4(pcSurf->ulOffsetFrameBaseFrames);
178 AI_SWAP4(pcSurf->ulOffsetFrameCompFrames);
179 AI_SWAP4(pcSurf->ulOffsetEnd);
181 const unsigned int iMax = this->fileSize - (unsigned int)((int8_t*)pcSurf-(int8_t*)pcHeader);
183 if (pcSurf->ulOffsetBaseVerts + pcSurf->ulNumVertices * sizeof(MDC::BaseVertex) > iMax ||
184 (pcSurf->ulNumCompFrames && pcSurf->ulOffsetCompVerts + pcSurf->ulNumVertices * sizeof(MDC::CompressedVertex) > iMax) ||
185 pcSurf->ulOffsetTriangles + pcSurf->ulNumTriangles * sizeof(MDC::Triangle) > iMax ||
186 pcSurf->ulOffsetTexCoords + pcSurf->ulNumVertices * sizeof(MDC::TexturCoord) > iMax ||
187 pcSurf->ulOffsetShaders + pcSurf->ulNumShaders * sizeof(MDC::Shader) > iMax ||
188 pcSurf->ulOffsetFrameBaseFrames + pcSurf->ulNumBaseFrames * 2 > iMax ||
189 (pcSurf->ulNumCompFrames && pcSurf->ulOffsetFrameCompFrames + pcSurf->ulNumCompFrames * 2 > iMax))
190 {
191 throw DeadlyImportError("Some of the offset values in the MDC surface header "
192 "are invalid and point somewhere behind the file.");
193 }
194 }
196 // ------------------------------------------------------------------------------------------------
197 // Setup configuration properties
198 void MDCImporter::SetupProperties(const Importer* pImp)
199 {
200 // The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the
201 // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
202 if(static_cast<unsigned int>(-1) == (configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_MDC_KEYFRAME,-1))){
203 configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
204 }
205 }
207 // ------------------------------------------------------------------------------------------------
208 // Imports the given file into the given scene structure.
209 void MDCImporter::InternReadFile(
210 const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
211 {
212 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
214 // Check whether we can read from the file
215 if( file.get() == NULL)
216 throw DeadlyImportError( "Failed to open MDC file " + pFile + ".");
218 // check whether the mdc file is large enough to contain the file header
219 fileSize = (unsigned int)file->FileSize();
220 if( fileSize < sizeof(MDC::Header))
221 throw DeadlyImportError( "MDC File is too small.");
223 std::vector<unsigned char> mBuffer2(fileSize);
224 file->Read( &mBuffer2[0], 1, fileSize);
225 mBuffer = &mBuffer2[0];
227 // validate the file header
228 this->pcHeader = (BE_NCONST MDC::Header*)this->mBuffer;
229 this->ValidateHeader();
231 std::vector<std::string> aszShaders;
233 // get a pointer to the frame we want to read
234 BE_NCONST MDC::Frame* pcFrame = (BE_NCONST MDC::Frame*)(this->mBuffer+
235 this->pcHeader->ulOffsetBorderFrames);
237 // no need to swap the other members, we won't need them
238 pcFrame += configFrameID;
239 AI_SWAP4( pcFrame->localOrigin[0] );
240 AI_SWAP4( pcFrame->localOrigin[1] );
241 AI_SWAP4( pcFrame->localOrigin[2] );
243 // get the number of valid surfaces
244 BE_NCONST MDC::Surface* pcSurface, *pcSurface2;
245 pcSurface = pcSurface2 = new (mBuffer + pcHeader->ulOffsetSurfaces) MDC::Surface;
246 unsigned int iNumShaders = 0;
247 for (unsigned int i = 0; i < pcHeader->ulNumSurfaces;++i)
248 {
249 // validate the surface header
250 this->ValidateSurfaceHeader(pcSurface2);
252 if (pcSurface2->ulNumVertices && pcSurface2->ulNumTriangles)++pScene->mNumMeshes;
253 iNumShaders += pcSurface2->ulNumShaders;
254 pcSurface2 = new ((int8_t*)pcSurface2 + pcSurface2->ulOffsetEnd) MDC::Surface;
255 }
256 aszShaders.reserve(iNumShaders);
257 pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
259 // necessary that we don't crash if an exception occurs
260 for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
261 pScene->mMeshes[i] = NULL;
263 // now read all surfaces
264 unsigned int iDefaultMatIndex = UINT_MAX;
265 for (unsigned int i = 0, iNum = 0; i < pcHeader->ulNumSurfaces;++i)
266 {
267 if (!pcSurface->ulNumVertices || !pcSurface->ulNumTriangles)continue;
268 aiMesh* pcMesh = pScene->mMeshes[iNum++] = new aiMesh();
270 pcMesh->mNumFaces = pcSurface->ulNumTriangles;
271 pcMesh->mNumVertices = pcMesh->mNumFaces * 3;
273 // store the name of the surface for use as node name.
274 // FIX: make sure there is a 0 termination
275 const_cast<char&>(pcSurface->ucName[AI_MDC_MAXQPATH-1]) = '\0';
276 pcMesh->mTextureCoords[3] = (aiVector3D*)pcSurface->ucName;
278 // go to the first shader in the file. ignore the others.
279 if (pcSurface->ulNumShaders)
280 {
281 const MDC::Shader* pcShader = (const MDC::Shader*)((int8_t*)pcSurface + pcSurface->ulOffsetShaders);
282 pcMesh->mMaterialIndex = (unsigned int)aszShaders.size();
284 // create a new shader
285 aszShaders.push_back(std::string( pcShader->ucName, std::min(
286 ::strlen(pcShader->ucName),sizeof(pcShader->ucName)) ));
287 }
288 // need to create a default material
289 else if (UINT_MAX == iDefaultMatIndex)
290 {
291 pcMesh->mMaterialIndex = iDefaultMatIndex = (unsigned int)aszShaders.size();
292 aszShaders.push_back(std::string());
293 }
294 // otherwise assign a reference to the default material
295 else pcMesh->mMaterialIndex = iDefaultMatIndex;
297 // allocate output storage for the mesh
298 aiVector3D* pcVertCur = pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
299 aiVector3D* pcNorCur = pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
300 aiVector3D* pcUVCur = pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
301 aiFace* pcFaceCur = pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
303 // create all vertices/faces
304 BE_NCONST MDC::Triangle* pcTriangle = (BE_NCONST MDC::Triangle*)
305 ((int8_t*)pcSurface+pcSurface->ulOffsetTriangles);
307 BE_NCONST MDC::TexturCoord* const pcUVs = (BE_NCONST MDC::TexturCoord*)
308 ((int8_t*)pcSurface+pcSurface->ulOffsetTexCoords);
310 // get a pointer to the uncompressed vertices
311 int16_t iOfs = *((int16_t*) ((int8_t*) pcSurface +
312 pcSurface->ulOffsetFrameBaseFrames) + this->configFrameID);
314 AI_SWAP2(iOfs);
316 BE_NCONST MDC::BaseVertex* const pcVerts = (BE_NCONST MDC::BaseVertex*)
317 ((int8_t*)pcSurface+pcSurface->ulOffsetBaseVerts) +
318 ((int)iOfs * pcSurface->ulNumVertices * 4);
320 // do the main swapping stuff ...
321 #if (defined AI_BUILD_BIG_ENDIAN)
323 // swap all triangles
324 for (unsigned int i = 0; i < pcSurface->ulNumTriangles;++i)
325 {
326 AI_SWAP4( pcTriangle[i].aiIndices[0] );
327 AI_SWAP4( pcTriangle[i].aiIndices[1] );
328 AI_SWAP4( pcTriangle[i].aiIndices[2] );
329 }
331 // swap all vertices
332 for (unsigned int i = 0; i < pcSurface->ulNumVertices*pcSurface->ulNumBaseFrames;++i)
333 {
334 AI_SWAP2( pcVerts->normal );
335 AI_SWAP2( pcVerts->x );
336 AI_SWAP2( pcVerts->y );
337 AI_SWAP2( pcVerts->z );
338 }
340 // swap all texture coordinates
341 for (unsigned int i = 0; i < pcSurface->ulNumVertices;++i)
342 {
343 AI_SWAP4( pcUVs->v );
344 AI_SWAP4( pcUVs->v );
345 }
347 #endif
349 const MDC::CompressedVertex* pcCVerts = NULL;
350 int16_t* mdcCompVert = NULL;
352 // access compressed frames for large frame numbers, but never for the first
353 if( this->configFrameID && pcSurface->ulNumCompFrames > 0 )
354 {
355 mdcCompVert = (int16_t*) ((int8_t*)pcSurface+pcSurface->ulOffsetFrameCompFrames) + this->configFrameID;
356 AI_SWAP2P(mdcCompVert);
357 if( *mdcCompVert >= 0 )
358 {
359 pcCVerts = (const MDC::CompressedVertex*)((int8_t*)pcSurface +
360 pcSurface->ulOffsetCompVerts) + *mdcCompVert * pcSurface->ulNumVertices;
361 }
362 else mdcCompVert = NULL;
363 }
365 // copy all faces
366 for (unsigned int iFace = 0; iFace < pcSurface->ulNumTriangles;++iFace,
367 ++pcTriangle,++pcFaceCur)
368 {
369 const unsigned int iOutIndex = iFace*3;
370 pcFaceCur->mNumIndices = 3;
371 pcFaceCur->mIndices = new unsigned int[3];
373 for (unsigned int iIndex = 0; iIndex < 3;++iIndex,
374 ++pcVertCur,++pcUVCur,++pcNorCur)
375 {
376 uint32_t quak = pcTriangle->aiIndices[iIndex];
377 if (quak >= pcSurface->ulNumVertices)
378 {
379 DefaultLogger::get()->error("MDC vertex index is out of range");
380 quak = pcSurface->ulNumVertices-1;
381 }
383 // compressed vertices?
384 if (mdcCompVert)
385 {
386 MDC::BuildVertex(*pcFrame,pcVerts[quak],pcCVerts[quak],
387 *pcVertCur,*pcNorCur);
388 }
389 else
390 {
391 // copy position
392 pcVertCur->x = pcVerts[quak].x * AI_MDC_BASE_SCALING;
393 pcVertCur->y = pcVerts[quak].y * AI_MDC_BASE_SCALING;
394 pcVertCur->z = pcVerts[quak].z * AI_MDC_BASE_SCALING;
396 // copy normals
397 MD3::LatLngNormalToVec3( pcVerts[quak].normal, &pcNorCur->x );
399 // copy texture coordinates
400 pcUVCur->x = pcUVs[quak].u;
401 pcUVCur->y = 1.0f-pcUVs[quak].v; // DX to OGL
402 }
403 pcVertCur->x += pcFrame->localOrigin[0] ;
404 pcVertCur->y += pcFrame->localOrigin[1] ;
405 pcVertCur->z += pcFrame->localOrigin[2] ;
406 }
408 // swap the face order - DX to OGL
409 pcFaceCur->mIndices[0] = iOutIndex + 2;
410 pcFaceCur->mIndices[1] = iOutIndex + 1;
411 pcFaceCur->mIndices[2] = iOutIndex + 0;
412 }
414 pcSurface = new ((int8_t*)pcSurface + pcSurface->ulOffsetEnd) MDC::Surface;
415 }
417 // create a flat node graph with a root node and one child for each surface
418 if (!pScene->mNumMeshes)
419 throw DeadlyImportError( "Invalid MDC file: File contains no valid mesh");
420 else if (1 == pScene->mNumMeshes)
421 {
422 pScene->mRootNode = new aiNode();
423 pScene->mRootNode->mName.Set(std::string((const char*)pScene->mMeshes[0]->mTextureCoords[3]));
424 pScene->mRootNode->mNumMeshes = 1;
425 pScene->mRootNode->mMeshes = new unsigned int[1];
426 pScene->mRootNode->mMeshes[0] = 0;
427 }
428 else
429 {
430 pScene->mRootNode = new aiNode();
431 pScene->mRootNode->mNumChildren = pScene->mNumMeshes;
432 pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes];
433 pScene->mRootNode->mName.Set("<root>");
434 for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
435 {
436 aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode();
437 pcNode->mParent = pScene->mRootNode;
438 pcNode->mName.Set(std::string((const char*)pScene->mMeshes[i]->mTextureCoords[3]));
439 pcNode->mNumMeshes = 1;
440 pcNode->mMeshes = new unsigned int[1];
441 pcNode->mMeshes[0] = i;
442 }
443 }
445 // make sure we invalidate the pointer to the mesh name
446 for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
447 pScene->mMeshes[i]->mTextureCoords[3] = NULL;
449 // create materials
450 pScene->mNumMaterials = (unsigned int)aszShaders.size();
451 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials];
452 for (unsigned int i = 0; i < pScene->mNumMaterials;++i)
453 {
454 aiMaterial* pcMat = new aiMaterial();
455 pScene->mMaterials[i] = pcMat;
457 const std::string& name = aszShaders[i];
459 int iMode = (int)aiShadingMode_Gouraud;
460 pcMat->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
462 // add a small ambient color value - RtCW seems to have one
463 aiColor3D clr;
464 clr.b = clr.g = clr.r = 0.05f;
465 pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
467 if (name.length())clr.b = clr.g = clr.r = 1.0f;
468 else clr.b = clr.g = clr.r = 0.6f;
470 pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
471 pcMat->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
473 if (name.length())
474 {
475 aiString path;
476 path.Set(name);
477 pcMat->AddProperty(&path,AI_MATKEY_TEXTURE_DIFFUSE(0));
478 }
479 }
480 }
482 #endif // !! ASSIMP_BUILD_NO_MDC_IMPORTER