mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 00:02:55 +00:00
Dump IW4 xmodels as obj
This commit is contained in:
parent
2c96bc5ef8
commit
24145e15e2
@ -0,0 +1,25 @@
|
||||
#include "CommonIW4.h"
|
||||
|
||||
#include "Utils/Pack.h"
|
||||
|
||||
using namespace IW4;
|
||||
|
||||
PackedTexCoords Common::Vec2PackTexCoords(const vec2_t* in)
|
||||
{
|
||||
return PackedTexCoords{Pack32::Vec2PackTexCoords(reinterpret_cast<const float*>(in))};
|
||||
}
|
||||
|
||||
PackedUnitVec Common::Vec3PackUnitVec(const vec3_t* in)
|
||||
{
|
||||
return PackedUnitVec{Pack32::Vec3PackUnitVec(reinterpret_cast<const float*>(in))};
|
||||
}
|
||||
|
||||
void Common::Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out)
|
||||
{
|
||||
Pack32::Vec2UnpackTexCoords(in.packed, reinterpret_cast<float*>(out));
|
||||
}
|
||||
|
||||
void Common::Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out)
|
||||
{
|
||||
Pack32::Vec3UnpackUnitVec(in.packed, reinterpret_cast<float*>(out));
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "IW4.h"
|
||||
|
||||
namespace IW4
|
||||
{
|
||||
inline const char* szWeapTypeNames[]
|
||||
@ -192,4 +194,13 @@ namespace IW4
|
||||
"rear",
|
||||
"all",
|
||||
};
|
||||
}
|
||||
|
||||
class Common
|
||||
{
|
||||
public:
|
||||
static PackedTexCoords Vec2PackTexCoords(const vec2_t* in);
|
||||
static PackedUnitVec Vec3PackUnitVec(const vec3_t* in);
|
||||
static void Vec2UnpackTexCoords(const PackedTexCoords& in, vec2_t* out);
|
||||
static void Vec3UnpackUnitVec(const PackedUnitVec& in, vec3_t* out);
|
||||
};
|
||||
}
|
||||
|
@ -650,9 +650,9 @@ namespace IW4
|
||||
{
|
||||
MaterialInfo info;
|
||||
char stateBitsEntry[48];
|
||||
char textureCount;
|
||||
char constantCount;
|
||||
char stateBitsCount;
|
||||
unsigned char textureCount;
|
||||
unsigned char constantCount;
|
||||
unsigned char stateBitsCount;
|
||||
char stateFlags;
|
||||
char cameraRegion;
|
||||
MaterialTechniqueSet* techniqueSet;
|
||||
|
57
src/Common/Utils/Pack.cpp
Normal file
57
src/Common/Utils/Pack.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "Pack.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
union PackUtil32
|
||||
{
|
||||
uint32_t u;
|
||||
int32_t i;
|
||||
float f;
|
||||
int8_t c[4];
|
||||
uint8_t uc[4];
|
||||
};
|
||||
|
||||
uint32_t Pack32::Vec2PackTexCoords(const float* in)
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Pack32::Vec3PackUnitVec(const float* in)
|
||||
{
|
||||
// TODO
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Pack32::Vec2UnpackTexCoords(const uint32_t in, float* out)
|
||||
{
|
||||
PackUtil32 packTemp{};
|
||||
|
||||
const auto inHiDw = (in >> 16) & UINT16_MAX;
|
||||
const auto inLoDw = in & UINT16_MAX;
|
||||
|
||||
if (inHiDw)
|
||||
packTemp.u = ((inHiDw << 16) & 0x80000000) | (((((inHiDw << 14) & 0xFFFC000)
|
||||
- (~(inHiDw << 14) & 0x10000000)) ^ 0x80000000) >> 1);
|
||||
else
|
||||
packTemp.f = 0.0f;
|
||||
out[0] = packTemp.f;
|
||||
|
||||
if (inLoDw)
|
||||
packTemp.u = ((inLoDw << 16) & 0x80000000) | (((((inLoDw << 14) & 0xFFFC000)
|
||||
- (~(inLoDw << 14) & 0x10000000)) ^ 0x80000000) >> 1);
|
||||
else
|
||||
packTemp.f = 0.0f;
|
||||
out[1] = packTemp.f;
|
||||
}
|
||||
|
||||
void Pack32::Vec3UnpackUnitVec(const uint32_t in, float* out)
|
||||
{
|
||||
assert(out != nullptr);
|
||||
|
||||
PackUtil32 _in{in};
|
||||
const float decodeScale = (static_cast<float>(_in.uc[3]) - -192.0f) / 32385.0f;
|
||||
out[0] = (static_cast<float>(_in.uc[0]) + -127.0f) * decodeScale;
|
||||
out[1] = (static_cast<float>(_in.uc[1]) + -127.0f) * decodeScale;
|
||||
out[2] = (static_cast<float>(_in.uc[2]) + -127.0f) * decodeScale;
|
||||
}
|
12
src/Common/Utils/Pack.h
Normal file
12
src/Common/Utils/Pack.h
Normal file
@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Pack32
|
||||
{
|
||||
public:
|
||||
static uint32_t Vec2PackTexCoords(const float* in);
|
||||
static uint32_t Vec3PackUnitVec(const float* in);
|
||||
static void Vec2UnpackTexCoords(uint32_t in, float* out);
|
||||
static void Vec3UnpackUnitVec(uint32_t in, float* out);
|
||||
};
|
183
src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp
Normal file
183
src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.cpp
Normal file
@ -0,0 +1,183 @@
|
||||
#include "AssetDumperXModel.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "Game/IW4/CommonIW4.h"
|
||||
|
||||
using namespace IW4;
|
||||
|
||||
bool AssetDumperXModel::ShouldDump(XAssetInfo<XModel>* asset)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpObjMatMaterial(AssetDumpingContext& context, const Material* material, std::ostream& stream)
|
||||
{
|
||||
stream << "\n";
|
||||
stream << "newmtl " << material->info.name << "\n";
|
||||
|
||||
GfxImage* colorMap = nullptr;
|
||||
GfxImage* normalMap = nullptr;
|
||||
GfxImage* specularMap = nullptr;
|
||||
|
||||
for(auto i = 0u; i < material->textureCount; i++)
|
||||
{
|
||||
const auto& texture = material->textureTable[i];
|
||||
|
||||
switch (texture.semantic)
|
||||
{
|
||||
case TS_COLOR_MAP:
|
||||
colorMap = texture.u.image;
|
||||
break;
|
||||
|
||||
case TS_NORMAL_MAP:
|
||||
normalMap = texture.u.image;
|
||||
break;
|
||||
|
||||
case TS_SPECULAR_MAP:
|
||||
specularMap = texture.u.image;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (colorMap)
|
||||
stream << "map_Ka " << colorMap->name << ".dds\n";
|
||||
|
||||
if (normalMap)
|
||||
stream << "map_bump " << normalMap->name << ".dds\n";
|
||||
|
||||
if (specularMap)
|
||||
stream << "map_Ks " << specularMap->name << ".dds\n";
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpObjMat(AssetDumpingContext& context, XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
const auto matFile = context.OpenAssetFile("xmodelsurfs/" + std::string(model->name) + ".mat");
|
||||
|
||||
if (!matFile)
|
||||
return;
|
||||
|
||||
auto& stream = *matFile;
|
||||
stream << "# OpenAssetTools MAT File (IW4)\n";
|
||||
|
||||
if (model->numsurfs == 0 || model->materialHandles == nullptr)
|
||||
return;
|
||||
|
||||
std::set<Material*> 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);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpObjLod(AssetDumpingContext& context, XAssetInfo<XModel>* 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 (!assetFile)
|
||||
return;
|
||||
|
||||
auto& stream = *assetFile;
|
||||
stream << "# OpenAssetTools OBJ File (IW4)\n";
|
||||
|
||||
stream << "mtllib " << model->name << ".mtl\n";
|
||||
|
||||
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[1] << " " << vertex->xyz[2] << "\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[1] << " " << normalVec[2] << "\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<r_index16_t*>(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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset)
|
||||
{
|
||||
const auto* model = asset->Asset();
|
||||
|
||||
DumpObjMat(context, asset);
|
||||
for (auto currentLod = 0u; currentLod < model->numLods; currentLod++)
|
||||
{
|
||||
DumpObjLod(context, asset, currentLod);
|
||||
}
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpXModelExportLod(AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod)
|
||||
{
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpXModelExport(AssetDumpingContext& context, XAssetInfo<XModel>* asset)
|
||||
{
|
||||
}
|
||||
|
||||
void AssetDumperXModel::DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset)
|
||||
{
|
||||
DumpObj(context, asset);
|
||||
}
|
23
src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h
Normal file
23
src/ObjWriting/Game/IW4/AssetDumpers/AssetDumperXModel.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "Dumping/AbstractAssetDumper.h"
|
||||
#include "Game/IW4/IW4.h"
|
||||
|
||||
namespace IW4
|
||||
{
|
||||
class AssetDumperXModel final : public AbstractAssetDumper<XModel>
|
||||
{
|
||||
static void DumpObjLod(AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpObjMatMaterial(AssetDumpingContext& context, const Material* material, std::ostream& stream);
|
||||
static void DumpObjMat(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpObj(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
static void DumpXModelExportLod(AssetDumpingContext& context, XAssetInfo<XModel>* asset, unsigned lod);
|
||||
static void DumpXModelExport(AssetDumpingContext& context, XAssetInfo<XModel>* asset);
|
||||
|
||||
protected:
|
||||
bool ShouldDump(XAssetInfo<XModel>* asset) override;
|
||||
void DumpAsset(AssetDumpingContext& context, XAssetInfo<XModel>* asset) override;
|
||||
};
|
||||
}
|
@ -11,6 +11,7 @@
|
||||
#include "AssetDumpers/AssetDumperStringTable.h"
|
||||
#include "AssetDumpers/AssetDumperVehicle.h"
|
||||
#include "AssetDumpers/AssetDumperWeapon.h"
|
||||
#include "AssetDumpers/AssetDumperXModel.h"
|
||||
|
||||
using namespace IW4;
|
||||
|
||||
@ -33,7 +34,7 @@ bool ZoneDumper::DumpZone(AssetDumpingContext& context) const
|
||||
// DUMP_ASSET_POOL(AssetDumperPhysPreset, m_phys_preset)
|
||||
// DUMP_ASSET_POOL(AssetDumperPhysCollmap, m_phys_collmap)
|
||||
// DUMP_ASSET_POOL(AssetDumperXAnimParts, m_xanim_parts)
|
||||
// DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel)
|
||||
DUMP_ASSET_POOL(AssetDumperXModel, m_xmodel)
|
||||
// DUMP_ASSET_POOL(AssetDumperMaterial, m_material)
|
||||
// DUMP_ASSET_POOL(AssetDumperMaterialPixelShader, m_material_pixel_shader)
|
||||
// DUMP_ASSET_POOL(AssetDumperMaterialVertexShader, m_material_vertex_shader)
|
||||
|
@ -14,8 +14,15 @@ public:
|
||||
IWI
|
||||
};
|
||||
|
||||
enum class ModelOutputFormat_e
|
||||
{
|
||||
XMODEL_EXPORT,
|
||||
OBJ
|
||||
};
|
||||
|
||||
bool Verbose = false;
|
||||
ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS;
|
||||
ModelOutputFormat_e ModelOutputFormat = ModelOutputFormat_e::XMODEL_EXPORT;
|
||||
|
||||
} Configuration;
|
||||
|
||||
|
@ -66,6 +66,13 @@ const CommandLineOption* const OPTION_IMAGE_FORMAT =
|
||||
.WithParameter("imageFormatValue")
|
||||
.Build();
|
||||
|
||||
const CommandLineOption* const OPTION_MODEL_FORMAT =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithLongName("model-format")
|
||||
.WithDescription("Specifies the format of dumped model files. Valid values are: XMODEL_EXPORT, OBJ")
|
||||
.WithParameter("modelFormatValue")
|
||||
.Build();
|
||||
|
||||
const CommandLineOption* const OPTION_GDT =
|
||||
CommandLineOption::Builder::Create()
|
||||
.WithLongName("gdt")
|
||||
@ -82,6 +89,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]
|
||||
OPTION_OUTPUT_FOLDER,
|
||||
OPTION_SEARCH_PATH,
|
||||
OPTION_IMAGE_FORMAT,
|
||||
OPTION_MODEL_FORMAT,
|
||||
OPTION_GDT
|
||||
};
|
||||
|
||||
@ -140,6 +148,29 @@ bool UnlinkerArgs::SetImageDumpingMode()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnlinkerArgs::SetModelDumpingMode()
|
||||
{
|
||||
auto specifiedValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT);
|
||||
for (auto& c : specifiedValue)
|
||||
c = static_cast<char>(tolower(c));
|
||||
|
||||
if (specifiedValue == "xmodel_export")
|
||||
{
|
||||
ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (specifiedValue == "obj")
|
||||
{
|
||||
ObjWriting::Configuration.ModelOutputFormat = ObjWriting::Configuration_t::ModelOutputFormat_e::XMODEL_EXPORT;
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string originalValue = m_argument_parser.GetValueForOption(OPTION_MODEL_FORMAT);
|
||||
printf("Illegal value: \"%s\" is not a valid model output format. Use -? to see usage information.\n", originalValue.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UnlinkerArgs::ParseArgs(const int argc, const char** argv)
|
||||
{
|
||||
if (!m_argument_parser.ParseArguments(argc - 1, &argv[1]))
|
||||
@ -203,6 +234,15 @@ bool UnlinkerArgs::ParseArgs(const int argc, const char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
// --model-format
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_MODEL_FORMAT))
|
||||
{
|
||||
if (!SetModelDumpingMode())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// --gdt
|
||||
m_use_gdt = m_argument_parser.IsOptionSpecified(OPTION_GDT);
|
||||
|
||||
|
@ -22,6 +22,7 @@ private:
|
||||
|
||||
void SetVerbose(bool isVerbose);
|
||||
bool SetImageDumpingMode();
|
||||
bool SetModelDumpingMode();
|
||||
|
||||
public:
|
||||
enum class ProcessingTask
|
||||
|
Loading…
x
Reference in New Issue
Block a user