vrshoot

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