diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp index fdccaf89..add6b39f 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.cpp +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.cpp @@ -138,8 +138,9 @@ namespace class GltfLoaderImpl final : public Loader { public: - explicit GltfLoaderImpl(const Input* input) - : m_input(input) + GltfLoaderImpl(const Input& input, const bool useBadRotationFormulas) + : m_input(input), + m_bad_rotation_formulas(useBadRotationFormulas) { } @@ -449,7 +450,7 @@ namespace return std::nullopt; } - static void ApplyNodeMatrixTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) + void ApplyNodeMatrixTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) { const auto matrix = Eigen::Matrix4f({ {(*node.matrix)[0], (*node.matrix)[4], (*node.matrix)[8], (*node.matrix)[12]}, @@ -460,29 +461,45 @@ namespace Eigen::Affine3f transform(matrix); const auto translation = transform.translation(); + localOffsetRhc[0] = translation.x(); localOffsetRhc[1] = translation.y(); localOffsetRhc[2] = translation.z(); + if (m_bad_rotation_formulas) + RhcToLhcCoordinates(localOffsetRhc); const auto rotation = transform.rotation(); const auto rotationQuat = Eigen::Quaternionf(rotation); - localRotationRhc[0] = rotationQuat.x(); - localRotationRhc[1] = rotationQuat.y(); - localRotationRhc[2] = rotationQuat.z(); - localRotationRhc[3] = rotationQuat.w(); + if (!m_bad_rotation_formulas) + { + localRotationRhc[0] = rotationQuat.x(); + localRotationRhc[1] = rotationQuat.y(); + localRotationRhc[2] = rotationQuat.z(); + localRotationRhc[3] = rotationQuat.w(); + } + else + { + // Backwards compatibility + localRotationRhc[0] = rotationQuat.x(); + localRotationRhc[1] = -rotationQuat.z(); + localRotationRhc[2] = rotationQuat.y(); + localRotationRhc[3] = rotationQuat.w(); + } scaleRhc[0] = matrix.block<3, 1>(0, 0).norm(); scaleRhc[1] = matrix.block<3, 1>(0, 1).norm(); scaleRhc[2] = matrix.block<3, 1>(0, 2).norm(); } - static void ApplyNodeSeparateTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) + void ApplyNodeSeparateTRS(const JsonNode& node, float (&localOffsetRhc)[3], float (&localRotationRhc)[4], float (&scaleRhc)[3]) { if (node.translation) { localOffsetRhc[0] = (*node.translation)[0]; localOffsetRhc[1] = (*node.translation)[1]; localOffsetRhc[2] = (*node.translation)[2]; + if (m_bad_rotation_formulas) + RhcToLhcCoordinates(localOffsetRhc); } else { @@ -493,10 +510,21 @@ namespace if (node.rotation) { - localRotationRhc[0] = (*node.rotation)[0]; - localRotationRhc[1] = (*node.rotation)[1]; - localRotationRhc[2] = (*node.rotation)[2]; - localRotationRhc[3] = (*node.rotation)[3]; + if (!m_bad_rotation_formulas) + { + localRotationRhc[0] = (*node.rotation)[0]; + localRotationRhc[1] = (*node.rotation)[1]; + localRotationRhc[2] = (*node.rotation)[2]; + localRotationRhc[3] = (*node.rotation)[3]; + } + else + { + // Backwards compatibility + localRotationRhc[0] = (*node.rotation)[0]; + localRotationRhc[1] = -(*node.rotation)[2]; + localRotationRhc[2] = (*node.rotation)[1]; + localRotationRhc[3] = (*node.rotation)[3]; + } } else { @@ -520,15 +548,15 @@ namespace } } - static bool ConvertJoint(const JsonRoot& jRoot, - const JsonSkin& skin, - XModelCommon& common, - const unsigned skinBoneOffset, - const unsigned nodeIndex, - const std::optional parentIndex, - const Eigen::Vector3f& parentTranslationEigenRhc, - const Eigen::Quaternionf& parentRotationEigenRhc, - const float (&parentScale)[3]) + bool ConvertJoint(const JsonRoot& jRoot, + const JsonSkin& skin, + XModelCommon& common, + const unsigned skinBoneOffset, + const unsigned nodeIndex, + const std::optional parentIndex, + const Eigen::Vector3f& parentTranslationEigenRhc, + const Eigen::Quaternionf& parentRotationEigenRhc, + const float (&parentScale)[3]) { if (!jRoot.nodes || nodeIndex >= jRoot.nodes->size()) return false; @@ -555,7 +583,8 @@ namespace bone.scale[0] = localScaleRhc[0] * parentScale[0]; bone.scale[1] = localScaleRhc[1] * parentScale[1]; bone.scale[2] = localScaleRhc[2] * parentScale[2]; - RhcToLhcScale(bone.scale); + if (!m_bad_rotation_formulas) + RhcToLhcScale(bone.scale); const Eigen::Vector3f localTranslationEigen(localOffsetRhc[0], localOffsetRhc[1], localOffsetRhc[2]); const Eigen::Quaternionf localRotationEigen(localRotationRhc[3], localRotationRhc[0], localRotationRhc[1], localRotationRhc[2]); @@ -566,13 +595,15 @@ namespace bone.globalOffset[0] = globalTranslationEigenRhc.x(); bone.globalOffset[1] = globalTranslationEigenRhc.y(); bone.globalOffset[2] = globalTranslationEigenRhc.z(); - RhcToLhcCoordinates(bone.globalOffset); + if (!m_bad_rotation_formulas) + RhcToLhcCoordinates(bone.globalOffset); bone.globalRotation.x = globalRotationEigenRhc.x(); bone.globalRotation.y = globalRotationEigenRhc.y(); bone.globalRotation.z = globalRotationEigenRhc.z(); bone.globalRotation.w = globalRotationEigenRhc.w(); - RhcToLhcQuaternion(bone.globalRotation); + if (!m_bad_rotation_formulas) + RhcToLhcQuaternion(bone.globalRotation); if (node.children) { @@ -587,7 +618,7 @@ namespace return true; } - static bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common) + bool ConvertSkin(const JsonRoot& jRoot, const JsonSkin& skin, XModelCommon& common) { if (skin.joints.empty()) return true; @@ -683,7 +714,7 @@ namespace { const void* embeddedBufferPtr = nullptr; size_t embeddedBufferSize = 0u; - if (!m_input->GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) + if (!m_input.GetEmbeddedBuffer(embeddedBufferPtr, embeddedBufferSize) || embeddedBufferSize == 0u) throw GltfLoadException("Buffer tried to access embedded data when there is none"); m_buffers.emplace_back(std::make_unique(embeddedBufferPtr, embeddedBufferSize)); @@ -763,7 +794,7 @@ namespace JsonRoot jRoot; try { - jRoot = m_input->GetJson().get(); + jRoot = m_input.GetJson().get(); } catch (const nlohmann::json::exception& e) { @@ -793,7 +824,7 @@ namespace } private: - const Input* m_input; + const Input& m_input; std::vector m_load_objects; std::unordered_map m_vertex_offset_for_accessors; std::vector> m_accessors; @@ -804,7 +835,7 @@ namespace }; } // namespace -std::unique_ptr Loader::CreateLoader(const Input* input) +std::unique_ptr Loader::CreateLoader(const Input& input, bool useBadRotationFormulas) { return std::make_unique(input, useBadRotationFormulas); } diff --git a/src/ObjLoading/XModel/Gltf/GltfLoader.h b/src/ObjLoading/XModel/Gltf/GltfLoader.h index 872394e4..46ae42ad 100644 --- a/src/ObjLoading/XModel/Gltf/GltfLoader.h +++ b/src/ObjLoading/XModel/Gltf/GltfLoader.h @@ -19,6 +19,13 @@ namespace gltf Loader& operator=(const Loader& other) = default; Loader& operator=(Loader&& other) noexcept = default; - static std::unique_ptr CreateLoader(const Input* input); + /** + * \brief Creates a loader capable of loading gltf-like files + * \param input The gltf input + * \param useBadRotationFormulas Old versions used bad formulas for converting into gltf space. Set to \c true to use them for loading to preserve + * backwards compatibility. + * \return + */ + static std::unique_ptr CreateLoader(const Input& input, bool useBadRotationFormulas); }; } // namespace gltf diff --git a/src/ObjLoading/XModel/LoaderXModel.cpp.template b/src/ObjLoading/XModel/LoaderXModel.cpp.template index c47b260e..90ef18ba 100644 --- a/src/ObjLoading/XModel/LoaderXModel.cpp.template +++ b/src/ObjLoading/XModel/LoaderXModel.cpp.template @@ -57,7 +57,8 @@ namespace { public: XModelLoader(MemoryManager& memory, ISearchPath& searchPath, ZoneScriptStrings& scriptStrings) - : m_memory(memory), + : m_gltf_bad_rotation_formulas(false), + m_memory(memory), m_search_path(searchPath), m_script_strings(scriptStrings) { @@ -97,12 +98,14 @@ namespace jRoot.at("_type").get_to(type); jRoot.at("_version").get_to(version); - if (type != "xmodel" || version != 1u) + if (type != "xmodel" || version < 1u || version > 2u) { - std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1\n", xmodel.name); + std::cerr << std::format("Tried to load xmodel \"{}\" but did not find expected type material of version 1 or 2\n", xmodel.name); return false; } + m_gltf_bad_rotation_formulas = version == 1u; + const auto jXModel = jRoot.get(); return CreateXModelFromJson(jXModel, xmodel, context, registration); } @@ -119,7 +122,7 @@ namespace std::cerr << std::format("Cannot load xmodel \"{}\": {}\n", xmodel.name, message); } - static std::unique_ptr LoadModelByExtension(std::istream& stream, const std::string& extension) + std::unique_ptr LoadModelByExtension(std::istream& stream, const std::string& extension) const { if (extension == ".glb") { @@ -127,7 +130,7 @@ namespace if (!input.ReadGltfData(stream)) return nullptr; - const auto loader = gltf::Loader::CreateLoader(&input); + const auto loader = gltf::Loader::CreateLoader(input, m_gltf_bad_rotation_formulas); return loader->Load(); } @@ -137,7 +140,7 @@ namespace if (!input.ReadGltfData(stream)) return nullptr; - const auto loader = gltf::Loader::CreateLoader(&input); + const auto loader = gltf::Loader::CreateLoader(input, m_gltf_bad_rotation_formulas); return loader->Load(); } @@ -1072,6 +1075,8 @@ namespace return true; } + bool m_gltf_bad_rotation_formulas; + std::vector m_surfaces; std::vector m_materials; diff --git a/src/ObjWriting/XModel/XModelDumper.cpp.template b/src/ObjWriting/XModel/XModelDumper.cpp.template index a79a4ef2..4a569a56 100644 --- a/src/ObjWriting/XModel/XModelDumper.cpp.template +++ b/src/ObjWriting/XModel/XModelDumper.cpp.template @@ -690,7 +690,7 @@ namespace jRoot["$schema"] = "http://openassettools.dev/schema/xmodel.v1.json"; jRoot["_type"] = "xmodel"; - jRoot["_version"] = 1; + jRoot["_version"] = 2; jRoot["_game"] = GAME_LOWER; m_stream << std::setw(4) << jRoot << "\n";