summaryrefslogtreecommitdiff
path: root/libs/assimp/test/unit/ImportExport/MDL
diff options
context:
space:
mode:
Diffstat (limited to 'libs/assimp/test/unit/ImportExport/MDL')
-rw-r--r--libs/assimp/test/unit/ImportExport/MDL/MDLHL1TestFiles.h57
-rw-r--r--libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_ImportSettings.cpp228
-rw-r--r--libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Materials.cpp134
-rw-r--r--libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp455
4 files changed, 874 insertions, 0 deletions
diff --git a/libs/assimp/test/unit/ImportExport/MDL/MDLHL1TestFiles.h b/libs/assimp/test/unit/ImportExport/MDL/MDLHL1TestFiles.h
new file mode 100644
index 0000000..3c0630f
--- /dev/null
+++ b/libs/assimp/test/unit/ImportExport/MDL/MDLHL1TestFiles.h
@@ -0,0 +1,57 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 MDLHL1TestFiles.h
+ * @brief Definitions for Half-Life 1 MDL loader tests.
+ */
+
+#ifndef AI_MDLHL1TESTFILES_INCLUDED
+#define AI_MDLHL1TESTFILES_INCLUDED
+
+#ifndef ASSIMP_TEST_MDL_HL1_MODELS_DIR
+#define ASSIMP_TEST_MDL_HL1_MODELS_DIR ASSIMP_TEST_MODELS_DIR"/MDL/MDL (HL1)/"
+#endif
+
+#ifndef MDL_HL1_FILE_MAN
+#define MDL_HL1_FILE_MAN ASSIMP_TEST_MDL_HL1_MODELS_DIR "man.mdl"
+#endif
+
+#endif // AI_MDLHL1TESTFILES_INCLUDED
diff --git a/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_ImportSettings.cpp b/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_ImportSettings.cpp
new file mode 100644
index 0000000..02969e1
--- /dev/null
+++ b/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_ImportSettings.cpp
@@ -0,0 +1,228 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 utMDLImporter_HL1_ImportSettings.cpp
+ * @brief Half-Life 1 MDL loader import settings tests.
+ */
+
+#include "AbstractImportExportBase.h"
+#include "AssetLib/MDL/HalfLife/HL1ImportDefinitions.h"
+#include "MDLHL1TestFiles.h"
+#include "UnitTestPCH.h"
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/Importer.hpp>
+#include <functional>
+#include <initializer_list>
+
+using namespace Assimp;
+
+class utMDLImporter_HL1_ImportSettings : public ::testing::Test {
+
+public:
+ // Test various import settings scenarios.
+
+ void importSettings() {
+
+ /* Verify that animations are *NOT* imported when
+ 'Read animations' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATIONS,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_EQ(0u, scene->mNumAnimations);
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_INFOS));
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_GROUPS));
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH));
+
+ expect_global_info_eq<int>(scene, { { 0, "NumSequences" },
+ { 0, "NumTransitionNodes" } });
+ });
+
+ /* Verify that blend controllers info is *NOT* imported when
+ 'Read blend controllers' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_BLEND_CONTROLLERS,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_NE(0u, scene->mNumAnimations);
+
+ const aiNode *sequence_infos = scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_INFOS);
+ EXPECT_NE(nullptr, sequence_infos);
+
+ for (unsigned int i = 0; i < sequence_infos->mNumChildren; ++i)
+ EXPECT_EQ(nullptr, sequence_infos->mChildren[i]->FindNode(AI_MDL_HL1_NODE_BLEND_CONTROLLERS));
+
+ expect_global_info_eq(scene, 0, "NumBlendControllers");
+ });
+
+ /* Verify that animation events are *NOT* imported when
+ 'Read animation events' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_ANIMATION_EVENTS,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_NE(0u, scene->mNumAnimations);
+
+ const aiNode *sequence_infos = scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_INFOS);
+ EXPECT_NE(nullptr, sequence_infos);
+
+ for (unsigned int i = 0; i < sequence_infos->mNumChildren; ++i)
+ EXPECT_EQ(nullptr, sequence_infos->mChildren[i]->FindNode(AI_MDL_HL1_NODE_ANIMATION_EVENTS));
+ });
+
+ /* Verify that sequence transitions info is read when
+ 'Read sequence transitions' is enabled. */
+ load_with_import_setting_bool(
+ ASSIMP_TEST_MDL_HL1_MODELS_DIR "sequence_transitions.mdl",
+ AI_CONFIG_IMPORT_MDL_HL1_READ_SEQUENCE_TRANSITIONS,
+ true, // Set config value to true.
+ [&](const aiScene *scene) {
+ EXPECT_NE(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH));
+ expect_global_info_eq(scene, 4, "NumTransitionNodes");
+ });
+
+ /* Verify that sequence transitions info is *NOT* read when
+ 'Read sequence transitions' is disabled. */
+ load_with_import_setting_bool(
+ ASSIMP_TEST_MDL_HL1_MODELS_DIR "sequence_transitions.mdl",
+ AI_CONFIG_IMPORT_MDL_HL1_READ_SEQUENCE_TRANSITIONS,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_SEQUENCE_TRANSITION_GRAPH));
+ expect_global_info_eq(scene, 0, "NumTransitionNodes");
+ });
+
+ /* Verify that bone controllers info is *NOT* read when
+ 'Read bone controllers' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_BONE_CONTROLLERS,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_BONE_CONTROLLERS));
+ expect_global_info_eq(scene, 0, "NumBoneControllers");
+ });
+
+ /* Verify that attachments info is *NOT* read when
+ 'Read attachments' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_ATTACHMENTS,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_ATTACHMENTS));
+ expect_global_info_eq(scene, 0, "NumAttachments");
+ });
+
+ /* Verify that hitboxes info is *NOT* read when
+ 'Read hitboxes' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_HITBOXES,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ EXPECT_EQ(nullptr, scene->mRootNode->FindNode(AI_MDL_HL1_NODE_HITBOXES));
+ expect_global_info_eq(scene, 0, "NumHitboxes");
+ });
+
+ /* Verify that misc global info is *NOT* read when
+ 'Read misc global info' is disabled. */
+ load_with_import_setting_bool(
+ MDL_HL1_FILE_MAN,
+ AI_CONFIG_IMPORT_MDL_HL1_READ_MISC_GLOBAL_INFO,
+ false, // Set config value to false.
+ [&](const aiScene *scene) {
+ aiNode *global_info = get_global_info(scene);
+ EXPECT_NE(nullptr, global_info);
+ aiVector3D temp;
+ EXPECT_FALSE(global_info->mMetaData->Get("EyePosition", temp));
+ });
+ }
+
+private:
+ void load_with_import_setting_bool(
+ const char *file_path,
+ const char *setting_key,
+ bool setting_value,
+ std::function<void(const aiScene *)> &&func) {
+ Assimp::Importer importer;
+ importer.SetPropertyBool(setting_key, setting_value);
+ const aiScene *scene = importer.ReadFile(file_path, aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+ func(scene);
+ }
+
+ inline static aiNode *get_global_info(const aiScene *scene) {
+ return scene->mRootNode->FindNode(AI_MDL_HL1_NODE_GLOBAL_INFO);
+ }
+
+ template <typename T>
+ static void expect_global_info_eq(
+ const aiScene *scene,
+ T expected_value,
+ const char *key_name) {
+ aiNode *global_info = get_global_info(scene);
+ EXPECT_NE(nullptr, global_info);
+ T temp;
+ EXPECT_TRUE(global_info->mMetaData->Get(key_name, temp));
+ EXPECT_EQ(expected_value, temp);
+ }
+
+ template <typename T>
+ static void expect_global_info_eq(const aiScene *scene,
+ std::initializer_list<std::pair<T, const char *>> p_kv) {
+ aiNode *global_info = get_global_info(scene);
+ EXPECT_NE(nullptr, global_info);
+ for (auto it = p_kv.begin(); it != p_kv.end(); ++it) {
+ T temp;
+ EXPECT_TRUE(global_info->mMetaData->Get(it->second, temp));
+ EXPECT_EQ(it->first, temp);
+ }
+ }
+};
+
+TEST_F(utMDLImporter_HL1_ImportSettings, importSettings) {
+ importSettings();
+}
diff --git a/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Materials.cpp b/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Materials.cpp
new file mode 100644
index 0000000..c47667e
--- /dev/null
+++ b/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Materials.cpp
@@ -0,0 +1,134 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 utMDLImporter_HL1_Materials.cpp
+ * @brief Half-Life 1 MDL loader materials tests.
+ */
+
+#include "AbstractImportExportBase.h"
+#include "AssetLib/MDL/HalfLife/HL1ImportDefinitions.h"
+#include "MDLHL1TestFiles.h"
+#include "UnitTestPCH.h"
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/Importer.hpp>
+
+using namespace Assimp;
+
+class utMDLImporter_HL1_Materials : public ::testing::Test {
+
+public:
+ /* Given an MDL model with a texture flagged as flatshade,
+ verify that the imported model has a flat shading model. */
+ void flatShadeTexture() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "chrome_sphere.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+ EXPECT_NE(nullptr, scene->mMaterials);
+
+ aiShadingMode shading_mode;
+ scene->mMaterials[0]->Get(AI_MATKEY_SHADING_MODEL, shading_mode);
+ EXPECT_EQ(aiShadingMode_Flat, shading_mode);
+ }
+
+ /* Given an MDL model with a chrome texture, verify that
+ the imported model has a chrome material. */
+ void chromeTexture() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "chrome_sphere.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+ EXPECT_NE(nullptr, scene->mMaterials);
+
+ int chrome;
+ scene->mMaterials[0]->Get(AI_MDL_HL1_MATKEY_CHROME(aiTextureType_DIFFUSE, 0), chrome);
+ EXPECT_EQ(1, chrome);
+ }
+
+ /* Given an MDL model with an additive texture, verify that
+ the imported model has an additive material. */
+ void additiveBlendTexture() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "blend_additive.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+ EXPECT_NE(nullptr, scene->mMaterials);
+
+ aiBlendMode blend_mode;
+ scene->mMaterials[0]->Get(AI_MATKEY_BLEND_FUNC, blend_mode);
+ EXPECT_EQ(aiBlendMode_Additive, blend_mode);
+ }
+
+ /* Given an MDL model with a color masked texture, verify that
+ the imported model has a color masked material. Ensure too
+ that the transparency color is the correct one. */
+ void textureWithColorMask() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "alpha_test.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+ EXPECT_NE(nullptr, scene->mMaterials);
+
+ int texture_flags;
+ scene->mMaterials[0]->Get(AI_MATKEY_TEXFLAGS_DIFFUSE(0), texture_flags);
+ EXPECT_EQ(aiTextureFlags_UseAlpha, texture_flags);
+
+ // The model has only one texture, a 256 color bitmap with
+ // a palette. Pure blue is the last color in the palette,
+ // and should be the transparency color.
+ aiColor3D transparency_color;
+ scene->mMaterials[0]->Get(AI_MATKEY_COLOR_TRANSPARENT, transparency_color);
+ EXPECT_EQ(aiColor3D(0, 0, 255), transparency_color);
+ }
+};
+
+TEST_F(utMDLImporter_HL1_Materials, flatShadeTexture) {
+ flatShadeTexture();
+}
+
+TEST_F(utMDLImporter_HL1_Materials, chromeTexture) {
+ chromeTexture();
+}
+
+TEST_F(utMDLImporter_HL1_Materials, additiveBlendTexture) {
+ additiveBlendTexture();
+}
+
+TEST_F(utMDLImporter_HL1_Materials, textureWithColorMask) {
+ textureWithColorMask();
+}
diff --git a/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp b/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp
new file mode 100644
index 0000000..4420727
--- /dev/null
+++ b/libs/assimp/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp
@@ -0,0 +1,455 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, 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 utMDLImporter_HL1_Nodes.cpp
+ * @brief Half-Life 1 MDL loader nodes tests.
+ */
+
+#include "AbstractImportExportBase.h"
+#include "AssetLib/MDL/HalfLife/HL1ImportDefinitions.h"
+#include "MDLHL1TestFiles.h"
+#include "UnitTestPCH.h"
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/Importer.hpp>
+
+using namespace Assimp;
+
+class utMDLImporter_HL1_Nodes : public ::testing::Test {
+
+public:
+ /**
+ * @note The following tests require a basic understanding
+ * of the SMD format. For more information about SMD format,
+ * please refer to the SMD importer or go to VDC
+ * (Valve Developer Community).
+ */
+
+ /* Given a model with bones that have empty names,
+ verify that all the bones of the imported model
+ have unique and no empty names.
+
+ "" <----+---- empty names
+ "" <----+
+ "" <----+
+ "Bone_3" |
+ "" <----+
+ "Bone_2" |
+ "Bone_5" |
+ "" <----+
+ "" <----+
+ */
+ void emptyBonesNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "unnamed_bones.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_bones_names = {
+ "Bone",
+ "Bone_0",
+ "Bone_1",
+ "Bone_3",
+ "Bone_4",
+ "Bone_2",
+ "Bone_5",
+ "Bone_6",
+ "Bone_7"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_BONES, expected_bones_names);
+ }
+
+ /* Given a model with bodyparts that have empty names,
+ verify that the imported model contains bodyparts with
+ unique and no empty names.
+
+ $body "" <----+---- empty names
+ $body "Bodypart_1" |
+ $body "Bodypart_5" |
+ $body "Bodypart_6" |
+ $body "" <----+
+ $body "Bodypart_2" |
+ $body "" <----+
+ $body "Bodypart_3" |
+ $body "" <----+
+ */
+ void emptyBodypartsNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "unnamed_bodyparts.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_bodyparts_names = {
+ "Bodypart",
+ "Bodypart_1",
+ "Bodypart_5",
+ "Bodypart_6",
+ "Bodypart_0",
+ "Bodypart_2",
+ "Bodypart_4",
+ "Bodypart_3",
+ "Bodypart_7"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_BODYPARTS, expected_bodyparts_names);
+ }
+
+ /* Given a model with bodyparts that have duplicate names,
+ verify that the imported model contains bodyparts with
+ unique and no duplicate names.
+
+ $body "Bodypart" <-----+
+ $body "Bodypart_1" <--+ |
+ $body "Bodypart_2" | |
+ $body "Bodypart1" | |
+ $body "Bodypart" ---|--+
+ $body "Bodypart_1" ---+ |
+ $body "Bodypart2" |
+ $body "Bodypart" ------+
+ $body "Bodypart_4"
+ */
+ void duplicateBodypartsNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "duplicate_bodyparts.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_bodyparts_names = {
+ "Bodypart",
+ "Bodypart_1",
+ "Bodypart_2",
+ "Bodypart1",
+ "Bodypart_0",
+ "Bodypart_1_0",
+ "Bodypart2",
+ "Bodypart_3",
+ "Bodypart_4"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_BODYPARTS, expected_bodyparts_names);
+ }
+
+ /* Given a model with several bodyparts that contains multiple
+ sub models with the same file name, verify for each bodypart
+ sub model of the imported model that they have a unique name.
+
+ $bodygroup "first_bodypart"
+ {
+ studio "triangle" <------+ duplicate file names.
+ studio "triangle" -------+
+ } |
+ |
+ $bodygroup "second_bodypart" |
+ { |
+ studio "triangle" -------+ same as first bodypart, but with same file.
+ studio "triangle" -------+
+ }
+
+ $bodygroup "last_bodypart"
+ {
+ studio "triangle2" <------+ duplicate names.
+ studio "triangle2" -------+
+ }
+ */
+ void duplicateSubModelsNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "duplicate_submodels.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::vector<std::string>> expected_bodypart_sub_models_names = {
+ {
+ "triangle",
+ "triangle_0",
+ },
+ {
+ "triangle_1",
+ "triangle_2",
+ },
+ {
+ "triangle2",
+ "triangle2_0",
+ }
+ };
+
+ const aiNode *bodyparts_node = scene->mRootNode->FindNode(AI_MDL_HL1_NODE_BODYPARTS);
+ EXPECT_NE(nullptr, bodyparts_node);
+ EXPECT_EQ(3u, bodyparts_node->mNumChildren);
+ for (unsigned int i = 0; i < bodyparts_node->mNumChildren; ++i) {
+ expect_named_children(bodyparts_node->mChildren[i],
+ expected_bodypart_sub_models_names[i]);
+ }
+ }
+
+ /* Given a model with sequences that have duplicate names, verify
+ that each sequence from the imported model has a unique
+ name.
+
+ $sequence "idle_1" <-------+
+ $sequence "idle" <----+ |
+ $sequence "idle_2" | |
+ $sequence "idle" -----+ |
+ $sequence "idle_0" | |
+ $sequence "idle_1" -----|--+
+ $sequence "idle_3" |
+ $sequence "idle" -----+
+ $sequence "idle_7"
+ */
+ void duplicateSequenceNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "duplicate_sequences.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_sequence_names = {
+ "idle_1",
+ "idle",
+ "idle_2",
+ "idle_4",
+ "idle_0",
+ "idle_1_0",
+ "idle_3",
+ "idle_5",
+ "idle_7"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_SEQUENCE_INFOS, expected_sequence_names);
+ }
+
+ /* Given a model with sequences that have empty names, verify
+ that each sequence from the imported model has a unique
+ name.
+
+ $sequence "" <----+---- empty names
+ $sequence "Sequence_1" |
+ $sequence "" <----+
+ $sequence "Sequence_4" |
+ $sequence "" <----+
+ $sequence "Sequence_8" |
+ $sequence "" <----+
+ $sequence "Sequence_2" |
+ $sequence "" <----+
+ */
+ void emptySequenceNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "unnamed_sequences.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_sequence_names = {
+ "Sequence",
+ "Sequence_1",
+ "Sequence_0",
+ "Sequence_4",
+ "Sequence_3",
+ "Sequence_8",
+ "Sequence_5",
+ "Sequence_2",
+ "Sequence_6"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_SEQUENCE_INFOS, expected_sequence_names);
+ }
+
+ /* Given a model with sequence groups that have duplicate names,
+ verify that each sequence group from the imported model has
+ a unique name.
+
+ "default"
+ $sequencegroup "SequenceGroup" <----+
+ $sequencegroup "SequenceGroup_1" |
+ $sequencegroup "SequenceGroup_5" <----|--+
+ $sequencegroup "SequenceGroup" -----+ |
+ $sequencegroup "SequenceGroup_0" | |
+ $sequencegroup "SequenceGroup" -----+ |
+ $sequencegroup "SequenceGroup_5" --------+
+ $sequencegroup "SequenceGroup_6"
+ $sequencegroup "SequenceGroup_2"
+ */
+ void duplicateSequenceGroupNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "duplicate_sequence_groups/duplicate_sequence_groups.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_sequence_names = {
+ "default",
+ "SequenceGroup",
+ "SequenceGroup_1",
+ "SequenceGroup_5",
+ "SequenceGroup_3",
+ "SequenceGroup_0",
+ "SequenceGroup_4",
+ "SequenceGroup_5_0",
+ "SequenceGroup_6",
+ "SequenceGroup_2"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_SEQUENCE_GROUPS, expected_sequence_names);
+ }
+
+ /* Given a model with sequence groups that have empty names,
+ verify that each sequence group from the imported model has
+ a unique name.
+
+ "default"
+ $sequencegroup "" <----+---- empty names
+ $sequencegroup "SequenceGroup_2" |
+ $sequencegroup "SequenceGroup_6" |
+ $sequencegroup "" <----+
+ $sequencegroup "" <----+
+ $sequencegroup "SequenceGroup_1" |
+ $sequencegroup "SequenceGroup_5" |
+ $sequencegroup "" <----+
+ $sequencegroup "SequenceGroup_4"
+ */
+ void emptySequenceGroupNames() {
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MDL_HL1_MODELS_DIR "unnamed_sequence_groups/unnamed_sequence_groups.mdl", aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ const std::vector<std::string> expected_sequence_names = {
+ "default",
+ "SequenceGroup",
+ "SequenceGroup_2",
+ "SequenceGroup_6",
+ "SequenceGroup_0",
+ "SequenceGroup_3",
+ "SequenceGroup_1",
+ "SequenceGroup_5",
+ "SequenceGroup_7",
+ "SequenceGroup_4"
+ };
+
+ expect_named_children(scene, AI_MDL_HL1_NODE_SEQUENCE_GROUPS, expected_sequence_names);
+ }
+
+ /* Verify that mOffsetMatrix applies the correct
+ inverse bind pose transform. */
+ void offsetMatrixUnappliesTransformations() {
+
+ const float TOLERANCE = 0.01f;
+
+ Assimp::Importer importer;
+ const aiScene *scene = importer.ReadFile(MDL_HL1_FILE_MAN, aiProcess_ValidateDataStructure);
+ EXPECT_NE(nullptr, scene);
+
+ aiNode *scene_bones_node = scene->mRootNode->FindNode(AI_MDL_HL1_NODE_BONES);
+
+ const aiMatrix4x4 identity_matrix;
+
+ for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
+ aiMesh *scene_mesh = scene->mMeshes[i];
+ for (unsigned int j = 0; j < scene_mesh->mNumBones; ++j) {
+ aiBone *scene_mesh_bone = scene_mesh->mBones[j];
+
+ // Store local node transforms.
+ aiNode *n = scene_bones_node->FindNode(scene_mesh_bone->mName);
+ std::vector<aiMatrix4x4> bone_matrices = { n->mTransformation };
+ while (n->mParent != scene->mRootNode) {
+ n = n->mParent;
+ bone_matrices.push_back(n->mTransformation);
+ }
+
+ // Compute absolute node transform.
+ aiMatrix4x4 transform;
+ for (auto it = bone_matrices.rbegin(); it != bone_matrices.rend(); ++it)
+ transform *= *it;
+
+ // Unapply the transformation using the offset matrix.
+ aiMatrix4x4 unapplied_transform = scene_mesh_bone->mOffsetMatrix * transform;
+
+ // Ensure that we have, approximately, the identity matrix.
+ expect_equal_matrices(identity_matrix, unapplied_transform, TOLERANCE);
+ }
+ }
+ }
+
+private:
+ void expect_named_children(const aiNode *parent_node, const std::vector<std::string> &expected_names) {
+ EXPECT_NE(nullptr, parent_node);
+ EXPECT_EQ(expected_names.size(), parent_node->mNumChildren);
+
+ for (unsigned int i = 0; i < parent_node->mNumChildren; ++i)
+ EXPECT_EQ(expected_names[i], parent_node->mChildren[i]->mName.C_Str());
+ }
+
+ void expect_named_children(const aiScene *scene, const char *node_name, const std::vector<std::string> &expected_names) {
+ expect_named_children(scene->mRootNode->FindNode(node_name), expected_names);
+ }
+
+ void expect_equal_matrices(const aiMatrix4x4 &expected, const aiMatrix4x4 &actual, float abs_error) {
+ for (int i = 0; i < 4; ++i) {
+ for (int j = 0; j < 4; ++j)
+ EXPECT_NEAR(expected[i][j], actual[i][j], abs_error);
+ }
+ }
+};
+
+TEST_F(utMDLImporter_HL1_Nodes, emptyBonesNames) {
+ emptyBonesNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, emptyBodypartsNames) {
+ emptyBodypartsNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, duplicateBodypartsNames) {
+ duplicateBodypartsNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, duplicateSubModelsNames) {
+ duplicateSubModelsNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, emptySequenceNames) {
+ emptySequenceNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, duplicateSequenceNames) {
+ duplicateSequenceNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, emptySequenceGroupNames) {
+ emptySequenceGroupNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, duplicateSequenceGroupNames) {
+ duplicateSequenceGroupNames();
+}
+
+TEST_F(utMDLImporter_HL1_Nodes, offsetMatrixUnappliesTransformations) {
+ offsetMatrixUnappliesTransformations();
+}