|
@@ -0,0 +1,448 @@
|
|
|
|
+/*
|
|
|
|
+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 FBXDocumentUtil.cpp
|
|
|
|
+ * @brief Implementation of the FBX DOM utility functions declared in FBXDocumentUtil.h
|
|
|
|
+ */
|
|
|
|
+#include "AssimpPCH.h"
|
|
|
|
+
|
|
|
|
+#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
|
|
|
|
+
|
|
|
|
+#include <functional>
|
|
|
|
+
|
|
|
|
+#include "FBXParser.h"
|
|
|
|
+#include "FBXDocument.h"
|
|
|
|
+#include "FBXUtil.h"
|
|
|
|
+#include "FBXDocumentUtil.h"
|
|
|
|
+#include "FBXProperties.h"
|
|
|
|
+
|
|
|
|
+namespace Assimp {
|
|
|
|
+namespace FBX {
|
|
|
|
+namespace Util {
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// signal DOM construction error, this is always unrecoverable. Throws DeadlyImportError.
|
|
|
|
+void DOMError(const std::string& message, const Token& token)
|
|
|
|
+{
|
|
|
|
+ throw DeadlyImportError(Util::AddTokenText("FBX-DOM",message,&token));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+void DOMError(const std::string& message, const Element* element /*= NULL*/)
|
|
|
|
+{
|
|
|
|
+ if(element) {
|
|
|
|
+ DOMError(message,element->KeyToken());
|
|
|
|
+ }
|
|
|
|
+ throw DeadlyImportError("FBX-DOM " + message);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// print warning, do return
|
|
|
|
+void DOMWarning(const std::string& message, const Token& token)
|
|
|
|
+{
|
|
|
|
+ if(DefaultLogger::get()) {
|
|
|
|
+ DefaultLogger::get()->warn(Util::AddTokenText("FBX-DOM",message,&token));
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+void DOMWarning(const std::string& message, const Element* element /*= NULL*/)
|
|
|
|
+{
|
|
|
|
+ if(element) {
|
|
|
|
+ DOMWarning(message,element->KeyToken());
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ if(DefaultLogger::get()) {
|
|
|
|
+ DefaultLogger::get()->warn("FBX-DOM: " + message);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// extract required compound scope
|
|
|
|
+const Scope& GetRequiredScope(const Element& el)
|
|
|
|
+{
|
|
|
|
+ const Scope* const s = el.Compound();
|
|
|
|
+ if(!s) {
|
|
|
|
+ DOMError("expected compound scope",&el);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return *s;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// get token at a particular index
|
|
|
|
+const Token& GetRequiredToken(const Element& el, unsigned int index)
|
|
|
|
+{
|
|
|
|
+ const TokenList& t = el.Tokens();
|
|
|
|
+ if(index >= t.size()) {
|
|
|
|
+ DOMError(Formatter::format( "missing token at index " ) << index,&el);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return *t[index];
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// wrapper around ParseTokenAsID() with DOMError handling
|
|
|
|
+uint64_t ParseTokenAsID(const Token& t)
|
|
|
|
+{
|
|
|
|
+ const char* err;
|
|
|
|
+ const uint64_t i = ParseTokenAsID(t,err);
|
|
|
|
+ if(err) {
|
|
|
|
+ DOMError(err,t);
|
|
|
|
+ }
|
|
|
|
+ return i;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// wrapper around ParseTokenAsDim() with DOMError handling
|
|
|
|
+size_t ParseTokenAsDim(const Token& t)
|
|
|
|
+{
|
|
|
|
+ const char* err;
|
|
|
|
+ const size_t i = ParseTokenAsDim(t,err);
|
|
|
|
+ if(err) {
|
|
|
|
+ DOMError(err,t);
|
|
|
|
+ }
|
|
|
|
+ return i;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// wrapper around ParseTokenAsFloat() with DOMError handling
|
|
|
|
+float ParseTokenAsFloat(const Token& t)
|
|
|
|
+{
|
|
|
|
+ const char* err;
|
|
|
|
+ const float i = ParseTokenAsFloat(t,err);
|
|
|
|
+ if(err) {
|
|
|
|
+ DOMError(err,t);
|
|
|
|
+ }
|
|
|
|
+ return i;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// wrapper around ParseTokenAsInt() with DOMError handling
|
|
|
|
+int ParseTokenAsInt(const Token& t)
|
|
|
|
+{
|
|
|
|
+ const char* err;
|
|
|
|
+ const int i = ParseTokenAsInt(t,err);
|
|
|
|
+ if(err) {
|
|
|
|
+ DOMError(err,t);
|
|
|
|
+ }
|
|
|
|
+ return i;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// wrapper around ParseTokenAsString() with DOMError handling
|
|
|
|
+std::string ParseTokenAsString(const Token& t)
|
|
|
|
+{
|
|
|
|
+ const char* err;
|
|
|
|
+ const std::string& i = ParseTokenAsString(t,err);
|
|
|
|
+ if(err) {
|
|
|
|
+ DOMError(err,t);
|
|
|
|
+ }
|
|
|
|
+ return i;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// extract a required element from a scope, abort if the element cannot be found
|
|
|
|
+const Element& GetRequiredElement(const Scope& sc, const std::string& index, const Element* element /*= NULL*/)
|
|
|
|
+{
|
|
|
|
+ const Element* el = sc[index];
|
|
|
|
+ if(!el) {
|
|
|
|
+ DOMError("did not find required element \"" + index + "\"",element);
|
|
|
|
+ }
|
|
|
|
+ return *el;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// XXX: tacke code duplication in the various ReadVectorDataArray() overloads below.
|
|
|
|
+// could use a type traits based solution.
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of float3 tuples
|
|
|
|
+void ReadVectorDataArray(std::vector<aiVector3D>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // may throw bad_alloc if the input is rubbish, but this need
|
|
|
|
+ // not to be prevented - importing would fail but we wouldn't
|
|
|
|
+ // crash since assimp handles this case properly.
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ if (a.Tokens().size() % 3 != 0) {
|
|
|
|
+ DOMError("number of floats is not a multiple of three (3)",&el);
|
|
|
|
+ }
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ aiVector3D v;
|
|
|
|
+ v.x = ParseTokenAsFloat(**it++);
|
|
|
|
+ v.y = ParseTokenAsFloat(**it++);
|
|
|
|
+ v.z = ParseTokenAsFloat(**it++);
|
|
|
|
+
|
|
|
|
+ out.push_back(v);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of color4 tuples
|
|
|
|
+void ReadVectorDataArray(std::vector<aiColor4D>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // see notes in ReadVectorDataArray() above
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ if (a.Tokens().size() % 4 != 0) {
|
|
|
|
+ DOMError("number of floats is not a multiple of four (4)",&el);
|
|
|
|
+ }
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ aiColor4D v;
|
|
|
|
+ v.r = ParseTokenAsFloat(**it++);
|
|
|
|
+ v.g = ParseTokenAsFloat(**it++);
|
|
|
|
+ v.b = ParseTokenAsFloat(**it++);
|
|
|
|
+ v.a = ParseTokenAsFloat(**it++);
|
|
|
|
+
|
|
|
|
+ out.push_back(v);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of float2 tuples
|
|
|
|
+void ReadVectorDataArray(std::vector<aiVector2D>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // see notes in ReadVectorDataArray() above
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ if (a.Tokens().size() % 2 != 0) {
|
|
|
|
+ DOMError("number of floats is not a multiple of two (2)",&el);
|
|
|
|
+ }
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ aiVector2D v;
|
|
|
|
+ v.x = ParseTokenAsFloat(**it++);
|
|
|
|
+ v.y = ParseTokenAsFloat(**it++);
|
|
|
|
+
|
|
|
|
+ out.push_back(v);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of ints
|
|
|
|
+void ReadVectorDataArray(std::vector<int>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // see notes in ReadVectorDataArray()
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ const int ival = ParseTokenAsInt(**it++);
|
|
|
|
+ out.push_back(ival);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of floats
|
|
|
|
+void ReadVectorDataArray(std::vector<float>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // see notes in ReadVectorDataArray()
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ const float ival = ParseTokenAsFloat(**it++);
|
|
|
|
+ out.push_back(ival);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of uints
|
|
|
|
+void ReadVectorDataArray(std::vector<unsigned int>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // see notes in ReadVectorDataArray()
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ const int ival = ParseTokenAsInt(**it++);
|
|
|
|
+ if(ival < 0) {
|
|
|
|
+ DOMError("encountered negative integer index");
|
|
|
|
+ }
|
|
|
|
+ out.push_back(static_cast<unsigned int>(ival));
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// read an array of uint64_ts
|
|
|
|
+void ReadVectorDataArray(std::vector<uint64_t>& out, const Element& el)
|
|
|
|
+{
|
|
|
|
+ out.clear();
|
|
|
|
+ const TokenList& tok = el.Tokens();
|
|
|
|
+ const size_t dim = ParseTokenAsDim(*tok[0]);
|
|
|
|
+
|
|
|
|
+ // see notes in ReadVectorDataArray()
|
|
|
|
+ out.reserve(dim);
|
|
|
|
+
|
|
|
|
+ const Scope& scope = GetRequiredScope(el);
|
|
|
|
+ const Element& a = GetRequiredElement(scope,"a",&el);
|
|
|
|
+
|
|
|
|
+ for (TokenList::const_iterator it = a.Tokens().begin(), end = a.Tokens().end(); it != end; ) {
|
|
|
|
+ const uint64_t ival = ParseTokenAsID(**it++);
|
|
|
|
+
|
|
|
|
+ out.push_back(ival);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+aiMatrix4x4 ReadMatrix(const Element& element)
|
|
|
|
+{
|
|
|
|
+ std::vector<float> values;
|
|
|
|
+ ReadVectorDataArray(values,element);
|
|
|
|
+
|
|
|
|
+ if(values.size() != 16) {
|
|
|
|
+ DOMError("expected 16 matrix elements");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ aiMatrix4x4 result;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ result.a1 = values[0];
|
|
|
|
+ result.a2 = values[1];
|
|
|
|
+ result.a3 = values[2];
|
|
|
|
+ result.a4 = values[3];
|
|
|
|
+
|
|
|
|
+ result.b1 = values[4];
|
|
|
|
+ result.b2 = values[5];
|
|
|
|
+ result.b3 = values[6];
|
|
|
|
+ result.b4 = values[7];
|
|
|
|
+
|
|
|
|
+ result.c1 = values[8];
|
|
|
|
+ result.c2 = values[9];
|
|
|
|
+ result.c3 = values[10];
|
|
|
|
+ result.c4 = values[11];
|
|
|
|
+
|
|
|
|
+ result.d1 = values[12];
|
|
|
|
+ result.d2 = values[13];
|
|
|
|
+ result.d3 = values[14];
|
|
|
|
+ result.d4 = values[15];
|
|
|
|
+
|
|
|
|
+ result.Transpose();
|
|
|
|
+ return result;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+// ------------------------------------------------------------------------------------------------
|
|
|
|
+// fetch a property table and the corresponding property template
|
|
|
|
+boost::shared_ptr<const PropertyTable> GetPropertyTable(const Document& doc,
|
|
|
|
+ const std::string& templateName,
|
|
|
|
+ const Element &element,
|
|
|
|
+ const Scope& sc)
|
|
|
|
+{
|
|
|
|
+ const Element* const Properties70 = sc["Properties70"];
|
|
|
|
+ boost::shared_ptr<const PropertyTable> templateProps = boost::shared_ptr<const PropertyTable>(
|
|
|
|
+ static_cast<const PropertyTable*>(NULL));
|
|
|
|
+
|
|
|
|
+ if(templateName.length()) {
|
|
|
|
+ PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
|
|
|
|
+ if(it != doc.Templates().end()) {
|
|
|
|
+ templateProps = (*it).second;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(!Properties70) {
|
|
|
|
+ DOMWarning("material property table (Properties70) not found",&element);
|
|
|
|
+ if(templateProps) {
|
|
|
|
+ return templateProps;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ return boost::make_shared<const PropertyTable>();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return boost::make_shared<const PropertyTable>(*Properties70,templateProps);
|
|
|
|
+}
|
|
|
|
+} // !Util
|
|
|
|
+} // !FBX
|
|
|
|
+} // !Assimp
|
|
|
|
+
|
|
|
|
+#endif
|