- fbx: add DOM layer to represent the parsed FBX file in-memory, capturing the essential semantics we need for importing.

pull/14/head
Alexander Gessler 2012-06-26 18:31:19 +02:00
parent 05b98c279b
commit 25dfbdf58d
7 changed files with 1961 additions and 1398 deletions

View File

@ -0,0 +1,173 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2012, 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 FBXDocument.cpp
* @brief Implementation of the FBX DOM classes
*/
#include "AssimpPCH.h"
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
#include "FBXParser.h"
#include "FBXDocument.h"
#include "FBXUtil.h"
namespace {
using namespace Assimp;
using namespace Assimp::FBX;
// ------------------------------------------------------------------------------------------------
// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
void DOMError(const std::string& message, Element* element = NULL)
{
throw DeadlyImportError(element ? Util::AddTokenText("FBX-DOM",message,element->KeyToken()) : ("FBX-DOM " + message));
}
}
namespace Assimp {
namespace FBX {
// ------------------------------------------------------------------------------------------------
LazyObject::LazyObject(const Element& element)
: element(element)
{
}
// ------------------------------------------------------------------------------------------------
LazyObject::~LazyObject()
{
}
// ------------------------------------------------------------------------------------------------
const Object* LazyObject::Get()
{
if (object.get()) {
return object.get();
}
// XXX
return NULL;
}
// ------------------------------------------------------------------------------------------------
Object::Object(const Element& element)
: element(element)
{
}
// ------------------------------------------------------------------------------------------------
Object::~Object()
{
}
// ------------------------------------------------------------------------------------------------
Geometry::Geometry(const Element& element)
: Object(element)
{
}
// ------------------------------------------------------------------------------------------------
Geometry::~Geometry()
{
}
// ------------------------------------------------------------------------------------------------
MeshGeometry::MeshGeometry(const Element& element)
: Geometry(element)
{
}
// ------------------------------------------------------------------------------------------------
MeshGeometry::~MeshGeometry()
{
}
// ------------------------------------------------------------------------------------------------
Document::Document(const Parser& parser)
: parser(parser)
{
const Scope& sc = parser.GetRootScope();
const Element* const eobjects = sc["Objects"];
if(!eobjects || !eobjects->Compound()) {
DOMError("no Objects dictionary found");
}
const Scope* const sobjects = eobjects->Compound();
BOOST_FOREACH(const ElementMap::value_type& el, sobjects->Elements()) {
// extract ID
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);
}
objects[id] = new LazyObject(*el.second);
}
}
// ------------------------------------------------------------------------------------------------
Document::~Document()
{
}
} // !FBX
} // !Assimp
#endif

211
code/FBXDocument.h 100644
View File

@ -0,0 +1,211 @@
/*
Open Asset Import Library (assimp)
----------------------------------------------------------------------
Copyright (c) 2006-2012, 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 FBXDocument.h
* @brief FBX DOM
*/
#ifndef INCLUDED_AI_FBX_DOCUMENT_H
#define INCLUDED_AI_FBX_DOCUMENT_H
#include <vector>
#include <map>
#include <string>
namespace Assimp {
namespace FBX {
class Parser;
class Object;
/** Represents a delay-parsed FBX objects. Many objects in the scene
* are not needed by assimp, so it makes no sense to parse them
* upfront. */
class LazyObject
{
public:
LazyObject(const Element& element);
~LazyObject();
public:
const Object* Get();
template <typename T>
T* Get() {
const Object* const ob = Get();
return ob ? dynamic_cast<T*>(ob) : NULL;
}
private:
const Element& element;
boost::scoped_ptr<const Object> object;
};
/** Base class for in-memory (DOM) representations of FBX objects */
class Object
{
public:
Object(const Element& element);
~Object();
public:
protected:
const Element& element;
};
/** DOM base class for all kinds of FBX geometry */
class Geometry : public Object
{
public:
Geometry(const Element& element);
~Geometry();
};
/** DOM class for FBX geometry of type "Mesh"*/
class MeshGeometry : public Geometry
{
public:
MeshGeometry(const Element& element);
~MeshGeometry();
public:
/** Get a list of all vertex points, non-unique*/
const std::vector<aiVector3D>& GetVertices() const {
return vertices;
}
/** Get a list of all vertex normals or an empty array if
* no normals are specified. */
const std::vector<aiVector3D>& GetNormals() const {
return normals;
}
/** Get a list of all vertex tangents or an empty array
* if no tangents are specified */
const std::vector<aiVector3D>& GetTangents() const {
return tangents;
}
/** Return list of faces - each entry denotes a face and specifies
* how many vertices it has. Vertices are taken from the
* vertex data arrays in sequential order. */
const std::vector<unsigned int>& GetFaceIndexCounts() const {
return faces;
}
/** Get a UV coordinate slot, returns an empty array if
* the requested slot does not exist. */
const std::vector<aiVector3D>& GetTextureCoords(unsigned int index) const {
static const std::vector<aiVector3D> empty;
return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : uvs[index];
}
/** Get a vertex color coordinate slot, returns an empty array if
* the requested slot does not exist. */
const std::vector<aiColor4D>& GetVertexColors(unsigned int index) const {
static const std::vector<aiColor4D> empty;
return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : colors[index];
}
/** Get per-face-vertex material assignments */
const std::vector<unsigned int>& GetMaterialIndices() const {
return materials;
}
public:
private:
// cached data arrays
std::vector<unsigned int> materials;
std::vector<aiVector3D> vertices;
std::vector<unsigned int> faces;
std::vector<aiVector3D> tangents;
std::vector<aiVector3D> normals;
std::vector<aiVector3D> uvs[AI_MAX_NUMBER_OF_TEXTURECOORDS];
std::vector<aiColor4D> colors[AI_MAX_NUMBER_OF_COLOR_SETS];
};
// XXX again, unique_ptr would be useful. shared_ptr is too
// bloated since the objects have a well-defined single owner
// during their entire lifetime (Document). FBX files have
// up to many thousands of objects (most of which we never use),
// so the memory overhead for them should be kept at a minimum.
typedef std::map<uint64_t, LazyObject*> ObjectMap;
/** DOM root for a FBX file */
class Document
{
public:
Document(const Parser& parser);
~Document();
public:
const ObjectMap& Objects() const {
return objects;
}
private:
ObjectMap objects;
const Parser& parser;
};
}
}
#endif

View File

@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXTokenizer.h"
#include "FBXParser.h"
#include "FBXUtil.h"
#include "FBXDocument.h"
#include "StreamReader.h"
#include "MemoryIOWrapper.h"
@ -155,6 +156,9 @@ void FBXImporter::InternReadFile( const std::string& pFile,
// use this information to construct a very rudimentary
// parse-tree representing the FBX scope structure
Parser parser(tokens);
// take the raw parse-tree and convert it to a FBX DOM
Document doc(parser);
}
catch(...) {
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());

View File

@ -49,6 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXParser.h"
#include "FBXUtil.h"
#include "ParsingUtils.h"
#include "fast_atof.h"
using namespace Assimp;
using namespace Assimp::FBX;
@ -63,8 +66,12 @@ void ParseError(const std::string& message, TokenPtr token)
}
namespace Assimp {
namespace FBX {
// ------------------------------------------------------------------------------------------------
Element::Element(Parser& parser)
Element::Element(TokenPtr key_token, Parser& parser)
: key_token(key_token)
{
TokenPtr n = NULL;
do {
@ -108,7 +115,7 @@ Element::Element(Parser& parser)
// ------------------------------------------------------------------------------------------------
Element::~Element()
{
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
// no need to delete tokens, they are owned by the parser
}
// ------------------------------------------------------------------------------------------------
@ -133,7 +140,7 @@ Scope::Scope(Parser& parser,bool topLevel)
}
const std::string& str = n->StringContents();
elements.insert(ElementMap::value_type(str,new_Element(parser)));
elements.insert(ElementMap::value_type(str,new_Element(n,parser)));
// Element() should stop at the next Key token (or right after a Close token)
n = parser.CurrentToken();
@ -200,5 +207,138 @@ TokenPtr Parser::LastToken() const
}
// ------------------------------------------------------------------------------------------------
uint64_t ParseTokenAsID(const Token& t, const char*& err_out)
{
err_out = NULL;
if (t.Type() != TokenType_DATA) {
err_out = "expected TOK_DATA token";
return 0L;
}
// XXX: should use size_t here
unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
ai_assert(length > 0);
const char* out;
const uint64_t id = strtoul10_64(t.begin(),&out,&length);
if (out != t.end()) {
err_out = "failed to parse ID";
return 0L;
}
return id;
}
// ------------------------------------------------------------------------------------------------
uint64_t ParseTokenAsDim(const Token& t, const char*& err_out)
{
// same as ID parsing, except there is a trailing asterisk
err_out = NULL;
if (t.Type() != TokenType_DATA) {
err_out = "expected TOK_DATA token";
return 0L;
}
if(*t.begin() != '*') {
err_out = "expected asterisk before array dimension";
return 0L;
}
// XXX: should use size_t here
unsigned int length = static_cast<unsigned int>(t.end() - t.begin());
if(length == 0) {
err_out = "expected valid integer number after asterisk";
return 0L;
}
const char* out;
const uint64_t id = strtoul10_64(t.begin() + 1,&out,&length);
if (out != t.end()) {
err_out = "failed to parse ID";
return 0L;
}
return id;
}
// ------------------------------------------------------------------------------------------------
float ParseTokenAsFloat(const Token& t, const char*& err_out)
{
err_out = NULL;
if (t.Type() != TokenType_DATA) {
err_out = "expected TOK_DATA token";
return 0.0f;
}
const char* inout = t.begin();
float f;
fast_atof(&inout);
if (inout != t.end()) {
err_out = "failed to parse floating point number";
return 0.0f;
}
return f;
}
// ------------------------------------------------------------------------------------------------
int ParseTokenAsInt(const Token& t, const char*& err_out)
{
err_out = NULL;
if (t.Type() != TokenType_DATA) {
err_out = "expected TOK_DATA token";
return 0;
}
ai_assert(static_cast<size_t>(t.end() - t.begin()) > 0);
const char* out;
const int intval = strtol10(t.begin(),&out);
if (out != t.end()) {
err_out = "failed to parse ID";
return 0;
}
return intval;
}
// ------------------------------------------------------------------------------------------------
std::string ParseTokenAsString(const Token& t, const char*& err_out)
{
err_out = NULL;
if (t.Type() != TokenType_DATA) {
err_out = "expected TOK_DATA token";
return "";
}
const size_t length = static_cast<size_t>(t.end() - t.begin());
if(length < 2) {
err_out = "token is too short to hold a string";
return "";
}
const char* s = t.begin(), *e = t.end() - 1;
if (*s != '\"' || *e != '\*') {
err_out = "expected double quoted string";
return "";
}
return std::string(s+1,length-2);
}
} // !FBX
} // !Assimp
#endif

View File

@ -85,17 +85,26 @@ class Element
{
public:
Element(Parser& parser);
Element(TokenPtr key_token, Parser& parser);
~Element();
public:
const Scope* Compound() const {
return compound.get();
}
TokenPtr KeyToken() const {
return key_token;
}
const TokenList& Tokens() const {
return tokens;
}
private:
TokenPtr key_token;
TokenList tokens;
boost::scoped_ptr<Scope> compound;
};
@ -123,6 +132,11 @@ public:
public:
const Element* operator[] (const std::string& index) const {
ElementMap::const_iterator it = elements.find(index);
return it == elements.end() ? NULL : (*it).second;
}
const ElementMap& Elements() const {
return elements;
}
@ -139,6 +153,8 @@ class Parser
{
public:
/** Parse given a token list. Does not take ownership of the tokens -
* the objects must persist during the entire parser lifetime */
Parser (const TokenList& tokens);
~Parser();
@ -168,6 +184,14 @@ private:
};
/* token parsing - this happens when building the DOM out of the parse-tree*/
uint64_t ParseTokenAsID(const Token& t, const char*& err_out);
uint64_t ParseTokenAsDim(const Token& t, const char*& err_out);
float ParseTokenAsFloat(const Token& t, const char*& err_out);
int ParseTokenAsInt(const Token& t, const char*& err_out);
std::string ParseTokenAsString(const Token& t, const char*& err_out);
} // ! FBX
} // ! Assimp

View File

@ -69,6 +69,9 @@ Token::Token(const char* sbegin, const char* send, TokenType type, unsigned int
{
ai_assert(sbegin);
ai_assert(send);
// tokens must be of non-zero length
ai_assert(static_cast<size_t>(send-sbegin) > 0);
}

File diff suppressed because it is too large Load Diff