diff options
Diffstat (limited to 'libs/assimp/test/unit/ImportExport/MDL')
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(); +} |