summaryrefslogtreecommitdiff
path: root/modules/fbx/fbx_parser
diff options
context:
space:
mode:
Diffstat (limited to 'modules/fbx/fbx_parser')
-rw-r--r--modules/fbx/fbx_parser/ByteSwapper.h282
-rw-r--r--modules/fbx/fbx_parser/CREDITS183
-rw-r--r--modules/fbx/fbx_parser/FBXAnimation.cpp290
-rw-r--r--modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp467
-rw-r--r--modules/fbx/fbx_parser/FBXCommon.h110
-rw-r--r--modules/fbx/fbx_parser/FBXDeformer.cpp279
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.cpp713
-rw-r--r--modules/fbx/fbx_parser/FBXDocument.h1319
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.cpp172
-rw-r--r--modules/fbx/fbx_parser/FBXDocumentUtil.h141
-rw-r--r--modules/fbx/fbx_parser/FBXImportSettings.h173
-rw-r--r--modules/fbx/fbx_parser/FBXMaterial.cpp407
-rw-r--r--modules/fbx/fbx_parser/FBXMeshGeometry.cpp485
-rw-r--r--modules/fbx/fbx_parser/FBXMeshGeometry.h263
-rw-r--r--modules/fbx/fbx_parser/FBXModel.cpp176
-rw-r--r--modules/fbx/fbx_parser/FBXNodeAttribute.cpp183
-rw-r--r--modules/fbx/fbx_parser/FBXParseTools.h111
-rw-r--r--modules/fbx/fbx_parser/FBXParser.cpp1295
-rw-r--r--modules/fbx/fbx_parser/FBXParser.h263
-rw-r--r--modules/fbx/fbx_parser/FBXPose.cpp104
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.cpp237
-rw-r--r--modules/fbx/fbx_parser/FBXProperties.h221
-rw-r--r--modules/fbx/fbx_parser/FBXTokenizer.cpp251
-rw-r--r--modules/fbx/fbx_parser/FBXTokenizer.h203
-rw-r--r--modules/fbx/fbx_parser/FBXUtil.cpp220
-rw-r--r--modules/fbx/fbx_parser/FBXUtil.h122
-rw-r--r--modules/fbx/fbx_parser/LICENSE39
27 files changed, 8709 insertions, 0 deletions
diff --git a/modules/fbx/fbx_parser/ByteSwapper.h b/modules/fbx/fbx_parser/ByteSwapper.h
new file mode 100644
index 0000000000..0a46a7df26
--- /dev/null
+++ b/modules/fbx/fbx_parser/ByteSwapper.h
@@ -0,0 +1,282 @@
+/*************************************************************************/
+/* ByteSwapper.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, 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 Helper class tp perform various byte oder swappings
+ (e.g. little to big endian) */
+#ifndef BYTE_SWAPPER_H
+#define BYTE_SWAPPER_H
+
+#include <stdint.h>
+#include <algorithm>
+#include <locale>
+
+namespace FBXDocParser {
+// --------------------------------------------------------------------------------------
+/** Defines some useful byte order swap routines.
+ *
+ * This is required to read big-endian model formats on little-endian machines,
+ * and vice versa. Direct use of this class is DEPRECATED. Use #StreamReader instead. */
+// --------------------------------------------------------------------------------------
+class ByteSwap {
+ ByteSwap() {}
+
+public:
+ // ----------------------------------------------------------------------
+ /** Swap two bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap2(void *_szOut) {
+ uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+ std::swap(szOut[0], szOut[1]);
+ }
+
+ // ----------------------------------------------------------------------
+ /** Swap four bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap4(void *_szOut) {
+ uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+ std::swap(szOut[0], szOut[3]);
+ std::swap(szOut[1], szOut[2]);
+ }
+
+ // ----------------------------------------------------------------------
+ /** Swap eight bytes of data
+ * @param[inout] _szOut A void* to save the reintcasts for the caller. */
+ static inline void Swap8(void *_szOut) {
+ uint8_t *const szOut = reinterpret_cast<uint8_t *>(_szOut);
+ std::swap(szOut[0], szOut[7]);
+ std::swap(szOut[1], szOut[6]);
+ std::swap(szOut[2], szOut[5]);
+ std::swap(szOut[3], szOut[4]);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap a float. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(float *fOut) {
+ Swap4(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap a double. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(double *fOut) {
+ Swap8(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap an int16t. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(int16_t *fOut) {
+ Swap2(fOut);
+ }
+
+ static inline void Swap(uint16_t *fOut) {
+ Swap2(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap an int32t. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(int32_t *fOut) {
+ Swap4(fOut);
+ }
+
+ static inline void Swap(uint32_t *fOut) {
+ Swap4(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ /** ByteSwap an int64t. Not a joke.
+ * @param[inout] fOut ehm. .. */
+ static inline void Swap(int64_t *fOut) {
+ Swap8(fOut);
+ }
+
+ static inline void Swap(uint64_t *fOut) {
+ Swap8(fOut);
+ }
+
+ // ----------------------------------------------------------------------
+ //! Templatized ByteSwap
+ //! \returns param tOut as swapped
+ template <typename Type>
+ static inline Type Swapped(Type tOut) {
+ return _swapper<Type, sizeof(Type)>()(tOut);
+ }
+
+private:
+ template <typename T, size_t size>
+ struct _swapper;
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 2> {
+ T operator()(T tOut) {
+ Swap2(&tOut);
+ return tOut;
+ }
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 4> {
+ T operator()(T tOut) {
+ Swap4(&tOut);
+ return tOut;
+ }
+};
+
+template <typename T>
+struct ByteSwap::_swapper<T, 8> {
+ T operator()(T tOut) {
+ Swap8(&tOut);
+ return tOut;
+ }
+};
+
+// --------------------------------------------------------------------------------------
+// ByteSwap macros for BigEndian/LittleEndian support
+// --------------------------------------------------------------------------------------
+#if (defined AI_BUILD_BIG_ENDIAN)
+#define AI_LE(t) (t)
+#define AI_BE(t) ByteSwap::Swapped(t)
+#define AI_LSWAP2(p)
+#define AI_LSWAP4(p)
+#define AI_LSWAP8(p)
+#define AI_LSWAP2P(p)
+#define AI_LSWAP4P(p)
+#define AI_LSWAP8P(p)
+#define LE_NCONST const
+#define AI_SWAP2(p) ByteSwap::Swap2(&(p))
+#define AI_SWAP4(p) ByteSwap::Swap4(&(p))
+#define AI_SWAP8(p) ByteSwap::Swap8(&(p))
+#define AI_SWAP2P(p) ByteSwap::Swap2((p))
+#define AI_SWAP4P(p) ByteSwap::Swap4((p))
+#define AI_SWAP8P(p) ByteSwap::Swap8((p))
+#define BE_NCONST
+#else
+#define AI_BE(t) (t)
+#define AI_LE(t) ByteSwap::Swapped(t)
+#define AI_SWAP2(p)
+#define AI_SWAP4(p)
+#define AI_SWAP8(p)
+#define AI_SWAP2P(p)
+#define AI_SWAP4P(p)
+#define AI_SWAP8P(p)
+#define BE_NCONST const
+#define AI_LSWAP2(p) ByteSwap::Swap2(&(p))
+#define AI_LSWAP4(p) ByteSwap::Swap4(&(p))
+#define AI_LSWAP8(p) ByteSwap::Swap8(&(p))
+#define AI_LSWAP2P(p) ByteSwap::Swap2((p))
+#define AI_LSWAP4P(p) ByteSwap::Swap4((p))
+#define AI_LSWAP8P(p) ByteSwap::Swap8((p))
+#define LE_NCONST
+#endif
+
+namespace Intern {
+
+// --------------------------------------------------------------------------------------------
+template <typename T, bool doit>
+struct ByteSwapper {
+ void operator()(T *inout) {
+ ByteSwap::Swap(inout);
+ }
+};
+
+template <typename T>
+struct ByteSwapper<T, false> {
+ void operator()(T *) {
+ }
+};
+
+// --------------------------------------------------------------------------------------------
+template <bool SwapEndianess, typename T, bool RuntimeSwitch>
+struct Getter {
+ void operator()(T *inout, bool le) {
+ le = !le;
+ if (le) {
+ ByteSwapper<T, (sizeof(T) > 1 ? true : false)>()(inout);
+ } else
+ ByteSwapper<T, false>()(inout);
+ }
+};
+
+template <bool SwapEndianess, typename T>
+struct Getter<SwapEndianess, T, false> {
+ void operator()(T *inout, bool /*le*/) {
+ // static branch
+ ByteSwapper<T, (SwapEndianess && sizeof(T) > 1)>()(inout);
+ }
+};
+} // namespace Intern
+} // namespace FBXDocParser
+
+#endif // BYTE_SWAPPER_H
diff --git a/modules/fbx/fbx_parser/CREDITS b/modules/fbx/fbx_parser/CREDITS
new file mode 100644
index 0000000000..62b449614e
--- /dev/null
+++ b/modules/fbx/fbx_parser/CREDITS
@@ -0,0 +1,183 @@
+===============================================================
+Open Asset Import Library (Assimp)
+Developers and Contributors
+===============================================================
+
+The following is a non-exhaustive list of all constributors over the years.
+If you think your name should be listed here, drop us a line and we'll add you.
+
+- Alexander Gessler,
+3DS-, BLEND-, ASE-, DXF-, HMP-, MDL-, MD2-, MD3-, MD5-, MDC-, NFF-, PLY-, STL-, RAW-, OFF-, MS3D-, Q3D- and LWO-Loader, Assimp-Viewer, assimp-cmd, -noboost, Website (Design).
+
+- Thomas Schulze,
+X-, Collada-, BVH-Loader, Postprocessing framework. Data structure & Interface design, documentation.
+
+- Kim Kulling,
+Obj-, Q3BSD-, OpenGEX-Loader, Logging system, CMake-build-environment, Linux-build, Website ( Admin ), Coverity ( Admin ), Glitter ( Admin ).
+
+- R.Schmidt,
+Linux build, eclipse support.
+
+- Matthias Gubisch,
+Assimp.net
+Visual Studio 9 support, bugfixes.
+
+- Mark Sibly
+B3D-Loader, Assimp testing
+
+- Jonathan Klein
+Ogre Loader, VC2010 fixes and CMake fixes.
+
+- Sebastian Hempel,
+PyAssimp (first version)
+Compile-Bugfixes for mingw, add environment for static library support in make.
+
+- Jonathan Pokrass
+Supplied a bugfix concerning the scaling in the md3 loader.
+
+- Andrew Galante,
+Submitted patches to make Assimp compile with GCC-4, a makefile and the xcode3 workspace.
+
+- Andreas Nagel
+First Assimp testing & verification under Windows Vista 64 Bit.
+
+- Marius Schr�der
+Allowed us to use many of his models for screenshots and testing.
+
+- Christian Schubert
+Supplied various XFiles for testing purposes.
+
+- Tizian Wieland
+Searched the web for hundreds of test models for internal use
+
+- John Connors
+Supplied patches for linux and SCons.
+
+- T. R.
+The GUY who performed some of the CSM mocaps.
+
+- Andy Maloney
+Contributed fixes for the documentation and the doxygen markup
+
+- Zhao Lei
+Contributed several bugfixes fixing memory leaks and improving float parsing
+
+- sueastside
+Updated PyAssimp to the latest Assimp data structures and provided a script to keep the Python binding up-to-date.
+
+- Tobias Rittig
+Collada testing with Cinema 4D
+
+- Brad Grantham
+Improvements in OpenGL-Sample.
+
+- Robert Ramirez
+Add group loading feature to Obj-Loader.
+
+- Chris Maiwald
+Many bugreports, improving Assimp's portability, regular testing & feedback.
+
+- Stepan Hrbek
+Bugreport and fix for a obj-materialloader crash.
+
+- David Nadlinger
+D bindings, CMake install support.
+
+- Dario Accornero
+Contributed several patches regarding Mac OS/XCode targets, bug reports.
+
+- Martin Walser (Samhayne)
+Contributed the 'SimpleTexturedOpenGl' sample.
+
+- Matthias Fauconneau
+Contributed a fix for the Q3-BSP loader.
+
+- Jørgen P. Tjernø
+Contributed updated and improved xcode workspaces
+
+- drparallax
+Contributed the /samples/SimpleAssimpViewX sample
+
+- Carsten Fuchs
+Contributed a fix for the Normalize method in aiQuaternion.
+
+- dbburgess
+Contributes a Android-specific build issue: log the hardware architecture for ARM.
+
+- alfiereinre7
+Contributes a obj-fileparser fix: missing tokens in the obj-token list.
+
+- Roman Kharitonov
+Contributes a fix for the configure script environment.
+
+- Ed Diana
+Contributed AssimpDelphi (/port/AssimpDelphi).
+
+- rdb
+Contributes a bundle of fixes and improvements for the bsp-importer.
+
+- Mick P
+For contributing the De-bone postprocessing step and filing various bug reports.
+
+- Rosen Diankov
+Contributed patches to build assimp debian packages using cmake.
+
+- Mark Page
+Contributed a patch to fix the VertexTriangleAdjacency postprocessing step.
+
+- IOhannes
+Contributed the Debian build fixes ( architecture macro ).
+
+- gellule
+Several LWO and LWS fixes (pivoting).
+
+- Marcel Metz
+GCC/Linux fixes for the SimpleOpenGL sample.
+
+- Brian Miller
+Bugfix for a compiler fix for iOS on arm.
+
+- Séverin Lemaignan
+Rewrite of PyAssimp, distutils and Python3 support
+
+- albert-wang
+Bugfixes for the collada parser
+
+- Ya ping Jin
+Bugfixes for uv-tanget calculation.
+
+- Jonne Nauha
+Ogre Binary format support
+
+- Filip Wasil, Tieto Poland Sp. z o.o.
+Android JNI asset extraction support
+
+- Richard Steffen
+Contributed ExportProperties interface
+Contributed X File exporter
+Contributed Step (stp) exporter
+
+- Thomas Iorns (mesilliac)
+Initial FBX Export support
+
+For a more detailed list just check: https://github.com/assimp/assimp/network/members
+
+
+========
+Patreons
+========
+
+Huge thanks to our Patreons!
+
+- migenius
+- Marcus
+- Cort
+- elect
+- Steffen
+
+
+===================
+Commercial Sponsors
+===================
+
+- MyDidimo (mydidimo.com): Sponsored development of FBX Export support
diff --git a/modules/fbx/fbx_parser/FBXAnimation.cpp b/modules/fbx/fbx_parser/FBXAnimation.cpp
new file mode 100644
index 0000000000..bfd3e97314
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXAnimation.cpp
@@ -0,0 +1,290 @@
+/*************************************************************************/
+/* FBXAnimation.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXAnimation.cpp
+ * @brief Assimp::FBX::AnimationCurve, Assimp::FBX::AnimationCurveNode,
+ * Assimp::FBX::AnimationLayer, Assimp::FBX::AnimationStack
+ */
+
+#include "FBXCommon.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document & /*doc*/) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr KeyTime = GetRequiredElement(sc, "KeyTime");
+ const ElementPtr KeyValueFloat = GetRequiredElement(sc, "KeyValueFloat");
+
+ // note preserved keys and values for legacy FBXConverter.cpp
+ // we can remove this once the animation system is written
+ // and clean up this code so we do not have copies everywhere.
+ ParseVectorDataArray(keys, KeyTime);
+ ParseVectorDataArray(values, KeyValueFloat);
+
+ if (keys.size() != values.size()) {
+ DOMError("the number of key times does not match the number of keyframe values", KeyTime);
+ }
+
+ // put the two lists into the map, underlying container is really just a dictionary
+ // these will always match, if not an error will throw and the file will not import
+ // this is useful because we then can report something and fix this later if it becomes an issue
+ // at this point we do not need a different count of these elements so this makes the
+ // most sense to do.
+ for (size_t x = 0; x < keys.size(); x++) {
+ keyvalues[keys[x]] = values[x];
+ }
+
+ const ElementPtr KeyAttrDataFloat = sc->GetElement("KeyAttrDataFloat");
+ if (KeyAttrDataFloat) {
+ ParseVectorDataArray(attributes, KeyAttrDataFloat);
+ }
+
+ const ElementPtr KeyAttrFlags = sc->GetElement("KeyAttrFlags");
+ if (KeyAttrFlags) {
+ ParseVectorDataArray(flags, KeyAttrFlags);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurve::~AnimationCurve() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name,
+ const Document &doc, const char *const *target_prop_whitelist /*= NULL*/,
+ size_t whitelist_size /*= 0*/) :
+ Object(id, element, name), target(), doc(doc) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // find target node
+ const char *whitelist[] = { "Model", "NodeAttribute", "Deformer" };
+ const std::vector<const Connection *> &conns = doc.GetConnectionsBySourceSequenced(ID(), whitelist, 3);
+
+ for (const Connection *con : conns) {
+ // link should go for a property
+ if (!con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *object = con->DestinationObject();
+
+ if (!object) {
+ DOMWarning("failed to read destination object for AnimationCurveNode->Model link, ignoring", element);
+ continue;
+ }
+
+ target = object;
+ prop = con->PropertyName();
+ break;
+ }
+
+ props = GetPropertyTable(doc, "AnimationCurveNode.FbxAnimCurveNode", element, sc, false);
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationCurveNode::~AnimationCurveNode() {
+ curves.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationMap &AnimationCurveNode::Curves() const {
+ /* Lazy loaded animation curves, will only load if required */
+ if (curves.empty()) {
+ // resolve attached animation curves
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurve");
+
+ for (const Connection *con : conns) {
+ // So the advantage of having this STL boilerplate is that it's dead simple once you get it.
+ // The other advantage is casting is guaranteed to be safe and nullptr will be returned in the last step if it fails.
+ Object *ob = con->SourceObject();
+ AnimationCurve *anim_curve = dynamic_cast<AnimationCurve *>(ob);
+ ERR_CONTINUE_MSG(!anim_curve, "Failed to convert animation curve from object");
+
+ curves.insert(std::make_pair(con->PropertyName(), anim_curve));
+ }
+ }
+
+ return curves;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Object(id, element, name), doc(doc) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // note: the props table here bears little importance and is usually absent
+ props = GetPropertyTable(doc, "AnimationLayer.FbxAnimLayer", element, sc, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationLayer::~AnimationLayer() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const AnimationCurveNodeList AnimationLayer::Nodes(const char *const *target_prop_whitelist,
+ size_t whitelist_size /*= 0*/) const {
+ AnimationCurveNodeList nodes;
+
+ // resolve attached animation nodes
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationCurveNode");
+ nodes.reserve(conns.size());
+
+ for (const Connection *con : conns) {
+ // link should not go to a property
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *ob = con->SourceObject();
+
+ if (!ob) {
+ DOMWarning("failed to read source object for AnimationCurveNode->AnimationLayer link, ignoring", element);
+ continue;
+ }
+
+ const AnimationCurveNode *anim = dynamic_cast<AnimationCurveNode *>(ob);
+ if (!anim) {
+ DOMWarning("source object for ->AnimationLayer link is not an AnimationCurveNode", element);
+ continue;
+ }
+
+ if (target_prop_whitelist) {
+ const char *s = anim->TargetProperty().c_str();
+ bool ok = false;
+ for (size_t i = 0; i < whitelist_size; ++i) {
+ if (!strcmp(s, target_prop_whitelist[i])) {
+ ok = true;
+ break;
+ }
+ }
+ if (!ok) {
+ continue;
+ }
+ }
+ nodes.push_back(anim);
+ }
+
+ return nodes;
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // note: we don't currently use any of these properties so we shouldn't bother if it is missing
+ props = GetPropertyTable(doc, "AnimationStack.FbxAnimStack", element, sc, true);
+
+ // resolve attached animation layers
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "AnimationLayer");
+ layers.reserve(conns.size());
+
+ for (const Connection *con : conns) {
+ // link should not go to a property
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for AnimationLayer->AnimationStack link, ignoring", element);
+ continue;
+ }
+
+ const AnimationLayer *anim = dynamic_cast<const AnimationLayer *>(ob);
+
+ if (!anim) {
+ DOMWarning("source object for ->AnimationStack link is not an AnimationLayer", element);
+ continue;
+ }
+
+ layers.push_back(anim);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+AnimationStack::~AnimationStack() {
+ if (props != nullptr) {
+ delete props;
+ props = nullptr;
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
new file mode 100644
index 0000000000..0fc66b13cd
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXBinaryTokenizer.cpp
@@ -0,0 +1,467 @@
+/*************************************************************************/
+/* FBXBinaryTokenizer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXBinaryTokenizer.cpp
+ * @brief Implementation of a fake lexer for binary fbx files -
+ * we emit tokens so the parser needs almost no special handling
+ * for binary files.
+ */
+
+#include "ByteSwapper.h"
+#include "FBXTokenizer.h"
+#include "core/string/print_string.h"
+
+#include <stdint.h>
+
+namespace FBXDocParser {
+//enum Flag
+//{
+// e_unknown_0 = 1 << 0,
+// e_unknown_1 = 1 << 1,
+// e_unknown_2 = 1 << 2,
+// e_unknown_3 = 1 << 3,
+// e_unknown_4 = 1 << 4,
+// e_unknown_5 = 1 << 5,
+// e_unknown_6 = 1 << 6,
+// e_unknown_7 = 1 << 7,
+// e_unknown_8 = 1 << 8,
+// e_unknown_9 = 1 << 9,
+// e_unknown_10 = 1 << 10,
+// e_unknown_11 = 1 << 11,
+// e_unknown_12 = 1 << 12,
+// e_unknown_13 = 1 << 13,
+// e_unknown_14 = 1 << 14,
+// e_unknown_15 = 1 << 15,
+// e_unknown_16 = 1 << 16,
+// e_unknown_17 = 1 << 17,
+// e_unknown_18 = 1 << 18,
+// e_unknown_19 = 1 << 19,
+// e_unknown_20 = 1 << 20,
+// e_unknown_21 = 1 << 21,
+// e_unknown_22 = 1 << 22,
+// e_unknown_23 = 1 << 23,
+// e_flag_field_size_64_bit = 1 << 24, // Not sure what is
+// e_unknown_25 = 1 << 25,
+// e_unknown_26 = 1 << 26,
+// e_unknown_27 = 1 << 27,
+// e_unknown_28 = 1 << 28,
+// e_unknown_29 = 1 << 29,
+// e_unknown_30 = 1 << 30,
+// e_unknown_31 = 1 << 31
+//};
+//
+//bool check_flag(uint32_t flags, Flag to_check)
+//{
+// return (flags & to_check) != 0;
+//}
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char *sbegin, const char *send, TokenType type, size_t offset) :
+ sbegin(sbegin),
+ send(send),
+ type(type),
+ line(offset),
+ column(BINARY_MARKER) {
+#ifdef DEBUG_ENABLED
+ contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
+#endif
+ // calc length
+ // measure from sBegin to sEnd and validate?
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// signal tokenization error
+void TokenizeError(const std::string &message, size_t offset) {
+ print_error("[FBX-Tokenize] " + String(message.c_str()) + ", offset " + itos(offset));
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t Offset(const char *begin, const char *cursor) {
+ //ai_assert(begin <= cursor);
+
+ return cursor - begin;
+}
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string &message, const char *begin, const char *cursor) {
+ TokenizeError(message, Offset(begin, cursor));
+}
+
+// ------------------------------------------------------------------------------------------------
+uint32_t ReadWord(const char *input, const char *&cursor, const char *end) {
+ const size_t k_to_read = sizeof(uint32_t);
+ if (Offset(cursor, end) < k_to_read) {
+ TokenizeError("cannot ReadWord, out of bounds", input, cursor);
+ }
+
+ uint32_t word;
+ ::memcpy(&word, cursor, 4);
+ AI_SWAP4(word);
+
+ cursor += k_to_read;
+
+ return word;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ReadDoubleWord(const char *input, const char *&cursor, const char *end) {
+ const size_t k_to_read = sizeof(uint64_t);
+ if (Offset(cursor, end) < k_to_read) {
+ TokenizeError("cannot ReadDoubleWord, out of bounds", input, cursor);
+ }
+
+ uint64_t dword /*= *reinterpret_cast<const uint64_t*>(cursor)*/;
+ ::memcpy(&dword, cursor, sizeof(uint64_t));
+ AI_SWAP8(dword);
+
+ cursor += k_to_read;
+
+ return dword;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint8_t ReadByte(const char *input, const char *&cursor, const char *end) {
+ if (Offset(cursor, end) < sizeof(uint8_t)) {
+ TokenizeError("cannot ReadByte, out of bounds", input, cursor);
+ }
+
+ uint8_t word; /* = *reinterpret_cast< const uint8_t* >( cursor )*/
+ ::memcpy(&word, cursor, sizeof(uint8_t));
+ ++cursor;
+
+ return word;
+}
+
+// ------------------------------------------------------------------------------------------------
+unsigned int ReadString(const char *&sbegin_out, const char *&send_out, const char *input,
+ const char *&cursor, const char *end, bool long_length = false, bool allow_null = false) {
+ const uint32_t len_len = long_length ? 4 : 1;
+ if (Offset(cursor, end) < len_len) {
+ TokenizeError("cannot ReadString, out of bounds reading length", input, cursor);
+ }
+
+ const uint32_t length = long_length ? ReadWord(input, cursor, end) : ReadByte(input, cursor, end);
+
+ if (Offset(cursor, end) < length) {
+ TokenizeError("cannot ReadString, length is out of bounds", input, cursor);
+ }
+
+ sbegin_out = cursor;
+ cursor += length;
+
+ send_out = cursor;
+
+ if (!allow_null) {
+ for (unsigned int i = 0; i < length; ++i) {
+ if (sbegin_out[i] == '\0') {
+ TokenizeError("failed ReadString, unexpected NUL character in string", input, cursor);
+ }
+ }
+ }
+
+ return length;
+}
+
+// ------------------------------------------------------------------------------------------------
+void ReadData(const char *&sbegin_out, const char *&send_out, const char *input, const char *&cursor, const char *end) {
+ if (Offset(cursor, end) < 1) {
+ TokenizeError("cannot ReadData, out of bounds reading length", input, cursor);
+ }
+
+ const char type = *cursor;
+ sbegin_out = cursor++;
+
+ switch (type) {
+ // 16 bit int
+ case 'Y':
+ cursor += 2;
+ break;
+
+ // 1 bit bool flag (yes/no)
+ case 'C':
+ cursor += 1;
+ break;
+
+ // 32 bit int
+ case 'I':
+ // <- fall through
+
+ // float
+ case 'F':
+ cursor += 4;
+ break;
+
+ // double
+ case 'D':
+ cursor += 8;
+ break;
+
+ // 64 bit int
+ case 'L':
+ cursor += 8;
+ break;
+
+ // note: do not write cursor += ReadWord(...cursor) as this would be UB
+
+ // raw binary data
+ case 'R': {
+ const uint32_t length = ReadWord(input, cursor, end);
+ cursor += length;
+ break;
+ }
+
+ case 'b':
+ // TODO: what is the 'b' type code? Right now we just skip over it /
+ // take the full range we could get
+ cursor = end;
+ break;
+
+ // array of *
+ case 'f':
+ case 'd':
+ case 'l':
+ case 'i':
+ case 'c': {
+ const uint32_t length = ReadWord(input, cursor, end);
+ const uint32_t encoding = ReadWord(input, cursor, end);
+
+ const uint32_t comp_len = ReadWord(input, cursor, end);
+
+ // compute length based on type and check against the stored value
+ if (encoding == 0) {
+ uint32_t stride = 0;
+ switch (type) {
+ case 'f':
+ case 'i':
+ stride = 4;
+ break;
+
+ case 'd':
+ case 'l':
+ stride = 8;
+ break;
+
+ case 'c':
+ stride = 1;
+ break;
+
+ default:
+ break;
+ };
+ //ai_assert(stride > 0);
+ if (length * stride != comp_len) {
+ TokenizeError("cannot ReadData, calculated data stride differs from what the file claims", input, cursor);
+ }
+ }
+ // zip/deflate algorithm (encoding==1)? take given length. anything else? die
+ else if (encoding != 1) {
+ TokenizeError("cannot ReadData, unknown encoding", input, cursor);
+ }
+ cursor += comp_len;
+ break;
+ }
+
+ // string
+ case 'S': {
+ const char *sb, *se;
+ // 0 characters can legally happen in such strings
+ ReadString(sb, se, input, cursor, end, true, true);
+ break;
+ }
+ default:
+ TokenizeError("cannot ReadData, unexpected type code: " + std::string(&type, 1), input, cursor);
+ }
+
+ if (cursor > end) {
+ TokenizeError("cannot ReadData, the remaining size is too small for the data type: " + std::string(&type, 1), input, cursor);
+ }
+
+ // the type code is contained in the returned range
+ send_out = cursor;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool ReadScope(TokenList &output_tokens, const char *input, const char *&cursor, const char *end, bool const is64bits) {
+ // the first word contains the offset at which this block ends
+ const uint64_t end_offset = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+ // we may get 0 if reading reached the end of the file -
+ // fbx files have a mysterious extra footer which I don't know
+ // how to extract any information from, but at least it always
+ // starts with a 0.
+ if (!end_offset) {
+ return false;
+ }
+
+ if (end_offset > Offset(input, end)) {
+ TokenizeError("block offset is out of range", input, cursor);
+ } else if (end_offset < Offset(input, cursor)) {
+ TokenizeError("block offset is negative out of range", input, cursor);
+ }
+
+ // the second data word contains the number of properties in the scope
+ const uint64_t prop_count = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+ // the third data word contains the length of the property list
+ const uint64_t prop_length = is64bits ? ReadDoubleWord(input, cursor, end) : ReadWord(input, cursor, end);
+
+ // now comes the name of the scope/key
+ const char *sbeg, *send;
+ ReadString(sbeg, send, input, cursor, end);
+
+ output_tokens.push_back(new_Token(sbeg, send, TokenType_KEY, Offset(input, cursor)));
+
+ // now come the individual properties
+ const char *begin_cursor = cursor;
+ for (unsigned int i = 0; i < prop_count; ++i) {
+ ReadData(sbeg, send, input, cursor, begin_cursor + prop_length);
+
+ output_tokens.push_back(new_Token(sbeg, send, TokenType_DATA, Offset(input, cursor)));
+
+ if (i != prop_count - 1) {
+ output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_COMMA, Offset(input, cursor)));
+ }
+ }
+
+ if (Offset(begin_cursor, cursor) != prop_length) {
+ TokenizeError("property length not reached, something is wrong", input, cursor);
+ }
+
+ // at the end of each nested block, there is a NUL record to indicate
+ // that the sub-scope exists (i.e. to distinguish between P: and P : {})
+ // this NUL record is 13 bytes long on 32 bit version and 25 bytes long on 64 bit.
+ const size_t sentinel_block_length = is64bits ? (sizeof(uint64_t) * 3 + 1) : (sizeof(uint32_t) * 3 + 1);
+
+ if (Offset(input, cursor) < end_offset) {
+ if (end_offset - Offset(input, cursor) < sentinel_block_length) {
+ TokenizeError("insufficient padding bytes at block end", input, cursor);
+ }
+
+ output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_OPEN_BRACKET, Offset(input, cursor)));
+
+ // XXX this is vulnerable to stack overflowing ..
+ while (Offset(input, cursor) < end_offset - sentinel_block_length) {
+ ReadScope(output_tokens, input, cursor, input + end_offset - sentinel_block_length, is64bits);
+ }
+ output_tokens.push_back(new_Token(cursor, cursor + 1, TokenType_CLOSE_BRACKET, Offset(input, cursor)));
+
+ for (unsigned int i = 0; i < sentinel_block_length; ++i) {
+ if (cursor[i] != '\0') {
+ TokenizeError("failed to read nested block sentinel, expected all bytes to be 0", input, cursor);
+ }
+ }
+ cursor += sentinel_block_length;
+ }
+
+ if (Offset(input, cursor) != end_offset) {
+ TokenizeError("scope length not reached, something is wrong", input, cursor);
+ }
+
+ return true;
+}
+} // anonymous namespace
+
+// ------------------------------------------------------------------------------------------------
+// TODO: Test FBX Binary files newer than the 7500 version to check if the 64 bits address behaviour is consistent
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length) {
+ if (length < 0x1b) {
+ //TokenizeError("file is too short",0);
+ }
+
+ //uint32_t offset = 0x15;
+ /* const char* cursor = input + 0x15;
+ const uint32_t flags = ReadWord(input, cursor, input + length);
+ const uint8_t padding_0 = ReadByte(input, cursor, input + length); // unused
+ const uint8_t padding_1 = ReadByte(input, cursor, input + length); // unused*/
+
+ if (strncmp(input, "Kaydara FBX Binary", 18)) {
+ TokenizeError("magic bytes not found", 0);
+ }
+
+ const char *cursor = input + 18;
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ /*Result ignored*/ ReadByte(input, cursor, input + length);
+ const uint32_t version = ReadWord(input, cursor, input + length);
+ print_verbose("FBX Version: " + itos(version));
+ //ASSIMP_LOG_DEBUG_F("FBX version: ", version);
+ const bool is64bits = version >= 7500;
+ const char *end = input + length;
+ while (cursor < end) {
+ if (!ReadScope(output_tokens, input, cursor, input + length, is64bits)) {
+ break;
+ }
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXCommon.h b/modules/fbx/fbx_parser/FBXCommon.h
new file mode 100644
index 0000000000..98a3d78f0d
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXCommon.h
@@ -0,0 +1,110 @@
+/*************************************************************************/
+/* FBXCommon.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXCommon.h
+* Some useful constants and enums for dealing with FBX files.
+*/
+#ifndef FBX_COMMON_H
+#define FBX_COMMON_H
+
+#include <string>
+
+namespace FBXDocParser {
+const std::string NULL_RECORD = { // 13 null bytes
+ '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0'
+}; // who knows why
+const std::string SEPARATOR = { '\x00', '\x01' }; // for use inside strings
+const std::string MAGIC_NODE_TAG = "_$AssimpFbx$"; // from import
+const int64_t SECOND = 46186158000; // FBX's kTime unit
+
+// rotation order. We'll probably use EulerXYZ for everything
+enum RotOrder {
+ RotOrder_EulerXYZ = 0,
+ RotOrder_EulerXZY,
+ RotOrder_EulerYZX,
+ RotOrder_EulerYXZ,
+ RotOrder_EulerZXY,
+ RotOrder_EulerZYX,
+
+ RotOrder_SphericXYZ,
+
+ RotOrder_MAX // end-of-enum sentinel
+};
+
+enum TransformInheritance {
+ Transform_RrSs = 0,
+ Transform_RSrs = 1,
+ Transform_Rrs = 2,
+ TransformInheritance_MAX // end-of-enum sentinel
+};
+} // namespace FBXDocParser
+
+#endif // FBX_COMMON_H
diff --git a/modules/fbx/fbx_parser/FBXDeformer.cpp b/modules/fbx/fbx_parser/FBXDeformer.cpp
new file mode 100644
index 0000000000..325641bc32
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDeformer.cpp
@@ -0,0 +1,279 @@
+/*************************************************************************/
+/* FBXDeformer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXNoteAttribute.cpp
+ * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "core/math/math_funcs.h"
+#include "core/math/transform.h"
+
+#include <iostream>
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Deformer::Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+ props = GetPropertyTable(doc, "Deformer.Fbx" + classname, element, sc, true);
+}
+
+// ------------------------------------------------------------------------------------------------
+Deformer::~Deformer() {
+}
+
+Constraint::Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+ // used something.fbx as this is a cache name.
+ props = GetPropertyTable(doc, "Something.Fbx" + classname, element, sc, true);
+}
+
+Constraint::~Constraint() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Cluster::Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name), valid_transformAssociateModel(false) {
+ const ScopePtr sc = GetRequiredScope(element);
+ // for( auto element : sc.Elements())
+ // {
+ // std::cout << "cluster element: " << element.first << std::endl;
+ // }
+ //
+ // element: Indexes
+ // element: Transform
+ // element: TransformAssociateModel
+ // element: TransformLink
+ // element: UserData
+ // element: Version
+ // element: Weights
+
+ const ElementPtr Indexes = sc->GetElement("Indexes");
+ const ElementPtr Weights = sc->GetElement("Weights");
+
+ const ElementPtr TransformAssociateModel = sc->GetElement("TransformAssociateModel");
+ if (TransformAssociateModel != nullptr) {
+ //Transform t = ReadMatrix(*TransformAssociateModel);
+ link_mode = SkinLinkMode_Additive;
+ valid_transformAssociateModel = true;
+ } else {
+ link_mode = SkinLinkMode_Normalized;
+ valid_transformAssociateModel = false;
+ }
+
+ const ElementPtr Transform = GetRequiredElement(sc, "Transform", element);
+ const ElementPtr TransformLink = GetRequiredElement(sc, "TransformLink", element);
+
+ // todo: check if we need this
+ //const Element& TransformAssociateModel = GetRequiredElement(sc, "TransformAssociateModel", &element);
+
+ transform = ReadMatrix(Transform);
+ transformLink = ReadMatrix(TransformLink);
+
+ // it is actually possible that there be Deformer's with no weights
+ if (!!Indexes != !!Weights) {
+ DOMError("either Indexes or Weights are missing from Cluster", element);
+ }
+
+ if (Indexes) {
+ ParseVectorDataArray(indices, Indexes);
+ ParseVectorDataArray(weights, Weights);
+ }
+
+ if (indices.size() != weights.size()) {
+ DOMError("sizes of index and weight array don't match up", element);
+ }
+
+ // read assigned node
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Model");
+ for (const Connection *con : conns) {
+ const Model *mod = ProcessSimpleConnection<Model>(*con, false, "Model -> Cluster", element);
+ if (mod) {
+ node = mod;
+ break;
+ }
+ }
+
+ if (!node) {
+ DOMError("failed to read target Node for Cluster", element);
+ node = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Cluster::~Cluster() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Skin::Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name), accuracy(0.0f) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // keep this it is used for debugging and any FBX format changes
+ // for (auto element : sc.Elements()) {
+ // std::cout << "skin element: " << element.first << std::endl;
+ // }
+
+ const ElementPtr Link_DeformAcuracy = sc->GetElement("Link_DeformAcuracy");
+ if (Link_DeformAcuracy) {
+ accuracy = ParseTokenAsFloat(GetRequiredToken(Link_DeformAcuracy, 0));
+ }
+
+ const ElementPtr SkinType = sc->GetElement("SkinningType");
+
+ if (SkinType) {
+ std::string skin_type = ParseTokenAsString(GetRequiredToken(SkinType, 0));
+
+ if (skin_type == "Linear") {
+ skinType = Skin_Linear;
+ } else if (skin_type == "Rigid") {
+ skinType = Skin_Rigid;
+ } else if (skin_type == "DualQuaternion") {
+ skinType = Skin_DualQuaternion;
+ } else if (skin_type == "Blend") {
+ skinType = Skin_Blend;
+ } else {
+ print_error("[doc:skin] could not find valid skin type: " + String(skin_type.c_str()));
+ }
+ }
+
+ // resolve assigned clusters
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+
+ //
+
+ clusters.reserve(conns.size());
+ for (const Connection *con : conns) {
+ const Cluster *cluster = ProcessSimpleConnection<Cluster>(*con, false, "Cluster -> Skin", element);
+ if (cluster) {
+ clusters.push_back(cluster);
+ continue;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Skin::~Skin() {
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+ blendShapeChannels.reserve(conns.size());
+ for (const Connection *con : conns) {
+ const BlendShapeChannel *bspc = ProcessSimpleConnection<BlendShapeChannel>(*con, false, "BlendShapeChannel -> BlendShape", element);
+ if (bspc) {
+ blendShapeChannels.push_back(bspc);
+ continue;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+BlendShape::~BlendShape() {
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Deformer(id, element, doc, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr DeformPercent = sc->GetElement("DeformPercent");
+ if (DeformPercent) {
+ percent = ParseTokenAsFloat(GetRequiredToken(DeformPercent, 0));
+ }
+ const ElementPtr FullWeights = sc->GetElement("FullWeights");
+ if (FullWeights) {
+ ParseVectorDataArray(fullWeights, FullWeights);
+ }
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Geometry");
+ shapeGeometries.reserve(conns.size());
+ for (const Connection *con : conns) {
+ const ShapeGeometry *const sg = ProcessSimpleConnection<ShapeGeometry>(*con, false, "Shape -> BlendShapeChannel", element);
+ if (sg) {
+ shapeGeometries.push_back(sg);
+ continue;
+ }
+ }
+}
+// ------------------------------------------------------------------------------------------------
+BlendShapeChannel::~BlendShapeChannel() {
+}
+// ------------------------------------------------------------------------------------------------
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocument.cpp b/modules/fbx/fbx_parser/FBXDocument.cpp
new file mode 100644
index 0000000000..74798a655a
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocument.cpp
@@ -0,0 +1,713 @@
+/*************************************************************************/
+/* FBXDocument.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <map>
+#include <memory>
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::LazyObject(uint64_t id, const ElementPtr element, const Document &doc) :
+ doc(doc), element(element), id(id), flags() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject::~LazyObject() {
+ object.reset();
+}
+
+ObjectPtr LazyObject::LoadObject() {
+ if (IsBeingConstructed() || FailedToConstruct()) {
+ return nullptr;
+ }
+
+ if (object) {
+ return object.get();
+ }
+
+ TokenPtr key = element->KeyToken();
+ ERR_FAIL_COND_V(!key, nullptr);
+ const TokenList &tokens = element->Tokens();
+
+ if (tokens.size() < 3) {
+ //DOMError("expected at least 3 tokens: id, name and class tag",&element);
+ return nullptr;
+ }
+
+ const char *err = nullptr;
+ std::string name = ParseTokenAsString(tokens[1], err);
+ if (err) {
+ DOMError(err, element);
+ }
+
+ // small fix for binary reading: binary fbx files don't use
+ // prefixes such as Model:: in front of their names. The
+ // loading code expects this at many places, though!
+ // so convert the binary representation (a 0x0001) to the
+ // double colon notation.
+ 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);
+ }
+
+ // prevent recursive calls
+ flags |= BEING_CONSTRUCTED;
+
+ // this needs to be relatively fast since it happens a lot,
+ // so avoid constructing strings all the time.
+ const char *obtype = key->begin();
+ const size_t length = static_cast<size_t>(key->end() - key->begin());
+
+ if (!strncmp(obtype, "Pose", length)) {
+ object.reset(new FbxPose(id, element, doc, name));
+ } else if (!strncmp(obtype, "Geometry", length)) {
+ if (!strcmp(classtag.c_str(), "Mesh")) {
+ object.reset(new MeshGeometry(id, element, name, doc));
+ }
+ if (!strcmp(classtag.c_str(), "Shape")) {
+ object.reset(new ShapeGeometry(id, element, name, doc));
+ }
+ if (!strcmp(classtag.c_str(), "Line")) {
+ object.reset(new LineGeometry(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")) {
+ // This is an older format for bones
+ // this is what blender uses I believe
+ object.reset(new LimbNode(id, element, doc, name));
+ }
+ } else if (!strncmp(obtype, "Constraint", length)) {
+ object.reset(new Constraint(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 (!strcmp(classtag.c_str(), "BlendShape")) {
+ object.reset(new BlendShape(id, element, doc, name));
+ } else if (!strcmp(classtag.c_str(), "BlendShapeChannel")) {
+ object.reset(new BlendShapeChannel(id, element, doc, name));
+ }
+ } else if (!strncmp(obtype, "Model", length)) {
+ // Model is normal node
+
+ // LimbNode model is a 'bone' node.
+ if (!strcmp(classtag.c_str(), "LimbNode")) {
+ object.reset(new ModelLimbNode(id, element, doc, name));
+
+ } else if (strcmp(classtag.c_str(), "IKEffector") && strcmp(classtag.c_str(), "FKEffector")) {
+ // FK and IK effectors are not supporte
+ 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));
+ } else {
+ ERR_FAIL_V_MSG(nullptr, "FBX contains unsupported object: " + String(obtype));
+ }
+
+ flags &= ~BEING_CONSTRUCTED;
+
+ return object.get();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::Object(uint64_t id, const ElementPtr element, const std::string &name) :
+ element(element), name(name), id(id) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Object::~Object() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::FileGlobalSettings(const Document &doc, const PropertyTable *props) :
+ props(props), doc(doc) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+FileGlobalSettings::~FileGlobalSettings() {
+ if (props != nullptr) {
+ delete props;
+ props = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::Document(const Parser &parser, const ImportSettings &settings) :
+ settings(settings), parser(parser), SafeToImport(false) {
+ // Cannot use array default initialization syntax because vc8 fails on it
+ for (unsigned int &timeStamp : creationTimeStamp) {
+ timeStamp = 0;
+ }
+
+ // we must check if we can read the header version safely, if its outdated then drop it.
+ if (ReadHeader()) {
+ SafeToImport = true;
+ ReadPropertyTemplates();
+
+ ReadGlobalSettings();
+
+ // This order is important, connections need parsed objects to check
+ // whether connections are ok or not. Objects may not be evaluated yet,
+ // though, since this may require valid connections.
+ ReadObjects();
+ ReadConnections();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Document::~Document() {
+ for (PropertyTemplateMap::value_type v : templates) {
+ delete v.second;
+ }
+
+ for (ObjectMap::value_type &v : objects) {
+ delete v.second;
+ }
+
+ for (ConnectionMap::value_type &v : src_connections) {
+ delete v.second;
+ }
+
+ if (metadata_properties != nullptr) {
+ delete metadata_properties;
+ }
+ // clear globals import pointer
+ globals.reset();
+}
+
+// ------------------------------------------------------------------------------------------------
+static const unsigned int LowerSupportedVersion = 7300;
+static const unsigned int UpperSupportedVersion = 7700;
+
+bool Document::ReadHeader() {
+ // Read ID objects from "Objects" section
+ ScopePtr sc = parser.GetRootScope();
+ ElementPtr ehead = sc->GetElement("FBXHeaderExtension");
+ if (!ehead || !ehead->Compound()) {
+ DOMError("no FBXHeaderExtension dictionary found");
+ }
+
+ const ScopePtr shead = ehead->Compound();
+ fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead, "FBXVersion", ehead), 0));
+
+ // While we may have some success with newer files, we don't support
+ // the older 6.n fbx format
+ if (fbxVersion < LowerSupportedVersion) {
+ DOMWarning("unsupported, old format version, FBX 2015-2020, you must re-export in a more modern version of your original modelling application");
+ return false;
+ }
+ if (fbxVersion > UpperSupportedVersion) {
+ DOMWarning("unsupported, newer format version, supported are only FBX 2015, up to FBX 2020"
+ " trying to read it nevertheless");
+ }
+
+ const ElementPtr ecreator = shead->GetElement("Creator");
+ if (ecreator) {
+ creator = ParseTokenAsString(GetRequiredToken(ecreator, 0));
+ }
+
+ //
+ // Scene Info
+ //
+
+ const ElementPtr scene_info = shead->GetElement("SceneInfo");
+
+ if (scene_info) {
+ PropertyTable *fileExportProps = const_cast<PropertyTable *>(GetPropertyTable(*this, "", scene_info, scene_info->Compound(), true));
+
+ if (fileExportProps) {
+ metadata_properties = fileExportProps;
+ }
+ }
+
+ const ElementPtr etimestamp = shead->GetElement("CreationTimeStamp");
+ if (etimestamp && etimestamp->Compound()) {
+ const ScopePtr 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));
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadGlobalSettings() {
+ ERR_FAIL_COND_MSG(globals != nullptr, "Global settings is already setup this is a serious error and should be reported");
+
+ const ScopePtr sc = parser.GetRootScope();
+ const ElementPtr ehead = sc->GetElement("GlobalSettings");
+ if (nullptr == ehead || !ehead->Compound()) {
+ DOMWarning("no GlobalSettings dictionary found");
+ globals = std::make_shared<FileGlobalSettings>(*this, new PropertyTable());
+ return;
+ }
+
+ const PropertyTable *props = GetPropertyTable(*this, "", ehead, ehead->Compound(), true);
+
+ //double v = PropertyGet<float>( *props, std::string("UnitScaleFactor"), 1.0 );
+
+ if (!props) {
+ DOMError("GlobalSettings dictionary contains no property table");
+ }
+
+ globals = std::make_shared<FileGlobalSettings>(*this, props);
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadObjects() {
+ // read ID objects from "Objects" section
+ const ScopePtr sc = parser.GetRootScope();
+ const ElementPtr eobjects = sc->GetElement("Objects");
+ if (!eobjects || !eobjects->Compound()) {
+ DOMError("no Objects dictionary found");
+ }
+
+ // add a dummy entry to represent the Model::RootNode object (id 0),
+ // which is only indirectly defined in the input file
+ objects[0] = new LazyObject(0L, eobjects, *this);
+
+ const ScopePtr sobjects = eobjects->Compound();
+ for (const ElementMap::value_type &iter : sobjects->Elements()) {
+ // extract ID
+ const TokenList &tok = iter.second->Tokens();
+
+ if (tok.empty()) {
+ DOMError("expected ID after object key", iter.second);
+ }
+
+ const char *err;
+ const uint64_t id = ParseTokenAsID(tok[0], err);
+ if (err) {
+ DOMError(err, iter.second);
+ }
+
+ // id=0 is normally implicit
+ if (id == 0L) {
+ DOMError("encountered object with implicitly defined id 0", iter.second);
+ }
+
+ if (objects.find(id) != objects.end()) {
+ DOMWarning("encountered duplicate object id, ignoring first occurrence", iter.second);
+ }
+
+ objects[id] = new LazyObject(id, iter.second, *this);
+
+ // grab all animation stacks upfront since there is no listing of them
+ if (!strcmp(iter.first.c_str(), "AnimationStack")) {
+ animationStacks.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Constraint")) {
+ constraints.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Pose")) {
+ bind_poses.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Material")) {
+ materials.push_back(id);
+ } else if (!strcmp(iter.first.c_str(), "Deformer")) {
+ TokenPtr key = iter.second->KeyToken();
+ ERR_CONTINUE_MSG(!key, "[parser bug] invalid token key for deformer");
+ const TokenList &tokens = iter.second->Tokens();
+ const std::string class_tag = ParseTokenAsString(tokens[2], err);
+
+ if (err) {
+ DOMError(err, iter.second);
+ }
+
+ if (class_tag == "Skin") {
+ //print_verbose("registered skin:" + itos(id));
+ skins.push_back(id);
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadPropertyTemplates() {
+ const ScopePtr sc = parser.GetRootScope();
+ // read property templates from "Definitions" section
+ const ElementPtr edefs = sc->GetElement("Definitions");
+ if (!edefs || !edefs->Compound()) {
+ DOMWarning("no Definitions dictionary found");
+ return;
+ }
+
+ const ScopePtr sdefs = edefs->Compound();
+ const ElementCollection otypes = sdefs->GetCollection("ObjectType");
+ for (ElementMap::const_iterator it = otypes.first; it != otypes.second; ++it) {
+ const ElementPtr el = (*it).second;
+ const ScopePtr sc_2 = el->Compound();
+ if (!sc_2) {
+ 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_2->GetCollection("PropertyTemplate");
+ for (ElementMap::const_iterator iter = templs.first; iter != templs.second; ++iter) {
+ const ElementPtr el_2 = (*iter).second;
+ const ScopePtr sc_3 = el_2->Compound();
+ if (!sc_3) {
+ DOMWarning("expected nested scope in PropertyTemplate, ignoring", el);
+ continue;
+ }
+
+ const TokenList &tok_2 = el_2->Tokens();
+ if (tok_2.empty()) {
+ DOMWarning("expected name for PropertyTemplate element, ignoring", el);
+ continue;
+ }
+
+ const std::string &pname = ParseTokenAsString(tok_2[0]);
+
+ const ElementPtr Properties70 = sc_3->GetElement("Properties70");
+ if (Properties70) {
+ // PropertyTable(const ElementPtr element, const PropertyTable* templateProps);
+ const PropertyTable *props = new PropertyTable(Properties70, nullptr);
+
+ templates[oname + "." + pname] = props;
+ }
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+void Document::ReadConnections() {
+ const ScopePtr sc = parser.GetRootScope();
+
+ // read property templates from "Definitions" section
+ const ElementPtr econns = sc->GetElement("Connections");
+ if (!econns || !econns->Compound()) {
+ DOMError("no Connections dictionary found");
+ }
+
+ uint64_t insertionOrder = 0l;
+ const ScopePtr sconns = econns->Compound();
+ const ElementCollection conns = sconns->GetCollection("C");
+ for (ElementMap::const_iterator it = conns.first; it != conns.second; ++it) {
+ const ElementPtr el = (*it).second;
+ const std::string &type = ParseTokenAsString(GetRequiredToken(el, 0));
+
+ // PP = property-property connection, ignored for now
+ // (tokens: "PP", ID1, "Property1", ID2, "Property2")
+ if (type == "PP") {
+ continue;
+ }
+
+ const uint64_t src = ParseTokenAsID(GetRequiredToken(el, 1));
+ const uint64_t dest = ParseTokenAsID(GetRequiredToken(el, 2));
+
+ // OO = object-object connection
+ // OP = object-property connection, in which case the destination property follows the object ID
+ 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;
+ }
+
+ // dest may be 0 (root node) but we added a dummy object before
+ if (objects.find(dest) == objects.end()) {
+ DOMWarning("destination object for connection does not exist", el);
+ continue;
+ }
+
+ // add new connection
+ 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.empty()) {
+ return animationStacksResolved;
+ }
+
+ animationStacksResolved.reserve(animationStacks.size());
+ for (uint64_t id : animationStacks) {
+ LazyObject *lazy = GetObject(id);
+
+ // Two things happen here:
+ // We cast internally an Object PTR to an Animation Stack PTR
+ // We return invalid weak_ptrs for objects which are invalid
+
+ const AnimationStack *stack = lazy->Get<AnimationStack>();
+ ERR_CONTINUE_MSG(!stack, "invalid ptr to AnimationStack - conversion failure");
+
+ // We push back the weak reference :) to keep things simple, as ownership is on the parser side so it wont be cleaned up.
+ animationStacksResolved.push_back(stack);
+ }
+
+ return animationStacksResolved;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Document::GetObject(uint64_t id) const {
+ ObjectMap::const_iterator it = objects.find(id);
+ return it == objects.end() ? nullptr : (*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; // NRVO should handle this
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsSequenced(uint64_t id, bool is_src,
+ const ConnectionMap &conns,
+ const char *const *classnames,
+ size_t count) const
+
+{
+ size_t lengths[MAX_CLASSNAMES];
+
+ const size_t c = count;
+ for (size_t i = 0; i < c; ++i) {
+ lengths[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) {
+ TokenPtr 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())) == lengths[i] && !strncmp(classnames[i], obtype, lengths[i])) {
+ obtype = nullptr;
+ break;
+ }
+ }
+
+ if (obtype) {
+ continue;
+ }
+
+ temp.push_back((*it).second);
+ }
+
+ std::sort(temp.begin(), temp.end(), std::mem_fn(&Connection::Compare));
+ return temp; // NRVO should handle this
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t source) const {
+ return GetConnectionsSequenced(source, ConnectionsBySource());
+}
+
+// ------------------------------------------------------------------------------------------------
+std::vector<const Connection *> Document::GetConnectionsBySourceSequenced(uint64_t src, const char *classname) const {
+ const char *arr[] = { classname };
+ return GetConnectionsBySourceSequenced(src, 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) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Connection::~Connection() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Connection::LazySourceObject() const {
+ LazyObject *const lazy = doc.GetObject(src);
+ return lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+LazyObject *Connection::LazyDestinationObject() const {
+ LazyObject *const lazy = doc.GetObject(dest);
+ return lazy;
+}
+
+// ------------------------------------------------------------------------------------------------
+Object *Connection::SourceObject() const {
+ LazyObject *lazy = doc.GetObject(src);
+ //ai_assert(lazy);
+ return lazy->LoadObject();
+}
+
+// ------------------------------------------------------------------------------------------------
+Object *Connection::DestinationObject() const {
+ LazyObject *lazy = doc.GetObject(dest);
+ //ai_assert(lazy);
+ return lazy->LoadObject();
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocument.h b/modules/fbx/fbx_parser/FBXDocument.h
new file mode 100644
index 0000000000..7c08f3de65
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocument.h
@@ -0,0 +1,1319 @@
+/*************************************************************************/
+/* FBXDocument.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/** @file FBXDocument.h
+ * @brief FBX DOM
+ */
+#ifndef FBX_DOCUMENT_H
+#define FBX_DOCUMENT_H
+
+#include "FBXCommon.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "core/math/transform.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+#include "core/string/print_string.h"
+#include <stdint.h>
+#include <numeric>
+
+#define _AI_CONCAT(a, b) a##b
+#define AI_CONCAT(a, b) _AI_CONCAT(a, b)
+
+namespace FBXDocParser {
+
+class Parser;
+class Object;
+struct ImportSettings;
+class Connection;
+
+class PropertyTable;
+class Document;
+class Material;
+class ShapeGeometry;
+class LineGeometry;
+class Geometry;
+
+class Video;
+
+class AnimationCurve;
+class AnimationCurveNode;
+class AnimationLayer;
+class AnimationStack;
+
+class BlendShapeChannel;
+class BlendShape;
+class Skin;
+class Cluster;
+
+typedef Object *ObjectPtr;
+#define new_Object new 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(uint64_t id, const ElementPtr element, const Document &doc);
+ ~LazyObject();
+
+ ObjectPtr LoadObject();
+
+ /* Casting weak pointers to their templated type safely and preserving ref counting and safety
+ * with lock() keyword to prevent leaking memory
+ */
+ template <typename T>
+ const T *Get() {
+ ObjectPtr ob = LoadObject();
+ return dynamic_cast<const T *>(ob);
+ }
+
+ uint64_t ID() const {
+ return id;
+ }
+
+ bool IsBeingConstructed() const {
+ return (flags & BEING_CONSTRUCTED) != 0;
+ }
+
+ bool FailedToConstruct() const {
+ return (flags & FAILED_TO_CONSTRUCT) != 0;
+ }
+
+ ElementPtr GetElement() const {
+ return element;
+ }
+
+ const Document &GetDocument() const {
+ return doc;
+ }
+
+private:
+ const Document &doc;
+ ElementPtr element = nullptr;
+ std::shared_ptr<Object> object = nullptr;
+ const uint64_t id = 0;
+
+ enum Flags {
+ BEING_CONSTRUCTED = 0x1,
+ FAILED_TO_CONSTRUCT = 0x2
+ };
+
+ unsigned int flags = 0;
+};
+
+/** Base class for in-memory (DOM) representations of FBX objects */
+class Object {
+public:
+ Object(uint64_t id, const ElementPtr element, const std::string &name);
+
+ virtual ~Object();
+
+ ElementPtr SourceElement() const {
+ return element;
+ }
+
+ const std::string &Name() const {
+ return name;
+ }
+
+ uint64_t ID() const {
+ return id;
+ }
+
+protected:
+ const ElementPtr element;
+ const std::string name;
+ const uint64_t id = 0;
+};
+
+/** DOM class for generic FBX NoteAttribute blocks. NoteAttribute's just hold a property table,
+ * fixed members are added by deriving classes. */
+class NodeAttribute : public Object {
+public:
+ NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~NodeAttribute();
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+private:
+ const PropertyTable *props;
+};
+
+/** DOM base class for FBX camera settings attached to a node */
+class CameraSwitcher : public NodeAttribute {
+public:
+ CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~CameraSwitcher();
+
+ int CameraID() const {
+ return cameraId;
+ }
+
+ const std::string &CameraName() const {
+ return cameraName;
+ }
+
+ const std::string &CameraIndexName() const {
+ return cameraIndexName;
+ }
+
+private:
+ int cameraId;
+ std::string cameraName;
+ std::string cameraIndexName;
+};
+
+#define fbx_stringize(a) #a
+
+#define fbx_simple_property(name, type, default_value) \
+ type name() const { \
+ return PropertyGet<type>(Props(), fbx_stringize(name), (default_value)); \
+ }
+
+// XXX improve logging
+#define fbx_simple_enum_property(name, type, default_value) \
+ type name() const { \
+ const int ival = PropertyGet<int>(Props(), fbx_stringize(name), static_cast<int>(default_value)); \
+ if (ival < 0 || ival >= AI_CONCAT(type, _MAX)) { \
+ return static_cast<type>(default_value); \
+ } \
+ return static_cast<type>(ival); \
+ }
+
+class FbxPoseNode;
+class FbxPose : public Object {
+public:
+ FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ const std::vector<FbxPoseNode *> &GetBindPoses() const {
+ return pose_nodes;
+ }
+
+ virtual ~FbxPose();
+
+private:
+ std::vector<FbxPoseNode *> pose_nodes;
+};
+
+class FbxPoseNode {
+public:
+ FbxPoseNode(const ElementPtr element, const Document &doc, const std::string &name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ // get pose node transform
+ const ElementPtr Transform = GetRequiredElement(sc, "Matrix", element);
+ transform = ReadMatrix(Transform);
+
+ // get node id this pose node is for
+ const ElementPtr NodeId = sc->GetElement("Node3D");
+ if (NodeId) {
+ target_id = ParseTokenAsInt64(GetRequiredToken(NodeId, 0));
+ }
+
+ print_verbose("added posenode " + itos(target_id) + " transform: " + transform);
+ }
+ virtual ~FbxPoseNode() {
+ }
+
+ uint64_t GetNodeID() const {
+ return target_id;
+ }
+
+ Transform GetBindPose() const {
+ return transform;
+ }
+
+private:
+ uint64_t target_id;
+ Transform transform;
+};
+
+/** DOM base class for FBX cameras attached to a node */
+class Camera : public NodeAttribute {
+public:
+ Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Camera();
+
+ fbx_simple_property(Position, Vector3, Vector3(0, 0, 0));
+ fbx_simple_property(UpVector, Vector3, Vector3(0, 1, 0));
+ fbx_simple_property(InterestPosition, Vector3, Vector3(0, 0, 0));
+
+ fbx_simple_property(AspectWidth, float, 1.0f);
+ fbx_simple_property(AspectHeight, float, 1.0f);
+ fbx_simple_property(FilmWidth, float, 1.0f);
+ fbx_simple_property(FilmHeight, float, 1.0f);
+
+ fbx_simple_property(NearPlane, float, 0.1f);
+ fbx_simple_property(FarPlane, float, 100.0f);
+
+ fbx_simple_property(FilmAspectRatio, float, 1.0f);
+ fbx_simple_property(ApertureMode, int, 0);
+
+ fbx_simple_property(FieldOfView, float, 1.0f);
+ fbx_simple_property(FocalLength, float, 1.0f);
+};
+
+/** DOM base class for FBX null markers attached to a node */
+class Null : public NodeAttribute {
+public:
+ Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Null();
+};
+
+/** DOM base class for FBX limb node markers attached to a node */
+class LimbNode : public NodeAttribute {
+public:
+ LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~LimbNode();
+};
+
+/** DOM base class for FBX lights attached to a node */
+class Light : public NodeAttribute {
+public:
+ Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Light();
+
+ enum Type {
+ Type_Point,
+ Type_Directional,
+ Type_Spot,
+ Type_Area,
+ Type_Volume,
+
+ Type_MAX // end-of-enum sentinel
+ };
+
+ enum Decay {
+ Decay_None,
+ Decay_Linear,
+ Decay_Quadratic,
+ Decay_Cubic,
+
+ Decay_MAX // end-of-enum sentinel
+ };
+
+ fbx_simple_property(Color, Vector3, Vector3(1, 1, 1));
+ fbx_simple_enum_property(LightType, Type, 0);
+ fbx_simple_property(CastLightOnObject, bool, false);
+ fbx_simple_property(DrawVolumetricLight, bool, true);
+ fbx_simple_property(DrawGroundProjection, bool, true);
+ fbx_simple_property(DrawFrontFacingVolumetricLight, bool, false);
+ fbx_simple_property(Intensity, float, 100.0f);
+ fbx_simple_property(InnerAngle, float, 0.0f);
+ fbx_simple_property(OuterAngle, float, 45.0f);
+ fbx_simple_property(Fog, int, 50);
+ fbx_simple_enum_property(DecayType, Decay, 2);
+ fbx_simple_property(DecayStart, float, 1.0f);
+ fbx_simple_property(FileName, std::string, "");
+
+ fbx_simple_property(EnableNearAttenuation, bool, false);
+ fbx_simple_property(NearAttenuationStart, float, 0.0f);
+ fbx_simple_property(NearAttenuationEnd, float, 0.0f);
+ fbx_simple_property(EnableFarAttenuation, bool, false);
+ fbx_simple_property(FarAttenuationStart, float, 0.0f);
+ fbx_simple_property(FarAttenuationEnd, float, 0.0f);
+
+ fbx_simple_property(CastShadows, bool, true);
+ fbx_simple_property(ShadowColor, Vector3, Vector3(0, 0, 0));
+
+ fbx_simple_property(AreaLightShape, int, 0);
+
+ fbx_simple_property(LeftBarnDoor, float, 20.0f);
+ fbx_simple_property(RightBarnDoor, float, 20.0f);
+ fbx_simple_property(TopBarnDoor, float, 20.0f);
+ fbx_simple_property(BottomBarnDoor, float, 20.0f);
+ fbx_simple_property(EnableBarnDoor, bool, true);
+};
+
+class Model;
+
+typedef Model *ModelPtr;
+#define new_Model new Model
+
+/** DOM base class for FBX models (even though its semantics are more "node" than "model" */
+class Model : public Object {
+public:
+ enum RotOrder {
+ RotOrder_EulerXYZ = 0,
+ RotOrder_EulerXZY,
+ RotOrder_EulerYZX,
+ RotOrder_EulerYXZ,
+ RotOrder_EulerZXY,
+ RotOrder_EulerZYX,
+
+ RotOrder_SphericXYZ,
+
+ RotOrder_MAX // end-of-enum sentinel
+ };
+
+ Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Model();
+
+ fbx_simple_property(QuaternionInterpolate, int, 0);
+
+ fbx_simple_property(RotationOffset, Vector3, Vector3());
+ fbx_simple_property(RotationPivot, Vector3, Vector3());
+ fbx_simple_property(ScalingOffset, Vector3, Vector3());
+ fbx_simple_property(ScalingPivot, Vector3, Vector3());
+ fbx_simple_property(TranslationActive, bool, false);
+ fbx_simple_property(TranslationMin, Vector3, Vector3());
+ fbx_simple_property(TranslationMax, Vector3, Vector3());
+
+ fbx_simple_property(TranslationMinX, bool, false);
+ fbx_simple_property(TranslationMaxX, bool, false);
+ fbx_simple_property(TranslationMinY, bool, false);
+ fbx_simple_property(TranslationMaxY, bool, false);
+ fbx_simple_property(TranslationMinZ, bool, false);
+ fbx_simple_property(TranslationMaxZ, bool, false);
+
+ fbx_simple_enum_property(RotationOrder, RotOrder, 0);
+ fbx_simple_property(RotationSpaceForLimitOnly, bool, false);
+ fbx_simple_property(RotationStiffnessX, float, 0.0f);
+ fbx_simple_property(RotationStiffnessY, float, 0.0f);
+ fbx_simple_property(RotationStiffnessZ, float, 0.0f);
+ fbx_simple_property(AxisLen, float, 0.0f);
+
+ fbx_simple_property(PreRotation, Vector3, Vector3());
+ fbx_simple_property(PostRotation, Vector3, Vector3());
+ fbx_simple_property(RotationActive, bool, false);
+
+ fbx_simple_property(RotationMin, Vector3, Vector3());
+ fbx_simple_property(RotationMax, Vector3, Vector3());
+
+ fbx_simple_property(RotationMinX, bool, false);
+ fbx_simple_property(RotationMaxX, bool, false);
+ fbx_simple_property(RotationMinY, bool, false);
+ fbx_simple_property(RotationMaxY, bool, false);
+ fbx_simple_property(RotationMinZ, bool, false);
+ fbx_simple_property(RotationMaxZ, bool, false);
+ fbx_simple_enum_property(InheritType, TransformInheritance, 0);
+
+ fbx_simple_property(ScalingActive, bool, false);
+ fbx_simple_property(ScalingMin, Vector3, Vector3());
+ fbx_simple_property(ScalingMax, Vector3, Vector3(1, 1, 1));
+ fbx_simple_property(ScalingMinX, bool, false);
+ fbx_simple_property(ScalingMaxX, bool, false);
+ fbx_simple_property(ScalingMinY, bool, false);
+ fbx_simple_property(ScalingMaxY, bool, false);
+ fbx_simple_property(ScalingMinZ, bool, false);
+ fbx_simple_property(ScalingMaxZ, bool, false);
+
+ fbx_simple_property(GeometricTranslation, Vector3, Vector3());
+ fbx_simple_property(GeometricRotation, Vector3, Vector3());
+ fbx_simple_property(GeometricScaling, Vector3, Vector3(1, 1, 1));
+
+ fbx_simple_property(MinDampRangeX, float, 0.0f);
+ fbx_simple_property(MinDampRangeY, float, 0.0f);
+ fbx_simple_property(MinDampRangeZ, float, 0.0f);
+ fbx_simple_property(MaxDampRangeX, float, 0.0f);
+ fbx_simple_property(MaxDampRangeY, float, 0.0f);
+ fbx_simple_property(MaxDampRangeZ, float, 0.0f);
+
+ fbx_simple_property(MinDampStrengthX, float, 0.0f);
+ fbx_simple_property(MinDampStrengthY, float, 0.0f);
+ fbx_simple_property(MinDampStrengthZ, float, 0.0f);
+ fbx_simple_property(MaxDampStrengthX, float, 0.0f);
+ fbx_simple_property(MaxDampStrengthY, float, 0.0f);
+ fbx_simple_property(MaxDampStrengthZ, float, 0.0f);
+
+ fbx_simple_property(PreferredAngleX, float, 0.0f);
+ fbx_simple_property(PreferredAngleY, float, 0.0f);
+ fbx_simple_property(PreferredAngleZ, float, 0.0f);
+
+ fbx_simple_property(Show, bool, true);
+ fbx_simple_property(LODBox, bool, false);
+ fbx_simple_property(Freeze, bool, false);
+
+ const std::string &Shading() const {
+ return shading;
+ }
+
+ const std::string &Culling() const {
+ return culling;
+ }
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ /** Get material links */
+ const std::vector<const Material *> &GetMaterials() const {
+ return materials;
+ }
+
+ /** Get geometry links */
+ const std::vector<const Geometry *> &GetGeometry() const {
+ return geometry;
+ }
+
+ /** Get node attachments */
+ const std::vector<const NodeAttribute *> &GetAttributes() const {
+ return attributes;
+ }
+
+ /** convenience method to check if the node has a Null node marker */
+ bool IsNull() const;
+
+private:
+ void ResolveLinks(const ElementPtr element, const Document &doc);
+
+private:
+ std::vector<const Material *> materials;
+ std::vector<const Geometry *> geometry;
+ std::vector<const NodeAttribute *> attributes;
+
+ std::string shading;
+ std::string culling;
+ const PropertyTable *props = nullptr;
+};
+
+class ModelLimbNode : public Model {
+public:
+ ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~ModelLimbNode();
+};
+
+/** DOM class for generic FBX textures */
+class Texture : public Object {
+public:
+ Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Texture();
+
+ const std::string &Type() const {
+ return type;
+ }
+
+ const std::string &FileName() const {
+ return fileName;
+ }
+
+ const std::string &RelativeFilename() const {
+ return relativeFileName;
+ }
+
+ const std::string &AlphaSource() const {
+ return alphaSource;
+ }
+
+ const Vector2 &UVTranslation() const {
+ return uvTrans;
+ }
+
+ const Vector2 &UVScaling() const {
+ return uvScaling;
+ }
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ // return a 4-tuple
+ const unsigned int *Crop() const {
+ return crop;
+ }
+
+ const Video *Media() const {
+ return media;
+ }
+
+private:
+ Vector2 uvTrans;
+ Vector2 uvScaling;
+
+ std::string type;
+ std::string relativeFileName;
+ std::string fileName;
+ std::string alphaSource;
+ const PropertyTable *props = nullptr;
+
+ unsigned int crop[4] = { 0 };
+
+ const Video *media = nullptr;
+};
+
+/** DOM class for layered FBX textures */
+class LayeredTexture : public Object {
+public:
+ LayeredTexture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~LayeredTexture();
+
+ // Can only be called after construction of the layered texture object due to construction flag.
+ void fillTexture(const Document &doc);
+
+ enum BlendMode {
+ BlendMode_Translucent,
+ BlendMode_Additive,
+ BlendMode_Modulate,
+ BlendMode_Modulate2,
+ BlendMode_Over,
+ BlendMode_Normal,
+ BlendMode_Dissolve,
+ BlendMode_Darken,
+ BlendMode_ColorBurn,
+ BlendMode_LinearBurn,
+ BlendMode_DarkerColor,
+ BlendMode_Lighten,
+ BlendMode_Screen,
+ BlendMode_ColorDodge,
+ BlendMode_LinearDodge,
+ BlendMode_LighterColor,
+ BlendMode_SoftLight,
+ BlendMode_HardLight,
+ BlendMode_VividLight,
+ BlendMode_LinearLight,
+ BlendMode_PinLight,
+ BlendMode_HardMix,
+ BlendMode_Difference,
+ BlendMode_Exclusion,
+ BlendMode_Subtract,
+ BlendMode_Divide,
+ BlendMode_Hue,
+ BlendMode_Saturation,
+ BlendMode_Color,
+ BlendMode_Luminosity,
+ BlendMode_Overlay,
+ BlendMode_BlendModeCount
+ };
+
+ const Texture *getTexture(int index = 0) const {
+ return textures[index];
+ }
+ int textureCount() const {
+ return static_cast<int>(textures.size());
+ }
+ BlendMode GetBlendMode() const {
+ return blendMode;
+ }
+ float Alpha() {
+ return alpha;
+ }
+
+private:
+ std::vector<const Texture *> textures;
+ BlendMode blendMode;
+ float alpha;
+};
+
+typedef std::map<std::string, const Texture *> TextureMap;
+typedef std::map<std::string, const LayeredTexture *> LayeredTextureMap;
+
+/** DOM class for generic FBX videos */
+class Video : public Object {
+public:
+ Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Video();
+
+ const std::string &Type() const {
+ return type;
+ }
+
+ bool IsEmbedded() const {
+ return contentLength > 0;
+ }
+
+ const std::string &FileName() const {
+ return fileName;
+ }
+
+ const std::string &RelativeFilename() const {
+ return relativeFileName;
+ }
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ const uint8_t *Content() const {
+ return content;
+ }
+
+ uint64_t ContentLength() const {
+ return contentLength;
+ }
+
+ uint8_t *RelinquishContent() {
+ uint8_t *ptr = content;
+ content = 0;
+ return ptr;
+ }
+
+ bool operator==(const Video &other) const {
+ return (
+ type == other.type && relativeFileName == other.relativeFileName && fileName == other.fileName);
+ }
+
+ bool operator<(const Video &other) const {
+ return std::tie(type, relativeFileName, fileName) < std::tie(other.type, other.relativeFileName, other.fileName);
+ }
+
+private:
+ std::string type;
+ std::string relativeFileName;
+ std::string fileName;
+ const PropertyTable *props = nullptr;
+
+ uint64_t contentLength = 0;
+ uint8_t *content = nullptr;
+};
+
+/** DOM class for generic FBX materials */
+class Material : public Object {
+public:
+ Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Material();
+
+ const std::string &GetShadingModel() const {
+ return shading;
+ }
+
+ bool IsMultilayer() const {
+ return multilayer;
+ }
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ const TextureMap &Textures() const {
+ return textures;
+ }
+
+ const LayeredTextureMap &LayeredTextures() const {
+ return layeredTextures;
+ }
+
+private:
+ std::string shading;
+ bool multilayer;
+ const PropertyTable *props;
+
+ TextureMap textures;
+ LayeredTextureMap layeredTextures;
+};
+
+// signed int keys (this can happen!)
+typedef std::vector<int64_t> KeyTimeList;
+typedef std::vector<float> KeyValueList;
+
+/** Represents a FBX animation curve (i.e. a 1-dimensional set of keyframes and values therefor) */
+class AnimationCurve : public Object {
+public:
+ AnimationCurve(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~AnimationCurve();
+
+ /** get list of keyframe positions (time).
+ * Invariant: |GetKeys()| > 0 */
+ const KeyTimeList &GetKeys() const {
+ return keys;
+ }
+
+ /** get list of keyframe values.
+ * Invariant: |GetKeys()| == |GetValues()| && |GetKeys()| > 0*/
+ const KeyValueList &GetValues() const {
+ return values;
+ }
+
+ const std::map<int64_t, float> &GetValueTimeTrack() const {
+ return keyvalues;
+ }
+
+ const std::vector<float> &GetAttributes() const {
+ return attributes;
+ }
+
+ const std::vector<unsigned int> &GetFlags() const {
+ return flags;
+ }
+
+private:
+ KeyTimeList keys;
+ KeyValueList values;
+ std::vector<float> attributes;
+ std::map<int64_t, float> keyvalues;
+ std::vector<unsigned int> flags;
+};
+
+/* Typedef for pointers for the animation handler */
+typedef std::shared_ptr<AnimationCurve> AnimationCurvePtr;
+typedef std::weak_ptr<AnimationCurve> AnimationCurveWeakPtr;
+typedef std::map<std::string, const AnimationCurve *> AnimationMap;
+
+/* Animation Curve node ptr */
+typedef std::shared_ptr<AnimationCurveNode> AnimationCurveNodePtr;
+typedef std::weak_ptr<AnimationCurveNode> AnimationCurveNodeWeakPtr;
+
+/** Represents a FBX animation curve (i.e. a mapping from single animation curves to nodes) */
+class AnimationCurveNode : public Object {
+public:
+ /* the optional white list specifies a list of property names for which the caller
+ wants animations for. If the curve node does not match one of these, std::range_error
+ will be thrown. */
+ AnimationCurveNode(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc,
+ const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0);
+
+ virtual ~AnimationCurveNode();
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ const AnimationMap &Curves() const;
+
+ /** Object the curve is assigned to, this can be NULL if the
+ * target object has no DOM representation or could not
+ * be read for other reasons.*/
+ Object *Target() const {
+ return target;
+ }
+
+ Model *TargetAsModel() const {
+ return dynamic_cast<Model *>(target);
+ }
+
+ NodeAttribute *TargetAsNodeAttribute() const {
+ return dynamic_cast<NodeAttribute *>(target);
+ }
+
+ /** Property of Target() that is being animated*/
+ const std::string &TargetProperty() const {
+ return prop;
+ }
+
+private:
+ Object *target = nullptr;
+ const PropertyTable *props;
+ mutable AnimationMap curves;
+ std::string prop;
+ const Document &doc;
+};
+
+typedef std::vector<const AnimationCurveNode *> AnimationCurveNodeList;
+
+typedef std::shared_ptr<AnimationLayer> AnimationLayerPtr;
+typedef std::weak_ptr<AnimationLayer> AnimationLayerWeakPtr;
+typedef std::vector<const AnimationLayer *> AnimationLayerList;
+
+/** Represents a FBX animation layer (i.e. a list of node animations) */
+class AnimationLayer : public Object {
+public:
+ AnimationLayer(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~AnimationLayer();
+
+ const PropertyTable *Props() const {
+ //ai_assert(props.get());
+ return props;
+ }
+
+ /* the optional white list specifies a list of property names for which the caller
+ wants animations for. Curves not matching this list will not be added to the
+ animation layer. */
+ const AnimationCurveNodeList Nodes(const char *const *target_prop_whitelist = nullptr, size_t whitelist_size = 0) const;
+
+private:
+ const PropertyTable *props;
+ const Document &doc;
+};
+
+/** Represents a FBX animation stack (i.e. a list of animation layers) */
+class AnimationStack : public Object {
+public:
+ AnimationStack(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~AnimationStack();
+
+ fbx_simple_property(LocalStart, int64_t, 0L);
+ fbx_simple_property(LocalStop, int64_t, 0L);
+ fbx_simple_property(ReferenceStart, int64_t, 0L);
+ fbx_simple_property(ReferenceStop, int64_t, 0L);
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ const AnimationLayerList &Layers() const {
+ return layers;
+ }
+
+private:
+ const PropertyTable *props = nullptr;
+ AnimationLayerList layers;
+};
+
+/** DOM class for deformers */
+class Deformer : public Object {
+public:
+ Deformer(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Deformer();
+
+ const PropertyTable *Props() const {
+ //ai_assert(props.get());
+ return props;
+ }
+
+private:
+ const PropertyTable *props;
+};
+
+/** Constraints are from Maya they can help us with BoneAttachments :) **/
+class Constraint : public Object {
+public:
+ Constraint(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+ virtual ~Constraint();
+
+private:
+ const PropertyTable *props;
+};
+
+typedef std::vector<float> WeightArray;
+typedef std::vector<unsigned int> WeightIndexArray;
+
+/** DOM class for BlendShapeChannel deformers */
+class BlendShapeChannel : public Deformer {
+public:
+ BlendShapeChannel(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~BlendShapeChannel();
+
+ float DeformPercent() const {
+ return percent;
+ }
+
+ const WeightArray &GetFullWeights() const {
+ return fullWeights;
+ }
+
+ const std::vector<const ShapeGeometry *> &GetShapeGeometries() const {
+ return shapeGeometries;
+ }
+
+private:
+ float percent;
+ WeightArray fullWeights;
+ std::vector<const ShapeGeometry *> shapeGeometries;
+};
+
+/** DOM class for BlendShape deformers */
+class BlendShape : public Deformer {
+public:
+ BlendShape(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~BlendShape();
+
+ const std::vector<const BlendShapeChannel *> &BlendShapeChannels() const {
+ return blendShapeChannels;
+ }
+
+private:
+ std::vector<const BlendShapeChannel *> blendShapeChannels;
+};
+
+/** DOM class for skin deformer clusters (aka sub-deformers) */
+class Cluster : public Deformer {
+public:
+ Cluster(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Cluster();
+
+ /** get the list of deformer weights associated with this cluster.
+ * Use #GetIndices() to get the associated vertices. Both arrays
+ * have the same size (and may also be empty). */
+ const std::vector<float> &GetWeights() const {
+ return weights;
+ }
+
+ /** get indices into the vertex data of the geometry associated
+ * with this cluster. Use #GetWeights() to get the associated weights.
+ * Both arrays have the same size (and may also be empty). */
+ const std::vector<unsigned int> &GetIndices() const {
+ return indices;
+ }
+
+ /** */
+ const Transform &GetTransform() const {
+ return transform;
+ }
+
+ const Transform &TransformLink() const {
+ return transformLink;
+ }
+
+ const Model *TargetNode() const {
+ return node;
+ }
+
+ const Transform &TransformAssociateModel() const {
+ return transformAssociateModel;
+ }
+
+ bool TransformAssociateModelValid() const {
+ return valid_transformAssociateModel;
+ }
+
+ // property is not in the fbx file
+ // if the cluster has an associate model
+ // we then have an additive type
+ enum SkinLinkMode {
+ SkinLinkMode_Normalized = 0,
+ SkinLinkMode_Additive = 1
+ };
+
+ SkinLinkMode GetLinkMode() {
+ return link_mode;
+ }
+
+private:
+ std::vector<float> weights;
+ std::vector<unsigned int> indices;
+
+ Transform transform;
+ Transform transformLink;
+ Transform transformAssociateModel;
+ SkinLinkMode link_mode;
+ bool valid_transformAssociateModel;
+ const Model *node = nullptr;
+};
+
+/** DOM class for skin deformers */
+class Skin : public Deformer {
+public:
+ Skin(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name);
+
+ virtual ~Skin();
+
+ float DeformAccuracy() const {
+ return accuracy;
+ }
+
+ const std::vector<const Cluster *> &Clusters() const {
+ return clusters;
+ }
+
+ enum SkinType {
+ Skin_Rigid = 0,
+ Skin_Linear,
+ Skin_DualQuaternion,
+ Skin_Blend
+ };
+
+ const SkinType &GetSkinType() const {
+ return skinType;
+ }
+
+private:
+ float accuracy;
+ SkinType skinType;
+ std::vector<const Cluster *> clusters;
+};
+
+/** Represents a link between two FBX objects. */
+class Connection {
+public:
+ Connection(uint64_t insertionOrder, uint64_t src, uint64_t dest, const std::string &prop, const Document &doc);
+ ~Connection();
+
+ // note: a connection ensures that the source and dest objects exist, but
+ // not that they have DOM representations, so the return value of one of
+ // these functions can still be NULL.
+ Object *SourceObject() const;
+ Object *DestinationObject() const;
+
+ // these, however, are always guaranteed to be valid
+ LazyObject *LazySourceObject() const;
+ LazyObject *LazyDestinationObject() const;
+
+ /** return the name of the property the connection is attached to.
+ * this is an empty string for object to object (OO) connections. */
+ const std::string &PropertyName() const {
+ return prop;
+ }
+
+ uint64_t InsertionOrder() const {
+ return insertionOrder;
+ }
+
+ int CompareTo(const Connection *c) const {
+ //ai_assert(nullptr != c);
+
+ // note: can't subtract because this would overflow uint64_t
+ if (InsertionOrder() > c->InsertionOrder()) {
+ return 1;
+ } else if (InsertionOrder() < c->InsertionOrder()) {
+ return -1;
+ }
+ return 0;
+ }
+
+ bool Compare(const Connection *c) const {
+ //ai_assert(nullptr != c);
+
+ return InsertionOrder() < c->InsertionOrder();
+ }
+
+public:
+ uint64_t insertionOrder;
+ const std::string prop;
+
+ uint64_t src, dest;
+ const Document &doc;
+};
+
+// 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;
+typedef std::map<std::string, const PropertyTable *> PropertyTemplateMap;
+typedef std::multimap<uint64_t, const Connection *> ConnectionMap;
+
+/** DOM class for global document settings, a single instance per document can
+ * be accessed via Document.Globals(). */
+class FileGlobalSettings {
+public:
+ FileGlobalSettings(const Document &doc, const PropertyTable *props);
+
+ ~FileGlobalSettings();
+
+ const PropertyTable *Props() const {
+ return props;
+ }
+
+ const Document &GetDocument() const {
+ return doc;
+ }
+
+ fbx_simple_property(UpAxis, int, 1);
+ fbx_simple_property(UpAxisSign, int, 1);
+ fbx_simple_property(FrontAxis, int, 2);
+ fbx_simple_property(FrontAxisSign, int, 1);
+ fbx_simple_property(CoordAxis, int, 0);
+ fbx_simple_property(CoordAxisSign, int, 1);
+ fbx_simple_property(OriginalUpAxis, int, 0);
+ fbx_simple_property(OriginalUpAxisSign, int, 1);
+ fbx_simple_property(UnitScaleFactor, float, 1);
+ fbx_simple_property(OriginalUnitScaleFactor, float, 1);
+ fbx_simple_property(AmbientColor, Vector3, Vector3(0, 0, 0));
+ fbx_simple_property(DefaultCamera, std::string, "");
+
+ enum FrameRate {
+ FrameRate_DEFAULT = 0,
+ FrameRate_120 = 1,
+ FrameRate_100 = 2,
+ FrameRate_60 = 3,
+ FrameRate_50 = 4,
+ FrameRate_48 = 5,
+ FrameRate_30 = 6,
+ FrameRate_30_DROP = 7,
+ FrameRate_NTSC_DROP_FRAME = 8,
+ FrameRate_NTSC_FULL_FRAME = 9,
+ FrameRate_PAL = 10,
+ FrameRate_CINEMA = 11,
+ FrameRate_1000 = 12,
+ FrameRate_CINEMA_ND = 13,
+ FrameRate_CUSTOM = 14,
+
+ FrameRate_MAX // end-of-enum sentinel
+ };
+
+ fbx_simple_enum_property(TimeMode, FrameRate, FrameRate_DEFAULT);
+ fbx_simple_property(TimeSpanStart, uint64_t, 0L);
+ fbx_simple_property(TimeSpanStop, uint64_t, 0L);
+ fbx_simple_property(CustomFrameRate, float, -1.0f);
+
+private:
+ const PropertyTable *props = nullptr;
+ const Document &doc;
+};
+
+/** DOM root for a FBX file */
+class Document {
+public:
+ Document(const Parser &parser, const ImportSettings &settings);
+
+ ~Document();
+
+ LazyObject *GetObject(uint64_t id) const;
+
+ bool IsSafeToImport() const {
+ return SafeToImport;
+ }
+
+ bool IsBinary() const {
+ return parser.IsBinary();
+ }
+
+ unsigned int FBXVersion() const {
+ return fbxVersion;
+ }
+
+ const std::string &Creator() const {
+ return creator;
+ }
+
+ // elements (in this order): Year, Month, Day, Hour, Second, Millisecond
+ const unsigned int *CreationTimeStamp() const {
+ return creationTimeStamp;
+ }
+
+ const FileGlobalSettings *GlobalSettingsPtr() const {
+ return globals.get();
+ }
+
+ const PropertyTable *GetMetadataProperties() const {
+ return metadata_properties;
+ }
+
+ const PropertyTemplateMap &Templates() const {
+ return templates;
+ }
+
+ const ObjectMap &Objects() const {
+ return objects;
+ }
+
+ const ImportSettings &Settings() const {
+ return settings;
+ }
+
+ const ConnectionMap &ConnectionsBySource() const {
+ return src_connections;
+ }
+
+ const ConnectionMap &ConnectionsByDestination() const {
+ return dest_connections;
+ }
+
+ // note: the implicit rule in all DOM classes is to always resolve
+ // from destination to source (since the FBX object hierarchy is,
+ // with very few exceptions, a DAG, this avoids cycles). In all
+ // cases that may involve back-facing edges in the object graph,
+ // use LazyObject::IsBeingConstructed() to check.
+
+ std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source) const;
+ std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest) const;
+
+ std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source, const char *classname) const;
+ std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest, const char *classname) const;
+
+ std::vector<const Connection *> GetConnectionsBySourceSequenced(uint64_t source,
+ const char *const *classnames, size_t count) const;
+ std::vector<const Connection *> GetConnectionsByDestinationSequenced(uint64_t dest,
+ const char *const *classnames,
+ size_t count) const;
+
+ const std::vector<const AnimationStack *> &AnimationStacks() const;
+ const std::vector<uint64_t> &GetAnimationStackIDs() const {
+ return animationStacks;
+ }
+
+ const std::vector<uint64_t> &GetConstraintStackIDs() const {
+ return constraints;
+ }
+
+ const std::vector<uint64_t> &GetBindPoseIDs() const {
+ return bind_poses;
+ };
+
+ const std::vector<uint64_t> &GetMaterialIDs() const {
+ return materials;
+ };
+
+ const std::vector<uint64_t> &GetSkinIDs() const {
+ return skins;
+ }
+
+private:
+ std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, const ConnectionMap &) const;
+ std::vector<const Connection *> GetConnectionsSequenced(uint64_t id, bool is_src,
+ const ConnectionMap &,
+ const char *const *classnames,
+ size_t count) const;
+ bool ReadHeader();
+ void ReadObjects();
+ void ReadPropertyTemplates();
+ void ReadConnections();
+ void ReadGlobalSettings();
+
+private:
+ const ImportSettings &settings;
+
+ ObjectMap objects;
+ const Parser &parser;
+ bool SafeToImport = false;
+
+ PropertyTemplateMap templates;
+ ConnectionMap src_connections;
+ ConnectionMap dest_connections;
+
+ unsigned int fbxVersion = 0;
+ std::string creator;
+ unsigned int creationTimeStamp[7] = { 0 };
+
+ std::vector<uint64_t> animationStacks;
+ std::vector<uint64_t> bind_poses;
+ // constraints aren't in the tree / at least they are not easy to access.
+ std::vector<uint64_t> constraints;
+ std::vector<uint64_t> materials;
+ std::vector<uint64_t> skins;
+ mutable std::vector<const AnimationStack *> animationStacksResolved;
+ PropertyTable *metadata_properties = nullptr;
+ std::shared_ptr<FileGlobalSettings> globals = nullptr;
+};
+} // namespace FBXDocParser
+
+namespace std {
+template <>
+struct hash<const FBXDocParser::Video> {
+ std::size_t operator()(const FBXDocParser::Video &video) const {
+ using std::hash;
+ using std::size_t;
+ using std::string;
+
+ size_t res = 17;
+ res = res * 31 + hash<string>()(video.Name());
+ res = res * 31 + hash<string>()(video.RelativeFilename());
+ res = res * 31 + hash<string>()(video.Type());
+
+ return res;
+ }
+};
+} // namespace std
+
+#endif // FBX_DOCUMENT_H
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.cpp b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
new file mode 100644
index 0000000000..70b2b3bbe9
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.cpp
@@ -0,0 +1,172 @@
+/*************************************************************************/
+/* FBXDocumentUtil.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 "FBXDocumentUtil.h"
+#include "FBXDocument.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+#include "FBXUtil.h"
+#include "core/string/print_string.h"
+
+namespace FBXDocParser {
+namespace Util {
+
+void DOMError(const std::string &message) {
+ print_error("[FBX-DOM]" + String(message.c_str()));
+}
+
+void DOMError(const std::string &message, const Token *token) {
+ print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMError(const std::string &message, const std::shared_ptr<Token> token) {
+ print_error("[FBX-DOM]" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMError(const std::string &message, const Element *element /*= NULL*/) {
+ if (element) {
+ DOMError(message, element->KeyToken());
+ }
+ print_error("[FBX-DOM] " + String(message.c_str()));
+}
+
+void DOMError(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) {
+ if (element) {
+ DOMError(message, element->KeyToken());
+ }
+ print_error("[FBX-DOM] " + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message) {
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message, const Token *token) {
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMWarning(const std::string &message, const Element *element /*= NULL*/) {
+ if (element) {
+ DOMWarning(message, element->KeyToken());
+ return;
+ }
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+void DOMWarning(const std::string &message, const std::shared_ptr<Token> token) {
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()) + ";" + String(token->StringContents().c_str()));
+}
+
+void DOMWarning(const std::string &message, const std::shared_ptr<Element> element /*= NULL*/) {
+ if (element) {
+ DOMWarning(message, element->KeyToken());
+ return;
+ }
+ print_verbose("[FBX-DOM] warning:" + String(message.c_str()));
+}
+
+// ------------------------------------------------------------------------------------------------
+// fetch a property table and the corresponding property template
+const PropertyTable *GetPropertyTable(const Document &doc,
+ const std::string &templateName,
+ const ElementPtr element,
+ const ScopePtr sc,
+ bool no_warn /*= false*/) {
+ // todo: make this an abstraction
+ const ElementPtr Properties70 = sc->GetElement("Properties70");
+ const PropertyTable *templateProps = static_cast<const PropertyTable *>(nullptr);
+
+ if (templateName.length()) {
+ PropertyTemplateMap::const_iterator it = doc.Templates().find(templateName);
+ if (it != doc.Templates().end()) {
+ templateProps = (*it).second;
+ }
+ }
+
+ if (!Properties70 || !Properties70->Compound()) {
+ if (!no_warn) {
+ DOMWarning("property table (Properties70) not found", element);
+ }
+ if (templateProps) {
+ return templateProps;
+ } else {
+ return new const PropertyTable();
+ }
+ }
+
+ return new PropertyTable(Properties70, templateProps);
+}
+} // namespace Util
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXDocumentUtil.h b/modules/fbx/fbx_parser/FBXDocumentUtil.h
new file mode 100644
index 0000000000..fb6eb04aea
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXDocumentUtil.h
@@ -0,0 +1,141 @@
+/*************************************************************************/
+/* FBXDocumentUtil.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+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.h
+ * @brief FBX internal utilities used by the DOM reading code
+ */
+#ifndef FBX_DOCUMENT_UTIL_H
+#define FBX_DOCUMENT_UTIL_H
+
+#include "FBXDocument.h"
+#include <memory>
+#include <string>
+
+struct Token;
+struct Element;
+
+namespace FBXDocParser {
+namespace Util {
+
+// Parser errors
+void DOMError(const std::string &message);
+void DOMError(const std::string &message, const Token *token);
+void DOMError(const std::string &message, const Element *element);
+void DOMError(const std::string &message, const std::shared_ptr<Element> element);
+void DOMError(const std::string &message, const std::shared_ptr<Token> token);
+
+// Parser warnings
+void DOMWarning(const std::string &message);
+void DOMWarning(const std::string &message, const Token *token);
+void DOMWarning(const std::string &message, const Element *element);
+void DOMWarning(const std::string &message, const std::shared_ptr<Token> token);
+void DOMWarning(const std::string &message, const std::shared_ptr<Element> element);
+
+// fetch a property table and the corresponding property template
+const PropertyTable *GetPropertyTable(const Document &doc,
+ const std::string &templateName,
+ const ElementPtr element,
+ const ScopePtr sc,
+ bool no_warn = false);
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+const T *ProcessSimpleConnection(const Connection &con,
+ bool is_object_property_conn,
+ const char *name,
+ const ElementPtr element,
+ const char **propNameOut = nullptr) {
+ if (is_object_property_conn && !con.PropertyName().length()) {
+ DOMWarning("expected incoming " + std::string(name) +
+ " link to be an object-object connection, ignoring",
+ element);
+ return nullptr;
+ } else if (!is_object_property_conn && con.PropertyName().length()) {
+ DOMWarning("expected incoming " + std::string(name) +
+ " link to be an object-property connection, ignoring",
+ element);
+ return nullptr;
+ }
+
+ if (is_object_property_conn && propNameOut) {
+ // note: this is ok, the return value of PropertyValue() is guaranteed to
+ // remain valid and unchanged as long as the document exists.
+ *propNameOut = con.PropertyName().c_str();
+ }
+
+ // Cast Object to AnimationPlayer for example using safe functions, which return nullptr etc
+ Object *ob = con.SourceObject();
+ ERR_FAIL_COND_V_MSG(!ob, nullptr, "Failed to load object from SourceObject ptr");
+ return dynamic_cast<const T *>(ob);
+}
+} // namespace Util
+} // namespace FBXDocParser
+
+#endif // FBX_DOCUMENT_UTIL_H
diff --git a/modules/fbx/fbx_parser/FBXImportSettings.h b/modules/fbx/fbx_parser/FBXImportSettings.h
new file mode 100644
index 0000000000..4abca6b990
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXImportSettings.h
@@ -0,0 +1,173 @@
+/*************************************************************************/
+/* FBXImportSettings.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXImportSettings.h
+ * @brief FBX importer runtime configuration
+ */
+#ifndef FBX_IMPORT_SETTINGS_H
+#define FBX_IMPORT_SETTINGS_H
+
+namespace FBXDocParser {
+
+/** FBX import settings, parts of which are publicly accessible via their corresponding AI_CONFIG constants */
+struct ImportSettings {
+ ImportSettings() :
+ strictMode(true), readAllLayers(true), readAllMaterials(true), readMaterials(true), readTextures(true), readCameras(true), readLights(true), readAnimations(true), readWeights(true), preservePivots(true), optimizeEmptyAnimationCurves(true), useLegacyEmbeddedTextureNaming(false), removeEmptyBones(true), convertToMeters(false) {
+ // empty
+ }
+
+ /** enable strict mode:
+ * - only accept fbx 2012, 2013 files
+ * - on the slightest error, give up.
+ *
+ * Basically, strict mode means that the fbx file will actually
+ * be validated. Strict mode is off by default. */
+ bool strictMode;
+
+ /** specifies whether all geometry layers are read and scanned for
+ * usable data channels. The FBX spec indicates that many readers
+ * will only read the first channel and that this is in some way
+ * the recommended way- in reality, however, it happens a lot that
+ * vertex data is spread among multiple layers. The default
+ * value for this option is true.*/
+ bool readAllLayers;
+
+ /** specifies whether all materials are read, or only those that
+ * are referenced by at least one mesh. Reading all materials
+ * may make FBX reading a lot slower since all objects
+ * need to be processed .
+ * This bit is ignored unless readMaterials=true*/
+ bool readAllMaterials;
+
+ /** import materials (true) or skip them and assign a default
+ * material. The default value is true.*/
+ bool readMaterials;
+
+ /** import embedded textures? Default value is true.*/
+ bool readTextures;
+
+ /** import cameras? Default value is true.*/
+ bool readCameras;
+
+ /** import light sources? Default value is true.*/
+ bool readLights;
+
+ /** import animations (i.e. animation curves, the node
+ * skeleton is always imported). Default value is true. */
+ bool readAnimations;
+
+ /** read bones (vertex weights and deform info).
+ * Default value is true. */
+ bool readWeights;
+
+ /** preserve transformation pivots and offsets. Since these can
+ * not directly be represented in assimp, additional dummy
+ * nodes will be generated. Note that settings this to false
+ * can make animation import a lot slower. The default value
+ * is true.
+ *
+ * The naming scheme for the generated nodes is:
+ * <OriginalName>_$AssimpFbx$_<TransformName>
+ *
+ * where <TransformName> is one of
+ * RotationPivot
+ * RotationOffset
+ * PreRotation
+ * PostRotation
+ * ScalingPivot
+ * ScalingOffset
+ * Translation
+ * Scaling
+ * Rotation
+ **/
+ bool preservePivots;
+
+ /** do not import animation curves that specify a constant
+ * values matching the corresponding node transformation.
+ * The default value is true. */
+ bool optimizeEmptyAnimationCurves;
+
+ /** use legacy naming for embedded textures eg: (*0, *1, *2)
+ */
+ bool useLegacyEmbeddedTextureNaming;
+
+ /** Empty bones shall be removed
+ */
+ bool removeEmptyBones;
+
+ /** Set to true to perform a conversion from cm to meter after the import
+ */
+ bool convertToMeters;
+};
+} // namespace FBXDocParser
+
+#endif // FBX_IMPORT_SETTINGS_H
diff --git a/modules/fbx/fbx_parser/FBXMaterial.cpp b/modules/fbx/fbx_parser/FBXMaterial.cpp
new file mode 100644
index 0000000000..5d25809d7b
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXMaterial.cpp
@@ -0,0 +1,407 @@
+/*************************************************************************/
+/* FBXMaterial.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2020, 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 FBXMaterial.cpp
+ * @brief Assimp::FBX::Material and Assimp::FBX::Texture implementation
+ */
+
+#include "ByteSwapper.h"
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXParser.h"
+#include "FBXProperties.h"
+
+#include "FBXUtil.h"
+#include <algorithm> // std::transform
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Material::Material(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const ElementPtr ShadingModel = sc->GetElement("ShadingModel");
+ const ElementPtr MultiLayer = sc->GetElement("MultiLayer");
+
+ if (MultiLayer) {
+ multilayer = !!ParseTokenAsInt(GetRequiredToken(MultiLayer, 0));
+ }
+
+ if (ShadingModel) {
+ shading = ParseTokenAsString(GetRequiredToken(ShadingModel, 0));
+ } else {
+ DOMWarning("shading mode not specified, assuming phong", element);
+ shading = "phong";
+ }
+
+ std::string templateName;
+
+ if (shading == "phong") {
+ templateName = "Material.Phong";
+ } else if (shading == "lambert") {
+ templateName = "Material.Lambert";
+ } else if (shading == "unknown") {
+ templateName = "Material.StingRay";
+ } else {
+ DOMWarning("shading mode not recognized: " + shading, element);
+ }
+
+ props = GetPropertyTable(doc, templateName, element, sc);
+
+ // resolve texture links
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+ for (const Connection *con : conns) {
+ // texture link to properties, not objects
+ if (!con->PropertyName().length()) {
+ continue;
+ }
+
+ Object *ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for texture link, ignoring", element);
+ continue;
+ }
+
+ const Texture *tex = dynamic_cast<const Texture *>(ob);
+ if (!tex) {
+ LayeredTexture *layeredTexture = dynamic_cast<LayeredTexture *>(ob);
+
+ if (!layeredTexture) {
+ DOMWarning("source object for texture link is not a texture or layered texture, ignoring", element);
+ continue;
+ }
+
+ const std::string &prop = con->PropertyName();
+ if (layeredTextures.find(prop) != layeredTextures.end()) {
+ DOMWarning("duplicate layered texture link: " + prop, element);
+ }
+
+ layeredTextures[prop] = layeredTexture;
+ layeredTexture->fillTexture(doc);
+ } else {
+ const std::string &prop = con->PropertyName();
+ if (textures.find(prop) != textures.end()) {
+ DOMWarning("duplicate texture link: " + prop, element);
+ }
+
+ textures[prop] = tex;
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Material::~Material() {
+ if (props != nullptr) {
+ delete props;
+ props = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Texture::Texture(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name), uvScaling(1.0f, 1.0f), media(nullptr) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const ElementPtr Type = sc->GetElement("Type");
+ const ElementPtr FileName = sc->GetElement("FileName");
+ const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
+ const ElementPtr ModelUVTranslation = sc->GetElement("ModelUVTranslation");
+ const ElementPtr ModelUVScaling = sc->GetElement("ModelUVScaling");
+ const ElementPtr Texture_Alpha_Source = sc->GetElement("Texture_Alpha_Source");
+ const ElementPtr Cropping = sc->GetElement("Cropping");
+
+ if (Type) {
+ type = ParseTokenAsString(GetRequiredToken(Type, 0));
+ }
+
+ if (FileName) {
+ fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
+ }
+
+ if (RelativeFilename) {
+ relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
+ }
+
+ if (ModelUVTranslation) {
+ uvTrans = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 0)),
+ ParseTokenAsFloat(GetRequiredToken(ModelUVTranslation, 1)));
+ }
+
+ if (ModelUVScaling) {
+ uvScaling = Vector2(ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 0)),
+ ParseTokenAsFloat(GetRequiredToken(ModelUVScaling, 1)));
+ }
+
+ if (Cropping) {
+ crop[0] = ParseTokenAsInt(GetRequiredToken(Cropping, 0));
+ crop[1] = ParseTokenAsInt(GetRequiredToken(Cropping, 1));
+ crop[2] = ParseTokenAsInt(GetRequiredToken(Cropping, 2));
+ crop[3] = ParseTokenAsInt(GetRequiredToken(Cropping, 3));
+ } else {
+ // vc8 doesn't support the crop() syntax in initialization lists
+ // (and vc9 WARNS about the new (i.e. compliant) behaviour).
+ crop[0] = crop[1] = crop[2] = crop[3] = 0;
+ }
+
+ if (Texture_Alpha_Source) {
+ alphaSource = ParseTokenAsString(GetRequiredToken(Texture_Alpha_Source, 0));
+ }
+
+ props = GetPropertyTable(doc, "Texture.FbxFileTexture", element, sc);
+
+ // 3DS Max and FBX SDK use "Scaling" and "Translation" instead of "ModelUVScaling" and "ModelUVTranslation". Use these properties if available.
+ bool ok;
+ const Vector3 &scaling = PropertyGet<Vector3>(props, "Scaling", ok);
+ if (ok) {
+ uvScaling.x = scaling.x;
+ uvScaling.y = scaling.y;
+ }
+
+ const Vector3 &trans = PropertyGet<Vector3>(props, "Translation", ok);
+ if (ok) {
+ uvTrans.x = trans.x;
+ uvTrans.y = trans.y;
+ }
+
+ // resolve video links
+ if (doc.Settings().readTextures) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+ for (const Connection *con : conns) {
+ const Object *const ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for texture link, ignoring", element);
+ continue;
+ }
+
+ const Video *const video = dynamic_cast<const Video *>(ob);
+ if (video) {
+ media = video;
+ }
+ }
+ }
+}
+
+Texture::~Texture() {
+ if (props != nullptr) {
+ delete props;
+ props = nullptr;
+ }
+}
+
+LayeredTexture::LayeredTexture(uint64_t id, const ElementPtr element, const Document & /*doc*/, const std::string &name) :
+ Object(id, element, name), blendMode(BlendMode_Modulate), alpha(1) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ ElementPtr BlendModes = sc->GetElement("BlendModes");
+ ElementPtr Alphas = sc->GetElement("Alphas");
+
+ if (BlendModes != 0) {
+ blendMode = (BlendMode)ParseTokenAsInt(GetRequiredToken(BlendModes, 0));
+ }
+ if (Alphas != 0) {
+ alpha = ParseTokenAsFloat(GetRequiredToken(Alphas, 0));
+ }
+}
+
+LayeredTexture::~LayeredTexture() {
+}
+
+void LayeredTexture::fillTexture(const Document &doc) {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID());
+ for (size_t i = 0; i < conns.size(); ++i) {
+ const Connection *con = conns.at(i);
+
+ const Object *const ob = con->SourceObject();
+ if (!ob) {
+ DOMWarning("failed to read source object for texture link, ignoring", element);
+ continue;
+ }
+
+ const Texture *const tex = dynamic_cast<const Texture *>(ob);
+
+ textures.push_back(tex);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Video::Video(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name), contentLength(0), content(0) {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const ElementPtr Type = sc->GetElement("Type");
+ // File Version 7500 Crashes if this is not checked fully.
+ // As of writing this comment 7700 exists, in August 2020
+ ElementPtr FileName = nullptr;
+ if (HasElement(sc, "Filename")) {
+ FileName = (ElementPtr)sc->GetElement("Filename");
+ } else if (HasElement(sc, "FileName")) {
+ FileName = (ElementPtr)sc->GetElement("FileName");
+ } else {
+ print_error("file has invalid video material returning...");
+ return;
+ }
+ const ElementPtr RelativeFilename = sc->GetElement("RelativeFilename");
+ const ElementPtr Content = sc->GetElement("Content");
+
+ if (Type) {
+ type = ParseTokenAsString(GetRequiredToken(Type, 0));
+ }
+
+ if (FileName) {
+ fileName = ParseTokenAsString(GetRequiredToken(FileName, 0));
+ }
+
+ if (RelativeFilename) {
+ relativeFileName = ParseTokenAsString(GetRequiredToken(RelativeFilename, 0));
+ }
+
+ if (Content && !Content->Tokens().empty()) {
+ //this field is omitted when the embedded texture is already loaded, let's ignore if it's not found
+ try {
+ const Token *token = GetRequiredToken(Content, 0);
+ const char *data = token->begin();
+ if (!token->IsBinary()) {
+ if (*data != '"') {
+ DOMError("embedded content is not surrounded by quotation marks", element);
+ } else {
+ size_t targetLength = 0;
+ auto numTokens = Content->Tokens().size();
+ // First time compute size (it could be large like 64Gb and it is good to allocate it once)
+ for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
+ const Token *dataToken = GetRequiredToken(Content, tokenIdx);
+ size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
+ const char *base64data = dataToken->begin() + 1;
+ const size_t outLength = Util::ComputeDecodedSizeBase64(base64data, tokenLength);
+ if (outLength == 0) {
+ DOMError("Corrupted embedded content found", element);
+ }
+ targetLength += outLength;
+ }
+ if (targetLength == 0) {
+ DOMError("Corrupted embedded content found", element);
+ } else {
+ content = new uint8_t[targetLength];
+ contentLength = static_cast<uint64_t>(targetLength);
+ size_t dst_offset = 0;
+ for (uint32_t tokenIdx = 0; tokenIdx < numTokens; ++tokenIdx) {
+ const Token *dataToken = GetRequiredToken(Content, tokenIdx);
+ ERR_FAIL_COND(!dataToken);
+ size_t tokenLength = dataToken->end() - dataToken->begin() - 2; // ignore double quotes
+ const char *base64data = dataToken->begin() + 1;
+ dst_offset += Util::DecodeBase64(base64data, tokenLength, content + dst_offset, targetLength - dst_offset);
+ }
+ if (targetLength != dst_offset) {
+ delete[] content;
+ contentLength = 0;
+ DOMError("Corrupted embedded content found", element);
+ }
+ }
+ }
+ } else if (static_cast<size_t>(token->end() - data) < 5) {
+ DOMError("binary data array is too short, need five (5) bytes for type signature and element count", element);
+ } else if (*data != 'R') {
+ DOMWarning("video content is not raw binary data, ignoring", element);
+ } else {
+ // read number of elements
+ uint32_t len = 0;
+ ::memcpy(&len, data + 1, sizeof(len));
+ AI_SWAP4(len);
+
+ contentLength = len;
+
+ content = new uint8_t[len];
+ ::memcpy(content, data + 5, len);
+ }
+ } catch (...) {
+ // //we don't need the content data for contents that has already been loaded
+ // ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ",
+ // runtimeError.what());
+ }
+ }
+
+ props = GetPropertyTable(doc, "Video.FbxVideo", element, sc);
+}
+
+Video::~Video() {
+ if (content) {
+ delete[] content;
+ }
+
+ if (props != nullptr) {
+ delete props;
+ props = nullptr;
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.cpp b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
new file mode 100644
index 0000000000..4fa5fc0ac9
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXMeshGeometry.cpp
@@ -0,0 +1,485 @@
+/*************************************************************************/
+/* FBXMeshGeometry.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXMeshGeometry.cpp
+ * @brief Assimp::FBX::MeshGeometry implementation
+ */
+
+#include <functional>
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXImportSettings.h"
+#include "FBXMeshGeometry.h"
+#include "core/math/vector3.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Geometry::Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Object(id, element, name), skin() {
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), "Deformer");
+ for (const Connection *con : conns) {
+ const Skin *sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
+ if (sk) {
+ skin = sk;
+ }
+ const BlendShape *bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry",
+ element);
+ if (bsp) {
+ blendShapes.push_back(bsp);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Geometry::~Geometry() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+const std::vector<const BlendShape *> &Geometry::get_blend_shapes() const {
+ return blendShapes;
+}
+
+// ------------------------------------------------------------------------------------------------
+const Skin *Geometry::DeformerSkin() const {
+ return skin;
+}
+
+// ------------------------------------------------------------------------------------------------
+MeshGeometry::MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Geometry(id, element, name, doc) {
+ print_verbose("mesh name: " + String(name.c_str()));
+
+ ScopePtr sc = element->Compound();
+ ERR_FAIL_COND_MSG(sc == nullptr, "failed to read geometry, prevented crash");
+ ERR_FAIL_COND_MSG(!HasElement(sc, "Vertices"), "Detected mesh with no vertexes, didn't populate the mesh");
+
+ // must have Mesh elements:
+ const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
+ const ElementPtr PolygonVertexIndex = GetRequiredElement(sc, "PolygonVertexIndex", element);
+
+ if (HasElement(sc, "Edges")) {
+ const ElementPtr element_edges = GetRequiredElement(sc, "Edges", element);
+ ParseVectorDataArray(m_edges, element_edges);
+ }
+
+ // read mesh data into arrays
+ ParseVectorDataArray(m_vertices, Vertices);
+ ParseVectorDataArray(m_face_indices, PolygonVertexIndex);
+
+ ERR_FAIL_COND_MSG(m_vertices.empty(), "mesh with no vertexes in FBX file, did you mean to delete it?");
+ ERR_FAIL_COND_MSG(m_face_indices.empty(), "mesh has no faces, was this intended?");
+
+ // Retrieve layer elements, for all of the mesh
+ const ElementCollection &Layer = sc->GetCollection("Layer");
+
+ // Store all layers
+ std::vector<std::tuple<int, std::string>> valid_layers;
+
+ // now read the sub mesh information from the geometry (normals, uvs, etc)
+ for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
+ const ScopePtr layer = GetRequiredScope(it->second);
+ const ElementCollection &LayerElement = layer->GetCollection("LayerElement");
+ for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
+ std::string layer_name = eit->first;
+ ElementPtr element_layer = eit->second;
+ const ScopePtr layer_element = GetRequiredScope(element_layer);
+
+ // Actual usable 'type' LayerElementUV, LayerElementNormal, etc
+ const ElementPtr Type = GetRequiredElement(layer_element, "Type");
+ const ElementPtr TypedIndex = GetRequiredElement(layer_element, "TypedIndex");
+ const std::string &type = ParseTokenAsString(GetRequiredToken(Type, 0));
+ const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex, 0));
+
+ // we only need the layer name and the typed index.
+ valid_layers.push_back(std::tuple<int, std::string>(typedIndex, type));
+ }
+ }
+
+ // get object / mesh directly from the FBX by the element ID.
+ const ScopePtr top = GetRequiredScope(element);
+
+ // iterate over all layers for the mesh (uvs, normals, smoothing groups, colors, etc)
+ for (size_t x = 0; x < valid_layers.size(); x++) {
+ const int layer_id = std::get<0>(valid_layers[x]);
+ const std::string &layer_type_name = std::get<1>(valid_layers[x]);
+
+ // Get collection of elements from the XLayerMap (example: LayerElementUV)
+ // this must contain our proper elements.
+
+ // This is stupid, because it means we select them ALL not just the one we want.
+ // but it's fine we can match by id.
+
+ const ElementCollection &candidates = top->GetCollection(layer_type_name);
+
+ ElementMap::const_iterator iter;
+ for (iter = candidates.first; iter != candidates.second; ++iter) {
+ const ScopePtr layer_scope = GetRequiredScope(iter->second);
+ TokenPtr layer_token = GetRequiredToken(iter->second, 0);
+ const int index = ParseTokenAsInt(layer_token);
+
+ ERR_FAIL_COND_MSG(layer_scope == nullptr, "prevented crash, layer scope is invalid");
+
+ if (index == layer_id) {
+ const std::string &MappingInformationType = ParseTokenAsString(GetRequiredToken(
+ GetRequiredElement(layer_scope, "MappingInformationType"), 0));
+
+ const std::string &ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
+ GetRequiredElement(layer_scope, "ReferenceInformationType"), 0));
+
+ if (layer_type_name == "LayerElementUV") {
+ if (index == 0) {
+ m_uv_0 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
+ } else if (index == 1) {
+ m_uv_1 = resolve_vertex_data_array<Vector2>(layer_scope, MappingInformationType, ReferenceInformationType, "UV");
+ }
+ } else if (layer_type_name == "LayerElementMaterial") {
+ m_material_allocation_ids = resolve_vertex_data_array<int>(layer_scope, MappingInformationType, ReferenceInformationType, "Materials");
+ } else if (layer_type_name == "LayerElementNormal") {
+ m_normals = resolve_vertex_data_array<Vector3>(layer_scope, MappingInformationType, ReferenceInformationType, "Normals");
+ } else if (layer_type_name == "LayerElementColor") {
+ m_colors = resolve_vertex_data_array<Color>(layer_scope, MappingInformationType, ReferenceInformationType, "Colors", "ColorIndex");
+ // NOTE: this is a useful sanity check to ensure you're getting any color data which is not default.
+ // const Color first_color_check = m_colors.data[0];
+ // bool colors_are_all_the_same = true;
+ // size_t i = 1;
+ // for(i = 1; i < m_colors.data.size(); i++)
+ // {
+ // const Color current_color = m_colors.data[i];
+ // if(current_color.is_equal_approx(first_color_check))
+ // {
+ // continue;
+ // }
+ // else
+ // {
+ // colors_are_all_the_same = false;
+ // break;
+ // }
+ // }
+ //
+ // if(colors_are_all_the_same)
+ // {
+ // print_error("Color serialisation is not working for vertex colors some should be different in the test asset.");
+ // }
+ // else
+ // {
+ // print_verbose("Color array has unique colors at index: " + itos(i));
+ // }
+ }
+ }
+ }
+ }
+
+ print_verbose("Mesh statistics \nuv_0: " + m_uv_0.debug_info() + "\nuv_1: " + m_uv_1.debug_info() + "\nvertices: " + itos(m_vertices.size()));
+
+ // Compose the edge of the mesh.
+ // You can see how the edges are stored into the FBX here: https://gist.github.com/AndreaCatania/da81840f5aa3b2feedf189e26c5a87e6
+ for (size_t i = 0; i < m_edges.size(); i += 1) {
+ ERR_FAIL_INDEX_MSG((size_t)m_edges[i], m_face_indices.size(), "The edge is pointing to a weird location in the face indices. The FBX is corrupted.");
+ int polygon_vertex_0 = m_face_indices[m_edges[i]];
+ int polygon_vertex_1;
+ if (polygon_vertex_0 < 0) {
+ // The polygon_vertex_0 points to the end of a polygon, so it's
+ // connected with the beginning of polygon in the edge list.
+
+ // Fist invert the vertex.
+ polygon_vertex_0 = ~polygon_vertex_0;
+
+ // Search the start vertex of the polygon.
+ // Iterate from the polygon_vertex_index backward till the start of
+ // the polygon is found.
+ ERR_FAIL_COND_MSG(m_edges[i] - 1 < 0, "The polygon is not yet started and we already need the final vertex. This FBX is corrupted.");
+ bool found_it = false;
+ for (int x = m_edges[i] - 1; x >= 0; x -= 1) {
+ if (x == 0) {
+ // This for sure is the start.
+ polygon_vertex_1 = m_face_indices[x];
+ found_it = true;
+ break;
+ } else if (m_face_indices[x] < 0) {
+ // This is the end of the previous polygon, so the next is
+ // the start of the polygon we need.
+ polygon_vertex_1 = m_face_indices[x + 1];
+ found_it = true;
+ break;
+ }
+ }
+ // As the algorithm above, this check is useless. Because the first
+ // ever vertex is always considered the begining of a polygon.
+ ERR_FAIL_COND_MSG(found_it == false, "Was not possible to find the first vertex of this polygon. FBX file is corrupted.");
+
+ } else {
+ ERR_FAIL_INDEX_MSG((size_t)(m_edges[i] + 1), m_face_indices.size(), "FBX The other FBX edge seems to point to an invalid vertices. This FBX file is corrupted.");
+ // Take the next vertex
+ polygon_vertex_1 = m_face_indices[m_edges[i] + 1];
+ }
+
+ if (polygon_vertex_1 < 0) {
+ // We don't care if the `polygon_vertex_1` is the end of the polygon,
+ // for `polygon_vertex_1` so we can just invert it.
+ polygon_vertex_1 = ~polygon_vertex_1;
+ }
+
+ ERR_FAIL_COND_MSG(polygon_vertex_0 == polygon_vertex_1, "The vertices of this edge can't be the same, Is this a point???. This FBX file is corrupted.");
+
+ // Just create the edge.
+ edge_map.push_back({ polygon_vertex_0, polygon_vertex_1 });
+ }
+}
+
+MeshGeometry::~MeshGeometry() {
+ // empty
+}
+
+const std::vector<Vector3> &MeshGeometry::get_vertices() const {
+ return m_vertices;
+}
+
+const std::vector<MeshGeometry::Edge> &MeshGeometry::get_edge_map() const {
+ return edge_map;
+}
+
+const std::vector<int> &MeshGeometry::get_polygon_indices() const {
+ return m_face_indices;
+}
+
+const std::vector<int> &MeshGeometry::get_edges() const {
+ return m_edges;
+}
+
+const MeshGeometry::MappingData<Vector3> &MeshGeometry::get_normals() const {
+ return m_normals;
+}
+
+const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_0() const {
+ //print_verbose("get uv_0 " + m_uv_0.debug_info() );
+ return m_uv_0;
+}
+
+const MeshGeometry::MappingData<Vector2> &MeshGeometry::get_uv_1() const {
+ //print_verbose("get uv_1 " + m_uv_1.debug_info() );
+ return m_uv_1;
+}
+
+const MeshGeometry::MappingData<Color> &MeshGeometry::get_colors() const {
+ return m_colors;
+}
+
+const MeshGeometry::MappingData<int> &MeshGeometry::get_material_allocation_id() const {
+ return m_material_allocation_ids;
+}
+
+int MeshGeometry::get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b) {
+ for (size_t i = 0; i < p_map.size(); i += 1) {
+ if ((p_map[i].vertex_0 == p_vertex_a && p_map[i].vertex_1 == p_vertex_b) || (p_map[i].vertex_1 == p_vertex_a && p_map[i].vertex_0 == p_vertex_b)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+MeshGeometry::Edge MeshGeometry::get_edge(const std::vector<Edge> &p_map, int p_id) {
+ ERR_FAIL_INDEX_V_MSG((size_t)p_id, p_map.size(), Edge({ -1, -1 }), "ID not found.");
+ return p_map[p_id];
+}
+
+template <class T>
+MeshGeometry::MappingData<T> MeshGeometry::resolve_vertex_data_array(
+ const ScopePtr source,
+ const std::string &MappingInformationType,
+ const std::string &ReferenceInformationType,
+ const std::string &dataElementName,
+ const std::string &indexOverride) {
+ ERR_FAIL_COND_V_MSG(source == nullptr, MappingData<T>(), "Invalid scope operator preventing memory corruption");
+
+ // UVIndex, MaterialIndex, NormalIndex, etc..
+ std::string indexDataElementName;
+
+ if (indexOverride != "") {
+ // Colors should become ColorIndex
+ indexDataElementName = indexOverride;
+ } else {
+ // Some indexes will exist.
+ indexDataElementName = dataElementName + "Index";
+ }
+
+ // goal: expand everything to be per vertex
+
+ ReferenceType l_ref_type = ReferenceType::direct;
+
+ // Read the reference type into the enumeration
+ if (ReferenceInformationType == "IndexToDirect") {
+ l_ref_type = ReferenceType::index_to_direct;
+ } else if (ReferenceInformationType == "Index") {
+ // set non legacy index to direct mapping
+ l_ref_type = ReferenceType::index;
+ } else if (ReferenceInformationType == "Direct") {
+ l_ref_type = ReferenceType::direct;
+ } else {
+ ERR_FAIL_V_MSG(MappingData<T>(), "invalid reference type has the FBX format changed?");
+ }
+
+ MapType l_map_type = MapType::none;
+
+ if (MappingInformationType == "None") {
+ l_map_type = MapType::none;
+ } else if (MappingInformationType == "ByVertice") {
+ l_map_type = MapType::vertex;
+ } else if (MappingInformationType == "ByPolygonVertex") {
+ l_map_type = MapType::polygon_vertex;
+ } else if (MappingInformationType == "ByPolygon") {
+ l_map_type = MapType::polygon;
+ } else if (MappingInformationType == "ByEdge") {
+ l_map_type = MapType::edge;
+ } else if (MappingInformationType == "AllSame") {
+ l_map_type = MapType::all_the_same;
+ } else {
+ print_error("invalid mapping type: " + String(MappingInformationType.c_str()));
+ }
+
+ // create mapping data
+ MeshGeometry::MappingData<T> tempData;
+ tempData.map_type = l_map_type;
+ tempData.ref_type = l_ref_type;
+
+ // parse data into array
+ ParseVectorDataArray(tempData.data, GetRequiredElement(source, dataElementName));
+
+ // index array wont always exist
+ const ElementPtr element = GetOptionalElement(source, indexDataElementName);
+ if (element) {
+ ParseVectorDataArray(tempData.index, element);
+ }
+
+ return tempData;
+}
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Geometry(id, element, name, doc) {
+ const ScopePtr sc = element->Compound();
+ if (nullptr == sc) {
+ DOMError("failed to read Geometry object (class: Shape), no data scope found");
+ }
+ const ElementPtr Indexes = GetRequiredElement(sc, "Indexes", element);
+ const ElementPtr Normals = GetRequiredElement(sc, "Normals", element);
+ const ElementPtr Vertices = GetRequiredElement(sc, "Vertices", element);
+ ParseVectorDataArray(m_indices, Indexes);
+ ParseVectorDataArray(m_vertices, Vertices);
+ ParseVectorDataArray(m_normals, Normals);
+}
+
+// ------------------------------------------------------------------------------------------------
+ShapeGeometry::~ShapeGeometry() {
+ // empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &ShapeGeometry::GetVertices() const {
+ return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &ShapeGeometry::GetNormals() const {
+ return m_normals;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<unsigned int> &ShapeGeometry::GetIndices() const {
+ return m_indices;
+}
+// ------------------------------------------------------------------------------------------------
+LineGeometry::LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc) :
+ Geometry(id, element, name, doc) {
+ const ScopePtr sc = element->Compound();
+ if (!sc) {
+ DOMError("failed to read Geometry object (class: Line), no data scope found");
+ }
+ const ElementPtr Points = GetRequiredElement(sc, "Points", element);
+ const ElementPtr PointsIndex = GetRequiredElement(sc, "PointsIndex", element);
+ ParseVectorDataArray(m_vertices, Points);
+ ParseVectorDataArray(m_indices, PointsIndex);
+}
+
+// ------------------------------------------------------------------------------------------------
+LineGeometry::~LineGeometry() {
+ // empty
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<Vector3> &LineGeometry::GetVertices() const {
+ return m_vertices;
+}
+// ------------------------------------------------------------------------------------------------
+const std::vector<int> &LineGeometry::GetIndices() const {
+ return m_indices;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXMeshGeometry.h b/modules/fbx/fbx_parser/FBXMeshGeometry.h
new file mode 100644
index 0000000000..666da46edd
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXMeshGeometry.h
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* FBXMeshGeometry.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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.
+
+----------------------------------------------------------------------
+*/
+
+#ifndef FBX_MESH_GEOMETRY_H
+#define FBX_MESH_GEOMETRY_H
+
+#include "core/math/color.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+#include "core/templates/vector.h"
+
+#include "FBXDocument.h"
+#include "FBXParser.h"
+
+#include <iostream>
+
+#define AI_MAX_NUMBER_OF_TEXTURECOORDS 4
+#define AI_MAX_NUMBER_OF_COLOR_SETS 8
+
+namespace FBXDocParser {
+
+/*
+ * DOM base class for all kinds of FBX geometry
+ */
+class Geometry : public Object {
+public:
+ Geometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+ virtual ~Geometry();
+
+ /** Get the Skin attached to this geometry or NULL */
+ const Skin *DeformerSkin() const;
+
+ const std::vector<const BlendShape *> &get_blend_shapes() const;
+
+ size_t get_blend_shape_count() const {
+ return blendShapes.size();
+ }
+
+private:
+ const Skin *skin = nullptr;
+ std::vector<const BlendShape *> blendShapes;
+};
+
+typedef std::vector<int> MatIndexArray;
+
+/// Map Geometry stores the FBX file information.
+///
+/// # FBX doc.
+/// ## Reference type declared:
+/// - Direct (directly related to the mapping information type)
+/// - IndexToDirect (Map with key value, meaning depends on the MappingInformationType)
+///
+/// ## Map Type:
+/// * None The mapping is undetermined.
+/// * ByVertex There will be one mapping coordinate for each surface control point/vertex (ControlPoint is a vertex).
+/// * If you have direct reference type verticies[x]
+/// * If you have IndexToDirect reference type the UV
+/// * ByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part. (Sorted by polygon, referencing vertex)
+/// * ByPolygon There can be only one mapping coordinate for the whole polygon.
+/// * One mapping per polygon polygon x has this normal x
+/// * For each vertex of the polygon then set the normal to x
+/// * ByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements. (Mapping is referencing the edge id)
+/// * AllSame There can be only one mapping coordinate for the whole surface.
+class MeshGeometry : public Geometry {
+public:
+ enum class MapType {
+ none = 0, // No mapping type. Stored as "None".
+ vertex, // Maps per vertex. Stored as "ByVertice".
+ polygon_vertex, // Maps per polygon vertex. Stored as "ByPolygonVertex".
+ polygon, // Maps per polygon. Stored as "ByPolygon".
+ edge, // Maps per edge. Stored as "ByEdge".
+ all_the_same // Uaps to everything. Stored as "AllSame".
+ };
+
+ enum class ReferenceType {
+ direct = 0,
+ index = 1,
+ index_to_direct = 2
+ };
+
+ template <class T>
+ struct MappingData {
+ MapType map_type = MapType::none;
+ ReferenceType ref_type = ReferenceType::direct;
+ std::vector<T> data;
+ /// The meaning of the indices depends from the `MapType`.
+ /// If `ref_type` is `direct` this map is hollow.
+ std::vector<int> index;
+
+ String debug_info() const {
+ return "indexes: " + itos(index.size()) + " data: " + itos(data.size());
+ }
+ };
+
+ struct Edge {
+ int vertex_0 = 0, vertex_1 = 0;
+ Edge(int v0, int v1) :
+ vertex_0(v0), vertex_1(v1) {}
+ Edge() {}
+ };
+
+public:
+ MeshGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+ virtual ~MeshGeometry();
+
+ const std::vector<Vector3> &get_vertices() const;
+ const std::vector<Edge> &get_edge_map() const;
+ const std::vector<int> &get_polygon_indices() const;
+ const std::vector<int> &get_edges() const;
+ const MappingData<Vector3> &get_normals() const;
+ const MappingData<Vector2> &get_uv_0() const;
+ const MappingData<Vector2> &get_uv_1() const;
+ const MappingData<Color> &get_colors() const;
+ const MappingData<int> &get_material_allocation_id() const;
+
+ /// Returns -1 if the vertices doesn't form an edge. Vertex order, doesn't
+ // matter.
+ static int get_edge_id(const std::vector<Edge> &p_map, int p_vertex_a, int p_vertex_b);
+ // Retuns the edge point bu that ID, or the edge with -1 vertices if the
+ // id is not valid.
+ static Edge get_edge(const std::vector<Edge> &p_map, int p_id);
+
+private:
+ // Read directly from the FBX file.
+ std::vector<Vector3> m_vertices;
+ std::vector<Edge> edge_map;
+ std::vector<int> m_face_indices;
+ std::vector<int> m_edges;
+ MappingData<Vector3> m_normals;
+ MappingData<Vector2> m_uv_0; // first uv coordinates
+ MappingData<Vector2> m_uv_1; // second uv coordinates
+ MappingData<Color> m_colors; // colors for the mesh
+ MappingData<int> m_material_allocation_ids; // slot of material used
+
+ template <class T>
+ MappingData<T> resolve_vertex_data_array(
+ const ScopePtr source,
+ const std::string &MappingInformationType,
+ const std::string &ReferenceInformationType,
+ const std::string &dataElementName,
+ const std::string &indexOverride = "");
+};
+
+/*
+ * DOM class for FBX geometry of type "Shape"
+ */
+class ShapeGeometry : public Geometry {
+public:
+ /** The class constructor */
+ ShapeGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+ /** The class destructor */
+ virtual ~ShapeGeometry();
+
+ /** Get a list of all vertex points, non-unique*/
+ const std::vector<Vector3> &GetVertices() const;
+
+ /** Get a list of all vertex normals or an empty array if
+ * no normals are specified. */
+ const std::vector<Vector3> &GetNormals() const;
+
+ /** Return list of vertex indices. */
+ const std::vector<unsigned int> &GetIndices() const;
+
+private:
+ std::vector<Vector3> m_vertices;
+ std::vector<Vector3> m_normals;
+ std::vector<unsigned int> m_indices;
+};
+/**
+* DOM class for FBX geometry of type "Line"
+*/
+class LineGeometry : public Geometry {
+public:
+ /** The class constructor */
+ LineGeometry(uint64_t id, const ElementPtr element, const std::string &name, const Document &doc);
+
+ /** The class destructor */
+ virtual ~LineGeometry();
+
+ /** Get a list of all vertex points, non-unique*/
+ const std::vector<Vector3> &GetVertices() const;
+
+ /** Return list of vertex indices. */
+ const std::vector<int> &GetIndices() const;
+
+private:
+ std::vector<Vector3> m_vertices;
+ std::vector<int> m_indices;
+};
+} // namespace FBXDocParser
+
+#endif // FBX_MESH_GEOMETRY_H
diff --git a/modules/fbx/fbx_parser/FBXModel.cpp b/modules/fbx/fbx_parser/FBXModel.cpp
new file mode 100644
index 0000000000..5867f59d6a
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXModel.cpp
@@ -0,0 +1,176 @@
+/*************************************************************************/
+/* FBXModel.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXModel.cpp
+ * @brief Assimp::FBX::Model implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXMeshGeometry.h"
+#include "FBXParser.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Model::Model(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name), shading("Y") {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr Shading = sc->GetElement("Shading");
+ const ElementPtr Culling = sc->GetElement("Culling");
+
+ if (Shading) {
+ shading = GetRequiredToken(Shading, 0)->StringContents();
+ }
+
+ if (Culling) {
+ culling = ParseTokenAsString(GetRequiredToken(Culling, 0));
+ }
+
+ props = GetPropertyTable(doc, "Model.FbxNode", element, sc);
+ ResolveLinks(element, doc);
+}
+
+// ------------------------------------------------------------------------------------------------
+Model::~Model() {
+ if (props != nullptr) {
+ delete props;
+ props = nullptr;
+ }
+}
+
+ModelLimbNode::ModelLimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Model(id, element, doc, name){};
+
+ModelLimbNode::~ModelLimbNode() {
+}
+
+// ------------------------------------------------------------------------------------------------
+void Model::ResolveLinks(const ElementPtr element, const Document &doc) {
+ const char *const arr[] = { "Geometry", "Material", "NodeAttribute" };
+
+ // resolve material
+ const std::vector<const Connection *> &conns = doc.GetConnectionsByDestinationSequenced(ID(), arr, 3);
+
+ materials.reserve(conns.size());
+ geometry.reserve(conns.size());
+ attributes.reserve(conns.size());
+ for (const Connection *con : conns) {
+ // material and geometry links should be Object-Object connections
+ if (con->PropertyName().length()) {
+ continue;
+ }
+
+ const Object *const ob = con->SourceObject();
+ if (!ob) {
+ //DOMWarning("failed to read source object for incoming Model link, ignoring",&element);
+ continue;
+ }
+
+ const Material *const mat = dynamic_cast<const Material *>(ob);
+ if (mat) {
+ materials.push_back(mat);
+ continue;
+ }
+
+ const Geometry *const geo = dynamic_cast<const Geometry *>(ob);
+ if (geo) {
+ geometry.push_back(geo);
+ continue;
+ }
+
+ const NodeAttribute *const att = dynamic_cast<const NodeAttribute *>(ob);
+ if (att) {
+ attributes.push_back(att);
+ continue;
+ }
+
+ DOMWarning("source object for model link is neither Material, NodeAttribute nor Geometry, ignoring", element);
+ continue;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+bool Model::IsNull() const {
+ const std::vector<const NodeAttribute *> &attrs = GetAttributes();
+ for (const NodeAttribute *att : attrs) {
+ const Null *null_tag = dynamic_cast<const Null *>(att);
+ if (null_tag) {
+ return true;
+ }
+ }
+
+ return false;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXNodeAttribute.cpp b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
new file mode 100644
index 0000000000..c5051bd147
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXNodeAttribute.cpp
@@ -0,0 +1,183 @@
+/*************************************************************************/
+/* FBXNodeAttribute.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXNoteAttribute.cpp
+ * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+#include <iostream>
+
+namespace FBXDocParser {
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::NodeAttribute(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name), props() {
+ const ScopePtr sc = GetRequiredScope(element);
+
+ const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+
+ // hack on the deriving type but Null/LimbNode attributes are the only case in which
+ // the property table is by design absent and no warning should be generated
+ // for it.
+ const bool is_null_or_limb = !strcmp(classname.c_str(), "Null") || !strcmp(classname.c_str(), "LimbNode");
+ props = GetPropertyTable(doc, "NodeAttribute.Fbx" + classname, element, sc, is_null_or_limb);
+}
+
+// ------------------------------------------------------------------------------------------------
+NodeAttribute::~NodeAttribute() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+CameraSwitcher::CameraSwitcher(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ const ElementPtr CameraId = sc->GetElement("CameraId");
+ const ElementPtr CameraName = sc->GetElement("CameraName");
+ const ElementPtr CameraIndexName = sc->GetElement("CameraIndexName");
+
+ if (CameraId) {
+ cameraId = ParseTokenAsInt(GetRequiredToken(CameraId, 0));
+ }
+
+ if (CameraName) {
+ cameraName = GetRequiredToken(CameraName, 0)->StringContents();
+ }
+
+ if (CameraIndexName && CameraIndexName->Tokens().size()) {
+ cameraIndexName = GetRequiredToken(CameraIndexName, 0)->StringContents();
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+CameraSwitcher::~CameraSwitcher() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::Camera(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Camera::~Camera() {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::Light(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ // empty
+}
+
+// ------------------------------------------------------------------------------------------------
+Light::~Light() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Null::Null(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+}
+
+// ------------------------------------------------------------------------------------------------
+Null::~Null() {
+}
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::LimbNode(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ NodeAttribute(id, element, doc, name) {
+ //std::cout << "limb node: " << name << std::endl;
+ //const Scope &sc = GetRequiredScope(element);
+
+ //const ElementPtr const TypeFlag = sc["TypeFlags"];
+
+ // keep this it can dump new properties for you
+ // for( auto element : sc.Elements())
+ // {
+ // std::cout << "limbnode element: " << element.first << std::endl;
+ // }
+
+ // if(TypeFlag)
+ // {
+ // // std::cout << "type flag: " << GetRequiredToken(*TypeFlag, 0).StringContents() << std::endl;
+ // }
+}
+
+// ------------------------------------------------------------------------------------------------
+LimbNode::~LimbNode() {
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXParseTools.h b/modules/fbx/fbx_parser/FBXParseTools.h
new file mode 100644
index 0000000000..edc52009b3
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXParseTools.h
@@ -0,0 +1,111 @@
+/*************************************************************************/
+/* FBXParseTools.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef FBX_PARSE_TOOLS_H
+#define FBX_PARSE_TOOLS_H
+
+#include "core/error/error_macros.h"
+#include "core/string/ustring.h"
+
+#include <stdint.h>
+#include <algorithm>
+#include <locale>
+
+template <class char_t>
+inline bool IsNewLine(char_t c) {
+ return c == '\n' || c == '\r';
+}
+template <class char_t>
+inline bool IsSpace(char_t c) {
+ return (c == (char_t)' ' || c == (char_t)'\t');
+}
+
+template <class char_t>
+inline bool IsSpaceOrNewLine(char_t c) {
+ return IsNewLine(c) || IsSpace(c);
+}
+
+template <class char_t>
+inline bool IsLineEnd(char_t c) {
+ return (c == (char_t)'\r' || c == (char_t)'\n' || c == (char_t)'\0' || c == (char_t)'\f');
+}
+
+// ------------------------------------------------------------------------------------
+// Special version of the function, providing higher accuracy and safety
+// It is mainly used by fast_atof to prevent ugly and unwanted integer overflows.
+// ------------------------------------------------------------------------------------
+inline uint64_t strtoul10_64(const char *in, bool &errored, const char **out = 0, unsigned int *max_inout = 0) {
+ unsigned int cur = 0;
+ uint64_t value = 0;
+
+ errored = *in < '0' || *in > '9';
+ ERR_FAIL_COND_V_MSG(errored, 0, "The string cannot be converted parser error");
+
+ for (;;) {
+ if (*in < '0' || *in > '9') {
+ break;
+ }
+
+ const uint64_t new_value = (value * (uint64_t)10) + ((uint64_t)(*in - '0'));
+
+ // numeric overflow, we rely on you
+ if (new_value < value) {
+ //WARN_PRINT( "Converting the string \" " + in + " \" into a value resulted in overflow." );
+ return 0;
+ }
+
+ value = new_value;
+
+ ++in;
+ ++cur;
+
+ if (max_inout && *max_inout == cur) {
+ if (out) { /* skip to end */
+ while (*in >= '0' && *in <= '9') {
+ ++in;
+ }
+ *out = in;
+ }
+
+ return value;
+ }
+ }
+ if (out) {
+ *out = in;
+ }
+
+ if (max_inout) {
+ *max_inout = cur;
+ }
+
+ return value;
+}
+
+#endif // FBX_PARSE_TOOLS_H
diff --git a/modules/fbx/fbx_parser/FBXParser.cpp b/modules/fbx/fbx_parser/FBXParser.cpp
new file mode 100644
index 0000000000..208358f55e
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXParser.cpp
@@ -0,0 +1,1295 @@
+/*************************************************************************/
+/* FBXParser.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXParser.cpp
+ * @brief Implementation of the FBX parser and the rudimentary DOM that we use
+ */
+
+#include "thirdparty/zlib/zlib.h"
+#include <stdlib.h> /* strtol */
+
+#include "ByteSwapper.h"
+#include "FBXParseTools.h"
+#include "FBXParser.h"
+#include "FBXTokenizer.h"
+#include "core/math/math_defs.h"
+#include "core/math/transform.h"
+#include "core/math/vector3.h"
+#include "core/string/print_string.h"
+
+using namespace FBXDocParser;
+namespace {
+
+// Initially, we did reinterpret_cast, breaking strict aliasing rules.
+// This actually caused trouble on Android, so let's be safe this time.
+// https://github.com/assimp/assimp/issues/24
+template <typename T>
+T SafeParse(const char *data, const char *end) {
+ // Actual size validation happens during Tokenization so
+ // this is valid as an assertion.
+ (void)(end);
+ //ai_assert(static_cast<size_t>(end - data) >= sizeof(T));
+ T result = static_cast<T>(0);
+ ::memcpy(&result, data, sizeof(T));
+ return result;
+}
+} // namespace
+
+namespace FBXDocParser {
+
+// ------------------------------------------------------------------------------------------------
+Element::Element(const TokenPtr key_token, Parser &parser) :
+ key_token(key_token) {
+ TokenPtr n = nullptr;
+ do {
+ n = parser.AdvanceToNextToken();
+ if (n == nullptr) {
+ continue;
+ }
+
+ if (!n) {
+ print_error("unexpected end of file, expected closing bracket" + String(parser.LastToken()->StringContents().c_str()));
+ }
+
+ if (n && n->Type() == TokenType_DATA) {
+ tokens.push_back(n);
+ TokenPtr prev = n;
+ n = parser.AdvanceToNextToken();
+
+ if (n == nullptr) {
+ break;
+ }
+
+ if (!n) {
+ print_error("unexpected end of file, expected bracket, comma or key" + String(parser.LastToken()->StringContents().c_str()));
+ }
+
+ const TokenType ty = n->Type();
+
+ // some exporters are missing a comma on the next line
+ if (ty == TokenType_DATA && prev->Type() == TokenType_DATA && (n->Line() == prev->Line() + 1)) {
+ tokens.push_back(n);
+ continue;
+ }
+
+ if (ty != TokenType_OPEN_BRACKET && ty != TokenType_CLOSE_BRACKET && ty != TokenType_COMMA && ty != TokenType_KEY) {
+ print_error("unexpected token; expected bracket, comma or key" + String(n->StringContents().c_str()));
+ }
+ }
+
+ if (n && n->Type() == TokenType_OPEN_BRACKET) {
+ compound = new_Scope(parser);
+ parser.scopes.push_back(compound);
+
+ // current token should be a TOK_CLOSE_BRACKET
+ n = parser.CurrentToken();
+
+ if (n && n->Type() != TokenType_CLOSE_BRACKET) {
+ print_error("expected closing bracket" + String(n->StringContents().c_str()));
+ }
+
+ parser.AdvanceToNextToken();
+ return;
+ }
+ } while (n && n->Type() != TokenType_KEY && n->Type() != TokenType_CLOSE_BRACKET);
+}
+
+// ------------------------------------------------------------------------------------------------
+Element::~Element() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::Scope(Parser &parser, bool topLevel) {
+ if (!topLevel) {
+ TokenPtr t = parser.CurrentToken();
+ if (t->Type() != TokenType_OPEN_BRACKET) {
+ print_error("expected open bracket" + String(t->StringContents().c_str()));
+ }
+ }
+
+ TokenPtr n = parser.AdvanceToNextToken();
+ if (n == nullptr) {
+ print_error("unexpected end of file");
+ }
+
+ // note: empty scopes are allowed
+ while (n && n->Type() != TokenType_CLOSE_BRACKET) {
+ if (n->Type() != TokenType_KEY) {
+ print_error("unexpected token, expected TOK_KEY" + String(n->StringContents().c_str()));
+ }
+
+ const std::string str = n->StringContents();
+
+ // std::multimap<std::string, ElementPtr> (key and value)
+ 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();
+ if (n == nullptr) {
+ if (topLevel) {
+ return;
+ }
+
+ //print_error("unexpected end of file" + String(parser.LastToken()->StringContents().c_str()));
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Scope::~Scope() {
+ for (ElementMap::value_type &v : elements) {
+ delete v.second;
+ v.second = nullptr;
+ }
+
+ elements.clear();
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::Parser(const TokenList &tokens, bool is_binary) :
+ tokens(tokens), last(), current(), cursor(tokens.begin()), is_binary(is_binary) {
+ root = new_Scope(*this, true);
+ scopes.push_back(root);
+}
+
+// ------------------------------------------------------------------------------------------------
+Parser::~Parser() {
+ for (ScopePtr scope : scopes) {
+ delete scope;
+ scope = nullptr;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::AdvanceToNextToken() {
+ last = current;
+ if (cursor == tokens.end()) {
+ current = nullptr;
+ } else {
+ current = *cursor++;
+ }
+ return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::CurrentToken() const {
+ return current;
+}
+
+// ------------------------------------------------------------------------------------------------
+TokenPtr Parser::LastToken() const {
+ return last;
+}
+
+// ------------------------------------------------------------------------------------------------
+uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out) {
+ ERR_FAIL_COND_V_MSG(t == nullptr, 0L, "Invalid token passed to ParseTokenAsID");
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0L;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'L') {
+ err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
+ return 0L;
+ }
+
+ uint64_t id = SafeParse<uint64_t>(data + 1, t->end());
+ return id;
+ }
+
+ // XXX: should use size_t here
+ unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+ //ai_assert(length > 0);
+
+ const char *out = nullptr;
+ bool errored = false;
+
+ const uint64_t id = strtoul10_64(t->begin(), errored, &out, &length);
+ if (errored || out > t->end()) {
+ err_out = "failed to parse ID (text)";
+ return 0L;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsID() with print_error handling
+uint64_t ParseTokenAsID(const TokenPtr t) {
+ const char *err = nullptr;
+ const uint64_t i = ParseTokenAsID(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out) {
+ // same as ID parsing, except there is a trailing asterisk
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'L') {
+ err_out = "failed to parse ID, unexpected data type, expected L(ong) (binary)";
+ return 0;
+ }
+
+ uint64_t id = SafeParse<uint64_t>(data + 1, t->end());
+ AI_SWAP8(id);
+ return static_cast<size_t>(id);
+ }
+
+ if (*t->begin() != '*') {
+ err_out = "expected asterisk before array dimension";
+ return 0;
+ }
+
+ // 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 0;
+ }
+
+ const char *out = nullptr;
+ bool errored = false;
+ const size_t id = static_cast<size_t>(strtoul10_64(t->begin() + 1, errored, &out, &length));
+ if (errored || out > t->end()) {
+ print_error("failed to parse id");
+ err_out = "failed to parse ID";
+ return 0;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+float ParseTokenAsFloat(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0.0f;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'F' && data[0] != 'D') {
+ err_out = "failed to parse F(loat) or D(ouble), unexpected data type (binary)";
+ return 0.0f;
+ }
+
+ if (data[0] == 'F') {
+ return SafeParse<float>(data + 1, t->end());
+ } else {
+ return static_cast<float>(SafeParse<double>(data + 1, t->end()));
+ }
+ }
+
+// need to copy the input string to a temporary buffer
+// first - next in the fbx token stream comes ',',
+// which fast_atof could interpret as decimal point.
+#define MAX_FLOAT_LENGTH 31
+ char temp[MAX_FLOAT_LENGTH + 1];
+ const size_t length = static_cast<size_t>(t->end() - t->begin());
+ std::copy(t->begin(), t->end(), temp);
+ temp[std::min(static_cast<size_t>(MAX_FLOAT_LENGTH), length)] = '\0';
+
+ return atof(temp);
+}
+
+// ------------------------------------------------------------------------------------------------
+int ParseTokenAsInt(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0;
+ }
+
+ // binary files are simple to parse
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'I') {
+ err_out = "failed to parse I(nt), unexpected data type (binary)";
+ return 0;
+ }
+
+ int32_t ival = SafeParse<int32_t>(data + 1, t->end());
+ AI_SWAP4(ival);
+ return static_cast<int>(ival);
+ }
+
+ // ASCII files are unsafe.
+ const size_t length = static_cast<size_t>(t->end() - t->begin());
+ if (length == 0) {
+ err_out = "expected valid integer number after asterisk";
+ ERR_FAIL_V_MSG(0, "expected valid integer number after asterisk");
+ }
+
+ // must not be null for strtol to work
+ char *out = (char *)t->end();
+ // string begin, end ptr ref, base 10
+ const int value = strtol(t->begin(), &out, 10);
+ if (out == nullptr || out != t->end()) {
+ err_out = "failed to parse ID";
+ ERR_FAIL_V_MSG(0, "failed to parse ID");
+ }
+
+ return value;
+}
+
+// ------------------------------------------------------------------------------------------------
+int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return 0L;
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'L') {
+ err_out = "failed to parse Int64, unexpected data type";
+ return 0L;
+ }
+
+ int64_t id = SafeParse<int64_t>(data + 1, t->end());
+ AI_SWAP8(id);
+ return id;
+ }
+
+ // XXX: should use size_t here
+ unsigned int length = static_cast<unsigned int>(t->end() - t->begin());
+ //ai_assert(length > 0);
+
+ char *out = nullptr;
+ const int64_t id = strtol(t->begin(), &out, length);
+ if (out > t->end()) {
+ err_out = "failed to parse Int64 (text)";
+ return 0L;
+ }
+
+ return id;
+}
+
+// ------------------------------------------------------------------------------------------------
+std::string ParseTokenAsString(const TokenPtr t, const char *&err_out) {
+ err_out = nullptr;
+
+ if (t->Type() != TokenType_DATA) {
+ err_out = "expected TOK_DATA token";
+ return "";
+ }
+
+ if (t->IsBinary()) {
+ const char *data = t->begin();
+ if (data[0] != 'S') {
+ err_out = "failed to parse String, unexpected data type (binary)";
+ return "";
+ }
+
+ // read string length
+ int32_t len = SafeParse<int32_t>(data + 1, t->end());
+ AI_SWAP4(len);
+
+ //ai_assert(t.end() - data == 5 + len);
+ return std::string(data + 5, len);
+ }
+
+ 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);
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read the type code and element count of a binary data array and stop there
+void ReadBinaryDataArrayHead(const char *&data, const char *end, char &type, uint32_t &count,
+ const ElementPtr el) {
+ TokenPtr token = el->KeyToken();
+ if (static_cast<size_t>(end - data) < 5) {
+ print_error("binary data array is too short, need five (5) bytes for type signature and element count: " + String(token->StringContents().c_str()));
+ }
+
+ // data type
+ type = *data;
+
+ // read number of elements
+ uint32_t len = SafeParse<uint32_t>(data + 1, end);
+ AI_SWAP4(len);
+
+ count = len;
+ data += 5;
+}
+
+// ------------------------------------------------------------------------------------------------
+// read binary data array, assume cursor points to the 'compression mode' field (i.e. behind the header)
+void ReadBinaryDataArray(char type, uint32_t count, const char *&data, const char *end,
+ std::vector<char> &buff,
+ const ElementPtr /*el*/) {
+ uint32_t encmode = SafeParse<uint32_t>(data, end);
+ AI_SWAP4(encmode);
+ data += 4;
+
+ // next comes the compressed length
+ uint32_t comp_len = SafeParse<uint32_t>(data, end);
+ AI_SWAP4(comp_len);
+ data += 4;
+
+ //ai_assert(data + comp_len == end);
+
+ // determine the length of the uncompressed data by looking at the type signature
+ uint32_t stride = 0;
+ switch (type) {
+ case 'f':
+ case 'i':
+ stride = 4;
+ break;
+
+ case 'd':
+ case 'l':
+ stride = 8;
+ break;
+ }
+
+ const uint32_t full_length = stride * count;
+ buff.resize(full_length);
+
+ if (encmode == 0) {
+ //ai_assert(full_length == comp_len);
+
+ // plain data, no compression
+ std::copy(data, end, buff.begin());
+ } else if (encmode == 1) {
+ // zlib/deflate, next comes ZIP head (0x78 0x01)
+ // see http://www.ietf.org/rfc/rfc1950.txt
+
+ z_stream zstream;
+ zstream.opaque = Z_NULL;
+ zstream.zalloc = Z_NULL;
+ zstream.zfree = Z_NULL;
+ zstream.data_type = Z_BINARY;
+
+ // http://hewgill.com/journal/entries/349-how-to-decompress-gzip-stream-with-zlib
+ if (Z_OK != inflateInit(&zstream)) {
+ print_error("failure initializing zlib");
+ }
+
+ zstream.next_in = reinterpret_cast<Bytef *>(const_cast<char *>(data));
+ zstream.avail_in = comp_len;
+
+ zstream.avail_out = static_cast<uInt>(buff.size());
+ zstream.next_out = reinterpret_cast<Bytef *>(&*buff.begin());
+ const int ret = inflate(&zstream, Z_FINISH);
+
+ if (ret != Z_STREAM_END && ret != Z_OK) {
+ print_error("failure decompressing compressed data section");
+ }
+
+ // terminate zlib
+ inflateEnd(&zstream);
+ }
+#ifdef ASSIMP_BUILD_DEBUG
+ else {
+ // runtime check for this happens at tokenization stage
+ //ai_assert(false);
+ }
+#endif
+
+ data += comp_len;
+ //ai_assert(data == end);
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+// read an array of float3 tuples
+void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el) {
+ out.resize(0);
+
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (count % 3 != 0) {
+ print_error("number of floats is not a multiple of three (3) (binary)" + String(token->StringContents().c_str()));
+ }
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ const uint32_t count3 = count / 3;
+ out.reserve(count3);
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count3; ++i, d += 3) {
+ out.push_back(Vector3(static_cast<real_t>(d[0]),
+ static_cast<real_t>(d[1]),
+ static_cast<real_t>(d[2])));
+ }
+ // for debugging
+ /*for ( size_t i = 0; i < out.size(); i++ ) {
+ aiVector3D vec3( out[ i ] );
+ std::stringstream stream;
+ stream << " vec3.x = " << vec3.x << " vec3.y = " << vec3.y << " vec3.z = " << vec3.z << std::endl;
+ DefaultLogger::get()->info( stream.str() );
+ }*/
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count3; ++i, f += 3) {
+ out.push_back(Vector3(f[0], f[1], f[2]));
+ }
+ }
+
+ return;
+ }
+
+ 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 ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ if (a->Tokens().size() % 3 != 0) {
+ print_error("number of floats is not a multiple of three (3)" + String(token->StringContents().c_str()));
+ } else {
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ Vector3 v;
+ v.x = ParseTokenAsFloat(*it++);
+ v.y = ParseTokenAsFloat(*it++);
+ v.z = ParseTokenAsFloat(*it++);
+
+ out.push_back(v);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of color4 tuples
+void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+
+ TokenPtr token = el->KeyToken();
+
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (count % 4 != 0) {
+ print_error("number of floats is not a multiple of four (4) (binary)" + String(token->StringContents().c_str()));
+ }
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ const uint32_t count4 = count / 4;
+ out.reserve(count4);
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count4; ++i, d += 4) {
+ out.push_back(Color(static_cast<float>(d[0]),
+ static_cast<float>(d[1]),
+ static_cast<float>(d[2]),
+ static_cast<float>(d[3])));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count4; ++i, f += 4) {
+ out.push_back(Color(f[0], f[1], f[2], f[3]));
+ }
+ }
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray() above
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ if (a->Tokens().size() % 4 != 0) {
+ print_error("number of floats is not a multiple of four (4)" + String(token->StringContents().c_str()));
+ }
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ Color 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 ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (count % 2 != 0) {
+ print_error("number of floats is not a multiple of two (2) (binary)" + String(token->StringContents().c_str()));
+ }
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ const uint32_t count2 = count / 2;
+ out.reserve(count2);
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count2; ++i, d += 2) {
+ out.push_back(Vector2(static_cast<float>(d[0]),
+ static_cast<float>(d[1])));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count2; ++i, f += 2) {
+ out.push_back(Vector2(f[0], f[1]));
+ }
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray() above
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ if (a->Tokens().size() % 2 != 0) {
+ print_error("number of floats is not a multiple of two (2)" + String(token->StringContents().c_str()));
+ } else {
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ Vector2 v;
+ v.x = ParseTokenAsFloat(*it++);
+ v.y = ParseTokenAsFloat(*it++);
+ out.push_back(v);
+ }
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of ints
+void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element" + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'i') {
+ print_error("expected int array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 4);
+
+ out.reserve(count);
+
+ const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ int32_t val = *ip;
+ AI_SWAP4(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr 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 ParseVectorDataArray(std::vector<float> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ if (tok.empty()) {
+ print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'd' && type != 'f') {
+ print_error("expected float or double array (binary) " + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * (type == 'd' ? 8 : 4));
+
+ if (type == 'd') {
+ const double *d = reinterpret_cast<const double *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++d) {
+ out.push_back(static_cast<float>(*d));
+ }
+ } else if (type == 'f') {
+ const float *f = reinterpret_cast<const float *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++f) {
+ out.push_back(*f);
+ }
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr 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 ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ const TokenPtr token = el->KeyToken();
+
+ ERR_FAIL_COND_MSG(!token, "invalid ParseVectorDataArrat token invalid");
+
+ if (tok.empty()) {
+ print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'i') {
+ print_error("expected (u)int array (binary)" + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 4);
+
+ out.reserve(count);
+
+ const int32_t *ip = reinterpret_cast<const int32_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ int32_t val = *ip;
+ if (val < 0) {
+ print_error("encountered negative integer index (binary)");
+ }
+
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr 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) {
+ print_error("encountered negative integer index");
+ }
+ out.push_back(static_cast<unsigned int>(ival));
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of uint64_ts
+void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr el) {
+ out.resize(0);
+
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND(!token);
+
+ if (tok.empty()) {
+ print_error("unexpected empty element " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'l') {
+ print_error("expected long array (binary): " + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 8);
+
+ out.reserve(count);
+
+ const uint64_t *ip = reinterpret_cast<const uint64_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ uint64_t val = *ip;
+ AI_SWAP8(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr 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);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+// read an array of int64_ts
+void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el) {
+ out.resize(0);
+ const TokenList &tok = el->Tokens();
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND(!token);
+ if (tok.empty()) {
+ print_error("unexpected empty element: " + String(token->StringContents().c_str()));
+ }
+
+ if (tok[0]->IsBinary()) {
+ const char *data = tok[0]->begin(), *end = tok[0]->end();
+
+ char type;
+ uint32_t count;
+ ReadBinaryDataArrayHead(data, end, type, count, el);
+
+ if (!count) {
+ return;
+ }
+
+ if (type != 'l') {
+ print_error("expected long array (binary) " + String(token->StringContents().c_str()));
+ }
+
+ std::vector<char> buff;
+ ReadBinaryDataArray(type, count, data, end, buff, el);
+
+ //ai_assert(data == end);
+ //ai_assert(buff.size() == count * 8);
+
+ out.reserve(count);
+
+ const int64_t *ip = reinterpret_cast<const int64_t *>(&buff[0]);
+ for (unsigned int i = 0; i < count; ++i, ++ip) {
+ int64_t val = *ip;
+ AI_SWAP8(val);
+ out.push_back(val);
+ }
+
+ return;
+ }
+
+ const size_t dim = ParseTokenAsDim(tok[0]);
+
+ // see notes in ParseVectorDataArray()
+ out.reserve(dim);
+
+ const ScopePtr scope = GetRequiredScope(el);
+ const ElementPtr a = GetRequiredElement(scope, "a", el);
+
+ for (TokenList::const_iterator it = a->Tokens().begin(), end = a->Tokens().end(); it != end;) {
+ const int64_t val = ParseTokenAsInt64(*it++);
+ out.push_back(val);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+Transform ReadMatrix(const ElementPtr element) {
+ std::vector<float> values;
+ ParseVectorDataArray(values, element);
+
+ if (values.size() != 16) {
+ print_error("expected 16 matrix elements");
+ }
+
+ // clean values to prevent any IBM damage on inverse() / affine_inverse()
+ for (float &value : values) {
+ if (::Math::is_equal_approx(0, value)) {
+ value = 0;
+ }
+ }
+
+ Transform xform;
+ Basis basis;
+
+ basis.set(
+ Vector3(values[0], values[1], values[2]),
+ Vector3(values[4], values[5], values[6]),
+ Vector3(values[8], values[9], values[10]));
+
+ xform.basis = basis;
+ xform.origin = Vector3(values[12], values[13], values[14]);
+ // determine if we need to think about this with dynamic rotation order?
+ // for example:
+ // xform.basis = z_axis * y_axis * x_axis;
+ //xform.basis.transpose();
+
+ print_verbose("xform verbose basis: " + (xform.basis.get_euler() * (180 / Math_PI)) + " xform origin:" + xform.origin);
+
+ return xform;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsString() with print_error handling
+std::string ParseTokenAsString(const TokenPtr t) {
+ ERR_FAIL_COND_V(!t, "");
+ const char *err;
+ const std::string &i = ParseTokenAsString(t, err);
+ if (err) {
+ print_error(String(err) + ", " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= NULL*/) {
+ const ElementPtr el = sc->GetElement(index);
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND_V(!token, nullptr);
+ if (!el) {
+ print_error("did not find required element \"" + String(index.c_str()) + "\" " + String(token->StringContents().c_str()));
+ }
+ return el;
+}
+
+bool HasElement(const ScopePtr sc, const std::string &index) {
+ const ElementPtr el = sc->GetElement(index);
+ if (nullptr == el) {
+ return false;
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract a required element from a scope, abort if the element cannot be found
+ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element /*= NULL*/) {
+ const ElementPtr el = sc->GetElement(index);
+ return el;
+}
+
+// ------------------------------------------------------------------------------------------------
+// extract required compound scope
+ScopePtr GetRequiredScope(const ElementPtr el) {
+ if (el) {
+ ScopePtr s = el->Compound();
+ TokenPtr token = el->KeyToken();
+ ERR_FAIL_COND_V(!token, nullptr);
+ if (s) {
+ return s;
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "expected compound scope " + String(token->StringContents().c_str()));
+ }
+
+ ERR_FAIL_V_MSG(nullptr, "Invalid element supplied to parser");
+}
+
+// ------------------------------------------------------------------------------------------------
+// get token at a particular index
+TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index) {
+ if (el) {
+ const TokenList &x = el->Tokens();
+ TokenPtr token = el->KeyToken();
+
+ ERR_FAIL_COND_V(!token, nullptr);
+
+ if (index >= x.size()) {
+ ERR_FAIL_V_MSG(nullptr, "missing token at index: " + itos(index) + " " + String(token->StringContents().c_str()));
+ }
+
+ return x[index];
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsDim() with print_error handling
+size_t ParseTokenAsDim(const TokenPtr t) {
+ const char *err;
+ const size_t i = ParseTokenAsDim(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsFloat() with print_error handling
+float ParseTokenAsFloat(const TokenPtr t) {
+ const char *err;
+ const float i = ParseTokenAsFloat(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt() with print_error handling
+int ParseTokenAsInt(const TokenPtr t) {
+ const char *err;
+ const int i = ParseTokenAsInt(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+
+// ------------------------------------------------------------------------------------------------
+// wrapper around ParseTokenAsInt64() with print_error handling
+int64_t ParseTokenAsInt64(const TokenPtr t) {
+ const char *err;
+ const int64_t i = ParseTokenAsInt64(t, err);
+ if (err) {
+ print_error(String(err) + " " + String(t->StringContents().c_str()));
+ }
+ return i;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXParser.h b/modules/fbx/fbx_parser/FBXParser.h
new file mode 100644
index 0000000000..3cba81ff83
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXParser.h
@@ -0,0 +1,263 @@
+/*************************************************************************/
+/* FBXParser.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXParser.h
+ * @brief FBX parsing code
+ */
+#ifndef FBX_PARSER_H
+#define FBX_PARSER_H
+
+#include <stdint.h>
+#include <map>
+#include <memory>
+
+#include "core/math/color.h"
+#include "core/math/transform.h"
+#include "core/math/vector2.h"
+#include "core/math/vector3.h"
+
+#include "FBXTokenizer.h"
+
+namespace FBXDocParser {
+
+class Scope;
+class Parser;
+class Element;
+
+typedef Element *ElementPtr;
+typedef Scope *ScopePtr;
+
+typedef std::vector<ScopePtr> ScopeList;
+typedef std::multimap<std::string, ElementPtr> ElementMap;
+typedef std::pair<ElementMap::const_iterator, ElementMap::const_iterator> ElementCollection;
+
+#define new_Scope new Scope
+#define new_Element new Element
+
+/** FBX data entity that consists of a key:value tuple.
+ *
+ * Example:
+ * @verbatim
+ * AnimationCurve: 23, "AnimCurve::", "" {
+ * [..]
+ * }
+ * @endverbatim
+ *
+ * As can be seen in this sample, elements can contain nested #Scope
+ * as their trailing member. **/
+class Element {
+public:
+ Element(TokenPtr key_token, Parser &parser);
+ ~Element();
+
+ ScopePtr Compound() const {
+ return compound;
+ }
+
+ TokenPtr KeyToken() const {
+ return key_token;
+ }
+
+ const TokenList &Tokens() const {
+ return tokens;
+ }
+
+private:
+ TokenList tokens;
+ ScopePtr compound = nullptr;
+ std::vector<ScopePtr> compound_scope;
+ TokenPtr key_token = nullptr;
+};
+
+/** FBX data entity that consists of a 'scope', a collection
+ * of not necessarily unique #Element instances.
+ *
+ * Example:
+ * @verbatim
+ * GlobalSettings: {
+ * Version: 1000
+ * Properties70:
+ * [...]
+ * }
+ * @endverbatim */
+class Scope {
+public:
+ Scope(Parser &parser, bool topLevel = false);
+ ~Scope();
+
+ ElementPtr GetElement(const std::string &index) const {
+ ElementMap::const_iterator it = elements.find(index);
+ return it == elements.end() ? nullptr : (*it).second;
+ }
+
+ ElementPtr FindElementCaseInsensitive(const std::string &elementName) const {
+ for (auto element = elements.begin(); element != elements.end(); ++element) {
+ if (element->first.compare(elementName)) {
+ return element->second;
+ }
+ }
+
+ // nothing to reference / expired.
+ return nullptr;
+ }
+
+ ElementCollection GetCollection(const std::string &index) const {
+ return elements.equal_range(index);
+ }
+
+ const ElementMap &Elements() const {
+ return elements;
+ }
+
+private:
+ ElementMap elements;
+};
+
+/** FBX parsing class, takes a list of input tokens and generates a hierarchy
+ * of nested #Scope instances, representing the fbx DOM.*/
+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, bool is_binary);
+ ~Parser();
+
+ ScopePtr GetRootScope() const {
+ return root;
+ }
+
+ bool IsBinary() const {
+ return is_binary;
+ }
+
+private:
+ friend class Scope;
+ friend class Element;
+
+ TokenPtr AdvanceToNextToken();
+ TokenPtr LastToken() const;
+ TokenPtr CurrentToken() const;
+
+private:
+ ScopeList scopes;
+ const TokenList &tokens;
+
+ TokenPtr last = nullptr, current = nullptr;
+ TokenList::const_iterator cursor;
+ ScopePtr root = nullptr;
+
+ const bool is_binary;
+};
+
+/* token parsing - this happens when building the DOM out of the parse-tree*/
+uint64_t ParseTokenAsID(const TokenPtr t, const char *&err_out);
+size_t ParseTokenAsDim(const TokenPtr t, const char *&err_out);
+float ParseTokenAsFloat(const TokenPtr t, const char *&err_out);
+int ParseTokenAsInt(const TokenPtr t, const char *&err_out);
+int64_t ParseTokenAsInt64(const TokenPtr t, const char *&err_out);
+std::string ParseTokenAsString(const TokenPtr t, const char *&err_out);
+
+/* wrapper around ParseTokenAsXXX() with DOMError handling */
+uint64_t ParseTokenAsID(const TokenPtr t);
+size_t ParseTokenAsDim(const TokenPtr t);
+float ParseTokenAsFloat(const TokenPtr t);
+int ParseTokenAsInt(const TokenPtr t);
+int64_t ParseTokenAsInt64(const TokenPtr t);
+std::string ParseTokenAsString(const TokenPtr t);
+
+/* read data arrays */
+void ParseVectorDataArray(std::vector<Vector3> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<Color> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<Vector2> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<int> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<float> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<unsigned int> &out, const ElementPtr el);
+void ParseVectorDataArray(std::vector<uint64_t> &out, const ElementPtr ep);
+void ParseVectorDataArray(std::vector<int64_t> &out, const ElementPtr el);
+bool HasElement(const ScopePtr sc, const std::string &index);
+
+// extract a required element from a scope, abort if the element cannot be found
+ElementPtr GetRequiredElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
+ScopePtr GetRequiredScope(const ElementPtr el); // New in 2020. (less likely to destroy application)
+ElementPtr GetOptionalElement(const ScopePtr sc, const std::string &index, const ElementPtr element = nullptr);
+// extract required compound scope
+ScopePtr GetRequiredScope(const ElementPtr el);
+// get token at a particular index
+TokenPtr GetRequiredToken(const ElementPtr el, unsigned int index);
+
+// ------------------------------------------------------------------------------------------------
+// read a 4x4 matrix from an array of 16 floats
+Transform ReadMatrix(const ElementPtr element);
+} // namespace FBXDocParser
+
+#endif // FBX_PARSER_H
diff --git a/modules/fbx/fbx_parser/FBXPose.cpp b/modules/fbx/fbx_parser/FBXPose.cpp
new file mode 100644
index 0000000000..1bee2fa2f0
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXPose.cpp
@@ -0,0 +1,104 @@
+/*************************************************************************/
+/* FBXPose.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXNoteAttribute.cpp
+ * @brief Assimp::FBX::NodeAttribute (and subclasses) implementation
+ */
+
+#include "FBXDocument.h"
+#include "FBXParser.h"
+#include <iostream>
+
+namespace FBXDocParser {
+
+class FbxPoseNode;
+// ------------------------------------------------------------------------------------------------
+FbxPose::FbxPose(uint64_t id, const ElementPtr element, const Document &doc, const std::string &name) :
+ Object(id, element, name) {
+ const ScopePtr sc = GetRequiredScope(element);
+ //const std::string &classname = ParseTokenAsString(GetRequiredToken(element, 2));
+
+ const ElementCollection &PoseNodes = sc->GetCollection("PoseNode");
+ for (ElementMap::const_iterator it = PoseNodes.first; it != PoseNodes.second; ++it) {
+ std::string entry_name = (*it).first;
+ ElementPtr some_element = (*it).second;
+ FbxPoseNode *pose_node = new FbxPoseNode(some_element, doc, entry_name);
+ pose_nodes.push_back(pose_node);
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+FbxPose::~FbxPose() {
+ pose_nodes.clear();
+ // empty
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXProperties.cpp b/modules/fbx/fbx_parser/FBXProperties.cpp
new file mode 100644
index 0000000000..2994a84dbd
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXProperties.cpp
@@ -0,0 +1,237 @@
+/*************************************************************************/
+/* FBXProperties.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXProperties.cpp
+ * @brief Implementation of the FBX dynamic properties system
+ */
+
+#include "FBXProperties.h"
+#include "FBXDocumentUtil.h"
+#include "FBXParser.h"
+#include "FBXTokenizer.h"
+
+namespace FBXDocParser {
+
+using namespace Util;
+
+// ------------------------------------------------------------------------------------------------
+Property::Property() {
+}
+
+// ------------------------------------------------------------------------------------------------
+Property::~Property() {
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+// read a typed property out of a FBX element. The return value is NULL if the property cannot be read.
+PropertyPtr ReadTypedProperty(const ElementPtr element) {
+ //ai_assert(element.KeyToken().StringContents() == "P");
+
+ const TokenList &tok = element->Tokens();
+ //ai_assert(tok.size() >= 5);
+
+ const std::string &s = ParseTokenAsString(tok[1]);
+ const char *const cs = s.c_str();
+ if (!strcmp(cs, "KString")) {
+ return new TypedProperty<std::string>(ParseTokenAsString(tok[4]));
+ } else if (!strcmp(cs, "bool") || !strcmp(cs, "Bool")) {
+ return new TypedProperty<bool>(ParseTokenAsInt(tok[4]) != 0);
+ } else if (!strcmp(cs, "int") || !strcmp(cs, "Int") || !strcmp(cs, "enum") || !strcmp(cs, "Enum")) {
+ return new TypedProperty<int>(ParseTokenAsInt(tok[4]));
+ } else if (!strcmp(cs, "ULongLong")) {
+ return new TypedProperty<uint64_t>(ParseTokenAsID(tok[4]));
+ } else if (!strcmp(cs, "KTime")) {
+ return new TypedProperty<int64_t>(ParseTokenAsInt64(tok[4]));
+ } else if (!strcmp(cs, "Vector3D") ||
+ !strcmp(cs, "ColorRGB") ||
+ !strcmp(cs, "Vector") ||
+ !strcmp(cs, "Color") ||
+ !strcmp(cs, "Lcl Translation") ||
+ !strcmp(cs, "Lcl Rotation") ||
+ !strcmp(cs, "Lcl Scaling")) {
+ return new TypedProperty<Vector3>(Vector3(
+ ParseTokenAsFloat(tok[4]),
+ ParseTokenAsFloat(tok[5]),
+ ParseTokenAsFloat(tok[6])));
+ } else if (!strcmp(cs, "double") || !strcmp(cs, "Number") || !strcmp(cs, "Float") || !strcmp(cs, "float") || !strcmp(cs, "FieldOfView") || !strcmp(cs, "UnitScaleFactor")) {
+ return new TypedProperty<float>(ParseTokenAsFloat(tok[4]));
+ }
+
+ return nullptr;
+}
+
+// ------------------------------------------------------------------------------------------------
+// peek into an element and check if it contains a FBX property, if so return its name.
+std::string PeekPropertyName(const Element &element) {
+ //ai_assert(element.KeyToken().StringContents() == "P");
+ const TokenList &tok = element.Tokens();
+ if (tok.size() < 4) {
+ return "";
+ }
+
+ return ParseTokenAsString(tok[0]);
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable() :
+ templateProps(), element() {
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::PropertyTable(const ElementPtr element, const PropertyTable *templateProps) :
+ templateProps(templateProps), element(element) {
+ const ScopePtr scope = GetRequiredScope(element);
+ ERR_FAIL_COND(!scope);
+ for (const ElementMap::value_type &v : scope->Elements()) {
+ if (v.first != "P") {
+ DOMWarning("expected only P elements in property table", v.second);
+ continue;
+ }
+
+ const std::string &name = PeekPropertyName(*v.second);
+ if (!name.length()) {
+ DOMWarning("could not read property name", v.second);
+ continue;
+ }
+
+ LazyPropertyMap::const_iterator it = lazyProps.find(name);
+ if (it != lazyProps.end()) {
+ DOMWarning("duplicate property name, will hide previous value: " + name, v.second);
+ continue;
+ }
+
+ // since the above checks for duplicates we can be sure to insert the only match here.
+ lazyProps[name] = v.second;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyTable::~PropertyTable() {
+ for (PropertyMap::value_type &v : props) {
+ delete v.second;
+ }
+}
+
+// ------------------------------------------------------------------------------------------------
+PropertyPtr PropertyTable::Get(const std::string &name) const {
+ PropertyMap::const_iterator it = props.find(name);
+ if (it == props.end()) {
+ // hasn't been parsed yet?
+ LazyPropertyMap::const_iterator lit = lazyProps.find(name);
+ if (lit != lazyProps.end()) {
+ props[name] = ReadTypedProperty(lit->second);
+ it = props.find(name);
+
+ //ai_assert(it != props.end());
+ }
+
+ if (it == props.end()) {
+ // check property template
+ if (templateProps) {
+ return templateProps->Get(name);
+ }
+
+ return nullptr;
+ }
+ }
+
+ return (*it).second;
+}
+
+DirectPropertyMap PropertyTable::GetUnparsedProperties() const {
+ DirectPropertyMap result;
+
+ // Loop through all the lazy properties (which is all the properties)
+ for (const LazyPropertyMap::value_type &element : lazyProps) {
+ // Skip parsed properties
+ if (props.end() != props.find(element.first))
+ continue;
+
+ // Read the element's value.
+ // Wrap the naked pointer (since the call site is required to acquire ownership)
+ // std::unique_ptr from C++11 would be preferred both as a wrapper and a return value.
+ Property *prop = ReadTypedProperty(element.second);
+
+ // Element could not be read. Skip it.
+ if (!prop)
+ continue;
+
+ // Add to result
+ result[element.first] = prop;
+ }
+
+ return result;
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXProperties.h b/modules/fbx/fbx_parser/FBXProperties.h
new file mode 100644
index 0000000000..a007660c45
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXProperties.h
@@ -0,0 +1,221 @@
+/*************************************************************************/
+/* FBXProperties.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXProperties.h
+ * @brief FBX dynamic properties
+ */
+#ifndef FBX_PROPERTIES_H
+#define FBX_PROPERTIES_H
+
+#include "FBXParser.h"
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FBXDocParser {
+
+// Forward declarations
+class Element;
+
+/** Represents a dynamic property. Type info added by deriving classes,
+ * see #TypedProperty.
+ Example:
+ @verbatim
+ P: "ShininessExponent", "double", "Number", "",0.5
+ @endvebatim
+*/
+class Property {
+protected:
+ Property();
+
+public:
+ virtual ~Property();
+
+public:
+ template <typename T>
+ const T *As() const {
+ return dynamic_cast<const T *>(this);
+ }
+};
+
+template <typename T>
+class TypedProperty : public Property {
+public:
+ explicit TypedProperty(const T &value) :
+ value(value) {
+ // empty
+ }
+
+ const T &Value() const {
+ return value;
+ }
+
+private:
+ T value;
+};
+
+#define new_Property new Property
+typedef Property *PropertyPtr;
+typedef std::map<std::string, PropertyPtr> DirectPropertyMap;
+typedef std::map<std::string, PropertyPtr> PropertyMap;
+typedef std::map<std::string, ElementPtr> LazyPropertyMap;
+
+/**
+ * Represents a property table as can be found in the newer FBX files (Properties60, Properties70)
+ */
+class PropertyTable {
+public:
+ // in-memory property table with no source element
+ PropertyTable();
+ PropertyTable(const ElementPtr element, const PropertyTable *templateProps);
+ ~PropertyTable();
+
+ PropertyPtr Get(const std::string &name) const;
+
+ // PropertyTable's need not be coupled with FBX elements so this can be NULL
+ ElementPtr GetElement() const {
+ return element;
+ }
+
+ PropertyMap &GetProperties() const {
+ return props;
+ }
+
+ const LazyPropertyMap &GetLazyProperties() const {
+ return lazyProps;
+ }
+
+ const PropertyTable *TemplateProps() const {
+ return templateProps;
+ }
+
+ DirectPropertyMap GetUnparsedProperties() const;
+
+private:
+ LazyPropertyMap lazyProps;
+ mutable PropertyMap props;
+ const PropertyTable *templateProps = nullptr;
+ const ElementPtr element = nullptr;
+};
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline T PropertyGet(const PropertyTable *in, const std::string &name, const T &defaultValue) {
+ PropertyPtr prop = in->Get(name);
+ if (nullptr == prop) {
+ return defaultValue;
+ }
+
+ // strong typing, no need to be lenient
+ const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>();
+ if (nullptr == tprop) {
+ return defaultValue;
+ }
+
+ return tprop->Value();
+}
+
+// ------------------------------------------------------------------------------------------------
+template <typename T>
+inline T PropertyGet(const PropertyTable *in, const std::string &name, bool &result, bool useTemplate = false) {
+ PropertyPtr prop = in->Get(name);
+ if (nullptr == prop) {
+ if (!useTemplate) {
+ result = false;
+ return T();
+ }
+ const PropertyTable *templ = in->TemplateProps();
+ if (nullptr == templ) {
+ result = false;
+ return T();
+ }
+ prop = templ->Get(name);
+ if (nullptr == prop) {
+ result = false;
+ return T();
+ }
+ }
+
+ // strong typing, no need to be lenient
+ const TypedProperty<T> *const tprop = prop->As<TypedProperty<T>>();
+ if (nullptr == tprop) {
+ result = false;
+ return T();
+ }
+
+ result = true;
+ return tprop->Value();
+}
+} // namespace FBXDocParser
+
+#endif // FBX_PROPERTIES_H
diff --git a/modules/fbx/fbx_parser/FBXTokenizer.cpp b/modules/fbx/fbx_parser/FBXTokenizer.cpp
new file mode 100644
index 0000000000..3748bfabf8
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXTokenizer.cpp
@@ -0,0 +1,251 @@
+/*************************************************************************/
+/* FBXTokenizer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXTokenizer.cpp
+ * @brief Implementation of the FBX broadphase lexer
+ */
+
+// tab width for logging columns
+#define ASSIMP_FBX_TAB_WIDTH 4
+
+#include "FBXTokenizer.h"
+#include "core/string/print_string.h"
+
+namespace FBXDocParser {
+
+// ------------------------------------------------------------------------------------------------
+Token::Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column) :
+ sbegin(p_sbegin),
+ send(p_send),
+ type(p_type),
+ line(p_line),
+ column(p_column) {
+#ifdef DEBUG_ENABLED
+ contents = std::string(sbegin, static_cast<size_t>(send - sbegin));
+#endif
+}
+
+// ------------------------------------------------------------------------------------------------
+Token::~Token() {
+}
+
+namespace {
+
+// ------------------------------------------------------------------------------------------------
+void TokenizeError(const std::string &message, unsigned int line, unsigned int column) {
+ print_error("[FBX-Tokenize]" + String(message.c_str()) + " " + itos(line) + ":" + itos(column));
+}
+
+// process a potential data token up to 'cur', adding it to 'output_tokens'.
+// ------------------------------------------------------------------------------------------------
+void ProcessDataToken(TokenList &output_tokens, const char *&start, const char *&end,
+ unsigned int line,
+ unsigned int column,
+ TokenType type = TokenType_DATA,
+ bool must_have_token = false) {
+ if (start && end) {
+ // sanity check:
+ // tokens should have no whitespace outside quoted text and [start,end] should
+ // properly delimit the valid range.
+ bool in_double_quotes = false;
+ for (const char *c = start; c != end + 1; ++c) {
+ if (*c == '\"') {
+ in_double_quotes = !in_double_quotes;
+ }
+
+ if (!in_double_quotes && IsSpaceOrNewLine(*c)) {
+ TokenizeError("unexpected whitespace in token", line, column);
+ }
+ }
+
+ if (in_double_quotes) {
+ TokenizeError("non-terminated double quotes", line, column);
+ }
+
+ output_tokens.push_back(new_Token(start, end + 1, type, line, column));
+ } else if (must_have_token) {
+ TokenizeError("unexpected character, expected data token", line, column);
+ }
+
+ start = end = nullptr;
+}
+} // namespace
+
+// ------------------------------------------------------------------------------------------------
+void Tokenize(TokenList &output_tokens, const char *input, size_t length) {
+ // line and column numbers numbers are one-based
+ unsigned int line = 1;
+ unsigned int column = 1;
+
+ bool comment = false;
+ bool in_double_quotes = false;
+ bool pending_data_token = false;
+
+ const char *token_begin = nullptr, *token_end = nullptr;
+
+ // input (starting string), *cur the current string, column +=
+ // modified to fix strlen() and stop buffer overflow
+ for (size_t x = 0; x < length; x++) {
+ const char c = input[x];
+ const char *cur = &input[x];
+ column += (c == '\t' ? ASSIMP_FBX_TAB_WIDTH : 1);
+
+ if (IsLineEnd(c)) {
+ comment = false;
+
+ column = 0;
+ ++line;
+ }
+
+ if (comment) {
+ continue;
+ }
+
+ if (in_double_quotes) {
+ if (c == '\"') {
+ in_double_quotes = false;
+ token_end = cur;
+
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ pending_data_token = false;
+ }
+ continue;
+ }
+
+ switch (c) {
+ case '\"':
+ if (token_begin) {
+ TokenizeError("unexpected double-quote", line, column);
+ }
+ token_begin = cur;
+ in_double_quotes = true;
+ continue;
+
+ case ';':
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ comment = true;
+ continue;
+
+ case '{':
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ output_tokens.push_back(new_Token(cur, cur + 1, TokenType_OPEN_BRACKET, line, column));
+ continue;
+
+ case '}':
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column);
+ output_tokens.push_back(new_Token(cur, cur + 1, TokenType_CLOSE_BRACKET, line, column));
+ continue;
+
+ case ',':
+ if (pending_data_token) {
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_DATA, true);
+ }
+ output_tokens.push_back(new_Token(cur, cur + 1, TokenType_COMMA, line, column));
+ continue;
+
+ case ':':
+ if (pending_data_token) {
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column, TokenType_KEY, true);
+ } else {
+ TokenizeError("unexpected colon", line, column);
+ }
+ continue;
+ }
+
+ if (IsSpaceOrNewLine(c)) {
+ if (token_begin) {
+ // peek ahead and check if the next token is a colon in which
+ // case this counts as KEY token.
+ TokenType type = TokenType_DATA;
+ for (const char *peek = cur; *peek && IsSpaceOrNewLine(*peek); ++peek) {
+ if (*peek == ':') {
+ type = TokenType_KEY;
+ cur = peek;
+ break;
+ }
+ }
+
+ ProcessDataToken(output_tokens, token_begin, token_end, line, column, type);
+ }
+
+ pending_data_token = false;
+ } else {
+ token_end = cur;
+ if (!token_begin) {
+ token_begin = cur;
+ }
+
+ pending_data_token = true;
+ }
+ }
+}
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXTokenizer.h b/modules/fbx/fbx_parser/FBXTokenizer.h
new file mode 100644
index 0000000000..1761869c69
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXTokenizer.h
@@ -0,0 +1,203 @@
+/*************************************************************************/
+/* FBXTokenizer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXTokenizer.h
+ * @brief FBX lexer
+ */
+#ifndef FBX_TOKENIZER_H
+#define FBX_TOKENIZER_H
+
+#include "FBXParseTools.h"
+#include "core/string/ustring.h"
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace FBXDocParser {
+/** Rough classification for text FBX tokens used for constructing the
+ * basic scope hierarchy. */
+enum TokenType {
+ // {
+ TokenType_OPEN_BRACKET = 0,
+
+ // }
+ TokenType_CLOSE_BRACKET,
+
+ // '"blablubb"', '2', '*14' - very general token class,
+ // further processing happens at a later stage.
+ TokenType_DATA,
+
+ //
+ TokenType_BINARY_DATA,
+
+ // ,
+ TokenType_COMMA,
+
+ // blubb:
+ TokenType_KEY
+};
+
+/** Represents a single token in a FBX file. Tokens are
+ * classified by the #TokenType enumerated types.
+ *
+ * Offers iterator protocol. Tokens are immutable. */
+class Token {
+private:
+ static const unsigned int BINARY_MARKER = static_cast<unsigned int>(-1);
+
+public:
+ /** construct a textual token */
+ Token(const char *p_sbegin, const char *p_send, TokenType p_type, unsigned int p_line, unsigned int p_column);
+
+ /** construct a binary token */
+ Token(const char *p_sbegin, const char *p_send, TokenType p_type, size_t p_offset);
+ ~Token();
+
+public:
+ std::string StringContents() const {
+ return std::string(begin(), end());
+ }
+
+ bool IsBinary() const {
+ return column == BINARY_MARKER;
+ }
+
+ const char *begin() const {
+ return sbegin;
+ }
+
+ const char *end() const {
+ return send;
+ }
+
+ TokenType Type() const {
+ return type;
+ }
+
+ size_t Offset() const {
+ return offset;
+ }
+
+ unsigned int Line() const {
+ return static_cast<unsigned int>(line);
+ }
+
+ unsigned int Column() const {
+ return column;
+ }
+
+private:
+#ifdef DEBUG_ENABLED
+ // full string copy for the sole purpose that it nicely appears
+ // in msvc's debugger window.
+ std::string contents;
+#endif
+
+ const char *sbegin = nullptr;
+ const char *send = nullptr;
+ const TokenType type;
+
+ union {
+ size_t line;
+ size_t offset;
+ };
+ const unsigned int column = 0;
+};
+
+// Fixed leak by using shared_ptr for tokens
+typedef Token *TokenPtr;
+typedef std::vector<TokenPtr> TokenList;
+
+#define new_Token new Token
+
+/** Main FBX tokenizer function. Transform input buffer into a list of preprocessed tokens.
+ *
+ * Skips over comments and generates line and column numbers.
+ *
+ * @param output_tokens Receives a list of all tokens in the input data.
+ * @param input_buffer Textual input buffer to be processed, 0-terminated.
+ * @print_error if something goes wrong */
+void Tokenize(TokenList &output_tokens, const char *input, size_t length);
+
+/** Tokenizer function for binary FBX files.
+ *
+ * Emits a token list suitable for direct parsing.
+ *
+ * @param output_tokens Receives a list of all tokens in the input data.
+ * @param input_buffer Binary input buffer to be processed.
+ * @param length Length of input buffer, in bytes. There is no 0-terminal.
+ * @print_error if something goes wrong */
+void TokenizeBinary(TokenList &output_tokens, const char *input, size_t length);
+} // namespace FBXDocParser
+
+#endif // FBX_TOKENIZER_H
diff --git a/modules/fbx/fbx_parser/FBXUtil.cpp b/modules/fbx/fbx_parser/FBXUtil.cpp
new file mode 100644
index 0000000000..508407ea51
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXUtil.cpp
@@ -0,0 +1,220 @@
+/*************************************************************************/
+/* FBXUtil.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXUtil.cpp
+ * @brief Implementation of internal FBX utility functions
+ */
+
+#include "FBXUtil.h"
+#include "FBXTokenizer.h"
+#include <cstring>
+#include <string>
+
+namespace FBXDocParser {
+namespace Util {
+
+// ------------------------------------------------------------------------------------------------
+const char *TokenTypeString(TokenType t) {
+ switch (t) {
+ case TokenType_OPEN_BRACKET:
+ return "TOK_OPEN_BRACKET";
+
+ case TokenType_CLOSE_BRACKET:
+ return "TOK_CLOSE_BRACKET";
+
+ case TokenType_DATA:
+ return "TOK_DATA";
+
+ case TokenType_COMMA:
+ return "TOK_COMMA";
+
+ case TokenType_KEY:
+ return "TOK_KEY";
+
+ case TokenType_BINARY_DATA:
+ return "TOK_BINARY_DATA";
+ }
+
+ //ai_assert(false);
+ return "";
+}
+
+// Generated by this formula: T["ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[i]] = i;
+static const uint8_t base64DecodeTable[128] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255,
+ 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
+ 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255
+};
+
+uint8_t DecodeBase64(char ch) {
+ const auto idx = static_cast<uint8_t>(ch);
+ if (idx > 127)
+ return 255;
+ return base64DecodeTable[idx];
+}
+
+size_t ComputeDecodedSizeBase64(const char *in, size_t inLength) {
+ if (inLength < 2) {
+ return 0;
+ }
+ const size_t equals = size_t(in[inLength - 1] == '=') + size_t(in[inLength - 2] == '=');
+ const size_t full_length = (inLength * 3) >> 2; // div by 4
+ if (full_length < equals) {
+ return 0;
+ }
+ return full_length - equals;
+}
+
+size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength) {
+ if (maxOutLength == 0 || inLength < 2) {
+ return 0;
+ }
+ const size_t realLength = inLength - size_t(in[inLength - 1] == '=') - size_t(in[inLength - 2] == '=');
+ size_t dst_offset = 0;
+ int val = 0, valb = -8;
+ for (size_t src_offset = 0; src_offset < realLength; ++src_offset) {
+ const uint8_t table_value = Util::DecodeBase64(in[src_offset]);
+ if (table_value == 255) {
+ return 0;
+ }
+ val = (val << 6) + table_value;
+ valb += 6;
+ if (valb >= 0) {
+ out[dst_offset++] = static_cast<uint8_t>((val >> valb) & 0xFF);
+ valb -= 8;
+ val &= 0xFFF;
+ }
+ }
+ return dst_offset;
+}
+
+static const char to_base64_string[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+char EncodeBase64(char byte) {
+ return to_base64_string[(size_t)byte];
+}
+
+/** Encodes a block of 4 bytes to base64 encoding
+* @param bytes Bytes to encode.
+* @param out_string String to write encoded values to.
+* @param string_pos Position in out_string.
+*/
+void EncodeByteBlock(const char *bytes, std::string &out_string, size_t string_pos) {
+ char b0 = (bytes[0] & 0xFC) >> 2;
+ char b1 = (bytes[0] & 0x03) << 4 | ((bytes[1] & 0xF0) >> 4);
+ char b2 = (bytes[1] & 0x0F) << 2 | ((bytes[2] & 0xC0) >> 6);
+ char b3 = (bytes[2] & 0x3F);
+
+ out_string[string_pos + 0] = EncodeBase64(b0);
+ out_string[string_pos + 1] = EncodeBase64(b1);
+ out_string[string_pos + 2] = EncodeBase64(b2);
+ out_string[string_pos + 3] = EncodeBase64(b3);
+}
+
+std::string EncodeBase64(const char *data, size_t length) {
+ // calculate extra bytes needed to get a multiple of 3
+ size_t extraBytes = 3 - length % 3;
+
+ // number of base64 bytes
+ size_t encodedBytes = 4 * (length + extraBytes) / 3;
+
+ std::string encoded_string(encodedBytes, '=');
+
+ // read blocks of 3 bytes
+ for (size_t ib3 = 0; ib3 < length / 3; ib3++) {
+ const size_t iByte = ib3 * 3;
+ const size_t iEncodedByte = ib3 * 4;
+ const char *currData = &data[iByte];
+
+ EncodeByteBlock(currData, encoded_string, iEncodedByte);
+ }
+
+ // if size of data is not a multiple of 3, also encode the final bytes (and add zeros where needed)
+ if (extraBytes > 0) {
+ char finalBytes[4] = { 0, 0, 0, 0 };
+ memcpy(&finalBytes[0], &data[length - length % 3], length % 3);
+
+ const size_t iEncodedByte = encodedBytes - 4;
+ EncodeByteBlock(&finalBytes[0], encoded_string, iEncodedByte);
+
+ // add '=' at the end
+ for (size_t i = 0; i < 4 * extraBytes / 3; i++)
+ encoded_string[encodedBytes - i - 1] = '=';
+ }
+ return encoded_string;
+}
+} // namespace Util
+} // namespace FBXDocParser
diff --git a/modules/fbx/fbx_parser/FBXUtil.h b/modules/fbx/fbx_parser/FBXUtil.h
new file mode 100644
index 0000000000..553d5fbc6b
--- /dev/null
+++ b/modules/fbx/fbx_parser/FBXUtil.h
@@ -0,0 +1,122 @@
+/*************************************************************************/
+/* FBXUtil.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+/*
+Open Asset Import Library (assimp)
+----------------------------------------------------------------------
+
+Copyright (c) 2006-2019, 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 FBXUtil.h
+ * @brief FBX utility functions for internal use
+ */
+#ifndef FBX_UTIL_H
+#define FBX_UTIL_H
+
+#include "FBXTokenizer.h"
+#include <stdint.h>
+
+namespace FBXDocParser {
+
+namespace Util {
+
+/** Get a string representation for a #TokenType. */
+const char *TokenTypeString(TokenType t);
+
+/** Decode a single Base64-encoded character.
+*
+* @param ch Character to decode (from base64 to binary).
+* @return decoded byte value*/
+uint8_t DecodeBase64(char ch);
+
+/** Compute decoded size of a Base64-encoded string
+*
+* @param in Characters to decode.
+* @param inLength Number of characters to decode.
+* @return size of the decoded data (number of bytes)*/
+size_t ComputeDecodedSizeBase64(const char *in, size_t inLength);
+
+/** Decode a Base64-encoded string
+*
+* @param in Characters to decode.
+* @param inLength Number of characters to decode.
+* @param out Pointer where we will store the decoded data.
+* @param maxOutLength Size of output buffer.
+* @return size of the decoded data (number of bytes)*/
+size_t DecodeBase64(const char *in, size_t inLength, uint8_t *out, size_t maxOutLength);
+
+char EncodeBase64(char byte);
+
+/** Encode bytes in base64-encoding
+*
+* @param data Binary data to encode.
+* @param inLength Number of bytes to encode.
+* @return base64-encoded string*/
+std::string EncodeBase64(const char *data, size_t length);
+} // namespace Util
+} // namespace FBXDocParser
+
+#endif // FBX_UTIL_H
diff --git a/modules/fbx/fbx_parser/LICENSE b/modules/fbx/fbx_parser/LICENSE
new file mode 100644
index 0000000000..b42fc6efe6
--- /dev/null
+++ b/modules/fbx/fbx_parser/LICENSE
@@ -0,0 +1,39 @@
+The files in this folder were originally from ASSIMP, but have been heavily modified to fix bugs and match coding
+conventions of the Godot Engine project. We have kept a copy of the applicable licenses in the folder as required by
+the license.
+
+Open Asset Import Library (assimp)
+
+Copyright (c) 2006-2020, 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.
+