diff --git a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp index 3feeada7..2f148aad 100644 --- a/src/ObjWriting/XModel/Gltf/GltfWriter.cpp +++ b/src/ObjWriting/XModel/Gltf/GltfWriter.cpp @@ -1,6 +1,7 @@ #include "GltfWriter.h" #include "GitVersion.h" +#include "Math/Vector.h" #include "XModel/Gltf/GltfConstants.h" #include "XModel/Gltf/JsonGltf.h" @@ -22,9 +23,6 @@ namespace class GltfWriterImpl final : public gltf::Writer { - static constexpr auto NODE_INDEX_MESH = 0u; - static constexpr auto NODE_FIRST_INDEX_BONES = 1u; - public: GltfWriterImpl(const Output* output, std::string gameName, std::string zoneName) : m_output(output), @@ -40,8 +38,8 @@ namespace CreateJsonAsset(gltf.asset); CreateMeshNode(gltf, xmodel); - CreateMaterials(gltf, xmodel); CreateSkeletonNodes(gltf, xmodel); + CreateMaterials(gltf, xmodel); CreateSkin(gltf, xmodel); CreateBufferViews(gltf, xmodel); CreateAccessors(gltf, xmodel); @@ -66,7 +64,7 @@ namespace asset.generator = GLTF_GENERATOR; } - static void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateMeshNode(JsonRoot& gltf, const XModelCommon& xmodel) { JsonNode meshNode; @@ -83,6 +81,7 @@ namespace if (!gltf.nodes.has_value()) gltf.nodes.emplace(); + m_mesh_node = gltf.nodes->size(); gltf.nodes->emplace_back(std::move(meshNode)); } @@ -93,6 +92,8 @@ namespace JsonMesh mesh; + const auto hasBoneWeightData = !xmodel.m_vertex_bone_weights.empty(); + auto objectIndex = 0u; for (const auto& object : xmodel.m_objects) { @@ -105,6 +106,12 @@ namespace primitives.attributes.NORMAL = m_normal_accessor; primitives.attributes.TEXCOORD_0 = m_uv_accessor; + if (hasBoneWeightData) + { + primitives.attributes.JOINTS_0 = m_joints_accessor; + primitives.attributes.WEIGHTS_0 = m_weights_accessor; + } + primitives.mode = JsonMeshPrimitivesMode::TRIANGLES; primitives.indices = m_first_index_accessor + objectIndex; @@ -136,7 +143,7 @@ namespace } if (!modelMaterial.normalMapName.empty()) - material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.colorMapName); + material.normalTexture.emplace().index = CreateTexture(gltf, modelMaterial.normalMapName); material.doubleSided = true; gltf.materials->emplace_back(material); @@ -176,7 +183,7 @@ namespace return textureIndex; } - static void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateSkeletonNodes(JsonRoot& gltf, const XModelCommon& xmodel) { if (xmodel.m_bones.empty()) return; @@ -185,20 +192,32 @@ namespace gltf.nodes.emplace(); const auto boneCount = xmodel.m_bones.size(); + m_first_bone_node = gltf.nodes->size(); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) { JsonNode boneNode; const auto& bone = xmodel.m_bones[boneIndex]; + Vector3f translation(bone.globalOffset[0], bone.globalOffset[2], -bone.globalOffset[1]); + Quaternion32 rotation(bone.globalRotation.m_x, bone.globalRotation.m_z, -bone.globalRotation.m_y, bone.globalRotation.m_w); + if (bone.parentIndex >= 0) + { + const auto& parentBone = xmodel.m_bones[bone.parentIndex]; + translation -= Vector3f(parentBone.globalOffset[0], parentBone.globalOffset[2], -parentBone.globalOffset[1]); + rotation -= Quaternion32( + parentBone.globalRotation.m_x, parentBone.globalRotation.m_z, -parentBone.globalRotation.m_y, parentBone.globalRotation.m_w); + } + rotation.Normalize(); + boneNode.name = bone.name; - boneNode.translation = std::to_array(bone.globalOffset); - boneNode.rotation = std::to_array({bone.globalRotation.m_x, bone.globalRotation.m_y, bone.globalRotation.m_z, bone.globalRotation.m_w}); + boneNode.translation = std::to_array({translation.x(), translation.y(), translation.z()}); + boneNode.rotation = std::to_array({rotation.m_x, rotation.m_y, rotation.m_z, rotation.m_w}); std::vector children; for (auto maybeChildIndex = 0u; maybeChildIndex < boneCount; maybeChildIndex++) { if (xmodel.m_bones[maybeChildIndex].parentIndex == static_cast(boneIndex)) - children.emplace_back(maybeChildIndex + NODE_FIRST_INDEX_BONES); + children.emplace_back(maybeChildIndex + m_first_bone_node); } if (!children.empty()) boneNode.children = std::move(children); @@ -207,7 +226,7 @@ namespace } } - static void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) + void CreateSkin(JsonRoot& gltf, const XModelCommon& xmodel) const { if (xmodel.m_bones.empty()) return; @@ -221,7 +240,7 @@ namespace skin.joints.reserve(boneCount); for (auto boneIndex = 0u; boneIndex < boneCount; boneIndex++) - skin.joints.emplace_back(boneIndex + NODE_FIRST_INDEX_BONES); + skin.joints.emplace_back(boneIndex + m_first_bone_node); gltf.skins->emplace_back(std::move(skin)); } @@ -231,10 +250,7 @@ namespace JsonScene scene; // Only add skin if the model has bones - if (xmodel.m_bones.empty()) - scene.nodes.emplace_back(NODE_INDEX_MESH); - else - scene.nodes.emplace_back(m_skeleton_node); + scene.nodes.emplace_back(m_mesh_node); if (!gltf.scenes.has_value()) gltf.scenes.emplace(); @@ -261,6 +277,29 @@ namespace m_vertex_buffer_view = gltf.bufferViews->size(); gltf.bufferViews->emplace_back(vertexBufferView); + if (!xmodel.m_vertex_bone_weights.empty()) + { + JsonBufferView jointsBufferView; + jointsBufferView.buffer = 0u; + jointsBufferView.byteOffset = bufferOffset; + jointsBufferView.byteLength = sizeof(uint8_t) * xmodel.m_vertices.size() * 4u; + jointsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER; + bufferOffset += jointsBufferView.byteLength; + + m_joints_buffer_view = gltf.bufferViews->size(); + gltf.bufferViews->emplace_back(jointsBufferView); + + JsonBufferView weightsBufferView; + weightsBufferView.buffer = 0u; + weightsBufferView.byteOffset = bufferOffset; + weightsBufferView.byteLength = sizeof(float) * xmodel.m_vertices.size() * 4u; + weightsBufferView.target = JsonBufferViewTarget::ARRAY_BUFFER; + bufferOffset += weightsBufferView.byteLength; + + m_weights_buffer_view = gltf.bufferViews->size(); + gltf.bufferViews->emplace_back(weightsBufferView); + } + m_first_index_buffer_view = gltf.bufferViews->size(); for (const auto& object : xmodel.m_objects) { @@ -307,6 +346,25 @@ namespace m_uv_accessor = gltf.accessors->size(); gltf.accessors->emplace_back(uvAccessor); + if (!xmodel.m_vertex_bone_weights.empty()) + { + JsonAccessor jointsAccessor; + jointsAccessor.bufferView = m_joints_buffer_view; + jointsAccessor.componentType = JsonAccessorComponentType::UNSIGNED_BYTE; + jointsAccessor.count = xmodel.m_vertices.size(); + jointsAccessor.type = JsonAccessorType::VEC4; + m_joints_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(jointsAccessor); + + JsonAccessor weightsAccessor; + weightsAccessor.bufferView = m_weights_buffer_view; + weightsAccessor.componentType = JsonAccessorComponentType::FLOAT; + weightsAccessor.count = xmodel.m_vertices.size(); + weightsAccessor.type = JsonAccessorType::VEC4; + m_weights_accessor = gltf.accessors->size(); + gltf.accessors->emplace_back(weightsAccessor); + } + m_first_index_accessor = gltf.accessors->size(); for (auto i = 0u; i < xmodel.m_objects.size(); i++) { @@ -377,6 +435,37 @@ namespace gltf.accessors.value()[m_position_accessor].max = std::vector({maxPosition[0], maxPosition[1], maxPosition[2]}); } + if (!xmodel.m_vertex_bone_weights.empty()) + { + assert(xmodel.m_vertex_bone_weights.size() == xmodel.m_vertices.size()); + + auto* joints = reinterpret_cast(&bufferData[currentBufferOffset]); + auto* weights = reinterpret_cast(&bufferData[currentBufferOffset + sizeof(uint8_t) * 4u * xmodel.m_vertex_bone_weights.size()]); + for (const auto& commonVertexWeights : xmodel.m_vertex_bone_weights) + { + assert(commonVertexWeights.weights != nullptr); + assert(commonVertexWeights.weightCount <= 4u); + + const auto commonVertexWeightCount = std::min(commonVertexWeights.weightCount, 4u); + for (auto i = 0u; i < commonVertexWeightCount; i++) + { + joints[i] = static_cast(commonVertexWeights.weights[i].boneIndex); + weights[i] = commonVertexWeights.weights[i].weight; + } + + for (auto i = commonVertexWeightCount; i < 4u; i++) + { + joints[i] = 0u; + weights[i] = 0.0f; + } + + joints += 4u; + weights += 4u; + } + + currentBufferOffset += (sizeof(uint8_t) + sizeof(float)) * 4u * xmodel.m_vertex_bone_weights.size(); + } + for (const auto& object : xmodel.m_objects) { for (const auto& face : object.m_faces) @@ -399,6 +488,11 @@ namespace result += xmodel.m_vertices.size() * sizeof(GltfVertex); + if (!xmodel.m_vertex_bone_weights.empty()) + { + result += xmodel.m_vertices.size() * 4u * (sizeof(uint8_t) + sizeof(float)); + } + for (const auto& object : xmodel.m_objects) { result += object.m_faces.size() * sizeof(unsigned short) * 3u; @@ -421,11 +515,16 @@ namespace gltf.buffers->emplace_back(std::move(jsonBuffer)); } - unsigned m_skeleton_node = 0u; + unsigned m_mesh_node = 0u; + unsigned m_first_bone_node = 0u; unsigned m_position_accessor = 0u; unsigned m_normal_accessor = 0u; unsigned m_uv_accessor = 0u; + unsigned m_joints_accessor = 0u; + unsigned m_weights_accessor = 0u; unsigned m_vertex_buffer_view = 0u; + unsigned m_joints_buffer_view = 0u; + unsigned m_weights_buffer_view = 0u; unsigned m_first_index_buffer_view = 0u; unsigned m_first_index_accessor = 0u; diff --git a/src/Utils/Math/Quaternion.h b/src/Utils/Math/Quaternion.h index 1090f96e..f2a88e55 100644 --- a/src/Utils/Math/Quaternion.h +++ b/src/Utils/Math/Quaternion.h @@ -51,6 +51,96 @@ public: return Matrix(m00, m01, m02, 0, m10, m11, m12, 0, m20, m21, m22, 0, 0, 0, 0, T(1.0)); } + + static T dot(const Quaternion& q1, const Quaternion& q2) + { + return static_cast((q1.m_x * q2.m_x) + (q1.m_y * q2.m_y) + (q1.m_z * q2.m_z) + (q1.m_w * q2.m_w)); + } + + T lengthSquared() + { + return Quaternion::dot(*this, *this); + } + + T length() + { + return sqrt(lengthSquared()); + } + + void Normalize() + { + const auto l = length(); + + // return if no magnitude (already as normalized as possible) + if (l < static_cast(0.0001)) + return; + + T inverseLength = static_cast(1.0) / l; + m_x *= inverseLength; + m_y *= inverseLength; + m_z *= inverseLength; + m_w *= inverseLength; + } + + friend Quaternion operator+(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z, lhs.m_w + rhs.m_w); + } + + friend Quaternion operator-(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z, lhs.m_w - rhs.m_w); + } + + friend Quaternion& operator+=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x += rhs.m_x; + lhs.m_y += rhs.m_y; + lhs.m_z += rhs.m_z; + lhs.m_w += rhs.m_w; + + return lhs; + } + + friend Quaternion& operator-=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x -= rhs.m_x; + lhs.m_y -= rhs.m_y; + lhs.m_z -= rhs.m_z; + lhs.m_w -= rhs.m_w; + + return lhs; + } + + friend Quaternion operator*(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x + rhs.m_x, lhs.m_y + rhs.m_y, lhs.m_z + rhs.m_z, lhs.m_w + rhs.m_w); + } + + friend Quaternion operator/(const Quaternion& lhs, const Quaternion& rhs) + { + return Quaternion(lhs.m_x - rhs.m_x, lhs.m_y - rhs.m_y, lhs.m_z - rhs.m_z, lhs.m_w - rhs.m_w); + } + + friend Quaternion& operator*=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x += rhs.m_x; + lhs.m_y += rhs.m_y; + lhs.m_z += rhs.m_z; + lhs.m_w += rhs.m_w; + + return lhs; + } + + friend Quaternion& operator/=(Quaternion& lhs, const Quaternion& rhs) + { + lhs.m_x -= rhs.m_x; + lhs.m_y -= rhs.m_y; + lhs.m_z -= rhs.m_z; + lhs.m_w -= rhs.m_w; + + return lhs; + } }; typedef Quaternion Quaternion32; diff --git a/src/Utils/Math/Vector.h b/src/Utils/Math/Vector.h index 2a188a1d..60092681 100644 --- a/src/Utils/Math/Vector.h +++ b/src/Utils/Math/Vector.h @@ -24,6 +24,12 @@ public: { } + ~Vector2() = default; + Vector2(const Vector2& other) = default; + Vector2(Vector2&& other) noexcept = default; + Vector2& operator=(const Vector2& other) = default; + Vector2& operator=(Vector2&& other) noexcept = default; + _NODISCARD T& operator()(const size_t index) { assert(index < 2); @@ -55,6 +61,32 @@ public: { return m_value[1]; } + + friend Vector2 operator+(const Vector2& lhs, const Vector2& rhs) + { + return Vector2(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1]); + } + + friend Vector2 operator-(const Vector2& lhs, const Vector2& rhs) + { + return Vector2(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1]); + } + + friend Vector2& operator+=(Vector2& lhs, const Vector2& rhs) + { + lhs.m_value[0] += rhs.m_value[0]; + lhs.m_value[1] += rhs.m_value[1]; + + return lhs; + } + + friend Vector2& operator-=(Vector2& lhs, const Vector2& rhs) + { + lhs.m_value[0] -= rhs.m_value[0]; + lhs.m_value[1] -= rhs.m_value[1]; + + return lhs; + } }; typedef Vector2 Vector2f; @@ -80,6 +112,12 @@ public: { } + ~Vector3() = default; + Vector3(const Vector3& other) = default; + Vector3(Vector3&& other) noexcept = default; + Vector3& operator=(const Vector3& other) = default; + Vector3& operator=(Vector3&& other) noexcept = default; + _NODISCARD T& operator()(const size_t index) { assert(index < 3); @@ -151,6 +189,34 @@ public: { return m_value[2]; } + + friend Vector3 operator+(const Vector3& lhs, const Vector3& rhs) + { + return Vector3(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1], lhs.m_value[2] + rhs.m_value[2]); + } + + friend Vector3 operator-(const Vector3& lhs, const Vector3& rhs) + { + return Vector3(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1], lhs.m_value[2] - rhs.m_value[2]); + } + + friend Vector3& operator+=(Vector3& lhs, const Vector3& rhs) + { + lhs.m_value[0] += rhs.m_value[0]; + lhs.m_value[1] += rhs.m_value[1]; + lhs.m_value[2] += rhs.m_value[2]; + + return lhs; + } + + friend Vector3& operator-=(Vector3& lhs, const Vector3& rhs) + { + lhs.m_value[0] -= rhs.m_value[0]; + lhs.m_value[1] -= rhs.m_value[1]; + lhs.m_value[2] -= rhs.m_value[2]; + + return lhs; + } }; typedef Vector3 Vector3f; @@ -176,6 +242,12 @@ public: { } + ~Vector4() = default; + Vector4(const Vector4& other) = default; + Vector4(Vector4&& other) noexcept = default; + Vector4& operator=(const Vector4& other) = default; + Vector4& operator=(Vector4&& other) noexcept = default; + _NODISCARD T& operator()(const size_t index) { assert(index < 4); @@ -267,6 +339,36 @@ public: { return m_value[3]; } + + friend Vector4 operator+(const Vector4& lhs, const Vector4& rhs) + { + return Vector4(lhs.m_value[0] + rhs.m_value[0], lhs.m_value[1] + rhs.m_value[1], lhs.m_value[2] + rhs.m_value[2], lhs.m_value[3] + rhs.m_value[3]); + } + + friend Vector4 operator-(const Vector4& lhs, const Vector4& rhs) + { + return Vector4(lhs.m_value[0] - rhs.m_value[0], lhs.m_value[1] - rhs.m_value[1], lhs.m_value[2] - rhs.m_value[2], lhs.m_value[3] - rhs.m_value[3]); + } + + friend Vector4& operator+=(Vector4& lhs, const Vector4& rhs) + { + lhs.m_value[0] += rhs.m_value[0]; + lhs.m_value[1] += rhs.m_value[1]; + lhs.m_value[2] += rhs.m_value[2]; + lhs.m_value[3] += rhs.m_value[3]; + + return lhs; + } + + friend Vector4& operator-=(Vector4& lhs, const Vector4& rhs) + { + lhs.m_value[0] -= rhs.m_value[0]; + lhs.m_value[1] -= rhs.m_value[1]; + lhs.m_value[2] -= rhs.m_value[2]; + lhs.m_value[3] -= rhs.m_value[3]; + + return lhs; + } }; typedef Vector4 Vector4f;