vrshoot

diff 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 diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/libs/assimp/LWOLoader.cpp	Sat Feb 01 19:58:19 2014 +0200
     1.3 @@ -0,0 +1,1436 @@
     1.4 +/*
     1.5 +---------------------------------------------------------------------------
     1.6 +Open Asset Import Library (assimp)
     1.7 +---------------------------------------------------------------------------
     1.8 +
     1.9 +Copyright (c) 2006-2012, assimp team
    1.10 +
    1.11 +All rights reserved.
    1.12 +
    1.13 +Redistribution and use of this software in source and binary forms, 
    1.14 +with or without modification, are permitted provided that the following 
    1.15 +conditions are met:
    1.16 +
    1.17 +* Redistributions of source code must retain the above
    1.18 +  copyright notice, this list of conditions and the
    1.19 +  following disclaimer.
    1.20 +
    1.21 +* Redistributions in binary form must reproduce the above
    1.22 +  copyright notice, this list of conditions and the
    1.23 +  following disclaimer in the documentation and/or other
    1.24 +  materials provided with the distribution.
    1.25 +
    1.26 +* Neither the name of the assimp team, nor the names of its
    1.27 +  contributors may be used to endorse or promote products
    1.28 +  derived from this software without specific prior
    1.29 +  written permission of the assimp team.
    1.30 +
    1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
    1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
    1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
    1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
    1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
    1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
    1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
    1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.42 +---------------------------------------------------------------------------
    1.43 +*/
    1.44 +
    1.45 +/** @file  LWOLoader.cpp
    1.46 + *  @brief Implementation of the LWO importer class
    1.47 + */
    1.48 +
    1.49 +#include "AssimpPCH.h"
    1.50 +#ifndef ASSIMP_BUILD_NO_LWO_IMPORTER
    1.51 +
    1.52 +// internal headers
    1.53 +#include "LWOLoader.h"
    1.54 +#include "StringComparison.h"
    1.55 +#include "SGSpatialSort.h"
    1.56 +#include "ByteSwap.h"
    1.57 +#include "ProcessHelper.h"
    1.58 +#include "ConvertToLHProcess.h"
    1.59 +
    1.60 +using namespace Assimp;
    1.61 +
    1.62 +static const aiImporterDesc desc = {
    1.63 +	"LightWave/Modo Object Importer",
    1.64 +	"",
    1.65 +	"",
    1.66 +	"http://www.newtek.com/lightwave.html\nhttp://www.luxology.com/modo/",
    1.67 +	aiImporterFlags_SupportTextFlavour,
    1.68 +	0,
    1.69 +	0,
    1.70 +	0,
    1.71 +	0,
    1.72 +	"lwo lxo"
    1.73 +};
    1.74 +
    1.75 +// ------------------------------------------------------------------------------------------------
    1.76 +// Constructor to be privately used by Importer
    1.77 +LWOImporter::LWOImporter()
    1.78 +{}
    1.79 +
    1.80 +// ------------------------------------------------------------------------------------------------
    1.81 +// Destructor, private as well 
    1.82 +LWOImporter::~LWOImporter()
    1.83 +{}
    1.84 +
    1.85 +// ------------------------------------------------------------------------------------------------
    1.86 +// Returns whether the class can handle the format of the given file. 
    1.87 +bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
    1.88 +{
    1.89 +	const std::string extension = GetExtension(pFile);
    1.90 +	if (extension == "lwo" || extension == "lxo") {
    1.91 +		return true;
    1.92 +	}
    1.93 +
    1.94 +	// if check for extension is not enough, check for the magic tokens 
    1.95 +	if (!extension.length() || checkSig) {
    1.96 +		uint32_t tokens[3]; 
    1.97 +		tokens[0] = AI_LWO_FOURCC_LWOB;
    1.98 +		tokens[1] = AI_LWO_FOURCC_LWO2;
    1.99 +		tokens[2] = AI_LWO_FOURCC_LXOB;
   1.100 +		return CheckMagicToken(pIOHandler,pFile,tokens,3,8);
   1.101 +	}
   1.102 +	return false;
   1.103 +}
   1.104 +
   1.105 +// ------------------------------------------------------------------------------------------------
   1.106 +// Setup configuration properties
   1.107 +void LWOImporter::SetupProperties(const Importer* pImp)
   1.108 +{
   1.109 +	configSpeedFlag  = ( 0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0) ? true : false);
   1.110 +	configLayerIndex = pImp->GetPropertyInteger (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,UINT_MAX); 
   1.111 +	configLayerName  = pImp->GetPropertyString  (AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,"");
   1.112 +}
   1.113 +
   1.114 +// ------------------------------------------------------------------------------------------------
   1.115 +// Get list of file extensions
   1.116 +const aiImporterDesc* LWOImporter::GetInfo () const
   1.117 +{
   1.118 +	return &desc;
   1.119 +}
   1.120 +
   1.121 +// ------------------------------------------------------------------------------------------------
   1.122 +// Imports the given file into the given scene structure. 
   1.123 +void LWOImporter::InternReadFile( const std::string& pFile, 
   1.124 +	aiScene* pScene, 
   1.125 +	IOSystem* pIOHandler)
   1.126 +{
   1.127 +	boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
   1.128 +
   1.129 +	// Check whether we can read from the file
   1.130 +	if( file.get() == NULL)
   1.131 +		throw DeadlyImportError( "Failed to open LWO file " + pFile + ".");
   1.132 +
   1.133 +	if((this->fileSize = (unsigned int)file->FileSize()) < 12)
   1.134 +		throw DeadlyImportError("LWO: The file is too small to contain the IFF header");
   1.135 +
   1.136 +	// Allocate storage and copy the contents of the file to a memory buffer
   1.137 +	std::vector< uint8_t > mBuffer(fileSize);
   1.138 +	file->Read( &mBuffer[0], 1, fileSize);
   1.139 +	this->pScene = pScene;
   1.140 +
   1.141 +	// Determine the type of the file
   1.142 +	uint32_t fileType;
   1.143 +	const char* sz = IFF::ReadHeader(&mBuffer[0],fileType);
   1.144 +	if (sz)throw DeadlyImportError(sz);
   1.145 +
   1.146 +	mFileBuffer = &mBuffer[0] + 12;
   1.147 +	fileSize -= 12;
   1.148 +
   1.149 +	// Initialize some members with their default values
   1.150 +	hasNamedLayer   = false;
   1.151 +
   1.152 +	// Create temporary storage on the stack but store pointers to it in the class 
   1.153 +	// instance. Therefore everything will be destructed properly if an exception 
   1.154 +	// is thrown and we needn't take care of that.
   1.155 +	LayerList		_mLayers;
   1.156 +	SurfaceList		_mSurfaces;		
   1.157 +	TagList			_mTags;		
   1.158 +	TagMappingTable _mMapping;	
   1.159 +
   1.160 +	mLayers			= &_mLayers;
   1.161 +	mTags			= &_mTags;
   1.162 +	mMapping		= &_mMapping;
   1.163 +	mSurfaces		= &_mSurfaces;
   1.164 +
   1.165 +	// Allocate a default layer (layer indices are 1-based from now)
   1.166 +	mLayers->push_back(Layer());
   1.167 +	mCurLayer = &mLayers->back();
   1.168 +	mCurLayer->mName = "<LWODefault>";
   1.169 +	mCurLayer->mIndex = -1;
   1.170 +
   1.171 +	// old lightwave file format (prior to v6)
   1.172 +	if (AI_LWO_FOURCC_LWOB == fileType)	{
   1.173 +		DefaultLogger::get()->info("LWO file format: LWOB (<= LightWave 5.5)");
   1.174 +
   1.175 +		mIsLWO2 = false;
   1.176 +        mIsLXOB = false;
   1.177 +		LoadLWOBFile();
   1.178 +	}
   1.179 +	// New lightwave format
   1.180 +	else if (AI_LWO_FOURCC_LWO2 == fileType)	{
   1.181 +        mIsLXOB = false;
   1.182 +		DefaultLogger::get()->info("LWO file format: LWO2 (>= LightWave 6)");
   1.183 +	}
   1.184 +	// MODO file format
   1.185 +	else if (AI_LWO_FOURCC_LXOB == fileType)	{
   1.186 +        mIsLXOB = true;
   1.187 +		DefaultLogger::get()->info("LWO file format: LXOB (Modo)");
   1.188 +	}
   1.189 +	// we don't know this format
   1.190 +	else 
   1.191 +	{
   1.192 +		char szBuff[5];
   1.193 +		szBuff[0] = (char)(fileType >> 24u);
   1.194 +		szBuff[1] = (char)(fileType >> 16u);
   1.195 +		szBuff[2] = (char)(fileType >> 8u);
   1.196 +		szBuff[3] = (char)(fileType);
   1.197 +		szBuff[4] = '\0';
   1.198 +		throw DeadlyImportError(std::string("Unknown LWO sub format: ") + szBuff);
   1.199 +	}
   1.200 +
   1.201 +	if (AI_LWO_FOURCC_LWOB != fileType)	{
   1.202 +		mIsLWO2 = true;
   1.203 +		LoadLWO2File();
   1.204 +
   1.205 +		// The newer lightwave format allows the user to configure the
   1.206 +		// loader that just one layer is used. If this is the case
   1.207 +		// we need to check now whether the requested layer has been found.
   1.208 +		if (UINT_MAX != configLayerIndex) {
   1.209 +			unsigned int layerCount = 0;
   1.210 +			for(std::list<LWO::Layer>::iterator itLayers=mLayers->begin(); itLayers!=mLayers->end(); itLayers++)
   1.211 +				if (!itLayers->skip)
   1.212 +					layerCount++;
   1.213 +			if (layerCount!=2)
   1.214 +				throw DeadlyImportError("LWO2: The requested layer was not found");
   1.215 +		}
   1.216 +
   1.217 +		if (configLayerName.length() && !hasNamedLayer)	{
   1.218 +			throw DeadlyImportError("LWO2: Unable to find the requested layer: " 
   1.219 +				+ configLayerName);
   1.220 +		}
   1.221 +	}
   1.222 +
   1.223 +	// now, as we have loaded all data, we can resolve cross-referenced tags and clips
   1.224 +	ResolveTags();
   1.225 +	ResolveClips();
   1.226 +
   1.227 +	// now process all layers and build meshes and nodes
   1.228 +	std::vector<aiMesh*> apcMeshes;
   1.229 +	std::map<uint16_t, aiNode*> apcNodes;
   1.230 +
   1.231 +	apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u));
   1.232 +
   1.233 +	unsigned int iDefaultSurface = UINT_MAX; // index of the default surface
   1.234 +	for (LayerList::iterator lit = mLayers->begin(), lend = mLayers->end();lit != lend;++lit)	{
   1.235 +		LWO::Layer& layer = *lit;
   1.236 +		if (layer.skip)
   1.237 +			continue;
   1.238 +
   1.239 +		// I don't know whether there could be dummy layers, but it would be possible
   1.240 +		const unsigned int meshStart = (unsigned int)apcMeshes.size();
   1.241 +		if (!layer.mFaces.empty() && !layer.mTempPoints.empty())	{
   1.242 +
   1.243 +			// now sort all faces by the surfaces assigned to them
   1.244 +			std::vector<SortedRep> pSorted(mSurfaces->size()+1);
   1.245 +
   1.246 +			unsigned int i = 0;
   1.247 +			for (FaceList::iterator it = layer.mFaces.begin(), end = layer.mFaces.end();it != end;++it,++i)	{
   1.248 +				// Check whether we support this face's type
   1.249 +				if ((*it).type != AI_LWO_FACE && (*it).type != AI_LWO_PTCH &&
   1.250 +				    (*it).type != AI_LWO_BONE && (*it).type != AI_LWO_SUBD) {
   1.251 +					continue;
   1.252 +				}
   1.253 +
   1.254 +				unsigned int idx = (*it).surfaceIndex;
   1.255 +				if (idx >= mTags->size())
   1.256 +				{
   1.257 +					DefaultLogger::get()->warn("LWO: Invalid face surface index");
   1.258 +					idx = UINT_MAX;
   1.259 +				}
   1.260 +				if(UINT_MAX == idx || UINT_MAX == (idx = _mMapping[idx]))	{
   1.261 +					if (UINT_MAX == iDefaultSurface)	{
   1.262 +						iDefaultSurface = (unsigned int)mSurfaces->size();
   1.263 +						mSurfaces->push_back(LWO::Surface());
   1.264 +						LWO::Surface& surf = mSurfaces->back();
   1.265 +						surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f; 
   1.266 +						surf.mName = "LWODefaultSurface";
   1.267 +					}
   1.268 +					idx = iDefaultSurface;
   1.269 +				}
   1.270 +				pSorted[idx].push_back(i);
   1.271 +			}
   1.272 +			if (UINT_MAX == iDefaultSurface) {
   1.273 +				pSorted.erase(pSorted.end()-1);
   1.274 +			}
   1.275 +			for (unsigned int p = 0,i = 0;i < mSurfaces->size();++i)	{
   1.276 +				SortedRep& sorted = pSorted[i];
   1.277 +				if (sorted.empty())
   1.278 +					continue;
   1.279 +
   1.280 +				// generate the mesh 
   1.281 +				aiMesh* mesh = new aiMesh();
   1.282 +				apcMeshes.push_back(mesh);
   1.283 +				mesh->mNumFaces = (unsigned int)sorted.size();
   1.284 +
   1.285 +				// count the number of vertices
   1.286 +				SortedRep::const_iterator it = sorted.begin(), end = sorted.end();
   1.287 +				for (;it != end;++it)	{
   1.288 +					mesh->mNumVertices += layer.mFaces[*it].mNumIndices;
   1.289 +				}
   1.290 +
   1.291 +				aiVector3D *nrm = NULL, * pv = mesh->mVertices = new aiVector3D[mesh->mNumVertices];
   1.292 +				aiFace* pf = mesh->mFaces = new aiFace[mesh->mNumFaces];
   1.293 +				mesh->mMaterialIndex = i;
   1.294 +
   1.295 +				// find out which vertex color channels and which texture coordinate
   1.296 +				// channels are really required by the material attached to this mesh
   1.297 +				unsigned int vUVChannelIndices[AI_MAX_NUMBER_OF_TEXTURECOORDS];
   1.298 +				unsigned int vVColorIndices[AI_MAX_NUMBER_OF_COLOR_SETS];
   1.299 +
   1.300 +#if _DEBUG
   1.301 +				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui ) {
   1.302 +					vUVChannelIndices[mui] = UINT_MAX;
   1.303 +				}
   1.304 +				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui ) {
   1.305 +					vVColorIndices[mui] = UINT_MAX;
   1.306 +				}
   1.307 +#endif
   1.308 +
   1.309 +				FindUVChannels(_mSurfaces[i],sorted,layer,vUVChannelIndices);
   1.310 +				FindVCChannels(_mSurfaces[i],sorted,layer,vVColorIndices);
   1.311 +
   1.312 +				// allocate storage for UV and CV channels
   1.313 +				aiVector3D* pvUV[AI_MAX_NUMBER_OF_TEXTURECOORDS];
   1.314 +				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_TEXTURECOORDS;++mui )	{
   1.315 +					if (UINT_MAX == vUVChannelIndices[mui]) {
   1.316 +						break;
   1.317 +					}
   1.318 +					
   1.319 +					pvUV[mui] = mesh->mTextureCoords[mui] = new aiVector3D[mesh->mNumVertices];
   1.320 +
   1.321 +					// LightWave doesn't support more than 2 UV components (?)
   1.322 +					mesh->mNumUVComponents[0] = 2;
   1.323 +				}
   1.324 +
   1.325 +				if (layer.mNormals.name.length())
   1.326 +					nrm = mesh->mNormals = new aiVector3D[mesh->mNumVertices];
   1.327 +		
   1.328 +				aiColor4D* pvVC[AI_MAX_NUMBER_OF_COLOR_SETS];
   1.329 +				for (unsigned int mui = 0; mui < AI_MAX_NUMBER_OF_COLOR_SETS;++mui)	{
   1.330 +					if (UINT_MAX == vVColorIndices[mui]) {
   1.331 +						break;
   1.332 +					}
   1.333 +					pvVC[mui] = mesh->mColors[mui] = new aiColor4D[mesh->mNumVertices];
   1.334 +				}
   1.335 +
   1.336 +				// we would not need this extra array, but the code is much cleaner if we use it
   1.337 +				std::vector<unsigned int>& smoothingGroups = layer.mPointReferrers;
   1.338 +				smoothingGroups.erase (smoothingGroups.begin(),smoothingGroups.end());
   1.339 +				smoothingGroups.resize(mesh->mNumFaces,0);
   1.340 +
   1.341 +				// now convert all faces
   1.342 +				unsigned int vert = 0;
   1.343 +				std::vector<unsigned int>::iterator outIt = smoothingGroups.begin();
   1.344 +				for (it = sorted.begin(); it != end;++it,++outIt)	{
   1.345 +					const LWO::Face& face = layer.mFaces[*it];
   1.346 +					*outIt = face.smoothGroup;
   1.347 +
   1.348 +					// copy all vertices
   1.349 +					for (unsigned int q = 0; q  < face.mNumIndices;++q,++vert)	{
   1.350 +						register unsigned int idx = face.mIndices[q];
   1.351 +						*pv++ = layer.mTempPoints[idx] /*- layer.mPivot*/;
   1.352 +
   1.353 +						// process UV coordinates
   1.354 +						for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_TEXTURECOORDS;++w)	{
   1.355 +							if (UINT_MAX == vUVChannelIndices[w]) {
   1.356 +								break;
   1.357 +							}
   1.358 +							aiVector3D*& pp = pvUV[w];
   1.359 +							const aiVector2D& src = ((aiVector2D*)&layer.mUVChannels[vUVChannelIndices[w]].rawData[0])[idx];
   1.360 +							pp->x = src.x;
   1.361 +							pp->y = src.y; 
   1.362 +							pp++;
   1.363 +						}
   1.364 +
   1.365 +						// process normals (MODO extension)
   1.366 +						if (nrm)	{
   1.367 +							*nrm = ((aiVector3D*)&layer.mNormals.rawData[0])[idx];
   1.368 +							nrm->z *= -1.f;
   1.369 +							++nrm;
   1.370 +						}
   1.371 +
   1.372 +						// process vertex colors
   1.373 +						for (unsigned int w = 0; w < AI_MAX_NUMBER_OF_COLOR_SETS;++w)	{
   1.374 +							if (UINT_MAX == vVColorIndices[w]) {
   1.375 +								break;
   1.376 +							}
   1.377 +							*pvVC[w] = ((aiColor4D*)&layer.mVColorChannels[vVColorIndices[w]].rawData[0])[idx];
   1.378 +
   1.379 +							// If a RGB color map is explicitly requested delete the
   1.380 +							// alpha channel - it could theoretically be != 1.
   1.381 +							if(_mSurfaces[i].mVCMapType == AI_LWO_RGB)
   1.382 +								pvVC[w]->a = 1.f;
   1.383 +
   1.384 +							pvVC[w]++;
   1.385 +						}
   1.386 +
   1.387 +#if 0
   1.388 +						// process vertex weights. We can't properly reconstruct the whole skeleton for now,
   1.389 +						// but we can create dummy bones for all weight channels which we have.
   1.390 +						for (unsigned int w = 0; w < layer.mWeightChannels.size();++w)
   1.391 +						{
   1.392 +						}
   1.393 +#endif
   1.394 +
   1.395 +						face.mIndices[q] = vert;
   1.396 +					}
   1.397 +					pf->mIndices = face.mIndices;
   1.398 +					pf->mNumIndices = face.mNumIndices;
   1.399 +					unsigned int** p = (unsigned int**)&face.mIndices;*p = NULL; // HACK: make sure it won't be deleted
   1.400 +					pf++;
   1.401 +				}
   1.402 +
   1.403 +				if (!mesh->mNormals)	{
   1.404 +					// Compute normal vectors for the mesh - we can't use our GenSmoothNormal-
   1.405 +					// Step here since it wouldn't handle smoothing groups correctly for LWO.
   1.406 +					// So we use a separate implementation. 
   1.407 +					ComputeNormals(mesh,smoothingGroups,_mSurfaces[i]);
   1.408 +				}
   1.409 +				else DefaultLogger::get()->debug("LWO2: No need to compute normals, they're already there");
   1.410 +				++p;
   1.411 +			}
   1.412 +		}
   1.413 +
   1.414 +		// Generate nodes to render the mesh. Store the source layer in the mParent member of the nodes
   1.415 +		unsigned int num = apcMeshes.size() - meshStart;
   1.416 +		if (layer.mName != "<LWODefault>" || num > 0) {
   1.417 +			aiNode* pcNode = new aiNode();
   1.418 +			apcNodes[layer.mIndex] = pcNode;
   1.419 +			pcNode->mName.Set(layer.mName);
   1.420 +			pcNode->mParent = (aiNode*)&layer;
   1.421 +			pcNode->mNumMeshes = num;
   1.422 +
   1.423 +			if (pcNode->mNumMeshes) {
   1.424 +				pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
   1.425 +				for (unsigned int p = 0; p < pcNode->mNumMeshes;++p)
   1.426 +					pcNode->mMeshes[p] = p + meshStart;
   1.427 +			}
   1.428 +		}
   1.429 +	}
   1.430 +
   1.431 +	if (apcNodes.empty() || apcMeshes.empty())
   1.432 +		throw DeadlyImportError("LWO: No meshes loaded");
   1.433 +
   1.434 +	// The RemoveRedundantMaterials step will clean this up later
   1.435 +	pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()];
   1.436 +	for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat)	{
   1.437 +		aiMaterial* pcMat = new aiMaterial();
   1.438 +		pScene->mMaterials[mat] = pcMat;
   1.439 +		ConvertMaterial((*mSurfaces)[mat],pcMat);
   1.440 +	}
   1.441 +
   1.442 +	// copy the meshes to the output structure
   1.443 +	pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ];
   1.444 +	::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*));
   1.445 +
   1.446 +	// generate the final node graph
   1.447 +	GenerateNodeGraph(apcNodes);
   1.448 +}
   1.449 +
   1.450 +// ------------------------------------------------------------------------------------------------
   1.451 +void LWOImporter::ComputeNormals(aiMesh* mesh, const std::vector<unsigned int>& smoothingGroups,
   1.452 +	const LWO::Surface& surface)
   1.453 +{
   1.454 +	// Allocate output storage
   1.455 +	mesh->mNormals = new aiVector3D[mesh->mNumVertices];
   1.456 +
   1.457 +	// First generate per-face normals
   1.458 +	aiVector3D* out;
   1.459 +	std::vector<aiVector3D> faceNormals;
   1.460 +
   1.461 +	// ... in some cases that's already enough
   1.462 +	if (!surface.mMaximumSmoothAngle)
   1.463 +		out = mesh->mNormals;
   1.464 +	else	{
   1.465 +		faceNormals.resize(mesh->mNumVertices);
   1.466 +		out = &faceNormals[0];
   1.467 +	}
   1.468 +
   1.469 +	aiFace* begin = mesh->mFaces, *const end = mesh->mFaces+mesh->mNumFaces;
   1.470 +	for (; begin != end; ++begin)	{
   1.471 +		aiFace& face = *begin;
   1.472 +
   1.473 +		if(face.mNumIndices < 3) {
   1.474 +			continue;
   1.475 +		}
   1.476 +
   1.477 +		// LWO doc: "the normal is defined as the cross product of the first and last edges"
   1.478 +		aiVector3D* pV1 = mesh->mVertices + face.mIndices[0];
   1.479 +		aiVector3D* pV2 = mesh->mVertices + face.mIndices[1];
   1.480 +		aiVector3D* pV3 = mesh->mVertices + face.mIndices[face.mNumIndices-1];
   1.481 +
   1.482 +		aiVector3D vNor = ((*pV2 - *pV1) ^(*pV3 - *pV1)).Normalize();
   1.483 +		for (unsigned int i = 0; i < face.mNumIndices;++i)
   1.484 +			out[face.mIndices[i]] = vNor;
   1.485 +	}
   1.486 +	if (!surface.mMaximumSmoothAngle)return;
   1.487 +	const float posEpsilon = ComputePositionEpsilon(mesh);
   1.488 +	
   1.489 +	// Now generate the spatial sort tree
   1.490 +	SGSpatialSort sSort;
   1.491 +	std::vector<unsigned int>::const_iterator it = smoothingGroups.begin();
   1.492 +	for( begin =  mesh->mFaces; begin != end; ++begin, ++it)
   1.493 +	{
   1.494 +		aiFace& face = *begin;
   1.495 +		for (unsigned int i = 0; i < face.mNumIndices;++i)
   1.496 +		{
   1.497 +			register unsigned int tt = face.mIndices[i];
   1.498 +			sSort.Add(mesh->mVertices[tt],tt,*it);
   1.499 +		}
   1.500 +	}
   1.501 +	// Sort everything - this takes O(nlogn) time
   1.502 +	sSort.Prepare();
   1.503 +	std::vector<unsigned int> poResult;
   1.504 +	poResult.reserve(20);
   1.505 +
   1.506 +	// Generate vertex normals. We have O(logn) for the binary lookup, which we need
   1.507 +	// for n elements, thus the EXPECTED complexity is O(nlogn)
   1.508 +	if (surface.mMaximumSmoothAngle < 3.f && !configSpeedFlag)	{
   1.509 +		const float fLimit = cos(surface.mMaximumSmoothAngle);
   1.510 +
   1.511 +		for( begin =  mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it)	{
   1.512 +			const aiFace& face = *begin;
   1.513 +			unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
   1.514 +			for (; beginIdx != endIdx; ++beginIdx)
   1.515 +			{
   1.516 +				register unsigned int idx = *beginIdx;
   1.517 +				sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
   1.518 +				std::vector<unsigned int>::const_iterator a, end = poResult.end();
   1.519 +
   1.520 +				aiVector3D vNormals;
   1.521 +				for (a =  poResult.begin();a != end;++a)	{
   1.522 +					const aiVector3D& v = faceNormals[*a];
   1.523 +					if (v * faceNormals[idx] < fLimit)
   1.524 +						continue;
   1.525 +					vNormals += v;
   1.526 +				}
   1.527 +				mesh->mNormals[idx] = vNormals.Normalize();
   1.528 +			}
   1.529 +		}
   1.530 +	}
   1.531 +	 // faster code path in case there is no smooth angle
   1.532 +	else	{
   1.533 +		std::vector<bool> vertexDone(mesh->mNumVertices,false);
   1.534 +		for( begin =  mesh->mFaces, it = smoothingGroups.begin(); begin != end; ++begin, ++it)	{
   1.535 +			const aiFace& face = *begin;
   1.536 +			unsigned int* beginIdx = face.mIndices, *const endIdx = face.mIndices+face.mNumIndices;
   1.537 +			for (; beginIdx != endIdx; ++beginIdx)
   1.538 +			{
   1.539 +				register unsigned int idx = *beginIdx;
   1.540 +				if (vertexDone[idx])
   1.541 +					continue;
   1.542 +				sSort.FindPositions(mesh->mVertices[idx],*it,posEpsilon,poResult,true);
   1.543 +				std::vector<unsigned int>::const_iterator a, end = poResult.end();
   1.544 +
   1.545 +				aiVector3D vNormals;
   1.546 +				for (a =  poResult.begin();a != end;++a)	{
   1.547 +					const aiVector3D& v = faceNormals[*a];
   1.548 +					vNormals += v;
   1.549 +				}
   1.550 +				vNormals.Normalize();
   1.551 +				for (a =  poResult.begin();a != end;++a)	{
   1.552 +					mesh->mNormals[*a] = vNormals;
   1.553 +					vertexDone[*a] = true;
   1.554 +				}
   1.555 +			}
   1.556 +		}
   1.557 +	}
   1.558 +}
   1.559 +
   1.560 +// ------------------------------------------------------------------------------------------------
   1.561 +void LWOImporter::GenerateNodeGraph(std::map<uint16_t,aiNode*>& apcNodes)
   1.562 +{
   1.563 +	// now generate the final nodegraph - generate a root node and attach children
   1.564 +	aiNode* root = pScene->mRootNode = new aiNode();
   1.565 +	root->mName.Set("<LWORoot>");
   1.566 +
   1.567 +	//Set parent of all children, inserting pivots
   1.568 +	//std::cout << "Set parent of all children" << std::endl;
   1.569 +	std::map<uint16_t, aiNode*> mapPivot;
   1.570 +	for (std::map<uint16_t,aiNode*>::iterator itapcNodes = apcNodes.begin(); itapcNodes != apcNodes.end(); ++itapcNodes) {
   1.571 +
   1.572 +		//Get the parent index
   1.573 +		LWO::Layer* nodeLayer = (LWO::Layer*)(itapcNodes->second->mParent);
   1.574 +		uint16_t parentIndex = nodeLayer->mParent;
   1.575 +
   1.576 +		//Create pivot node, store it into the pivot map, and set the parent as the pivot
   1.577 +		aiNode* pivotNode = new aiNode();
   1.578 +		pivotNode->mName.Set("Pivot-"+std::string(itapcNodes->second->mName.data));
   1.579 +		mapPivot[-(itapcNodes->first+2)] = pivotNode;
   1.580 +		itapcNodes->second->mParent = pivotNode;
   1.581 +
   1.582 +		//Look for the parent node to attach the pivot to
   1.583 +		if (apcNodes.find(parentIndex) != apcNodes.end()) {
   1.584 +			pivotNode->mParent = apcNodes[parentIndex];
   1.585 +		} else {
   1.586 +			//If not, attach to the root node
   1.587 +			pivotNode->mParent = root;
   1.588 +		}
   1.589 +
   1.590 +		//Set the node and the pivot node transformation
   1.591 +		itapcNodes->second->mTransformation.a4 = -nodeLayer->mPivot.x;
   1.592 +		itapcNodes->second->mTransformation.b4 = -nodeLayer->mPivot.y;
   1.593 +		itapcNodes->second->mTransformation.c4 = -nodeLayer->mPivot.z;
   1.594 +		pivotNode->mTransformation.a4 = nodeLayer->mPivot.x;
   1.595 +		pivotNode->mTransformation.b4 = nodeLayer->mPivot.y;
   1.596 +		pivotNode->mTransformation.c4 = nodeLayer->mPivot.z;
   1.597 +	}
   1.598 +
   1.599 +	//Merge pivot map into node map
   1.600 +	//std::cout << "Merge pivot map into node map" << std::endl;
   1.601 +	for (std::map<uint16_t, aiNode*>::iterator itMapPivot = mapPivot.begin(); itMapPivot != mapPivot.end(); ++itMapPivot) {
   1.602 +		apcNodes[itMapPivot->first] = itMapPivot->second;
   1.603 +	}
   1.604 +
   1.605 +	//Set children of all parents
   1.606 +	apcNodes[-1] = root;
   1.607 +	for (std::map<uint16_t,aiNode*>::iterator itMapParentNodes = apcNodes.begin(); itMapParentNodes != apcNodes.end(); ++itMapParentNodes) {
   1.608 +		for (std::map<uint16_t,aiNode*>::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) {
   1.609 +			if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) {
   1.610 +				++(itMapParentNodes->second->mNumChildren);
   1.611 +			}
   1.612 +		}
   1.613 +		if (itMapParentNodes->second->mNumChildren) {
   1.614 +			itMapParentNodes->second->mChildren = new aiNode* [ itMapParentNodes->second->mNumChildren ];
   1.615 +			uint16_t p = 0;
   1.616 +			for (std::map<uint16_t,aiNode*>::iterator itMapChildNodes = apcNodes.begin(); itMapChildNodes != apcNodes.end(); ++itMapChildNodes) {
   1.617 +				if ((itMapParentNodes->first != itMapChildNodes->first) && (itMapParentNodes->second == itMapChildNodes->second->mParent)) {
   1.618 +					itMapParentNodes->second->mChildren[p++] = itMapChildNodes->second;
   1.619 +				}
   1.620 +			}
   1.621 +		}
   1.622 +	}
   1.623 +
   1.624 +	if (!pScene->mRootNode->mNumChildren)
   1.625 +		throw DeadlyImportError("LWO: Unable to build a valid node graph");
   1.626 +
   1.627 +	// Remove a single root node with no meshes assigned to it ... 
   1.628 +	if (1 == pScene->mRootNode->mNumChildren)	{
   1.629 +		aiNode* pc = pScene->mRootNode->mChildren[0];
   1.630 +		pc->mParent = pScene->mRootNode->mChildren[0] = NULL;
   1.631 +		delete pScene->mRootNode;
   1.632 +		pScene->mRootNode = pc;
   1.633 +	}
   1.634 +
   1.635 +	// convert the whole stuff to RH with CCW winding
   1.636 +	MakeLeftHandedProcess maker;
   1.637 +	maker.Execute(pScene);
   1.638 +
   1.639 +	FlipWindingOrderProcess flipper;
   1.640 +	flipper.Execute(pScene);
   1.641 +}
   1.642 +
   1.643 +// ------------------------------------------------------------------------------------------------
   1.644 +void LWOImporter::ResolveTags()
   1.645 +{
   1.646 +	// --- this function is used for both LWO2 and LWOB
   1.647 +	mMapping->resize(mTags->size(), UINT_MAX);
   1.648 +	for (unsigned int a = 0; a  < mTags->size();++a)	{
   1.649 +
   1.650 +		const std::string& c = (*mTags)[a];
   1.651 +		for (unsigned int i = 0; i < mSurfaces->size();++i)	{
   1.652 +
   1.653 +			const std::string& d = (*mSurfaces)[i].mName;
   1.654 +			if (!ASSIMP_stricmp(c,d))	{
   1.655 +
   1.656 +				(*mMapping)[a] = i;
   1.657 +				break;
   1.658 +			}
   1.659 +		}
   1.660 +	}
   1.661 +}
   1.662 +
   1.663 +// ------------------------------------------------------------------------------------------------
   1.664 +void LWOImporter::ResolveClips()
   1.665 +{
   1.666 +	for( unsigned int i = 0; i < mClips.size();++i)	{
   1.667 +
   1.668 +		Clip& clip = mClips[i];
   1.669 +		if (Clip::REF == clip.type)	{
   1.670 +
   1.671 +			if (clip.clipRef >= mClips.size())	{
   1.672 +				DefaultLogger::get()->error("LWO2: Clip referrer index is out of range");
   1.673 +				clip.clipRef = 0;
   1.674 +			}
   1.675 +
   1.676 +			Clip& dest = mClips[clip.clipRef];
   1.677 +			if (Clip::REF == dest.type) {
   1.678 +				DefaultLogger::get()->error("LWO2: Clip references another clip reference");
   1.679 +				clip.type = Clip::UNSUPPORTED;
   1.680 +			}
   1.681 +
   1.682 +			else	{
   1.683 +				clip.path = dest.path;
   1.684 +				clip.type = dest.type;
   1.685 +			}
   1.686 +		}
   1.687 +	}
   1.688 +}
   1.689 +
   1.690 +// ------------------------------------------------------------------------------------------------
   1.691 +void LWOImporter::AdjustTexturePath(std::string& out)
   1.692 +{
   1.693 +	// --- this function is used for both LWO2 and LWOB
   1.694 +	if (!mIsLWO2 && ::strstr(out.c_str(), "(sequence)"))	{
   1.695 +
   1.696 +		// remove the (sequence) and append 000
   1.697 +		DefaultLogger::get()->info("LWOB: Sequence of animated texture found. It will be ignored");
   1.698 +		out = out.substr(0,out.length()-10) + "000";
   1.699 +	}
   1.700 +
   1.701 +	// format: drive:path/file - we just need to insert a slash after the drive
   1.702 +	std::string::size_type n = out.find_first_of(':');
   1.703 +	if (std::string::npos != n)	{
   1.704 +		out.insert(n+1,"/");
   1.705 +	}
   1.706 +}
   1.707 +
   1.708 +// ------------------------------------------------------------------------------------------------
   1.709 +void LWOImporter::LoadLWOTags(unsigned int size)
   1.710 +{
   1.711 +	// --- this function is used for both LWO2 and LWOB
   1.712 +
   1.713 +	const char* szCur = (const char*)mFileBuffer, *szLast = szCur;
   1.714 +	const char* const szEnd = szLast+size;
   1.715 +	while (szCur < szEnd)
   1.716 +	{
   1.717 +		if (!(*szCur))
   1.718 +		{
   1.719 +			const size_t len = (size_t)(szCur-szLast);
   1.720 +			// FIX: skip empty-sized tags
   1.721 +			if (len)
   1.722 +				mTags->push_back(std::string(szLast,len));
   1.723 +			szCur += (len&0x1 ? 1 : 2);
   1.724 +			szLast = szCur;
   1.725 +		}
   1.726 +		szCur++;
   1.727 +	}
   1.728 +}
   1.729 +
   1.730 +// ------------------------------------------------------------------------------------------------
   1.731 +void LWOImporter::LoadLWOPoints(unsigned int length)
   1.732 +{
   1.733 +	// --- this function is used for both LWO2 and LWOB but for
   1.734 +	// LWO2 we need to allocate 25% more storage - it could be we'll 
   1.735 +	// need to duplicate some points later.
   1.736 +	register unsigned int regularSize = (unsigned int)mCurLayer->mTempPoints.size() + length / 12;
   1.737 +	if (mIsLWO2)
   1.738 +	{
   1.739 +		mCurLayer->mTempPoints.reserve	( regularSize + (regularSize>>2u) );
   1.740 +		mCurLayer->mTempPoints.resize	( regularSize );
   1.741 +
   1.742 +		// initialize all point referrers with the default values
   1.743 +		mCurLayer->mPointReferrers.reserve	( regularSize + (regularSize>>2u) );
   1.744 +		mCurLayer->mPointReferrers.resize	( regularSize, UINT_MAX );
   1.745 +	}
   1.746 +	else mCurLayer->mTempPoints.resize( regularSize );
   1.747 +
   1.748 +	// perform endianess conversions
   1.749 +#ifndef AI_BUILD_BIG_ENDIAN
   1.750 +	for (unsigned int i = 0; i < length>>2;++i)
   1.751 +		ByteSwap::Swap4( mFileBuffer + (i << 2));
   1.752 +#endif
   1.753 +	::memcpy(&mCurLayer->mTempPoints[0],mFileBuffer,length);
   1.754 +}
   1.755 +
   1.756 +// ------------------------------------------------------------------------------------------------
   1.757 +void LWOImporter::LoadLWO2Polygons(unsigned int length)
   1.758 +{
   1.759 +	LE_NCONST uint16_t* const end	= (LE_NCONST uint16_t*)(mFileBuffer+length);
   1.760 +	const uint32_t type = GetU4();
   1.761 +
   1.762 +	// Determine the type of the polygons
   1.763 +	switch (type)
   1.764 +	{
   1.765 +		// read unsupported stuff too (although we wont process it)
   1.766 +	case  AI_LWO_MBAL:
   1.767 +		DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (METABALL)");
   1.768 +		break;
   1.769 +	case  AI_LWO_CURV:
   1.770 +		DefaultLogger::get()->warn("LWO2: Encountered unsupported primitive chunk (SPLINE)");;
   1.771 +		break;
   1.772 +
   1.773 +		// These are ok with no restrictions
   1.774 +	case  AI_LWO_PTCH:
   1.775 +	case  AI_LWO_FACE:
   1.776 +	case  AI_LWO_BONE:
   1.777 +	case  AI_LWO_SUBD:
   1.778 +		break;
   1.779 +	default:
   1.780 +
   1.781 +		// hm!? wtf is this? ok ...
   1.782 +		DefaultLogger::get()->error("LWO2: Ignoring unknown polygon type.");
   1.783 +		break;
   1.784 +	}
   1.785 +
   1.786 +	// first find out how many faces and vertices we'll finally need
   1.787 +	uint16_t* cursor= (uint16_t*)mFileBuffer;
   1.788 +
   1.789 +	unsigned int iNumFaces = 0,iNumVertices = 0;
   1.790 +	CountVertsAndFacesLWO2(iNumVertices,iNumFaces,cursor,end);
   1.791 +
   1.792 +	// allocate the output array and copy face indices
   1.793 +	if (iNumFaces)	{
   1.794 +		cursor = (uint16_t*)mFileBuffer;
   1.795 +
   1.796 +		mCurLayer->mFaces.resize(iNumFaces,LWO::Face(type));
   1.797 +		FaceList::iterator it = mCurLayer->mFaces.begin();
   1.798 +		CopyFaceIndicesLWO2(it,cursor,end);
   1.799 +	}
   1.800 +}
   1.801 +
   1.802 +// ------------------------------------------------------------------------------------------------
   1.803 +void LWOImporter::CountVertsAndFacesLWO2(unsigned int& verts, unsigned int& faces,
   1.804 +	uint16_t*& cursor, const uint16_t* const end, unsigned int max)
   1.805 +{
   1.806 +	while (cursor < end && max--)
   1.807 +	{
   1.808 +		AI_LSWAP2P(cursor);
   1.809 +		uint16_t numIndices = *cursor++;
   1.810 +		numIndices &= 0x03FF;
   1.811 +		verts += numIndices;++faces;
   1.812 +
   1.813 +		for(uint16_t i = 0; i < numIndices; i++)
   1.814 +			ReadVSizedIntLWO2((uint8_t*&)cursor);
   1.815 +	}
   1.816 +}
   1.817 +
   1.818 +// ------------------------------------------------------------------------------------------------
   1.819 +void LWOImporter::CopyFaceIndicesLWO2(FaceList::iterator& it,
   1.820 +	uint16_t*& cursor, 
   1.821 +	const uint16_t* const end)
   1.822 +{
   1.823 +	while (cursor < end)	{
   1.824 +
   1.825 +		LWO::Face& face = *it++;;
   1.826 +		if((face.mNumIndices = (*cursor++) & 0x03FF)) /* byte swapping has already been done */ {
   1.827 +			face.mIndices = new unsigned int[face.mNumIndices];
   1.828 +			for(unsigned int i = 0; i < face.mNumIndices; i++)
   1.829 +			{
   1.830 +				face.mIndices[i] = ReadVSizedIntLWO2((uint8_t*&)cursor) + mCurLayer->mPointIDXOfs;
   1.831 +				if(face.mIndices[i] > mCurLayer->mTempPoints.size())
   1.832 +				{
   1.833 +					DefaultLogger::get()->warn("LWO2: Failure evaluating face record, index is out of range");
   1.834 +					face.mIndices[i] = (unsigned int)mCurLayer->mTempPoints.size()-1;
   1.835 +				}
   1.836 +			}
   1.837 +		}
   1.838 +		else throw DeadlyImportError("LWO2: Encountered invalid face record with zero indices");
   1.839 +	}
   1.840 +}
   1.841 +
   1.842 +
   1.843 +// ------------------------------------------------------------------------------------------------
   1.844 +void LWOImporter::LoadLWO2PolygonTags(unsigned int length)
   1.845 +{
   1.846 +	LE_NCONST uint8_t* const end = mFileBuffer+length;
   1.847 +
   1.848 +	AI_LWO_VALIDATE_CHUNK_LENGTH(length,PTAG,4);
   1.849 +	uint32_t type = GetU4();
   1.850 +
   1.851 +	if (type != AI_LWO_SURF && type != AI_LWO_SMGP)
   1.852 +		return;
   1.853 +
   1.854 +	while (mFileBuffer < end)	{
   1.855 +
   1.856 +		unsigned int i = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
   1.857 +		unsigned int j = GetU2();
   1.858 +
   1.859 +		if (i >= mCurLayer->mFaces.size())	{
   1.860 +			DefaultLogger::get()->warn("LWO2: face index in PTAG is out of range");
   1.861 +			continue;
   1.862 +		}
   1.863 +
   1.864 +		switch (type)	{
   1.865 +
   1.866 +		case AI_LWO_SURF:
   1.867 +			mCurLayer->mFaces[i].surfaceIndex = j;
   1.868 +			break;
   1.869 +		case AI_LWO_SMGP: /* is that really used? */
   1.870 +			mCurLayer->mFaces[i].smoothGroup = j;
   1.871 +			break;
   1.872 +		};
   1.873 +	}
   1.874 +}
   1.875 +
   1.876 +// ------------------------------------------------------------------------------------------------
   1.877 +template <class T>
   1.878 +VMapEntry* FindEntry(std::vector< T >& list,const std::string& name, bool perPoly)
   1.879 +{
   1.880 +	for (typename std::vector< T >::iterator it = list.begin(), end = list.end();it != end; ++it)	{
   1.881 +		if ((*it).name == name)	{
   1.882 +			if (!perPoly)	{
   1.883 +				DefaultLogger::get()->warn("LWO2: Found two VMAP sections with equal names");
   1.884 +			}
   1.885 +			return &(*it);
   1.886 +		}
   1.887 +	}
   1.888 +	list.push_back( T() );
   1.889 +	VMapEntry* p = &list.back();
   1.890 +	p->name = name;
   1.891 +	return p;
   1.892 +}
   1.893 +
   1.894 +// ------------------------------------------------------------------------------------------------
   1.895 +template <class T>
   1.896 +inline void CreateNewEntry(T& chan, unsigned int srcIdx)
   1.897 +{
   1.898 +	if (!chan.name.length())
   1.899 +		return;
   1.900 +
   1.901 +	chan.abAssigned[srcIdx] = true;
   1.902 +	chan.abAssigned.resize(chan.abAssigned.size()+1,false);
   1.903 +
   1.904 +	for (unsigned int a = 0; a < chan.dims;++a)
   1.905 +		chan.rawData.push_back(chan.rawData[srcIdx*chan.dims+a]);
   1.906 +}
   1.907 +
   1.908 +// ------------------------------------------------------------------------------------------------
   1.909 +template <class T>
   1.910 +inline void CreateNewEntry(std::vector< T >& list, unsigned int srcIdx)
   1.911 +{
   1.912 +	for (typename std::vector< T >::iterator it =  list.begin(), end = list.end();it != end;++it)	{
   1.913 +		CreateNewEntry( *it, srcIdx );
   1.914 +	}
   1.915 +}
   1.916 +
   1.917 +// ------------------------------------------------------------------------------------------------
   1.918 +inline void LWOImporter::DoRecursiveVMAPAssignment(VMapEntry* base, unsigned int numRead, 
   1.919 +	unsigned int idx, float* data)
   1.920 +{
   1.921 +	ai_assert(NULL != data);
   1.922 +	LWO::ReferrerList& refList	= mCurLayer->mPointReferrers;
   1.923 +	unsigned int i;
   1.924 +
   1.925 +	base->abAssigned[idx] = true;
   1.926 +	for (i = 0; i < numRead;++i) {
   1.927 +		base->rawData[idx*base->dims+i]= data[i];
   1.928 +	}
   1.929 +
   1.930 +	if (UINT_MAX != (i = refList[idx])) {
   1.931 +		DoRecursiveVMAPAssignment(base,numRead,i,data);
   1.932 +	}
   1.933 +}
   1.934 +
   1.935 +// ------------------------------------------------------------------------------------------------
   1.936 +inline void AddToSingleLinkedList(ReferrerList& refList, unsigned int srcIdx, unsigned int destIdx)
   1.937 +{
   1.938 +	if(UINT_MAX == refList[srcIdx])	{
   1.939 +		refList[srcIdx] = destIdx;
   1.940 +		return;
   1.941 +	}
   1.942 +	AddToSingleLinkedList(refList,refList[srcIdx],destIdx);
   1.943 +}
   1.944 +
   1.945 +// ------------------------------------------------------------------------------------------------
   1.946 +// Load LWO2 vertex map
   1.947 +void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly)
   1.948 +{
   1.949 +	LE_NCONST uint8_t* const end = mFileBuffer+length;
   1.950 +
   1.951 +	AI_LWO_VALIDATE_CHUNK_LENGTH(length,VMAP,6);
   1.952 +	unsigned int type = GetU4();
   1.953 +	unsigned int dims = GetU2();
   1.954 +
   1.955 +	VMapEntry* base;
   1.956 +
   1.957 +	// read the name of the vertex map 
   1.958 +	std::string name;
   1.959 +	GetS0(name,length);
   1.960 +
   1.961 +	switch (type)
   1.962 +	{
   1.963 +	case AI_LWO_TXUV:
   1.964 +		if (dims != 2)	{
   1.965 +			DefaultLogger::get()->warn("LWO2: Skipping UV channel \'" 
   1.966 +			+ name + "\' with !2 components"); 
   1.967 +			return;
   1.968 +		}
   1.969 +		base = FindEntry(mCurLayer->mUVChannels,name,perPoly);
   1.970 +		break;
   1.971 +	case AI_LWO_WGHT:
   1.972 +	case AI_LWO_MNVW:
   1.973 +		if (dims != 1)	{
   1.974 +			DefaultLogger::get()->warn("LWO2: Skipping Weight Channel \'" 
   1.975 +			+ name + "\' with !1 components"); 
   1.976 +			return;
   1.977 +		}
   1.978 +		base = FindEntry((type == AI_LWO_WGHT ? mCurLayer->mWeightChannels
   1.979 +			: mCurLayer->mSWeightChannels),name,perPoly);
   1.980 +		break;
   1.981 +	case AI_LWO_RGB:
   1.982 +	case AI_LWO_RGBA:
   1.983 +		if (dims != 3 && dims != 4)	{
   1.984 +			DefaultLogger::get()->warn("LWO2: Skipping Color Map \'" 
   1.985 +			+ name + "\' with a dimension > 4 or < 3"); 
   1.986 +			return;
   1.987 +		}
   1.988 +		base = FindEntry(mCurLayer->mVColorChannels,name,perPoly);
   1.989 +		break;
   1.990 +
   1.991 +	case AI_LWO_MODO_NORM:
   1.992 +		/*  This is a non-standard extension chunk used by Luxology's MODO.
   1.993 +		 *  It stores per-vertex normals. This VMAP exists just once, has
   1.994 +		 *  3 dimensions and is btw extremely beautiful.
   1.995 +		 */
   1.996 +		if (name != "vert_normals" || dims != 3 || mCurLayer->mNormals.name.length())
   1.997 +			return;
   1.998 +
   1.999 +		DefaultLogger::get()->info("Processing non-standard extension: MODO VMAP.NORM.vert_normals");
  1.1000 +		
  1.1001 +		mCurLayer->mNormals.name = name;
  1.1002 +		base = & mCurLayer->mNormals;
  1.1003 +		break;
  1.1004 +
  1.1005 +	case AI_LWO_PICK: /* these VMAPs are just silently dropped */
  1.1006 +	case AI_LWO_MORF:
  1.1007 +	case AI_LWO_SPOT:
  1.1008 +		return;
  1.1009 +
  1.1010 +	default: 
  1.1011 +		if (name == "APS.Level") {
  1.1012 +			// XXX handle this (seems to be subdivision-related).
  1.1013 +		}
  1.1014 +		DefaultLogger::get()->warn("LWO2: Skipping unknown VMAP/VMAD channel \'" + name + "\'"); 
  1.1015 +		return;
  1.1016 +	};
  1.1017 +	base->Allocate((unsigned int)mCurLayer->mTempPoints.size());
  1.1018 +
  1.1019 +	// now read all entries in the map
  1.1020 +	type = std::min(dims,base->dims); 
  1.1021 +	const unsigned int diff = (dims - type)<<2u;
  1.1022 +
  1.1023 +	LWO::FaceList& list	= mCurLayer->mFaces;
  1.1024 +	LWO::PointList& pointList = mCurLayer->mTempPoints;
  1.1025 +	LWO::ReferrerList& refList = mCurLayer->mPointReferrers;
  1.1026 +
  1.1027 +	float temp[4];
  1.1028 +
  1.1029 +	const unsigned int numPoints = (unsigned int)pointList.size();
  1.1030 +	const unsigned int numFaces  = (unsigned int)list.size();
  1.1031 +
  1.1032 +	while (mFileBuffer < end)	{
  1.1033 +
  1.1034 +		unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs;
  1.1035 +		if (idx >= numPoints)	{
  1.1036 +			DefaultLogger::get()->warn("LWO2: Failure evaluating VMAP/VMAD entry \'" + name + "\', vertex index is out of range");
  1.1037 +			mFileBuffer += base->dims<<2u;
  1.1038 +			continue;
  1.1039 +		}
  1.1040 +		if (perPoly)	{
  1.1041 +			unsigned int polyIdx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mFaceIDXOfs;
  1.1042 +			if (base->abAssigned[idx])	{
  1.1043 +				// we have already a VMAP entry for this vertex - thus
  1.1044 +				// we need to duplicate the corresponding polygon.
  1.1045 +				if (polyIdx >= numFaces)	{
  1.1046 +					DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', polygon index is out of range");
  1.1047 +					mFileBuffer += base->dims<<2u;
  1.1048 +					continue;
  1.1049 +				}
  1.1050 +
  1.1051 +				LWO::Face& src = list[polyIdx];
  1.1052 +
  1.1053 +				// generate a new unique vertex for the corresponding index - but only
  1.1054 +				// if we can find the index in the face
  1.1055 +				bool had = false;
  1.1056 +				for (unsigned int i = 0; i < src.mNumIndices;++i)	{
  1.1057 +
  1.1058 +					unsigned int srcIdx = src.mIndices[i], tmp = idx;
  1.1059 +					do {
  1.1060 +						if (tmp == srcIdx)
  1.1061 +							break;
  1.1062 +					}
  1.1063 +					while ((tmp = refList[tmp]) != UINT_MAX);
  1.1064 +					if (tmp == UINT_MAX) {
  1.1065 +						continue;
  1.1066 +					}
  1.1067 +
  1.1068 +					had = true;
  1.1069 +					refList.resize(refList.size()+1, UINT_MAX);
  1.1070 +						
  1.1071 +					idx = (unsigned int)pointList.size();
  1.1072 +					src.mIndices[i] = (unsigned int)pointList.size();
  1.1073 +
  1.1074 +					// store the index of the new vertex in the old vertex
  1.1075 +					// so we get a single linked list we can traverse in
  1.1076 +					// only one direction
  1.1077 +					AddToSingleLinkedList(refList,srcIdx,src.mIndices[i]);
  1.1078 +					pointList.push_back(pointList[srcIdx]);
  1.1079 +
  1.1080 +					CreateNewEntry(mCurLayer->mVColorChannels,	srcIdx );
  1.1081 +					CreateNewEntry(mCurLayer->mUVChannels,		srcIdx );
  1.1082 +					CreateNewEntry(mCurLayer->mWeightChannels,	srcIdx );
  1.1083 +					CreateNewEntry(mCurLayer->mSWeightChannels,	srcIdx );
  1.1084 +					CreateNewEntry(mCurLayer->mNormals, srcIdx );	
  1.1085 +				}
  1.1086 +				if (!had) {
  1.1087 +					DefaultLogger::get()->warn("LWO2: Failure evaluating VMAD entry \'" + name + "\', vertex index wasn't found in that polygon");
  1.1088 +					ai_assert(had);
  1.1089 +				}
  1.1090 +			}
  1.1091 +		}
  1.1092 +		for (unsigned int l = 0; l < type;++l)
  1.1093 +			temp[l] = GetF4();
  1.1094 +
  1.1095 +		DoRecursiveVMAPAssignment(base,type,idx, temp);
  1.1096 +		mFileBuffer += diff;
  1.1097 +	}
  1.1098 +}
  1.1099 +
  1.1100 +// ------------------------------------------------------------------------------------------------
  1.1101 +// Load LWO2 clip
  1.1102 +void LWOImporter::LoadLWO2Clip(unsigned int length)
  1.1103 +{
  1.1104 +	AI_LWO_VALIDATE_CHUNK_LENGTH(length,CLIP,10);
  1.1105 +
  1.1106 +	mClips.push_back(LWO::Clip());
  1.1107 +	LWO::Clip& clip = mClips.back();
  1.1108 +
  1.1109 +	// first - get the index of the clip
  1.1110 +	clip.idx = GetU4();
  1.1111 +
  1.1112 +	IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
  1.1113 +	switch (head->type)
  1.1114 +	{
  1.1115 +	case AI_LWO_STIL:
  1.1116 +		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,STIL,1);
  1.1117 +
  1.1118 +		// "Normal" texture
  1.1119 +		GetS0(clip.path,head->length);
  1.1120 +		clip.type = Clip::STILL;
  1.1121 +		break;
  1.1122 +
  1.1123 +	case AI_LWO_ISEQ:
  1.1124 +		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,ISEQ,16);
  1.1125 +		// Image sequence. We'll later take the first.
  1.1126 +		{
  1.1127 +			uint8_t digits = GetU1();  mFileBuffer++;
  1.1128 +			int16_t offset = GetU2();  mFileBuffer+=4;
  1.1129 +			int16_t start  = GetU2();  mFileBuffer+=4;
  1.1130 +
  1.1131 +			std::string s;
  1.1132 +			std::ostringstream ss;
  1.1133 +			GetS0(s,head->length);
  1.1134 +
  1.1135 +			head->length -= (unsigned int)s.length()+1;
  1.1136 +			ss << s;
  1.1137 +			ss << std::setw(digits) << offset + start;
  1.1138 +			GetS0(s,head->length);
  1.1139 +			ss << s;
  1.1140 +			clip.path = ss.str();
  1.1141 +			clip.type = Clip::SEQ;
  1.1142 +		}
  1.1143 +		break;
  1.1144 +
  1.1145 +	case AI_LWO_STCC:
  1.1146 +		DefaultLogger::get()->warn("LWO2: Color shifted images are not supported");
  1.1147 +		break;
  1.1148 +
  1.1149 +	case AI_LWO_ANIM:
  1.1150 +		DefaultLogger::get()->warn("LWO2: Animated textures are not supported");
  1.1151 +		break;
  1.1152 +
  1.1153 +	case AI_LWO_XREF:
  1.1154 +		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,XREF,4);
  1.1155 +
  1.1156 +		// Just a cross-reference to another CLIp
  1.1157 +		clip.type = Clip::REF;
  1.1158 +		clip.clipRef = GetU4();
  1.1159 +		break;
  1.1160 +
  1.1161 +	case AI_LWO_NEGA:
  1.1162 +		AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,NEGA,2);
  1.1163 +		clip.negate = (0 != GetU2());
  1.1164 +		break;
  1.1165 +
  1.1166 +	default:
  1.1167 +		DefaultLogger::get()->warn("LWO2: Encountered unknown CLIP subchunk");
  1.1168 +	}
  1.1169 +}
  1.1170 +
  1.1171 +// ------------------------------------------------------------------------------------------------
  1.1172 +// Load envelope description
  1.1173 +void LWOImporter::LoadLWO2Envelope(unsigned int length)
  1.1174 +{
  1.1175 +	LE_NCONST uint8_t* const end = mFileBuffer + length;
  1.1176 +	AI_LWO_VALIDATE_CHUNK_LENGTH(length,ENVL,4);
  1.1177 +
  1.1178 +	mEnvelopes.push_back(LWO::Envelope());
  1.1179 +	LWO::Envelope& envelope = mEnvelopes.back();
  1.1180 +
  1.1181 +	// Get the index of the envelope
  1.1182 +	envelope.index = ReadVSizedIntLWO2(mFileBuffer);
  1.1183 +
  1.1184 +	// It looks like there might be an extra U4 right after the index,
  1.1185 +	// at least in modo (LXOB) files: we'll ignore it if it's zero,
  1.1186 +	// otherwise it represents the start of a subchunk, so we backtrack.
  1.1187 +	if (mIsLXOB)
  1.1188 +	{
  1.1189 +        uint32_t extra = GetU4();
  1.1190 +        if (extra)
  1.1191 +        {
  1.1192 +            mFileBuffer -= 4;
  1.1193 +        }
  1.1194 +	}
  1.1195 +
  1.1196 +	// ... and read all subchunks
  1.1197 +	while (true)
  1.1198 +	{
  1.1199 +		if (mFileBuffer + 6 >= end)break;
  1.1200 +		LE_NCONST IFF::SubChunkHeader* const head = IFF::LoadSubChunk(mFileBuffer);
  1.1201 +
  1.1202 +		if (mFileBuffer + head->length > end)
  1.1203 +			throw DeadlyImportError("LWO2: Invalid envelope chunk length");
  1.1204 +
  1.1205 +		uint8_t* const next = mFileBuffer+head->length;
  1.1206 +		switch (head->type)
  1.1207 +		{
  1.1208 +			// Type & representation of the envelope
  1.1209 +		case AI_LWO_TYPE:
  1.1210 +			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,TYPE,2);
  1.1211 +			mFileBuffer++; // skip user format
  1.1212 +
  1.1213 +			// Determine type of envelope
  1.1214 +			envelope.type  = (LWO::EnvelopeType)*mFileBuffer;
  1.1215 +			++mFileBuffer;
  1.1216 +			break;
  1.1217 +
  1.1218 +			// precondition
  1.1219 +		case AI_LWO_PRE:
  1.1220 +			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,PRE,2);
  1.1221 +			envelope.pre = (LWO::PrePostBehaviour)GetU2();
  1.1222 +			break;
  1.1223 +		
  1.1224 +			// postcondition
  1.1225 +		case AI_LWO_POST:
  1.1226 +			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,POST,2);
  1.1227 +			envelope.post = (LWO::PrePostBehaviour)GetU2();
  1.1228 +			break;
  1.1229 +
  1.1230 +			// keyframe
  1.1231 +		case AI_LWO_KEY: 
  1.1232 +			{
  1.1233 +			AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,KEY,8);
  1.1234 +			
  1.1235 +			envelope.keys.push_back(LWO::Key());
  1.1236 +			LWO::Key& key = envelope.keys.back();
  1.1237 +
  1.1238 +			key.time = GetF4();
  1.1239 +			key.value = GetF4();
  1.1240 +			break;
  1.1241 +			}
  1.1242 +
  1.1243 +			// interval interpolation
  1.1244 +		case AI_LWO_SPAN: 
  1.1245 +			{
  1.1246 +				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,SPAN,4);
  1.1247 +				if (envelope.keys.size()<2)
  1.1248 +					DefaultLogger::get()->warn("LWO2: Unexpected SPAN chunk");
  1.1249 +				else {
  1.1250 +					LWO::Key& key = envelope.keys.back();
  1.1251 +					switch (GetU4())
  1.1252 +					{
  1.1253 +						case AI_LWO_STEP:
  1.1254 +							key.inter = LWO::IT_STEP;break;
  1.1255 +						case AI_LWO_LINE:
  1.1256 +							key.inter = LWO::IT_LINE;break;
  1.1257 +						case AI_LWO_TCB:
  1.1258 +							key.inter = LWO::IT_TCB;break;
  1.1259 +						case AI_LWO_HERM:
  1.1260 +							key.inter = LWO::IT_HERM;break;
  1.1261 +						case AI_LWO_BEZI:
  1.1262 +							key.inter = LWO::IT_BEZI;break;
  1.1263 +						case AI_LWO_BEZ2:
  1.1264 +							key.inter = LWO::IT_BEZ2;break;
  1.1265 +						default:
  1.1266 +							DefaultLogger::get()->warn("LWO2: Unknown interval interpolation mode");
  1.1267 +					};
  1.1268 +
  1.1269 +					// todo ... read params
  1.1270 +				}
  1.1271 +				break;
  1.1272 +			}
  1.1273 +
  1.1274 +		default:
  1.1275 +			DefaultLogger::get()->warn("LWO2: Encountered unknown ENVL subchunk");
  1.1276 +		}
  1.1277 +		// regardless how much we did actually read, go to the next chunk
  1.1278 +		mFileBuffer = next;
  1.1279 +	}
  1.1280 +}
  1.1281 +
  1.1282 +// ------------------------------------------------------------------------------------------------
  1.1283 +// Load file - master function
  1.1284 +void LWOImporter::LoadLWO2File()
  1.1285 +{
  1.1286 +	bool skip = false;
  1.1287 +
  1.1288 +	LE_NCONST uint8_t* const end = mFileBuffer + fileSize;
  1.1289 +	while (true)
  1.1290 +	{
  1.1291 +		if (mFileBuffer + sizeof(IFF::ChunkHeader) > end)break;
  1.1292 +		IFF::ChunkHeader* const head = IFF::LoadChunk(mFileBuffer);
  1.1293 +
  1.1294 +		if (mFileBuffer + head->length > end)
  1.1295 +		{
  1.1296 +			throw DeadlyImportError("LWO2: Chunk length points behind the file");
  1.1297 +			break;
  1.1298 +		}
  1.1299 +		uint8_t* const next = mFileBuffer+head->length;
  1.1300 +		unsigned int iUnnamed = 0;
  1.1301 +
  1.1302 +		switch (head->type)
  1.1303 +		{
  1.1304 +			// new layer
  1.1305 +		case AI_LWO_LAYR:
  1.1306 +			{
  1.1307 +				// add a new layer to the list ....
  1.1308 +				mLayers->push_back ( LWO::Layer() );
  1.1309 +				LWO::Layer& layer = mLayers->back();
  1.1310 +				mCurLayer = &layer;
  1.1311 +
  1.1312 +				AI_LWO_VALIDATE_CHUNK_LENGTH(head->length,LAYR,16);
  1.1313 +
  1.1314 +				// layer index.
  1.1315 +				layer.mIndex = GetU2();
  1.1316 +
  1.1317 +				// Continue loading this layer or ignore it? Check the layer index property
  1.1318 +				if (UINT_MAX != configLayerIndex && (configLayerIndex-1) != layer.mIndex)	{
  1.1319 +					skip = true;
  1.1320 +				}
  1.1321 +				else skip = false;
  1.1322 +
  1.1323 +				// pivot point
  1.1324 +				mFileBuffer += 2; /* unknown */
  1.1325 +				mCurLayer->mPivot.x = GetF4();
  1.1326 +				mCurLayer->mPivot.y = GetF4();
  1.1327 +				mCurLayer->mPivot.z = GetF4();
  1.1328 +				GetS0(layer.mName,head->length-16);
  1.1329 +
  1.1330 +				// if the name is empty, generate a default name
  1.1331 +				if (layer.mName.empty())	{
  1.1332 +					char buffer[128]; // should be sufficiently large
  1.1333 +					::sprintf(buffer,"Layer_%i", iUnnamed++);
  1.1334 +					layer.mName = buffer;
  1.1335 +				}
  1.1336 +
  1.1337 +				// load this layer or ignore it? Check the layer name property
  1.1338 +				if (configLayerName.length() && configLayerName != layer.mName)	{
  1.1339 +					skip = true;
  1.1340 +				}
  1.1341 +				else hasNamedLayer = true;
  1.1342 +
  1.1343 +				// optional: parent of this layer
  1.1344 +				if (mFileBuffer + 2 <= next)
  1.1345 +					layer.mParent = GetU2();
  1.1346 +				else layer.mParent = -1;
  1.1347 +
  1.1348 +				// Set layer skip parameter
  1.1349 +				layer.skip = skip;
  1.1350 +
  1.1351 +				break;
  1.1352 +			}
  1.1353 +
  1.1354 +			// vertex list
  1.1355 +		case AI_LWO_PNTS:
  1.1356 +			{
  1.1357 +				if (skip)
  1.1358 +					break;
  1.1359 +
  1.1360 +				unsigned int old = (unsigned int)mCurLayer->mTempPoints.size();
  1.1361 +				LoadLWOPoints(head->length);
  1.1362 +				mCurLayer->mPointIDXOfs = old;
  1.1363 +				break;
  1.1364 +			}
  1.1365 +			// vertex tags
  1.1366 +		case AI_LWO_VMAD:
  1.1367 +			if (mCurLayer->mFaces.empty())
  1.1368 +			{
  1.1369 +				DefaultLogger::get()->warn("LWO2: Unexpected VMAD chunk");
  1.1370 +				break;
  1.1371 +			}
  1.1372 +			// --- intentionally no break here
  1.1373 +		case AI_LWO_VMAP:
  1.1374 +			{
  1.1375 +				if (skip)
  1.1376 +					break;
  1.1377 +
  1.1378 +				if (mCurLayer->mTempPoints.empty())
  1.1379 +					DefaultLogger::get()->warn("LWO2: Unexpected VMAP chunk");
  1.1380 +				else LoadLWO2VertexMap(head->length,head->type == AI_LWO_VMAD);
  1.1381 +				break;
  1.1382 +			}
  1.1383 +			// face list
  1.1384 +		case AI_LWO_POLS:
  1.1385 +			{
  1.1386 +				if (skip)
  1.1387 +					break;
  1.1388 +
  1.1389 +				unsigned int old = (unsigned int)mCurLayer->mFaces.size();
  1.1390 +				LoadLWO2Polygons(head->length);
  1.1391 +				mCurLayer->mFaceIDXOfs = old;
  1.1392 +				break;
  1.1393 +			}
  1.1394 +			// polygon tags 
  1.1395 +		case AI_LWO_PTAG:
  1.1396 +			{
  1.1397 +				if (skip)
  1.1398 +					break;
  1.1399 +
  1.1400 +				if (mCurLayer->mFaces.empty())
  1.1401 +					DefaultLogger::get()->warn("LWO2: Unexpected PTAG");
  1.1402 +				else LoadLWO2PolygonTags(head->length);
  1.1403 +				break;
  1.1404 +			}
  1.1405 +			// list of tags
  1.1406 +		case AI_LWO_TAGS:
  1.1407 +			{
  1.1408 +				if (!mTags->empty())
  1.1409 +					DefaultLogger::get()->warn("LWO2: SRFS chunk encountered twice");
  1.1410 +				else LoadLWOTags(head->length);
  1.1411 +				break;
  1.1412 +			}
  1.1413 +
  1.1414 +			// surface chunk
  1.1415 +		case AI_LWO_SURF:
  1.1416 +			{
  1.1417 +				LoadLWO2Surface(head->length);
  1.1418 +				break;
  1.1419 +			}
  1.1420 +
  1.1421 +			// clip chunk
  1.1422 +		case AI_LWO_CLIP:
  1.1423 +			{
  1.1424 +				LoadLWO2Clip(head->length);
  1.1425 +				break;
  1.1426 +			}
  1.1427 +
  1.1428 +			// envelope chunk
  1.1429 +		case AI_LWO_ENVL:
  1.1430 +			{
  1.1431 +				LoadLWO2Envelope(head->length);
  1.1432 +				break;
  1.1433 +			}
  1.1434 +		}
  1.1435 +		mFileBuffer = next;
  1.1436 +	}
  1.1437 +}
  1.1438 +
  1.1439 +#endif // !! ASSIMP_BUILD_NO_LWO_IMPORTER