diff --git a/src/ObjCommon/Model/Obj/ObjCommon.cpp b/src/ObjCommon/Model/Obj/ObjCommon.cpp new file mode 100644 index 00000000..45b10163 --- /dev/null +++ b/src/ObjCommon/Model/Obj/ObjCommon.cpp @@ -0,0 +1,54 @@ +#include "ObjCommon.h" + +#include +#include + +bool operator==(const ObjVertex& lhs, const ObjVertex& rhs) +{ + return std::fabs(lhs.coordinates[0] - rhs.coordinates[0]) < std::numeric_limits::epsilon() + && std::fabs(lhs.coordinates[1] - rhs.coordinates[1]) < std::numeric_limits::epsilon() + && std::fabs(lhs.coordinates[2] - rhs.coordinates[2]) < std::numeric_limits::epsilon(); +} + +bool operator!=(const ObjVertex& lhs, const ObjVertex& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const ObjVertex& lhs, const ObjVertex& rhs) +{ + return std::tie(lhs.coordinates[0], lhs.coordinates[1], lhs.coordinates[2]) < std::tie(rhs.coordinates[0], rhs.coordinates[1], rhs.coordinates[2]); +} + +bool operator==(const ObjNormal& lhs, const ObjNormal& rhs) +{ + return std::fabs(lhs.normal[0] - rhs.normal[0]) < std::numeric_limits::epsilon() + && std::fabs(lhs.normal[1] - rhs.normal[1]) < std::numeric_limits::epsilon() + && std::fabs(lhs.normal[2] - rhs.normal[2]) < std::numeric_limits::epsilon(); +} + +bool operator!=(const ObjNormal& lhs, const ObjNormal& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const ObjNormal& lhs, const ObjNormal& rhs) +{ + return std::tie(lhs.normal[0], lhs.normal[1], lhs.normal[2]) < std::tie(rhs.normal[0], rhs.normal[1], rhs.normal[2]); +} + +bool operator==(const ObjUv& lhs, const ObjUv& rhs) +{ + return std::fabs(lhs.uv[0] - rhs.uv[0]) < std::numeric_limits::epsilon() + && std::fabs(lhs.uv[1] - rhs.uv[1]) < std::numeric_limits::epsilon(); +} + +bool operator!=(const ObjUv& lhs, const ObjUv& rhs) +{ + return !(lhs == rhs); +} + +bool operator<(const ObjUv& lhs, const ObjUv& rhs) +{ + return std::tie(lhs.uv[0], lhs.uv[1]) < std::tie(rhs.uv[0], rhs.uv[1]); +} diff --git a/src/ObjCommon/Model/Obj/ObjCommon.h b/src/ObjCommon/Model/Obj/ObjCommon.h index e69de29b..911b9e9e 100644 --- a/src/ObjCommon/Model/Obj/ObjCommon.h +++ b/src/ObjCommon/Model/Obj/ObjCommon.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +struct ObjObject +{ + std::string name; + int materialIndex; +}; + +struct ObjVertex +{ + float coordinates[3]; + + friend bool operator==(const ObjVertex& lhs, const ObjVertex& rhs); + friend bool operator!=(const ObjVertex& lhs, const ObjVertex& rhs); + friend bool operator<(const ObjVertex& lhs, const ObjVertex& rhs); +}; + +struct ObjNormal +{ + float normal[3]; + + friend bool operator==(const ObjNormal& lhs, const ObjNormal& rhs); + friend bool operator!=(const ObjNormal& lhs, const ObjNormal& rhs); + friend bool operator<(const ObjNormal& lhs, const ObjNormal& rhs); +}; + +struct ObjUv +{ + float uv[2]; + + friend bool operator==(const ObjUv& lhs, const ObjUv& rhs); + friend bool operator!=(const ObjUv& lhs, const ObjUv& rhs); + friend bool operator<(const ObjUv& lhs, const ObjUv& rhs); +}; + +struct ObjFace +{ + int vertexIndex[3]; + int normalIndex[3]; + int uvIndex[3]; +}; + +struct MtlMaterial +{ + std::string materialName; + std::string colorMapName; + std::string normalMapName; + std::string specularMapName; +}; \ No newline at end of file diff --git a/src/ObjCommon/Utils/DistinctMapper.h b/src/ObjCommon/Utils/DistinctMapper.h index eadaecf2..c34b1370 100644 --- a/src/ObjCommon/Utils/DistinctMapper.h +++ b/src/ObjCommon/Utils/DistinctMapper.h @@ -5,59 +5,6 @@ #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 { @@ -123,6 +70,16 @@ public: return m_distinct_values; } + _NODISCARD size_t GetInputValueCount() const + { + return m_input_entry_index; + } + + _NODISCARD size_t GetDistinctValueCount() const + { + return m_distinct_entry_index; + } + private: size_t m_input_entry_index; size_t m_distinct_entry_index; diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp index d8df20b7..a7710bfb 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp @@ -1,8 +1,6 @@ #include "AssetDumperXModel.h" #include -#include -#include #include "ObjWriting.h" #include "Game/IW4/CommonIW4.h" @@ -18,151 +16,168 @@ bool AssetDumperXModel::ShouldDump(XAssetInfo* asset) return !asset->m_name.empty() && asset->m_name[0] != ','; } -void AssetDumperXModel::DumpObjMatMaterial(AssetDumpingContext& context, const Material* material, std::ostream& stream) +void AssetDumperXModel::AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model) { - stream << "\n"; - stream << "newmtl " << material->info.name << "\n"; + if (!model->materialHandles) + return; - GfxImage* colorMap = nullptr; - GfxImage* normalMap = nullptr; - GfxImage* specularMap = nullptr; - - for (auto i = 0u; i < material->textureCount; i++) + for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++) { - const auto& texture = material->textureTable[i]; + Material* material = model->materialHandles[surfIndex]; + if (!materialMapper.Add(material)) + continue; - switch (texture.semantic) + MtlMaterial mtl; + mtl.materialName = std::string(material->info.name); + + GfxImage* colorMap = nullptr; + GfxImage* normalMap = nullptr; + GfxImage* specularMap = nullptr; + + for (auto i = 0u; i < material->textureCount; i++) { - case TS_COLOR_MAP: - colorMap = texture.u.image; - break; + const auto& texture = material->textureTable[i]; - case TS_NORMAL_MAP: - normalMap = texture.u.image; - break; + switch (texture.semantic) + { + case TS_COLOR_MAP: + colorMap = texture.u.image; + break; - case TS_SPECULAR_MAP: - specularMap = texture.u.image; - break; + // Disabled due to looking weird in Blender + // case TS_NORMAL_MAP: + // normalMap = texture.u.image; + // break; - default: - break; + case TS_SPECULAR_MAP: + specularMap = texture.u.image; + break; + + default: + break; + } + } + + if (colorMap != nullptr) + mtl.colorMapName = colorMap->name; + if (normalMap != nullptr) + mtl.normalMapName = normalMap->name; + if (specularMap != nullptr) + mtl.specularMapName = specularMap->name; + + writer.AddMaterial(std::move(mtl)); + } +} + +void AssetDumperXModel::AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex) +{ + for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) + { + ObjObject object; + object.name = "surf" + std::to_string(surfIndex); + object.materialIndex = static_cast(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex)); + + writer.AddObject(std::move(object)); + } +} + +void AssetDumperXModel::AddObjVertices(ObjWriter& 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); + + ObjVertex objVertex{}; + ObjNormal objNormal{}; + ObjUv objUv{}; + objVertex.coordinates[0] = v.xyz[0]; + objVertex.coordinates[1] = v.xyz[2]; + objVertex.coordinates[2] = -v.xyz[1]; + objNormal.normal[0] = normalVec[0]; + objNormal.normal[1] = normalVec[2]; + objNormal.normal[2] = -normalVec[1]; + objUv.uv[0] = uv[0]; + objUv.uv[1] = 1.0f - uv[1]; + + writer.AddVertex(static_cast(surfIndex), objVertex); + writer.AddNormal(static_cast(surfIndex), objNormal); + writer.AddUv(static_cast(surfIndex), objUv); } } +} - if (colorMap) - stream << "map_Kd " << colorMap->name << ".dds\n"; +void AssetDumperXModel::AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs) +{ + 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]; - if (normalMap) - stream << "map_bump " << normalMap->name << ".dds\n"; - - if (specularMap) - stream << "map_Ks " << specularMap->name << ".dds\n"; + ObjFace face{}; + face.vertexIndex[0] = tri[2] + surface.baseVertIndex; + face.vertexIndex[1] = tri[1] + surface.baseVertIndex; + face.vertexIndex[2] = tri[0] + surface.baseVertIndex; + face.normalIndex[0] = face.vertexIndex[0]; + face.normalIndex[1] = face.vertexIndex[1]; + face.normalIndex[2] = face.vertexIndex[2]; + face.uvIndex[0] = face.vertexIndex[0]; + face.uvIndex[1] = face.vertexIndex[1]; + face.uvIndex[2] = face.vertexIndex[2]; + writer.AddFace(static_cast(surfIndex), face); + } + } } void AssetDumperXModel::DumpObjMat(AssetDumpingContext& context, XAssetInfo* asset) { const auto* model = asset->Asset(); - const auto matFile = context.OpenAssetFile("xmodelsurfs/" + std::string(model->name) + ".mtl"); + const auto matFile = context.OpenAssetFile("model_export/" + std::string(model->name) + ".mtl"); if (!matFile) return; - auto& stream = *matFile; - stream << "# OpenAssetTools MAT File (IW4)\n"; + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); - if (model->numsurfs == 0 || model->materialHandles == nullptr) - return; - - std::set uniqueMaterials; - for (auto i = 0u; i < model->numsurfs; i++) - { - if (model->materialHandles[i] != nullptr) - uniqueMaterials.emplace(model->materialHandles[i]); - } - - stream << "# Material count: " << uniqueMaterials.size() << "\n"; - - for (const auto* material : uniqueMaterials) - { - DumpObjMatMaterial(context, material, stream); - } + AddObjMaterials(writer, materialMapper, model); + writer.WriteMtl(*matFile); } void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, const unsigned lod) { const auto* model = asset->Asset(); const auto* modelSurfs = model->lodInfo[lod].modelSurfs; - const auto assetFile = context.OpenAssetFile("xmodelsurfs/" + std::string(modelSurfs->name) + ".obj"); + + if (modelSurfs->name[0] == ',' || modelSurfs->surfs == nullptr) + return; + + const auto assetFile = context.OpenAssetFile("model_export/" + std::string(modelSurfs->name) + ".obj"); if (!assetFile) return; - auto& stream = *assetFile; - stream << "# OpenAssetTools OBJ File (IW4)\n"; + ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); + DistinctMapper materialMapper(model->numsurfs); - stream << "mtllib " << model->name << ".mtl\n"; + AddObjMaterials(writer, materialMapper, model); + AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + AddObjVertices(writer, modelSurfs); + AddObjFaces(writer, modelSurfs); - if (model->lodInfo[lod].modelSurfs == nullptr || model->lodInfo[lod].modelSurfs->surfs == nullptr) - return; - - for (auto i = 0; i < model->lodInfo[lod].numsurfs; i++) - { - const auto* surf = &modelSurfs->surfs[i]; - - stream << "o surf" << i << "\n"; - - for (auto vi = 0; vi < surf->vertCount; vi++) - { - const auto* vertex = &surf->verts0[vi]; - stream << "v " << vertex->xyz[0] << " " << vertex->xyz[2] << " " << -vertex->xyz[1] << "\n"; - } - - stream << "\n"; - - for (auto vi = 0; vi < surf->vertCount; vi++) - { - const auto* vertex = &surf->verts0[vi]; - vec2_t texCoords; - Common::Vec2UnpackTexCoords(vertex->texCoord, &texCoords); - - stream << "vt " << texCoords[0] << " " << (1.0f - texCoords[1]) << "\n"; - } - - stream << "\n"; - - for (auto vi = 0; vi < surf->vertCount; vi++) - { - const auto* vertex = &surf->verts0[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]) - { - stream << "usemtl " << model->materialHandles[i]->info.name << "\n"; - } - - stream << "\n"; - - for (auto ti = 0; ti < surf->triCount; ti++) - { - const auto* indices = reinterpret_cast(surf->triIndices); - - const auto i0 = surf->baseVertIndex + indices[ti * 3 + 0] + 1; - const auto i1 = surf->baseVertIndex + indices[ti * 3 + 1] + 1; - const auto i2 = surf->baseVertIndex + indices[ti * 3 + 2] + 1; - - stream << "f " << i2 << "/" << i2 << "/" << i2 - << " " << i1 << "/" << i1 << "/" << i1 - << " " << i0 << "/" << i0 << "/" << i0 - << "\n"; - } - } + writer.WriteObj(*assetFile, std::string(model->name) + ".mtl"); } void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo* asset) @@ -176,7 +191,7 @@ void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo } } -void AssetDumperXModel::AddBonesToWriter(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) +void AssetDumperXModel::AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model) { for (auto boneNum = 0u; boneNum < model->numBones; boneNum++) { @@ -224,7 +239,7 @@ void AssetDumperXModel::AddBonesToWriter(const AssetDumpingContext& context, Abs } } -void AssetDumperXModel::AddMaterialsToWriter(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) +void AssetDumperXModel::AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model) { for (auto surfaceMaterialNum = 0; surfaceMaterialNum < model->numsurfs; surfaceMaterialNum++) { @@ -251,7 +266,7 @@ void AssetDumperXModel::AddMaterialsToWriter(AbstractXModelWriter& writer, Disti } } -void AssetDumperXModel::AddObjectsToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) +void AssetDumperXModel::AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -262,7 +277,7 @@ void AssetDumperXModel::AddObjectsToWriter(AbstractXModelWriter& writer, const X } } -void AssetDumperXModel::AddVerticesToWriter(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) +void AssetDumperXModel::AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -298,7 +313,7 @@ void AssetDumperXModel::AddVerticesToWriter(AbstractXModelWriter& writer, const } } -void AssetDumperXModel::AllocateBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) +void AssetDumperXModel::AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) { weightCollection.totalWeightCount = 0u; for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) @@ -322,7 +337,7 @@ void AssetDumperXModel::AllocateBoneWeights(const XModelSurfs* modelSurfs, XMode weightCollection.weights = std::make_unique(weightCollection.totalWeightCount); } -void AssetDumperXModel::AddVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) +void AssetDumperXModel::AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection) { size_t weightOffset = 0u; @@ -484,8 +499,8 @@ void AssetDumperXModel::AddVertexBoneWeights(AbstractXModelWriter& writer, const } } -void AssetDumperXModel::AddFacesToWriter(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, - const int baseSurfaceIndex) +void AssetDumperXModel::AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, + const int baseSurfaceIndex) { for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++) { @@ -521,14 +536,14 @@ void AssetDumperXModel::DumpXModelExportLod(const AssetDumpingContext& context, const auto writer = XModelExportWriter::CreateWriterForVersion6(context.m_zone->m_game->GetShortName(), context.m_zone->m_name); DistinctMapper materialMapper(model->numsurfs); XModelVertexBoneWeightCollection boneWeightCollection; - AllocateBoneWeights(modelSurfs, boneWeightCollection); + AllocateXModelBoneWeights(modelSurfs, boneWeightCollection); - AddBonesToWriter(context, *writer, model); - AddMaterialsToWriter(*writer, materialMapper, model); - AddObjectsToWriter(*writer, modelSurfs); - AddVerticesToWriter(*writer, modelSurfs); - AddVertexBoneWeights(*writer, modelSurfs, boneWeightCollection); - AddFacesToWriter(*writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); + AddXModelBones(context, *writer, model); + AddXModelMaterials(*writer, materialMapper, model); + AddXModelObjects(*writer, modelSurfs); + AddXModelVertices(*writer, modelSurfs); + AddXModelVertexBoneWeights(*writer, modelSurfs, boneWeightCollection); + AddXModelFaces(*writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex); writer->Write(*assetFile); } diff --git a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h index 9aa6dc0f..f8dab979 100644 --- a/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h +++ b/src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h @@ -1,29 +1,31 @@ #pragma once -#include - #include "Dumping/AbstractAssetDumper.h" #include "Game/IW4/IW4.h" -#include "Model/XModel/XModelExportWriter.h" #include "Utils/DistinctMapper.h" +#include "Model/XModel/AbstractXModelWriter.h" +#include "Model/Obj/ObjWriter.h" namespace IW4 { class AssetDumperXModel final : public AbstractAssetDumper { + static void AddObjMaterials(ObjWriter& writer, DistinctMapper& materialMapper, const XModel* model); + static void AddObjObjects(ObjWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); + static void AddObjVertices(ObjWriter& writer, const XModelSurfs* modelSurfs); + static void AddObjFaces(ObjWriter& writer, const XModelSurfs* modelSurfs); static void DumpObjLod(AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); - 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 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 AllocateBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); - static void AddVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); - 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 AddXModelBones(const AssetDumpingContext& context, AbstractXModelWriter& writer, const XModel* model); + static void AddXModelMaterials(AbstractXModelWriter& writer, DistinctMapper& materialMapper, const XModel* model); + static void AddXModelObjects(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); + static void AddXModelVertices(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs); + static void AllocateXModelBoneWeights(const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); + static void AddXModelVertexBoneWeights(AbstractXModelWriter& writer, const XModelSurfs* modelSurfs, XModelVertexBoneWeightCollection& weightCollection); + static void AddXModelFaces(AbstractXModelWriter& writer, const DistinctMapper& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex); + static void DumpXModelExportLod(const AssetDumpingContext& context, XAssetInfo* asset, unsigned lod); static void DumpXModelExport(const AssetDumpingContext& context, XAssetInfo* asset); protected: diff --git a/src/ObjWriting/Model/Obj/ObjWriter.cpp b/src/ObjWriting/Model/Obj/ObjWriter.cpp index e69de29b..c476244d 100644 --- a/src/ObjWriting/Model/Obj/ObjWriter.cpp +++ b/src/ObjWriting/Model/Obj/ObjWriter.cpp @@ -0,0 +1,159 @@ +#include "ObjWriter.h" + +#include "Game/IW4/CommonIW4.h" + +ObjWriter::ObjWriter(std::string gameName, std::string zoneName) + : m_game_name(std::move(gameName)), + m_zone_name(std::move(zoneName)) +{ +} + +void ObjWriter::AddObject(ObjObject object) +{ + m_objects.emplace_back(std::move(object)); + m_object_data.emplace_back(); +} + +void ObjWriter::AddMaterial(MtlMaterial material) +{ + m_materials.emplace_back(std::move(material)); +} + +void ObjWriter::AddVertex(const int objectId, const ObjVertex vertex) +{ + if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) + return; + + m_object_data[objectId].m_vertices.Add(vertex); +} + +void ObjWriter::AddNormal(const int objectId, const ObjNormal normal) +{ + if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) + return; + + m_object_data[objectId].m_normals.Add(normal); +} + +void ObjWriter::AddUv(const int objectId, const ObjUv uv) +{ + if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) + return; + + m_object_data[objectId].m_uvs.Add(uv); +} + +void ObjWriter::AddFace(const int objectId, const ObjFace face) +{ + if (objectId < 0 || static_cast(objectId) >= m_object_data.size()) + return; + + m_object_data[objectId].m_faces.push_back(face); +} + +void ObjWriter::GetObjObjectDataOffsets(std::vector& inputOffsets, std::vector& distinctOffsets) +{ + ObjObjectDataOffsets currentInputOffsets{}; + ObjObjectDataOffsets currentDistinctOffsets{}; + + for (const auto& objectData : m_object_data) + { + inputOffsets.push_back(currentInputOffsets); + distinctOffsets.push_back(currentDistinctOffsets); + + currentInputOffsets.vertexOffset += objectData.m_vertices.GetInputValueCount(); + currentInputOffsets.normalOffset += objectData.m_normals.GetInputValueCount(); + currentInputOffsets.uvOffset += objectData.m_uvs.GetInputValueCount(); + currentDistinctOffsets.vertexOffset += objectData.m_vertices.GetDistinctValueCount(); + currentDistinctOffsets.normalOffset += objectData.m_normals.GetDistinctValueCount(); + currentDistinctOffsets.uvOffset += objectData.m_uvs.GetDistinctValueCount(); + } +} + +void ObjWriter::WriteObj(std::ostream& stream) +{ + WriteObj(stream, std::string()); +} + +void ObjWriter::WriteObj(std::ostream& stream, const std::string& mtlName) +{ + stream << "# OpenAssetTools OBJ File ( " << m_game_name << ")\n"; + stream << "# Game Origin: " << m_game_name << "\n"; + stream << "# Zone Origin: " << m_zone_name << "\n"; + + if (!mtlName.empty()) + stream << "mtllib " << mtlName << "\n"; + + std::vector inputOffsetsByObject; + std::vector distinctOffsetsByObject; + GetObjObjectDataOffsets(inputOffsetsByObject, distinctOffsetsByObject); + + auto objectIndex = 0; + for (const auto& object : m_objects) + { + const auto& objectData = m_object_data[objectIndex]; + stream << "o " << object.name << "\n"; + + for (const auto& v : objectData.m_vertices.GetDistinctValues()) + stream << "v " << v.coordinates[0] << " " << v.coordinates[1] << " " << v.coordinates[2] << "\n"; + for (const auto& uv : objectData.m_uvs.GetDistinctValues()) + stream << "vt " << uv.uv[0] << " " << uv.uv[1] << "\n"; + for (const auto& n : objectData.m_normals.GetDistinctValues()) + stream << "vn " << n.normal[0] << " " << n.normal[1] << " " << n.normal[2] << "\n"; + + if (object.materialIndex >= 0 && static_cast(object.materialIndex) < m_materials.size()) + stream << "usemtl " << m_materials[object.materialIndex].materialName << "\n"; + + for (const auto& f : objectData.m_faces) + { + const size_t v[3] + { + objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[0] - inputOffsetsByObject[objectIndex].vertexOffset) + distinctOffsetsByObject[objectIndex].vertexOffset + 1, + objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[1] - inputOffsetsByObject[objectIndex].vertexOffset) + distinctOffsetsByObject[objectIndex].vertexOffset + 1, + objectData.m_vertices.GetDistinctPositionByInputPosition(f.vertexIndex[2] - inputOffsetsByObject[objectIndex].vertexOffset) + distinctOffsetsByObject[objectIndex].vertexOffset + 1 + }; + const size_t n[3] + { + objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[0] - inputOffsetsByObject[objectIndex].normalOffset) + distinctOffsetsByObject[objectIndex].normalOffset + 1, + objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[1] - inputOffsetsByObject[objectIndex].normalOffset) + distinctOffsetsByObject[objectIndex].normalOffset + 1, + objectData.m_normals.GetDistinctPositionByInputPosition(f.normalIndex[2] - inputOffsetsByObject[objectIndex].normalOffset) + distinctOffsetsByObject[objectIndex].normalOffset + 1 + }; + const size_t uv[3] + { + objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[0] - inputOffsetsByObject[objectIndex].uvOffset) + distinctOffsetsByObject[objectIndex].uvOffset + 1, + objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[1] - inputOffsetsByObject[objectIndex].uvOffset) + distinctOffsetsByObject[objectIndex].uvOffset + 1, + objectData.m_uvs.GetDistinctPositionByInputPosition(f.uvIndex[2] - inputOffsetsByObject[objectIndex].uvOffset) + distinctOffsetsByObject[objectIndex].uvOffset + 1 + }; + + stream << "f " << v[0] << "/" << uv[0] << "/" << n[0] + << " " << v[1] << "/" << uv[1] << "/" << n[1] + << " " << v[2] << "/" << uv[2] << "/" << n[2] + << "\n"; + } + + objectIndex++; + } +} + +void ObjWriter::WriteMtl(std::ostream& stream) +{ + stream << "# OpenAssetTools MAT File ( " << m_game_name << ")\n"; + stream << "# Game Origin: " << m_game_name << "\n"; + stream << "# Zone Origin: " << m_zone_name << "\n"; + stream << "# Material count: " << m_materials.size() << "\n"; + + for (const auto& material : m_materials) + { + stream << "\n"; + stream << "newmtl " << material.materialName << "\n"; + + if (!material.colorMapName.empty()) + stream << "map_Kd ../images/" << material.colorMapName << ".dds\n"; + + if (!material.normalMapName.empty()) + stream << "map_bump ../images/" << material.normalMapName << ".dds\n"; + + if (!material.specularMapName.empty()) + stream << "map_Ks ../images/" << material.specularMapName << ".dds\n"; + } +} diff --git a/src/ObjWriting/Model/Obj/ObjWriter.h b/src/ObjWriting/Model/Obj/ObjWriter.h index e69de29b..5cb1660a 100644 --- a/src/ObjWriting/Model/Obj/ObjWriter.h +++ b/src/ObjWriting/Model/Obj/ObjWriter.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +#include "Model/Obj/ObjCommon.h" +#include "Utils/DistinctMapper.h" + +class ObjWriter +{ +protected: + struct ObjObjectData + { + DistinctMapper m_vertices; + DistinctMapper m_normals; + DistinctMapper m_uvs; + std::vector m_faces; + }; + + struct ObjObjectDataOffsets + { + size_t vertexOffset; + size_t normalOffset; + size_t uvOffset; + }; + + std::string m_game_name; + std::string m_zone_name; + std::vector m_objects; + std::vector m_object_data; + std::vector m_materials; + + void GetObjObjectDataOffsets(std::vector& inputOffsets, std::vector& distinctOffsets); + +public: + ObjWriter(std::string gameName, std::string zoneName); + + void AddObject(ObjObject object); + void AddMaterial(MtlMaterial material); + void AddVertex(int objectId, ObjVertex vertex); + void AddNormal(int objectId, ObjNormal normal); + void AddUv(int objectId, ObjUv uv); + void AddFace(int objectId, ObjFace face); + + void WriteObj(std::ostream& stream); + void WriteObj(std::ostream& stream, const std::string& mtlName); + void WriteMtl(std::ostream& stream); +}; diff --git a/src/Unlinker/UnlinkerArgs.cpp b/src/Unlinker/UnlinkerArgs.cpp index 7691bf7b..ed8e65b6 100644 --- a/src/Unlinker/UnlinkerArgs.cpp +++ b/src/Unlinker/UnlinkerArgs.cpp @@ -162,7 +162,7 @@ bool UnlinkerArgs::SetModelDumpingMode() if (specifiedValue == "obj") { - ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT; + ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ; return true; }