Fix identity matrix check

Adds an extra epsilon value to check the matrix4x4 identity. The method is also used to export to GLTF/GLTF2 format to check node transformation matrices. The epsilon value can be set using AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON with the default value set to 10e-3f for backward compatibility of legacy code.
pull/5445/head
fvbj 2024-01-30 16:05:36 +01:00
parent ac29847d56
commit b305195936
9 changed files with 80 additions and 7 deletions

View File

@ -56,6 +56,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/Exporter.hpp>
#include <assimp/material.h>
#include <assimp/scene.h>
#include <assimp/config.h>
// Header files, standard library.
#include <memory>
@ -113,6 +114,10 @@ glTFExporter::glTFExporter(const char* filename, IOSystem* pIOSystem, const aiSc
mAsset = std::make_shared<glTF::Asset>(pIOSystem);
configEpsilon = mProperties->GetPropertyFloat(
AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON,
(ai_real)AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT);
if (isBinary) {
mAsset->SetAsBinary();
}
@ -824,7 +829,7 @@ unsigned int glTFExporter::ExportNodeHierarchy(const aiNode* n)
{
Ref<Node> node = mAsset->nodes.Create(mAsset->FindUniqueID(n->mName.C_Str(), "node"));
if (!n->mTransformation.IsIdentity()) {
if (!n->mTransformation.IsIdentity(configEpsilon)) {
node->matrix.isPresent = true;
CopyValue(n->mTransformation, node->matrix.value);
}
@ -851,7 +856,7 @@ unsigned int glTFExporter::ExportNode(const aiNode* n, Ref<Node>& parent)
node->parent = parent;
if (!n->mTransformation.IsIdentity()) {
if (!n->mTransformation.IsIdentity(configEpsilon)) {
node->matrix.isPresent = true;
CopyValue(n->mTransformation, node->matrix.value);
}

View File

@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h>
#include <assimp/types.h>
#include <assimp/defs.h>
#include <map>
#include <memory>
@ -98,6 +99,8 @@ private:
std::vector<unsigned char> mBodyData;
ai_real configEpsilon;
void WriteBinaryData(IOStream *outfile, std::size_t sceneLength);
void GetTexSampler(const aiMaterial *mat, glTF::TexProperty &prop);

View File

@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/version.h>
#include <assimp/Exporter.hpp>
#include <assimp/IOSystem.hpp>
#include <assimp/config.h>
// Header files, standard library.
#include <cinttypes>
@ -90,6 +91,10 @@ glTF2Exporter::glTF2Exporter(const char *filename, IOSystem *pIOSystem, const ai
// Always on as our triangulation process is aware of this type of encoding
mAsset->extensionsUsed.FB_ngon_encoding = true;
configEpsilon = mProperties->GetPropertyFloat(
AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON,
(ai_real)AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT);
if (isBinary) {
mAsset->SetAsBinary();
}
@ -1455,7 +1460,7 @@ unsigned int glTF2Exporter::ExportNodeHierarchy(const aiNode *n) {
node->name = n->mName.C_Str();
if (!n->mTransformation.IsIdentity()) {
if (!n->mTransformation.IsIdentity(configEpsilon)) {
node->matrix.isPresent = true;
CopyValue(n->mTransformation, node->matrix.value);
}
@ -1485,7 +1490,7 @@ unsigned int glTF2Exporter::ExportNode(const aiNode *n, Ref<Node> &parent) {
ExportNodeExtras(n->mMetaData, node->extras);
if (!n->mTransformation.IsIdentity()) {
if (!n->mTransformation.IsIdentity(configEpsilon)) {
if (mScene->mNumAnimations > 0 || (mProperties && mProperties->HasPropertyBool("GLTF2_NODE_IN_TRS"))) {
aiQuaternion quaternion;
n->mTransformation.Decompose(*reinterpret_cast<aiVector3D *>(&node->scale.value), quaternion, *reinterpret_cast<aiVector3D *>(&node->translation.value));

View File

@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/material.h>
#include <assimp/types.h>
#include <assimp/defs.h>
#include <map>
#include <memory>
@ -142,6 +143,7 @@ private:
std::map<std::string, unsigned int> mTexturesByPath;
std::shared_ptr<glTF2::Asset> mAsset;
std::vector<unsigned char> mBodyData;
ai_real configEpsilon;
};
} // namespace Assimp

View File

@ -225,6 +225,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#define AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION \
"PP_PTV_ROOT_TRANSFORMATION"
// ---------------------------------------------------------------------------
/** @brief Set epsilon to check the identity of the matrix 4x4.
*
* This is used by aiMatrix4x4t<TReal>::IsIdentity(const TReal epsilon).
* @note The default value is 10e-3f for backward compatibility of legacy code.
* Property type: Float.
*/
#define AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON \
"CHECK_IDENTITY_MATRIX_EPSILON"
// default value for AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON
#if (!defined AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT)
# define AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT 10e-3f
#endif
// ---------------------------------------------------------------------------
/** @brief Configures the #aiProcess_FindDegenerates step to
* remove degenerated primitives from the import - immediately.

View File

@ -136,9 +136,13 @@ public:
// -------------------------------------------------------------------
/** @brief Returns true of the matrix is the identity matrix.
* @param epsilon Value of epsilon. Default value is 10e-3 for backward
* compatibility with legacy code.
* @return Returns true of the matrix is the identity matrix.
* The check is performed against a not so small epsilon.
*/
inline bool IsIdentity() const;
inline bool IsIdentity(const TReal
epsilon = AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT) const;
// -------------------------------------------------------------------
/** @brief Decompose a trafo matrix into its original components

View File

@ -545,9 +545,10 @@ aiMatrix4x4t<TReal>& aiMatrix4x4t<TReal>::FromEulerAnglesXYZ(TReal x, TReal y, T
// ----------------------------------------------------------------------------------------
template <typename TReal>
AI_FORCE_INLINE
bool aiMatrix4x4t<TReal>::IsIdentity() const {
bool aiMatrix4x4t<TReal>::IsIdentity(const TReal epsilon) const {
// Use a small epsilon to solve floating-point inaccuracies
const static TReal epsilon = 10e-3f;
// Default value for backward compatibility with legacy code:
// const static TReal epsilon = 10e-3f;
return (a2 <= epsilon && a2 >= -epsilon &&
a3 <= epsilon && a3 >= -epsilon &&

View File

@ -90,3 +90,17 @@ TEST_F(utMatrix4x4, indexOperatorTest) {
ai_real *a15 = a12 + 3;
EXPECT_FLOAT_EQ(1.0, *a15);
}
TEST_F(utMatrix4x4, identityMatrixTest) {
aiMatrix4x4 m1 = aiMatrix4x4(1.f,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0, 1);
EXPECT_TRUE(m1.IsIdentity());
aiMatrix4x4 m2 = aiMatrix4x4(1.02f,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0, 1);
EXPECT_FALSE(m2.IsIdentity());
aiMatrix4x4 m3 = aiMatrix4x4(1.009f,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0, 1);
EXPECT_TRUE(m3.IsIdentity());
EXPECT_TRUE(m1.IsIdentity(1e-3f));
EXPECT_FALSE(m2.IsIdentity(1e-3f));
EXPECT_TRUE(m2.IsIdentity(1e-1f));
EXPECT_FALSE(m3.IsIdentity(1e-3f));
}

View File

@ -988,3 +988,27 @@ TEST_F(utglTF2ImportExport, noSchemaFound) {
EXPECT_NE(scene, nullptr);
EXPECT_STREQ(importer.GetErrorString(), "");
}
// ------------------------------------------------------------------------------------------------
TEST_F(utglTF2ImportExport, testSetIdentityMatrixEpsilon) {
// Assimp::Exporter exporter;
Assimp::ExportProperties properties = Assimp::ExportProperties();
EXPECT_EQ(AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT,
properties.GetPropertyFloat("CHECK_IDENTITY_MATRIX_EPSILON",
AI_CONFIG_CHECK_IDENTITY_MATRIX_EPSILON_DEFAULT));
aiMatrix4x4 m;
m = aiMatrix4x4(1.02, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
EXPECT_FALSE(m.IsIdentity());
m = aiMatrix4x4(1.001, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
EXPECT_TRUE(m.IsIdentity());
bool b = properties.SetPropertyFloat("CHECK_IDENTITY_MATRIX_EPSILON", 0.0001f);
EXPECT_TRUE(!b);
ai_real epsilon = properties.GetPropertyFloat("CHECK_IDENTITY_MATRIX_EPSILON", 0.01f);
EXPECT_EQ(0.0001f, epsilon);
m = aiMatrix4x4(1.0002, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
EXPECT_FALSE(m.IsIdentity(epsilon));
m = aiMatrix4x4(1.00009, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1);
EXPECT_TRUE(m.IsIdentity(epsilon));
}