diff options
| author | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
|---|---|---|
| committer | Marijn Tamis <[email protected]> | 2018-05-03 18:22:48 +0200 |
| commit | ca32c59a58d37c1822e185a2d5f3d0d3e8943593 (patch) | |
| tree | b06b9eec03f34344ef8fc31aa147b2714d3962ee /NvCloth/samples/external/assimp-4.1.0/code/LWSLoader.cpp | |
| parent | Forced rename of platform folders in cmake dir. Git didn't pick this up before. (diff) | |
| download | nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.tar.xz nvcloth-ca32c59a58d37c1822e185a2d5f3d0d3e8943593.zip | |
NvCloth 1.1.4 Release. (24070740)
Diffstat (limited to 'NvCloth/samples/external/assimp-4.1.0/code/LWSLoader.cpp')
| -rw-r--r-- | NvCloth/samples/external/assimp-4.1.0/code/LWSLoader.cpp | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/NvCloth/samples/external/assimp-4.1.0/code/LWSLoader.cpp b/NvCloth/samples/external/assimp-4.1.0/code/LWSLoader.cpp new file mode 100644 index 0000000..2017cdb --- /dev/null +++ b/NvCloth/samples/external/assimp-4.1.0/code/LWSLoader.cpp @@ -0,0 +1,935 @@ +/* +--------------------------------------------------------------------------- +Open Asset Import Library (assimp) +--------------------------------------------------------------------------- + +Copyright (c) 2006-2017, assimp team + + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the + following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the + following disclaimer in the documentation and/or other + materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior + written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------- +*/ + +/** @file LWSLoader.cpp + * @brief Implementation of the LWS importer class + */ + + +#ifndef ASSIMP_BUILD_NO_LWS_IMPORTER + +#include "LWSLoader.h" +#include "ParsingUtils.h" +#include "fast_atof.h" + +#include <assimp/SceneCombiner.h> +#include "GenericProperty.h" +#include "SkeletonMeshBuilder.h" +#include "ConvertToLHProcess.h" +#include "Importer.h" +#include <assimp/DefaultLogger.hpp> +#include <assimp/scene.h> +#include <assimp/IOSystem.hpp> +#include <assimp/importerdesc.h> +#include <memory> + +using namespace Assimp; + +static const aiImporterDesc desc = { + "LightWave Scene Importer", + "", + "", + "http://www.newtek.com/lightwave.html=", + aiImporterFlags_SupportTextFlavour, + 0, + 0, + 0, + 0, + "lws mot" +}; + +// ------------------------------------------------------------------------------------------------ +// Recursive parsing of LWS files +void LWS::Element::Parse (const char*& buffer) +{ + for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) { + + // begin of a new element with children + bool sub = false; + if (*buffer == '{') { + ++buffer; + SkipSpaces(&buffer); + sub = true; + } + else if (*buffer == '}') + return; + + children.push_back(Element()); + + // copy data line - read token per token + + const char* cur = buffer; + while (!IsSpaceOrNewLine(*buffer)) ++buffer; + children.back().tokens[0] = std::string(cur,(size_t) (buffer-cur)); + SkipSpaces(&buffer); + + if (children.back().tokens[0] == "Plugin") + { + DefaultLogger::get()->debug("LWS: Skipping over plugin-specific data"); + + // strange stuff inside Plugin/Endplugin blocks. Needn't + // follow LWS syntax, so we skip over it + for (;SkipSpacesAndLineEnd(&buffer);SkipLine(&buffer)) { + if (!::strncmp(buffer,"EndPlugin",9)) { + //SkipLine(&buffer); + break; + } + } + continue; + } + + cur = buffer; + while (!IsLineEnd(*buffer)) ++buffer; + children.back().tokens[1] = std::string(cur,(size_t) (buffer-cur)); + + // parse more elements recursively + if (sub) + children.back().Parse(buffer); + } +} + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +LWSImporter::LWSImporter() + : configSpeedFlag(), + io(), + first(), + last(), + fps(), + noSkeletonMesh() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +LWSImporter::~LWSImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool LWSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler,bool checkSig) const +{ + const std::string extension = GetExtension(pFile); + if (extension == "lws" || extension == "mot") + return true; + + // if check for extension is not enough, check for the magic tokens LWSC and LWMO + if (!extension.length() || checkSig) { + uint32_t tokens[2]; + tokens[0] = AI_MAKE_MAGIC("LWSC"); + tokens[1] = AI_MAKE_MAGIC("LWMO"); + return CheckMagicToken(pIOHandler,pFile,tokens,2); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +// Get list of file extensions +const aiImporterDesc* LWSImporter::GetInfo () const +{ + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Setup configuration properties +void LWSImporter::SetupProperties(const Importer* pImp) +{ + // AI_CONFIG_FAVOUR_SPEED + configSpeedFlag = (0 != pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0)); + + // AI_CONFIG_IMPORT_LWS_ANIM_START + first = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_START, + 150392 /* magic hack */); + + // AI_CONFIG_IMPORT_LWS_ANIM_END + last = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWS_ANIM_END, + 150392 /* magic hack */); + + if (last < first) { + std::swap(last,first); + } + + noSkeletonMesh = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_NO_SKELETON_MESHES,0) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Read an envelope description +void LWSImporter::ReadEnvelope(const LWS::Element& dad, LWO::Envelope& fill ) +{ + if (dad.children.empty()) { + DefaultLogger::get()->error("LWS: Envelope descriptions must not be empty"); + return; + } + + // reserve enough storage + std::list< LWS::Element >::const_iterator it = dad.children.begin();; + fill.keys.reserve(strtoul10(it->tokens[1].c_str())); + + for (++it; it != dad.children.end(); ++it) { + const char* c = (*it).tokens[1].c_str(); + + if ((*it).tokens[0] == "Key") { + fill.keys.push_back(LWO::Key()); + LWO::Key& key = fill.keys.back(); + + float f; + SkipSpaces(&c); + c = fast_atoreal_move<float>(c,key.value); + SkipSpaces(&c); + c = fast_atoreal_move<float>(c,f); + + key.time = f; + + unsigned int span = strtoul10(c,&c), num = 0; + switch (span) { + + case 0: + key.inter = LWO::IT_TCB; + num = 5; + break; + case 1: + case 2: + key.inter = LWO::IT_HERM; + num = 5; + break; + case 3: + key.inter = LWO::IT_LINE; + num = 0; + break; + case 4: + key.inter = LWO::IT_STEP; + num = 0; + break; + case 5: + key.inter = LWO::IT_BEZ2; + num = 4; + break; + default: + DefaultLogger::get()->error("LWS: Unknown span type"); + } + for (unsigned int i = 0; i < num;++i) { + SkipSpaces(&c); + c = fast_atoreal_move<float>(c,key.params[i]); + } + } + else if ((*it).tokens[0] == "Behaviors") { + SkipSpaces(&c); + fill.pre = (LWO::PrePostBehaviour) strtoul10(c,&c); + SkipSpaces(&c); + fill.post = (LWO::PrePostBehaviour) strtoul10(c,&c); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Read animation channels in the old LightWave animation format +void LWSImporter::ReadEnvelope_Old( + std::list< LWS::Element >::const_iterator& it, + const std::list< LWS::Element >::const_iterator& end, + LWS::NodeDesc& nodes, + unsigned int /*version*/) +{ + unsigned int num,sub_num; + if (++it == end)goto unexpected_end; + + num = strtoul10((*it).tokens[0].c_str()); + for (unsigned int i = 0; i < num; ++i) { + + nodes.channels.push_back(LWO::Envelope()); + LWO::Envelope& envl = nodes.channels.back(); + + envl.index = i; + envl.type = (LWO::EnvelopeType)(i+1); + + if (++it == end)goto unexpected_end; + sub_num = strtoul10((*it).tokens[0].c_str()); + + for (unsigned int n = 0; n < sub_num;++n) { + + if (++it == end)goto unexpected_end; + + // parse value and time, skip the rest for the moment. + LWO::Key key; + const char* c = fast_atoreal_move<float>((*it).tokens[0].c_str(),key.value); + SkipSpaces(&c); + float f; + fast_atoreal_move<float>((*it).tokens[0].c_str(),f); + key.time = f; + + envl.keys.push_back(key); + } + } + return; + +unexpected_end: + DefaultLogger::get()->error("LWS: Encountered unexpected end of file while parsing object motion"); +} + +// ------------------------------------------------------------------------------------------------ +// Setup a nice name for a node +void LWSImporter::SetupNodeName(aiNode* nd, LWS::NodeDesc& src) +{ + const unsigned int combined = src.number | ((unsigned int)src.type) << 28u; + + // the name depends on the type. We break LWS's strange naming convention + // and return human-readable, but still machine-parsable and unique, strings. + if (src.type == LWS::NodeDesc::OBJECT) { + + if (src.path.length()) { + std::string::size_type s = src.path.find_last_of("\\/"); + if (s == std::string::npos) + s = 0; + else ++s; + std::string::size_type t = src.path.substr(s).find_last_of("."); + + nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)",src.path.substr(s).substr(0,t).c_str(),combined); + return; + } + } + nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)",src.name,combined); +} + +// ------------------------------------------------------------------------------------------------ +// Recursively build the scenegraph +void LWSImporter::BuildGraph(aiNode* nd, LWS::NodeDesc& src, std::vector<AttachmentInfo>& attach, + BatchLoader& batch, + aiCamera**& camOut, + aiLight**& lightOut, + std::vector<aiNodeAnim*>& animOut) +{ + // Setup a very cryptic name for the node, we want the user to be happy + SetupNodeName(nd,src); + aiNode* ndAnim = nd; + + // If the node is an object + if (src.type == LWS::NodeDesc::OBJECT) { + + // If the object is from an external file, get it + aiScene* obj = NULL; + if (src.path.length() ) { + obj = batch.GetImport(src.id); + if (!obj) { + DefaultLogger::get()->error("LWS: Failed to read external file " + src.path); + } + else { + if (obj->mRootNode->mNumChildren == 1) { + + //If the pivot is not set for this layer, get it from the external object + if (!src.isPivotSet) { + src.pivotPos.x = +obj->mRootNode->mTransformation.a4; + src.pivotPos.y = +obj->mRootNode->mTransformation.b4; + src.pivotPos.z = -obj->mRootNode->mTransformation.c4; //The sign is the RH to LH back conversion + } + + //Remove first node from obj (the old pivot), reset transform of second node (the mesh node) + aiNode* newRootNode = obj->mRootNode->mChildren[0]; + obj->mRootNode->mChildren[0] = NULL; + delete obj->mRootNode; + + obj->mRootNode = newRootNode; + obj->mRootNode->mTransformation.a4 = 0.0; + obj->mRootNode->mTransformation.b4 = 0.0; + obj->mRootNode->mTransformation.c4 = 0.0; + } + } + } + + //Setup the pivot node (also the animation node), the one we received + nd->mName = std::string("Pivot:") + nd->mName.data; + ndAnim = nd; + + //Add the attachment node to it + nd->mNumChildren = 1; + nd->mChildren = new aiNode*[1]; + nd->mChildren[0] = new aiNode(); + nd->mChildren[0]->mParent = nd; + nd->mChildren[0]->mTransformation.a4 = -src.pivotPos.x; + nd->mChildren[0]->mTransformation.b4 = -src.pivotPos.y; + nd->mChildren[0]->mTransformation.c4 = -src.pivotPos.z; + SetupNodeName(nd->mChildren[0], src); + + //Update the attachment node + nd = nd->mChildren[0]; + + //Push attachment, if the object came from an external file + if (obj) { + attach.push_back(AttachmentInfo(obj,nd)); + } + } + + // If object is a light source - setup a corresponding ai structure + else if (src.type == LWS::NodeDesc::LIGHT) { + aiLight* lit = *lightOut++ = new aiLight(); + + // compute final light color + lit->mColorDiffuse = lit->mColorSpecular = src.lightColor*src.lightIntensity; + + // name to attach light to node -> unique due to LWs indexing system + lit->mName = nd->mName; + + // detemine light type and setup additional members + if (src.lightType == 2) { /* spot light */ + + lit->mType = aiLightSource_SPOT; + lit->mAngleInnerCone = (float)AI_DEG_TO_RAD( src.lightConeAngle ); + lit->mAngleOuterCone = lit->mAngleInnerCone+(float)AI_DEG_TO_RAD( src.lightEdgeAngle ); + + } + else if (src.lightType == 1) { /* directional light source */ + lit->mType = aiLightSource_DIRECTIONAL; + } + else lit->mType = aiLightSource_POINT; + + // fixme: no proper handling of light falloffs yet + if (src.lightFalloffType == 1) + lit->mAttenuationConstant = 1.f; + else if (src.lightFalloffType == 1) + lit->mAttenuationLinear = 1.f; + else + lit->mAttenuationQuadratic = 1.f; + } + + // If object is a camera - setup a corresponding ai structure + else if (src.type == LWS::NodeDesc::CAMERA) { + aiCamera* cam = *camOut++ = new aiCamera(); + + // name to attach cam to node -> unique due to LWs indexing system + cam->mName = nd->mName; + } + + // Get the node transformation from the LWO key + LWO::AnimResolver resolver(src.channels,fps); + resolver.ExtractBindPose(ndAnim->mTransformation); + + // .. and construct animation channels + aiNodeAnim* anim = NULL; + + if (first != last) { + resolver.SetAnimationRange(first,last); + resolver.ExtractAnimChannel(&anim,AI_LWO_ANIM_FLAG_SAMPLE_ANIMS|AI_LWO_ANIM_FLAG_START_AT_ZERO); + if (anim) { + anim->mNodeName = ndAnim->mName; + animOut.push_back(anim); + } + } + + // Add children + if (!src.children.empty()) { + nd->mChildren = new aiNode*[src.children.size()]; + for (std::list<LWS::NodeDesc*>::iterator it = src.children.begin(); it != src.children.end(); ++it) { + aiNode* ndd = nd->mChildren[nd->mNumChildren++] = new aiNode(); + ndd->mParent = nd; + + BuildGraph(ndd,**it,attach,batch,camOut,lightOut,animOut); + } + } +} + +// ------------------------------------------------------------------------------------------------ +// Determine the exact location of a LWO file +std::string LWSImporter::FindLWOFile(const std::string& in) +{ + // insert missing directory separator if necessary + std::string tmp; + if (in.length() > 3 && in[1] == ':'&& in[2] != '\\' && in[2] != '/') + { + tmp = in[0] + (std::string(":\\") + in.substr(2)); + } + else tmp = in; + + if (io->Exists(tmp)) { + return in; + } + + // file is not accessible for us ... maybe it's packed by + // LightWave's 'Package Scene' command? + + // Relevant for us are the following two directories: + // <folder>\Objects\<hh>\<*>.lwo + // <folder>\Scenes\<hh>\<*>.lws + // where <hh> is optional. + + std::string test = std::string("..") + (io->getOsSeparator() + tmp); + if (io->Exists(test)) { + return test; + } + + test = std::string("..") + (io->getOsSeparator() + test); + if (io->Exists(test)) { + return test; + } + + + // return original path, maybe the IOsystem knows better + return tmp; +} + +// ------------------------------------------------------------------------------------------------ +// Read file into given scene data structure +void LWSImporter::InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler) +{ + io = pIOHandler; + std::unique_ptr<IOStream> file( pIOHandler->Open( pFile, "rb")); + + // Check whether we can read from the file + if( file.get() == NULL) { + throw DeadlyImportError( "Failed to open LWS file " + pFile + "."); + } + + // Allocate storage and copy the contents of the file to a memory buffer + std::vector< char > mBuffer; + TextFileToBuffer(file.get(),mBuffer); + + // Parse the file structure + LWS::Element root; const char* dummy = &mBuffer[0]; + root.Parse(dummy); + + // Construct a Batchimporter to read more files recursively + BatchLoader batch(pIOHandler); +// batch.SetBasePath(pFile); + + // Construct an array to receive the flat output graph + std::list<LWS::NodeDesc> nodes; + + unsigned int cur_light = 0, cur_camera = 0, cur_object = 0; + unsigned int num_light = 0, num_camera = 0, num_object = 0; + + // check magic identifier, 'LWSC' + bool motion_file = false; + std::list< LWS::Element >::const_iterator it = root.children.begin(); + + if ((*it).tokens[0] == "LWMO") + motion_file = true; + + if ((*it).tokens[0] != "LWSC" && !motion_file) + throw DeadlyImportError("LWS: Not a LightWave scene, magic tag LWSC not found"); + + // get file format version and print to log + ++it; + unsigned int version = strtoul10((*it).tokens[0].c_str()); + DefaultLogger::get()->info("LWS file format version is " + (*it).tokens[0]); + first = 0.; + last = 60.; + fps = 25.; /* seems to be a good default frame rate */ + + // Now read all elements in a very straghtforward manner + for (; it != root.children.end(); ++it) { + const char* c = (*it).tokens[1].c_str(); + + // 'FirstFrame': begin of animation slice + if ((*it).tokens[0] == "FirstFrame") { + if (150392. != first /* see SetupProperties() */) + first = strtoul10(c,&c)-1.; /* we're zero-based */ + } + + // 'LastFrame': end of animation slice + else if ((*it).tokens[0] == "LastFrame") { + if (150392. != last /* see SetupProperties() */) + last = strtoul10(c,&c)-1.; /* we're zero-based */ + } + + // 'FramesPerSecond': frames per second + else if ((*it).tokens[0] == "FramesPerSecond") { + fps = strtoul10(c,&c); + } + + // 'LoadObjectLayer': load a layer of a specific LWO file + else if ((*it).tokens[0] == "LoadObjectLayer") { + + // get layer index + const int layer = strtoul10(c,&c); + + // setup the layer to be loaded + BatchLoader::PropertyMap props; + SetGenericProperty(props.ints,AI_CONFIG_IMPORT_LWO_ONE_LAYER_ONLY,layer); + + // add node to list + LWS::NodeDesc d; + d.type = LWS::NodeDesc::OBJECT; + if (version >= 4) { // handle LWSC 4 explicit ID + SkipSpaces(&c); + d.number = strtoul16(c,&c) & AI_LWS_MASK; + } + else d.number = cur_object++; + + // and add the file to the import list + SkipSpaces(&c); + std::string path = FindLWOFile( c ); + d.path = path; + d.id = batch.AddLoadRequest(path,0,&props); + + nodes.push_back(d); + num_object++; + } + // 'LoadObject': load a LWO file into the scenegraph + else if ((*it).tokens[0] == "LoadObject") { + + // add node to list + LWS::NodeDesc d; + d.type = LWS::NodeDesc::OBJECT; + + if (version >= 4) { // handle LWSC 4 explicit ID + d.number = strtoul16(c,&c) & AI_LWS_MASK; + SkipSpaces(&c); + } + else d.number = cur_object++; + std::string path = FindLWOFile( c ); + d.id = batch.AddLoadRequest(path,0,NULL); + + d.path = path; + nodes.push_back(d); + num_object++; + } + // 'AddNullObject': add a dummy node to the hierarchy + else if ((*it).tokens[0] == "AddNullObject") { + + // add node to list + LWS::NodeDesc d; + d.type = LWS::NodeDesc::OBJECT; + if (version >= 4) { // handle LWSC 4 explicit ID + d.number = strtoul16(c,&c) & AI_LWS_MASK; + SkipSpaces(&c); + } + else d.number = cur_object++; + d.name = c; + nodes.push_back(d); + + num_object++; + } + // 'NumChannels': Number of envelope channels assigned to last layer + else if ((*it).tokens[0] == "NumChannels") { + // ignore for now + } + // 'Channel': preceedes any envelope description + else if ((*it).tokens[0] == "Channel") { + if (nodes.empty()) { + if (motion_file) { + + // LightWave motion file. Add dummy node + LWS::NodeDesc d; + d.type = LWS::NodeDesc::OBJECT; + d.name = c; + d.number = cur_object++; + nodes.push_back(d); + } + else DefaultLogger::get()->error("LWS: Unexpected keyword: \'Channel\'"); + } + + // important: index of channel + nodes.back().channels.push_back(LWO::Envelope()); + LWO::Envelope& env = nodes.back().channels.back(); + + env.index = strtoul10(c); + + // currently we can just interpret the standard channels 0...9 + // (hack) assume that index-i yields the binary channel type from LWO + env.type = (LWO::EnvelopeType)(env.index+1); + + } + // 'Envelope': a single animation channel + else if ((*it).tokens[0] == "Envelope") { + if (nodes.empty() || nodes.back().channels.empty()) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'Envelope\'"); + else { + ReadEnvelope((*it),nodes.back().channels.back()); + } + } + // 'ObjectMotion': animation information for older lightwave formats + else if (version < 3 && ((*it).tokens[0] == "ObjectMotion" || + (*it).tokens[0] == "CameraMotion" || + (*it).tokens[0] == "LightMotion")) { + + if (nodes.empty()) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'<Light|Object|Camera>Motion\'"); + else { + ReadEnvelope_Old(it,root.children.end(),nodes.back(),version); + } + } + // 'Pre/PostBehavior': pre/post animation behaviour for LWSC 2 + else if (version == 2 && (*it).tokens[0] == "Pre/PostBehavior") { + if (nodes.empty()) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'Pre/PostBehavior'"); + else { + for (std::list<LWO::Envelope>::iterator it = nodes.back().channels.begin(); it != nodes.back().channels.end(); ++it) { + // two ints per envelope + LWO::Envelope& env = *it; + env.pre = (LWO::PrePostBehaviour) strtoul10(c,&c); SkipSpaces(&c); + env.post = (LWO::PrePostBehaviour) strtoul10(c,&c); SkipSpaces(&c); + } + } + } + // 'ParentItem': specifies the parent of the current element + else if ((*it).tokens[0] == "ParentItem") { + if (nodes.empty()) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentItem\'"); + + else nodes.back().parent = strtoul16(c,&c); + } + // 'ParentObject': deprecated one for older formats + else if (version < 3 && (*it).tokens[0] == "ParentObject") { + if (nodes.empty()) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'ParentObject\'"); + + else { + nodes.back().parent = strtoul10(c,&c) | (1u << 28u); + } + } + // 'AddCamera': add a camera to the scenegraph + else if ((*it).tokens[0] == "AddCamera") { + + // add node to list + LWS::NodeDesc d; + d.type = LWS::NodeDesc::CAMERA; + + if (version >= 4) { // handle LWSC 4 explicit ID + d.number = strtoul16(c,&c) & AI_LWS_MASK; + } + else d.number = cur_camera++; + nodes.push_back(d); + + num_camera++; + } + // 'CameraName': set name of currently active camera + else if ((*it).tokens[0] == "CameraName") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::CAMERA) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'CameraName\'"); + + else nodes.back().name = c; + } + // 'AddLight': add a light to the scenegraph + else if ((*it).tokens[0] == "AddLight") { + + // add node to list + LWS::NodeDesc d; + d.type = LWS::NodeDesc::LIGHT; + + if (version >= 4) { // handle LWSC 4 explicit ID + d.number = strtoul16(c,&c) & AI_LWS_MASK; + } + else d.number = cur_light++; + nodes.push_back(d); + + num_light++; + } + // 'LightName': set name of currently active light + else if ((*it).tokens[0] == "LightName") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightName\'"); + + else nodes.back().name = c; + } + // 'LightIntensity': set intensity of currently active light + else if ((*it).tokens[0] == "LightIntensity" || (*it).tokens[0] == "LgtIntensity" ) { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightIntensity\'"); + + else fast_atoreal_move<float>(c, nodes.back().lightIntensity ); + + } + // 'LightType': set type of currently active light + else if ((*it).tokens[0] == "LightType") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightType\'"); + + else nodes.back().lightType = strtoul10(c); + + } + // 'LightFalloffType': set falloff type of currently active light + else if ((*it).tokens[0] == "LightFalloffType") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightFalloffType\'"); + + else nodes.back().lightFalloffType = strtoul10(c); + + } + // 'LightConeAngle': set cone angle of currently active light + else if ((*it).tokens[0] == "LightConeAngle") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightConeAngle\'"); + + else nodes.back().lightConeAngle = fast_atof(c); + + } + // 'LightEdgeAngle': set area where we're smoothing from min to max intensity + else if ((*it).tokens[0] == "LightEdgeAngle") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightEdgeAngle\'"); + + else nodes.back().lightEdgeAngle = fast_atof(c); + + } + // 'LightColor': set color of currently active light + else if ((*it).tokens[0] == "LightColor") { + if (nodes.empty() || nodes.back().type != LWS::NodeDesc::LIGHT) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'LightColor\'"); + + else { + c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.r ); + SkipSpaces(&c); + c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.g ); + SkipSpaces(&c); + c = fast_atoreal_move<float>(c, (float&) nodes.back().lightColor.b ); + } + } + + // 'PivotPosition': position of local transformation origin + else if ((*it).tokens[0] == "PivotPosition" || (*it).tokens[0] == "PivotPoint") { + if (nodes.empty()) + DefaultLogger::get()->error("LWS: Unexpected keyword: \'PivotPosition\'"); + else { + c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.x ); + SkipSpaces(&c); + c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.y ); + SkipSpaces(&c); + c = fast_atoreal_move<float>(c, (float&) nodes.back().pivotPos.z ); + // Mark pivotPos as set + nodes.back().isPivotSet = true; + } + } + } + + // resolve parenting + for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) { + + // check whether there is another node which calls us a parent + for (std::list<LWS::NodeDesc>::iterator dit = nodes.begin(); dit != nodes.end(); ++dit) { + if (dit != it && *it == (*dit).parent) { + if ((*dit).parent_resolved) { + // fixme: it's still possible to produce an overflow due to cross references .. + DefaultLogger::get()->error("LWS: Found cross reference in scenegraph"); + continue; + } + + (*it).children.push_back(&*dit); + (*dit).parent_resolved = &*it; + } + } + } + + // find out how many nodes have no parent yet + unsigned int no_parent = 0; + for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) { + if (!(*it).parent_resolved) + ++ no_parent; + } + if (!no_parent) + throw DeadlyImportError("LWS: Unable to find scene root node"); + + + // Load all subsequent files + batch.LoadAll(); + + // and build the final output graph by attaching the loaded external + // files to ourselves. first build a master graph + aiScene* master = new aiScene(); + aiNode* nd = master->mRootNode = new aiNode(); + + // allocate storage for cameras&lights + if (num_camera) { + master->mCameras = new aiCamera*[master->mNumCameras = num_camera]; + } + aiCamera** cams = master->mCameras; + if (num_light) { + master->mLights = new aiLight*[master->mNumLights = num_light]; + } + aiLight** lights = master->mLights; + + std::vector<AttachmentInfo> attach; + std::vector<aiNodeAnim*> anims; + + nd->mName.Set("<LWSRoot>"); + nd->mChildren = new aiNode*[no_parent]; + for (std::list<LWS::NodeDesc>::iterator it = nodes.begin(); it != nodes.end(); ++it) { + if (!(*it).parent_resolved) { + aiNode* ro = nd->mChildren[ nd->mNumChildren++ ] = new aiNode(); + ro->mParent = nd; + + // ... and build the scene graph. If we encounter object nodes, + // add then to our attachment table. + BuildGraph(ro,*it, attach, batch, cams, lights, anims); + } + } + + // create a master animation channel for us + if (anims.size()) { + master->mAnimations = new aiAnimation*[master->mNumAnimations = 1]; + aiAnimation* anim = master->mAnimations[0] = new aiAnimation(); + anim->mName.Set("LWSMasterAnim"); + + // LWS uses seconds as time units, but we convert to frames + anim->mTicksPerSecond = fps; + anim->mDuration = last-(first-1); /* fixme ... zero or one-based?*/ + + anim->mChannels = new aiNodeAnim*[anim->mNumChannels = static_cast<unsigned int>(anims.size())]; + std::copy(anims.begin(),anims.end(),anim->mChannels); + } + + // convert the master scene to RH + MakeLeftHandedProcess monster_cheat; + monster_cheat.Execute(master); + + // .. ccw + FlipWindingOrderProcess flipper; + flipper.Execute(master); + + // OK ... finally build the output graph + SceneCombiner::MergeScenes(&pScene,master,attach, + AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES | (!configSpeedFlag ? ( + AI_INT_MERGE_SCENE_GEN_UNIQUE_NAMES_IF_NECESSARY | AI_INT_MERGE_SCENE_GEN_UNIQUE_MATNAMES) : 0)); + + // Check flags + if (!pScene->mNumMeshes || !pScene->mNumMaterials) { + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + + if (pScene->mNumAnimations && !noSkeletonMesh) { + // construct skeleton mesh + SkeletonMeshBuilder builder(pScene); + } + } + +} + +#endif // !! ASSIMP_BUILD_NO_LWS_IMPORTER |