vrshoot

annotate libs/assimp/LWSLoader.cpp @ 0:b2f14e535253

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 01 Feb 2014 19:58:19 +0200
parents
children
rev   line source
nuclear@0 1 /*
nuclear@0 2 ---------------------------------------------------------------------------
nuclear@0 3 Open Asset Import Library (assimp)
nuclear@0 4 ---------------------------------------------------------------------------
nuclear@0 5
nuclear@0 6 Copyright (c) 2006-2012, assimp team
nuclear@0 7
nuclear@0 8 All rights reserved.
nuclear@0 9
nuclear@0 10 Redistribution and use of this software in source and binary forms,
nuclear@0 11 with or without modification, are permitted provided that the following
nuclear@0 12 conditions are met:
nuclear@0 13
nuclear@0 14 * Redistributions of source code must retain the above
nuclear@0 15 copyright notice, this list of conditions and the
nuclear@0 16 following disclaimer.
nuclear@0 17
nuclear@0 18 * Redistributions in binary form must reproduce the above
nuclear@0 19 copyright notice, this list of conditions and the
nuclear@0 20 following disclaimer in the documentation and/or other
nuclear@0 21 materials provided with the distribution.
nuclear@0 22
nuclear@0 23 * Neither the name of the assimp team, nor the names of its
nuclear@0 24 contributors may be used to endorse or promote products
nuclear@0 25 derived from this software without specific prior
nuclear@0 26 written permission of the assimp team.
nuclear@0 27
nuclear@0 28 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
nuclear@0 29 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
nuclear@0 30 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
nuclear@0 31 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
nuclear@0 32 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
nuclear@0 33 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
nuclear@0 34 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
nuclear@0 35 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
nuclear@0 36 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
nuclear@0 37 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
nuclear@0 38 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
nuclear@0 39 ---------------------------------------------------------------------------
nuclear@0 40 */
nuclear@0 41
nuclear@0 42 /** @file LWSLoader.cpp
nuclear@0 43 * @brief Implementation of the LWS importer class
nuclear@0 44 */
nuclear@0 45
nuclear@0 46 #include "AssimpPCH.h"
nuclear@0 47 #ifndef ASSIMP_BUILD_NO_LWS_IMPORTER
nuclear@0 48
nuclear@0 49 #include "LWSLoader.h"
nuclear@0 50 #include "ParsingUtils.h"
nuclear@0 51 #include "fast_atof.h"
nuclear@0 52
nuclear@0 53 #include "SceneCombiner.h"
nuclear@0 54 #include "GenericProperty.h"
nuclear@0 55 #include "SkeletonMeshBuilder.h"
nuclear@0 56 #include "ConvertToLHProcess.h"
nuclear@0 57 #include "Importer.h"
nuclear@0 58
nuclear@0 59 using namespace Assimp;
nuclear@0 60
nuclear@0 61 static const aiImporterDesc desc = {
nuclear@0 62 "LightWave Scene Importer",
nuclear@0 63 "",
nuclear@0 64 "",
nuclear@0 65 "http://www.newtek.com/lightwave.html=",
nuclear@0 66 aiImporterFlags_SupportTextFlavour,
nuclear@0 67 0,
nuclear@0 68 0,
nuclear@0 69 0,
nuclear@0 70 0,
nuclear@0 71 "lws mot"
nuclear@0 72 };
nuclear@0 73
nuclear@0 74 // ------------------------------------------------------------------------------------------------
nuclear@0 75 // Recursive parsing of LWS files
nuclear@0 76 void LWS::Element::Parse (const char*& buffer)
nuclear@0 77 {
nuclear@0 78 for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
nuclear@0 79
nuclear@0 80 // begin of a new element with children
nuclear@0 81 bool sub = false;
nuclear@0 82 if (*buffer == '{') {
nuclear@0 83 ++buffer;
nuclear@0 84 SkipSpaces(&buffer);
nuclear@0 85 sub = true;
nuclear@0 86 }
nuclear@0 87 else if (*buffer == '}')
nuclear@0 88 return;
nuclear@0 89
nuclear@0 90 children.push_back(Element());
nuclear@0 91
nuclear@0 92 // copy data line - read token per token
nuclear@0 93
nuclear@0 94 const char* cur = buffer;
nuclear@0 95 while (!IsSpaceOrNewLine(*buffer)) ++buffer;
nuclear@0 96 children.back().tokens[0] = std::string(cur,(size_t) (buffer-cur));
nuclear@0 97 SkipSpaces(&buffer);
nuclear@0 98
nuclear@0 99 if (children.back().tokens[0] == "Plugin")
nuclear@0 100 {
nuclear@0 101 DefaultLogger::get()->debug("LWS: Skipping over plugin-specific data");
nuclear@0 102
nuclear@0 103 // strange stuff inside Plugin/Endplugin blocks. Needn't
nuclear@0 104 // follow LWS syntax, so we skip over it
nuclear@0 105 for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) {
nuclear@0 106 if (!::strncmp(buffer,"EndPlugin",9)) {
nuclear@0 107 //SkipLine(&buffer);
nuclear@0 108 break;
nuclear@0 109 }
nuclear@0 110 }
nuclear@0 111 continue;
nuclear@0 112 }
nuclear@0 113
nuclear@0 114 cur = buffer;
nuclear@0 115 while (!IsLineEnd(*buffer)) ++buffer;
nuclear@0 116 children.back().tokens[1] = std::string(cur,(size_t) (buffer-cur));
nuclear@0 117
nuclear@0 118 // parse more elements recursively
nuclear@0 119 if (sub)
nuclear@0 120 children.back().Parse(buffer);
nuclear@0 121 }
nuclear@0 122 }
nuclear@0 123
nuclear@0 124 // ------------------------------------------------------------------------------------------------
nuclear@0 125 // Constructor to be privately used by Importer
nuclear@0 126 LWSImporter::LWSImporter()
nuclear@0 127 : noSkeletonMesh()
nuclear@0 128 {
nuclear@0 129 // nothing to do here
nuclear@0 130 }
nuclear@0 131
nuclear@0 132 // ------------------------------------------------------------------------------------------------
nuclear@0 133 // Destructor, private as well
nuclear@0 134 LWSImporter::~LWSImporter()
nuclear@0 135 {
nuclear@0 136 // nothing to do here
nuclear@0 137 }
nuclear@0 138
nuclear@0 139 // ------------------------------------------------------------------------------------------------
nuclear@0 140 // Returns whether the class can handle the format of the given file.
nuclear@0 141 bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,bool checkSig) const
nuclear@0 142 {
nuclear@0 143 const std::string extension = GetExtension(pFile);
nuclear@0 144 if (extension == "lws" || extension == "mot")
nuclear@0 145 return true;
nuclear@0 146
nuclear@0 147 // if check for extension is not enough, check for the magic tokens LWSC and LWMO
nuclear@0 148 if (!extension.length() || checkSig) {
nuclear@0 149 uint32_t tokens[2];
nuclear@0 150 tokens[0] = AI_MAKE_MAGIC("LWSC");
nuclear@0 151 tokens[1] = AI_MAKE_MAGIC("LWMO");
nuclear@0 152 return CheckMagicToken(pIOHandler,pFile,tokens,2);
nuclear@0 153 }
nuclear@0 154 return false;
nuclear@0 155 }
nuclear@0 156
nuclear@0 157 // ------------------------------------------------------------------------------------------------
nuclear@0 158 // Get list of file extensions
nuclear@0 159 const aiImporterDesc* LWSImporter::GetInfo () const
nuclear@0 160 {
nuclear@0 161 return &desc;
nuclear@0 162 }
nuclear@0 163
nuclear@0 164 // ------------------------------------------------------------------------------------------------
nuclear@0 165 // Setup configuration properties
nuclear@0 166 void LWSImporter::SetupProperties(const Importer* pImp)
nuclear@0 167 {
nuclear@0 168 // AI_CONFIG_FAVOUR_SPEED
nuclear@0 169 configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0));
nuclear@0 170
nuclear@0 171 // AI_CONFIG_IMPORT_LWS_ANIM_START
nuclear@0 172 first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START,
nuclear@0 173 150392 /* magic hack */);
nuclear@0 174
nuclear@0 175 // AI_CONFIG_IMPORT_LWS_ANIM_END
nuclear@0 176 last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END,
nuclear@0 177 150392 /* magic hack */);
nuclear@0 178
nuclear@0 179 if (last < first) {
nuclear@0 180 std::swap(last,first);
nuclear@0 181 }
nuclear@0 182
nuclear@0 183 noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0;
nuclear@0 184 }
nuclear@0 185
nuclear@0 186 // ------------------------------------------------------------------------------------------------
nuclear@0 187 // Read an envelope description
nuclear@0 188 void LWSImporter::ReadEnvelope(const LWS::Element& dad, LWO::Envelope& fill )
nuclear@0 189 {
nuclear@0 190 if (dad.children.empty()) {
nuclear@0 191 DefaultLogger::get()->error("LWS: Envelope descriptions must not be empty");
nuclear@0 192 return;
nuclear@0 193 }
nuclear@0 194
nuclear@0 195 // reserve enough storage
nuclear@0 196 std::list< LWS::Element >::const_iterator it = dad.children.begin();;
nuclear@0 197 fill.keys.reserve(strtoul10(it->tokens[1].c_str()));
nuclear@0 198
nuclear@0 199 for (++it; it != dad.children.end(); ++it) {
nuclear@0 200 const char* c = (*it).tokens[1].c_str();
nuclear@0 201
nuclear@0 202 if ((*it).tokens[0] == "Key") {
nuclear@0 203 fill.keys.push_back(LWO::Key());
nuclear@0 204 LWO::Key& key = fill.keys.back();
nuclear@0 205
nuclear@0 206 float f;
nuclear@0 207 SkipSpaces(&c);
nuclear@0 208 c = fast_atoreal_move<float>(c,key.value);
nuclear@0 209 SkipSpaces(&c);
nuclear@0 210 c = fast_atoreal_move<float>(c,f);
nuclear@0 211
nuclear@0 212 key.time = f;
nuclear@0 213
nuclear@0 214 unsigned int span = strtoul10(c,&c), num = 0;
nuclear@0 215 switch (span) {
nuclear@0 216
nuclear@0 217 case 0:
nuclear@0 218 key.inter = LWO::IT_TCB;
nuclear@0 219 num = 5;
nuclear@0 220 break;
nuclear@0 221 case 1:
nuclear@0 222 case 2:
nuclear@0 223 key.inter = LWO::IT_HERM;
nuclear@0 224 num = 5;
nuclear@0 225 break;
nuclear@0 226 case 3:
nuclear@0 227 key.inter = LWO::IT_LINE;
nuclear@0 228 num = 0;
nuclear@0 229 break;
nuclear@0 230 case 4:
nuclear@0 231 key.inter = LWO::IT_STEP;
nuclear@0 232 num = 0;
nuclear@0 233 break;
nuclear@0 234 case 5:
nuclear@0 235 key.inter = LWO::IT_BEZ2;
nuclear@0 236 num = 4;
nuclear@0 237 break;
nuclear@0 238 default:
nuclear@0 239 DefaultLogger::get()->error("LWS: Unknown span type");
nuclear@0 240 }
nuclear@0 241 for (unsigned int i = 0; i < num;++i) {
nuclear@0 242 SkipSpaces(&c);
nuclear@0 243 c = fast_atoreal_move<float>(c,key.params[i]);
nuclear@0 244 }
nuclear@0 245 }
nuclear@0 246 else if ((*it).tokens[0] == "Behaviors") {
nuclear@0 247 SkipSpaces(&c);
nuclear@0 248 fill.pre = (LWO::PrePostBehaviour) strtoul10(c,&c);
nuclear@0 249 SkipSpaces(&c);
nuclear@0 250 fill.post = (LWO::PrePostBehaviour) strtoul10(c,&c);
nuclear@0 251 }
nuclear@0 252 }
nuclear@0 253 }
nuclear@0 254
nuclear@0 255 // ------------------------------------------------------------------------------------------------
nuclear@0 256 // Read animation channels in the old LightWave animation format
nuclear@0 257 void LWSImporter::ReadEnvelope_Old(
nuclear@0 258 std::list< LWS::Element >::const_iterator& it,
nuclear@0 259 const std::list< LWS::Element >::const_iterator& end,
nuclear@0 260 LWS::NodeDesc& nodes,
nuclear@0 261 unsigned int /*version*/)
nuclear@0 262 {
nuclear@0 263 unsigned int num,sub_num;
nuclear@0 264 if (++it == end)goto unexpected_end;
nuclear@0 265
nuclear@0 266 num = strtoul10((*it).tokens[0].c_str());
nuclear@0 267 for (unsigned int i = 0; i < num; ++i) {
nuclear@0 268
nuclear@0 269 nodes.channels.push_back(LWO::Envelope());
nuclear@0 270 LWO::Envelope& envl = nodes.channels.back();
nuclear@0 271
nuclear@0 272 envl.index = i;
nuclear@0 273 envl.type = (LWO::EnvelopeType)(i+1);
nuclear@0 274
nuclear@0 275 if (++it == end)goto unexpected_end;
nuclear@0 276 sub_num = strtoul10((*it).tokens[0].c_str());
nuclear@0 277
nuclear@0 278 for (unsigned int n = 0; n < sub_num;++n) {
nuclear@0 279
nuclear@0 280 if (++it == end)goto unexpected_end;
nuclear@0 281
nuclear@0 282 // parse value and time, skip the rest for the moment.
nuclear@0 283 LWO::Key key;
nuclear@0 284 const char* c = fast_atoreal_move<float>((*it).tokens[0].c_str(),key.value);
nuclear@0 285 SkipSpaces(&c);
nuclear@0 286 float f;
nuclear@0 287 fast_atoreal_move<float>((*it).tokens[0].c_str(),f);
nuclear@0 288 key.time = f;
nuclear@0 289
nuclear@0 290 envl.keys.push_back(key);
nuclear@0 291 }
nuclear@0 292 }
nuclear@0 293 return;
nuclear@0 294
nuclear@0 295 unexpected_end:
nuclear@0 296 DefaultLogger::get()->error("LWS: Encountered unexpected end of file while parsing object motion");
nuclear@0 297 }
nuclear@0 298
nuclear@0 299 // ------------------------------------------------------------------------------------------------
nuclear@0 300 // Setup a nice name for a node
nuclear@0 301 void LWSImporter::SetupNodeName(aiNode* nd, LWS::NodeDesc& src)
nuclear@0 302 {
nuclear@0 303 const unsigned int combined = src.number | ((unsigned int)src.type) << 28u;
nuclear@0 304
nuclear@0 305 // the name depends on the type. We break LWS's strange naming convention
nuclear@0 306 // and return human-readable, but still machine-parsable and unique, strings.
nuclear@0 307 if (src.type == LWS::NodeDesc::OBJECT) {
nuclear@0 308
nuclear@0 309 if (src.path.length()) {
nuclear@0 310 std::string::size_type s = src.path.find_last_of("\\/");
nuclear@0 311 if (s == std::string::npos)
nuclear@0 312 s = 0;
nuclear@0 313 else ++s;
nuclear@0 314 std::string::size_type t = src.path.substr(s).find_last_of(".");
nuclear@0 315
nuclear@0 316 nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.path.substr(s).substr(0,t).c_str(),combined);
nuclear@0 317 return;
nuclear@0 318 }
nuclear@0 319 }
nuclear@0 320 nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.name,combined);
nuclear@0 321 }
nuclear@0 322
nuclear@0 323 // ------------------------------------------------------------------------------------------------
nuclear@0 324 // Recursively build the scenegraph
nuclear@0 325 void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<AttachmentInfo>& attach,
nuclear@0 326 BatchLoader& batch,
nuclear@0 327 aiCamera**& camOut,
nuclear@0 328 aiLight**& lightOut,
nuclear@0 329 std::vector<aiNodeAnim*>& animOut)
nuclear@0 330 {
nuclear@0 331 // Setup a very cryptic name for the node, we want the user to be happy
nuclear@0 332 SetupNodeName(nd,src);
nuclear@0 333 aiNode* ndAnim = nd;
nuclear@0 334
nuclear@0 335 // If the node is an object
nuclear@0 336 if (src.type == LWS::NodeDesc::OBJECT) {
nuclear@0 337
nuclear@0 338 // If the object is from an external file, get it
nuclear@0 339 aiScene* obj = NULL;
nuclear@0 340 if (src.path.length() ) {
nuclear@0 341 obj = batch.GetImport(src.id);
nuclear@0 342 if (!obj) {
nuclear@0 343 DefaultLogger::get()->error("LWS: Failed to read external file " + src.path);
nuclear@0 344 }
nuclear@0 345 else {
nuclear@0 346 if (obj->mRootNode->mNumChildren == 1) {
nuclear@0 347
nuclear@0 348 //If the pivot is not set for this layer, get it from the external object
nuclear@0 349 if (!src.isPivotSet) {
nuclear@0 350 src.pivotPos.x = +obj->mRootNode->mTransformation.a4;
nuclear@0 351 src.pivotPos.y = +obj->mRootNode->mTransformation.b4;
nuclear@0 352 src.pivotPos.z = -obj->mRootNode->mTransformation.c4; //The sign is the RH to LH back conversion
nuclear@0 353 }
nuclear@0 354
nuclear@0 355 //Remove first node from obj (the old pivot), reset transform of second node (the mesh node)
nuclear@0 356 aiNode* newRootNode = obj->mRootNode->mChildren[0];
nuclear@0 357 obj->mRootNode->mChildren[0] = NULL;
nuclear@0 358 delete obj->mRootNode;
nuclear@0 359
nuclear@0 360 obj->mRootNode = newRootNode;
nuclear@0 361 obj->mRootNode->mTransformation.a4 = 0.0;
nuclear@0 362 obj->mRootNode->mTransformation.b4 = 0.0;
nuclear@0 363 obj->mRootNode->mTransformation.c4 = 0.0;
nuclear@0 364 }
nuclear@0 365 }
nuclear@0 366 }
nuclear@0 367
nuclear@0 368 //Setup the pivot node (also the animation node), the one we received
nuclear@0 369 nd->mName = std::string("Pivot:") + nd->mName.data;
nuclear@0 370 ndAnim = nd;
nuclear@0 371
nuclear@0 372 //Add the attachment node to it
nuclear@0 373 nd->mNumChildren = 1;
nuclear@0 374 nd->mChildren = new aiNode*[1];
nuclear@0 375 nd->mChildren[0] = new aiNode();
nuclear@0 376 nd->mChildren[0]->mParent = nd;
nuclear@0 377 nd->mChildren[0]->mTransformation.a4 = -src.pivotPos.x;
nuclear@0 378 nd->mChildren[0]->mTransformation.b4 = -src.pivotPos.y;
nuclear@0 379 nd->mChildren[0]->mTransformation.c4 = -src.pivotPos.z;
nuclear@0 380 SetupNodeName(nd->mChildren[0], src);
nuclear@0 381
nuclear@0 382 //Update the attachment node
nuclear@0 383 nd = nd->mChildren[0];
nuclear@0 384
nuclear@0 385 //Push attachment, if the object came from an external file
nuclear@0 386 if (obj) {
nuclear@0 387 attach.push_back(AttachmentInfo(obj,nd));
nuclear@0 388 }
nuclear@0 389 }
nuclear@0 390
nuclear@0 391 // If object is a light source - setup a corresponding ai structure
nuclear@0 392 else if (src.type == LWS::NodeDesc::LIGHT) {
nuclear@0 393 aiLight* lit = *lightOut++ = new aiLight();
nuclear@0 394
nuclear@0 395 // compute final light color
nuclear@0 396 lit->mColorDiffuse = lit->mColorSpecular = src.lightColor*src.lightIntensity;
nuclear@0 397
nuclear@0 398 // name to attach light to node -> unique due to LWs indexing system
nuclear@0 399 lit->mName = nd->mName;
nuclear@0 400
nuclear@0 401 // detemine light type and setup additional members
nuclear@0 402 if (src.lightType == 2) { /* spot light */
nuclear@0 403
nuclear@0 404 lit->mType = aiLightSource_SPOT;
nuclear@0 405 lit->mAngleInnerCone = (float)AI_DEG_TO_RAD( src.lightConeAngle );
nuclear@0 406 lit->mAngleOuterCone = lit->mAngleInnerCone+(float)AI_DEG_TO_RAD( src.lightEdgeAngle );
nuclear@0 407
nuclear@0 408 }
nuclear@0 409 else if (src.lightType == 1) { /* directional light source */
nuclear@0 410 lit->mType = aiLightSource_DIRECTIONAL;
nuclear@0 411 }
nuclear@0 412 else lit->mType = aiLightSource_POINT;
nuclear@0 413
nuclear@0 414 // fixme: no proper handling of light falloffs yet
nuclear@0 415 if (src.lightFalloffType == 1)
nuclear@0 416 lit->mAttenuationConstant = 1.f;
nuclear@0 417 else if (src.lightFalloffType == 1)
nuclear@0 418 lit->mAttenuationLinear = 1.f;
nuclear@0 419 else
nuclear@0 420 lit->mAttenuationQuadratic = 1.f;
nuclear@0 421 }
nuclear@0 422
nuclear@0 423 // If object is a camera - setup a corresponding ai structure
nuclear@0 424 else if (src.type == LWS::NodeDesc::CAMERA) {
nuclear@0 425 aiCamera* cam = *camOut++ = new aiCamera();
nuclear@0 426
nuclear@0 427 // name to attach cam to node -> unique due to LWs indexing system
nuclear@0 428 cam->mName = nd->mName;
nuclear@0 429 }
nuclear@0 430
nuclear@0 431 // Get the node transformation from the LWO key
nuclear@0 432 LWO::AnimResolver resolver(src.channels,fps);
nuclear@0 433 resolver.ExtractBindPose(ndAnim->mTransformation);
nuclear@0 434
nuclear@0 435 // .. and construct animation channels
nuclear@0 436 aiNodeAnim* anim = NULL;
nuclear@0 437
nuclear@0 438 if (first != last) {
nuclear@0 439 resolver.SetAnimationRange(first,last);
nuclear@0 440 resolver.ExtractAnimChannel(&anim,AI_LWO_ANIM_FLAG_SAMPLE_ANIMS|AI_LWO_ANIM_FLAG_START_AT_ZERO);
nuclear@0 441 if (anim) {
nuclear@0 442 anim->mNodeName = ndAnim->mName;
nuclear@0 443 animOut.push_back(anim);
nuclear@0 444 }
nuclear@0 445 }
nuclear@0 446
nuclear@0 447 // Add children
nuclear@0 448 if (src.children.size()) {
nuclear@0 449 nd->mChildren = new aiNode*[src.children.size()];
nuclear@0 450 for (std::list<LWS::NodeDesc*>::iterator it = src.children.begin(); it != src.children.end(); ++it) {
nuclear@0 451 aiNode* ndd = nd->mChildren[nd->mNumChildren++] = new aiNode();
nuclear@0 452 ndd->mParent = nd;
nuclear@0 453
nuclear@0 454 BuildGraph(ndd,**it,attach,batch,camOut,lightOut,animOut);
nuclear@0 455 }
nuclear@0 456 }
nuclear@0 457 }
nuclear@0 458
nuclear@0 459 // ------------------------------------------------------------------------------------------------
nuclear@0 460 // Determine the exact location of a LWO file
nuclear@0 461 std::string LWSImporter::FindLWOFile(const std::string& in)
nuclear@0 462 {
nuclear@0 463 // insert missing directory seperator if necessary
nuclear@0 464 std::string tmp;
nuclear@0 465 if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/')
nuclear@0 466 {
nuclear@0 467 tmp = in[0] + (":\\" + in.substr(2));
nuclear@0 468 }
nuclear@0 469 else tmp = in;
nuclear@0 470
nuclear@0 471 if (io->Exists(tmp)) {
nuclear@0 472 return in;
nuclear@0 473 }
nuclear@0 474
nuclear@0 475 // file is not accessible for us ... maybe it's packed by
nuclear@0 476 // LightWave's 'Package Scene' command?
nuclear@0 477
nuclear@0 478 // Relevant for us are the following two directories:
nuclear@0 479 // <folder>\Objects\<hh>\<*>.lwo
nuclear@0 480 // <folder>\Scenes\<hh>\<*>.lws
nuclear@0 481 // where <hh> is optional.
nuclear@0 482
nuclear@0 483 std::string test = ".." + (io->getOsSeparator() + tmp);
nuclear@0 484 if (io->Exists(test)) {
nuclear@0 485 return test;
nuclear@0 486 }
nuclear@0 487
nuclear@0 488 test = ".." + (io->getOsSeparator() + test);
nuclear@0 489 if (io->Exists(test)) {
nuclear@0 490 return test;
nuclear@0 491 }
nuclear@0 492
nuclear@0 493
nuclear@0 494 // return original path, maybe the IOsystem knows better
nuclear@0 495 return tmp;
nuclear@0 496 }
nuclear@0 497
nuclear@0 498 // ------------------------------------------------------------------------------------------------
nuclear@0 499 // Read file into given scene data structure
nuclear@0 500 void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene,
nuclear@0 501 IOSystem* pIOHandler)
nuclear@0 502 {
nuclear@0 503 io = pIOHandler;
nuclear@0 504 boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
nuclear@0 505
nuclear@0 506 // Check whether we can read from the file
nuclear@0 507 if( file.get() == NULL) {
nuclear@0 508 throw DeadlyImportError( "Failed to open LWS file " + pFile + ".");
nuclear@0 509 }
nuclear@0 510
nuclear@0 511 // Allocate storage and copy the contents of the file to a memory buffer
nuclear@0 512 std::vector< char > mBuffer;
nuclear@0 513 TextFileToBuffer(file.get(),mBuffer);
nuclear@0 514
nuclear@0 515 // Parse the file structure
nuclear@0 516 LWS::Element root; const char* dummy = &mBuffer[0];
nuclear@0 517 root.Parse(dummy);
nuclear@0 518
nuclear@0 519 // Construct a Batchimporter to read more files recursively
nuclear@0 520 BatchLoader batch(pIOHandler);
nuclear@0 521 // batch.SetBasePath(pFile);
nuclear@0 522
nuclear@0 523 // Construct an array to receive the flat output graph
nuclear@0 524 std::list<LWS::NodeDesc> nodes;
nuclear@0 525
nuclear@0 526 unsigned int cur_light = 0, cur_camera = 0, cur_object = 0;
nuclear@0 527 unsigned int num_light = 0, num_camera = 0, num_object = 0;
nuclear@0 528
nuclear@0 529 // check magic identifier, 'LWSC'
nuclear@0 530 bool motion_file = false;
nuclear@0 531 std::list< LWS::Element >::const_iterator it = root.children.begin();
nuclear@0 532
nuclear@0 533 if ((*it).tokens[0] == "LWMO")
nuclear@0 534 motion_file = true;
nuclear@0 535
nuclear@0 536 if ((*it).tokens[0] != "LWSC" && !motion_file)
nuclear@0 537 throw DeadlyImportError("LWS: Not a LightWave scene, magic tag LWSC not found");
nuclear@0 538
nuclear@0 539 // get file format version and print to log
nuclear@0 540 ++it;
nuclear@0 541 unsigned int version = strtoul10((*it).tokens[0].c_str());
nuclear@0 542 DefaultLogger::get()->info("LWS file format version is " + (*it).tokens[0]);
nuclear@0 543 first = 0.;
nuclear@0 544 last = 60.;
nuclear@0 545 fps = 25.; /* seems to be a good default frame rate */
nuclear@0 546
nuclear@0 547 // Now read all elements in a very straghtforward manner
nuclear@0 548 for (; it != root.children.end(); ++it) {
nuclear@0 549 const char* c = (*it).tokens[1].c_str();
nuclear@0 550
nuclear@0 551 // 'FirstFrame': begin of animation slice
nuclear@0 552 if ((*it).tokens[0] == "FirstFrame") {
nuclear@0 553 if (150392. != first /* see SetupProperties() */)
nuclear@0 554 first = strtoul10(c,&c)-1.; /* we're zero-based */
nuclear@0 555 }
nuclear@0 556
nuclear@0 557 // 'LastFrame': end of animation slice
nuclear@0 558 else if ((*it).tokens[0] == "LastFrame") {
nuclear@0 559 if (150392. != last /* see SetupProperties() */)
nuclear@0 560 last = strtoul10(c,&c)-1.; /* we're zero-based */
nuclear@0 561 }
nuclear@0 562
nuclear@0 563 // 'FramesPerSecond': frames per second
nuclear@0 564 else if ((*it).tokens[0] == "FramesPerSecond") {
nuclear@0 565 fps = strtoul10(c,&c);
nuclear@0 566 }
nuclear@0 567
nuclear@0 568 // 'LoadObjectLayer': load a layer of a specific LWO file
nuclear@0 569 else if ((*it).tokens[0] == "LoadObjectLayer") {
nuclear@0 570
nuclear@0 571 // get layer index
nuclear@0 572 const int layer = strtoul10(c,&c);
nuclear@0 573
nuclear@0 574 // setup the layer to be loaded
nuclear@0 575 BatchLoader::PropertyMap props;
nuclear@0 576 SetGenericProperty(props.ints,AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,layer);
nuclear@0 577
nuclear@0 578 // add node to list
nuclear@0 579 LWS::NodeDesc d;
nuclear@0 580 d.type = LWS::NodeDesc::OBJECT;
nuclear@0 581 if (version >= 4) { // handle LWSC 4 explicit ID
nuclear@0 582 SkipSpaces(&c);
nuclear@0 583 d.number = strtoul16(c,&c) & AI_LWS_MASK;
nuclear@0 584 }
nuclear@0 585 else d.number = cur_object++;
nuclear@0 586
nuclear@0 587 // and add the file to the import list
nuclear@0 588 SkipSpaces(&c);
nuclear@0 589 std::string path = FindLWOFile( c );
nuclear@0 590 d.path = path;
nuclear@0 591 d.id = batch.AddLoadRequest(path,0,&props);
nuclear@0 592
nuclear@0 593 nodes.push_back(d);
nuclear@0 594 num_object++;
nuclear@0 595 }
nuclear@0 596 // 'LoadObject': load a LWO file into the scenegraph
nuclear@0 597 else if ((*it).tokens[0] == "LoadObject") {
nuclear@0 598
nuclear@0 599 // add node to list
nuclear@0 600 LWS::NodeDesc d;
nuclear@0 601 d.type = LWS::NodeDesc::OBJECT;
nuclear@0 602
nuclear@0 603 if (version >= 4) { // handle LWSC 4 explicit ID
nuclear@0 604 d.number = strtoul16(c,&c) & AI_LWS_MASK;
nuclear@0 605 SkipSpaces(&c);
nuclear@0 606 }
nuclear@0 607 else d.number = cur_object++;
nuclear@0 608 std::string path = FindLWOFile( c );
nuclear@0 609 d.id = batch.AddLoadRequest(path,0,NULL);
nuclear@0 610
nuclear@0 611 d.path = path;
nuclear@0 612 nodes.push_back(d);
nuclear@0 613 num_object++;
nuclear@0 614 }
nuclear@0 615 // 'AddNullObject': add a dummy node to the hierarchy
nuclear@0 616 else if ((*it).tokens[0] == "AddNullObject") {
nuclear@0 617
nuclear@0 618 // add node to list
nuclear@0 619 LWS::NodeDesc d;
nuclear@0 620 d.type = LWS::NodeDesc::OBJECT;
nuclear@0 621 if (version >= 4) { // handle LWSC 4 explicit ID
nuclear@0 622 d.number = strtoul16(c,&c) & AI_LWS_MASK;
nuclear@0 623 SkipSpaces(&c);
nuclear@0 624 }
nuclear@0 625 else d.number = cur_object++;
nuclear@0 626 d.name = c;
nuclear@0 627 nodes.push_back(d);
nuclear@0 628
nuclear@0 629 num_object++;
nuclear@0 630 }
nuclear@0 631 // 'NumChannels': Number of envelope channels assigned to last layer
nuclear@0 632 else if ((*it).tokens[0] == "NumChannels") {
nuclear@0 633 // ignore for now
nuclear@0 634 }
nuclear@0 635 // 'Channel': preceedes any envelope description
nuclear@0 636 else if ((*it).tokens[0] == "Channel") {
nuclear@0 637 if (nodes.empty()) {
nuclear@0 638 if (motion_file) {
nuclear@0 639
nuclear@0 640 // LightWave motion file. Add dummy node
nuclear@0 641 LWS::NodeDesc d;
nuclear@0 642 d.type = LWS::NodeDesc::OBJECT;
nuclear@0 643 d.name = c;
nuclear@0 644 d.number = cur_object++;
nuclear@0 645 nodes.push_back(d);
nuclear@0 646 }
nuclear@0 647 else DefaultLogger::get()->error("LWS: Unexpected keyword: \'Channel\'");
nuclear@0 648 }
nuclear@0 649
nuclear@0 650 // important: index of channel
nuclear@0 651 nodes.back().channels.push_back(LWO::Envelope());
nuclear@0 652 LWO::Envelope& env = nodes.back().channels.back();
nuclear@0 653
nuclear@0 654 env.index = strtoul10(c);
nuclear@0 655
nuclear@0 656 // currently we can just interpret the standard channels 0...9
nuclear@0 657 // (hack) assume that index-i yields the binary channel type from LWO
nuclear@0 658 env.type = (LWO::EnvelopeType)(env.index+1);
nuclear@0 659
nuclear@0 660 }
nuclear@0 661 // 'Envelope': a single animation channel
nuclear@0 662 else if ((*it).tokens[0] == "Envelope") {
nuclear@0 663 if (nodes.empty() || nodes.back().channels.empty())
nuclear@0 664 DefaultLogger::get()->error("LWS: Unexpected keyword: \'Envelope\'");
nuclear@0 665 else {
nuclear@0 666 ReadEnvelope((*it),nodes.back().channels.back());
nuclear@0 667 }
nuclear@0 668 }
nuclear@0 669 // 'ObjectMotion': animation information for older lightwave formats
nuclear@0 670 else if (version < 3 && ((*it).tokens[0] == "ObjectMotion" ||
nuclear@0 671 (*it).tokens[0] == "CameraMotion" ||
nuclear@0 672 (*it).tokens[0] == "LightMotion")) {
nuclear@0 673
nuclear@0 674 if (nodes.empty())
nuclear@0 675 DefaultLogger::get()->error("LWS: Unexpected keyword: \'<Light|Object|Camera>Motion\'");
nuclear@0 676 else {
nuclear@0 677 ReadEnvelope_Old(it,root.children.end(),nodes.back(),version);
nuclear@0 678 }
nuclear@0 679 }
nuclear@0 680 // 'Pre/PostBehavior': pre/post animation behaviour for LWSC 2
nuclear@0 681 else if (version == 2 && (*it).tokens[0] == "Pre/PostBehavior") {
nuclear@0 682 if (nodes.empty())
nuclear@0 683 DefaultLogger::get()->error("LWS: Unexpected keyword: \'Pre/PostBehavior'");
nuclear@0 684 else {
nuclear@0 685 for (std::list<LWO::Envelope>::iterator it = nodes.back().channels.begin(); it != nodes.back().channels.end(); ++it) {
nuclear@0 686 // two ints per envelope
nuclear@0 687 LWO::Envelope& env = *it;
nuclear@0 688 env.pre = (LWO::PrePostBehaviour) strtoul10(c,&c); SkipSpaces(&c);
nuclear@0 689 env.post = (LWO::PrePostBehaviour) strtoul10(c,&c); SkipSpaces(&c);
nuclear@0 690 }
nuclear@0 691 }
nuclear@0 692 }
nuclear@0 693 // 'ParentItem': specifies the parent of the current element
nuclear@0 694 else if ((*it).tokens[0] == "ParentItem") {
nuclear@0 695 if (nodes.empty())
nuclear@0 696 DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentItem\'");
nuclear@0 697
nuclear@0 698 else nodes.back().parent = strtoul16(c,&c);
nuclear@0 699 }
nuclear@0 700 // 'ParentObject': deprecated one for older formats
nuclear@0 701 else if (version < 3 && (*it).tokens[0] == "ParentObject") {
nuclear@0 702 if (nodes.empty())
nuclear@0 703 DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentObject\'");
nuclear@0 704
nuclear@0 705 else {
nuclear@0 706 nodes.back().parent = strtoul10(c,&c) | (1u << 28u);
nuclear@0 707 }
nuclear@0 708 }
nuclear@0 709 // 'AddCamera': add a camera to the scenegraph
nuclear@0 710 else if ((*it).tokens[0] == "AddCamera") {
nuclear@0 711
nuclear@0 712 // add node to list
nuclear@0 713 LWS::NodeDesc d;
nuclear@0 714 d.type = LWS::NodeDesc::CAMERA;
nuclear@0 715
nuclear@0 716 if (version >= 4) { // handle LWSC 4 explicit ID
nuclear@0 717 d.number = strtoul16(c,&c) & AI_LWS_MASK;
nuclear@0 718 }
nuclear@0 719 else d.number = cur_camera++;
nuclear@0 720 nodes.push_back(d);
nuclear@0 721
nuclear@0 722 num_camera++;
nuclear@0 723 }
nuclear@0 724 // 'CameraName': set name of currently active camera
nuclear@0 725 else if ((*it).tokens[0] == "CameraName") {
nuclear@0 726 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA)
nuclear@0 727 DefaultLogger::get()->error("LWS: Unexpected keyword: \'CameraName\'");
nuclear@0 728
nuclear@0 729 else nodes.back().name = c;
nuclear@0 730 }
nuclear@0 731 // 'AddLight': add a light to the scenegraph
nuclear@0 732 else if ((*it).tokens[0] == "AddLight") {
nuclear@0 733
nuclear@0 734 // add node to list
nuclear@0 735 LWS::NodeDesc d;
nuclear@0 736 d.type = LWS::NodeDesc::LIGHT;
nuclear@0 737
nuclear@0 738 if (version >= 4) { // handle LWSC 4 explicit ID
nuclear@0 739 d.number = strtoul16(c,&c) & AI_LWS_MASK;
nuclear@0 740 }
nuclear@0 741 else d.number = cur_light++;
nuclear@0 742 nodes.push_back(d);
nuclear@0 743
nuclear@0 744 num_light++;
nuclear@0 745 }
nuclear@0 746 // 'LightName': set name of currently active light
nuclear@0 747 else if ((*it).tokens[0] == "LightName") {
nuclear@0 748 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 749 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightName\'");
nuclear@0 750
nuclear@0 751 else nodes.back().name = c;
nuclear@0 752 }
nuclear@0 753 // 'LightIntensity': set intensity of currently active light
nuclear@0 754 else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity" ) {
nuclear@0 755 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 756 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightIntensity\'");
nuclear@0 757
nuclear@0 758 else fast_atoreal_move<float>(c, nodes.back().lightIntensity );
nuclear@0 759
nuclear@0 760 }
nuclear@0 761 // 'LightType': set type of currently active light
nuclear@0 762 else if ((*it).tokens[0] == "LightType") {
nuclear@0 763 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 764 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightType\'");
nuclear@0 765
nuclear@0 766 else nodes.back().lightType = strtoul10(c);
nuclear@0 767
nuclear@0 768 }
nuclear@0 769 // 'LightFalloffType': set falloff type of currently active light
nuclear@0 770 else if ((*it).tokens[0] == "LightFalloffType") {
nuclear@0 771 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 772 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightFalloffType\'");
nuclear@0 773
nuclear@0 774 else nodes.back().lightFalloffType = strtoul10(c);
nuclear@0 775
nuclear@0 776 }
nuclear@0 777 // 'LightConeAngle': set cone angle of currently active light
nuclear@0 778 else if ((*it).tokens[0] == "LightConeAngle") {
nuclear@0 779 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 780 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightConeAngle\'");
nuclear@0 781
nuclear@0 782 else nodes.back().lightConeAngle = fast_atof(c);
nuclear@0 783
nuclear@0 784 }
nuclear@0 785 // 'LightEdgeAngle': set area where we're smoothing from min to max intensity
nuclear@0 786 else if ((*it).tokens[0] == "LightEdgeAngle") {
nuclear@0 787 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 788 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightEdgeAngle\'");
nuclear@0 789
nuclear@0 790 else nodes.back().lightEdgeAngle = fast_atof(c);
nuclear@0 791
nuclear@0 792 }
nuclear@0 793 // 'LightColor': set color of currently active light
nuclear@0 794 else if ((*it).tokens[0] == "LightColor") {
nuclear@0 795 if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT)
nuclear@0 796 DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightColor\'");
nuclear@0 797
nuclear@0 798 else {
nuclear@0 799 c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.r );
nuclear@0 800 SkipSpaces(&c);
nuclear@0 801 c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.g );
nuclear@0 802 SkipSpaces(&c);
nuclear@0 803 c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.b );
nuclear@0 804 }
nuclear@0 805 }
nuclear@0 806
nuclear@0 807 // 'PivotPosition': position of local transformation origin
nuclear@0 808 else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") {
nuclear@0 809 if (nodes.empty())
nuclear@0 810 DefaultLogger::get()->error("LWS: Unexpected keyword: \'PivotPosition\'");
nuclear@0 811 else {
nuclear@0 812 c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.x );
nuclear@0 813 SkipSpaces(&c);
nuclear@0 814 c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.y );
nuclear@0 815 SkipSpaces(&c);
nuclear@0 816 c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.z );
nuclear@0 817 // Mark pivotPos as set
nuclear@0 818 nodes.back().isPivotSet = true;
nuclear@0 819 }
nuclear@0 820 }
nuclear@0 821 }
nuclear@0 822
nuclear@0 823 // resolve parenting
nuclear@0 824 for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
nuclear@0 825
nuclear@0 826 // check whether there is another node which calls us a parent
nuclear@0 827 for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) {
nuclear@0 828 if (dit != it && *it == (*dit).parent) {
nuclear@0 829 if ((*dit).parent_resolved) {
nuclear@0 830 // fixme: it's still possible to produce an overflow due to cross references ..
nuclear@0 831 DefaultLogger::get()->error("LWS: Found cross reference in scenegraph");
nuclear@0 832 continue;
nuclear@0 833 }
nuclear@0 834
nuclear@0 835 (*it).children.push_back(&*dit);
nuclear@0 836 (*dit).parent_resolved = &*it;
nuclear@0 837 }
nuclear@0 838 }
nuclear@0 839 }
nuclear@0 840
nuclear@0 841 // find out how many nodes have no parent yet
nuclear@0 842 unsigned int no_parent = 0;
nuclear@0 843 for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
nuclear@0 844 if (!(*it).parent_resolved)
nuclear@0 845 ++ no_parent;
nuclear@0 846 }
nuclear@0 847 if (!no_parent)
nuclear@0 848 throw DeadlyImportError("LWS: Unable to find scene root node");
nuclear@0 849
nuclear@0 850
nuclear@0 851 // Load all subsequent files
nuclear@0 852 batch.LoadAll();
nuclear@0 853
nuclear@0 854 // and build the final output graph by attaching the loaded external
nuclear@0 855 // files to ourselves. first build a master graph
nuclear@0 856 aiScene* master = new aiScene();
nuclear@0 857 aiNode* nd = master->mRootNode = new aiNode();
nuclear@0 858
nuclear@0 859 // allocate storage for cameras&lights
nuclear@0 860 if (num_camera) {
nuclear@0 861 master->mCameras = new aiCamera*[master->mNumCameras = num_camera];
nuclear@0 862 }
nuclear@0 863 aiCamera** cams = master->mCameras;
nuclear@0 864 if (num_light) {
nuclear@0 865 master->mLights = new aiLight*[master->mNumLights = num_light];
nuclear@0 866 }
nuclear@0 867 aiLight** lights = master->mLights;
nuclear@0 868
nuclear@0 869 std::vector<AttachmentInfo> attach;
nuclear@0 870 std::vector<aiNodeAnim*> anims;
nuclear@0 871
nuclear@0 872 nd->mName.Set("<LWSRoot>");
nuclear@0 873 nd->mChildren = new aiNode*[no_parent];
nuclear@0 874 for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) {
nuclear@0 875 if (!(*it).parent_resolved) {
nuclear@0 876 aiNode* ro = nd->mChildren[ nd->mNumChildren++ ] = new aiNode();
nuclear@0 877 ro->mParent = nd;
nuclear@0 878
nuclear@0 879 // ... and build the scene graph. If we encounter object nodes,
nuclear@0 880 // add then to our attachment table.
nuclear@0 881 BuildGraph(ro,*it, attach, batch, cams, lights, anims);
nuclear@0 882 }
nuclear@0 883 }
nuclear@0 884
nuclear@0 885 // create a master animation channel for us
nuclear@0 886 if (anims.size()) {
nuclear@0 887 master->mAnimations = new aiAnimation*[master->mNumAnimations = 1];
nuclear@0 888 aiAnimation* anim = master->mAnimations[0] = new aiAnimation();
nuclear@0 889 anim->mName.Set("LWSMasterAnim");
nuclear@0 890
nuclear@0 891 // LWS uses seconds as time units, but we convert to frames
nuclear@0 892 anim->mTicksPerSecond = fps;
nuclear@0 893 anim->mDuration = last-(first-1); /* fixme ... zero or one-based?*/
nuclear@0 894
nuclear@0 895 anim->mChannels = new aiNodeAnim*[anim->mNumChannels = anims.size()];
nuclear@0 896 std::copy(anims.begin(),anims.end(),anim->mChannels);
nuclear@0 897 }
nuclear@0 898
nuclear@0 899 // convert the master scene to RH
nuclear@0 900 MakeLeftHandedProcess monster_cheat;
nuclear@0 901 monster_cheat.Execute(master);
nuclear@0 902
nuclear@0 903 // .. ccw
nuclear@0 904 FlipWindingOrderProcess flipper;
nuclear@0 905 flipper.Execute(master);
nuclear@0 906
nuclear@0 907 // OK ... finally build the output graph
nuclear@0 908 SceneCombiner::MergeScenes(&pScene,master,attach,
nuclear@0 909 AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? (
nuclear@0 910 AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0));
nuclear@0 911
nuclear@0 912 // Check flags
nuclear@0 913 if (!pScene->mNumMeshes || !pScene->mNumMaterials) {
nuclear@0 914 pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE;
nuclear@0 915
nuclear@0 916 if (pScene->mNumAnimations && !noSkeletonMesh) {
nuclear@0 917 // construct skeleton mesh
nuclear@0 918 SkeletonMeshBuilder builder(pScene);
nuclear@0 919 }
nuclear@0 920 }
nuclear@0 921
nuclear@0 922 }
nuclear@0 923
nuclear@0 924 #endif // !! ASSIMP_BUILD_NO_LWS_IMPORTER