diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 35448d4dc..e1ab13396 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,2 +1 @@
-patreon: assimp
open_collective: assimp
diff --git a/.gitignore b/.gitignore
index f9c1a490f..0a999d3aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -116,3 +116,6 @@ tools/assimp_qt_viewer/moc_glview.cpp_parameters
tools/assimp_qt_viewer/moc_mainwindow.cpp
tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters
tools/assimp_qt_viewer/ui_mainwindow.h
+
+#Generated directory
+generated/*
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6d9b3977..97a3641f5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -454,7 +454,7 @@ IF(ASSIMP_HUNTER_ENABLED)
ELSE()
# If the zlib is already found outside, add an export in case assimpTargets can't find it.
IF( ZLIB_FOUND )
- INSTALL( TARGETS zlib
+ INSTALL( TARGETS zlib zlibstatic
EXPORT "${TARGETS_EXPORT_NAME}")
ENDIF()
diff --git a/INSTALL b/INSTALL
index ecec2585b..2cfd38078 100644
--- a/INSTALL
+++ b/INSTALL
@@ -8,43 +8,10 @@ Getting the documentation
------------------------------
A regularly-updated copy is available at
-http://assimp.sourceforge.net/lib_html/index.html
-
-A CHM file is included in the SVN repos: ./doc/AssimpDoc_Html/AssimpDoc.chm.
-To build the doxygen documentation on your own, follow these steps:
-
-a) download & install latest doxygen
-b) make sure doxygen is in the executable search path
-c) navigate to ./doc
-d) and run 'doxygen'
-
-Open the generated HTML (AssimpDoc_Html/index.html) in the browser of your choice.
-Windows only: To generate the CHM doc, install 'Microsoft HTML Workshop'
-and configure the path to it in the DOXYFILE first.
+https://assimp-docs.readthedocs.io/en/latest/
------------------------------
Building Assimp
------------------------------
-More detailed build instructions can be found in the documentation,
-this section is just for the inpatient among you.
-
-CMake is the preferred build system for Assimp. The minimum required version
-is 2.6. If you don't have it yet, downloads for CMake can be found on
-http://www.cmake.org/.
-
-For Unix:
-
-1. mkdir build && cd build
-2. cmake .. -G 'Unix Makefiles'
-3. make -j4
-
-For Windows:
-1. Open a command prompt
-2. mkdir build
-3. cd build
-4. cmake ..
-5. cmake --build .
-
-For iOS:
-Just check the following project, which deploys a compiler toolchain for different iOS-versions: https://github.com/assimp/assimp/tree/master/port/iOS
+Just check the build-instaructions which you can find here: https://github.com/assimp/assimp/blob/master/Build.md
diff --git a/LICENSE b/LICENSE
index dc8e24706..acaaf016e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
Open Asset Import Library (assimp)
-Copyright (c) 2006-2020, assimp team
+Copyright (c) 2006-2021, assimp team
All rights reserved.
Redistribution and use of this software in source and binary forms,
diff --git a/Readme.md b/Readme.md
index 71b3c7f10..51684c4aa 100644
--- a/Readme.md
+++ b/Readme.md
@@ -76,9 +76,6 @@ For more information, visit [our website](http://assimp.org/). Or check out the
If the docs don't solve your problem, ask on [StackOverflow with the assimp-tag](http://stackoverflow.com/questions/tagged/assimp?sort=newest). If you think you found a bug, please open an issue on Github.
-For development discussions, there is also a (very low-volume) mailing list, _assimp-discussions_
- [(subscribe here)]( https://lists.sourceforge.net/lists/listinfo/assimp-discussions)
-
Open Asset Import Library is a library to load various 3d file formats into a shared, in-memory format. It supports more than __40 file formats__ for import and a growing selection of file formats for export.
And we also have a Gitter-channel:Gitter [![Join the chat at https://gitter.im/assimp/assimp](https://badges.gitter.im/assimp/assimp.svg)](https://gitter.im/assimp/assimp?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -103,9 +100,6 @@ 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)
-
#### Organizations
diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp
index 3dabf9da1..0a64f6870 100644
--- a/code/AssetLib/3DS/3DSLoader.cpp
+++ b/code/AssetLib/3DS/3DSLoader.cpp
@@ -981,9 +981,9 @@ void Discreet3DSImporter::ParseMeshChunk() {
mMesh.mMat.a3 = stream->GetF4();
mMesh.mMat.b3 = stream->GetF4();
mMesh.mMat.c3 = stream->GetF4();
- mMesh.mMat.a4 = stream->GetF4();
- mMesh.mMat.b4 = stream->GetF4();
- mMesh.mMat.c4 = stream->GetF4();
+ mMesh.mMat.d1 = stream->GetF4();
+ mMesh.mMat.d2 = stream->GetF4();
+ mMesh.mMat.d3 = stream->GetF4();
} break;
case Discreet3DS::CHUNK_MAPLIST: {
diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp
index 7d6ef2fd0..37756aea0 100644
--- a/code/AssetLib/AMF/AMFImporter.cpp
+++ b/code/AssetLib/AMF/AMFImporter.cpp
@@ -39,16 +39,10 @@ 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
@@ -307,14 +301,14 @@ void AMFImporter::ParseNode_Root() {
throw DeadlyImportError("Root node \"amf\" not found.");
}
XmlNode node = *root;
- mUnit = ai_str_tolower(std::string(node.attribute("unit").as_string()));
+ mUnit = ai_tolower(std::string(node.attribute("unit").as_string()));
mVersion = node.attribute("version").as_string();
// Read attributes for node .
// Check attributes
if (!mUnit.empty()) {
- if ((mUnit != "inch") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
+ if ((mUnit != "inch") && (mUnit != "millimeters") && (mUnit != "millimeter") && (mUnit != "meter") && (mUnit != "feet") && (mUnit != "micron")) {
Throw_IncorrectAttrValue("unit", mUnit);
}
}
@@ -409,20 +403,20 @@ void AMFImporter::ParseNode_Instance(XmlNode &node) {
if (!node.empty()) {
ParseHelper_Node_Enter(ne);
- for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+ for (auto ¤tNode : node.children()) {
const std::string ¤tName = currentNode.name();
if (currentName == "deltax") {
- als.Delta.x = (ai_real)std::atof(currentNode.value());
+ XmlParser::getValueAsFloat(currentNode, als.Delta.x);
} else if (currentName == "deltay") {
- als.Delta.y = (ai_real)std::atof(currentNode.value());
+ XmlParser::getValueAsFloat(currentNode, als.Delta.y);
} else if (currentName == "deltaz") {
- als.Delta.z = (ai_real)std::atof(currentNode.value());
+ XmlParser::getValueAsFloat(currentNode, als.Delta.z);
} else if (currentName == "rx") {
- als.Delta.x = (ai_real)std::atof(currentNode.value());
+ XmlParser::getValueAsFloat(currentNode, als.Delta.x);
} else if (currentName == "ry") {
- als.Delta.y = (ai_real)std::atof(currentNode.value());
+ XmlParser::getValueAsFloat(currentNode, als.Delta.y);
} else if (currentName == "rz") {
- als.Delta.z = (ai_real)std::atof(currentNode.value());
+ XmlParser::getValueAsFloat(currentNode, als.Delta.z);
}
}
ParseHelper_Node_Exit();
@@ -458,7 +452,7 @@ void AMFImporter::ParseNode_Object(XmlNode &node) {
// Check for child nodes
if (!node.empty()) {
ParseHelper_Node_Enter(ne);
- for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+ for (auto ¤tNode : node.children()) {
const std::string ¤tName = currentNode.name();
if (currentName == "color") {
ParseNode_Color(currentNode);
diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp
index 3d50488a2..1fd2c49a4 100644
--- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp
+++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp
@@ -39,16 +39,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
-/// \file AMFImporter_Geometry.cpp
-/// \brief Parsing data from geometry nodes.
-/// \date 2016
-/// \author smal.root@gmail.com
-
#ifndef ASSIMP_BUILD_NO_AMF_IMPORTER
#include "AMFImporter.hpp"
-#include "AMFImporter_Macro.hpp"
-
#include
namespace Assimp {
@@ -103,18 +96,18 @@ void AMFImporter::ParseNode_Vertices(XmlNode &node) {
// create new mesh object.
ne = new AMFVertices(mNodeElement_Cur);
// Check for child nodes
- pugi::xml_node vertexNode = node.child("vertex");
- if (!vertexNode.empty()) {
- ParseHelper_Node_Enter(ne);
-
- ParseNode_Vertex(vertexNode);
-
- ParseHelper_Node_Exit();
-
- } else {
+ if (node.empty()) {
mNodeElement_Cur->Child.push_back(ne); // Add element to child list of current element
- } // if(!mReader->isEmptyElement()) else
-
+ return;
+ }
+ ParseHelper_Node_Enter(ne);
+ for (XmlNode ¤tNode : node.children()) {
+ const std::string ¤tName = currentNode.name();
+ if (currentName == "vertex") {
+ ParseNode_Vertex(currentNode);
+ }
+ }
+ ParseHelper_Node_Exit();
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
}
@@ -166,27 +159,25 @@ void AMFImporter::ParseNode_Vertex(XmlNode &node) {
// X, Y, or Z coordinate, respectively, of a vertex position in space.
void AMFImporter::ParseNode_Coordinates(XmlNode &node) {
AMFNodeElementBase *ne = nullptr;
-
- // create new color object.
- ne = new AMFCoordinates(mNodeElement_Cur);
-
- AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
if (!node.empty()) {
+ ne = new AMFCoordinates(mNodeElement_Cur);
ParseHelper_Node_Enter(ne);
for (XmlNode ¤tNode : node.children()) {
- const std::string ¤tName = currentNode.name();
- if (currentName == "X") {
+ // create new color object.
+ AMFCoordinates &als = *((AMFCoordinates *)ne); // alias for convenience
+ const std::string ¤tName = ai_tolower(currentNode.name());
+ if (currentName == "x") {
XmlParser::getValueAsFloat(currentNode, als.Coordinate.x);
- } else if (currentName == "Y") {
+ } else if (currentName == "y") {
XmlParser::getValueAsFloat(currentNode, als.Coordinate.y);
- } else if (currentName == "Z") {
+ } else if (currentName == "z") {
XmlParser::getValueAsFloat(currentNode, als.Coordinate.z);
}
}
-
ParseHelper_Node_Exit();
+
} else {
- mNodeElement_Cur->Child.push_back(ne);
+ mNodeElement_Cur->Child.push_back(new AMFCoordinates(mNodeElement_Cur));
}
mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
@@ -216,7 +207,7 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) {
bool col_read = false;
if (!node.empty()) {
ParseHelper_Node_Enter(ne);
- for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+ for (auto ¤tNode : node.children()) {
const std::string currentName = currentNode.name();
if (currentName == "color") {
if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for .");
@@ -258,7 +249,8 @@ void AMFImporter::ParseNode_Triangle(XmlNode &node) {
bool col_read = false;
if (!node.empty()) {
ParseHelper_Node_Enter(ne);
- for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) {
+ std::string v;
+ for (auto ¤tNode : node.children()) {
const std::string currentName = currentNode.name();
if (currentName == "color") {
if (col_read) Throw_MoreThanOnceDefined(currentName, "color", "Only one color can be defined for .");
@@ -269,11 +261,14 @@ void AMFImporter::ParseNode_Triangle(XmlNode &node) {
} else if (currentName == "map") {
ParseNode_TexMap(currentNode, true);
} else if (currentName == "v1") {
- als.V[0] = std::atoi(currentNode.value());
+ XmlParser::getValueAsString(currentNode, v);
+ als.V[0] = std::atoi(v.c_str());
} else if (currentName == "v2") {
- als.V[1] = std::atoi(currentNode.value());
+ XmlParser::getValueAsString(currentNode, v);
+ als.V[1] = std::atoi(v.c_str());
} else if (currentName == "v3") {
- als.V[2] = std::atoi(currentNode.value());
+ XmlParser::getValueAsString(currentNode, v);
+ als.V[2] = std::atoi(v.c_str());
}
}
ParseHelper_Node_Exit();
diff --git a/code/AssetLib/AMF/AMFImporter_Macro.hpp b/code/AssetLib/AMF/AMFImporter_Macro.hpp
deleted file mode 100644
index 43dcf76ba..000000000
--- a/code/AssetLib/AMF/AMFImporter_Macro.hpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
----------------------------------------------------------------------------
-Open Asset Import Library (assimp)
----------------------------------------------------------------------------
-
-Copyright (c) 2006-2021, 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_Macro.hpp
-/// \brief Useful macrodefines.
-/// \date 2016
-/// \author smal.root@gmail.com
-
-#pragma once
-#ifndef AMFIMPORTER_MACRO_HPP_INCLUDED
-#define AMFIMPORTER_MACRO_HPP_INCLUDED
-
-/// \def MACRO_ATTRREAD_LOOPBEG
-/// Begin of loop that read attributes values.
-#define MACRO_ATTRREAD_LOOPBEG \
- for(int idx = 0, idx_end = mReader->getAttributeCount(); idx < idx_end; idx++) \
- { \
- std::string an(mReader->getAttributeName(idx));
-
-/// \def MACRO_ATTRREAD_LOOPEND
-/// End of loop that read attributes values.
-#define MACRO_ATTRREAD_LOOPEND \
- Throw_IncorrectAttr(an); \
- }
-
-/// \def MACRO_ATTRREAD_LOOPEND_WSKIP
-/// End of loop that read attributes values. Difference from \ref MACRO_ATTRREAD_LOOPEND in that: current macro skip unknown attributes, but
-/// \ref MACRO_ATTRREAD_LOOPEND throw an exception.
-#define MACRO_ATTRREAD_LOOPEND_WSKIP \
- continue; \
- }
-
-/// \def MACRO_ATTRREAD_CHECK_REF
-/// Check current attribute name and if it equal to requested then read value. Result write to output variable by reference. If result was read then
-/// "continue" will called.
-/// \param [in] pAttrName - attribute name.
-/// \param [out] pVarName - output variable name.
-/// \param [in] pFunction - function which read attribute value and write it to pVarName.
-#define MACRO_ATTRREAD_CHECK_REF(pAttrName, pVarName, pFunction) \
- if(an == pAttrName) \
- { \
- pFunction(idx, pVarName); \
- continue; \
- }
-
-/// \def MACRO_ATTRREAD_CHECK_RET
-/// Check current attribute name and if it equal to requested then read value. Result write to output variable using return value of \ref pFunction.
-/// If result was read then "continue" will called.
-/// \param [in] pAttrName - attribute name.
-/// \param [out] pVarName - output variable name.
-/// \param [in] pFunction - function which read attribute value and write it to pVarName.
-#define MACRO_ATTRREAD_CHECK_RET(pAttrName, pVarName, pFunction) \
- if(an == pAttrName) \
- { \
- pVarName = pFunction(idx); \
- continue; \
- }
-
-/// \def MACRO_NODECHECK_LOOPBEGIN(pNodeName)
-/// Begin of loop of parsing child nodes. Do not add ';' at end.
-/// \param [in] pNodeName - current node name.
-#define MACRO_NODECHECK_LOOPBEGIN(pNodeName) \
- do { \
- bool close_found = false; \
- \
- while(mReader->read()) \
- { \
- if(mReader->getNodeType() == irr::io::EXN_ELEMENT) \
- {
-
-/// \def MACRO_NODECHECK_LOOPEND(pNodeName)
-/// End of loop of parsing child nodes.
-/// \param [in] pNodeName - current node name.
-#define MACRO_NODECHECK_LOOPEND(pNodeName) \
- XML_CheckNode_SkipUnsupported(pNodeName); \
- }/* if(mReader->getNodeType() == irr::io::EXN_ELEMENT) */ \
- else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) \
- { \
- if(XML_CheckNode_NameEqual(pNodeName)) \
- { \
- close_found = true; \
- \
- break; \
- } \
- }/* else if(mReader->getNodeType() == irr::io::EXN_ELEMENT_END) */ \
- }/* while(mReader->read()) */ \
- \
- if(!close_found) Throw_CloseNotFound(pNodeName); \
- \
- } while(false)
-
-/// \def MACRO_NODECHECK_READCOMP_F
-/// Check current node name and if it equal to requested then read value. Result write to output variable of type "float".
-/// If result was read then "continue" will called. Also check if node data already read then raise exception.
-/// \param [in] pNodeName - node name.
-/// \param [in, out] pReadFlag - read flag.
-/// \param [out] pVarName - output variable name.
-#define MACRO_NODECHECK_READCOMP_F(pNodeName, pReadFlag, pVarName) \
- if(XML_CheckNode_NameEqual(pNodeName)) \
- { \
- /* Check if field already read before. */ \
- if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
- /* Read color component and assign it to object. */ \
- pVarName = XML_ReadNode_GetVal_AsFloat(); \
- pReadFlag = true; \
- continue; \
- }
-
-/// \def MACRO_NODECHECK_READCOMP_U32
-/// Check current node name and if it equal to requested then read value. Result write to output variable of type "uint32_t".
-/// If result was read then "continue" will called. Also check if node data already read then raise exception.
-/// \param [in] pNodeName - node name.
-/// \param [in, out] pReadFlag - read flag.
-/// \param [out] pVarName - output variable name.
-#define MACRO_NODECHECK_READCOMP_U32(pNodeName, pReadFlag, pVarName) \
- if(XML_CheckNode_NameEqual(pNodeName)) \
- { \
- /* Check if field already read before. */ \
- if(pReadFlag) Throw_MoreThanOnceDefined(pNodeName, "Only one component can be defined."); \
- /* Read color component and assign it to object. */ \
- pVarName = XML_ReadNode_GetVal_AsU32(); \
- pReadFlag = true; \
- continue; \
- }
-
-#endif // AMFIMPORTER_MACRO_HPP_INCLUDED
diff --git a/code/AssetLib/AMF/AMFImporter_Material.cpp b/code/AssetLib/AMF/AMFImporter_Material.cpp
index 1fcc7eed9..6d93a43b6 100644
--- a/code/AssetLib/AMF/AMFImporter_Material.cpp
+++ b/code/AssetLib/AMF/AMFImporter_Material.cpp
@@ -65,48 +65,45 @@ namespace Assimp {
// Red, Greed, Blue and Alpha (transparency) component of a color in sRGB space, values ranging from 0 to 1. The
// values can be specified as constants, or as a formula depending on the coordinates.
void AMFImporter::ParseNode_Color(XmlNode &node) {
- std::string profile = node.attribute("profile").as_string();
-
- // create new color object.
- AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur);
- AMFColor& als = *((AMFColor*)ne);// alias for convenience
+ if (node.empty()) {
+ return;
+ }
- als.Profile = profile;
- if (!node.empty()) {
- ParseHelper_Node_Enter(ne);
- bool read_flag[4] = { false, false, false, false };
- for (pugi::xml_node &child : node.children()) {
- std::string name = child.name();
- if ( name == "r") {
- read_flag[0] = true;
- XmlParser::getValueAsFloat(child, als.Color.r);
- } else if (name == "g") {
- read_flag[1] = true;
- XmlParser::getValueAsFloat(child, als.Color.g);
- } else if (name == "b") {
- read_flag[2] = true;
- XmlParser::getValueAsFloat(child, als.Color.b);
- } else if (name == "a") {
- read_flag[3] = true;
- XmlParser::getValueAsFloat(child, als.Color.a);
- }
- ParseHelper_Node_Exit();
+ const std::string &profile = node.attribute("profile").as_string();
+ bool read_flag[4] = { false, false, false, false };
+ AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur);
+ AMFColor &als = *((AMFColor *)ne); // alias for convenience
+ ParseHelper_Node_Enter(ne);
+ for (pugi::xml_node &child : node.children()) {
+ // create new color object.
+ als.Profile = profile;
+
+ const std::string &name = child.name();
+ if ( name == "r") {
+ read_flag[0] = true;
+ XmlParser::getValueAsFloat(child, als.Color.r);
+ } else if (name == "g") {
+ read_flag[1] = true;
+ XmlParser::getValueAsFloat(child, als.Color.g);
+ } else if (name == "b") {
+ read_flag[2] = true;
+ XmlParser::getValueAsFloat(child, als.Color.b);
+ } else if (name == "a") {
+ read_flag[3] = true;
+ XmlParser::getValueAsFloat(child, als.Color.a);
}
- // check that all components was defined
- if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
- throw DeadlyImportError("Not all color components are defined.");
- }
-
- // check if is absent. Then manually add "a == 1".
- if (!read_flag[3]) {
- als.Color.a = 1;
- }
- } else {
- mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element
+ // check if is absent. Then manually add "a == 1".
+ if (!read_flag[3]) {
+ als.Color.a = 1;
+ }
+ }
+ als.Composed = false;
+ mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph.
+ ParseHelper_Node_Exit();
+ // check that all components was defined
+ if (!(read_flag[0] && read_flag[1] && read_flag[2])) {
+ throw DeadlyImportError("Not all color components are defined.");
}
-
- als.Composed = false;
- mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph.
}
// .
void AMFImporter::ParseNode_Texture(XmlNode &node) {
- std::string id = node.attribute("id").as_string();
- uint32_t width = node.attribute("width").as_uint();
- uint32_t height = node.attribute("height").as_uint();
- uint32_t depth = node.attribute("depth").as_uint();
- std::string type = node.attribute("type").as_string();
+ const std::string id = node.attribute("id").as_string();
+ const uint32_t width = node.attribute("width").as_uint();
+ const uint32_t height = node.attribute("height").as_uint();
+ uint32_t depth = node.attribute("depth").as_uint();
+ const std::string type = node.attribute("type").as_string();
bool tiled = node.attribute("tiled").as_bool();
if (node.empty()) {
@@ -174,22 +171,20 @@ void AMFImporter::ParseNode_Texture(XmlNode &node) {
AMFTexture& als = *((AMFTexture*)ne);// alias for convenience
- std::string enc64_data = node.value();
- // Check for child nodes
+ std::string enc64_data;
+ XmlParser::getValueAsString(node, enc64_data);
+ // Check for child nodes
// check that all components was defined
if (id.empty()) {
throw DeadlyImportError("ID for texture must be defined.");
}
if (width < 1) {
- throw DeadlyImportError("INvalid width for texture.");
+ throw DeadlyImportError("Invalid width for texture.");
}
if (height < 1) {
throw DeadlyImportError("Invalid height for texture.");
}
- if (depth < 1) {
- throw DeadlyImportError("Invalid depth for texture.");
- }
if (type != "grayscale") {
throw DeadlyImportError("Invalid type for texture.");
}
@@ -203,7 +198,9 @@ void AMFImporter::ParseNode_Texture(XmlNode &node) {
als.Depth = depth;
als.Tiled = tiled;
ParseHelper_Decode_Base64(enc64_data, als.Data);
-
+ if (depth == 0) {
+ depth = (uint32_t)(als.Data.size() / (width * height));
+ }
// check data size
if ((width * height * depth) != als.Data.size()) {
throw DeadlyImportError("Texture has incorrect data size.");
@@ -233,20 +230,18 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
AMFTexMap &als = *((AMFTexMap *)ne); //
std::string rtexid, gtexid, btexid, atexid;
if (!node.empty()) {
- ParseHelper_Node_Enter(ne);
- for (XmlNode ¤tNode : node.children()) {
- const std::string ¤tName = currentNode.name();
- if (currentName == "rtexid") {
- XmlParser::getValueAsString(node, rtexid);
- } else if (currentName == "gtexid") {
- XmlParser::getValueAsString(node, gtexid);
- } else if (currentName == "btexid") {
- XmlParser::getValueAsString(node, btexid);
- } else if (currentName == "atexid") {
- XmlParser::getValueAsString(node, atexid);
+ for (pugi::xml_attribute &attr : node.attributes()) {
+ const std::string ¤tAttr = attr.name();
+ if (currentAttr == "rtexid") {
+ rtexid = attr.as_string();
+ } else if (currentAttr == "gtexid") {
+ gtexid = attr.as_string();
+ } else if (currentAttr == "btexid") {
+ btexid = attr.as_string();
+ } else if (currentAttr == "atexid") {
+ atexid = attr.as_string();
}
}
- ParseHelper_Node_Exit();
}
// create new texture coordinates object, alias for convenience
@@ -256,7 +251,6 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
}
// Check for children nodes
- //XML_CheckNode_MustHaveChildren();
if (node.children().begin() == node.children().end()) {
throw DeadlyImportError("Invalid children definition.");
}
@@ -264,28 +258,31 @@ void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) {
bool read_flag[6] = { false, false, false, false, false, false };
if (!pUseOldName) {
- for (pugi::xml_attribute &attr : node.attributes()) {
- const std::string name = attr.name();
+ ParseHelper_Node_Enter(ne);
+ for ( XmlNode ¤tNode : node.children()) {
+ const std::string &name = currentNode.name();
if (name == "utex1") {
read_flag[0] = true;
- als.TextureCoordinate[0].x = attr.as_float();
+ XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].x);
} else if (name == "utex2") {
read_flag[1] = true;
- als.TextureCoordinate[1].x = attr.as_float();
+ XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].x);
} else if (name == "utex3") {
read_flag[2] = true;
- als.TextureCoordinate[2].x = attr.as_float();
+ XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].x);
} else if (name == "vtex1") {
read_flag[3] = true;
- als.TextureCoordinate[0].y = attr.as_float();
+ XmlParser::getValueAsFloat(node, als.TextureCoordinate[0].y);
} else if (name == "vtex2") {
read_flag[4] = true;
- als.TextureCoordinate[1].y = attr.as_float();
+ XmlParser::getValueAsFloat(node, als.TextureCoordinate[1].y);
} else if (name == "vtex3") {
read_flag[5] = true;
- als.TextureCoordinate[0].y = attr.as_float();
+ XmlParser::getValueAsFloat(node, als.TextureCoordinate[2].y);
}
}
+ ParseHelper_Node_Exit();
+
} else {
for (pugi::xml_attribute &attr : node.attributes()) {
const std::string name = attr.name();
diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp
index 9bbfc3249..036b647e8 100644
--- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp
+++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp
@@ -62,12 +62,14 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
// Check if stored data are supported.
if (!Composition.empty()) {
throw DeadlyImportError("IME. GetColor for composition");
- } else if (Color->Composed) {
- throw DeadlyImportError("IME. GetColor, composed color");
- } else {
- tcol = Color->Color;
}
+ if (Color->Composed) {
+ throw DeadlyImportError("IME. GetColor, composed color");
+ }
+
+ tcol = Color->Color;
+
// Check if default color must be used
if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) {
tcol.r = 0.5f;
@@ -79,13 +81,13 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /*
return tcol;
}
-void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElement, std::vector &pVertexCoordinateArray,
+void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeElement, std::vector &vertexCoordinateArray,
std::vector &pVertexColorArray) const {
AMFVertices *vn = nullptr;
size_t col_idx;
// All data stored in "vertices", search for it.
- for (AMFNodeElementBase *ne_child : pNodeElement.Child) {
+ for (AMFNodeElementBase *ne_child : nodeElement.Child) {
if (ne_child->Type == AMFNodeElementBase::ENET_Vertices) {
vn = (AMFVertices*)ne_child;
}
@@ -97,7 +99,7 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElem
}
// all coordinates stored as child and we need to reserve space for future push_back's.
- pVertexCoordinateArray.reserve(vn->Child.size());
+ vertexCoordinateArray.reserve(vn->Child.size());
// colors count equal vertices count.
pVertexColorArray.resize(vn->Child.size());
@@ -112,7 +114,7 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &pNodeElem
for (AMFNodeElementBase *vtx : vn_child->Child) {
if (vtx->Type == AMFNodeElementBase::ENET_Coordinates) {
- pVertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate);
+ vertexCoordinateArray.push_back(((AMFCoordinates *)vtx)->Coordinate);
continue;
}
diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp
index ae93cba02..322161411 100644
--- a/code/AssetLib/FBX/FBXExporter.cpp
+++ b/code/AssetLib/FBX/FBXExporter.cpp
@@ -812,6 +812,18 @@ void FBXExporter::WriteDefinitions ()
// Geometry / FbxMesh
// <~~ aiMesh
count = mScene->mNumMeshes;
+
+ // Blendshapes are considered Geometry
+ int32_t bsDeformerCount=0;
+ for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+ aiMesh* m = mScene->mMeshes[mi];
+ if (m->mNumAnimMeshes > 0) {
+ count+=m->mNumAnimMeshes;
+ bsDeformerCount+=m->mNumAnimMeshes; // One deformer per blendshape
+ bsDeformerCount++; // Plus one master blendshape deformer
+ }
+ }
+
if (count) {
n = FBX::Node("ObjectType", "Geometry");
n.AddChild("Count", count);
@@ -978,7 +990,7 @@ void FBXExporter::WriteDefinitions ()
}
// Deformer
- count = int32_t(count_deformers(mScene));
+ count = int32_t(count_deformers(mScene))+bsDeformerCount;
if (count) {
n = FBX::Node("ObjectType", "Deformer");
n.AddChild("Count", count);
@@ -1363,6 +1375,7 @@ void FBXExporter::WriteObjects ()
n.End(outstream, binary, indent, true);
}
+
// aiMaterial
material_uids.clear();
for (size_t i = 0; i < mScene->mNumMaterials; ++i) {
@@ -1697,6 +1710,100 @@ void FBXExporter::WriteObjects ()
}
}
+ // Blendshapes, if any
+ for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) {
+ const aiMesh* m = mScene->mMeshes[mi];
+ if (m->mNumAnimMeshes == 0) {
+ continue;
+ }
+ // make a deformer for this mesh
+ int64_t deformer_uid = generate_uid();
+ FBX::Node dnode("Deformer");
+ dnode.AddProperties(deformer_uid, m->mName.data + FBX::SEPARATOR + "Blendshapes", "BlendShape");
+ dnode.AddChild("Version", int32_t(101));
+ dnode.Dump(outstream, binary, indent);
+ // connect it
+ connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
+ std::vector vertex_indices = vVertexIndice[mi];
+
+ for (unsigned int am = 0; am < m->mNumAnimMeshes; ++am) {
+ aiAnimMesh *pAnimMesh = m->mAnimMeshes[am];
+ std::string blendshape_name = pAnimMesh->mName.data;
+
+ // start the node record
+ FBX::Node bsnode("Geometry");
+ int64_t blendshape_uid = generate_uid();
+ mesh_uids.push_back(blendshape_uid);
+ bsnode.AddProperty(blendshape_uid);
+ bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape");
+ bsnode.AddProperty("Shape");
+ bsnode.AddChild("Version", int32_t(100));
+ bsnode.Begin(outstream, binary, indent);
+ bsnode.DumpProperties(outstream, binary, indent);
+ bsnode.EndProperties(outstream, binary, indent);
+ bsnode.BeginChildren(outstream, binary, indent);
+ indent++;
+ if (pAnimMesh->HasPositions()) {
+ std::vectorshape_indices;
+ std::vectorpPositionDiff;
+ std::vectorpNormalDiff;
+
+ for (unsigned int vt = 0; vt < vertex_indices.size(); ++vt) {
+ aiVector3D pDiff = (pAnimMesh->mVertices[vertex_indices[vt]] - m->mVertices[vertex_indices[vt]]);
+ if(pDiff.Length()>1e-8){
+ shape_indices.push_back(vertex_indices[vt]);
+ pPositionDiff.push_back(pDiff[0]);
+ pPositionDiff.push_back(pDiff[1]);
+ pPositionDiff.push_back(pDiff[2]);
+
+ if (pAnimMesh->HasNormals()) {
+ aiVector3D nDiff = (pAnimMesh->mNormals[vertex_indices[vt]] - m->mNormals[vertex_indices[vt]]);
+ pNormalDiff.push_back(nDiff[0]);
+ pNormalDiff.push_back(nDiff[1]);
+ pNormalDiff.push_back(nDiff[2]);
+ }
+ }
+ }
+
+ FBX::Node::WritePropertyNode(
+ "Indexes", shape_indices, outstream, binary, indent
+ );
+
+ FBX::Node::WritePropertyNode(
+ "Vertices", pPositionDiff, outstream, binary, indent
+ );
+
+ if (pNormalDiff.size()>0) {
+ FBX::Node::WritePropertyNode(
+ "Normals", pNormalDiff, outstream, binary, indent
+ );
+ }
+ }
+ indent--;
+ bsnode.End(outstream, binary, indent, true);
+
+ // Add blendshape Channel Deformer
+ FBX::Node sdnode("Deformer");
+ const int64_t blendchannel_uid = generate_uid();
+ sdnode.AddProperties(
+ blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel"
+ );
+ sdnode.AddChild("Version", int32_t(100));
+ sdnode.AddChild("DeformPercent", int32_t(100));
+ FBX::Node p("Properties70");
+ p.AddP70numberA("DeformPercent", 100.);
+ sdnode.AddChild(p);
+ // TODO: Normally just one weight per channel, adding stub for later development
+ std::vectorfFullWeights;
+ fFullWeights.push_back(0.);
+ sdnode.AddChild("FullWeights", fFullWeights);
+ sdnode.Dump(outstream, binary, indent);
+
+ connections.emplace_back("C", "OO", blendchannel_uid, deformer_uid);
+ connections.emplace_back("C", "OO", blendshape_uid, blendchannel_uid);
+ }
+ }
+
// bones.
//
// output structure:
diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp
index 9151e9d5d..56401f5c9 100644
--- a/code/AssetLib/HMP/HMPLoader.cpp
+++ b/code/AssetLib/HMP/HMPLoader.cpp
@@ -157,7 +157,10 @@ void HMPImporter::InternReadFile(const std::string &pFile,
szBuffer[2] = ((char *)&iMagic)[2];
szBuffer[3] = ((char *)&iMagic)[3];
szBuffer[4] = '\0';
-
+
+ delete[] mBuffer;
+ mBuffer = nullptr;
+
// We're definitely unable to load this file
throw DeadlyImportError("Unknown HMP subformat ", pFile,
". Magic word (", szBuffer, ") is not known");
diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp
index 35a9bf4c5..eced651f6 100644
--- a/code/AssetLib/Ogre/OgreMaterial.cpp
+++ b/code/AssetLib/Ogre/OgreMaterial.cpp
@@ -419,7 +419,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr
size_t posUnderscore = textureRef.find_last_of("_");
if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) {
- string identifier = ai_str_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore));
+ string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore));
ASSIMP_LOG_VERBOSE_DEBUG_F("Detecting texture type from filename postfix '", identifier, "'");
if (identifier == "_n" || identifier == "_nrm" || identifier == "_nrml" || identifier == "_normal" || identifier == "_normals" || identifier == "_normalmap") {
@@ -440,7 +440,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr
// Detect from texture unit name. This cannot be too broad as
// authors might give names like "LightSaber" or "NormalNinja".
else {
- string unitNameLower = ai_str_tolower(textureUnitName);
+ string unitNameLower = ai_tolower(textureUnitName);
if (unitNameLower.find("normalmap") != string::npos) {
textureType = aiTextureType_NORMALS;
} else if (unitNameLower.find("specularmap") != string::npos) {
diff --git a/code/AssetLib/Ogre/OgreParsingUtils.h b/code/AssetLib/Ogre/OgreParsingUtils.h
index 70797a237..0925c0305 100644
--- a/code/AssetLib/Ogre/OgreParsingUtils.h
+++ b/code/AssetLib/Ogre/OgreParsingUtils.h
@@ -64,7 +64,7 @@ static inline bool EndsWith(const std::string &s, const std::string &suffix, boo
}
if (!caseSensitive) {
- return EndsWith(ai_str_tolower(s), ai_str_tolower(suffix), true);
+ return EndsWith(ai_tolower(s), ai_tolower(suffix), true);
}
size_t len = suffix.length();
diff --git a/code/AssetLib/Ogre/OgreXmlSerializer.cpp b/code/AssetLib/Ogre/OgreXmlSerializer.cpp
index 99c05a522..938ae48f3 100644
--- a/code/AssetLib/Ogre/OgreXmlSerializer.cpp
+++ b/code/AssetLib/Ogre/OgreXmlSerializer.cpp
@@ -120,7 +120,7 @@ std::string OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, cons
template <>
bool OgreXmlSerializer::ReadAttribute(XmlNode &xmlNode, const char *name) const {
- std::string value = ai_str_tolower(ReadAttribute(xmlNode, name));
+ std::string value = ai_tolower(ReadAttribute(xmlNode, name));
if (ASSIMP_stricmp(value, "true") == 0) {
return true;
} else if (ASSIMP_stricmp(value, "false") == 0) {
@@ -545,7 +545,7 @@ void OgreXmlSerializer::ReadSkeleton(XmlNode &node, Skeleton *skeleton) {
// Optional blend mode from root node
if (XmlParser::hasAttribute(node, "blendmode")) {
- skeleton->blendMode = (ai_str_tolower(ReadAttribute(node, "blendmode")) == "cumulative" ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE);
+ skeleton->blendMode = (ai_tolower(ReadAttribute(node, "blendmode")) == "cumulative" ? Skeleton::ANIMBLEND_CUMULATIVE : Skeleton::ANIMBLEND_AVERAGE);
}
for (XmlNode ¤tNode : node.children()) {
diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp
index 113b8dd6a..cbf8c07c2 100644
--- a/code/AssetLib/STEPParser/STEPFileReader.cpp
+++ b/code/AssetLib/STEPParser/STEPFileReader.cpp
@@ -278,7 +278,7 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme,
--ne;
} while (IsSpace(s.at(ne)));
std::string type = s.substr(ns, ne - ns + 1);
- type = ai_str_tolower(type);
+ type = ai_tolower(type);
const char* sz = scheme.GetStaticStringForToken(type);
if(sz) {
const std::string::size_type szLen = n2-n1+1;
diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h
index 0e4ba6eda..85eff4ac8 100644
--- a/code/AssetLib/glTF2/glTF2Asset.h
+++ b/code/AssetLib/glTF2/glTF2Asset.h
@@ -813,6 +813,11 @@ struct Mesh : public Object {
AccessorList position, normal, tangent;
};
std::vector targets;
+
+ // extension: FB_ngon_encoding
+ bool ngonEncoded;
+
+ Primitive(): ngonEncoded(false) {}
};
std::vector primitives;
@@ -1108,6 +1113,7 @@ public:
bool KHR_materials_clearcoat;
bool KHR_materials_transmission;
bool KHR_draco_mesh_compression;
+ bool FB_ngon_encoding;
} extensionsUsed;
//! Keeps info about the required extensions
diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl
index 166eada0f..01a28d4b7 100644
--- a/code/AssetLib/glTF2/glTF2AssetWriter.inl
+++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl
@@ -507,6 +507,20 @@ namespace glTF2 {
Mesh::Primitive& p = m.primitives[i];
Value prim;
prim.SetObject();
+
+ // Extensions
+ if (p.ngonEncoded)
+ {
+ Value exts;
+ exts.SetObject();
+
+ Value FB_ngon_encoding;
+ FB_ngon_encoding.SetObject();
+
+ exts.AddMember(StringRef("FB_ngon_encoding"), FB_ngon_encoding, w.mAl);
+ prim.AddMember("extensions", exts, w.mAl);
+ }
+
{
prim.AddMember("mode", Value(int(p.mode)).Move(), w.mAl);
@@ -874,6 +888,10 @@ namespace glTF2 {
if (this->mAsset.extensionsUsed.KHR_materials_transmission) {
exts.PushBack(StringRef("KHR_materials_transmission"), mAl);
}
+
+ if (this->mAsset.extensionsUsed.FB_ngon_encoding) {
+ exts.PushBack(StringRef("FB_ngon_encoding"), mAl);
+ }
}
if (!exts.Empty())
diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp
index aa89e96da..51aef013d 100644
--- a/code/AssetLib/glTF2/glTF2Exporter.cpp
+++ b/code/AssetLib/glTF2/glTF2Exporter.cpp
@@ -97,6 +97,9 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai
mAsset.reset( new Asset( pIOSystem ) );
+ // Always on as our triangulation process is aware of this type of encoding
+ mAsset->extensionsUsed.FB_ngon_encoding = true;
+
if (isBinary) {
mAsset->SetAsBinary();
}
@@ -955,6 +958,7 @@ void glTF2Exporter::ExportMeshes()
m->name = name;
p.material = mAsset->materials.Get(aim->mMaterialIndex);
+ p.ngonEncoded = (aim->mPrimitiveTypes & aiPrimitiveType_NGONEncodingFlag) != 0;
/******************* Vertices ********************/
Ref v = ExportData(*mAsset, meshId, b, aim->mNumVertices, aim->mVertices, AttribType::VEC3, AttribType::VEC3, ComponentType_FLOAT, BufferViewTarget_ARRAY_BUFFER);
diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp
index 8442c4958..dca6fcb2d 100644
--- a/code/AssetLib/glTF2/glTF2Importer.cpp
+++ b/code/AssetLib/glTF2/glTF2Importer.cpp
@@ -527,8 +527,8 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
std::fill(aim->mAnimMeshes, aim->mAnimMeshes + aim->mNumAnimMeshes, nullptr);
for (size_t i = 0; i < targets.size(); i++) {
bool needPositions = targets[i].position.size() > 0;
- bool needNormals = targets[i].normal.size() > 0;
- bool needTangents = targets[i].tangent.size() > 0;
+ bool needNormals = (targets[i].normal.size() > 0) && aim->HasNormals();
+ bool needTangents = (targets[i].tangent.size() > 0) && aim->HasTangentsAndBitangents();
// GLTF morph does not support colors and texCoords
aim->mAnimMeshes[i] = aiCreateAnimMesh(aim,
needPositions, needNormals, needTangents, false, false);
@@ -536,35 +536,47 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) {
Mesh::Primitive::Target &target = targets[i];
if (needPositions) {
- aiVector3D *positionDiff = nullptr;
- target.position[0]->ExtractData(positionDiff);
- for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
- aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
+ if (target.position[0]->count != aim->mNumVertices) {
+ ASSIMP_LOG_WARN_F("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
+ } else {
+ aiVector3D *positionDiff = nullptr;
+ target.position[0]->ExtractData(positionDiff);
+ for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
+ aiAnimMesh.mVertices[vertexId] += positionDiff[vertexId];
+ }
+ delete[] positionDiff;
}
- delete[] positionDiff;
}
if (needNormals) {
- aiVector3D *normalDiff = nullptr;
- target.normal[0]->ExtractData(normalDiff);
- for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
- aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
+ if (target.normal[0]->count != aim->mNumVertices) {
+ ASSIMP_LOG_WARN_F("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
+ } else {
+ aiVector3D *normalDiff = nullptr;
+ target.normal[0]->ExtractData(normalDiff);
+ for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; vertexId++) {
+ aiAnimMesh.mNormals[vertexId] += normalDiff[vertexId];
+ }
+ delete[] normalDiff;
}
- delete[] normalDiff;
}
if (needTangents) {
- Tangent *tangent = nullptr;
- attr.tangent[0]->ExtractData(tangent);
+ if (target.tangent[0]->count != aim->mNumVertices) {
+ ASSIMP_LOG_WARN_F("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count");
+ } else {
+ Tangent *tangent = nullptr;
+ attr.tangent[0]->ExtractData(tangent);
- aiVector3D *tangentDiff = nullptr;
- target.tangent[0]->ExtractData(tangentDiff);
+ aiVector3D *tangentDiff = nullptr;
+ target.tangent[0]->ExtractData(tangentDiff);
- for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
- tangent[vertexId].xyz += tangentDiff[vertexId];
- aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz;
- aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w;
+ for (unsigned int vertexId = 0; vertexId < aim->mNumVertices; ++vertexId) {
+ tangent[vertexId].xyz += tangentDiff[vertexId];
+ aiAnimMesh.mTangents[vertexId] = tangent[vertexId].xyz;
+ aiAnimMesh.mBitangents[vertexId] = (aiAnimMesh.mNormals[vertexId] ^ tangent[vertexId].xyz) * tangent[vertexId].w;
+ }
+ delete[] tangent;
+ delete[] tangentDiff;
}
- delete[] tangent;
- delete[] tangentDiff;
}
if (mesh.weights.size() > i) {
aiAnimMesh.mWeight = mesh.weights[i];
diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt
index 815d5dd6a..ebc3e0116 100644
--- a/code/CMakeLists.txt
+++ b/code/CMakeLists.txt
@@ -298,7 +298,6 @@ SET(ASSIMP_EXPORTERS_DISABLED "") # disabled exporters list (used to print)
ADD_ASSIMP_IMPORTER( AMF
AssetLib/AMF/AMFImporter.hpp
- AssetLib/AMF/AMFImporter_Macro.hpp
AssetLib/AMF/AMFImporter_Node.hpp
AssetLib/AMF/AMFImporter.cpp
AssetLib/AMF/AMFImporter_Geometry.cpp
diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp
index cbecbf075..0657216cf 100644
--- a/code/Common/BaseImporter.cpp
+++ b/code/Common/BaseImporter.cpp
@@ -276,7 +276,7 @@ std::string BaseImporter::GetExtension(const std::string &file) {
// thanks to Andy Maloney for the hint
std::string ret = file.substr(pos + 1);
- ret = ai_str_tolower(ret);
+ ret = ai_tolower(ret);
return ret;
}
diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp
index 34953074c..db7fc9e1c 100644
--- a/code/Common/Importer.cpp
+++ b/code/Common/Importer.cpp
@@ -982,7 +982,7 @@ size_t Importer::GetImporterIndex (const char* szExtension) const {
if (ext.empty()) {
return static_cast(-1);
}
- ext = ai_str_tolower(ext);
+ ext = ai_tolower(ext);
std::set str;
for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) {
str.clear();
diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp
index b7aadcb23..dc304ff9c 100644
--- a/code/PostProcessing/EmbedTexturesProcess.cpp
+++ b/code/PostProcessing/EmbedTexturesProcess.cpp
@@ -137,7 +137,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const {
pTexture->pcData = imageContent;
auto extension = path.substr(path.find_last_of('.') + 1u);
- extension = ai_str_tolower(extension);
+ extension = ai_tolower(extension);
if (extension == "jpeg") {
extension = "jpg";
}
diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp
index e971bf85f..0f71320b8 100644
--- a/code/PostProcessing/TriangulateProcess.cpp
+++ b/code/PostProcessing/TriangulateProcess.cpp
@@ -76,6 +76,87 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
+namespace {
+
+ /**
+ * @brief Helper struct used to simplify NGON encoding functions.
+ */
+ struct NGONEncoder {
+ NGONEncoder() : mLastNGONFirstIndex((unsigned int)-1) {}
+
+ /**
+ * @brief Encode the current triangle, and make sure it is recognized as a triangle.
+ *
+ * This method will rotate indices in tri if needed in order to avoid tri to be considered
+ * part of the previous ngon. This method is to be used whenever you want to emit a real triangle,
+ * and make sure it is seen as a triangle.
+ *
+ * @param tri Triangle to encode.
+ */
+ void ngonEncodeTriangle(aiFace * tri) {
+ ai_assert(tri->mNumIndices == 3);
+
+ // Rotate indices in new triangle to avoid ngon encoding false ngons
+ // Otherwise, the new triangle would be considered part of the previous NGON.
+ if (isConsideredSameAsLastNgon(tri)) {
+ std::swap(tri->mIndices[0], tri->mIndices[2]);
+ std::swap(tri->mIndices[1], tri->mIndices[2]);
+ }
+
+ mLastNGONFirstIndex = tri->mIndices[0];
+ }
+
+ /**
+ * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon.
+ *
+ * @param tri1 First quad triangle
+ * @param tri2 Second quad triangle
+ *
+ * @pre Triangles must be properly fanned from the most appropriate vertex.
+ */
+ void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) {
+ ai_assert(tri1->mNumIndices == 3);
+ ai_assert(tri2->mNumIndices == 3);
+ ai_assert(tri1->mIndices[0] == tri2->mIndices[0]);
+
+ // If the selected fanning vertex is the same as the previously
+ // emitted ngon, we use the opposite vertex which also happens to work
+ // for tri-fanning a concave quad.
+ // ref: https://github.com/assimp/assimp/pull/3695#issuecomment-805999760
+ if (isConsideredSameAsLastNgon(tri1)) {
+ // Right-rotate indices for tri1 (index 2 becomes the new fanning vertex)
+ std::swap(tri1->mIndices[0], tri1->mIndices[2]);
+ std::swap(tri1->mIndices[1], tri1->mIndices[2]);
+
+ // Left-rotate indices for tri2 (index 2 becomes the new fanning vertex)
+ std::swap(tri2->mIndices[1], tri2->mIndices[2]);
+ std::swap(tri2->mIndices[0], tri2->mIndices[2]);
+
+ ai_assert(tri1->mIndices[0] == tri2->mIndices[0]);
+ }
+
+ mLastNGONFirstIndex = tri1->mIndices[0];
+ }
+
+ /**
+ * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not.
+ *
+ * @param tri Current triangle.
+ * @return true If used as is, this triangle will be part of last ngon.
+ * @return false If used as is, this triangle is not considered part of the last ngon.
+ */
+ bool isConsideredSameAsLastNgon(const aiFace * tri) const {
+ ai_assert(tri->mNumIndices == 3);
+ return tri->mIndices[0] == mLastNGONFirstIndex;
+ }
+
+ private:
+ unsigned int mLastNGONFirstIndex;
+ };
+
+}
+
+
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
TriangulateProcess::TriangulateProcess()
@@ -175,10 +256,15 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
pMesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE;
pMesh->mPrimitiveTypes &= ~aiPrimitiveType_POLYGON;
+ // The mesh becomes NGON encoded now, during the triangulation process.
+ pMesh->mPrimitiveTypes |= aiPrimitiveType_NGONEncodingFlag;
+
aiFace* out = new aiFace[numOut](), *curOut = out;
std::vector temp_verts3d(max_out+2); /* temporary storage for vertices */
std::vector temp_verts(max_out+2);
+ NGONEncoder ngonEncoder;
+
// Apply vertex colors to represent the face winding?
#ifdef AI_BUILD_TRIANGULATE_COLOR_FACE_WINDING
if (!pMesh->mColors[0])
@@ -220,8 +306,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
aiFace& nface = *curOut++;
nface.mNumIndices = face.mNumIndices;
nface.mIndices = face.mIndices;
-
face.mIndices = nullptr;
+
+ // points and lines don't require ngon encoding (and are not supported either!)
+ if (nface.mNumIndices == 3) ngonEncoder.ngonEncodeTriangle(&nface);
+
continue;
}
// optimized code for quadrilaterals
@@ -274,6 +363,9 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// prevent double deletion of the indices field
face.mIndices = nullptr;
+
+ ngonEncoder.ngonEncodeQuad(&nface, &sface);
+
continue;
}
else
@@ -284,11 +376,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
// modeling suite to make extensive use of highly concave, monster polygons ...
// so we need to apply the full 'ear cutting' algorithm to get it right.
- // RERQUIREMENT: polygon is expected to be simple and *nearly* planar.
+ // REQUIREMENT: polygon is expected to be simple and *nearly* planar.
// We project it onto a plane to get a 2d triangle.
// Collect all vertices of of the polygon.
- for (tmp = 0; tmp < max; ++tmp) {
+ for (tmp = 0; tmp < max; ++tmp) {
temp_verts3d[tmp] = verts[idx[tmp]];
}
@@ -508,6 +600,11 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh)
i[0] = idx[i[0]];
i[1] = idx[i[1]];
i[2] = idx[i[2]];
+
+ // IMPROVEMENT: Polygons are not supported yet by this ngon encoding + triangulation step.
+ // So we encode polygons as regular triangles. No way to reconstruct the original
+ // polygon in this case.
+ ngonEncoder.ngonEncodeTriangle(f);
++f;
}
diff --git a/include/assimp/MathFunctions.h b/include/assimp/MathFunctions.h
index 1031eab9d..2088c394c 100644
--- a/include/assimp/MathFunctions.h
+++ b/include/assimp/MathFunctions.h
@@ -87,7 +87,7 @@ inline IntegerType lcm( IntegerType a, IntegerType b ) {
}
return a / t * b;
}
-/// @brief Will return the smallest epsilon-value for the requested type.
+/// @brief Will return the smallest epsilon-value for the requested type.
/// @return The numercical limit epsilon depending on its type.
template
inline T getEpsilon() {
@@ -97,7 +97,7 @@ inline T getEpsilon() {
/// @brief Will return the constant PI for the requested type.
/// @return Pi
template
-inline T PI() {
+inline T aiPi() {
return static_cast(3.14159265358979323846);
}
diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h
index b612a84cc..4afd717cf 100644
--- a/include/assimp/StringUtils.h
+++ b/include/assimp/StringUtils.h
@@ -225,7 +225,7 @@ AI_FORCE_INLINE char_t ai_tolower(char_t in) {
/// @param in The incoming string.
/// @return The string as lowercase.
// ---------------------------------------------------------------------------------
-AI_FORCE_INLINE std::string ai_str_tolower(const std::string &in) {
+AI_FORCE_INLINE std::string ai_tolower(const std::string &in) {
std::string out(in);
ai_trim_left(out);
ai_trim_right(out);
diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h
index 8f17f541d..427dba008 100644
--- a/include/assimp/mesh.h
+++ b/include/assimp/mesh.h
@@ -398,6 +398,24 @@ enum aiPrimitiveType {
*/
aiPrimitiveType_POLYGON = 0x8,
+ /**
+ * A flag to determine whether this triangles only mesh is NGON encoded.
+ *
+ * NGON encoding is a special encoding that tells whether 2 or more consecutive triangles
+ * should be considered as a triangle fan. This is identified by looking at the first vertex index.
+ * 2 consecutive triangles with the same 1st vertex index are part of the same
+ * NGON.
+ *
+ * At the moment, only quads (concave or convex) are supported, meaning that polygons are 'seen' as
+ * triangles, as usual after a triangulation pass.
+ *
+ * To get an NGON encoded mesh, please use the aiProcess_Triangulate post process.
+ *
+ * @see aiProcess_Triangulate
+ * @link https://github.com/KhronosGroup/glTF/pull/1620
+ */
+ aiPrimitiveType_NGONEncodingFlag = 0x10,
+
/** This value is not used. It is just here to force the
* compiler to map this enum to a 32 Bit integer.
*/
diff --git a/port/iOS/build.sh b/port/iOS/build.sh
index f118c45c1..335b450d1 100755
--- a/port/iOS/build.sh
+++ b/port/iOS/build.sh
@@ -22,7 +22,7 @@ BUILD_TYPE=Release
################################################
# Minimum iOS deployment target version
################################################
-MIN_IOS_VERSION="6.0"
+MIN_IOS_VERSION="10.0"
IOS_SDK_TARGET=$MIN_IOS_VERSION
XCODE_ROOT_DIR=$(xcode-select --print-path)
@@ -60,8 +60,8 @@ build_arch()
unset DEVROOT SDKROOT CFLAGS LDFLAGS CPPFLAGS CXXFLAGS CMAKE_CLI_INPUT
- #export CC="$(xcrun -sdk iphoneos -find clang)"
- #export CPP="$CC -E"
+ export CC="$(xcrun -sdk iphoneos -find clang)"
+ export CPP="$CC -E"
export DEVROOT=$XCODE_ROOT_DIR/Platforms/$IOS_SDK_DEVICE.platform/Developer
export SDKROOT=$DEVROOT/SDKs/$IOS_SDK_DEVICE$IOS_SDK_VERSION.sdk
export CFLAGS="-arch $1 -pipe -no-cpp-precomp -stdlib=$CPP_STD_LIB -isysroot $SDKROOT -I$SDKROOT/usr/include/ -miphoneos-version-min=$IOS_SDK_TARGET"
diff --git a/test/unit/AssimpAPITest_aiMatrix4x4.cpp b/test/unit/AssimpAPITest_aiMatrix4x4.cpp
index c47da877d..d2f57b19d 100644
--- a/test/unit/AssimpAPITest_aiMatrix4x4.cpp
+++ b/test/unit/AssimpAPITest_aiMatrix4x4.cpp
@@ -57,7 +57,7 @@ protected:
aiMatrix4x4 get_predetermined_transformation_matrix_for_decomposition() const {
aiMatrix4x4 t, r;
aiMatrix4x4::Translation(aiVector3D(14,-25,-8), t);
- aiMatrix4x4::Rotation(Math::PI() / 4.0f, aiVector3D(1).Normalize(), r);
+ aiMatrix4x4::Rotation(Math::aiPi() / 4.0f, aiVector3D(1).Normalize(), r);
return t * r;
}
diff --git a/test/unit/AssimpAPITest_aiQuaternion.cpp b/test/unit/AssimpAPITest_aiQuaternion.cpp
index 93c93f674..79f89337e 100644
--- a/test/unit/AssimpAPITest_aiQuaternion.cpp
+++ b/test/unit/AssimpAPITest_aiQuaternion.cpp
@@ -59,7 +59,7 @@ TEST_F(AssimpAPITest_aiQuaternion, aiCreateQuaternionFromMatrixTest) {
// to prevent running into division by zero.
aiMatrix3x3 m, r;
aiMatrix3x3::Translation(aiVector2D(14,-25), m);
- aiMatrix3x3::RotationZ(Math::PI() / 4.0f, r);
+ aiMatrix3x3::RotationZ(Math::aiPi() / 4.0f, r);
m = m * r;
result_cpp = aiQuaternion(m);
@@ -127,8 +127,8 @@ TEST_F(AssimpAPITest_aiQuaternion, aiQuaternionInterpolateTest) {
// Use predetermined quaternions to prevent division by zero
// during slerp calculations.
const float INTERPOLATION(0.5f);
- const auto q1 = aiQuaternion(aiVector3D(-1,1,1).Normalize(), Math::PI() / 4.0f);
- const auto q2 = aiQuaternion(aiVector3D(1,2,1).Normalize(), Math::PI() / 2.0f);
+ const auto q1 = aiQuaternion(aiVector3D(-1,1,1).Normalize(), Math::aiPi() / 4.0f);
+ const auto q2 = aiQuaternion(aiVector3D(1,2,1).Normalize(), Math::aiPi() / 2.0f);
aiQuaternion::Interpolate(result_cpp, q1, q2, INTERPOLATION);
aiQuaternionInterpolate(&result_c, &q1, &q2, INTERPOLATION);
EXPECT_EQ(result_cpp, result_c);
diff --git a/test/unit/MathTest.cpp b/test/unit/MathTest.cpp
index 88c95006c..ecc23545c 100644
--- a/test/unit/MathTest.cpp
+++ b/test/unit/MathTest.cpp
@@ -51,6 +51,6 @@ const float AssimpMathTest::Epsilon = Math::getEpsilon();
RandomUniformFloatGenerator AssimpMathTest::RandNonZero(1.0f, 100.0f);
// Initialize with an interval of [-PI,PI] inclusively.
-RandomUniformFloatGenerator AssimpMathTest::RandPI(-Math::PI(), Math::PI());
+RandomUniformFloatGenerator AssimpMathTest::RandPI(-Math::aiPi(), Math::aiPi());
}
diff --git a/tools/assimp_view/LogWindow.cpp b/tools/assimp_view/LogWindow.cpp
index 5397594dc..8a47b259f 100644
--- a/tools/assimp_view/LogWindow.cpp
+++ b/tools/assimp_view/LogWindow.cpp
@@ -105,7 +105,7 @@ void CLogWindow::Init() {
// setup the log text
this->szText = AI_VIEW_RTF_LOG_HEADER;
- ;
+
this->szPlainText = "";
}