From 767daca2eac2a141b2e112d7a2a487959fcd31bb Mon Sep 17 00:00:00 2001 From: Jan Date: Fri, 13 Aug 2021 09:40:12 +0200 Subject: [PATCH] Add generic XModel Export dumper without bone support yet --- src/Common/Game/IW4/CommonIW4.cpp | 10 + src/Common/Game/IW4/CommonIW4.h | 2 + src/Common/Game/IW4/IW4_Assets.h | 5 +- src/Common/Utils/HalfFloat.cpp | 24 ++ src/Common/Utils/HalfFloat.h | 14 ++ src/Common/Utils/Pack.cpp | 42 ++-- src/Common/Utils/Pack.h | 4 + src/Common/Utils/QuatInt16.cpp | 11 + src/Common/Utils/QuatInt16.h | 14 ++ src/ObjCommon/Model/Obj/ObjCommon.h | 0 src/ObjCommon/Model/VertexMerger.cpp | 63 +++++ src/ObjCommon/Model/VertexMerger.h | 51 ++++ src/ObjCommon/Model/XModel/XModelCommon.cpp | 44 ++++ src/ObjCommon/Model/XModel/XModelCommon.h | 66 +++++ src/ObjCommon/Utils/DistinctMapper.h | 133 ++++++++++ .../IW4/AssetDumpers/AssetDumperXModel.cpp | 200 ++++++++++++++- .../Game/IW4/AssetDumpers/AssetDumperXModel.h | 12 +- src/ObjWriting/Model/Obj/ObjWriter.cpp | 0 src/ObjWriting/Model/Obj/ObjWriter.h | 0 .../Model/XModel/AbstractXModelWriter.cpp | 29 +++ .../Model/XModel/AbstractXModelWriter.h | 24 ++ .../Model/XModel/XModelBinWriter.cpp | 0 src/ObjWriting/Model/XModel/XModelBinWriter.h | 0 .../Model/XModel/XModelExportWriter.cpp | 231 ++++++++++++++++++ .../Model/XModel/XModelExportWriter.h | 21 ++ src/Utils/Math/Matrix.h | 39 +++ src/Utils/Math/Quaternion.h | 63 +++++ src/Utils/Math/Vector.h | 40 +++ 28 files changed, 1112 insertions(+), 30 deletions(-) create mode 100644 src/Common/Utils/HalfFloat.cpp create mode 100644 src/Common/Utils/HalfFloat.h create mode 100644 src/Common/Utils/QuatInt16.cpp create mode 100644 src/Common/Utils/QuatInt16.h create mode 100644 src/ObjCommon/Model/Obj/ObjCommon.h create mode 100644 src/ObjCommon/Model/VertexMerger.cpp create mode 100644 src/ObjCommon/Model/VertexMerger.h create mode 100644 src/ObjCommon/Model/XModel/XModelCommon.cpp create mode 100644 src/ObjCommon/Model/XModel/XModelCommon.h create mode 100644 src/ObjCommon/Utils/DistinctMapper.h create mode 100644 src/ObjWriting/Model/Obj/ObjWriter.cpp create mode 100644 src/ObjWriting/Model/Obj/ObjWriter.h create mode 100644 src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp create mode 100644 src/ObjWriting/Model/XModel/AbstractXModelWriter.h create mode 100644 src/ObjWriting/Model/XModel/XModelBinWriter.cpp create mode 100644 src/ObjWriting/Model/XModel/XModelBinWriter.h create mode 100644 src/ObjWriting/Model/XModel/XModelExportWriter.cpp create mode 100644 src/ObjWriting/Model/XModel/XModelExportWriter.h create mode 100644 src/Utils/Math/Matrix.h create mode 100644 src/Utils/Math/Quaternion.h create mode 100644 src/Utils/Math/Vector.h diff --git a/src/Common/Game/IW4/CommonIW4.cpp b/src/Common/Game/IW4/CommonIW4.cpp index b3240062..c9fc6bd4 100644 --- a/src/Common/Game/IW4/CommonIW4.cpp +++ b/src/Common/Game/IW4/CommonIW4.cpp @@ -14,6 +14,11 @@ PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in) return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast(in))}; } +GfxColor Common::Vec4PackGfxColor(const vec4_t* in) +{ + return GfxColor{Pack32::Vec4PackGfxColor(reinterpret_cast(in))}; +} + void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out) { Pack32::Vec2UnpackTexCoords(in.packed, reinterpret_cast(out)); @@ -23,3 +28,8 @@ void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out) { Pack32::Vec3UnpackUnitVec(in.packed, reinterpret_cast(out)); } + +void Common::Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out) +{ + Pack32::Vec4UnpackGfxColor(in.packed, reinterpret_cast(out)); +} diff --git a/src/Common/Game/IW4/CommonIW4.h b/src/Common/Game/IW4/CommonIW4.h index cc601e8f..8357c5e1 100644 --- a/src/Common/Game/IW4/CommonIW4.h +++ b/src/Common/Game/IW4/CommonIW4.h @@ -200,7 +200,9 @@ namespace IW4 public: static PackedTexCoords Vec2PackTexCoords(const vec2_t* in); static PackedUnitVec Vec3PackUnitVec(const vec3_t* in); + static GfxColor Vec4PackGfxColor(const vec4_t* in); static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out); static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out); + static void Vec4UnpackGfxColor(const GfxColor& in, vec4_t* out); }; } diff --git a/src/Common/Game/IW4/IW4_Assets.h b/src/Common/Game/IW4/IW4_Assets.h index 7202e538..da7d35c7 100644 --- a/src/Common/Game/IW4/IW4_Assets.h +++ b/src/Common/Game/IW4/IW4_Assets.h @@ -163,6 +163,7 @@ namespace IW4 typedef unsigned short r_index_t; typedef float vec2_t[2]; typedef float vec3_t[3]; + typedef float vec4_t[4]; struct PhysPreset { @@ -534,10 +535,10 @@ namespace IW4 float scale; unsigned int noScalePartBits[6]; uint16_t* boneNames; - char* parentList; + unsigned char* parentList; int16_t(*quats)[4]; float(*trans)[3]; - char* partClassification; + unsigned char* partClassification; DObjAnimMat* baseMat; Material** materialHandles; XModelLodInfo lodInfo[4]; diff --git a/src/Common/Utils/HalfFloat.cpp b/src/Common/Utils/HalfFloat.cpp new file mode 100644 index 00000000..1261b4a6 --- /dev/null +++ b/src/Common/Utils/HalfFloat.cpp @@ -0,0 +1,24 @@ +#include "HalfFloat.h" + +float HalfFloat::ToFloat(const half_float_t half) +{ + if (half) + { + union + { + uint32_t u; + float f; + } result{}; + + result.u = ((half << 16) & 0x80000000) | (((((half << 14) & 0xFFFC000) + - (~(half << 14) & 0x10000000)) ^ 0x80000000) >> 1); + return result.f; + } + + return 0.0f; +} + +half_float_t HalfFloat::ToHalf(float f) +{ + return 0; +} diff --git a/src/Common/Utils/HalfFloat.h b/src/Common/Utils/HalfFloat.h new file mode 100644 index 00000000..1729228f --- /dev/null +++ b/src/Common/Utils/HalfFloat.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef uint16_t half_float_t; + +class HalfFloat +{ + HalfFloat() = default; + +public: + static float ToFloat(half_float_t half); + static half_float_t ToHalf(float f); +}; \ No newline at end of file diff --git a/src/Common/Utils/Pack.cpp b/src/Common/Utils/Pack.cpp index b13f3a70..5681e963 100644 --- a/src/Common/Utils/Pack.cpp +++ b/src/Common/Utils/Pack.cpp @@ -1,7 +1,10 @@ #include "Pack.h" +#include #include +#include "HalfFloat.h" + union PackUtil32 { uint32_t u; @@ -13,8 +16,8 @@ union PackUtil32 uint32_t Pack32::Vec2PackTexCoords(const float* in) { - // TODO - return 0; + return static_cast(HalfFloat::ToHalf(in[0])) << 16 + | HalfFloat::ToHalf(in[1]); } uint32_t Pack32::Vec3PackUnitVec(const float* in) @@ -23,26 +26,23 @@ uint32_t Pack32::Vec3PackUnitVec(const float* in) return 0; } +uint32_t Pack32::Vec4PackGfxColor(const float* in) +{ + return static_cast(std::clamp(in[0], 0.0f, 1.0f) * 255.0f) + | static_cast(std::clamp(in[1], 0.0f, 1.0f) * 255.0f) << 8 + | static_cast(std::clamp(in[2], 0.0f, 1.0f) * 255.0f) << 16 + | static_cast(std::clamp(in[3], 0.0f, 1.0f) * 255.0f) << 24; +} + void Pack32::Vec2UnpackTexCoords(const uint32_t in, float* out) { PackUtil32 packTemp{}; - const auto inHiDw = (in >> 16) & UINT16_MAX; - const auto inLoDw = in & UINT16_MAX; + const auto inHiDw = static_cast((in >> 16) & UINT16_MAX); + const auto inLoDw = static_cast(in & UINT16_MAX); - if (inHiDw) - packTemp.u = ((inHiDw << 16) & 0x80000000) | (((((inHiDw << 14) & 0xFFFC000) - - (~(inHiDw << 14) & 0x10000000)) ^ 0x80000000) >> 1); - else - packTemp.f = 0.0f; - out[0] = packTemp.f; - - if (inLoDw) - packTemp.u = ((inLoDw << 16) & 0x80000000) | (((((inLoDw << 14) & 0xFFFC000) - - (~(inLoDw << 14) & 0x10000000)) ^ 0x80000000) >> 1); - else - packTemp.f = 0.0f; - out[1] = packTemp.f; + out[0] = HalfFloat::ToFloat(inHiDw); + out[1] = HalfFloat::ToFloat(inLoDw); } void Pack32::Vec3UnpackUnitVec(const uint32_t in, float* out) @@ -55,3 +55,11 @@ void Pack32::Vec3UnpackUnitVec(const uint32_t in, float* out) out[1] = (static_cast(_in.uc[1]) + -127.0f) * decodeScale; out[2] = (static_cast(_in.uc[2]) + -127.0f) * decodeScale; } + +void Pack32::Vec4UnpackGfxColor(uint32_t in, float* out) +{ + out[0] = static_cast(in & UINT8_MAX) / 255.0f; + out[1] = static_cast((in >> 8) & UINT8_MAX) / 255.0f; + out[2] = static_cast((in >> 16) & UINT8_MAX) / 255.0f; + out[3] = static_cast((in >> 24) & UINT8_MAX) / 255.0f; +} diff --git a/src/Common/Utils/Pack.h b/src/Common/Utils/Pack.h index 8d558e8b..7745818c 100644 --- a/src/Common/Utils/Pack.h +++ b/src/Common/Utils/Pack.h @@ -4,9 +4,13 @@ class Pack32 { + Pack32() = default; + public: static uint32_t Vec2PackTexCoords(const float* in); static uint32_t Vec3PackUnitVec(const float* in); + static uint32_t Vec4PackGfxColor(const float* in); static void Vec2UnpackTexCoords(uint32_t in, float* out); static void Vec3UnpackUnitVec(uint32_t in, float* out); + static void Vec4UnpackGfxColor(uint32_t in, float* out); }; \ No newline at end of file diff --git a/src/Common/Utils/QuatInt16.cpp b/src/Common/Utils/QuatInt16.cpp new file mode 100644 index 00000000..cc0933f2 --- /dev/null +++ b/src/Common/Utils/QuatInt16.cpp @@ -0,0 +1,11 @@ +#include "QuatInt16.h" + +quat_int_16 QuatInt16::ToInt16(const float quat) +{ + return static_cast(quat * INT16_MAX); +} + +float QuatInt16::ToFloat(const quat_int_16 quat) +{ + return static_cast(quat) / static_cast(INT16_MAX); +} diff --git a/src/Common/Utils/QuatInt16.h b/src/Common/Utils/QuatInt16.h new file mode 100644 index 00000000..b2f660fd --- /dev/null +++ b/src/Common/Utils/QuatInt16.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +typedef int16_t quat_int_16; + +class QuatInt16 +{ + QuatInt16() = default; + +public: + static quat_int_16 ToInt16(float quat); + static float ToFloat(quat_int_16 quat); +}; \ No newline at end of file diff --git a/src/ObjCommon/Model/Obj/ObjCommon.h b/src/ObjCommon/Model/Obj/ObjCommon.h new file mode 100644 index 00000000..e69de29b diff --git a/src/ObjCommon/Model/VertexMerger.cpp b/src/ObjCommon/Model/VertexMerger.cpp new file mode 100644 index 00000000..71f8acc0 --- /dev/null +++ b/src/ObjCommon/Model/VertexMerger.cpp @@ -0,0 +1,63 @@ +#include "VertexMerger.h" + +#include +#include + +bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs) +{ + return std::fabs(lhs.x - rhs.x) < std::numeric_limits::epsilon() + && std::fabs(lhs.y - rhs.y) < std::numeric_limits::epsilon() + && std::fabs(lhs.z - rhs.z) < std::numeric_limits::epsilon(); +} + +bool operator!=(const VertexMergerPos& lhs, VertexMergerPos& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs) +{ + return std::tie(lhs.x, lhs.y, lhs.z) < std::tie(rhs.x, rhs.y, rhs.z); +} +/* +VertexMerger::VertexMerger() + : m_current_vertex_index(0) +{ +} + +VertexMerger::VertexMerger(const size_t totalVertexCount) + : m_current_vertex_index(0) +{ + m_position_lookup.reserve(totalVertexCount); +} + +void VertexMerger::ProcessVertex(VertexPos pos) +{ + const auto mapEntry = m_vertex_index_map.find(pos); + if (mapEntry == m_vertex_index_map.end()) + { + m_position_lookup.push_back(m_current_vertex_index); + m_unique_vertex_position_indices.push_back(m_current_vertex_index); + m_vertex_index_map.emplace(std::make_pair(pos, m_current_vertex_index)); + } + else + { + m_position_lookup.push_back(mapEntry->second); + } + + m_current_vertex_index++; +} + +size_t VertexMerger::GetUniqueVertexIndexForVertexIndex(const size_t vertexIndex) +{ + if (vertexIndex >= m_position_lookup.size()) + return 0; + + return m_position_lookup[vertexIndex]; +} + +const std::vector& VertexMerger::GetUniqueVertexIndices() const +{ + return m_unique_vertex_position_indices; +} +*/ \ No newline at end of file diff --git a/src/ObjCommon/Model/VertexMerger.h b/src/ObjCommon/Model/VertexMerger.h new file mode 100644 index 00000000..ad877389 --- /dev/null +++ b/src/ObjCommon/Model/VertexMerger.h @@ -0,0 +1,51 @@ +#pragma once + +#include "Utils/DistinctMapper.h" + +struct VertexMergerPos +{ + float x; + float y; + float z; + + friend bool operator==(const VertexMergerPos& lhs, const VertexMergerPos& rhs); + friend bool operator!=(const VertexMergerPos& lhs, const VertexMergerPos& rhs); + friend bool operator<(const VertexMergerPos& lhs, const VertexMergerPos& rhs); +}; + +typedef DistinctMapper VertexMerger; + +/* +#include +#include + +#include "Utils/ClassUtils.h" + +class VertexMerger +{ +public: + struct VertexPos + { + float x; + float y; + float z; + + friend bool operator==(const VertexPos& lhs, const VertexPos& rhs); + friend bool operator!=(const VertexPos& lhs, const VertexPos& rhs); + friend bool operator<(const VertexPos& lhs, const VertexPos& rhs); + }; + + VertexMerger(); + explicit VertexMerger(size_t totalVertexCount); + + void ProcessVertex(VertexPos pos); + size_t GetUniqueVertexIndexForVertexIndex(size_t vertexIndex); + _NODISCARD const std::vector& GetUniqueVertexIndices() const; + +private: + size_t m_current_vertex_index; + std::vector m_unique_vertex_position_indices; + std::vector m_position_lookup; + std::map m_vertex_index_map; +}; +*/ \ No newline at end of file diff --git a/src/ObjCommon/Model/XModel/XModelCommon.cpp b/src/ObjCommon/Model/XModel/XModelCommon.cpp new file mode 100644 index 00000000..e1364fb5 --- /dev/null +++ b/src/ObjCommon/Model/XModel/XModelCommon.cpp @@ -0,0 +1,44 @@ +#include "XModelCommon.h" + +void XModelMaterial::ApplyDefaults() +{ + // Phong = Color, Bump, Spec, CosinePower + // Blinn = Color, Bump, Spec, Eccentricity + // Lambert = Color, Bump + materialTypeName = "Phong"; + color[0] = 0; + color[1] = 0; + color[2] = 0; + color[3] = 1; + transparency[0] = 0; + transparency[1] = 0; + transparency[2] = 0; + transparency[3] = 1; + ambientColor[0] = 0; + ambientColor[1] = 0; + ambientColor[2] = 0; + ambientColor[3] = 1; + incandescence[0] = 0; + incandescence[1] = 0; + incandescence[2] = 0; + incandescence[3] = 1; + coeffs[0] = 0.8f; + coeffs[1] = 0; + glow.x = 0; + glow.y = 0; + refractive.x = 6; + refractive.y = 1; + specularColor[0] = -1; + specularColor[1] = -1; + specularColor[2] = -1; + specularColor[3] = 1; + reflectiveColor[0] = -1; + reflectiveColor[1] = -1; + reflectiveColor[2] = -1; + reflectiveColor[3] = 1; + reflective.x = -1; + reflective.y = -1; + blinn[0] = -1; + blinn[1] = -1; + phong = -1; +} diff --git a/src/ObjCommon/Model/XModel/XModelCommon.h b/src/ObjCommon/Model/XModel/XModelCommon.h new file mode 100644 index 00000000..cfcdab3b --- /dev/null +++ b/src/ObjCommon/Model/XModel/XModelCommon.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include "Math/Quaternion.h" + +struct XModelObject +{ + std::string name; +}; + +struct XModelBone +{ + std::string name; + int parentIndex; + float scale[3]; + float offset[3]; + Quaternion32 rotation; +}; + +struct XModelVertex +{ + float coordinates[3]; + float normal[3]; + float color[4]; + float uv[2]; +}; + +struct XModelFace +{ + int vertexIndex[3]; + int objectIndex; + int materialIndex; +}; + +struct XModelMaterial +{ + std::string name; + std::string materialTypeName; + float color[4]; + float transparency[4]; + float ambientColor[4]; + float incandescence[4]; + float coeffs[2]; + struct { + float x; + int y; + } glow; + struct + { + int x; + float y; + } refractive; + float specularColor[4]; + float reflectiveColor[4]; + struct + { + int x; + float y; + } reflective; + float blinn[2]; + float phong; + std::string colorMapName; + + void ApplyDefaults(); +}; \ No newline at end of file diff --git a/src/ObjCommon/Utils/DistinctMapper.h b/src/ObjCommon/Utils/DistinctMapper.h new file mode 100644 index 00000000..eadaecf2 --- /dev/null +++ b/src/ObjCommon/Utils/DistinctMapper.h @@ -0,0 +1,133 @@ +#pragma once + +#include +#include + +#include "Utils/ClassUtils.h" + +/* +template +class Deduplicator +{ +public: + Deduplicator() + : m_current_entry_index(0) + { + } + + explicit Deduplicator(const size_t totalEntryCount) + : m_current_entry_index(0) + { + m_position_lookup.reserve(totalEntryCount); + } + + bool AddEntry(T pos) + { + const auto mapEntry = m_index_map.find(pos); + if (mapEntry == m_index_map.end()) + { + m_position_lookup.push_back(m_current_entry_index); + m_unique_entry_position_indices.push_back(m_current_entry_index); + m_index_map.emplace(std::make_pair(pos, m_current_entry_index)); + m_current_entry_index++; + return true; + } + + m_position_lookup.push_back(mapEntry->second); + m_current_entry_index++; + return false; + } + + size_t GetUniqueIndexForIndex(const size_t entryIndex) + { + if (entryIndex >= m_position_lookup.size()) + return 0; + + return m_position_lookup[entryIndex]; + } + + _NODISCARD const std::vector& GetUniqueEntryIndices() const + { + return m_unique_entry_position_indices; + } + +private: + size_t m_current_entry_index; + std::vector m_unique_entry_position_indices; + std::vector m_position_lookup; + std::map m_index_map; +};*/ + +template +class DistinctMapper +{ +public: + DistinctMapper() + : m_input_entry_index(0), + m_distinct_entry_index(0) + { + } + + explicit DistinctMapper(const size_t totalInputCount) + : m_input_entry_index(0), + m_distinct_entry_index(0) + { + m_distinct_position_by_input_position.reserve(totalInputCount); + } + + bool Add(T inputValue) + { + const auto mapEntry = m_distinct_position_by_value_map.find(inputValue); + if (mapEntry == m_distinct_position_by_value_map.end()) + { + m_distinct_position_by_input_position.push_back(m_distinct_entry_index); + m_input_position_by_distinct_position.push_back(m_input_entry_index); + m_distinct_values.push_back(inputValue); + m_distinct_position_by_value_map.emplace(std::make_pair(std::move(inputValue), m_distinct_entry_index)); + m_distinct_entry_index++; + m_input_entry_index++; + return true; + } + + m_distinct_position_by_input_position.push_back(mapEntry->second); + m_input_entry_index++; + return false; + } + + _NODISCARD size_t GetDistinctPositionByInputPosition(const size_t inputPosition) const + { + if (inputPosition >= m_distinct_position_by_input_position.size()) + return 0; + + return m_distinct_position_by_input_position[inputPosition]; + } + + _NODISCARD T GetDistinctValueByInputPosition(const size_t inputPosition) const + { + if (inputPosition >= m_distinct_values.size()) + return T{}; + + return m_distinct_values[inputPosition]; + } + + _NODISCARD size_t GetInputPositionByDistinctPosition(const size_t distinctPosition) const + { + if (distinctPosition >= m_input_position_by_distinct_position.size()) + return 0; + + return m_input_position_by_distinct_position[distinctPosition]; + } + + _NODISCARD const std::vector& GetDistinctValues() const + { + return m_distinct_values; + } + +private: + size_t m_input_entry_index; + size_t m_distinct_entry_index; + std::map m_distinct_position_by_value_map; + std::vector m_distinct_position_by_input_position; + std::vector m_input_position_by_distinct_position; + std::vector m_distinct_values; +}; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index 0ed3c636..72b5ae26 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -1,14 +1,21 @@ #include "AssetDumperXModel.h" +#include #include +#include +#include "ObjWriting.h" #include "Game/IW4/CommonIW4.h" +#include "Math/Quaternion.h" +#include "Model/XModel/XModelExportWriter.h" +#include "Utils/HalfFloat.h" +#include "Utils/QuatInt16.h" using namespace IW4; bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) { - return true; + return !asset->m_name.empty() && asset->m_name[0] != ','; } void AssetDumperXModel::DumpObjMatMaterial(AssetDumpingContext& context, const Material* material, std::ostream& stream) @@ -20,7 +27,7 @@ void AssetDumperXModel::DumpObjMatMaterial(AssetDumpingContext& context, const M GfxImage* normalMap = nullptr; GfxImage* specularMap = nullptr; - for(auto i = 0u; i < material->textureCount; i++) + for (auto i = 0u; i < material->textureCount; i++) { const auto& texture = material->textureTable[i]; @@ -70,13 +77,13 @@ void AssetDumperXModel::DumpObjMat(AssetDumpingContext& context, XAssetInfo uniqueMaterials; for (auto i = 0u; i < model->numsurfs; i++) { - if(model->materialHandles[i] != nullptr) + if (model->materialHandles[i] != nullptr) uniqueMaterials.emplace(model->materialHandles[i]); } stream << "# Material count: " << uniqueMaterials.size() << "\n"; - for(const auto* material : uniqueMaterials) + for (const auto* material : uniqueMaterials) { DumpObjMatMaterial(context, material, stream); } @@ -129,13 +136,13 @@ void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfoverts0[vi]; vec3_t normalVec; Common::Vec3UnpackUnitVec(vertex->normal, &normalVec); - + stream << "vn " << normalVec[0] << " " << normalVec[2] << " " << -normalVec[1] << "\n"; } stream << "\n"; - if(model->numsurfs > i && model->materialHandles && model->materialHandles[i]) + if (model->numsurfs > i && model->materialHandles && model->materialHandles[i]) { stream << "usemtl " << model->materialHandles[i]->info.name << "\n"; } @@ -169,15 +176,190 @@ void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo } } -void AssetDumperXModel::DumpXModelExportLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod) +void AssetDumperXModel::AddBonesToWriter(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) { + for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) + { + XModelBone bone; + if (model->boneNames[boneNum] < context.m_zone->m_script_strings.Count()) + bone.name = context.m_zone->m_script_strings[model->boneNames[boneNum]]; + else + bone.name = "INVALID_BONE_NAME"; + + if (boneNum < model->numRootBones) + bone.parentIndex = -1; + else + bone.parentIndex = static_cast(boneNum - static_cast(model->parentList[boneNum - model->numRootBones])); + + bone.scale[0] = 1.0f; + bone.scale[1] = 1.0f; + bone.scale[2] = 1.0f; + + if (boneNum < model->numRootBones) + { + bone.offset[0] = 0; + bone.offset[1] = 0; + bone.offset[2] = 0; + bone.rotation = Quaternion32(0, 0, 0, 1); + } + else + { + bone.offset[0] = model->trans[boneNum - model->numRootBones][0]; + bone.offset[1] = model->trans[boneNum - model->numRootBones][1]; + bone.offset[2] = model->trans[boneNum - model->numRootBones][2]; + bone.rotation = Quaternion32( + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][0]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][1]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][2]), + QuatInt16::ToFloat(model->quats[boneNum - model->numRootBones][3]) + ); + } + + writer.AddBone(std::move(bone)); + } } -void AssetDumperXModel::DumpXModelExport(AssetDumpingContext& context, XAssetInfo* asset) +void AssetDumperXModel::AddMaterialsToWriter(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) { + for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) + { + Material* material = model->materialHandles[surfaceMaterialNum]; + if (materialMapper.Add(material)) + { + XModelMaterial xMaterial; + xMaterial.ApplyDefaults(); + + xMaterial.name = material->info.name; + + for (auto textureIndex = 0; textureIndex < material->textureCount; textureIndex++) + { + const auto* textureTableEntry = &material->textureTable[textureIndex]; + if (textureTableEntry->semantic == TS_COLOR_MAP && textureTableEntry->u.image) + { + xMaterial.colorMapName = textureTableEntry->u.image->name; + break; + } + } + + writer.AddMaterial(std::move(xMaterial)); + } + } +} + +void AssetDumperXModel::AddObjectsToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) +{ + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + XModelObject object; + object.name = "surf" + std::to_string(surfIndex); + + writer.AddObject(std::move(object)); + } +} + +void AssetDumperXModel::AddVerticesToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) +{ + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++) + { + const auto& v = surface.verts0[vertexIndex]; + vec2_t uv; + vec3_t normalVec; + vec4_t color; + + Common::Vec2UnpackTexCoords(v.texCoord, &uv); + Common::Vec3UnpackUnitVec(v.normal, &normalVec); + Common::Vec4UnpackGfxColor(v.color, &color); + + XModelVertex vertex{}; + vertex.coordinates[0] = v.xyz[0]; + vertex.coordinates[1] = v.xyz[1]; + vertex.coordinates[2] = v.xyz[2]; + vertex.normal[0] = normalVec[0]; + vertex.normal[1] = normalVec[1]; + vertex.normal[2] = normalVec[2]; + vertex.color[0] = color[0]; + vertex.color[1] = color[1]; + vertex.color[2] = color[2]; + vertex.color[3] = color[3]; + vertex.uv[0] = uv[0]; + vertex.uv[1] = uv[1]; + writer.AddVertex(vertex); + } + } +} + +void AssetDumperXModel::AddFacesToWriter(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, + const int baseSurfaceIndex) +{ + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + const auto& surface = modelSurfs->surfs[surfIndex]; + for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++) + { + const auto& tri = surface.triIndices[triIndex]; + + XModelFace face{}; + face.vertexIndex[0] = tri[0] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[2] + surface.baseVertIndex; + face.objectIndex = static_cast(surfIndex); + face.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); + writer.AddFace(face); + } + } +} + +void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) +{ + const auto* model = asset->Asset(); + const auto* modelSurfs = model->lodInfo[lod].modelSurfs; + + if (modelSurfs->name[0] == ',' || modelSurfs->surfs == nullptr) + return; + + const auto assetFile = context.OpenAssetFile("model_export/" + std::string(modelSurfs->name) + ".XMODEL_EXPORT"); + + if (!assetFile) + return; + + const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); + + AddBonesToWriter(context, *writer, model); + AddMaterialsToWriter(*writer, materialMapper, model); + AddObjectsToWriter(*writer, modelSurfs); + AddVerticesToWriter(*writer, modelSurfs); + AddFacesToWriter(*writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + + writer->Write(*assetFile); +} + +void AssetDumperXModel::DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset) +{ + const auto* model = asset->Asset(); + for (auto currentLod = 0u; currentLod < model->numLods; currentLod++) + { + DumpXModelExportLod(context, asset, currentLod); + } } void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo* asset) { - DumpObj(context, asset); + switch (ObjWriting::Configuration.ModelOutputFormat) + { + case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ: + DumpObj(context, asset); + break; + + case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT: + DumpXModelExport(context, asset); + break; + + default: + assert(false); + break; + } } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h index 05237c5c..93f09ce3 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h @@ -4,6 +4,8 @@ #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" +#include "Model/XModel/XModelExportWriter.h" +#include "Utils/DistinctMapper.h" namespace IW4 { @@ -13,8 +15,14 @@ namespace IW4 static void DumpObjMatMaterial(AssetDumpingContext& context, const Material* material, std::ostream& stream); static void DumpObjMat(AssetDumpingContext& context, XAssetInfo* asset); static void DumpObj(AssetDumpingContext& context, XAssetInfo* asset); - static void DumpXModelExportLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - static void DumpXModelExport(AssetDumpingContext& context, XAssetInfo* asset); + + static void AddBonesToWriter(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); + static void AddMaterialsToWriter(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); + static void AddObjectsToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); + static void AddVerticesToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); + static void AddFacesToWriter(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); + static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod); + static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); protected: bool ShouldDump(XAssetInfo* asset) override; diff --git a/src/ObjWriting/Model/Obj/ObjWriter.cpp b/src/ObjWriting/Model/Obj/ObjWriter.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/ObjWriting/Model/Obj/ObjWriter.h b/src/ObjWriting/Model/Obj/ObjWriter.h new file mode 100644 index 00000000..e69de29b diff --git a/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp b/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp new file mode 100644 index 00000000..def57b9a --- /dev/null +++ b/src/ObjWriting/Model/XModel/AbstractXModelWriter.cpp @@ -0,0 +1,29 @@ +#include "AbstractXModelWriter.h" + +AbstractXModelWriter::AbstractXModelWriter() += default; + +void AbstractXModelWriter::AddObject(XModelObject object) +{ + m_objects.emplace_back(std::move(object)); +} + +void AbstractXModelWriter::AddBone(XModelBone bone) +{ + m_bones.emplace_back(std::move(bone)); +} + +void AbstractXModelWriter::AddMaterial(XModelMaterial material) +{ + m_materials.emplace_back(std::move(material)); +} + +void AbstractXModelWriter::AddVertex(XModelVertex vertex) +{ + m_vertices.emplace_back(vertex); +} + +void AbstractXModelWriter::AddFace(XModelFace face) +{ + m_faces.emplace_back(face); +} diff --git a/src/ObjWriting/Model/XModel/AbstractXModelWriter.h b/src/ObjWriting/Model/XModel/AbstractXModelWriter.h new file mode 100644 index 00000000..2a332531 --- /dev/null +++ b/src/ObjWriting/Model/XModel/AbstractXModelWriter.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "Model/XModel/XModelCommon.h" + +class AbstractXModelWriter +{ +protected: + std::vector m_objects; + std::vector m_bones; + std::vector m_materials; + std::vector m_vertices; + std::vector m_faces; + +public: + AbstractXModelWriter(); + + void AddObject(XModelObject object); + void AddBone(XModelBone bone); + void AddMaterial(XModelMaterial material); + void AddVertex(XModelVertex vertex); + void AddFace(XModelFace face); +}; diff --git a/src/ObjWriting/Model/XModel/XModelBinWriter.cpp b/src/ObjWriting/Model/XModel/XModelBinWriter.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/ObjWriting/Model/XModel/XModelBinWriter.h b/src/ObjWriting/Model/XModel/XModelBinWriter.h new file mode 100644 index 00000000..e69de29b diff --git a/src/ObjWriting/Model/XModel/XModelExportWriter.cpp b/src/ObjWriting/Model/XModel/XModelExportWriter.cpp new file mode 100644 index 00000000..d470869c --- /dev/null +++ b/src/ObjWriting/Model/XModel/XModelExportWriter.cpp @@ -0,0 +1,231 @@ +#include "XModelExportWriter.h" + +#include + +#include "Model/VertexMerger.h" +#include "Math/Quaternion.h" + +class XModelExportWriterBase : public XModelExportWriter +{ +protected: + std::string m_game_name; + std::string m_zone_name; + + VertexMerger m_vertex_merger; + + void PrepareVertexMerger() + { + m_vertex_merger = VertexMerger(m_vertices.size()); + for (const auto& vertex : m_vertices) + { + m_vertex_merger.Add(VertexMergerPos{ + vertex.coordinates[0], + vertex.coordinates[1], + vertex.coordinates[2] + }); + } + } + + void WriteHeader(std::ostream& stream, const int version) const + { + stream << "// OpenAssetTools XMODEL_EXPORT File\n"; + stream << "// Game Origin: " << m_game_name << "\n"; + stream << "// Zone Origin: " << m_zone_name << "\n"; + stream << "MODEL\n"; + stream << "VERSION " << version << "\n"; + stream << "\n"; + } + + void WriteBones(std::ostream& stream) const + { + stream << "NUMBONES " << m_bones.size() << "\n"; + size_t boneNum = 0u; + for (const auto& bone : m_bones) + { + stream << "BONE " << boneNum << " "; + if (bone.parentIndex < 0) + stream << "-1"; + else + stream << bone.parentIndex; + + stream << " \"" << bone.name << "\"\n"; + boneNum++; + } + stream << "\n"; + + boneNum = 0u; + for (const auto& bone : m_bones) + { + stream << "BONE " << boneNum << "\n"; + stream << "OFFSET "; + stream << std::setprecision(6) << std::fixed << bone.offset[0] + << ", " << std::setprecision(6) << std::fixed << bone.offset[1] + << ", " << std::setprecision(6) << std::fixed << bone.offset[2] << "\n"; + + stream << "SCALE "; + stream << std::setprecision(6) << std::fixed << bone.scale[0] + << ", " << std::setprecision(6) << std::fixed << bone.scale[1] + << ", " << std::setprecision(6) << std::fixed << bone.scale[2] << "\n"; + + const Matrix32 mat = bone.rotation.ToMatrix(); + stream << "X " << std::setprecision(6) << std::fixed << mat.m_data[0][0] + << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][0] + << ", " << std::setprecision(6) << std::fixed << mat.m_data[2][0] << "\n"; + stream << "Y " << std::setprecision(6) << std::fixed << mat.m_data[0][1] + << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][1] + << ", " << std::setprecision(6) << std::fixed << mat.m_data[2][1] << "\n"; + stream << "Z " << std::setprecision(6) << std::fixed << mat.m_data[0][2] + << ", " << std::setprecision(6) << std::fixed << mat.m_data[1][2] + << ", " << std::setprecision(6) << std::fixed << mat.m_data[2][2] << "\n"; + stream << "\n"; + boneNum++; + } + } + + XModelExportWriterBase(std::string gameName, std::string zoneName) + : m_game_name(std::move(gameName)), + m_zone_name(std::move(zoneName)) + { + } +}; + +class XModelExportWriter6 final : public XModelExportWriterBase +{ + void WriteVertices(std::ostream& stream) const + { + const auto& distinctVertexValues = m_vertex_merger.GetDistinctValues(); + stream << "NUMVERTS " << distinctVertexValues.size() << "\n"; + size_t vertexNum = 0u; + for (const auto& vertexPos : distinctVertexValues) + { + stream << "VERT " << vertexNum << "\n"; + stream << "OFFSET "; + stream << std::setprecision(6) << std::fixed << vertexPos.x + << ", " << std::setprecision(6) << std::fixed << vertexPos.y + << ", " << std::setprecision(6) << std::fixed << vertexPos.z << "\n"; + stream << "BONES 1\n"; // TODO: FIXME with bone weights + stream << "BONE 0 1.000000\n"; // TODO: FIXME with bone weights + stream << "\n"; + vertexNum++; + } + } + + static void WriteFaceVertex(std::ostream& stream, const size_t index, const XModelVertex& vertex) + { + stream << "VERT " << index << "\n"; + stream << "NORMAL " << std::setprecision(6) << std::fixed << vertex.normal[0] + << " " << std::setprecision(6) << std::fixed << vertex.normal[1] + << " " << std::setprecision(6) << std::fixed << vertex.normal[2] << "\n"; + stream << "COLOR " << std::setprecision(6) << std::fixed << vertex.color[0] + << " " << std::setprecision(6) << std::fixed << vertex.color[1] + << " " << std::setprecision(6) << std::fixed << vertex.color[2] + << " " << std::setprecision(6) << std::fixed << vertex.color[3] << "\n"; + stream << "UV 1 " << std::setprecision(6) << std::fixed << vertex.uv[0] + << " " << std::setprecision(6) << std::fixed << vertex.uv[1] << "\n"; + } + + void WriteFaces(std::ostream& stream) const + { + stream << "NUMFACES " << m_faces.size() << "\n"; + for (const auto& face : m_faces) + { + const size_t distinctPositions[3] + { + m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[0]), + m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[1]), + m_vertex_merger.GetDistinctPositionByInputPosition(face.vertexIndex[2]) + }; + + const XModelVertex& v0 = m_vertices[face.vertexIndex[0]]; + const XModelVertex& v1 = m_vertices[face.vertexIndex[1]]; + const XModelVertex& v2 = m_vertices[face.vertexIndex[2]]; + + stream << "TRI " << face.objectIndex << " " << face.materialIndex << " 0 0\n"; + WriteFaceVertex(stream, distinctPositions[0], v0); + WriteFaceVertex(stream, distinctPositions[1], v1); + WriteFaceVertex(stream, distinctPositions[2], v2); + stream << "\n"; + } + } + + void WriteObjects(std::ostream& stream) const + { + stream << "NUMOBJECTS " << m_objects.size() << "\n"; + size_t objectNum = 0u; + for (const auto& object : m_objects) + { + stream << "OBJECT " << objectNum << " \"" << object.name << "\"\n"; + objectNum++; + } + stream << "\n"; + } + + void WriteMaterials(std::ostream& stream) const + { + stream << "NUMMATERIALS " << m_materials.size() << "\n"; + size_t materialNum = 0u; + for (const auto& material : m_materials) + { + stream << "MATERIAL " << materialNum << " \"" << material.name << "\" \"" << material.materialTypeName << "\" \"" << material.colorMapName << "\"\n"; + stream << "COLOR " << std::setprecision(6) << std::fixed << material.color[0] + << " " << std::setprecision(6) << std::fixed << material.color[1] + << " " << std::setprecision(6) << std::fixed << material.color[2] + << " " << std::setprecision(6) << std::fixed << material.color[3] << "\n"; + stream << "TRANSPARENCY " << std::setprecision(6) << std::fixed << material.transparency[0] + << " " << std::setprecision(6) << std::fixed << material.transparency[1] + << " " << std::setprecision(6) << std::fixed << material.transparency[2] + << " " << std::setprecision(6) << std::fixed << material.transparency[3] << "\n"; + stream << "AMBIENTCOLOR " << std::setprecision(6) << std::fixed << material.ambientColor[0] + << " " << std::setprecision(6) << std::fixed << material.ambientColor[1] + << " " << std::setprecision(6) << std::fixed << material.ambientColor[2] + << " " << std::setprecision(6) << std::fixed << material.ambientColor[3] << "\n"; + stream << "INCANDESCENCE " << std::setprecision(6) << std::fixed << material.incandescence[0] + << " " << std::setprecision(6) << std::fixed << material.incandescence[1] + << " " << std::setprecision(6) << std::fixed << material.incandescence[2] + << " " << std::setprecision(6) << std::fixed << material.incandescence[3] << "\n"; + stream << "COEFFS " << std::setprecision(6) << std::fixed << material.coeffs[0] + << " " << std::setprecision(6) << std::fixed << material.coeffs[1] << "\n"; + stream << "GLOW " << std::setprecision(6) << std::fixed << material.glow.x + << " " << material.glow.y << "\n"; + stream << "REFRACTIVE " << material.refractive.x + << " " << std::setprecision(6) << std::fixed << material.refractive.y << "\n"; + stream << "SPECULARCOLOR " << std::setprecision(6) << std::fixed << material.specularColor[0] + << " " << std::setprecision(6) << std::fixed << material.specularColor[1] + << " " << std::setprecision(6) << std::fixed << material.specularColor[2] + << " " << std::setprecision(6) << std::fixed << material.specularColor[3] << "\n"; + stream << "REFLECTIVECOLOR " << std::setprecision(6) << std::fixed << material.reflectiveColor[0] + << " " << std::setprecision(6) << std::fixed << material.reflectiveColor[1] + << " " << std::setprecision(6) << std::fixed << material.reflectiveColor[2] + << " " << std::setprecision(6) << std::fixed << material.reflectiveColor[3] << "\n"; + stream << "REFLECTIVE " << material.reflective.x + << " " << std::setprecision(6) << std::fixed << material.reflective.y << "\n"; + stream << "BLINN " << std::setprecision(6) << std::fixed << material.blinn[0] + << " " << std::setprecision(6) << std::fixed << material.blinn[1] << "\n"; + stream << "PHONG " << std::setprecision(6) << std::fixed << material.phong << "\n"; + stream << "\n"; + materialNum++; + } + } + +public: + XModelExportWriter6(std::string gameName, std::string zoneName) + : XModelExportWriterBase(std::move(gameName), std::move(zoneName)) + { + } + + void Write(std::ostream& stream) override + { + PrepareVertexMerger(); + WriteHeader(stream, 6); + WriteBones(stream); + WriteVertices(stream); + WriteFaces(stream); + WriteObjects(stream); + WriteMaterials(stream); + } +}; + +std::unique_ptr XModelExportWriter::CreateWriterForVersion6(std::string gameName, std::string zoneName) +{ + return std::make_unique(std::move(gameName), std::move(zoneName)); +} diff --git a/src/ObjWriting/Model/XModel/XModelExportWriter.h b/src/ObjWriting/Model/XModel/XModelExportWriter.h new file mode 100644 index 00000000..0d4d217d --- /dev/null +++ b/src/ObjWriting/Model/XModel/XModelExportWriter.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +#include "AbstractXModelWriter.h" + +class XModelExportWriter : public AbstractXModelWriter +{ +public: + XModelExportWriter() = default; + virtual ~XModelExportWriter() = default; + XModelExportWriter(const XModelExportWriter& other) = default; + XModelExportWriter(XModelExportWriter&& other) noexcept = default; + XModelExportWriter& operator=(const XModelExportWriter& other) = default; + XModelExportWriter& operator=(XModelExportWriter&& other) noexcept = default; + + virtual void Write(std::ostream& stream) = 0; + + static std::unique_ptr CreateWriterForVersion6(std::string gameName, std::string zoneName); +}; diff --git a/src/Utils/Math/Matrix.h b/src/Utils/Math/Matrix.h new file mode 100644 index 00000000..4f5006e7 --- /dev/null +++ b/src/Utils/Math/Matrix.h @@ -0,0 +1,39 @@ +#pragma once + +template +class Matrix +{ +public: + T m_data[4][4]; + + Matrix() + : m_data{ + {T(1.0), 0, 0, 0}, + {0, T(1.0), 0, 0}, + {0, 0, T(1.0), 0}, + {0, 0, 0, T(1.0)} + } + { + } + + Matrix(T d00, T d01, T d02, T d03, + T d10, T d11, T d12, T d13, + T d20, T d21, T d22, T d23, + T d30, T d31, T d32, T d33) + : m_data{ + {d00, d01, d02, d03}, + {d10, d11, d12, d13}, + {d20, d21, d22, d23}, + {d30, d31, d32, d33} + } + { + } + + static Matrix Identity() + { + return Matrix(); + } +}; + +typedef Matrix Matrix32; +typedef Matrix Matrix64; diff --git a/src/Utils/Math/Quaternion.h b/src/Utils/Math/Quaternion.h new file mode 100644 index 00000000..2d0336ec --- /dev/null +++ b/src/Utils/Math/Quaternion.h @@ -0,0 +1,63 @@ +#pragma once + +#include "Utils/ClassUtils.h" +#include "Matrix.h" + +template +class Quaternion +{ +public: + T m_x; + T m_y; + T m_z; + T m_w; + + Quaternion() + { + m_x = T(0); + m_y = T(0); + m_z = T(0); + m_w = T(1); + } + + Quaternion(T x, T y, T z, T w) + { + m_x = x; + m_y = y; + m_z = z; + m_w = w; + } + + _NODISCARD Matrix ToMatrix() const + { + const T xx = m_x * m_x; + const T xy = m_x * m_y; + const T xz = m_x * m_z; + const T xw = m_x * m_w; + const T yy = m_y * m_y; + const T yz = m_y * m_z; + const T yw = m_y * m_w; + const T zz = m_z * m_z; + const T zw = m_z * m_w; + + const T m00 = 1 - 2 * yy - 2 * zz; + const T m01 = 2 * xy - 2 * zw; + const T m02 = 2 * xz + 2 * yw; + const T m10 = 2 * xy + 2 * zw; + const T m11 = 1 - 2 * xx - 2 * zz; + const T m12 = 2 * yz - 2 * xw; + const T m20 = 2 * xz - 2 * yw; + const T m21 = 2 * yz + 2 * xw; + const T m22 = 1 - 2 * xx - 2 * yy; + + return Matrix( + m00, m01, m02, 0, + m10, m11, m12, 0, + m20, m21, m22, 0, + 0, 0, 0, T(1.0) + ); + } +}; + +typedef Quaternion Quaternion32; +typedef Quaternion Quaternion64; diff --git a/src/Utils/Math/Vector.h b/src/Utils/Math/Vector.h new file mode 100644 index 00000000..20d2ba5e --- /dev/null +++ b/src/Utils/Math/Vector.h @@ -0,0 +1,40 @@ +#pragma once + +template +class Vector3 +{ +public: + union + { + struct + { + T m_x; + T m_y; + T m_z; + }; + T m_data[3]; + } u; +}; + +typedef Vector3 Vector3f; +typedef Vector3 Vector3d; + +template +class Vector4 +{ +public: + union + { + struct + { + T m_x; + T m_y; + T m_z; + T m_w; + }; + T m_data[4]; + } u; +}; + +typedef Vector3 Vector4f; +typedef Vector3 Vector4d; \ No newline at end of file