#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXDocument.h"
#include "FBXMeshGeometry.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXImporter.h"
#include "FBXImportSettings.h"
#include "FBXDocumentUtil.h"
#include "FBXProperties.h"
#include <memory>
#include <functional>
#include <map>
namespace Assimp {
namespace FBX {
using namespace Util;
LazyObject::LazyObject(uint64_t id, const Element& element, const Document& doc)
: doc(doc)
, element(element)
, id(id)
, flags()
{
}
LazyObject::~LazyObject()
{
}
const Object* LazyObject::Get(bool dieOnError)
{
if(IsBeingConstructed() || FailedToConstruct()) {
return NULL;
}
if (object.get()) {
return object.get();
}
if(id == 0L) {
object.reset(new Object(id, element, "Model::RootNode"));
return object.get();
}
const Token& key = element.KeyToken();
const TokenList& tokens = element.Tokens();
if(tokens.size() < 3) {
DOMError("expected at least 3 tokens: id, name and class tag",&element);
}
const char* err;
std::string name = ParseTokenAsString(*tokens[1],err);
if (err) {
DOMError(err,&element);
}
if(tokens[1]->IsBinary()) {
for (size_t i = 0; i < name.length(); ++i) {
if (name[i] == 0x0 && name[i+1] == 0x1) {
name = name.substr(i+2) + "::" + name.substr(0,i);
}
}
}
const std::string classtag = ParseTokenAsString(*tokens[2],err);
if (err) {
DOMError(err,&element);
}
flags |= BEING_CONSTRUCTED;
try {
const char* obtype = key.begin();
const size_t length = static_cast<size_t>(key.end()-key.begin());
if (!strncmp(obtype,"Geometry",length)) {
if (!strcmp(classtag.c_str(),"Mesh")) {
object.reset(new MeshGeometry(id,element,name,doc));
}
}
else if (!strncmp(obtype,"NodeAttribute",length)) {
if (!strcmp(classtag.c_str(),"Camera")) {
object.reset(new Camera(id,element,doc,name));
}
else if (!strcmp(classtag.c_str(),"CameraSwitcher")) {
object.reset(new CameraSwitcher(id,element,doc,name));
}
else if (!strcmp(classtag.c_str(),"Light")) {
object.reset(new Light(id,element,doc,name));
}
else if (!strcmp(classtag.c_str(),"Null")) {
object.reset(new Null(id,element,doc,name));
}
else if (!strcmp(classtag.c_str(),"LimbNode")) {
object.reset(new LimbNode(id,element,doc,name));
}
}
else if (!strncmp(obtype,"Deformer",length)) {
if (!strcmp(classtag.c_str(),"Cluster")) {
object.reset(new Cluster(id,element,doc,name));
}
else if (!strcmp(classtag.c_str(),"Skin")) {
object.reset(new Skin(id,element,doc,name));
}
}
else if ( !strncmp( obtype, "Model", length ) ) {
if ( strcmp( classtag.c_str(), "IKEffector" ) && strcmp( classtag.c_str(), "FKEffector" ) ) {
object.reset( new Model( id, element, doc, name ) );
}
}
else if (!strncmp(obtype,"Material",length)) {
object.reset(new Material(id,element,doc,name));
}
else if (!strncmp(obtype,"Texture",length)) {
object.reset(new Texture(id,element,doc,name));
}
else if (!strncmp(obtype,"LayeredTexture",length)) {
object.reset(new LayeredTexture(id,element,doc,name));
}
else if (!strncmp(obtype,"Video",length)) {
object.reset(new Video(id,element,doc,name));
}
else if (!strncmp(obtype,"AnimationStack",length)) {
object.reset(new AnimationStack(id,element,name,doc));
}
else if (!strncmp(obtype,"AnimationLayer",length)) {
object.reset(new AnimationLayer(id,element,name,doc));
}
else if (!strncmp(obtype,"AnimationCurve",length)) {
object.reset(new AnimationCurve(id,element,name,doc));
}
else if (!strncmp(obtype,"AnimationCurveNode",length)) {
object.reset(new AnimationCurveNode(id,element,name,doc));
}
}
catch(std::exception& ex) {
flags &= ~BEING_CONSTRUCTED;
flags |= FAILED_TO_CONSTRUCT;
if(dieOnError || doc.Settings().strictMode) {
throw;
}
if(!DefaultLogger::isNullLogger()) {
DefaultLogger::get()->error(ex.what());
}
return NULL;
}
if (!object.get()) {
}
flags &= ~BEING_CONSTRUCTED;
return object.get();
}
Object::Object(uint64_t id, const Element& element, const std::string& name)
: element(element)
, name(name)
, id(id)
{
}
Object::~Object()
{
}
FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr<const PropertyTable> props)
: props(props)
, doc(doc)
{
}
FileGlobalSettings::~FileGlobalSettings()
{
}
Document::Document(const Parser& parser, const ImportSettings& settings)
: settings(settings)
, parser(parser)
{
for (auto &timeStamp : creationTimeStamp) {
timeStamp = 0;
}
ReadHeader();
ReadPropertyTemplates();
ReadGlobalSettings();
ReadObjects();
ReadConnections();
}
Document::~Document()
{
for(ObjectMap::value_type& v : objects) {
delete v.second;
}
for(ConnectionMap::value_type& v : src_connections) {
delete v.second;
}
}
static const unsigned int LowerSupportedVersion = 7100;
static const unsigned int UpperSupportedVersion = 7400;
void Document::ReadHeader() {
const Scope& sc = parser.GetRootScope();
const Element* const ehead = sc["FBXHeaderExtension"];
if(!ehead || !ehead->Compound()) {
DOMError("no FBXHeaderExtension dictionary found");
}
const Scope& shead = *ehead->Compound();
fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0));
if(fbxVersion < LowerSupportedVersion ) {
DOMError("unsupported, old format version, supported are only FBX 2011, FBX 2012 and FBX 2013");
}
if(fbxVersion > UpperSupportedVersion ) {
if(Settings().strictMode) {
DOMError("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013"
" (turn off strict mode to try anyhow) ");
}
else {
DOMWarning("unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013,"
" trying to read it nevertheless");
}
}
const Element* const ecreator = shead["Creator"];
if(ecreator) {
creator = ParseTokenAsString(GetRequiredToken(*ecreator,0));
}
const Element* const etimestamp = shead["CreationTimeStamp"];
if(etimestamp && etimestamp->Compound()) {
const Scope& stimestamp = *etimestamp->Compound();
creationTimeStamp[0] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Year"),0));
creationTimeStamp[1] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Month"),0));
creationTimeStamp[2] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Day"),0));
creationTimeStamp[3] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Hour"),0));
creationTimeStamp[4] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Minute"),0));
creationTimeStamp[5] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Second"),0));
creationTimeStamp[6] = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(stimestamp,"Millisecond"),0));
}
}
void Document::ReadGlobalSettings()
{
const Scope& sc = parser.GetRootScope();
const Element* const ehead = sc["GlobalSettings"];
if(!ehead || !ehead->Compound()) {
DOMWarning("no GlobalSettings dictionary found");
globals.reset(new FileGlobalSettings(*this, std::make_shared<const PropertyTable>()));
return;
}
std::shared_ptr<const PropertyTable> props = GetPropertyTable(*this, "", *ehead, *ehead->Compound(), true);
if(!props) {
DOMError("GlobalSettings dictionary contains no property table");
}
globals.reset(new FileGlobalSettings(*this, props));
}
void Document::ReadObjects()
{
const Scope& sc = parser.GetRootScope();
const Element* const eobjects = sc["Objects"];
if(!eobjects || !eobjects->Compound()) {
DOMError("no Objects dictionary found");
}
objects[0] = new LazyObject(0L, *eobjects, *this);
const Scope& sobjects = *eobjects->Compound();
for(const ElementMap::value_type& el : sobjects.Elements()) {
const TokenList& tok = el.second->Tokens();
if (tok.empty()) {
DOMError("expected ID after object key",el.second);
}
const char* err;
const uint64_t id = ParseTokenAsID(*tok[0], err);
if(err) {
DOMError(err,el.second);
}
if(id == 0L) {
DOMError("encountered object with implicitly defined id 0",el.second);
}
if(objects.find(id) != objects.end()) {
DOMWarning("encountered duplicate object id, ignoring first occurrence",el.second);
}
objects[id] = new LazyObject(id, *el.second, *this);
if(!strcmp(el.first.c_str(),"AnimationStack")) {
animationStacks.push_back(id);
}
}
}
void Document::ReadPropertyTemplates()
{
const Scope& sc = parser.GetRootScope();
const Element* const edefs = sc["Definitions"];
if(!edefs || !edefs->Compound()) {
DOMWarning("no Definitions dictionary found");
return;
}
const Scope& sdefs = *edefs->Compound();
const ElementCollection otypes = sdefs.GetCollection("ObjectType");
for(ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
const Element& el = *(*it).second;
const Scope* sc = el.Compound();
if(!sc) {
DOMWarning("expected nested scope in ObjectType, ignoring",&el);
continue;
}
const TokenList& tok = el.Tokens();
if(tok.empty()) {
DOMWarning("expected name for ObjectType element, ignoring",&el);
continue;
}
const std::string& oname = ParseTokenAsString(*tok[0]);
const ElementCollection templs = sc->GetCollection("PropertyTemplate");
for(ElementMap::const_iterator it = templs.first; it != templs.second; ++it) {
const Element& el = *(*it).second;
const Scope* sc = el.Compound();
if(!sc) {
DOMWarning("expected nested scope in PropertyTemplate, ignoring",&el);
continue;
}
const TokenList& tok = el.Tokens();
if(tok.empty()) {
DOMWarning("expected name for PropertyTemplate element, ignoring",&el);
continue;
}
const std::string& pname = ParseTokenAsString(*tok[0]);
const Element* Properties70 = (*sc)["Properties70"];
if(Properties70) {
std::shared_ptr<const PropertyTable> props = std::make_shared<const PropertyTable>(
*Properties70,std::shared_ptr<const PropertyTable>(static_cast<const PropertyTable*>(NULL))
);
templates[oname+"."+pname] = props;
}
}
}
}
void Document::ReadConnections()
{
const Scope& sc = parser.GetRootScope();
const Element* const econns = sc["Connections"];
if(!econns || !econns->Compound()) {
DOMError("no Connections dictionary found");
}
uint64_t insertionOrder = 0l;
const Scope& sconns = *econns->Compound();
const ElementCollection conns = sconns.GetCollection("C");
for(ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
const Element& el = *(*it).second;
const std::string& type = ParseTokenAsString(GetRequiredToken(el,0));
if(type == "PP") continue;
const uint64_t src = ParseTokenAsID(GetRequiredToken(el,1));
const uint64_t dest = ParseTokenAsID(GetRequiredToken(el,2));
const std::string& prop = (type == "OP" ? ParseTokenAsString(GetRequiredToken(el,3)) : "");
if(objects.find(src) == objects.end()) {
DOMWarning("source object for connection does not exist",&el);
continue;
}
if(objects.find(dest) == objects.end()) {
DOMWarning("destination object for connection does not exist",&el);
continue;
}
const Connection* const c = new Connection(insertionOrder++,src,dest,prop,*this);
src_connections.insert(ConnectionMap::value_type(src,c));
dest_connections.insert(ConnectionMap::value_type(dest,c));
}
}
const std::vector<const AnimationStack*>& Document::AnimationStacks() const
{
if (!animationStacksResolved.empty() || !animationStacks.size()) {
return animationStacksResolved;
}
animationStacksResolved.reserve(animationStacks.size());
for(uint64_t id : animationStacks) {
LazyObject* const lazy = GetObject(id);
const AnimationStack* stack;
if(!lazy || !(stack = lazy->Get<AnimationStack>())) {
DOMWarning("failed to read AnimationStack object");
continue;
}
animationStacksResolved.push_back(stack);
}
return animationStacksResolved;
}
LazyObject* Document::GetObject(uint64_t id) const
{
ObjectMap::const_iterator it = objects.find(id);
return it == objects.end() ? NULL : (*it).second;
}
#define MAX_CLASSNAMES 6
std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id,
const ConnectionMap& conns) const
{
std::vector<const Connection*> temp;
const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
conns.equal_range(id);
temp.reserve(std::distance(range.first,range.second));
for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
temp.push_back((*it).second);
}
std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
return temp; }
std::vector<const Connection*> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
const ConnectionMap& conns,
const char* const* classnames,
size_t count) const
{
ai_assert(classnames);
ai_assert(count != 0 && count <= MAX_CLASSNAMES);
size_t lenghts[MAX_CLASSNAMES];
const size_t c = count;
for (size_t i = 0; i < c; ++i) {
lenghts[i] = strlen(classnames[i]);
}
std::vector<const Connection*> temp;
const std::pair<ConnectionMap::const_iterator,ConnectionMap::const_iterator> range =
conns.equal_range(id);
temp.reserve(std::distance(range.first,range.second));
for (ConnectionMap::const_iterator it = range.first; it != range.second; ++it) {
const Token& key = (is_src
? (*it).second->LazyDestinationObject()
: (*it).second->LazySourceObject()
).GetElement().KeyToken();
const char* obtype = key.begin();
for (size_t i = 0; i < c; ++i) {
ai_assert(classnames[i]);
if(static_cast<size_t>(std::distance(key.begin(),key.end())) == lenghts[i] && !strncmp(classnames[i],obtype,lenghts[i])) {
obtype = NULL;
break;
}
}
if(obtype) {
continue;
}
temp.push_back((*it).second);
}
std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
return temp; }
std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source) const
{
return GetConnectionsSequenced(source, ConnectionsBySource());
}
std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t dest,
const char* classname) const
{
const char* arr[] = {classname};
return GetConnectionsBySourceSequenced(dest, arr,1);
}
std::vector<const Connection*> Document::GetConnectionsBySourceSequenced(uint64_t source,
const char* const* classnames, size_t count) const
{
return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count);
}
std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
const char* classname) const
{
const char* arr[] = {classname};
return GetConnectionsByDestinationSequenced(dest, arr,1);
}
std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest) const
{
return GetConnectionsSequenced(dest, ConnectionsByDestination());
}
std::vector<const Connection*> Document::GetConnectionsByDestinationSequenced(uint64_t dest,
const char* const* classnames, size_t count) const
{
return GetConnectionsSequenced(dest, false, ConnectionsByDestination(),classnames, count);
}
Connection::Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string& prop,
const Document& doc)
: insertionOrder(insertionOrder)
, prop(prop)
, src(src)
, dest(dest)
, doc(doc)
{
ai_assert(doc.Objects().find(src) != doc.Objects().end());
ai_assert(!dest || doc.Objects().find(dest) != doc.Objects().end());
}
Connection::~Connection()
{
}
LazyObject& Connection::LazySourceObject() const
{
LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy);
return *lazy;
}
LazyObject& Connection::LazyDestinationObject() const
{
LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy);
return *lazy;
}
const Object* Connection::SourceObject() const
{
LazyObject* const lazy = doc.GetObject(src);
ai_assert(lazy);
return lazy->Get();
}
const Object* Connection::DestinationObject() const
{
LazyObject* const lazy = doc.GetObject(dest);
ai_assert(lazy);
return lazy->Get();
}
} }
#endif