vrshoot

view libs/assimp/LWOLoader.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 LWOLoader.cpp
43 * @brief Implementation of the LWO importer class
44 */
46 #include "AssimpPCH.h"
47 #ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
49 // internal headers
50 #include "LWOLoader.h"
51 #include "StringComparison.h"
52 #include "SGSpatialSort.h"
53 #include "ByteSwap.h"
54 #include "ProcessHelper.h"
55 #include "ConvertToLHProcess.h"
57 using namespace Assimp;
59 static const aiImporterDesc desc = {
60 "LightWave/Modo Object Importer",
61 "",
62 "",
63 "http://www.newtek.com/lightwave.html\nhttp://www.luxology.com/modo/",
64 aiImporterFlags_SupportTextFlavour,
65 0,
66 0,
67 0,
68 0,
69 "lwo lxo"
70 };
72 // ------------------------------------------------------------------------------------------------
73 // Constructor to be privately used by Importer
74 LWOImporter::LWOImporter()
75 {}
77 // ------------------------------------------------------------------------------------------------
78 // Destructor, private as well
79 LWOImporter::~LWOImporter()
80 {}
82 // ------------------------------------------------------------------------------------------------
83 // Returns whether the class can handle the format of the given file.
84 bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
85 {
86 const std::string extension = GetExtension(pFile);
87 if (extension == "lwo" || extension == "lxo") {
88 return true;
89 }
91 // if check for extension is not enough, check for the magic tokens
92 if (!extension.length() || checkSig) {
93 uint32_t tokens[3];
94 tokens[0] = AI_LWO_FOURCC_LWOB;
95 tokens[1] = AI_LWO_FOURCC_LWO2;
96 tokens[2] = AI_LWO_FOURCC_LXOB;
97 return CheckMagicToken(pIOHandler,pFile,tokens,3,8);
98 }
99 return false;
100 }
102 // ------------------------------------------------------------------------------------------------
103 // Setup configuration properties
104 void LWOImporter::SetupProperties(const Importer* pImp)
105 {
106 configSpeedFlag = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0) ? true : false);
107 configLayerIndex = pImp->GetPropertyInteger (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,UINT_MAX);
108 configLayerName = pImp->GetPropertyString (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,"");
109 }
111 // ------------------------------------------------------------------------------------------------
112 // Get list of file extensions
113 const aiImporterDesc* LWOImporter::GetInfo () const
114 {
115 return &desc;
116 }
118 // ------------------------------------------------------------------------------------------------
119 // Imports the given file into the given scene structure.
120 void LWOImporter::InternReadFile( const std::string& pFile,
121 aiScene* pScene,
122 IOSystem* pIOHandler)
123 {
124 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
126 // Check whether we can read from the file
127 if( file.get() == NULL)
128 throw DeadlyImportError( "Failed to open LWO file " + pFile + ".");
130 if((this->fileSize = (unsigned int)file->FileSize()) < 12)
131 throw DeadlyImportError("LWO: The file is too small to contain the IFF header");
133 // Allocate storage and copy the contents of the file to a memory buffer
134 std::vector< uint8_t > mBuffer(fileSize);
135 file->Read( &mBuffer[0], 1, fileSize);
136 this->pScene = pScene;
138 // Determine the type of the file
139 uint32_t fileType;
140 const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
141 if (sz)throw DeadlyImportError(sz);
143 mFileBuffer = &mBuffer[0] + 12;
144 fileSize -= 12;
146 // Initialize some members with their default values
147 hasNamedLayer = false;
149 // Create temporary storage on the stack but store pointers to it in the class
150 // instance. Therefore everything will be destructed properly if an exception
151 // is thrown and we needn't take care of that.
152 LayerList _mLayers;
153 SurfaceList _mSurfaces;
154 TagList _mTags;
155 TagMappingTable _mMapping;
157 mLayers = &_mLayers;
158 mTags = &_mTags;
159 mMapping = &_mMapping;
160 mSurfaces = &_mSurfaces;
162 // Allocate a default layer (layer indices are 1-based from now)
163 mLayers->push_back(Layer());
164 mCurLayer = &mLayers->back();
165 mCurLayer->mName = "<LWODefault>";
166 mCurLayer->mIndex = -1;
168 // old lightwave file format (prior to v6)
169 if (AI_LWO_FOURCC_LWOB == fileType) {
170 DefaultLogger::get()->info("LWO file format: LWOB (<= LightWave 5.5)");
172 mIsLWO2 = false;
173 mIsLXOB = false;
174 LoadLWOBFile();
175 }
176 // New lightwave format
177 else if (AI_LWO_FOURCC_LWO2 == fileType) {
178 mIsLXOB = false;
179 DefaultLogger::get()->info("LWO file format: LWO2 (>= LightWave 6)");
180 }
181 // MODO file format
182 else if (AI_LWO_FOURCC_LXOB == fileType) {
183 mIsLXOB = true;
184 DefaultLogger::get()->info("LWO file format: LXOB (Modo)");
185 }
186 // we don't know this format
187 else
188 {
189 char szBuff[5];
190 szBuff[0] = (char)(fileType >> 24u);
191 szBuff[1] = (char)(fileType >> 16u);
192 szBuff[2] = (char)(fileType >> 8u);
193 szBuff[3] = (char)(fileType);
194 szBuff[4] = '\0';
195 throw DeadlyImportError(std::string("Unknown LWO sub format: ") + szBuff);
196 }
198 if (AI_LWO_FOURCC_LWOB != fileType) {
199 mIsLWO2 = true;
200 LoadLWO2File();
202 // The newer lightwave format allows the user to configure the
203 // loader that just one layer is used. If this is the case
204 // we need to check now whether the requested layer has been found.
205 if (UINT_MAX != configLayerIndex) {
206 unsigned int layerCount = 0;
207 for(std::list<LWO::Layer>::iterator itLayers=mLayers->begin(); itLayers!=mLayers->end(); itLayers++)
208 if (!itLayers->skip)
209 layerCount++;
210 if (layerCount!=2)
211 throw DeadlyImportError("LWO2: The requested layer was not found");
212 }
214 if (configLayerName.length() && !hasNamedLayer) {
215 throw DeadlyImportError("LWO2: Unable to find the requested layer: "
216 + configLayerName);
217 }
218 }
220 // now, as we have loaded all data, we can resolve cross-referenced tags and clips
221 ResolveTags();
222 ResolveClips();
224 // now process all layers and build meshes and nodes
225 std::vector<aiMesh*> apcMeshes;
226 std::map<uint16_t, aiNode*> apcNodes;
228 apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u));
230 unsigned int iDefaultSurface = UINT_MAX; // index of the default surface
231 for (LayerList::iterator lit = mLayers->begin(), lend = mLayers->end();lit != lend;++lit) {
232 LWO::Layer& layer = *lit;
233 if (layer.skip)
234 continue;
236 // I don't know whether there could be dummy layers, but it would be possible
237 const unsigned int meshStart = (unsigned int)apcMeshes.size();
238 if (!layer.mFaces.empty() && !layer.mTempPoints.empty()) {
240 // now sort all faces by the surfaces assigned to them
241 std::vector<SortedRep> pSorted(mSurfaces->size()+1);
243 unsigned int i = 0;
244 for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();it != end;++it,++i) {
245 // Check whether we support this face's type
246 if ((*it).type != AI_LWO_FACE && (*it).type != AI_LWO_PTCH &&
247 (*it).type != AI_LWO_BONE && (*it).type != AI_LWO_SUBD) {
248 continue;
249 }
251 unsigned int idx = (*it).surfaceIndex;
252 if (idx >= mTags->size())
253 {
254 DefaultLogger::get()->warn("LWO: Invalid face surface index");
255 idx = UINT_MAX;
256 }
257 if(UINT_MAX == idx || UINT_MAX == (idx = _mMapping[idx])) {
258 if (UINT_MAX == iDefaultSurface) {
259 iDefaultSurface = (unsigned int)mSurfaces->size();
260 mSurfaces->push_back(LWO::Surface());
261 LWO::Surface& surf = mSurfaces->back();
262 surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f;
263 surf.mName = "LWODefaultSurface";
264 }
265 idx = iDefaultSurface;
266 }
267 pSorted[idx].push_back(i);
268 }
269 if (UINT_MAX == iDefaultSurface) {
270 pSorted.erase(pSorted.end()-1);
271 }
272 for (unsigned int p = 0,i = 0;i < mSurfaces->size();++i) {
273 SortedRep& sorted = pSorted[i];
274 if (sorted.empty())
275 continue;
277 // generate the mesh
278 aiMesh* mesh = new aiMesh();
279 apcMeshes.push_back(mesh);
280 mesh->mNumFaces = (unsigned int)sorted.size();
282 // count the number of vertices
283 SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
284 for (;it != end;++it) {
285 mesh->mNumVertices += layer.mFaces[*it].mNumIndices;
286 }
288 aiVector3D *nrm = NULL, * pv = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
289 aiFace* pf = mesh->mFaces = new aiFace[mesh->mNumFaces];
290 mesh->mMaterialIndex = i;
292 // find out which vertex color channels and which texture coordinate
293 // channels are really required by the material attached to this mesh
294 unsigned int vUVChannelIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS];
295 unsigned int vVColorIndices[AI_MAX_NUMBER_OF_COLOR_SETS];
297 #if _DEBUG
298 for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui ) {
299 vUVChannelIndices[mui] = UINT_MAX;
300 }
301 for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui ) {
302 vVColorIndices[mui] = UINT_MAX;
303 }
304 #endif
306 FindUVChannels(_mSurfaces[i],sorted,layer,vUVChannelIndices);
307 FindVCChannels(_mSurfaces[i],sorted,layer,vVColorIndices);
309 // allocate storage for UV and CV channels
310 aiVector3D* pvUV[AI_MAX_NUMBER_OF_TEXTURECOORDS];
311 for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui ) {
312 if (UINT_MAX == vUVChannelIndices[mui]) {
313 break;
314 }
316 pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
318 // LightWave doesn't support more than 2 UV components (?)
319 mesh->mNumUVComponents[0] = 2;
320 }
322 if (layer.mNormals.name.length())
323 nrm = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
325 aiColor4D* pvVC[AI_MAX_NUMBER_OF_COLOR_SETS];
326 for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui) {
327 if (UINT_MAX == vVColorIndices[mui]) {
328 break;
329 }
330 pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
331 }
333 // we would not need this extra array, but the code is much cleaner if we use it
334 std::vector<unsigned int>& smoothingGroups = layer.mPointReferrers;
335 smoothingGroups.erase (smoothingGroups.begin(),smoothingGroups.end());
336 smoothingGroups.resize(mesh->mNumFaces,0);
338 // now convert all faces
339 unsigned int vert = 0;
340 std::vector<unsigned int>::iterator outIt = smoothingGroups.begin();
341 for (it = sorted.begin(); it != end;++it,++outIt) {
342 const LWO::Face& face = layer.mFaces[*it];
343 *outIt = face.smoothGroup;
345 // copy all vertices
346 for (unsigned int q = 0; q < face.mNumIndices;++q,++vert) {
347 register unsigned int idx = face.mIndices[q];
348 *pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/;
350 // process UV coordinates
351 for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_TEXTURECOORDS;++w) {
352 if (UINT_MAX == vUVChannelIndices[w]) {
353 break;
354 }
355 aiVector3D*& pp = pvUV[w];
356 const aiVector2D& src = ((aiVector2D*)&layer.mUVChannels[vUVChannelIndices[w]].rawData[0])[idx];
357 pp->x = src.x;
358 pp->y = src.y;
359 pp++;
360 }
362 // process normals (MODO extension)
363 if (nrm) {
364 *nrm = ((aiVector3D*)&layer.mNormals.rawData[0])[idx];
365 nrm->z *= -1.f;
366 ++nrm;
367 }
369 // process vertex colors
370 for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_COLOR_SETS;++w) {
371 if (UINT_MAX == vVColorIndices[w]) {
372 break;
373 }
374 *pvVC[w] = ((aiColor4D*)&layer.mVColorChannels[vVColorIndices[w]].rawData[0])[idx];
376 // If a RGB color map is explicitly requested delete the
377 // alpha channel - it could theoretically be != 1.
378 if(_mSurfaces[i].mVCMapType == AI_LWO_RGB)
379 pvVC[w]->a = 1.f;
381 pvVC[w]++;
382 }
384 #if 0
385 // process vertex weights. We can't properly reconstruct the whole skeleton for now,
386 // but we can create dummy bones for all weight channels which we have.
387 for (unsigned int w = 0; w < layer.mWeightChannels.size();++w)
388 {
389 }
390 #endif
392 face.mIndices[q] = vert;
393 }
394 pf->mIndices = face.mIndices;
395 pf->mNumIndices = face.mNumIndices;
396 unsigned int** p = (unsigned int**)&face.mIndices;*p = NULL; // HACK: make sure it won't be deleted
397 pf++;
398 }
400 if (!mesh->mNormals) {
401 // Compute normal vectors for the mesh - we can't use our GenSmoothNormal-
402 // Step here since it wouldn't handle smoothing groups correctly for LWO.
403 // So we use a separate implementation.
404 ComputeNormals(mesh,smoothingGroups,_mSurfaces[i]);
405 }
406 else DefaultLogger::get()->debug("LWO2: No need to compute normals, they're already there");
407 ++p;
408 }
409 }
411 // Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
412 unsigned int num = apcMeshes.size() - meshStart;
413 if (layer.mName != "<LWODefault>" || num > 0) {
414 aiNode* pcNode = new aiNode();
415 apcNodes[layer.mIndex] = pcNode;
416 pcNode->mName.Set(layer.mName);
417 pcNode->mParent = (aiNode*)&layer;
418 pcNode->mNumMeshes = num;
420 if (pcNode->mNumMeshes) {
421 pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
422 for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
423 pcNode->mMeshes[p] = p + meshStart;
424 }
425 }
426 }
428 if (apcNodes.empty() || apcMeshes.empty())
429 throw DeadlyImportError("LWO: No meshes loaded");
431 // The RemoveRedundantMaterials step will clean this up later
432 pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
433 for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat) {
434 aiMaterial* pcMat = new aiMaterial();
435 pScene->mMaterials[mat] = pcMat;
436 ConvertMaterial((*mSurfaces)[mat],pcMat);
437 }
439 // copy the meshes to the output structure
440 pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ];
441 ::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
443 // generate the final node graph
444 GenerateNodeGraph(apcNodes);
445 }
447 // ------------------------------------------------------------------------------------------------
448 void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
449 const LWO::Surface& surface)
450 {
451 // Allocate output storage
452 mesh->mNormals = new aiVector3D[mesh->mNumVertices];
454 // First generate per-face normals
455 aiVector3D* out;
456 std::vector<aiVector3D> faceNormals;
458 // ... in some cases that's already enough
459 if (!surface.mMaximumSmoothAngle)
460 out = mesh->mNormals;
461 else {
462 faceNormals.resize(mesh->mNumVertices);
463 out = &faceNormals[0];
464 }
466 aiFace* begin = mesh->mFaces, *const end = mesh->mFaces+mesh->mNumFaces;
467 for (; begin != end; ++begin) {
468 aiFace& face = *begin;
470 if(face.mNumIndices < 3) {
471 continue;
472 }
474 // LWO doc: "the normal is defined as the cross product of the first and last edges"
475 aiVector3D* pV1 = mesh->mVertices + face.mIndices[0];
476 aiVector3D* pV2 = mesh->mVertices + face.mIndices[1];
477 aiVector3D* pV3 = mesh->mVertices + face.mIndices[face.mNumIndices-1];
479 aiVector3D vNor = ((*pV2 - *pV1) ^(*pV3 - *pV1)).Normalize();
480 for (unsigned int i = 0; i < face.mNumIndices;++i)
481 out[face.mIndices[i]] = vNor;
482 }
483 if (!surface.mMaximumSmoothAngle)return;
484 const float posEpsilon = ComputePositionEpsilon(mesh);
486 // Now generate the spatial sort tree
487 SGSpatialSort sSort;
488 std::vector<unsigned int>::const_iterator it = smoothingGroups.begin();
489 for( begin = mesh->mFaces; begin != end; ++begin, ++it)
490 {
491 aiFace& face = *begin;
492 for (unsigned int i = 0; i < face.mNumIndices;++i)
493 {
494 register unsigned int tt = face.mIndices[i];
495 sSort.Add(mesh->mVertices[tt],tt,*it);
496 }
497 }
498 // Sort everything - this takes O(nlogn) time
499 sSort.Prepare();
500 std::vector<unsigned int> poResult;
501 poResult.reserve(20);
503 // Generate vertex normals. We have O(logn) for the binary lookup, which we need
504 // for n elements, thus the EXPECTED complexity is O(nlogn)
505 if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag) {
506 const float fLimit = cos(surface.mMaximumSmoothAngle);
508 for( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
509 const aiFace& face = *begin;
510 unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
511 for (; beginIdx != endIdx; ++beginIdx)
512 {
513 register unsigned int idx = *beginIdx;
514 sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
515 std::vector<unsigned int>::const_iterator a, end = poResult.end();
517 aiVector3D vNormals;
518 for (a = poResult.begin();a != end;++a) {
519 const aiVector3D& v = faceNormals[*a];
520 if (v * faceNormals[idx] < fLimit)
521 continue;
522 vNormals += v;
523 }
524 mesh->mNormals[idx] = vNormals.Normalize();
525 }
526 }
527 }
528 // faster code path in case there is no smooth angle
529 else {
530 std::vector<bool> vertexDone(mesh->mNumVertices,false);
531 for( begin = mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it) {
532 const aiFace& face = *begin;
533 unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
534 for (; beginIdx != endIdx; ++beginIdx)
535 {
536 register unsigned int idx = *beginIdx;
537 if (vertexDone[idx])
538 continue;
539 sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
540 std::vector<unsigned int>::const_iterator a, end = poResult.end();
542 aiVector3D vNormals;
543 for (a = poResult.begin();a != end;++a) {
544 const aiVector3D& v = faceNormals[*a];
545 vNormals += v;
546 }
547 vNormals.Normalize();
548 for (a = poResult.begin();a != end;++a) {
549 mesh->mNormals[*a] = vNormals;
550 vertexDone[*a] = true;
551 }
552 }
553 }
554 }
555 }
557 // ------------------------------------------------------------------------------------------------
558 void LWOImporter::GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes)
559 {
560 // now generate the final nodegraph - generate a root node and attach children
561 aiNode* root = pScene->mRootNode = new aiNode();
562 root->mName.Set("<LWORoot>");
564 //Set parent of all children, inserting pivots
565 //std::cout << "Set parent of all children" << std::endl;
566 std::map<uint16_t, aiNode*> mapPivot;
567 for (std::map<uint16_t,aiNode*>::iterator itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
569 //Get the parent index
570 LWO::Layer* nodeLayer = (LWO::Layer*)(itapcNodes->second->mParent);
571 uint16_t parentIndex = nodeLayer->mParent;
573 //Create pivot node, store it into the pivot map, and set the parent as the pivot
574 aiNode* pivotNode = new aiNode();
575 pivotNode->mName.Set("Pivot-"+std::string(itapcNodes->second->mName.data));
576 mapPivot[-(itapcNodes->first+2)] = pivotNode;
577 itapcNodes->second->mParent = pivotNode;
579 //Look for the parent node to attach the pivot to
580 if (apcNodes.find(parentIndex) != apcNodes.end()) {
581 pivotNode->mParent = apcNodes[parentIndex];
582 } else {
583 //If not, attach to the root node
584 pivotNode->mParent = root;
585 }
587 //Set the node and the pivot node transformation
588 itapcNodes->second->mTransformation.a4 = -nodeLayer->mPivot.x;
589 itapcNodes->second->mTransformation.b4 = -nodeLayer->mPivot.y;
590 itapcNodes->second->mTransformation.c4 = -nodeLayer->mPivot.z;
591 pivotNode->mTransformation.a4 = nodeLayer->mPivot.x;
592 pivotNode->mTransformation.b4 = nodeLayer->mPivot.y;
593 pivotNode->mTransformation.c4 = nodeLayer->mPivot.z;
594 }
596 //Merge pivot map into node map
597 //std::cout << "Merge pivot map into node map" << std::endl;
598 for (std::map<uint16_t, aiNode*>::iterator itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) {
599 apcNodes[itMapPivot->first] = itMapPivot->second;
600 }
602 //Set children of all parents
603 apcNodes[-1] = root;
604 for (std::map<uint16_t,aiNode*>::iterator itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) {
605 for (std::map<uint16_t,aiNode*>::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) {
606 if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) {
607 ++(itMapParentNodes->second->mNumChildren);
608 }
609 }
610 if (itMapParentNodes->second->mNumChildren) {
611 itMapParentNodes->second->mChildren = new aiNode* [ itMapParentNodes->second->mNumChildren ];
612 uint16_t p = 0;
613 for (std::map<uint16_t,aiNode*>::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) {
614 if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) {
615 itMapParentNodes->second->mChildren[p++] = itMapChildNodes->second;
616 }
617 }
618 }
619 }
621 if (!pScene->mRootNode->mNumChildren)
622 throw DeadlyImportError("LWO: Unable to build a valid node graph");
624 // Remove a single root node with no meshes assigned to it ...
625 if (1 == pScene->mRootNode->mNumChildren) {
626 aiNode* pc = pScene->mRootNode->mChildren[0];
627 pc->mParent = pScene->mRootNode->mChildren[0] = NULL;
628 delete pScene->mRootNode;
629 pScene->mRootNode = pc;
630 }
632 // convert the whole stuff to RH with CCW winding
633 MakeLeftHandedProcess maker;
634 maker.Execute(pScene);
636 FlipWindingOrderProcess flipper;
637 flipper.Execute(pScene);
638 }
640 // ------------------------------------------------------------------------------------------------
641 void LWOImporter::ResolveTags()
642 {
643 // --- this function is used for both LWO2 and LWOB
644 mMapping->resize(mTags->size(), UINT_MAX);
645 for (unsigned int a = 0; a < mTags->size();++a) {
647 const std::string& c = (*mTags)[a];
648 for (unsigned int i = 0; i < mSurfaces->size();++i) {
650 const std::string& d = (*mSurfaces)[i].mName;
651 if (!ASSIMP_stricmp(c,d)) {
653 (*mMapping)[a] = i;
654 break;
655 }
656 }
657 }
658 }
660 // ------------------------------------------------------------------------------------------------
661 void LWOImporter::ResolveClips()
662 {
663 for( unsigned int i = 0; i < mClips.size();++i) {
665 Clip& clip = mClips[i];
666 if (Clip::REF == clip.type) {
668 if (clip.clipRef >= mClips.size()) {
669 DefaultLogger::get()->error("LWO2: Clip referrer index is out of range");
670 clip.clipRef = 0;
671 }
673 Clip& dest = mClips[clip.clipRef];
674 if (Clip::REF == dest.type) {
675 DefaultLogger::get()->error("LWO2: Clip references another clip reference");
676 clip.type = Clip::UNSUPPORTED;
677 }
679 else {
680 clip.path = dest.path;
681 clip.type = dest.type;
682 }
683 }
684 }
685 }
687 // ------------------------------------------------------------------------------------------------
688 void LWOImporter::AdjustTexturePath(std::string& out)
689 {
690 // --- this function is used for both LWO2 and LWOB
691 if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)")) {
693 // remove the (sequence) and append 000
694 DefaultLogger::get()->info("LWOB: Sequence of animated texture found. It will be ignored");
695 out = out.substr(0,out.length()-10) + "000";
696 }
698 // format: drive:path/file - we just need to insert a slash after the drive
699 std::string::size_type n = out.find_first_of(':');
700 if (std::string::npos != n) {
701 out.insert(n+1,"/");
702 }
703 }
705 // ------------------------------------------------------------------------------------------------
706 void LWOImporter::LoadLWOTags(unsigned int size)
707 {
708 // --- this function is used for both LWO2 and LWOB
710 const char* szCur = (const char*)mFileBuffer, *szLast = szCur;
711 const char* const szEnd = szLast+size;
712 while (szCur < szEnd)
713 {
714 if (!(*szCur))
715 {
716 const size_t len = (size_t)(szCur-szLast);
717 // FIX: skip empty-sized tags
718 if (len)
719 mTags->push_back(std::string(szLast,len));
720 szCur += (len&0x1 ? 1 : 2);
721 szLast = szCur;
722 }
723 szCur++;
724 }
725 }
727 // ------------------------------------------------------------------------------------------------
728 void LWOImporter::LoadLWOPoints(unsigned int length)
729 {
730 // --- this function is used for both LWO2 and LWOB but for
731 // LWO2 we need to allocate 25% more storage - it could be we'll
732 // need to duplicate some points later.
733 register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
734 if (mIsLWO2)
735 {
736 mCurLayer->mTempPoints.reserve ( regularSize + (regularSize>>2u) );
737 mCurLayer->mTempPoints.resize ( regularSize );
739 // initialize all point referrers with the default values
740 mCurLayer->mPointReferrers.reserve ( regularSize + (regularSize>>2u) );
741 mCurLayer->mPointReferrers.resize ( regularSize, UINT_MAX );
742 }
743 else mCurLayer->mTempPoints.resize( regularSize );
745 // perform endianess conversions
746 #ifndef AI_BUILD_BIG_ENDIAN
747 for (unsigned int i = 0; i < length>>2;++i)
748 ByteSwap::Swap4( mFileBuffer + (i << 2));
749 #endif
750 ::memcpy(&mCurLayer->mTempPoints[0],mFileBuffer,length);
751 }
753 // ------------------------------------------------------------------------------------------------
754 void LWOImporter::LoadLWO2Polygons(unsigned int length)
755 {
756 LE_NCONST uint16_t* const end = (LE_NCONST uint16_t*)(mFileBuffer+length);
757 const uint32_t type = GetU4();
759 // Determine the type of the polygons
760 switch (type)
761 {
762 // read unsupported stuff too (although we wont process it)
763 case AI_LWO_MBAL:
764 DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (METABALL)");
765 break;
766 case AI_LWO_CURV:
767 DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (SPLINE)");;
768 break;
770 // These are ok with no restrictions
771 case AI_LWO_PTCH:
772 case AI_LWO_FACE:
773 case AI_LWO_BONE:
774 case AI_LWO_SUBD:
775 break;
776 default:
778 // hm!? wtf is this? ok ...
779 DefaultLogger::get()->error("LWO2: Ignoring unknown polygon type.");
780 break;
781 }
783 // first find out how many faces and vertices we'll finally need
784 uint16_t* cursor= (uint16_t*)mFileBuffer;
786 unsigned int iNumFaces = 0,iNumVertices = 0;
787 CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
789 // allocate the output array and copy face indices
790 if (iNumFaces) {
791 cursor = (uint16_t*)mFileBuffer;
793 mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
794 FaceList::iterator it = mCurLayer->mFaces.begin();
795 CopyFaceIndicesLWO2(it,cursor,end);
796 }
797 }
799 // ------------------------------------------------------------------------------------------------
800 void LWOImporter::CountVertsAndFacesLWO2(unsigned int& verts, unsigned int& faces,
801 uint16_t*& cursor, const uint16_t* const end, unsigned int max)
802 {
803 while (cursor < end && max--)
804 {
805 AI_LSWAP2P(cursor);
806 uint16_t numIndices = *cursor++;
807 numIndices &= 0x03FF;
808 verts += numIndices;++faces;
810 for(uint16_t i = 0; i < numIndices; i++)
811 ReadVSizedIntLWO2((uint8_t*&)cursor);
812 }
813 }
815 // ------------------------------------------------------------------------------------------------
816 void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
817 uint16_t*& cursor,
818 const uint16_t* const end)
819 {
820 while (cursor < end) {
822 LWO::Face& face = *it++;;
823 if((face.mNumIndices = (*cursor++) & 0x03FF)) /* byte swapping has already been done */ {
824 face.mIndices = new unsigned int[face.mNumIndices];
825 for(unsigned int i = 0; i < face.mNumIndices; i++)
826 {
827 face.mIndices[i] = ReadVSizedIntLWO2((uint8_t*&)cursor) + mCurLayer->mPointIDXOfs;
828 if(face.mIndices[i] > mCurLayer->mTempPoints.size())
829 {
830 DefaultLogger::get()->warn("LWO2: Failure evaluating face record, index is out of range");
831 face.mIndices[i] = (unsigned int)mCurLayer->mTempPoints.size()-1;
832 }
833 }
834 }
835 else throw DeadlyImportError("LWO2: Encountered invalid face record with zero indices");
836 }
837 }
840 // ------------------------------------------------------------------------------------------------
841 void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
842 {
843 LE_NCONST uint8_t* const end = mFileBuffer+length;
845 AI_LWO_VALIDATE_CHUNK_LENGTH(length,PTAG,4);
846 uint32_t type = GetU4();
848 if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
849 return;
851 while (mFileBuffer < end) {
853 unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
854 unsigned int j = GetU2();
856 if (i >= mCurLayer->mFaces.size()) {
857 DefaultLogger::get()->warn("LWO2: face index in PTAG is out of range");
858 continue;
859 }
861 switch (type) {
863 case AI_LWO_SURF:
864 mCurLayer->mFaces[i].surfaceIndex = j;
865 break;
866 case AI_LWO_SMGP: /* is that really used? */
867 mCurLayer->mFaces[i].smoothGroup = j;
868 break;
869 };
870 }
871 }
873 // ------------------------------------------------------------------------------------------------
874 template <class T>
875 VMapEntry* FindEntry(std::vector< T >& list,const std::string& name, bool perPoly)
876 {
877 for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end; ++it) {
878 if ((*it).name == name) {
879 if (!perPoly) {
880 DefaultLogger::get()->warn("LWO2: Found two VMAP sections with equal names");
881 }
882 return &(*it);
883 }
884 }
885 list.push_back( T() );
886 VMapEntry* p = &list.back();
887 p->name = name;
888 return p;
889 }
891 // ------------------------------------------------------------------------------------------------
892 template <class T>
893 inline void CreateNewEntry(T& chan, unsigned int srcIdx)
894 {
895 if (!chan.name.length())
896 return;
898 chan.abAssigned[srcIdx] = true;
899 chan.abAssigned.resize(chan.abAssigned.size()+1,false);
901 for (unsigned int a = 0; a < chan.dims;++a)
902 chan.rawData.push_back(chan.rawData[srcIdx*chan.dims+a]);
903 }
905 // ------------------------------------------------------------------------------------------------
906 template <class T>
907 inline void CreateNewEntry(std::vector< T >& list, unsigned int srcIdx)
908 {
909 for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end;++it) {
910 CreateNewEntry( *it, srcIdx );
911 }
912 }
914 // ------------------------------------------------------------------------------------------------
915 inline void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead,
916 unsigned int idx, float* data)
917 {
918 ai_assert(NULL != data);
919 LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
920 unsigned int i;
922 base->abAssigned[idx] = true;
923 for (i = 0; i < numRead;++i) {
924 base->rawData[idx*base->dims+i]= data[i];
925 }
927 if (UINT_MAX != (i = refList[idx])) {
928 DoRecursiveVMAPAssignment(base,numRead,i,data);
929 }
930 }
932 // ------------------------------------------------------------------------------------------------
933 inline void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx)
934 {
935 if(UINT_MAX == refList[srcIdx]) {
936 refList[srcIdx] = destIdx;
937 return;
938 }
939 AddToSingleLinkedList(refList,refList[srcIdx],destIdx);
940 }
942 // ------------------------------------------------------------------------------------------------
943 // Load LWO2 vertex map
944 void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
945 {
946 LE_NCONST uint8_t* const end = mFileBuffer+length;
948 AI_LWO_VALIDATE_CHUNK_LENGTH(length,VMAP,6);
949 unsigned int type = GetU4();
950 unsigned int dims = GetU2();
952 VMapEntry* base;
954 // read the name of the vertex map
955 std::string name;
956 GetS0(name,length);
958 switch (type)
959 {
960 case AI_LWO_TXUV:
961 if (dims != 2) {
962 DefaultLogger::get()->warn("LWO2: Skipping UV channel \'"
963 + name + "\' with !2 components");
964 return;
965 }
966 base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
967 break;
968 case AI_LWO_WGHT:
969 case AI_LWO_MNVW:
970 if (dims != 1) {
971 DefaultLogger::get()->warn("LWO2: Skipping Weight Channel \'"
972 + name + "\' with !1 components");
973 return;
974 }
975 base = FindEntry((type == AI_LWO_WGHT ? mCurLayer->mWeightChannels
976 : mCurLayer->mSWeightChannels),name,perPoly);
977 break;
978 case AI_LWO_RGB:
979 case AI_LWO_RGBA:
980 if (dims != 3 && dims != 4) {
981 DefaultLogger::get()->warn("LWO2: Skipping Color Map \'"
982 + name + "\' with a dimension > 4 or < 3");
983 return;
984 }
985 base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
986 break;
988 case AI_LWO_MODO_NORM:
989 /* This is a non-standard extension chunk used by Luxology's MODO.
990 * It stores per-vertex normals. This VMAP exists just once, has
991 * 3 dimensions and is btw extremely beautiful.
992 */
993 if (name != "vert_normals" || dims != 3 || mCurLayer->mNormals.name.length())
994 return;
996 DefaultLogger::get()->info("Processing non-standard extension: MODO VMAP.NORM.vert_normals");
998 mCurLayer->mNormals.name = name;
999 base = & mCurLayer->mNormals;
1000 break;
1002 case AI_LWO_PICK: /* these VMAPs are just silently dropped */
1003 case AI_LWO_MORF:
1004 case AI_LWO_SPOT:
1005 return;
1007 default:
1008 if (name == "APS.Level") {
1009 // XXX handle this (seems to be subdivision-related).
1011 DefaultLogger::get()->warn("LWO2: Skipping unknown VMAP/VMAD channel \'" + name + "\'");
1012 return;
1013 };
1014 base->Allocate((unsigned int)mCurLayer->mTempPoints.size());
1016 // now read all entries in the map
1017 type = std::min(dims,base->dims);
1018 const unsigned int diff = (dims - type)<<2u;
1020 LWO::FaceList& list = mCurLayer->mFaces;
1021 LWO::PointList& pointList = mCurLayer->mTempPoints;
1022 LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
1024 float temp[4];
1026 const unsigned int numPoints = (unsigned int)pointList.size();
1027 const unsigned int numFaces = (unsigned int)list.size();
1029 while (mFileBuffer < end) {
1031 unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
1032 if (idx >= numPoints) {
1033 DefaultLogger::get()->warn("LWO2: Failure evaluating VMAP/VMAD entry \'" + name + "\', vertex index is out of range");
1034 mFileBuffer += base->dims<<2u;
1035 continue;
1037 if (perPoly) {
1038 unsigned int polyIdx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
1039 if (base->abAssigned[idx]) {
1040 // we have already a VMAP entry for this vertex - thus
1041 // we need to duplicate the corresponding polygon.
1042 if (polyIdx >= numFaces) {
1043 DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', polygon index is out of range");
1044 mFileBuffer += base->dims<<2u;
1045 continue;
1048 LWO::Face& src = list[polyIdx];
1050 // generate a new unique vertex for the corresponding index - but only
1051 // if we can find the index in the face
1052 bool had = false;
1053 for (unsigned int i = 0; i < src.mNumIndices;++i) {
1055 unsigned int srcIdx = src.mIndices[i], tmp = idx;
1056 do {
1057 if (tmp == srcIdx)
1058 break;
1060 while ((tmp = refList[tmp]) != UINT_MAX);
1061 if (tmp == UINT_MAX) {
1062 continue;
1065 had = true;
1066 refList.resize(refList.size()+1, UINT_MAX);
1068 idx = (unsigned int)pointList.size();
1069 src.mIndices[i] = (unsigned int)pointList.size();
1071 // store the index of the new vertex in the old vertex
1072 // so we get a single linked list we can traverse in
1073 // only one direction
1074 AddToSingleLinkedList(refList,srcIdx,src.mIndices[i]);
1075 pointList.push_back(pointList[srcIdx]);
1077 CreateNewEntry(mCurLayer->mVColorChannels, srcIdx );
1078 CreateNewEntry(mCurLayer->mUVChannels, srcIdx );
1079 CreateNewEntry(mCurLayer->mWeightChannels, srcIdx );
1080 CreateNewEntry(mCurLayer->mSWeightChannels, srcIdx );
1081 CreateNewEntry(mCurLayer->mNormals, srcIdx );
1083 if (!had) {
1084 DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', vertex index wasn't found in that polygon");
1085 ai_assert(had);
1089 for (unsigned int l = 0; l < type;++l)
1090 temp[l] = GetF4();
1092 DoRecursiveVMAPAssignment(base,type,idx, temp);
1093 mFileBuffer += diff;
1097 // ------------------------------------------------------------------------------------------------
1098 // Load LWO2 clip
1099 void LWOImporter::LoadLWO2Clip(unsigned int length)
1101 AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
1103 mClips.push_back(LWO::Clip());
1104 LWO::Clip& clip = mClips.back();
1106 // first - get the index of the clip
1107 clip.idx = GetU4();
1109 IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
1110 switch (head->type)
1112 case AI_LWO_STIL:
1113 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
1115 // "Normal" texture
1116 GetS0(clip.path,head->length);
1117 clip.type = Clip::STILL;
1118 break;
1120 case AI_LWO_ISEQ:
1121 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
1122 // Image sequence. We'll later take the first.
1124 uint8_t digits = GetU1(); mFileBuffer++;
1125 int16_t offset = GetU2(); mFileBuffer+=4;
1126 int16_t start = GetU2(); mFileBuffer+=4;
1128 std::string s;
1129 std::ostringstream ss;
1130 GetS0(s,head->length);
1132 head->length -= (unsigned int)s.length()+1;
1133 ss << s;
1134 ss << std::setw(digits) << offset + start;
1135 GetS0(s,head->length);
1136 ss << s;
1137 clip.path = ss.str();
1138 clip.type = Clip::SEQ;
1140 break;
1142 case AI_LWO_STCC:
1143 DefaultLogger::get()->warn("LWO2: Color shifted images are not supported");
1144 break;
1146 case AI_LWO_ANIM:
1147 DefaultLogger::get()->warn("LWO2: Animated textures are not supported");
1148 break;
1150 case AI_LWO_XREF:
1151 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
1153 // Just a cross-reference to another CLIp
1154 clip.type = Clip::REF;
1155 clip.clipRef = GetU4();
1156 break;
1158 case AI_LWO_NEGA:
1159 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
1160 clip.negate = (0 != GetU2());
1161 break;
1163 default:
1164 DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
1168 // ------------------------------------------------------------------------------------------------
1169 // Load envelope description
1170 void LWOImporter::LoadLWO2Envelope(unsigned int length)
1172 LE_NCONST uint8_t* const end = mFileBuffer + length;
1173 AI_LWO_VALIDATE_CHUNK_LENGTH(length,ENVL,4);
1175 mEnvelopes.push_back(LWO::Envelope());
1176 LWO::Envelope& envelope = mEnvelopes.back();
1178 // Get the index of the envelope
1179 envelope.index = ReadVSizedIntLWO2(mFileBuffer);
1181 // It looks like there might be an extra U4 right after the index,
1182 // at least in modo (LXOB) files: we'll ignore it if it's zero,
1183 // otherwise it represents the start of a subchunk, so we backtrack.
1184 if (mIsLXOB)
1186 uint32_t extra = GetU4();
1187 if (extra)
1189 mFileBuffer -= 4;
1193 // ... and read all subchunks
1194 while (true)
1196 if (mFileBuffer + 6 >= end)break;
1197 LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
1199 if (mFileBuffer + head->length > end)
1200 throw DeadlyImportError("LWO2: Invalid envelope chunk length");
1202 uint8_t* const next = mFileBuffer+head->length;
1203 switch (head->type)
1205 // Type & representation of the envelope
1206 case AI_LWO_TYPE:
1207 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TYPE,2);
1208 mFileBuffer++; // skip user format
1210 // Determine type of envelope
1211 envelope.type = (LWO::EnvelopeType)*mFileBuffer;
1212 ++mFileBuffer;
1213 break;
1215 // precondition
1216 case AI_LWO_PRE:
1217 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,PRE,2);
1218 envelope.pre = (LWO::PrePostBehaviour)GetU2();
1219 break;
1221 // postcondition
1222 case AI_LWO_POST:
1223 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,POST,2);
1224 envelope.post = (LWO::PrePostBehaviour)GetU2();
1225 break;
1227 // keyframe
1228 case AI_LWO_KEY:
1230 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,KEY,8);
1232 envelope.keys.push_back(LWO::Key());
1233 LWO::Key& key = envelope.keys.back();
1235 key.time = GetF4();
1236 key.value = GetF4();
1237 break;
1240 // interval interpolation
1241 case AI_LWO_SPAN:
1243 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPAN,4);
1244 if (envelope.keys.size()<2)
1245 DefaultLogger::get()->warn("LWO2: Unexpected SPAN chunk");
1246 else {
1247 LWO::Key& key = envelope.keys.back();
1248 switch (GetU4())
1250 case AI_LWO_STEP:
1251 key.inter = LWO::IT_STEP;break;
1252 case AI_LWO_LINE:
1253 key.inter = LWO::IT_LINE;break;
1254 case AI_LWO_TCB:
1255 key.inter = LWO::IT_TCB;break;
1256 case AI_LWO_HERM:
1257 key.inter = LWO::IT_HERM;break;
1258 case AI_LWO_BEZI:
1259 key.inter = LWO::IT_BEZI;break;
1260 case AI_LWO_BEZ2:
1261 key.inter = LWO::IT_BEZ2;break;
1262 default:
1263 DefaultLogger::get()->warn("LWO2: Unknown interval interpolation mode");
1264 };
1266 // todo ... read params
1268 break;
1271 default:
1272 DefaultLogger::get()->warn("LWO2: Encountered unknown ENVL subchunk");
1274 // regardless how much we did actually read, go to the next chunk
1275 mFileBuffer = next;
1279 // ------------------------------------------------------------------------------------------------
1280 // Load file - master function
1281 void LWOImporter::LoadLWO2File()
1283 bool skip = false;
1285 LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
1286 while (true)
1288 if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
1289 IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
1291 if (mFileBuffer + head->length > end)
1293 throw DeadlyImportError("LWO2: Chunk length points behind the file");
1294 break;
1296 uint8_t* const next = mFileBuffer+head->length;
1297 unsigned int iUnnamed = 0;
1299 switch (head->type)
1301 // new layer
1302 case AI_LWO_LAYR:
1304 // add a new layer to the list ....
1305 mLayers->push_back ( LWO::Layer() );
1306 LWO::Layer& layer = mLayers->back();
1307 mCurLayer = &layer;
1309 AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LAYR,16);
1311 // layer index.
1312 layer.mIndex = GetU2();
1314 // Continue loading this layer or ignore it? Check the layer index property
1315 if (UINT_MAX != configLayerIndex && (configLayerIndex-1) != layer.mIndex) {
1316 skip = true;
1318 else skip = false;
1320 // pivot point
1321 mFileBuffer += 2; /* unknown */
1322 mCurLayer->mPivot.x = GetF4();
1323 mCurLayer->mPivot.y = GetF4();
1324 mCurLayer->mPivot.z = GetF4();
1325 GetS0(layer.mName,head->length-16);
1327 // if the name is empty, generate a default name
1328 if (layer.mName.empty()) {
1329 char buffer[128]; // should be sufficiently large
1330 ::sprintf(buffer,"Layer_%i", iUnnamed++);
1331 layer.mName = buffer;
1334 // load this layer or ignore it? Check the layer name property
1335 if (configLayerName.length() && configLayerName != layer.mName) {
1336 skip = true;
1338 else hasNamedLayer = true;
1340 // optional: parent of this layer
1341 if (mFileBuffer + 2 <= next)
1342 layer.mParent = GetU2();
1343 else layer.mParent = -1;
1345 // Set layer skip parameter
1346 layer.skip = skip;
1348 break;
1351 // vertex list
1352 case AI_LWO_PNTS:
1354 if (skip)
1355 break;
1357 unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
1358 LoadLWOPoints(head->length);
1359 mCurLayer->mPointIDXOfs = old;
1360 break;
1362 // vertex tags
1363 case AI_LWO_VMAD:
1364 if (mCurLayer->mFaces.empty())
1366 DefaultLogger::get()->warn("LWO2: Unexpected VMAD chunk");
1367 break;
1369 // --- intentionally no break here
1370 case AI_LWO_VMAP:
1372 if (skip)
1373 break;
1375 if (mCurLayer->mTempPoints.empty())
1376 DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
1377 else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
1378 break;
1380 // face list
1381 case AI_LWO_POLS:
1383 if (skip)
1384 break;
1386 unsigned int old = (unsigned int)mCurLayer->mFaces.size();
1387 LoadLWO2Polygons(head->length);
1388 mCurLayer->mFaceIDXOfs = old;
1389 break;
1391 // polygon tags
1392 case AI_LWO_PTAG:
1394 if (skip)
1395 break;
1397 if (mCurLayer->mFaces.empty())
1398 DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
1399 else LoadLWO2PolygonTags(head->length);
1400 break;
1402 // list of tags
1403 case AI_LWO_TAGS:
1405 if (!mTags->empty())
1406 DefaultLogger::get()->warn("LWO2: SRFS chunk encountered twice");
1407 else LoadLWOTags(head->length);
1408 break;
1411 // surface chunk
1412 case AI_LWO_SURF:
1414 LoadLWO2Surface(head->length);
1415 break;
1418 // clip chunk
1419 case AI_LWO_CLIP:
1421 LoadLWO2Clip(head->length);
1422 break;
1425 // envelope chunk
1426 case AI_LWO_ENVL:
1428 LoadLWO2Envelope(head->length);
1429 break;
1432 mFileBuffer = next;
1436 #endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER