mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-22 09:05:44 +00:00
chore: use XModelCommon for dumping obj to reduce code duplication
This commit is contained in:
parent
45684ac828
commit
a39e993cc6
@ -6,12 +6,6 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct XModelObject
|
||||
{
|
||||
std::string name;
|
||||
int materialIndex;
|
||||
};
|
||||
|
||||
struct XModelBone
|
||||
{
|
||||
std::string name;
|
||||
@ -51,7 +45,6 @@ struct XModelVertex
|
||||
struct XModelFace
|
||||
{
|
||||
int vertexIndex[3];
|
||||
int objectIndex;
|
||||
};
|
||||
|
||||
struct XModelMaterial
|
||||
@ -88,10 +81,19 @@ struct XModelMaterial
|
||||
float blinn[2];
|
||||
float phong;
|
||||
std::string colorMapName;
|
||||
std::string normalMapName;
|
||||
std::string specularMapName;
|
||||
|
||||
void ApplyDefaults();
|
||||
};
|
||||
|
||||
struct XModelObject
|
||||
{
|
||||
std::string name;
|
||||
int materialIndex;
|
||||
std::vector<XModelFace> m_faces;
|
||||
};
|
||||
|
||||
struct XModelCommon
|
||||
{
|
||||
std::vector<XModelObject> m_objects;
|
||||
@ -99,7 +101,6 @@ struct XModelCommon
|
||||
std::vector<XModelMaterial> m_materials;
|
||||
std::vector<XModelVertex> m_vertices;
|
||||
std::vector<XModelVertexBoneWeights> m_vertex_bone_weights;
|
||||
std::vector<XModelFace> m_faces;
|
||||
XModelVertexBoneWeightCollection m_bone_weight_data;
|
||||
};
|
||||
|
||||
|
@ -103,148 +103,6 @@ namespace
|
||||
return potentialTextureDefs[0]->u.image;
|
||||
}
|
||||
|
||||
void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model)
|
||||
{
|
||||
if (!model->materialHandles)
|
||||
return;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++)
|
||||
{
|
||||
Material* material = model->materialHandles[surfIndex];
|
||||
if (!materialMapper.Add(material))
|
||||
continue;
|
||||
|
||||
MtlMaterial mtl;
|
||||
mtl.materialName = std::string(material->info.name);
|
||||
|
||||
GfxImage* colorMap = GetMaterialColorMap(material);
|
||||
GfxImage* normalMap = GetMaterialNormalMap(material);
|
||||
GfxImage* specularMap = GetMaterialSpecularMap(material);
|
||||
|
||||
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 AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
const auto baseSurfIndex = model->lodInfo[lod].surfIndex;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
ObjObject object;
|
||||
object.name = std::format("surf{}", surfIndex);
|
||||
object.materialIndex = static_cast<int>(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex));
|
||||
|
||||
writer.AddObject(std::move(object));
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex];
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
|
||||
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<int>(surfIndex), objVertex);
|
||||
writer.AddNormal(static_cast<int>(surfIndex), objNormal);
|
||||
writer.AddUv(static_cast<int>(surfIndex), objUv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex];
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
|
||||
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<int>(surfIndex), face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!matFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
writer.WriteMtl(*matFile);
|
||||
}
|
||||
|
||||
void DumpObjLod(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj"));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
AddObjObjects(writer, materialMapper, model, lod);
|
||||
AddObjVertices(writer, model, lod);
|
||||
AddObjFaces(writer, model, lod);
|
||||
|
||||
writer.WriteObj(*assetFile, std::format("{}.mtl", model->name));
|
||||
}
|
||||
|
||||
void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model)
|
||||
{
|
||||
for (auto boneNum = 0u; boneNum < model->numBones; boneNum++)
|
||||
@ -307,6 +165,14 @@ namespace
|
||||
if (colorMap)
|
||||
xMaterial.colorMapName = std::string(colorMap->name);
|
||||
|
||||
const auto* normalMap = GetMaterialNormalMap(material);
|
||||
if (normalMap)
|
||||
xMaterial.normalMapName = std::string(normalMap->name);
|
||||
|
||||
const auto* specularMap = GetMaterialSpecularMap(material);
|
||||
if (specularMap)
|
||||
xMaterial.specularMapName = std::string(specularMap->name);
|
||||
|
||||
out.m_materials.emplace_back(std::move(xMaterial));
|
||||
}
|
||||
}
|
||||
@ -517,6 +383,9 @@ namespace
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
auto& object = out.m_objects[surfIndex];
|
||||
object.m_faces.reserve(surface.triCount);
|
||||
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
@ -525,8 +394,7 @@ namespace
|
||||
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<int>(surfIndex);
|
||||
out.m_faces.emplace_back(face);
|
||||
object.m_faces.emplace_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -544,6 +412,35 @@ namespace
|
||||
AddXModelFaces(out, model, lod);
|
||||
}
|
||||
|
||||
void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!mtlFile)
|
||||
return;
|
||||
|
||||
const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj"));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
const auto writer =
|
||||
obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
@ -576,9 +473,6 @@ namespace
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
|
||||
if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ)
|
||||
DumpObjMat(context, asset);
|
||||
|
||||
for (auto currentLod = 0u; currentLod < model->numLods; currentLod++)
|
||||
{
|
||||
XModelCommon common;
|
||||
@ -587,7 +481,9 @@ namespace
|
||||
switch (ObjWriting::Configuration.ModelOutputFormat)
|
||||
{
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ:
|
||||
DumpObjLod(context, asset, currentLod);
|
||||
DumpObjLod(common, context, asset, currentLod);
|
||||
if (currentLod == 0u)
|
||||
DumpObjMtl(common, context, asset);
|
||||
break;
|
||||
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT:
|
||||
|
@ -20,21 +20,6 @@ using namespace IW4;
|
||||
|
||||
namespace
|
||||
{
|
||||
class SurfsDumpingZoneState final : public IZoneAssetDumperState
|
||||
{
|
||||
std::set<const XModelSurfs*> m_dumped_surfs;
|
||||
|
||||
public:
|
||||
bool ShouldDumpTechnique(const XModelSurfs* surfs)
|
||||
{
|
||||
if (m_dumped_surfs.contains(surfs))
|
||||
return false;
|
||||
|
||||
m_dumped_surfs.emplace(surfs);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GfxImage* GetMaterialColorMap(const Material* material)
|
||||
{
|
||||
std::vector<MaterialTextureDef*> potentialTextureDefs;
|
||||
@ -113,144 +98,6 @@ namespace
|
||||
return potentialTextureDefs[0]->u.image;
|
||||
}
|
||||
|
||||
void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model)
|
||||
{
|
||||
if (!model->materialHandles)
|
||||
return;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++)
|
||||
{
|
||||
Material* material = model->materialHandles[surfIndex];
|
||||
if (!materialMapper.Add(material))
|
||||
continue;
|
||||
|
||||
MtlMaterial mtl;
|
||||
mtl.materialName = std::string(material->info.name);
|
||||
|
||||
GfxImage* colorMap = GetMaterialColorMap(material);
|
||||
GfxImage* normalMap = GetMaterialNormalMap(material);
|
||||
GfxImage* specularMap = GetMaterialSpecularMap(material);
|
||||
|
||||
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 AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex)
|
||||
{
|
||||
for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++)
|
||||
{
|
||||
ObjObject object;
|
||||
object.name = std::format("surf{}", surfIndex);
|
||||
object.materialIndex = static_cast<int>(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex));
|
||||
|
||||
writer.AddObject(std::move(object));
|
||||
}
|
||||
}
|
||||
|
||||
void 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;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
|
||||
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<int>(surfIndex), objVertex);
|
||||
writer.AddNormal(static_cast<int>(surfIndex), objNormal);
|
||||
writer.AddUv(static_cast<int>(surfIndex), objUv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void 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];
|
||||
|
||||
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<int>(surfIndex), face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!matFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
writer.WriteMtl(*matFile);
|
||||
}
|
||||
|
||||
void DumpObjLod(const AssetDumpingContext& context, const XAssetInfo<XModel>* 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(std::format("model_export/{}.obj", modelSurfs->name));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex);
|
||||
AddObjVertices(writer, modelSurfs);
|
||||
AddObjFaces(writer, modelSurfs);
|
||||
|
||||
writer.WriteObj(*assetFile, std::format("{}.mtl", model->name));
|
||||
}
|
||||
|
||||
void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model)
|
||||
{
|
||||
for (auto boneNum = 0u; boneNum < model->numBones; boneNum++)
|
||||
@ -313,6 +160,14 @@ namespace
|
||||
if (colorMap)
|
||||
xMaterial.colorMapName = std::string(colorMap->name);
|
||||
|
||||
const auto* normalMap = GetMaterialNormalMap(material);
|
||||
if (normalMap)
|
||||
xMaterial.normalMapName = std::string(normalMap->name);
|
||||
|
||||
const auto* specularMap = GetMaterialSpecularMap(material);
|
||||
if (specularMap)
|
||||
xMaterial.specularMapName = std::string(specularMap->name);
|
||||
|
||||
out.m_materials.emplace_back(std::move(xMaterial));
|
||||
}
|
||||
}
|
||||
@ -508,6 +363,9 @@ namespace
|
||||
for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++)
|
||||
{
|
||||
const auto& surface = modelSurfs->surfs[surfIndex];
|
||||
auto& object = out.m_objects[surfIndex];
|
||||
object.m_faces.reserve(surface.triCount);
|
||||
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
@ -516,8 +374,7 @@ namespace
|
||||
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<int>(surfIndex);
|
||||
out.m_faces.emplace_back(face);
|
||||
object.m_faces.emplace_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -537,6 +394,40 @@ namespace
|
||||
AddXModelFaces(out, modelSurfs);
|
||||
}
|
||||
|
||||
void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!mtlFile)
|
||||
return;
|
||||
|
||||
const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* 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(std::format("model_export/{}.obj", modelSurfs->name));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
const auto writer =
|
||||
obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
@ -571,9 +462,6 @@ namespace
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
|
||||
if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ)
|
||||
DumpObjMat(context, asset);
|
||||
|
||||
for (auto currentLod = 0u; currentLod < model->numLods; currentLod++)
|
||||
{
|
||||
XModelCommon common;
|
||||
@ -582,7 +470,9 @@ namespace
|
||||
switch (ObjWriting::Configuration.ModelOutputFormat)
|
||||
{
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ:
|
||||
DumpObjLod(context, asset, currentLod);
|
||||
DumpObjLod(common, context, asset, currentLod);
|
||||
if (currentLod == 0u)
|
||||
DumpObjMtl(common, context, asset);
|
||||
break;
|
||||
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT:
|
||||
|
@ -20,21 +20,6 @@ using namespace IW5;
|
||||
|
||||
namespace
|
||||
{
|
||||
class SurfsDumpingZoneState final : public IZoneAssetDumperState
|
||||
{
|
||||
std::set<const XModelSurfs*> m_dumped_surfs;
|
||||
|
||||
public:
|
||||
bool ShouldDumpTechnique(const XModelSurfs* surfs)
|
||||
{
|
||||
if (m_dumped_surfs.contains(surfs))
|
||||
return false;
|
||||
|
||||
m_dumped_surfs.emplace(surfs);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
GfxImage* GetMaterialColorMap(const Material* material)
|
||||
{
|
||||
std::vector<MaterialTextureDef*> potentialTextureDefs;
|
||||
@ -113,143 +98,6 @@ namespace
|
||||
return potentialTextureDefs[0]->u.image;
|
||||
}
|
||||
|
||||
void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model)
|
||||
{
|
||||
if (!model->materialHandles)
|
||||
return;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++)
|
||||
{
|
||||
Material* material = model->materialHandles[surfIndex];
|
||||
if (!materialMapper.Add(material))
|
||||
continue;
|
||||
|
||||
MtlMaterial mtl;
|
||||
mtl.materialName = std::string(material->info.name);
|
||||
GfxImage* colorMap = GetMaterialColorMap(material);
|
||||
GfxImage* normalMap = GetMaterialNormalMap(material);
|
||||
GfxImage* specularMap = GetMaterialSpecularMap(material);
|
||||
|
||||
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 AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModelSurfs* modelSurfs, int baseSurfaceIndex)
|
||||
{
|
||||
for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++)
|
||||
{
|
||||
ObjObject object;
|
||||
object.name = std::format("surf{}", surfIndex);
|
||||
object.materialIndex = static_cast<int>(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfaceIndex));
|
||||
|
||||
writer.AddObject(std::move(object));
|
||||
}
|
||||
}
|
||||
|
||||
void 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.packedVerts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
|
||||
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<int>(surfIndex), objVertex);
|
||||
writer.AddNormal(static_cast<int>(surfIndex), objNormal);
|
||||
writer.AddUv(static_cast<int>(surfIndex), objUv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void 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];
|
||||
|
||||
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<int>(surfIndex), face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!matFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
writer.WriteMtl(*matFile);
|
||||
}
|
||||
|
||||
void DumpObjLod(const AssetDumpingContext& context, const XAssetInfo<XModel>* 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(std::format("model_export/{}.obj", modelSurfs->name));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
AddObjObjects(writer, materialMapper, modelSurfs, model->lodInfo[lod].surfIndex);
|
||||
AddObjVertices(writer, modelSurfs);
|
||||
AddObjFaces(writer, modelSurfs);
|
||||
|
||||
writer.WriteObj(*assetFile, std::format("{}.mtl", model->name));
|
||||
}
|
||||
|
||||
void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model)
|
||||
{
|
||||
for (auto boneNum = 0u; boneNum < model->numBones; boneNum++)
|
||||
@ -312,6 +160,14 @@ namespace
|
||||
if (colorMap)
|
||||
xMaterial.colorMapName = std::string(colorMap->name);
|
||||
|
||||
const auto* normalMap = GetMaterialNormalMap(material);
|
||||
if (normalMap)
|
||||
xMaterial.normalMapName = std::string(normalMap->name);
|
||||
|
||||
const auto* specularMap = GetMaterialSpecularMap(material);
|
||||
if (specularMap)
|
||||
xMaterial.specularMapName = std::string(specularMap->name);
|
||||
|
||||
out.m_materials.emplace_back(std::move(xMaterial));
|
||||
}
|
||||
}
|
||||
@ -507,6 +363,9 @@ namespace
|
||||
for (auto surfIndex = 0u; surfIndex < modelSurfs->numsurfs; surfIndex++)
|
||||
{
|
||||
const auto& surface = modelSurfs->surfs[surfIndex];
|
||||
auto& object = out.m_objects[surfIndex];
|
||||
object.m_faces.reserve(surface.triCount);
|
||||
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
@ -515,8 +374,7 @@ namespace
|
||||
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<int>(surfIndex);
|
||||
out.m_faces.emplace_back(face);
|
||||
object.m_faces.emplace_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -536,6 +394,40 @@ namespace
|
||||
AddXModelFaces(out, modelSurfs);
|
||||
}
|
||||
|
||||
void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!mtlFile)
|
||||
return;
|
||||
|
||||
const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* 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(std::format("model_export/{}.obj", modelSurfs->name));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
const auto writer =
|
||||
obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
@ -570,9 +462,6 @@ namespace
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
|
||||
if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ)
|
||||
DumpObjMat(context, asset);
|
||||
|
||||
for (auto currentLod = 0u; currentLod < model->numLods; currentLod++)
|
||||
{
|
||||
XModelCommon common;
|
||||
@ -581,7 +470,9 @@ namespace
|
||||
switch (ObjWriting::Configuration.ModelOutputFormat)
|
||||
{
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ:
|
||||
DumpObjLod(context, asset, currentLod);
|
||||
DumpObjLod(common, context, asset, currentLod);
|
||||
if (currentLod == 0u)
|
||||
DumpObjMtl(common, context, asset);
|
||||
break;
|
||||
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT:
|
||||
|
@ -103,148 +103,6 @@ namespace
|
||||
return potentialTextureDefs[0]->u.image;
|
||||
}
|
||||
|
||||
void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model)
|
||||
{
|
||||
if (!model->materialHandles)
|
||||
return;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++)
|
||||
{
|
||||
Material* material = model->materialHandles[surfIndex];
|
||||
if (!materialMapper.Add(material))
|
||||
continue;
|
||||
|
||||
MtlMaterial mtl;
|
||||
mtl.materialName = std::string(material->info.name);
|
||||
|
||||
GfxImage* colorMap = GetMaterialColorMap(material);
|
||||
GfxImage* normalMap = GetMaterialNormalMap(material);
|
||||
GfxImage* specularMap = GetMaterialSpecularMap(material);
|
||||
|
||||
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 AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
const auto baseSurfIndex = model->lodInfo[lod].surfIndex;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
ObjObject object;
|
||||
object.name = std::format("surf{}", surfIndex);
|
||||
object.materialIndex = static_cast<int>(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex));
|
||||
|
||||
writer.AddObject(std::move(object));
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex];
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv;
|
||||
vec3_t normalVec;
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
|
||||
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<int>(surfIndex), objVertex);
|
||||
writer.AddNormal(static_cast<int>(surfIndex), objNormal);
|
||||
writer.AddUv(static_cast<int>(surfIndex), objUv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex];
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
|
||||
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<int>(surfIndex), face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!matFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
writer.WriteMtl(*matFile);
|
||||
}
|
||||
|
||||
void DumpObjLod(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj"));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
AddObjObjects(writer, materialMapper, model, lod);
|
||||
AddObjVertices(writer, model, lod);
|
||||
AddObjFaces(writer, model, lod);
|
||||
|
||||
writer.WriteObj(*assetFile, std::format("{}.mtl", model->name));
|
||||
}
|
||||
|
||||
void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model)
|
||||
{
|
||||
for (auto boneNum = 0u; boneNum < model->numBones; boneNum++)
|
||||
@ -307,6 +165,14 @@ namespace
|
||||
if (colorMap)
|
||||
xMaterial.colorMapName = std::string(colorMap->name);
|
||||
|
||||
const auto* normalMap = GetMaterialNormalMap(material);
|
||||
if (normalMap)
|
||||
xMaterial.normalMapName = std::string(normalMap->name);
|
||||
|
||||
const auto* specularMap = GetMaterialSpecularMap(material);
|
||||
if (specularMap)
|
||||
xMaterial.specularMapName = std::string(specularMap->name);
|
||||
|
||||
out.m_materials.emplace_back(std::move(xMaterial));
|
||||
}
|
||||
}
|
||||
@ -517,6 +383,9 @@ namespace
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
auto& object = out.m_objects[surfIndex];
|
||||
object.m_faces.reserve(surface.triCount);
|
||||
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
@ -525,8 +394,7 @@ namespace
|
||||
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<int>(surfIndex);
|
||||
out.m_faces.emplace_back(face);
|
||||
object.m_faces.emplace_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -544,6 +412,35 @@ namespace
|
||||
AddXModelFaces(out, model, lod);
|
||||
}
|
||||
|
||||
void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!mtlFile)
|
||||
return;
|
||||
|
||||
const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj"));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
const auto writer =
|
||||
obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
@ -576,9 +473,6 @@ namespace
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
|
||||
if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ)
|
||||
DumpObjMat(context, asset);
|
||||
|
||||
for (auto currentLod = 0u; currentLod < model->numLods; currentLod++)
|
||||
{
|
||||
XModelCommon common;
|
||||
@ -587,7 +481,9 @@ namespace
|
||||
switch (ObjWriting::Configuration.ModelOutputFormat)
|
||||
{
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ:
|
||||
DumpObjLod(context, asset, currentLod);
|
||||
DumpObjLod(common, context, asset, currentLod);
|
||||
if (currentLod == 0u)
|
||||
DumpObjMtl(common, context, asset);
|
||||
break;
|
||||
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT:
|
||||
|
@ -115,148 +115,6 @@ namespace
|
||||
return potentialTextureDefs[0]->image;
|
||||
}
|
||||
|
||||
void AddObjMaterials(ObjWriter& writer, DistinctMapper<Material*>& materialMapper, const XModel* model)
|
||||
{
|
||||
if (!model->materialHandles)
|
||||
return;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < model->numsurfs; surfIndex++)
|
||||
{
|
||||
Material* material = model->materialHandles[surfIndex];
|
||||
if (!materialMapper.Add(material))
|
||||
continue;
|
||||
|
||||
MtlMaterial mtl;
|
||||
mtl.materialName = std::string(material->info.name);
|
||||
|
||||
GfxImage* colorMap = GetMaterialColorMap(material);
|
||||
GfxImage* normalMap = GetMaterialNormalMap(material);
|
||||
GfxImage* specularMap = GetMaterialSpecularMap(material);
|
||||
|
||||
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 AddObjObjects(ObjWriter& writer, const DistinctMapper<Material*>& materialMapper, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
const auto baseSurfIndex = model->lodInfo[lod].surfIndex;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
ObjObject object;
|
||||
object.name = std::format("surf{}", surfIndex);
|
||||
object.materialIndex = static_cast<int>(materialMapper.GetDistinctPositionByInputPosition(surfIndex + baseSurfIndex));
|
||||
|
||||
writer.AddObject(std::move(object));
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjVertices(ObjWriter& writer, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex];
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
|
||||
for (auto vertexIndex = 0u; vertexIndex < surface.vertCount; vertexIndex++)
|
||||
{
|
||||
const auto& v = surface.verts0[vertexIndex];
|
||||
vec2_t uv{};
|
||||
vec3_t normalVec{};
|
||||
|
||||
Common::Vec2UnpackTexCoords(v.texCoord, &uv);
|
||||
Common::Vec3UnpackUnitVec(v.normal, &normalVec);
|
||||
|
||||
ObjVertex objVertex{};
|
||||
ObjNormal objNormal{};
|
||||
ObjUv objUv{};
|
||||
objVertex.coordinates[0] = v.xyz.x;
|
||||
objVertex.coordinates[1] = v.xyz.z;
|
||||
objVertex.coordinates[2] = -v.xyz.y;
|
||||
objNormal.normal[0] = normalVec.x;
|
||||
objNormal.normal[1] = normalVec.z;
|
||||
objNormal.normal[2] = -normalVec.y;
|
||||
objUv.uv[0] = uv.x;
|
||||
objUv.uv[1] = 1.0f - uv.y;
|
||||
|
||||
writer.AddVertex(static_cast<int>(surfIndex), objVertex);
|
||||
writer.AddNormal(static_cast<int>(surfIndex), objNormal);
|
||||
writer.AddUv(static_cast<int>(surfIndex), objUv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AddObjFaces(ObjWriter& writer, const XModel* model, const unsigned lod)
|
||||
{
|
||||
const auto* surfs = &model->surfs[model->lodInfo[lod].surfIndex];
|
||||
const auto surfCount = model->lodInfo[lod].numsurfs;
|
||||
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
|
||||
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<int>(surfIndex), face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DumpObjMat(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto matFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!matFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
writer.WriteMtl(*matFile);
|
||||
}
|
||||
|
||||
void DumpObjLod(const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj"));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
ObjWriter writer(context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
AddObjMaterials(writer, materialMapper, model);
|
||||
AddObjObjects(writer, materialMapper, model, lod);
|
||||
AddObjVertices(writer, model, lod);
|
||||
AddObjFaces(writer, model, lod);
|
||||
|
||||
writer.WriteObj(*assetFile, std::format("{}.mtl", model->name));
|
||||
}
|
||||
|
||||
void AddXModelBones(XModelCommon& out, const AssetDumpingContext& context, const XModel* model)
|
||||
{
|
||||
for (auto boneNum = 0u; boneNum < model->numBones; boneNum++)
|
||||
@ -319,6 +177,14 @@ namespace
|
||||
if (colorMap)
|
||||
xMaterial.colorMapName = std::string(colorMap->name);
|
||||
|
||||
const auto* normalMap = GetMaterialNormalMap(material);
|
||||
if (normalMap)
|
||||
xMaterial.normalMapName = std::string(normalMap->name);
|
||||
|
||||
const auto* specularMap = GetMaterialSpecularMap(material);
|
||||
if (specularMap)
|
||||
xMaterial.specularMapName = std::string(specularMap->name);
|
||||
|
||||
out.m_materials.emplace_back(std::move(xMaterial));
|
||||
}
|
||||
}
|
||||
@ -541,6 +407,9 @@ namespace
|
||||
for (auto surfIndex = 0u; surfIndex < surfCount; surfIndex++)
|
||||
{
|
||||
const auto& surface = surfs[surfIndex];
|
||||
auto& object = out.m_objects[surfIndex];
|
||||
object.m_faces.reserve(surface.triCount);
|
||||
|
||||
for (auto triIndex = 0u; triIndex < surface.triCount; triIndex++)
|
||||
{
|
||||
const auto& tri = surface.triIndices[triIndex];
|
||||
@ -549,8 +418,7 @@ namespace
|
||||
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<int>(surfIndex);
|
||||
out.m_faces.emplace_back(face);
|
||||
object.m_faces.emplace_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -568,6 +436,35 @@ namespace
|
||||
AddXModelFaces(out, model, lod);
|
||||
}
|
||||
|
||||
void DumpObjMtl(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto mtlFile = context.OpenAssetFile(std::format("model_export/{}.mtl", model->name));
|
||||
|
||||
if (!mtlFile)
|
||||
return;
|
||||
|
||||
const auto writer = obj::CreateMtlWriter(*mtlFile, context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpObjLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto assetFile = context.OpenAssetFile(GetFileNameForLod(model->name, lod, ".obj"));
|
||||
|
||||
if (!assetFile)
|
||||
return;
|
||||
|
||||
const auto writer =
|
||||
obj::CreateObjWriter(*assetFile, std::format("{}.mtl", model->name), context.m_zone->m_game->GetShortName(), context.m_zone->m_name);
|
||||
DistinctMapper<Material*> materialMapper(model->numsurfs);
|
||||
|
||||
writer->Write(common);
|
||||
}
|
||||
|
||||
void DumpXModelExportLod(const XModelCommon& common, const AssetDumpingContext& context, const XAssetInfo<XModel>* asset, const unsigned lod)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
@ -600,9 +497,6 @@ namespace
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
|
||||
if (ObjWriting::Configuration.ModelOutputFormat == ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ)
|
||||
DumpObjMat(context, asset);
|
||||
|
||||
for (auto currentLod = 0u; currentLod < model->numLods; currentLod++)
|
||||
{
|
||||
XModelCommon common;
|
||||
@ -611,7 +505,9 @@ namespace
|
||||
switch (ObjWriting::Configuration.ModelOutputFormat)
|
||||
{
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::OBJ:
|
||||
DumpObjLod(context, asset, currentLod);
|
||||
DumpObjLod(common, context, asset, currentLod);
|
||||
if (currentLod == 0u)
|
||||
DumpObjMtl(common, context, asset);
|
||||
break;
|
||||
|
||||
case ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT:
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user