vrshoot

view libs/assimp/HMPLoader.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 MDL importer class */
44 #include "AssimpPCH.h"
45 #ifndef ASSIMP_BUILD_NO_HMP_IMPORTER
47 // internal headers
48 #include "HMPLoader.h"
49 #include "MD2FileData.h"
51 using namespace Assimp;
53 static const aiImporterDesc desc = {
54 "3D GameStudio Heightmap (HMP) Importer",
55 "",
56 "",
57 "",
58 aiImporterFlags_SupportBinaryFlavour,
59 0,
60 0,
61 0,
62 0,
63 "hmp"
64 };
66 // ------------------------------------------------------------------------------------------------
67 // Constructor to be privately used by Importer
68 HMPImporter::HMPImporter()
69 {
70 // nothing to do here
71 }
73 // ------------------------------------------------------------------------------------------------
74 // Destructor, private as well
75 HMPImporter::~HMPImporter()
76 {
77 // nothing to do here
78 }
80 // ------------------------------------------------------------------------------------------------
81 // Returns whether the class can handle the format of the given file.
82 bool HMPImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool cs) const
83 {
84 const std::string extension = GetExtension(pFile);
85 if (extension == "hmp" )
86 return true;
88 // if check for extension is not enough, check for the magic tokens
89 if (!extension.length() || cs) {
90 uint32_t tokens[3];
91 tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4;
92 tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5;
93 tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7;
94 return CheckMagicToken(pIOHandler,pFile,tokens,3,0);
95 }
96 return false;
97 }
99 // ------------------------------------------------------------------------------------------------
100 // Get list of all file extensions that are handled by this loader
101 const aiImporterDesc* HMPImporter::GetInfo () const
102 {
103 return &desc;
104 }
106 // ------------------------------------------------------------------------------------------------
107 // Imports the given file into the given scene structure.
108 void HMPImporter::InternReadFile( const std::string& pFile,
109 aiScene* _pScene, IOSystem* _pIOHandler)
110 {
111 pScene = _pScene;
112 pIOHandler = _pIOHandler;
113 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
115 // Check whether we can read from the file
116 if( file.get() == NULL)
117 throw DeadlyImportError( "Failed to open HMP file " + pFile + ".");
119 // Check whether the HMP file is large enough to contain
120 // at least the file header
121 const size_t fileSize = file->FileSize();
122 if( fileSize < 50)
123 throw DeadlyImportError( "HMP File is too small.");
125 // Allocate storage and copy the contents of the file to a memory buffer
126 std::vector<uint8_t> buffer(fileSize);
127 mBuffer = &buffer[0];
128 file->Read( (void*)mBuffer, 1, fileSize);
129 iFileSize = (unsigned int)fileSize;
131 // Determine the file subtype and call the appropriate member function
132 const uint32_t iMagic = *((uint32_t*)this->mBuffer);
134 // HMP4 format
135 if (AI_HMP_MAGIC_NUMBER_LE_4 == iMagic ||
136 AI_HMP_MAGIC_NUMBER_BE_4 == iMagic)
137 {
138 DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A4, magic word is HMP4");
139 InternReadFile_HMP4();
140 }
141 // HMP5 format
142 else if (AI_HMP_MAGIC_NUMBER_LE_5 == iMagic ||
143 AI_HMP_MAGIC_NUMBER_BE_5 == iMagic)
144 {
145 DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A5, magic word is HMP5");
146 InternReadFile_HMP5();
147 }
148 // HMP7 format
149 else if (AI_HMP_MAGIC_NUMBER_LE_7 == iMagic ||
150 AI_HMP_MAGIC_NUMBER_BE_7 == iMagic)
151 {
152 DefaultLogger::get()->debug("HMP subtype: 3D GameStudio A7, magic word is HMP7");
153 InternReadFile_HMP7();
154 }
155 else
156 {
157 // Print the magic word to the logger
158 char szBuffer[5];
159 szBuffer[0] = ((char*)&iMagic)[0];
160 szBuffer[1] = ((char*)&iMagic)[1];
161 szBuffer[2] = ((char*)&iMagic)[2];
162 szBuffer[3] = ((char*)&iMagic)[3];
163 szBuffer[4] = '\0';
165 // We're definitely unable to load this file
166 throw DeadlyImportError( "Unknown HMP subformat " + pFile +
167 ". Magic word (" + szBuffer + ") is not known");
168 }
170 // Set the AI_SCENE_FLAGS_TERRAIN bit
171 pScene->mFlags |= AI_SCENE_FLAGS_TERRAIN;
173 // File buffer destructs automatically now
174 }
176 // ------------------------------------------------------------------------------------------------
177 void HMPImporter::ValidateHeader_HMP457( )
178 {
179 const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
181 if (120 > iFileSize)
182 {
183 throw DeadlyImportError("HMP file is too small (header size is "
184 "120 bytes, this file is smaller)");
185 }
187 if (!pcHeader->ftrisize_x || !pcHeader->ftrisize_y)
188 throw DeadlyImportError("Size of triangles in either x or y direction is zero");
190 if(pcHeader->fnumverts_x < 1.0f || (pcHeader->numverts/pcHeader->fnumverts_x) < 1.0f)
191 throw DeadlyImportError("Number of triangles in either x or y direction is zero");
193 if(!pcHeader->numframes)
194 throw DeadlyImportError("There are no frames. At least one should be there");
196 }
198 // ------------------------------------------------------------------------------------------------
199 void HMPImporter::InternReadFile_HMP4( )
200 {
201 throw DeadlyImportError("HMP4 is currently not supported");
202 }
204 // ------------------------------------------------------------------------------------------------
205 void HMPImporter::InternReadFile_HMP5( )
206 {
207 // read the file header and skip everything to byte 84
208 const HMP::Header_HMP5* pcHeader = (const HMP::Header_HMP5*)mBuffer;
209 const unsigned char* szCurrent = (const unsigned char*)(mBuffer+84);
210 ValidateHeader_HMP457();
212 // generate an output mesh
213 pScene->mNumMeshes = 1;
214 pScene->mMeshes = new aiMesh*[1];
215 aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
217 pcMesh->mMaterialIndex = 0;
218 pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
219 pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
221 const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
222 const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
224 // generate/load a material for the terrain
225 CreateMaterial(szCurrent,&szCurrent);
227 // goto offset 120, I don't know why ...
228 // (fixme) is this the frame header? I assume yes since it starts with 2.
229 szCurrent += 36;
230 SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
232 // now load all vertices from the file
233 aiVector3D* pcVertOut = pcMesh->mVertices;
234 aiVector3D* pcNorOut = pcMesh->mNormals;
235 const HMP::Vertex_HMP5* src = (const HMP::Vertex_HMP5*) szCurrent;
236 for (unsigned int y = 0; y < height;++y)
237 {
238 for (unsigned int x = 0; x < width;++x)
239 {
240 pcVertOut->x = x * pcHeader->ftrisize_x;
241 pcVertOut->y = y * pcHeader->ftrisize_y;
242 pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
243 MD2::LookupNormalIndex(src->normals162index, *pcNorOut );
244 ++pcVertOut;++pcNorOut;++src;
245 }
246 }
248 // generate texture coordinates if necessary
249 if (pcHeader->numskins)
250 GenerateTextureCoords(width,height);
252 // now build a list of faces
253 CreateOutputFaceList(width,height);
255 // there is no nodegraph in HMP files. Simply assign the one mesh
256 // (no, not the one ring) to the root node
257 pScene->mRootNode = new aiNode();
258 pScene->mRootNode->mName.Set("terrain_root");
259 pScene->mRootNode->mNumMeshes = 1;
260 pScene->mRootNode->mMeshes = new unsigned int[1];
261 pScene->mRootNode->mMeshes[0] = 0;
262 }
264 // ------------------------------------------------------------------------------------------------
265 void HMPImporter::InternReadFile_HMP7( )
266 {
267 // read the file header and skip everything to byte 84
268 const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
269 const unsigned char* szCurrent = (const unsigned char*)(mBuffer+84);
270 ValidateHeader_HMP457();
272 // generate an output mesh
273 pScene->mNumMeshes = 1;
274 pScene->mMeshes = new aiMesh*[1];
275 aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
277 pcMesh->mMaterialIndex = 0;
278 pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
279 pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
281 const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
282 const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
284 // generate/load a material for the terrain
285 CreateMaterial(szCurrent,&szCurrent);
287 // goto offset 120, I don't know why ...
288 // (fixme) is this the frame header? I assume yes since it starts with 2.
289 szCurrent += 36;
291 SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
293 // now load all vertices from the file
294 aiVector3D* pcVertOut = pcMesh->mVertices;
295 aiVector3D* pcNorOut = pcMesh->mNormals;
296 const HMP::Vertex_HMP7* src = (const HMP::Vertex_HMP7*) szCurrent;
297 for (unsigned int y = 0; y < height;++y)
298 {
299 for (unsigned int x = 0; x < width;++x)
300 {
301 pcVertOut->x = x * pcHeader->ftrisize_x;
302 pcVertOut->y = y * pcHeader->ftrisize_y;
304 // FIXME: What exctly is the correct scaling factor to use?
305 // possibly pcHeader->scale_origin[2] in combination with a
306 // signed interpretation of src->z?
307 pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
309 pcNorOut->x = ((float)src->normal_x / 0x80 ); // * pcHeader->scale_origin[0];
310 pcNorOut->y = ((float)src->normal_y / 0x80 ); // * pcHeader->scale_origin[1];
311 pcNorOut->z = 1.0f;
312 pcNorOut->Normalize();
314 ++pcVertOut;++pcNorOut;++src;
315 }
316 }
318 // generate texture coordinates if necessary
319 if (pcHeader->numskins)GenerateTextureCoords(width,height);
321 // now build a list of faces
322 CreateOutputFaceList(width,height);
324 // there is no nodegraph in HMP files. Simply assign the one mesh
325 // (no, not the One Ring) to the root node
326 pScene->mRootNode = new aiNode();
327 pScene->mRootNode->mName.Set("terrain_root");
328 pScene->mRootNode->mNumMeshes = 1;
329 pScene->mRootNode->mMeshes = new unsigned int[1];
330 pScene->mRootNode->mMeshes[0] = 0;
331 }
333 // ------------------------------------------------------------------------------------------------
334 void HMPImporter::CreateMaterial(const unsigned char* szCurrent,
335 const unsigned char** szCurrentOut)
336 {
337 aiMesh* const pcMesh = pScene->mMeshes[0];
338 const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)mBuffer;
340 // we don't need to generate texture coordinates if
341 // we have no textures in the file ...
342 if (pcHeader->numskins)
343 {
344 pcMesh->mTextureCoords[0] = new aiVector3D[pcHeader->numverts];
345 pcMesh->mNumUVComponents[0] = 2;
347 // now read the first skin and skip all others
348 ReadFirstSkin(pcHeader->numskins,szCurrent,&szCurrent);
349 }
350 else
351 {
352 // generate a default material
353 const int iMode = (int)aiShadingMode_Gouraud;
354 aiMaterial* pcHelper = new aiMaterial();
355 pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
357 aiColor3D clr;
358 clr.b = clr.g = clr.r = 0.6f;
359 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
360 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
362 clr.b = clr.g = clr.r = 0.05f;
363 pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
365 aiString szName;
366 szName.Set(AI_DEFAULT_MATERIAL_NAME);
367 pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
369 // add the material to the scene
370 pScene->mNumMaterials = 1;
371 pScene->mMaterials = new aiMaterial*[1];
372 pScene->mMaterials[0] = pcHelper;
373 }
374 *szCurrentOut = szCurrent;
375 }
377 // ------------------------------------------------------------------------------------------------
378 void HMPImporter::CreateOutputFaceList(unsigned int width,unsigned int height)
379 {
380 aiMesh* const pcMesh = this->pScene->mMeshes[0];
382 // Allocate enough storage
383 pcMesh->mNumFaces = (width-1) * (height-1);
384 pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
386 pcMesh->mNumVertices = pcMesh->mNumFaces*4;
387 aiVector3D* pcVertices = new aiVector3D[pcMesh->mNumVertices];
388 aiVector3D* pcNormals = new aiVector3D[pcMesh->mNumVertices];
390 aiFace* pcFaceOut(pcMesh->mFaces);
391 aiVector3D* pcVertOut = pcVertices;
392 aiVector3D* pcNorOut = pcNormals;
394 aiVector3D* pcUVs = pcMesh->mTextureCoords[0] ? new aiVector3D[pcMesh->mNumVertices] : NULL;
395 aiVector3D* pcUVOut(pcUVs);
397 // Build the terrain square
398 unsigned int iCurrent = 0;
399 for (unsigned int y = 0; y < height-1;++y) {
400 for (unsigned int x = 0; x < width-1;++x,++pcFaceOut) {
401 pcFaceOut->mNumIndices = 4;
402 pcFaceOut->mIndices = new unsigned int[4];
404 *pcVertOut++ = pcMesh->mVertices[y*width+x];
405 *pcVertOut++ = pcMesh->mVertices[(y+1)*width+x];
406 *pcVertOut++ = pcMesh->mVertices[(y+1)*width+x+1];
407 *pcVertOut++ = pcMesh->mVertices[y*width+x+1];
410 *pcNorOut++ = pcMesh->mNormals[y*width+x];
411 *pcNorOut++ = pcMesh->mNormals[(y+1)*width+x];
412 *pcNorOut++ = pcMesh->mNormals[(y+1)*width+x+1];
413 *pcNorOut++ = pcMesh->mNormals[y*width+x+1];
415 if (pcMesh->mTextureCoords[0])
416 {
417 *pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x];
418 *pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x];
419 *pcUVOut++ = pcMesh->mTextureCoords[0][(y+1)*width+x+1];
420 *pcUVOut++ = pcMesh->mTextureCoords[0][y*width+x+1];
421 }
423 for (unsigned int i = 0; i < 4;++i)
424 pcFaceOut->mIndices[i] = iCurrent++;
425 }
426 }
427 delete[] pcMesh->mVertices;
428 pcMesh->mVertices = pcVertices;
430 delete[] pcMesh->mNormals;
431 pcMesh->mNormals = pcNormals;
433 if (pcMesh->mTextureCoords[0])
434 {
435 delete[] pcMesh->mTextureCoords[0];
436 pcMesh->mTextureCoords[0] = pcUVs;
437 }
438 }
440 // ------------------------------------------------------------------------------------------------
441 void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
442 const unsigned char** szCursorOut)
443 {
444 ai_assert(0 != iNumSkins && NULL != szCursor);
446 // read the type of the skin ...
447 // sometimes we need to skip 12 bytes here, I don't know why ...
448 uint32_t iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
449 if (0 == iType)
450 {
451 szCursor += sizeof(uint32_t) * 2;
452 iType = *((uint32_t*)szCursor);szCursor += sizeof(uint32_t);
453 if (!iType)
454 throw DeadlyImportError("Unable to read HMP7 skin chunk");
456 }
457 // read width and height
458 uint32_t iWidth = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
459 uint32_t iHeight = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
461 // allocate an output material
462 aiMaterial* pcMat = new aiMaterial();
464 // read the skin, this works exactly as for MDL7
465 ParseSkinLump_3DGS_MDL7(szCursor,&szCursor,
466 pcMat,iType,iWidth,iHeight);
468 // now we need to skip any other skins ...
469 for (unsigned int i = 1; i< iNumSkins;++i)
470 {
471 iType = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
472 iWidth = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
473 iHeight = *((uint32_t*)szCursor); szCursor += sizeof(uint32_t);
475 SkipSkinLump_3DGS_MDL7(szCursor,&szCursor,iType,iWidth,iHeight);
476 SizeCheck(szCursor);
477 }
479 // setup the material ...
480 pScene->mNumMaterials = 1;
481 pScene->mMaterials = new aiMaterial*[1];
482 pScene->mMaterials[0] = pcMat;
484 *szCursorOut = szCursor;
485 }
487 // ------------------------------------------------------------------------------------------------
488 // Generate proepr texture coords
489 void HMPImporter::GenerateTextureCoords(
490 const unsigned int width, const unsigned int height)
491 {
492 ai_assert(NULL != pScene->mMeshes && NULL != pScene->mMeshes[0] &&
493 NULL != pScene->mMeshes[0]->mTextureCoords[0]);
495 aiVector3D* uv = pScene->mMeshes[0]->mTextureCoords[0];
497 const float fY = (1.0f / height) + (1.0f / height) / (height-1);
498 const float fX = (1.0f / width) + (1.0f / width) / (width-1);
500 for (unsigned int y = 0; y < height;++y) {
501 for (unsigned int x = 0; x < width;++x,++uv) {
502 uv->y = fY*y;
503 uv->x = fX*x;
504 uv->z = 0.0f;
505 }
506 }
507 }
509 #endif // !! ASSIMP_BUILD_NO_HMP_IMPORTER