chore: use XModelCommon for dumping obj to reduce code duplication

This commit is contained in:
Jan
2024-04-28 14:37:21 +02:00
parent 45684ac828
commit a39e993cc6
9 changed files with 477 additions and 965 deletions

View File

@ -116,24 +116,35 @@ class XModelExportWriter6 final : public XModelExportWriterBase
void WriteFaces(const XModelCommon& xmodel) const
{
m_stream << "NUMFACES " << xmodel.m_faces.size() << "\n";
for (const auto& face : xmodel.m_faces)
auto totalFaceCount = 0u;
for (const auto& object : xmodel.m_objects)
totalFaceCount += object.m_faces.size();
m_stream << "NUMFACES " << totalFaceCount << "\n";
auto objectIndex = 0u;
for (const auto& object : xmodel.m_objects)
{
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]),
};
for (const auto& face : object.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 = xmodel.m_vertices[face.vertexIndex[0]];
const XModelVertex& v1 = xmodel.m_vertices[face.vertexIndex[1]];
const XModelVertex& v2 = xmodel.m_vertices[face.vertexIndex[2]];
const XModelVertex& v0 = xmodel.m_vertices[face.vertexIndex[0]];
const XModelVertex& v1 = xmodel.m_vertices[face.vertexIndex[1]];
const XModelVertex& v2 = xmodel.m_vertices[face.vertexIndex[2]];
m_stream << "TRI " << face.objectIndex << " " << xmodel.m_objects[face.objectIndex].materialIndex << " 0 0\n";
WriteFaceVertex(distinctPositions[0], v0);
WriteFaceVertex(distinctPositions[1], v1);
WriteFaceVertex(distinctPositions[2], v2);
m_stream << "\n";
m_stream << "TRI " << objectIndex << " " << object.materialIndex << " 0 0\n";
WriteFaceVertex(distinctPositions[0], v0);
WriteFaceVertex(distinctPositions[1], v1);
WriteFaceVertex(distinctPositions[2], v2);
m_stream << "\n";
}
objectIndex++;
}
}

View File

@ -1,157 +1,225 @@
#include "ObjWriter.h"
#include "Game/IW4/CommonIW4.h"
#include "Utils/DistinctMapper.h"
#include "XModel/Obj/ObjCommon.h"
ObjWriter::ObjWriter(std::string gameName, std::string zoneName)
: m_game_name(std::move(gameName)),
m_zone_name(std::move(zoneName))
namespace
{
}
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<unsigned>(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<unsigned>(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<unsigned>(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<unsigned>(objectId) >= m_object_data.size())
return;
m_object_data[objectId].m_faces.push_back(face);
}
void ObjWriter::GetObjObjectDataOffsets(std::vector<ObjObjectDataOffsets>& inputOffsets, std::vector<ObjObjectDataOffsets>& distinctOffsets)
{
ObjObjectDataOffsets currentInputOffsets{};
ObjObjectDataOffsets currentDistinctOffsets{};
for (const auto& objectData : m_object_data)
struct ObjObjectData
{
inputOffsets.push_back(currentInputOffsets);
distinctOffsets.push_back(currentDistinctOffsets);
DistinctMapper<ObjVertex> m_vertices;
DistinctMapper<ObjNormal> m_normals;
DistinctMapper<ObjUv> m_uvs;
};
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<ObjObjectDataOffsets> inputOffsetsByObject;
std::vector<ObjObjectDataOffsets> distinctOffsetsByObject;
GetObjObjectDataOffsets(inputOffsetsByObject, distinctOffsetsByObject);
auto objectIndex = 0;
for (const auto& object : m_objects)
struct ObjObjectDataOffsets
{
const auto& objectData = m_object_data[objectIndex];
stream << "o " << object.name << "\n";
size_t vertexOffset;
size_t normalOffset;
size_t uvOffset;
};
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<unsigned>(object.materialIndex) < m_materials.size())
stream << "usemtl " << m_materials[object.materialIndex].materialName << "\n";
for (const auto& f : objectData.m_faces)
class ObjWriter final : public XModelWriter
{
public:
ObjWriter(std::ostream& stream, std::string gameName, std::string zoneName, std::string mtlName)
: m_stream(stream),
m_mtl_name(std::move(mtlName)),
m_game_name(std::move(gameName)),
m_zone_name(std::move(zoneName))
{
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 Write(const XModelCommon& xmodel) override
{
m_stream << "# OpenAssetTools OBJ File ( " << m_game_name << ")\n";
m_stream << "# Game Origin: " << m_game_name << "\n";
m_stream << "# Zone Origin: " << m_zone_name << "\n";
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";
if (!m_mtl_name.empty())
m_stream << "mtllib " << m_mtl_name << "\n";
for (const auto& material : m_materials)
std::vector<ObjObjectDataOffsets> inputOffsetsByObject;
std::vector<ObjObjectDataOffsets> distinctOffsetsByObject;
GetObjObjectDataOffsets(xmodel, inputOffsetsByObject, distinctOffsetsByObject);
auto objectIndex = 0;
for (const auto& object : xmodel.m_objects)
{
const auto& objectData = m_object_data[objectIndex];
m_stream << "o " << object.name << "\n";
for (const auto& v : objectData.m_vertices.GetDistinctValues())
m_stream << "v " << v.coordinates[0] << " " << v.coordinates[1] << " " << v.coordinates[2] << "\n";
for (const auto& uv : objectData.m_uvs.GetDistinctValues())
m_stream << "vt " << uv.uv[0] << " " << uv.uv[1] << "\n";
for (const auto& n : objectData.m_normals.GetDistinctValues())
m_stream << "vn " << n.normal[0] << " " << n.normal[1] << " " << n.normal[2] << "\n";
if (object.materialIndex >= 0 && static_cast<unsigned>(object.materialIndex) < xmodel.m_materials.size())
m_stream << "usemtl " << xmodel.m_materials[object.materialIndex].name << "\n";
auto faceIndex = 0u;
for (const auto& f : object.m_faces)
{
const auto faceVertexOffset = 3u * faceIndex;
const size_t v[3]{
objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].vertexOffset + 1,
objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].vertexOffset + 1,
objectData.m_vertices.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].vertexOffset + 1,
};
const size_t n[3]{
objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].normalOffset + 1,
objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].normalOffset + 1,
objectData.m_normals.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].normalOffset + 1,
};
const size_t uv[3]{
objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 0) + distinctOffsetsByObject[objectIndex].uvOffset + 1,
objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 1) + distinctOffsetsByObject[objectIndex].uvOffset + 1,
objectData.m_uvs.GetDistinctPositionByInputPosition(faceVertexOffset + 2) + distinctOffsetsByObject[objectIndex].uvOffset + 1,
};
m_stream << std::format("f {}/{}/{} {}/{}/{} {}/{}/{}\n", v[0], uv[0], n[0], v[1], uv[1], n[1], v[2], uv[2], n[2]);
faceIndex++;
}
objectIndex++;
}
}
void GetObjObjectDataOffsets(const XModelCommon& xmodel,
std::vector<ObjObjectDataOffsets>& inputOffsets,
std::vector<ObjObjectDataOffsets>& distinctOffsets)
{
ObjObjectDataOffsets currentInputOffsets{};
ObjObjectDataOffsets currentDistinctOffsets{};
AddObjFaces(xmodel);
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 AddObjFaces(const XModelCommon& common)
{
m_object_data.resize(common.m_objects.size());
auto objectIndex = 0u;
for (const auto& commonObject : common.m_objects)
{
auto& objectData = m_object_data[objectIndex];
for (const auto& commonFace : commonObject.m_faces)
{
ObjFace face{};
face.vertexIndex[0] = commonFace.vertexIndex[2];
face.vertexIndex[1] = commonFace.vertexIndex[1];
face.vertexIndex[2] = commonFace.vertexIndex[0];
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];
// Reverse order for OBJ
AddObjVertex(objectData, common, commonFace.vertexIndex[2]);
AddObjVertex(objectData, common, commonFace.vertexIndex[1]);
AddObjVertex(objectData, common, commonFace.vertexIndex[0]);
}
objectIndex++;
}
}
static void AddObjVertex(ObjObjectData& objectData, const XModelCommon& common, const int commonVertexIndex)
{
const auto& commonVertex = common.m_vertices[commonVertexIndex];
ObjVertex objVertex{};
objVertex.coordinates[0] = commonVertex.coordinates[0];
objVertex.coordinates[1] = commonVertex.coordinates[2];
objVertex.coordinates[2] = -commonVertex.coordinates[1];
objectData.m_vertices.Add(objVertex);
ObjNormal objNormal{};
objNormal.normal[0] = commonVertex.normal[0];
objNormal.normal[1] = commonVertex.normal[2];
objNormal.normal[2] = -commonVertex.normal[1];
objectData.m_normals.Add(objNormal);
ObjUv objUv{};
objUv.uv[0] = commonVertex.uv[0];
objUv.uv[1] = 1.0f - commonVertex.uv[1];
objectData.m_uvs.Add(objUv);
}
std::ostream& m_stream;
std::string m_mtl_name;
std::string m_game_name;
std::string m_zone_name;
std::vector<ObjObjectData> m_object_data;
};
class MtlWriter final : public XModelWriter
{
stream << "\n";
stream << "newmtl " << material.materialName << "\n";
public:
MtlWriter(std::ostream& stream, std::string gameName, std::string zoneName)
: m_stream(stream),
m_game_name(std::move(gameName)),
m_zone_name(std::move(zoneName))
{
}
if (!material.colorMapName.empty())
stream << "map_Kd ../images/" << material.colorMapName << ".dds\n";
void Write(const XModelCommon& xmodel) override
{
m_stream << "# OpenAssetTools MAT File ( " << m_game_name << ")\n";
m_stream << "# Game Origin: " << m_game_name << "\n";
m_stream << "# Zone Origin: " << m_zone_name << "\n";
m_stream << "# Material count: " << xmodel.m_materials.size() << "\n";
if (!material.normalMapName.empty())
stream << "map_bump ../images/" << material.normalMapName << ".dds\n";
for (const auto& material : xmodel.m_materials)
{
m_stream << "\n";
m_stream << "newmtl " << material.name << "\n";
if (!material.specularMapName.empty())
stream << "map_Ks ../images/" << material.specularMapName << ".dds\n";
if (!material.colorMapName.empty())
m_stream << "map_Kd ../images/" << material.colorMapName << ".dds\n";
if (!material.normalMapName.empty())
m_stream << "map_bump ../images/" << material.normalMapName << ".dds\n";
if (!material.specularMapName.empty())
m_stream << "map_Ks ../images/" << material.specularMapName << ".dds\n";
}
}
private:
std::ostream& m_stream;
std::string m_game_name;
std::string m_zone_name;
};
} // namespace
namespace obj
{
std::unique_ptr<XModelWriter> CreateObjWriter(std::ostream& stream, std::string mtlName, std::string gameName, std::string zoneName)
{
return std::make_unique<ObjWriter>(stream, std::move(mtlName), std::move(gameName), std::move(zoneName));
}
}
std::unique_ptr<XModelWriter> CreateMtlWriter(std::ostream& stream, std::string gameName, std::string zoneName)
{
return std::make_unique<MtlWriter>(stream, std::move(gameName), std::move(zoneName));
}
} // namespace obj

View File

@ -1,48 +1,11 @@
#pragma once
#include "Utils/DistinctMapper.h"
#include "XModel/Obj/ObjCommon.h"
#include "XModel/XModelWriter.h"
#include <ostream>
#include <vector>
class ObjWriter
namespace obj
{
protected:
struct ObjObjectData
{
DistinctMapper<ObjVertex> m_vertices;
DistinctMapper<ObjNormal> m_normals;
DistinctMapper<ObjUv> m_uvs;
std::vector<ObjFace> 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<ObjObject> m_objects;
std::vector<ObjObjectData> m_object_data;
std::vector<MtlMaterial> m_materials;
void GetObjObjectDataOffsets(std::vector<ObjObjectDataOffsets>& inputOffsets, std::vector<ObjObjectDataOffsets>& 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);
};
std::unique_ptr<XModelWriter> CreateObjWriter(std::ostream& stream, std::string mtlName, std::string gameName, std::string zoneName);
std::unique_ptr<XModelWriter> CreateMtlWriter(std::ostream& stream, std::string gameName, std::string zoneName);
} // namespace obj