nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file FBXDocument.cpp nuclear@0: * @brief Implementation of the FBX DOM classes nuclear@0: */ nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER nuclear@0: nuclear@0: #include nuclear@0: nuclear@0: #include "FBXParser.h" nuclear@0: #include "FBXDocument.h" nuclear@0: #include "FBXUtil.h" nuclear@0: #include "FBXImporter.h" nuclear@0: #include "FBXImportSettings.h" nuclear@0: #include "FBXDocumentUtil.h" nuclear@0: #include "FBXProperties.h" nuclear@0: nuclear@0: namespace Assimp { nuclear@0: namespace FBX { nuclear@0: nuclear@0: using namespace Util; nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc) nuclear@0: : doc(doc) nuclear@0: , element(element) nuclear@0: , id(id) nuclear@0: , flags() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: LazyObject::~LazyObject() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const Object* LazyObject::Get(bool dieOnError) nuclear@0: { nuclear@0: if(IsBeingConstructed() || FailedToConstruct()) { nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: if (object.get()) { nuclear@0: return object.get(); nuclear@0: } nuclear@0: nuclear@0: // if this is the root object, we return a dummy since there nuclear@0: // is no root object int he fbx file - it is just referenced nuclear@0: // with id 0. nuclear@0: if(id == 0L) { nuclear@0: object.reset(new Object(id, element, "Model::RootNode")); nuclear@0: return object.get(); nuclear@0: } nuclear@0: nuclear@0: const Token& key = element.KeyToken(); nuclear@0: const TokenList& tokens = element.Tokens(); nuclear@0: nuclear@0: if(tokens.size() < 3) { nuclear@0: DOMError("expected at least 3 tokens: id, name and class tag",&element); nuclear@0: } nuclear@0: nuclear@0: const char* err; nuclear@0: std::string name = ParseTokenAsString(*tokens[1],err); nuclear@0: if (err) { nuclear@0: DOMError(err,&element); nuclear@0: } nuclear@0: nuclear@0: // small fix for binary reading: binary fbx files don't use nuclear@0: // prefixes such as Model:: in front of their names. The nuclear@0: // loading code expects this at many places, though! nuclear@0: // so convert the binary representation (a 0x0001) to the nuclear@0: // double colon notation. nuclear@0: if(tokens[1]->IsBinary()) { nuclear@0: for (size_t i = 0; i < name.length(); ++i) { nuclear@0: if (name[i] == 0x0 && name[i+1] == 0x1) { nuclear@0: name = name.substr(i+2) + "::" + name.substr(0,i); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: const std::string classtag = ParseTokenAsString(*tokens[2],err); nuclear@0: if (err) { nuclear@0: DOMError(err,&element); nuclear@0: } nuclear@0: nuclear@0: // prevent recursive calls nuclear@0: flags |= BEING_CONSTRUCTED; nuclear@0: nuclear@0: try { nuclear@0: // this needs to be relatively fast since it happens a lot, nuclear@0: // so avoid constructing strings all the time. nuclear@0: const char* obtype = key.begin(); nuclear@0: const size_t length = static_cast(key.end()-key.begin()); nuclear@0: if (!strncmp(obtype,"Geometry",length)) { nuclear@0: if (!strcmp(classtag.c_str(),"Mesh")) { nuclear@0: object.reset(new MeshGeometry(id,element,name,doc)); nuclear@0: } nuclear@0: } nuclear@0: else if (!strncmp(obtype,"NodeAttribute",length)) { nuclear@0: if (!strcmp(classtag.c_str(),"Camera")) { nuclear@0: object.reset(new Camera(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strcmp(classtag.c_str(),"CameraSwitcher")) { nuclear@0: object.reset(new CameraSwitcher(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strcmp(classtag.c_str(),"Light")) { nuclear@0: object.reset(new Light(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strcmp(classtag.c_str(),"Null")) { nuclear@0: object.reset(new Null(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strcmp(classtag.c_str(),"LimbNode")) { nuclear@0: object.reset(new LimbNode(id,element,doc,name)); nuclear@0: } nuclear@0: } nuclear@0: else if (!strncmp(obtype,"Deformer",length)) { nuclear@0: if (!strcmp(classtag.c_str(),"Cluster")) { nuclear@0: object.reset(new Cluster(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strcmp(classtag.c_str(),"Skin")) { nuclear@0: object.reset(new Skin(id,element,doc,name)); nuclear@0: } nuclear@0: } nuclear@0: else if (!strncmp(obtype,"Model",length)) { nuclear@0: // FK and IK effectors are not supported nuclear@0: if (strcmp(classtag.c_str(),"IKEffector") && strcmp(classtag.c_str(),"FKEffector")) { nuclear@0: object.reset(new Model(id,element,doc,name)); nuclear@0: } nuclear@0: } nuclear@0: else if (!strncmp(obtype,"Material",length)) { nuclear@0: object.reset(new Material(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strncmp(obtype,"Texture",length)) { nuclear@0: object.reset(new Texture(id,element,doc,name)); nuclear@0: } nuclear@0: else if (!strncmp(obtype,"AnimationStack",length)) { nuclear@0: object.reset(new AnimationStack(id,element,name,doc)); nuclear@0: } nuclear@0: else if (!strncmp(obtype,"AnimationLayer",length)) { nuclear@0: object.reset(new AnimationLayer(id,element,name,doc)); nuclear@0: } nuclear@0: // note: order matters for these two nuclear@0: else if (!strncmp(obtype,"AnimationCurve",length)) { nuclear@0: object.reset(new AnimationCurve(id,element,name,doc)); nuclear@0: } nuclear@0: else if (!strncmp(obtype,"AnimationCurveNode",length)) { nuclear@0: object.reset(new AnimationCurveNode(id,element,name,doc)); nuclear@0: } nuclear@0: } nuclear@0: catch(std::exception& ex) { nuclear@0: flags &= ~BEING_CONSTRUCTED; nuclear@0: flags |= FAILED_TO_CONSTRUCT; nuclear@0: nuclear@0: if(dieOnError || doc.Settings().strictMode) { nuclear@0: throw; nuclear@0: } nuclear@0: nuclear@0: // note: the error message is already formatted, so raw logging is ok nuclear@0: if(!DefaultLogger::isNullLogger()) { nuclear@0: DefaultLogger::get()->error(ex.what()); nuclear@0: } nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: if (!object.get()) { nuclear@0: //DOMError("failed to convert element to DOM object, class: " + classtag + ", name: " + name,&element); nuclear@0: } nuclear@0: nuclear@0: flags &= ~BEING_CONSTRUCTED; nuclear@0: return object.get(); nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Object::Object(uint64_t id, const Element& element, const std::string& name) nuclear@0: : element(element) nuclear@0: , name(name) nuclear@0: , id(id) nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Object::~Object() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: FileGlobalSettings::FileGlobalSettings(const Document& doc, boost::shared_ptr props) nuclear@0: : props(props) nuclear@0: , doc(doc) nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: FileGlobalSettings::~FileGlobalSettings() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Document::Document(const Parser& parser, const ImportSettings& settings) nuclear@0: : settings(settings) nuclear@0: , parser(parser) nuclear@0: { nuclear@0: // cannot use array default initialization syntax because vc8 fails on it nuclear@0: for (unsigned int i = 0; i < 7; ++i) { nuclear@0: creationTimeStamp[i] = 0; nuclear@0: } nuclear@0: nuclear@0: ReadHeader(); nuclear@0: ReadPropertyTemplates(); nuclear@0: nuclear@0: ReadGlobalSettings(); nuclear@0: nuclear@0: // this order is important, connections need parsed objects to check nuclear@0: // whether connections are ok or not. Objects may not be evaluated yet, nuclear@0: // though, since this may require valid connections. nuclear@0: ReadObjects(); nuclear@0: ReadConnections(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Document::~Document() nuclear@0: { nuclear@0: BOOST_FOREACH(ObjectMap::value_type& v, objects) { nuclear@0: delete v.second; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Document::ReadHeader() nuclear@0: { nuclear@0: // read ID objects from "Objects" section nuclear@0: const Scope& sc = parser.GetRootScope(); nuclear@0: const Element* const ehead = sc["FBXHeaderExtension"]; nuclear@0: if(!ehead || !ehead->Compound()) { nuclear@0: DOMError("no FBXHeaderExtension dictionary found"); nuclear@0: } nuclear@0: nuclear@0: const Scope& shead = *ehead->Compound(); nuclear@0: fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0)); nuclear@0: nuclear@0: nuclear@0: if(fbxVersion < 7200 || fbxVersion > 7300) { nuclear@0: if(Settings().strictMode) { nuclear@0: DOMError("unsupported format version, supported are only FBX 2012 and FBX 2013"\ nuclear@0: " in ASCII format (turn off strict mode to try anyhow) "); nuclear@0: } nuclear@0: else { nuclear@0: DOMWarning("unsupported format version, supported are only FBX 2012 and FBX 2013, trying to read it nevertheless"); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: const Element* const ecreator = shead["Creator"]; nuclear@0: if(ecreator) { nuclear@0: creator = ParseTokenAsString(GetRequiredToken(*ecreator,0)); nuclear@0: } nuclear@0: nuclear@0: const Element* const etimestamp = shead["CreationTimeStamp"]; nuclear@0: if(etimestamp && etimestamp->Compound()) { nuclear@0: const Scope& stimestamp = *etimestamp->Compound(); nuclear@0: creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0)); nuclear@0: creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0)); nuclear@0: creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0)); nuclear@0: creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0)); nuclear@0: creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0)); nuclear@0: creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0)); nuclear@0: creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Document::ReadGlobalSettings() nuclear@0: { nuclear@0: const Scope& sc = parser.GetRootScope(); nuclear@0: const Element* const ehead = sc["GlobalSettings"]; nuclear@0: if(!ehead || !ehead->Compound()) { nuclear@0: DOMWarning("no GlobalSettings dictionary found"); nuclear@0: nuclear@0: globals.reset(new FileGlobalSettings(*this, boost::make_shared())); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: boost::shared_ptr props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true); nuclear@0: nuclear@0: if(!props) { nuclear@0: DOMError("GlobalSettings dictionary contains no property table"); nuclear@0: } nuclear@0: nuclear@0: globals.reset(new FileGlobalSettings(*this, props)); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Document::ReadObjects() nuclear@0: { nuclear@0: // read ID objects from "Objects" section nuclear@0: const Scope& sc = parser.GetRootScope(); nuclear@0: const Element* const eobjects = sc["Objects"]; nuclear@0: if(!eobjects || !eobjects->Compound()) { nuclear@0: DOMError("no Objects dictionary found"); nuclear@0: } nuclear@0: nuclear@0: // add a dummy entry to represent the Model::RootNode object (id 0), nuclear@0: // which is only indirectly defined in the input file nuclear@0: objects[0] = new LazyObject(0L, *eobjects, *this); nuclear@0: nuclear@0: const Scope& sobjects = *eobjects->Compound(); nuclear@0: BOOST_FOREACH(const ElementMap::value_type& el, sobjects.Elements()) { nuclear@0: nuclear@0: // extract ID nuclear@0: const TokenList& tok = el.second->Tokens(); nuclear@0: nuclear@0: if (tok.empty()) { nuclear@0: DOMError("expected ID after object key",el.second); nuclear@0: } nuclear@0: nuclear@0: const char* err; nuclear@0: nuclear@0: const uint64_t id = ParseTokenAsID(*tok[0], err); nuclear@0: if(err) { nuclear@0: DOMError(err,el.second); nuclear@0: } nuclear@0: nuclear@0: // id=0 is normally implicit nuclear@0: if(id == 0L) { nuclear@0: DOMError("encountered object with implicitly defined id 0",el.second); nuclear@0: } nuclear@0: nuclear@0: if(objects.find(id) != objects.end()) { nuclear@0: DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second); nuclear@0: } nuclear@0: nuclear@0: objects[id] = new LazyObject(id, *el.second, *this); nuclear@0: nuclear@0: // grab all animation stacks upfront since there is no listing of them nuclear@0: if(!strcmp(el.first.c_str(),"AnimationStack")) { nuclear@0: animationStacks.push_back(id); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Document::ReadPropertyTemplates() nuclear@0: { nuclear@0: const Scope& sc = parser.GetRootScope(); nuclear@0: // read property templates from "Definitions" section nuclear@0: const Element* const edefs = sc["Definitions"]; nuclear@0: if(!edefs || !edefs->Compound()) { nuclear@0: DOMWarning("no Definitions dictionary found"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const Scope& sdefs = *edefs->Compound(); nuclear@0: const ElementCollection otypes = sdefs.GetCollection("ObjectType"); nuclear@0: for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) { nuclear@0: const Element& el = *(*it).second; nuclear@0: const Scope* sc = el.Compound(); nuclear@0: if(!sc) { nuclear@0: DOMWarning("expected nested scope in ObjectType, ignoring",&el); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: DOMWarning("expected name for ObjectType element, ignoring",&el); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: const std::string& oname = ParseTokenAsString(*tok[0]); nuclear@0: nuclear@0: const ElementCollection templs = sc->GetCollection("PropertyTemplate"); nuclear@0: for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) { nuclear@0: const Element& el = *(*it).second; nuclear@0: const Scope* sc = el.Compound(); nuclear@0: if(!sc) { nuclear@0: DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: const TokenList& tok = el.Tokens(); nuclear@0: if(tok.empty()) { nuclear@0: DOMWarning("expected name for PropertyTemplate element, ignoring",&el); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: const std::string& pname = ParseTokenAsString(*tok[0]); nuclear@0: nuclear@0: const Element* Properties70 = (*sc)["Properties70"]; nuclear@0: if(Properties70) { nuclear@0: boost::shared_ptr props = boost::make_shared( nuclear@0: *Properties70,boost::shared_ptr(static_cast(NULL)) nuclear@0: ); nuclear@0: nuclear@0: templates[oname+"."+pname] = props; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void Document::ReadConnections() nuclear@0: { nuclear@0: const Scope& sc = parser.GetRootScope(); nuclear@0: // read property templates from "Definitions" section nuclear@0: const Element* const econns = sc["Connections"]; nuclear@0: if(!econns || !econns->Compound()) { nuclear@0: DOMError("no Connections dictionary found"); nuclear@0: } nuclear@0: nuclear@0: uint64_t insertionOrder = 0l; nuclear@0: nuclear@0: const Scope& sconns = *econns->Compound(); nuclear@0: const ElementCollection conns = sconns.GetCollection("C"); nuclear@0: for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) { nuclear@0: const Element& el = *(*it).second; nuclear@0: const std::string& type = ParseTokenAsString(GetRequiredToken(el,0)); nuclear@0: const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1)); nuclear@0: const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2)); nuclear@0: nuclear@0: // OO = object-object connection nuclear@0: // OP = object-property connection, in which case the destination property follows the object ID nuclear@0: const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : ""); nuclear@0: nuclear@0: if(objects.find(src) == objects.end()) { nuclear@0: DOMWarning("source object for connection does not exist",&el); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // dest may be 0 (root node) but we added a dummy object before nuclear@0: if(objects.find(dest) == objects.end()) { nuclear@0: DOMWarning("destination object for connection does not exist",&el); nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: // add new connection nuclear@0: const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this); nuclear@0: src_connections.insert(ConnectionMap::value_type(src,c)); nuclear@0: dest_connections.insert(ConnectionMap::value_type(dest,c)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const std::vector& Document::AnimationStacks() const nuclear@0: { nuclear@0: if (!animationStacksResolved.empty() || !animationStacks.size()) { nuclear@0: return animationStacksResolved; nuclear@0: } nuclear@0: nuclear@0: animationStacksResolved.reserve(animationStacks.size()); nuclear@0: BOOST_FOREACH(uint64_t id, animationStacks) { nuclear@0: LazyObject* const lazy = GetObject(id); nuclear@0: const AnimationStack* stack; nuclear@0: if(!lazy || !(stack = lazy->Get())) { nuclear@0: DOMWarning("failed to read AnimationStack object"); nuclear@0: continue; nuclear@0: } nuclear@0: animationStacksResolved.push_back(stack); nuclear@0: } nuclear@0: nuclear@0: return animationStacksResolved; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: LazyObject* Document::GetObject(uint64_t id) const nuclear@0: { nuclear@0: ObjectMap::const_iterator it = objects.find(id); nuclear@0: return it == objects.end() ? NULL : (*it).second; nuclear@0: } nuclear@0: nuclear@0: #define MAX_CLASSNAMES 6 nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsSequenced(uint64_t id, nuclear@0: const ConnectionMap& conns) const nuclear@0: { nuclear@0: std::vector temp; nuclear@0: nuclear@0: const std::pair range = nuclear@0: conns.equal_range(id); nuclear@0: nuclear@0: temp.reserve(std::distance(range.first,range.second)); nuclear@0: for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { nuclear@0: temp.push_back((*it).second); nuclear@0: } nuclear@0: nuclear@0: std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare)); nuclear@0: nuclear@0: return temp; // NRVO should handle this nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsSequenced(uint64_t id, bool is_src, nuclear@0: const ConnectionMap& conns, nuclear@0: const char* const* classnames, nuclear@0: size_t count) const nuclear@0: nuclear@0: { nuclear@0: ai_assert(classnames); nuclear@0: ai_assert(count != 0 && count <= MAX_CLASSNAMES); nuclear@0: nuclear@0: size_t lenghts[MAX_CLASSNAMES]; nuclear@0: nuclear@0: const size_t c = count; nuclear@0: for (size_t i = 0; i < c; ++i) { nuclear@0: lenghts[i] = strlen(classnames[i]); nuclear@0: } nuclear@0: nuclear@0: std::vector temp; nuclear@0: nuclear@0: const std::pair range = nuclear@0: conns.equal_range(id); nuclear@0: nuclear@0: temp.reserve(std::distance(range.first,range.second)); nuclear@0: for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) { nuclear@0: const Token& key = (is_src nuclear@0: ? (*it).second->LazyDestinationObject() nuclear@0: : (*it).second->LazySourceObject() nuclear@0: ).GetElement().KeyToken(); nuclear@0: nuclear@0: const char* obtype = key.begin(); nuclear@0: nuclear@0: for (size_t i = 0; i < c; ++i) { nuclear@0: ai_assert(classnames[i]); nuclear@0: if(static_cast(std::distance(key.begin(),key.end())) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) { nuclear@0: obtype = NULL; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if(obtype) { nuclear@0: continue; nuclear@0: } nuclear@0: nuclear@0: temp.push_back((*it).second); nuclear@0: } nuclear@0: nuclear@0: std::sort(temp.begin(), temp.end(), std::mem_fun(&Connection::Compare)); nuclear@0: return temp; // NRVO should handle this nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsBySourceSequenced(uint64_t source) const nuclear@0: { nuclear@0: return GetConnectionsSequenced(source, ConnectionsBySource()); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsBySourceSequenced(uint64_t dest, nuclear@0: const char* classname) const nuclear@0: { nuclear@0: const char* arr[] = {classname}; nuclear@0: return GetConnectionsBySourceSequenced(dest, arr,1); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, nuclear@0: const char* const* classnames, size_t count) const nuclear@0: { nuclear@0: return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, nuclear@0: const char* classname) const nuclear@0: { nuclear@0: const char* arr[] = {classname}; nuclear@0: return GetConnectionsByDestinationSequenced(dest, arr,1); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest) const nuclear@0: { nuclear@0: return GetConnectionsSequenced(dest, ConnectionsByDestination()); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: std::vector Document::GetConnectionsByDestinationSequenced(uint64_t dest, nuclear@0: const char* const* classnames, size_t count) const nuclear@0: nuclear@0: { nuclear@0: return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop, nuclear@0: const Document& doc) nuclear@0: nuclear@0: : insertionOrder(insertionOrder) nuclear@0: , prop(prop) nuclear@0: , src(src) nuclear@0: , dest(dest) nuclear@0: , doc(doc) nuclear@0: { nuclear@0: ai_assert(doc.Objects().find(src) != doc.Objects().end()); nuclear@0: // dest may be 0 (root node) nuclear@0: ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end()); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Connection::~Connection() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: LazyObject& Connection::LazySourceObject() const nuclear@0: { nuclear@0: LazyObject* const lazy = doc.GetObject(src); nuclear@0: ai_assert(lazy); nuclear@0: return *lazy; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: LazyObject& Connection::LazyDestinationObject() const nuclear@0: { nuclear@0: LazyObject* const lazy = doc.GetObject(dest); nuclear@0: ai_assert(lazy); nuclear@0: return *lazy; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const Object* Connection::SourceObject() const nuclear@0: { nuclear@0: LazyObject* const lazy = doc.GetObject(src); nuclear@0: ai_assert(lazy); nuclear@0: return lazy->Get(); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: const Object* Connection::DestinationObject() const nuclear@0: { nuclear@0: LazyObject* const lazy = doc.GetObject(dest); nuclear@0: ai_assert(lazy); nuclear@0: return lazy->Get(); nuclear@0: } nuclear@0: nuclear@0: } // !FBX nuclear@0: } // !Assimp nuclear@0: nuclear@0: #endif nuclear@0: