vrshoot
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/libs/assimp/LWSLoader.cpp Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,924 @@ 1.4 +/* 1.5 +--------------------------------------------------------------------------- 1.6 +Open Asset Import Library (assimp) 1.7 +--------------------------------------------------------------------------- 1.8 + 1.9 +Copyright (c) 2006-2012, assimp team 1.10 + 1.11 +All rights reserved. 1.12 + 1.13 +Redistribution and use of this software in source and binary forms, 1.14 +with or without modification, are permitted provided that the following 1.15 +conditions are met: 1.16 + 1.17 +* Redistributions of source code must retain the above 1.18 + copyright notice, this list of conditions and the 1.19 + following disclaimer. 1.20 + 1.21 +* Redistributions in binary form must reproduce the above 1.22 + copyright notice, this list of conditions and the 1.23 + following disclaimer in the documentation and/or other 1.24 + materials provided with the distribution. 1.25 + 1.26 +* Neither the name of the assimp team, nor the names of its 1.27 + contributors may be used to endorse or promote products 1.28 + derived from this software without specific prior 1.29 + written permission of the assimp team. 1.30 + 1.31 +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.32 +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.33 +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.34 +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.35 +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.36 +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.37 +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.38 +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.39 +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.40 +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.41 +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.42 +--------------------------------------------------------------------------- 1.43 +*/ 1.44 + 1.45 +/** @file LWSLoader.cpp 1.46 + * @brief Implementation of the LWS importer class 1.47 + */ 1.48 + 1.49 +#include "AssimpPCH.h" 1.50 +#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER 1.51 + 1.52 +#include "LWSLoader.h" 1.53 +#include "ParsingUtils.h" 1.54 +#include "fast_atof.h" 1.55 + 1.56 +#include "SceneCombiner.h" 1.57 +#include "GenericProperty.h" 1.58 +#include "SkeletonMeshBuilder.h" 1.59 +#include "ConvertToLHProcess.h" 1.60 +#include "Importer.h" 1.61 + 1.62 +using namespace Assimp; 1.63 + 1.64 +static const aiImporterDesc desc = { 1.65 + "LightWave Scene Importer", 1.66 + "", 1.67 + "", 1.68 + "http://www.newtek.com/lightwave.html=", 1.69 + aiImporterFlags_SupportTextFlavour, 1.70 + 0, 1.71 + 0, 1.72 + 0, 1.73 + 0, 1.74 + "lws mot" 1.75 +}; 1.76 + 1.77 +// ------------------------------------------------------------------------------------------------ 1.78 +// Recursive parsing of LWS files 1.79 +void LWS::Element::Parse (const char*& buffer) 1.80 +{ 1.81 + for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) { 1.82 + 1.83 + // begin of a new element with children 1.84 + bool sub = false; 1.85 + if (*buffer == '{') { 1.86 + ++buffer; 1.87 + SkipSpaces(&buffer); 1.88 + sub = true; 1.89 + } 1.90 + else if (*buffer == '}') 1.91 + return; 1.92 + 1.93 + children.push_back(Element()); 1.94 + 1.95 + // copy data line - read token per token 1.96 + 1.97 + const char* cur = buffer; 1.98 + while (!IsSpaceOrNewLine(*buffer)) ++buffer; 1.99 + children.back().tokens[0] = std::string(cur,(size_t) (buffer-cur)); 1.100 + SkipSpaces(&buffer); 1.101 + 1.102 + if (children.back().tokens[0] == "Plugin") 1.103 + { 1.104 + DefaultLogger::get()->debug("LWS: Skipping over plugin-specific data"); 1.105 + 1.106 + // strange stuff inside Plugin/Endplugin blocks. Needn't 1.107 + // follow LWS syntax, so we skip over it 1.108 + for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) { 1.109 + if (!::strncmp(buffer,"EndPlugin",9)) { 1.110 + //SkipLine(&buffer); 1.111 + break; 1.112 + } 1.113 + } 1.114 + continue; 1.115 + } 1.116 + 1.117 + cur = buffer; 1.118 + while (!IsLineEnd(*buffer)) ++buffer; 1.119 + children.back().tokens[1] = std::string(cur,(size_t) (buffer-cur)); 1.120 + 1.121 + // parse more elements recursively 1.122 + if (sub) 1.123 + children.back().Parse(buffer); 1.124 + } 1.125 +} 1.126 + 1.127 +// ------------------------------------------------------------------------------------------------ 1.128 +// Constructor to be privately used by Importer 1.129 +LWSImporter::LWSImporter() 1.130 +: noSkeletonMesh() 1.131 +{ 1.132 + // nothing to do here 1.133 +} 1.134 + 1.135 +// ------------------------------------------------------------------------------------------------ 1.136 +// Destructor, private as well 1.137 +LWSImporter::~LWSImporter() 1.138 +{ 1.139 + // nothing to do here 1.140 +} 1.141 + 1.142 +// ------------------------------------------------------------------------------------------------ 1.143 +// Returns whether the class can handle the format of the given file. 1.144 +bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,bool checkSig) const 1.145 +{ 1.146 + const std::string extension = GetExtension(pFile); 1.147 + if (extension == "lws" || extension == "mot") 1.148 + return true; 1.149 + 1.150 + // if check for extension is not enough, check for the magic tokens LWSC and LWMO 1.151 + if (!extension.length() || checkSig) { 1.152 + uint32_t tokens[2]; 1.153 + tokens[0] = AI_MAKE_MAGIC("LWSC"); 1.154 + tokens[1] = AI_MAKE_MAGIC("LWMO"); 1.155 + return CheckMagicToken(pIOHandler,pFile,tokens,2); 1.156 + } 1.157 + return false; 1.158 +} 1.159 + 1.160 +// ------------------------------------------------------------------------------------------------ 1.161 +// Get list of file extensions 1.162 +const aiImporterDesc* LWSImporter::GetInfo () const 1.163 +{ 1.164 + return &desc; 1.165 +} 1.166 + 1.167 +// ------------------------------------------------------------------------------------------------ 1.168 +// Setup configuration properties 1.169 +void LWSImporter::SetupProperties(const Importer* pImp) 1.170 +{ 1.171 + // AI_CONFIG_FAVOUR_SPEED 1.172 + configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0)); 1.173 + 1.174 + // AI_CONFIG_IMPORT_LWS_ANIM_START 1.175 + first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START, 1.176 + 150392 /* magic hack */); 1.177 + 1.178 + // AI_CONFIG_IMPORT_LWS_ANIM_END 1.179 + last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END, 1.180 + 150392 /* magic hack */); 1.181 + 1.182 + if (last < first) { 1.183 + std::swap(last,first); 1.184 + } 1.185 + 1.186 + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; 1.187 +} 1.188 + 1.189 +// ------------------------------------------------------------------------------------------------ 1.190 +// Read an envelope description 1.191 +void LWSImporter::ReadEnvelope(const LWS::Element& dad, LWO::Envelope& fill ) 1.192 +{ 1.193 + if (dad.children.empty()) { 1.194 + DefaultLogger::get()->error("LWS: Envelope descriptions must not be empty"); 1.195 + return; 1.196 + } 1.197 + 1.198 + // reserve enough storage 1.199 + std::list< LWS::Element >::const_iterator it = dad.children.begin();; 1.200 + fill.keys.reserve(strtoul10(it->tokens[1].c_str())); 1.201 + 1.202 + for (++it; it != dad.children.end(); ++it) { 1.203 + const char* c = (*it).tokens[1].c_str(); 1.204 + 1.205 + if ((*it).tokens[0] == "Key") { 1.206 + fill.keys.push_back(LWO::Key()); 1.207 + LWO::Key& key = fill.keys.back(); 1.208 + 1.209 + float f; 1.210 + SkipSpaces(&c); 1.211 + c = fast_atoreal_move<float>(c,key.value); 1.212 + SkipSpaces(&c); 1.213 + c = fast_atoreal_move<float>(c,f); 1.214 + 1.215 + key.time = f; 1.216 + 1.217 + unsigned int span = strtoul10(c,&c), num = 0; 1.218 + switch (span) { 1.219 + 1.220 + case 0: 1.221 + key.inter = LWO::IT_TCB; 1.222 + num = 5; 1.223 + break; 1.224 + case 1: 1.225 + case 2: 1.226 + key.inter = LWO::IT_HERM; 1.227 + num = 5; 1.228 + break; 1.229 + case 3: 1.230 + key.inter = LWO::IT_LINE; 1.231 + num = 0; 1.232 + break; 1.233 + case 4: 1.234 + key.inter = LWO::IT_STEP; 1.235 + num = 0; 1.236 + break; 1.237 + case 5: 1.238 + key.inter = LWO::IT_BEZ2; 1.239 + num = 4; 1.240 + break; 1.241 + default: 1.242 + DefaultLogger::get()->error("LWS: Unknown span type"); 1.243 + } 1.244 + for (unsigned int i = 0; i < num;++i) { 1.245 + SkipSpaces(&c); 1.246 + c = fast_atoreal_move<float>(c,key.params[i]); 1.247 + } 1.248 + } 1.249 + else if ((*it).tokens[0] == "Behaviors") { 1.250 + SkipSpaces(&c); 1.251 + fill.pre = (LWO::PrePostBehaviour) strtoul10(c,&c); 1.252 + SkipSpaces(&c); 1.253 + fill.post = (LWO::PrePostBehaviour) strtoul10(c,&c); 1.254 + } 1.255 + } 1.256 +} 1.257 + 1.258 +// ------------------------------------------------------------------------------------------------ 1.259 +// Read animation channels in the old LightWave animation format 1.260 +void LWSImporter::ReadEnvelope_Old( 1.261 + std::list< LWS::Element >::const_iterator& it, 1.262 + const std::list< LWS::Element >::const_iterator& end, 1.263 + LWS::NodeDesc& nodes, 1.264 + unsigned int /*version*/) 1.265 +{ 1.266 + unsigned int num,sub_num; 1.267 + if (++it == end)goto unexpected_end; 1.268 + 1.269 + num = strtoul10((*it).tokens[0].c_str()); 1.270 + for (unsigned int i = 0; i < num; ++i) { 1.271 + 1.272 + nodes.channels.push_back(LWO::Envelope()); 1.273 + LWO::Envelope& envl = nodes.channels.back(); 1.274 + 1.275 + envl.index = i; 1.276 + envl.type = (LWO::EnvelopeType)(i+1); 1.277 + 1.278 + if (++it == end)goto unexpected_end; 1.279 + sub_num = strtoul10((*it).tokens[0].c_str()); 1.280 + 1.281 + for (unsigned int n = 0; n < sub_num;++n) { 1.282 + 1.283 + if (++it == end)goto unexpected_end; 1.284 + 1.285 + // parse value and time, skip the rest for the moment. 1.286 + LWO::Key key; 1.287 + const char* c = fast_atoreal_move<float>((*it).tokens[0].c_str(),key.value); 1.288 + SkipSpaces(&c); 1.289 + float f; 1.290 + fast_atoreal_move<float>((*it).tokens[0].c_str(),f); 1.291 + key.time = f; 1.292 + 1.293 + envl.keys.push_back(key); 1.294 + } 1.295 + } 1.296 + return; 1.297 + 1.298 +unexpected_end: 1.299 + DefaultLogger::get()->error("LWS: Encountered unexpected end of file while parsing object motion"); 1.300 +} 1.301 + 1.302 +// ------------------------------------------------------------------------------------------------ 1.303 +// Setup a nice name for a node 1.304 +void LWSImporter::SetupNodeName(aiNode* nd, LWS::NodeDesc& src) 1.305 +{ 1.306 + const unsigned int combined = src.number | ((unsigned int)src.type) << 28u; 1.307 + 1.308 + // the name depends on the type. We break LWS's strange naming convention 1.309 + // and return human-readable, but still machine-parsable and unique, strings. 1.310 + if (src.type == LWS::NodeDesc::OBJECT) { 1.311 + 1.312 + if (src.path.length()) { 1.313 + std::string::size_type s = src.path.find_last_of("\\/"); 1.314 + if (s == std::string::npos) 1.315 + s = 0; 1.316 + else ++s; 1.317 + std::string::size_type t = src.path.substr(s).find_last_of("."); 1.318 + 1.319 + nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.path.substr(s).substr(0,t).c_str(),combined); 1.320 + return; 1.321 + } 1.322 + } 1.323 + nd->mName.length = ::sprintf(nd->mName.data,"%s_(%08X)",src.name,combined); 1.324 +} 1.325 + 1.326 +// ------------------------------------------------------------------------------------------------ 1.327 +// Recursively build the scenegraph 1.328 +void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<AttachmentInfo>& attach, 1.329 + BatchLoader& batch, 1.330 + aiCamera**& camOut, 1.331 + aiLight**& lightOut, 1.332 + std::vector<aiNodeAnim*>& animOut) 1.333 +{ 1.334 + // Setup a very cryptic name for the node, we want the user to be happy 1.335 + SetupNodeName(nd,src); 1.336 + aiNode* ndAnim = nd; 1.337 + 1.338 + // If the node is an object 1.339 + if (src.type == LWS::NodeDesc::OBJECT) { 1.340 + 1.341 + // If the object is from an external file, get it 1.342 + aiScene* obj = NULL; 1.343 + if (src.path.length() ) { 1.344 + obj = batch.GetImport(src.id); 1.345 + if (!obj) { 1.346 + DefaultLogger::get()->error("LWS: Failed to read external file " + src.path); 1.347 + } 1.348 + else { 1.349 + if (obj->mRootNode->mNumChildren == 1) { 1.350 + 1.351 + //If the pivot is not set for this layer, get it from the external object 1.352 + if (!src.isPivotSet) { 1.353 + src.pivotPos.x = +obj->mRootNode->mTransformation.a4; 1.354 + src.pivotPos.y = +obj->mRootNode->mTransformation.b4; 1.355 + src.pivotPos.z = -obj->mRootNode->mTransformation.c4; //The sign is the RH to LH back conversion 1.356 + } 1.357 + 1.358 + //Remove first node from obj (the old pivot), reset transform of second node (the mesh node) 1.359 + aiNode* newRootNode = obj->mRootNode->mChildren[0]; 1.360 + obj->mRootNode->mChildren[0] = NULL; 1.361 + delete obj->mRootNode; 1.362 + 1.363 + obj->mRootNode = newRootNode; 1.364 + obj->mRootNode->mTransformation.a4 = 0.0; 1.365 + obj->mRootNode->mTransformation.b4 = 0.0; 1.366 + obj->mRootNode->mTransformation.c4 = 0.0; 1.367 + } 1.368 + } 1.369 + } 1.370 + 1.371 + //Setup the pivot node (also the animation node), the one we received 1.372 + nd->mName = std::string("Pivot:") + nd->mName.data; 1.373 + ndAnim = nd; 1.374 + 1.375 + //Add the attachment node to it 1.376 + nd->mNumChildren = 1; 1.377 + nd->mChildren = new aiNode*[1]; 1.378 + nd->mChildren[0] = new aiNode(); 1.379 + nd->mChildren[0]->mParent = nd; 1.380 + nd->mChildren[0]->mTransformation.a4 = -src.pivotPos.x; 1.381 + nd->mChildren[0]->mTransformation.b4 = -src.pivotPos.y; 1.382 + nd->mChildren[0]->mTransformation.c4 = -src.pivotPos.z; 1.383 + SetupNodeName(nd->mChildren[0], src); 1.384 + 1.385 + //Update the attachment node 1.386 + nd = nd->mChildren[0]; 1.387 + 1.388 + //Push attachment, if the object came from an external file 1.389 + if (obj) { 1.390 + attach.push_back(AttachmentInfo(obj,nd)); 1.391 + } 1.392 + } 1.393 + 1.394 + // If object is a light source - setup a corresponding ai structure 1.395 + else if (src.type == LWS::NodeDesc::LIGHT) { 1.396 + aiLight* lit = *lightOut++ = new aiLight(); 1.397 + 1.398 + // compute final light color 1.399 + lit->mColorDiffuse = lit->mColorSpecular = src.lightColor*src.lightIntensity; 1.400 + 1.401 + // name to attach light to node -> unique due to LWs indexing system 1.402 + lit->mName = nd->mName; 1.403 + 1.404 + // detemine light type and setup additional members 1.405 + if (src.lightType == 2) { /* spot light */ 1.406 + 1.407 + lit->mType = aiLightSource_SPOT; 1.408 + lit->mAngleInnerCone = (float)AI_DEG_TO_RAD( src.lightConeAngle ); 1.409 + lit->mAngleOuterCone = lit->mAngleInnerCone+(float)AI_DEG_TO_RAD( src.lightEdgeAngle ); 1.410 + 1.411 + } 1.412 + else if (src.lightType == 1) { /* directional light source */ 1.413 + lit->mType = aiLightSource_DIRECTIONAL; 1.414 + } 1.415 + else lit->mType = aiLightSource_POINT; 1.416 + 1.417 + // fixme: no proper handling of light falloffs yet 1.418 + if (src.lightFalloffType == 1) 1.419 + lit->mAttenuationConstant = 1.f; 1.420 + else if (src.lightFalloffType == 1) 1.421 + lit->mAttenuationLinear = 1.f; 1.422 + else 1.423 + lit->mAttenuationQuadratic = 1.f; 1.424 + } 1.425 + 1.426 + // If object is a camera - setup a corresponding ai structure 1.427 + else if (src.type == LWS::NodeDesc::CAMERA) { 1.428 + aiCamera* cam = *camOut++ = new aiCamera(); 1.429 + 1.430 + // name to attach cam to node -> unique due to LWs indexing system 1.431 + cam->mName = nd->mName; 1.432 + } 1.433 + 1.434 + // Get the node transformation from the LWO key 1.435 + LWO::AnimResolver resolver(src.channels,fps); 1.436 + resolver.ExtractBindPose(ndAnim->mTransformation); 1.437 + 1.438 + // .. and construct animation channels 1.439 + aiNodeAnim* anim = NULL; 1.440 + 1.441 + if (first != last) { 1.442 + resolver.SetAnimationRange(first,last); 1.443 + resolver.ExtractAnimChannel(&anim,AI_LWO_ANIM_FLAG_SAMPLE_ANIMS|AI_LWO_ANIM_FLAG_START_AT_ZERO); 1.444 + if (anim) { 1.445 + anim->mNodeName = ndAnim->mName; 1.446 + animOut.push_back(anim); 1.447 + } 1.448 + } 1.449 + 1.450 + // Add children 1.451 + if (src.children.size()) { 1.452 + nd->mChildren = new aiNode*[src.children.size()]; 1.453 + for (std::list<LWS::NodeDesc*>::iterator it = src.children.begin(); it != src.children.end(); ++it) { 1.454 + aiNode* ndd = nd->mChildren[nd->mNumChildren++] = new aiNode(); 1.455 + ndd->mParent = nd; 1.456 + 1.457 + BuildGraph(ndd,**it,attach,batch,camOut,lightOut,animOut); 1.458 + } 1.459 + } 1.460 +} 1.461 + 1.462 +// ------------------------------------------------------------------------------------------------ 1.463 +// Determine the exact location of a LWO file 1.464 +std::string LWSImporter::FindLWOFile(const std::string& in) 1.465 +{ 1.466 + // insert missing directory seperator if necessary 1.467 + std::string tmp; 1.468 + if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/') 1.469 + { 1.470 + tmp = in[0] + (":\\" + in.substr(2)); 1.471 + } 1.472 + else tmp = in; 1.473 + 1.474 + if (io->Exists(tmp)) { 1.475 + return in; 1.476 + } 1.477 + 1.478 + // file is not accessible for us ... maybe it's packed by 1.479 + // LightWave's 'Package Scene' command? 1.480 + 1.481 + // Relevant for us are the following two directories: 1.482 + // <folder>\Objects\<hh>\<*>.lwo 1.483 + // <folder>\Scenes\<hh>\<*>.lws 1.484 + // where <hh> is optional. 1.485 + 1.486 + std::string test = ".." + (io->getOsSeparator() + tmp); 1.487 + if (io->Exists(test)) { 1.488 + return test; 1.489 + } 1.490 + 1.491 + test = ".." + (io->getOsSeparator() + test); 1.492 + if (io->Exists(test)) { 1.493 + return test; 1.494 + } 1.495 + 1.496 + 1.497 + // return original path, maybe the IOsystem knows better 1.498 + return tmp; 1.499 +} 1.500 + 1.501 +// ------------------------------------------------------------------------------------------------ 1.502 +// Read file into given scene data structure 1.503 +void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene, 1.504 + IOSystem* pIOHandler) 1.505 +{ 1.506 + io = pIOHandler; 1.507 + boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); 1.508 + 1.509 + // Check whether we can read from the file 1.510 + if( file.get() == NULL) { 1.511 + throw DeadlyImportError( "Failed to open LWS file " + pFile + "."); 1.512 + } 1.513 + 1.514 + // Allocate storage and copy the contents of the file to a memory buffer 1.515 + std::vector< char > mBuffer; 1.516 + TextFileToBuffer(file.get(),mBuffer); 1.517 + 1.518 + // Parse the file structure 1.519 + LWS::Element root; const char* dummy = &mBuffer[0]; 1.520 + root.Parse(dummy); 1.521 + 1.522 + // Construct a Batchimporter to read more files recursively 1.523 + BatchLoader batch(pIOHandler); 1.524 +// batch.SetBasePath(pFile); 1.525 + 1.526 + // Construct an array to receive the flat output graph 1.527 + std::list<LWS::NodeDesc> nodes; 1.528 + 1.529 + unsigned int cur_light = 0, cur_camera = 0, cur_object = 0; 1.530 + unsigned int num_light = 0, num_camera = 0, num_object = 0; 1.531 + 1.532 + // check magic identifier, 'LWSC' 1.533 + bool motion_file = false; 1.534 + std::list< LWS::Element >::const_iterator it = root.children.begin(); 1.535 + 1.536 + if ((*it).tokens[0] == "LWMO") 1.537 + motion_file = true; 1.538 + 1.539 + if ((*it).tokens[0] != "LWSC" && !motion_file) 1.540 + throw DeadlyImportError("LWS: Not a LightWave scene, magic tag LWSC not found"); 1.541 + 1.542 + // get file format version and print to log 1.543 + ++it; 1.544 + unsigned int version = strtoul10((*it).tokens[0].c_str()); 1.545 + DefaultLogger::get()->info("LWS file format version is " + (*it).tokens[0]); 1.546 + first = 0.; 1.547 + last = 60.; 1.548 + fps = 25.; /* seems to be a good default frame rate */ 1.549 + 1.550 + // Now read all elements in a very straghtforward manner 1.551 + for (; it != root.children.end(); ++it) { 1.552 + const char* c = (*it).tokens[1].c_str(); 1.553 + 1.554 + // 'FirstFrame': begin of animation slice 1.555 + if ((*it).tokens[0] == "FirstFrame") { 1.556 + if (150392. != first /* see SetupProperties() */) 1.557 + first = strtoul10(c,&c)-1.; /* we're zero-based */ 1.558 + } 1.559 + 1.560 + // 'LastFrame': end of animation slice 1.561 + else if ((*it).tokens[0] == "LastFrame") { 1.562 + if (150392. != last /* see SetupProperties() */) 1.563 + last = strtoul10(c,&c)-1.; /* we're zero-based */ 1.564 + } 1.565 + 1.566 + // 'FramesPerSecond': frames per second 1.567 + else if ((*it).tokens[0] == "FramesPerSecond") { 1.568 + fps = strtoul10(c,&c); 1.569 + } 1.570 + 1.571 + // 'LoadObjectLayer': load a layer of a specific LWO file 1.572 + else if ((*it).tokens[0] == "LoadObjectLayer") { 1.573 + 1.574 + // get layer index 1.575 + const int layer = strtoul10(c,&c); 1.576 + 1.577 + // setup the layer to be loaded 1.578 + BatchLoader::PropertyMap props; 1.579 + SetGenericProperty(props.ints,AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,layer); 1.580 + 1.581 + // add node to list 1.582 + LWS::NodeDesc d; 1.583 + d.type = LWS::NodeDesc::OBJECT; 1.584 + if (version >= 4) { // handle LWSC 4 explicit ID 1.585 + SkipSpaces(&c); 1.586 + d.number = strtoul16(c,&c) & AI_LWS_MASK; 1.587 + } 1.588 + else d.number = cur_object++; 1.589 + 1.590 + // and add the file to the import list 1.591 + SkipSpaces(&c); 1.592 + std::string path = FindLWOFile( c ); 1.593 + d.path = path; 1.594 + d.id = batch.AddLoadRequest(path,0,&props); 1.595 + 1.596 + nodes.push_back(d); 1.597 + num_object++; 1.598 + } 1.599 + // 'LoadObject': load a LWO file into the scenegraph 1.600 + else if ((*it).tokens[0] == "LoadObject") { 1.601 + 1.602 + // add node to list 1.603 + LWS::NodeDesc d; 1.604 + d.type = LWS::NodeDesc::OBJECT; 1.605 + 1.606 + if (version >= 4) { // handle LWSC 4 explicit ID 1.607 + d.number = strtoul16(c,&c) & AI_LWS_MASK; 1.608 + SkipSpaces(&c); 1.609 + } 1.610 + else d.number = cur_object++; 1.611 + std::string path = FindLWOFile( c ); 1.612 + d.id = batch.AddLoadRequest(path,0,NULL); 1.613 + 1.614 + d.path = path; 1.615 + nodes.push_back(d); 1.616 + num_object++; 1.617 + } 1.618 + // 'AddNullObject': add a dummy node to the hierarchy 1.619 + else if ((*it).tokens[0] == "AddNullObject") { 1.620 + 1.621 + // add node to list 1.622 + LWS::NodeDesc d; 1.623 + d.type = LWS::NodeDesc::OBJECT; 1.624 + if (version >= 4) { // handle LWSC 4 explicit ID 1.625 + d.number = strtoul16(c,&c) & AI_LWS_MASK; 1.626 + SkipSpaces(&c); 1.627 + } 1.628 + else d.number = cur_object++; 1.629 + d.name = c; 1.630 + nodes.push_back(d); 1.631 + 1.632 + num_object++; 1.633 + } 1.634 + // 'NumChannels': Number of envelope channels assigned to last layer 1.635 + else if ((*it).tokens[0] == "NumChannels") { 1.636 + // ignore for now 1.637 + } 1.638 + // 'Channel': preceedes any envelope description 1.639 + else if ((*it).tokens[0] == "Channel") { 1.640 + if (nodes.empty()) { 1.641 + if (motion_file) { 1.642 + 1.643 + // LightWave motion file. Add dummy node 1.644 + LWS::NodeDesc d; 1.645 + d.type = LWS::NodeDesc::OBJECT; 1.646 + d.name = c; 1.647 + d.number = cur_object++; 1.648 + nodes.push_back(d); 1.649 + } 1.650 + else DefaultLogger::get()->error("LWS: Unexpected keyword: \'Channel\'"); 1.651 + } 1.652 + 1.653 + // important: index of channel 1.654 + nodes.back().channels.push_back(LWO::Envelope()); 1.655 + LWO::Envelope& env = nodes.back().channels.back(); 1.656 + 1.657 + env.index = strtoul10(c); 1.658 + 1.659 + // currently we can just interpret the standard channels 0...9 1.660 + // (hack) assume that index-i yields the binary channel type from LWO 1.661 + env.type = (LWO::EnvelopeType)(env.index+1); 1.662 + 1.663 + } 1.664 + // 'Envelope': a single animation channel 1.665 + else if ((*it).tokens[0] == "Envelope") { 1.666 + if (nodes.empty() || nodes.back().channels.empty()) 1.667 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'Envelope\'"); 1.668 + else { 1.669 + ReadEnvelope((*it),nodes.back().channels.back()); 1.670 + } 1.671 + } 1.672 + // 'ObjectMotion': animation information for older lightwave formats 1.673 + else if (version < 3 && ((*it).tokens[0] == "ObjectMotion" || 1.674 + (*it).tokens[0] == "CameraMotion" || 1.675 + (*it).tokens[0] == "LightMotion")) { 1.676 + 1.677 + if (nodes.empty()) 1.678 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'<Light|Object|Camera>Motion\'"); 1.679 + else { 1.680 + ReadEnvelope_Old(it,root.children.end(),nodes.back(),version); 1.681 + } 1.682 + } 1.683 + // 'Pre/PostBehavior': pre/post animation behaviour for LWSC 2 1.684 + else if (version == 2 && (*it).tokens[0] == "Pre/PostBehavior") { 1.685 + if (nodes.empty()) 1.686 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'Pre/PostBehavior'"); 1.687 + else { 1.688 + for (std::list<LWO::Envelope>::iterator it = nodes.back().channels.begin(); it != nodes.back().channels.end(); ++it) { 1.689 + // two ints per envelope 1.690 + LWO::Envelope& env = *it; 1.691 + env.pre = (LWO::PrePostBehaviour) strtoul10(c,&c); SkipSpaces(&c); 1.692 + env.post = (LWO::PrePostBehaviour) strtoul10(c,&c); SkipSpaces(&c); 1.693 + } 1.694 + } 1.695 + } 1.696 + // 'ParentItem': specifies the parent of the current element 1.697 + else if ((*it).tokens[0] == "ParentItem") { 1.698 + if (nodes.empty()) 1.699 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentItem\'"); 1.700 + 1.701 + else nodes.back().parent = strtoul16(c,&c); 1.702 + } 1.703 + // 'ParentObject': deprecated one for older formats 1.704 + else if (version < 3 && (*it).tokens[0] == "ParentObject") { 1.705 + if (nodes.empty()) 1.706 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentObject\'"); 1.707 + 1.708 + else { 1.709 + nodes.back().parent = strtoul10(c,&c) | (1u << 28u); 1.710 + } 1.711 + } 1.712 + // 'AddCamera': add a camera to the scenegraph 1.713 + else if ((*it).tokens[0] == "AddCamera") { 1.714 + 1.715 + // add node to list 1.716 + LWS::NodeDesc d; 1.717 + d.type = LWS::NodeDesc::CAMERA; 1.718 + 1.719 + if (version >= 4) { // handle LWSC 4 explicit ID 1.720 + d.number = strtoul16(c,&c) & AI_LWS_MASK; 1.721 + } 1.722 + else d.number = cur_camera++; 1.723 + nodes.push_back(d); 1.724 + 1.725 + num_camera++; 1.726 + } 1.727 + // 'CameraName': set name of currently active camera 1.728 + else if ((*it).tokens[0] == "CameraName") { 1.729 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA) 1.730 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'CameraName\'"); 1.731 + 1.732 + else nodes.back().name = c; 1.733 + } 1.734 + // 'AddLight': add a light to the scenegraph 1.735 + else if ((*it).tokens[0] == "AddLight") { 1.736 + 1.737 + // add node to list 1.738 + LWS::NodeDesc d; 1.739 + d.type = LWS::NodeDesc::LIGHT; 1.740 + 1.741 + if (version >= 4) { // handle LWSC 4 explicit ID 1.742 + d.number = strtoul16(c,&c) & AI_LWS_MASK; 1.743 + } 1.744 + else d.number = cur_light++; 1.745 + nodes.push_back(d); 1.746 + 1.747 + num_light++; 1.748 + } 1.749 + // 'LightName': set name of currently active light 1.750 + else if ((*it).tokens[0] == "LightName") { 1.751 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.752 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightName\'"); 1.753 + 1.754 + else nodes.back().name = c; 1.755 + } 1.756 + // 'LightIntensity': set intensity of currently active light 1.757 + else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity" ) { 1.758 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.759 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightIntensity\'"); 1.760 + 1.761 + else fast_atoreal_move<float>(c, nodes.back().lightIntensity ); 1.762 + 1.763 + } 1.764 + // 'LightType': set type of currently active light 1.765 + else if ((*it).tokens[0] == "LightType") { 1.766 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.767 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightType\'"); 1.768 + 1.769 + else nodes.back().lightType = strtoul10(c); 1.770 + 1.771 + } 1.772 + // 'LightFalloffType': set falloff type of currently active light 1.773 + else if ((*it).tokens[0] == "LightFalloffType") { 1.774 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.775 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightFalloffType\'"); 1.776 + 1.777 + else nodes.back().lightFalloffType = strtoul10(c); 1.778 + 1.779 + } 1.780 + // 'LightConeAngle': set cone angle of currently active light 1.781 + else if ((*it).tokens[0] == "LightConeAngle") { 1.782 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.783 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightConeAngle\'"); 1.784 + 1.785 + else nodes.back().lightConeAngle = fast_atof(c); 1.786 + 1.787 + } 1.788 + // 'LightEdgeAngle': set area where we're smoothing from min to max intensity 1.789 + else if ((*it).tokens[0] == "LightEdgeAngle") { 1.790 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.791 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightEdgeAngle\'"); 1.792 + 1.793 + else nodes.back().lightEdgeAngle = fast_atof(c); 1.794 + 1.795 + } 1.796 + // 'LightColor': set color of currently active light 1.797 + else if ((*it).tokens[0] == "LightColor") { 1.798 + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) 1.799 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightColor\'"); 1.800 + 1.801 + else { 1.802 + c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.r ); 1.803 + SkipSpaces(&c); 1.804 + c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.g ); 1.805 + SkipSpaces(&c); 1.806 + c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.b ); 1.807 + } 1.808 + } 1.809 + 1.810 + // 'PivotPosition': position of local transformation origin 1.811 + else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") { 1.812 + if (nodes.empty()) 1.813 + DefaultLogger::get()->error("LWS: Unexpected keyword: \'PivotPosition\'"); 1.814 + else { 1.815 + c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.x ); 1.816 + SkipSpaces(&c); 1.817 + c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.y ); 1.818 + SkipSpaces(&c); 1.819 + c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.z ); 1.820 + // Mark pivotPos as set 1.821 + nodes.back().isPivotSet = true; 1.822 + } 1.823 + } 1.824 + } 1.825 + 1.826 + // resolve parenting 1.827 + for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) { 1.828 + 1.829 + // check whether there is another node which calls us a parent 1.830 + for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) { 1.831 + if (dit != it && *it == (*dit).parent) { 1.832 + if ((*dit).parent_resolved) { 1.833 + // fixme: it's still possible to produce an overflow due to cross references .. 1.834 + DefaultLogger::get()->error("LWS: Found cross reference in scenegraph"); 1.835 + continue; 1.836 + } 1.837 + 1.838 + (*it).children.push_back(&*dit); 1.839 + (*dit).parent_resolved = &*it; 1.840 + } 1.841 + } 1.842 + } 1.843 + 1.844 + // find out how many nodes have no parent yet 1.845 + unsigned int no_parent = 0; 1.846 + for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) { 1.847 + if (!(*it).parent_resolved) 1.848 + ++ no_parent; 1.849 + } 1.850 + if (!no_parent) 1.851 + throw DeadlyImportError("LWS: Unable to find scene root node"); 1.852 + 1.853 + 1.854 + // Load all subsequent files 1.855 + batch.LoadAll(); 1.856 + 1.857 + // and build the final output graph by attaching the loaded external 1.858 + // files to ourselves. first build a master graph 1.859 + aiScene* master = new aiScene(); 1.860 + aiNode* nd = master->mRootNode = new aiNode(); 1.861 + 1.862 + // allocate storage for cameras&lights 1.863 + if (num_camera) { 1.864 + master->mCameras = new aiCamera*[master->mNumCameras = num_camera]; 1.865 + } 1.866 + aiCamera** cams = master->mCameras; 1.867 + if (num_light) { 1.868 + master->mLights = new aiLight*[master->mNumLights = num_light]; 1.869 + } 1.870 + aiLight** lights = master->mLights; 1.871 + 1.872 + std::vector<AttachmentInfo> attach; 1.873 + std::vector<aiNodeAnim*> anims; 1.874 + 1.875 + nd->mName.Set("<LWSRoot>"); 1.876 + nd->mChildren = new aiNode*[no_parent]; 1.877 + for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) { 1.878 + if (!(*it).parent_resolved) { 1.879 + aiNode* ro = nd->mChildren[ nd->mNumChildren++ ] = new aiNode(); 1.880 + ro->mParent = nd; 1.881 + 1.882 + // ... and build the scene graph. If we encounter object nodes, 1.883 + // add then to our attachment table. 1.884 + BuildGraph(ro,*it, attach, batch, cams, lights, anims); 1.885 + } 1.886 + } 1.887 + 1.888 + // create a master animation channel for us 1.889 + if (anims.size()) { 1.890 + master->mAnimations = new aiAnimation*[master->mNumAnimations = 1]; 1.891 + aiAnimation* anim = master->mAnimations[0] = new aiAnimation(); 1.892 + anim->mName.Set("LWSMasterAnim"); 1.893 + 1.894 + // LWS uses seconds as time units, but we convert to frames 1.895 + anim->mTicksPerSecond = fps; 1.896 + anim->mDuration = last-(first-1); /* fixme ... zero or one-based?*/ 1.897 + 1.898 + anim->mChannels = new aiNodeAnim*[anim->mNumChannels = anims.size()]; 1.899 + std::copy(anims.begin(),anims.end(),anim->mChannels); 1.900 + } 1.901 + 1.902 + // convert the master scene to RH 1.903 + MakeLeftHandedProcess monster_cheat; 1.904 + monster_cheat.Execute(master); 1.905 + 1.906 + // .. ccw 1.907 + FlipWindingOrderProcess flipper; 1.908 + flipper.Execute(master); 1.909 + 1.910 + // OK ... finally build the output graph 1.911 + SceneCombiner::MergeScenes(&pScene,master,attach, 1.912 + AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? ( 1.913 + AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0)); 1.914 + 1.915 + // Check flags 1.916 + if (!pScene->mNumMeshes || !pScene->mNumMaterials) { 1.917 + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; 1.918 + 1.919 + if (pScene->mNumAnimations && !noSkeletonMesh) { 1.920 + // construct skeleton mesh 1.921 + SkeletonMeshBuilder builder(pScene); 1.922 + } 1.923 + } 1.924 + 1.925 +} 1.926 + 1.927 +#endif // !! ASSIMP_BUILD_NO_LWS_IMPORTER