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