diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..dd84ea782
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..bbcbbe7d6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/Readme.md b/Readme.md
index 15e76b9d2..dc54870a0 100644
--- a/Readme.md
+++ b/Readme.md
@@ -25,107 +25,12 @@ Additionally, assimp features various __mesh post processing tools__: normals an
This is the development repo containing the latest features and bugfixes. For productive use though, we recommend one of the stable releases available from [Github Assimp Releases](https://github.com/assimp/assimp/releases).
-Monthly donations via Patreon:
-
[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp)
-
-
-
-One-off donations via PayPal:
-
[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4)
-
-
-
Please check our Wiki as well: https://github.com/assimp/assimp/wiki
If you want to check our Model-Database, use the following repo: https://github.com/assimp/assimp-mdb
#### Supported file formats ####
-
-__Importers__:
-
-- 3D
-- [3DS](https://en.wikipedia.org/wiki/.3ds)
-- [3MF](https://en.wikipedia.org/wiki/3D_Manufacturing_Format)
-- AC
-- [AC3D](https://en.wikipedia.org/wiki/AC3D)
-- ACC
-- AMJ
-- ASE
-- ASK
-- B3D
-- [BLEND](https://en.wikipedia.org/wiki/.blend_(file_format))
-- [BVH](https://en.wikipedia.org/wiki/Biovision_Hierarchy)
-- CMS
-- COB
-- [DAE/Collada](https://en.wikipedia.org/wiki/COLLADA)
-- [DXF](https://en.wikipedia.org/wiki/AutoCAD_DXF)
-- ENFF
-- [FBX](https://en.wikipedia.org/wiki/FBX)
-- [glTF 1.0](https://en.wikipedia.org/wiki/GlTF#glTF_1.0) + GLB
-- [glTF 2.0](https://en.wikipedia.org/wiki/GlTF#glTF_2.0):
- At the moment for glTF2.0 the following extensions are supported:
- + KHR_lights_punctual ( 5.0 )
- + KHR_materials_pbrSpecularGlossiness ( 5.0 )
- + KHR_materials_unlit ( 5.0 )
- + KHR_texture_transform ( 5.1 under test )
-- HMB
-- IFC-STEP
-- IRR / IRRMESH
-- [LWO](https://en.wikipedia.org/wiki/LightWave_3D)
-- LWS
-- LXO
-- [M3D](https://bztsrc.gitlab.io/model3d)
-- MD2
-- MD3
-- MD5
-- MDC
-- MDL
-- MESH / MESH.XML
-- MOT
-- MS3D
-- NDO
-- NFF
-- [OBJ](https://en.wikipedia.org/wiki/Wavefront_.obj_file)
-- [OFF](https://en.wikipedia.org/wiki/OFF_(file_format))
-- [OGEX](https://en.wikipedia.org/wiki/Open_Game_Engine_Exchange)
-- [PLY](https://en.wikipedia.org/wiki/PLY_(file_format))
-- PMX
-- PRJ
-- Q3O
-- Q3S
-- RAW
-- SCN
-- SIB
-- SMD
-- [STP](https://en.wikipedia.org/wiki/ISO_10303-21)
-- [STL](https://en.wikipedia.org/wiki/STL_(file_format))
-- TER
-- UC
-- VTA
-- X
-- [X3D](https://en.wikipedia.org/wiki/X3D)
-- XGL
-- ZGL
-
-Additionally, some formats are supported by dependency on non-free code or external SDKs (not built by default):
-
-- [C4D](https://en.wikipedia.org/wiki/Cinema_4D) (https://github.com/assimp/assimp/wiki/Cinema4D-&-Melange) IMporting geometry + node hierarchy are currently supported
-
-__Exporters__:
-
-- DAE (Collada)
-- STL
-- OBJ
-- PLY
-- X
-- 3DS
-- JSON (for WebGl, via https://github.com/acgessler/assimp2json)
-- ASSBIN
-- STEP
-- glTF 1.0 (partial)
-- glTF 2.0 (partial)
-- 3MF ( experimental )
-- FBX ( experimental )
+You can find the complete list of supported file-formats [here](https://github.com/assimp/assimp/blob/master/doc/Fileformats.md)
### Building ###
Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. We are available in vcpkg, and our build system is CMake; if you used CMake before there is a good chance you know what to do.
@@ -196,6 +101,16 @@ Become a financial contributor and help us sustain our community. [[Contribute](
+Monthly donations via Patreon:
+
[![Patreon](https://cloud.githubusercontent.com/assets/8225057/5990484/70413560-a9ab-11e4-8942-1a63607c0b00.png)](http://www.patreon.com/assimp)
+
+
+
+One-off donations via PayPal:
+
[![PayPal](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4JRJVPXC4QJM4)
+
+
+
#### Organizations
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/assimp/contribute)]
diff --git a/code/AMF/AMFImporter.cpp b/code/AMF/AMFImporter.cpp
new file mode 100644
index 000000000..d93ca54cf
--- /dev/null
+++ b/code/AMF/AMFImporter.cpp
@@ -0,0 +1,707 @@
+/*
+---------------------------------------------------------------------------
+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 AMFImporter.cpp
+/// \brief AMF-format files importer for Assimp: main algorithm implementation.
+/// \date 2016
+/// \author smal.root@gmail.com
+
+#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
+
+// Header files, Assimp.
+#include "AMFImporter.hpp"
+#include "AMFImporter_Macro.hpp"
+
+#include
+#include
+
+// Header files, stdlib.
+#include
+
+namespace Assimp
+{
+
+/// \var aiImporterDesc AMFImporter::Description
+/// Conastant which hold importer description
+const aiImporterDesc AMFImporter::Description = {
+ "Additive manufacturing file format(AMF) Importer",
+ "smalcom",
+ "",
+ "See documentation in source code. Chapter: Limitations.",
+ aiImporterFlags_SupportTextFlavour | aiImporterFlags_LimitedSupport | aiImporterFlags_Experimental,
+ 0,
+ 0,
+ 0,
+ 0,
+ "amf"
+};
+
+void AMFImporter::Clear()
+{
+ mNodeElement_Cur = nullptr;
+ mUnit.clear();
+ mMaterial_Converted.clear();
+ mTexture_Converted.clear();
+ // Delete all elements
+ if(!mNodeElement_List.empty())
+ {
+ for(CAMFImporter_NodeElement* ne: mNodeElement_List) { delete ne; }
+
+ mNodeElement_List.clear();
+ }
+}
+
+AMFImporter::~AMFImporter()
+{
+ if(mReader != nullptr) delete mReader;
+ // Clear() is accounting if data already is deleted. So, just check again if all data is deleted.
+ Clear();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: find set ************************************************************/
+/*********************************************************************************************************************************************/
+
+bool AMFImporter::Find_NodeElement(const std::string& pID, const CAMFImporter_NodeElement::EType pType, CAMFImporter_NodeElement** pNodeElement) const
+{
+ for(CAMFImporter_NodeElement* ne: mNodeElement_List)
+ {
+ if((ne->ID == pID) && (ne->Type == pType))
+ {
+ if(pNodeElement != nullptr) *pNodeElement = ne;
+
+ return true;
+ }
+ }// for(CAMFImporter_NodeElement* ne: mNodeElement_List)
+
+ return false;
+}
+
+bool AMFImporter::Find_ConvertedNode(const std::string& pID, std::list& pNodeList, aiNode** pNode) const
+{
+aiString node_name(pID.c_str());
+
+ for(aiNode* node: pNodeList)
+ {
+ if(node->mName == node_name)
+ {
+ if(pNode != nullptr) *pNode = node;
+
+ return true;
+ }
+ }// for(aiNode* node: pNodeList)
+
+ return false;
+}
+
+bool AMFImporter::Find_ConvertedMaterial(const std::string& pID, const SPP_Material** pConvertedMaterial) const
+{
+ for(const SPP_Material& mat: mMaterial_Converted)
+ {
+ if(mat.ID == pID)
+ {
+ if(pConvertedMaterial != nullptr) *pConvertedMaterial = &mat;
+
+ return true;
+ }
+ }// for(const SPP_Material& mat: mMaterial_Converted)
+
+ return false;
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: throw set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::Throw_CloseNotFound(const std::string& pNode)
+{
+ throw DeadlyImportError("Close tag for node <" + pNode + "> not found. Seems file is corrupt.");
+}
+
+void AMFImporter::Throw_IncorrectAttr(const std::string& pAttrName)
+{
+ throw DeadlyImportError("Node <" + std::string(mReader->getNodeName()) + "> has incorrect attribute \"" + pAttrName + "\".");
+}
+
+void AMFImporter::Throw_IncorrectAttrValue(const std::string& pAttrName)
+{
+ throw DeadlyImportError("Attribute \"" + pAttrName + "\" in node <" + std::string(mReader->getNodeName()) + "> has incorrect value.");
+}
+
+void AMFImporter::Throw_MoreThanOnceDefined(const std::string& pNodeType, const std::string& pDescription)
+{
+ throw DeadlyImportError("\"" + pNodeType + "\" node can be used only once in " + mReader->getNodeName() + ". Description: " + pDescription);
+}
+
+void AMFImporter::Throw_ID_NotFound(const std::string& pID) const
+{
+ throw DeadlyImportError("Not found node with name \"" + pID + "\".");
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************* Functions: XML set ************************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::XML_CheckNode_MustHaveChildren()
+{
+ if(mReader->isEmptyElement()) throw DeadlyImportError(std::string("Node <") + mReader->getNodeName() + "> must have children.");
+}
+
+void AMFImporter::XML_CheckNode_SkipUnsupported(const std::string& pParentNodeName)
+{
+ static const size_t Uns_Skip_Len = 3;
+ const char* Uns_Skip[Uns_Skip_Len] = { "composite", "edge", "normal" };
+
+ static bool skipped_before[Uns_Skip_Len] = { false, false, false };
+
+ std::string nn(mReader->getNodeName());
+ bool found = false;
+ bool close_found = false;
+ size_t sk_idx;
+
+ for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
+ {
+ if(nn != Uns_Skip[sk_idx]) continue;
+
+ found = true;
+ if(mReader->isEmptyElement())
+ {
+ close_found = true;
+
+ goto casu_cres;
+ }
+
+ while(mReader->read())
+ {
+ if((mReader->getNodeType() == irr::io::EXN_ELEMENT_END) && (nn == mReader->getNodeName()))
+ {
+ close_found = true;
+
+ goto casu_cres;
+ }
+ }
+ }// for(sk_idx = 0; sk_idx < Uns_Skip_Len; sk_idx++)
+
+casu_cres:
+
+ if(!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + ".");
+ if(!close_found) Throw_CloseNotFound(nn);
+
+ if(!skipped_before[sk_idx])
+ {
+ skipped_before[sk_idx] = true;
+ ASSIMP_LOG_WARN_F("Skipping node \"", nn, "\" in ", pParentNodeName, ".");
+ }
+}
+
+bool AMFImporter::XML_SearchNode(const std::string& pNodeName)
+{
+ while(mReader->read())
+ {
+ if((mReader->getNodeType() == irr::io::EXN_ELEMENT) && XML_CheckNode_NameEqual(pNodeName)) return true;
+ }
+
+ return false;
+}
+
+bool AMFImporter::XML_ReadNode_GetAttrVal_AsBool(const int pAttrIdx)
+{
+ std::string val(mReader->getAttributeValue(pAttrIdx));
+
+ if((val == "false") || (val == "0"))
+ return false;
+ else if((val == "true") || (val == "1"))
+ return true;
+ else
+ throw DeadlyImportError("Bool attribute value can contain \"false\"/\"0\" or \"true\"/\"1\" not the \"" + val + "\"");
+}
+
+float AMFImporter::XML_ReadNode_GetAttrVal_AsFloat(const int pAttrIdx)
+{
+ std::string val;
+ float tvalf;
+
+ ParseHelper_FixTruncatedFloatString(mReader->getAttributeValue(pAttrIdx), val);
+ fast_atoreal_move(val.c_str(), tvalf, false);
+
+ return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetAttrVal_AsU32(const int pAttrIdx)
+{
+ return strtoul10(mReader->getAttributeValue(pAttrIdx));
+}
+
+float AMFImporter::XML_ReadNode_GetVal_AsFloat()
+{
+ std::string val;
+ float tvalf;
+
+ if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. No data, seems file is corrupt.");
+ if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsFloat. Invalid type of XML element, seems file is corrupt.");
+
+ ParseHelper_FixTruncatedFloatString(mReader->getNodeData(), val);
+ fast_atoreal_move(val.c_str(), tvalf, false);
+
+ return tvalf;
+}
+
+uint32_t AMFImporter::XML_ReadNode_GetVal_AsU32()
+{
+ if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. No data, seems file is corrupt.");
+ if(mReader->getNodeType() != irr::io::EXN_TEXT) throw DeadlyImportError("XML_ReadNode_GetVal_AsU32. Invalid type of XML element, seems file is corrupt.");
+
+ return strtoul10(mReader->getNodeData());
+}
+
+void AMFImporter::XML_ReadNode_GetVal_AsString(std::string& pValue)
+{
+ if(!mReader->read()) throw DeadlyImportError("XML_ReadNode_GetVal_AsString. No data, seems file is corrupt.");
+ if(mReader->getNodeType() != irr::io::EXN_TEXT)
+ throw DeadlyImportError("XML_ReadNode_GetVal_AsString. Invalid type of XML element, seems file is corrupt.");
+
+ pValue = mReader->getNodeData();
+}
+
+/*********************************************************************************************************************************************/
+/************************************************************ Functions: parse set ***********************************************************/
+/*********************************************************************************************************************************************/
+
+void AMFImporter::ParseHelper_Node_Enter(CAMFImporter_NodeElement* pNode)
+{
+ mNodeElement_Cur->Child.push_back(pNode);// add new element to current element child list.
+ mNodeElement_Cur = pNode;// switch current element to new one.
+}
+
+void AMFImporter::ParseHelper_Node_Exit()
+{
+ // check if we can walk up.
+ if(mNodeElement_Cur != nullptr) mNodeElement_Cur = mNodeElement_Cur->Parent;
+}
+
+void AMFImporter::ParseHelper_FixTruncatedFloatString(const char* pInStr, std::string& pOutString)
+{
+ size_t instr_len;
+
+ pOutString.clear();
+ instr_len = strlen(pInStr);
+ if(!instr_len) return;
+
+ pOutString.reserve(instr_len * 3 / 2);
+ // check and correct floats in format ".x". Must be "x.y".
+ if(pInStr[0] == '.') pOutString.push_back('0');
+
+ pOutString.push_back(pInStr[0]);
+ for(size_t ci = 1; ci < instr_len; ci++)
+ {
+ if((pInStr[ci] == '.') && ((pInStr[ci - 1] == ' ') || (pInStr[ci - 1] == '-') || (pInStr[ci - 1] == '+') || (pInStr[ci - 1] == '\t')))
+ {
+ pOutString.push_back('0');
+ pOutString.push_back('.');
+ }
+ else
+ {
+ pOutString.push_back(pInStr[ci]);
+ }
+ }
+}
+
+static bool ParseHelper_Decode_Base64_IsBase64(const char pChar)
+{
+ return (isalnum(pChar) || (pChar == '+') || (pChar == '/'));
+}
+
+void AMFImporter::ParseHelper_Decode_Base64(const std::string& pInputBase64, std::vector& pOutputData) const
+{
+ // With help from
+ // René Nyffenegger http://www.adp-gmbh.ch/cpp/common/base64.html
+ const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ uint8_t tidx = 0;
+ uint8_t arr4[4], arr3[3];
+
+ // check input data
+ if(pInputBase64.size() % 4) throw DeadlyImportError("Base64-encoded data must have size multiply of four.");
+ // prepare output place
+ pOutputData.clear();
+ pOutputData.reserve(pInputBase64.size() / 4 * 3);
+
+ for(size_t in_len = pInputBase64.size(), in_idx = 0; (in_len > 0) && (pInputBase64[in_idx] != '='); in_len--)
+ {
+ if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
+ {
+ arr4[tidx++] = pInputBase64[in_idx++];
+ if(tidx == 4)
+ {
+ for(tidx = 0; tidx < 4; tidx++) arr4[tidx] = (uint8_t)base64_chars.find(arr4[tidx]);
+
+ arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+ arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+ arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+ for(tidx = 0; tidx < 3; tidx++) pOutputData.push_back(arr3[tidx]);
+
+ tidx = 0;
+ }// if(tidx == 4)
+ }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx]))
+ else
+ {
+ in_idx++;
+ }// if(ParseHelper_Decode_Base64_IsBase64(pInputBase64[in_idx])) else
+ }
+
+ if(tidx)
+ {
+ for(uint8_t i = tidx; i < 4; i++) arr4[i] = 0;
+ for(uint8_t i = 0; i < 4; i++) arr4[i] = (uint8_t)(base64_chars.find(arr4[i]));
+
+ arr3[0] = (arr4[0] << 2) + ((arr4[1] & 0x30) >> 4);
+ arr3[1] = ((arr4[1] & 0x0F) << 4) + ((arr4[2] & 0x3C) >> 2);
+ arr3[2] = ((arr4[2] & 0x03) << 6) + arr4[3];
+ for(uint8_t i = 0; i < (tidx - 1); i++) pOutputData.push_back(arr3[i]);
+ }
+}
+
+void AMFImporter::ParseFile(const std::string& pFile, IOSystem* pIOHandler)
+{
+ irr::io::IrrXMLReader* OldReader = mReader;// store current XMLreader.
+ std::unique_ptr file(pIOHandler->Open(pFile, "rb"));
+
+ // Check whether we can read from the file
+ if (file.get() == nullptr) {
+ throw DeadlyImportError("Failed to open AMF file " + pFile + ".");
+ }
+
+ // generate a XML reader for it
+ std::unique_ptr mIOWrapper(new CIrrXML_IOStreamReader(file.get()));
+ mReader = irr::io::createIrrXMLReader(mIOWrapper.get());
+ if(!mReader) throw DeadlyImportError("Failed to create XML reader for file" + pFile + ".");
+ //
+ // start reading
+ // search for root tag
+ if(XML_SearchNode("amf"))
+ ParseNode_Root();
+ else
+ throw DeadlyImportError("Root node \"amf\" not found.");
+
+ delete mReader;
+ // restore old XMLreader
+ mReader = OldReader;
+}
+
+//
+//
+// Root XML element.
+// Multi elements - No.
+void AMFImporter::ParseNode_Root()
+{
+ std::string unit, version;
+ CAMFImporter_NodeElement *ne( nullptr );
+
+ // Read attributes for node .
+ MACRO_ATTRREAD_LOOPBEG;
+ MACRO_ATTRREAD_CHECK_RET("unit", unit, mReader->getAttributeValue);
+ MACRO_ATTRREAD_CHECK_RET("version", version, mReader->getAttributeValue);
+ MACRO_ATTRREAD_LOOPEND_WSKIP;
+
+ // Check attributes
+ if(!mUnit.empty())
+ {
+ if((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) Throw_IncorrectAttrValue("unit");
+ }
+
+ // create root node element.
+ ne = new CAMFImporter_NodeElement_Root(nullptr);
+ mNodeElement_Cur = ne;// set first "current" element
+ // and assign attribute's values
+ ((CAMFImporter_NodeElement_Root*)ne)->Unit = unit;
+ ((CAMFImporter_NodeElement_Root*)ne)->Version = version;
+
+ // Check for child nodes
+ if(!mReader->isEmptyElement())
+ {
+ MACRO_NODECHECK_LOOPBEGIN("amf");
+ if(XML_CheckNode_NameEqual("object")) { ParseNode_Object(); continue; }
+ if(XML_CheckNode_NameEqual("material")) { ParseNode_Material(); continue; }
+ if(XML_CheckNode_NameEqual("texture")) { ParseNode_Texture(); continue; }
+ if(XML_CheckNode_NameEqual("constellation")) { ParseNode_Constellation(); continue; }
+ if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
+ MACRO_NODECHECK_LOOPEND("amf");
+ mNodeElement_Cur = ne;// force restore "current" element
+ }// if(!mReader->isEmptyElement())
+
+ mNodeElement_List.push_back(ne);// add to node element list because its a new object in graph.
+}
+
+//
+//
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - .
+void AMFImporter::ParseNode_Constellation()
+{
+ std::string id;
+ CAMFImporter_NodeElement* ne( nullptr );
+
+ // Read attributes for node .
+ MACRO_ATTRREAD_LOOPBEG;
+ MACRO_ATTRREAD_CHECK_RET("id", id, mReader->getAttributeValue);
+ MACRO_ATTRREAD_LOOPEND;
+
+ // create and if needed - define new grouping object.
+ ne = new CAMFImporter_NodeElement_Constellation(mNodeElement_Cur);
+
+ CAMFImporter_NodeElement_Constellation& als = *((CAMFImporter_NodeElement_Constellation*)ne);// alias for convenience
+
+ if(!id.empty()) als.ID = id;
+ // Check for child nodes
+ if(!mReader->isEmptyElement())
+ {
+ ParseHelper_Node_Enter(ne);
+ MACRO_NODECHECK_LOOPBEGIN("constellation");
+ if(XML_CheckNode_NameEqual("instance")) { ParseNode_Instance(); continue; }
+ if(XML_CheckNode_NameEqual("metadata")) { ParseNode_Metadata(); continue; }
+ MACRO_NODECHECK_LOOPEND("constellation");
+ ParseHelper_Node_Exit();
+ }// if(!mReader->isEmptyElement())
+ else
+ {
+ mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+ }// if(!mReader->isEmptyElement()) else
+
+ mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+//
+//
+// A collection of objects or constellations with specific relative locations.
+// Multi elements - Yes.
+// Parent element - .
+void AMFImporter::ParseNode_Instance()
+{
+ std::string objectid;
+ CAMFImporter_NodeElement* ne( nullptr );
+
+ // Read attributes for node .
+ MACRO_ATTRREAD_LOOPBEG;
+ MACRO_ATTRREAD_CHECK_RET("objectid", objectid, mReader->getAttributeValue);
+ MACRO_ATTRREAD_LOOPEND;
+
+ // used object id must be defined, check that.
+ if(objectid.empty()) throw DeadlyImportError("\"objectid\" in must be defined.");
+ // create and define new grouping object.
+ ne = new CAMFImporter_NodeElement_Instance(mNodeElement_Cur);
+
+ CAMFImporter_NodeElement_Instance& als = *((CAMFImporter_NodeElement_Instance*)ne);// alias for convenience
+
+ als.ObjectID = objectid;
+ // Check for child nodes
+ if(!mReader->isEmptyElement())
+ {
+ bool read_flag[6] = { false, false, false, false, false, false };
+
+ als.Delta.Set(0, 0, 0);
+ als.Rotation.Set(0, 0, 0);
+ ParseHelper_Node_Enter(ne);
+ MACRO_NODECHECK_LOOPBEGIN("instance");
+ MACRO_NODECHECK_READCOMP_F("deltax", read_flag[0], als.Delta.x);
+ MACRO_NODECHECK_READCOMP_F("deltay", read_flag[1], als.Delta.y);
+ MACRO_NODECHECK_READCOMP_F("deltaz", read_flag[2], als.Delta.z);
+ MACRO_NODECHECK_READCOMP_F("rx", read_flag[3], als.Rotation.x);
+ MACRO_NODECHECK_READCOMP_F("ry", read_flag[4], als.Rotation.y);
+ MACRO_NODECHECK_READCOMP_F("rz", read_flag[5], als.Rotation.z);
+ MACRO_NODECHECK_LOOPEND("instance");
+ ParseHelper_Node_Exit();
+ // also convert degrees to radians.
+ als.Rotation.x = AI_MATH_PI_F * als.Rotation.x / 180.0f;
+ als.Rotation.y = AI_MATH_PI_F * als.Rotation.y / 180.0f;
+ als.Rotation.z = AI_MATH_PI_F * als.Rotation.z / 180.0f;
+ }// if(!mReader->isEmptyElement())
+ else
+ {
+ mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+ }// if(!mReader->isEmptyElement()) else
+
+ mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
+}
+
+//
+// An object definition.
+// Multi elements - Yes.
+// Parent element - .
+void AMFImporter::ParseNode_Object()
+{
+ std::string id;
+ CAMFImporter_NodeElement* ne( nullptr );
+
+ // Read attributes for node