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