Browse Source

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

Alexander Gessler 13 years ago
parent
commit
25dfbdf58d
7 changed files with 776 additions and 213 deletions
  1. 173 0
      code/FBXDocument.cpp
  2. 211 0
      code/FBXDocument.h
  3. 4 0
      code/FBXImporter.cpp
  4. 143 3
      code/FBXParser.cpp
  5. 25 1
      code/FBXParser.h
  6. 3 0
      code/FBXTokenizer.cpp
  7. 217 209
      workspaces/vc9/assimp.vcproj

+ 173 - 0
code/FBXDocument.cpp

@@ -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 - 0
code/FBXDocument.h

@@ -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

+ 4 - 0
code/FBXImporter.cpp

@@ -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>());

+ 143 - 3
code/FBXParser.cpp

@@ -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
 

+ 25 - 1
code/FBXParser.h

@@ -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
 

+ 3 - 0
code/FBXTokenizer.cpp

@@ -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
+ 217 - 209
workspaces/vc9/assimp.vcproj


Some files were not shown because too many files changed in this diff