[MapDump] Introduce proper map dumping
This commit is contained in:
parent
15f38141d3
commit
dd42a06151
@ -40,7 +40,6 @@
|
||||
| `--disable-bitmessage` | Disable use of BitMessage completely. |
|
||||
| `--disable-base128` | Disable base128 encoding for minidumps. |
|
||||
| `--no-new-structure` | Do not use new virtual path structure (separating headers and source files). |
|
||||
| `--enable-dxsdk` | Enable DirectX SDK (required for GfxMap exporting). |
|
||||
|
||||
## Disclaimer
|
||||
|
||||
|
10
premake5.lua
10
premake5.lua
@ -74,11 +74,6 @@ newoption {
|
||||
description = "Upload minidumps even for Debug builds."
|
||||
}
|
||||
|
||||
newoption {
|
||||
trigger = "enable-dxsdk",
|
||||
description = "Enable DirectX SDK (required for GfxMap exporting)."
|
||||
}
|
||||
|
||||
newaction {
|
||||
trigger = "version",
|
||||
description = "Returns the version string for the current commit of the source code.",
|
||||
@ -329,11 +324,6 @@ workspace "iw4x"
|
||||
if _OPTIONS["force-exception-handler"] then
|
||||
defines { "FORCE_EXCEPTION_HANDLER" }
|
||||
end
|
||||
--if _OPTIONS["enable-dxsdk"] then
|
||||
defines { "ENABLE_DXSDK" }
|
||||
-- includedirs { "%DXSDK_DIR%Include" }
|
||||
-- libdirs { "%DXSDK_DIR%Lib/x86" }
|
||||
--end
|
||||
|
||||
-- Pre-compiled header
|
||||
pchheader "STDInclude.hpp" -- must be exactly same as used in #include directives
|
||||
|
@ -60,6 +60,7 @@ namespace Components
|
||||
Loader::Register(new Console());
|
||||
Loader::Register(new Friends());
|
||||
Loader::Register(new IPCPipe());
|
||||
Loader::Register(new MapDump());
|
||||
Loader::Register(new ModList());
|
||||
Loader::Register(new Monitor());
|
||||
Loader::Register(new Network());
|
||||
|
@ -93,6 +93,7 @@ namespace Components
|
||||
#include "Modules/Logger.hpp"
|
||||
#include "Modules/Friends.hpp"
|
||||
#include "Modules/IPCPipe.hpp"
|
||||
#include "Modules/MapDump.hpp"
|
||||
#include "Modules/Session.hpp"
|
||||
#include "Modules/ClanTags.hpp"
|
||||
#include "Modules/Download.hpp"
|
||||
|
451
src/Components/Modules/MapDump.cpp
Normal file
451
src/Components/Modules/MapDump.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class MapDumper
|
||||
{
|
||||
public:
|
||||
MapDumper(Game::GfxWorld* world) : world_(world)
|
||||
{
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
if (!this->world_) return;
|
||||
|
||||
Logger::Print("Exporting '%s'...\n", this->world_->baseName);
|
||||
|
||||
this->parseVertices();
|
||||
this->parseFaces();
|
||||
this->parseStaticModels();
|
||||
|
||||
this->write();
|
||||
}
|
||||
|
||||
private:
|
||||
struct Vertex
|
||||
{
|
||||
Game::vec3_t coordinate;
|
||||
Game::vec2_t texture;
|
||||
Game::vec3_t normal;
|
||||
};
|
||||
|
||||
struct Face
|
||||
{
|
||||
int a{};
|
||||
int b{};
|
||||
int c{};
|
||||
};
|
||||
|
||||
struct FaceList
|
||||
{
|
||||
std::vector<Face> indices{};
|
||||
};
|
||||
|
||||
class File
|
||||
{
|
||||
public:
|
||||
File() {}
|
||||
|
||||
File(const std::string& file)
|
||||
{
|
||||
Utils::IO::WriteFile(file, {});
|
||||
this->stream_ = std::ofstream(file, std::ofstream::out);
|
||||
}
|
||||
|
||||
void append(const std::string& str)
|
||||
{
|
||||
this->stream_.write(str.data(), str.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::ofstream stream_{};
|
||||
};
|
||||
|
||||
Game::GfxWorld* world_{};
|
||||
std::vector<Vertex> vertices_{};
|
||||
std::unordered_map<Game::Material*, FaceList> faces_{};
|
||||
std::vector<Game::Material*> facesOrder_{};
|
||||
|
||||
File object_{};
|
||||
File material_{};
|
||||
|
||||
void transformAxes(Game::vec3_t& vec) const
|
||||
{
|
||||
std::swap(vec[0], vec[1]);
|
||||
std::swap(vec[1], vec[2]);
|
||||
}
|
||||
|
||||
void parseVertices()
|
||||
{
|
||||
Logger::Print("Parsing vertices...\n");
|
||||
|
||||
for (unsigned int i = 0; i < this->world_->draw.vertexCount; ++i)
|
||||
{
|
||||
const auto* vertex = &this->world_->draw.vd.vertices[i];
|
||||
|
||||
Vertex v{};
|
||||
|
||||
v.coordinate[0] = vertex->xyz[0];
|
||||
v.coordinate[1] = vertex->xyz[1];
|
||||
v.coordinate[2] = vertex->xyz[2];
|
||||
this->transformAxes(v.coordinate);
|
||||
|
||||
v.texture[0] = vertex->texCoord[0];
|
||||
v.texture[1] = -vertex->texCoord[1];
|
||||
|
||||
Game::Vec3UnpackUnitVec(vertex->normal, &v.normal);
|
||||
this->transformAxes(v.normal);
|
||||
|
||||
this->vertices_.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
void parseFaces()
|
||||
{
|
||||
Logger::Print("Parsing faces...\n");
|
||||
|
||||
for (unsigned int i = 0; i < this->world_->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
const auto* surface = &this->world_->dpvs.surfaces[i];
|
||||
|
||||
const unsigned int vertOffset = surface->tris.firstVertex + 1;
|
||||
const unsigned int indexOffset = surface->tris.baseIndex;
|
||||
|
||||
auto& f = this->getFaceList(surface->material);
|
||||
|
||||
for (unsigned short j = 0; j < surface->tris.triCount; ++j)
|
||||
{
|
||||
Face face{};
|
||||
face.a = this->world_->draw.indices[indexOffset + j * 3 + 0] + vertOffset;
|
||||
face.b = this->world_->draw.indices[indexOffset + j * 3 + 1] + vertOffset;
|
||||
face.c = this->world_->draw.indices[indexOffset + j * 3 + 2] + vertOffset;
|
||||
|
||||
f.indices.push_back(face);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FaceList& getFaceList(Game::Material* material)
|
||||
{
|
||||
auto& faceList = this->faces_[material];
|
||||
|
||||
if (this->facesOrder_.size() < this->faces_.size())
|
||||
{
|
||||
this->facesOrder_.push_back(material);
|
||||
}
|
||||
|
||||
return faceList;
|
||||
}
|
||||
|
||||
void performWorldTransformation(const Game::GfxPackedPlacement& placement, Vertex& v) const
|
||||
{
|
||||
Game::MatrixVecMultiply(placement.axis, v.normal, v.normal);
|
||||
Game::Vec3Normalize(v.normal);
|
||||
|
||||
Game::MatrixVecMultiply(placement.axis, v.coordinate, v.coordinate);
|
||||
v.coordinate[0] = v.coordinate[0] * placement.scale + placement.origin[0];
|
||||
v.coordinate[1] = v.coordinate[1] * placement.scale + placement.origin[1];
|
||||
v.coordinate[2] = v.coordinate[2] * placement.scale + placement.origin[2];
|
||||
}
|
||||
|
||||
std::vector<Vertex> parseSurfaceVertices(const Game::XSurface* surface, const Game::GfxPackedPlacement& placement)
|
||||
{
|
||||
std::vector<Vertex> vertices;
|
||||
|
||||
for (unsigned short j = 0; j < surface->vertCount; j++)
|
||||
{
|
||||
const auto *vertex = &surface->verts0[j];
|
||||
|
||||
Vertex v{};
|
||||
|
||||
v.coordinate[0] = vertex->xyz[0];
|
||||
v.coordinate[1] = vertex->xyz[1];
|
||||
v.coordinate[2] = vertex->xyz[2];
|
||||
|
||||
// Why...
|
||||
Game::Vec2UnpackTexCoords(vertex->texCoord, &v.texture);
|
||||
std::swap(v.texture[0], v.texture[1]);
|
||||
v.texture[1] *= -1;
|
||||
|
||||
Game::Vec3UnpackUnitVec(vertex->normal, &v.normal);
|
||||
|
||||
this->performWorldTransformation(placement, v);
|
||||
this->transformAxes(v.coordinate);
|
||||
this->transformAxes(v.normal);
|
||||
|
||||
vertices.push_back(v);
|
||||
}
|
||||
|
||||
return vertices;
|
||||
}
|
||||
|
||||
std::vector<Face> parseSurfaceFaces(const Game::XSurface* surface) const
|
||||
{
|
||||
std::vector<Face> faces;
|
||||
|
||||
for (unsigned short j = 0; j < surface->triCount; ++j)
|
||||
{
|
||||
Face face{};
|
||||
face.a = surface->triIndices[j * 3 + 0];
|
||||
face.b = surface->triIndices[j * 3 + 1];
|
||||
face.c = surface->triIndices[j * 3 + 2];
|
||||
|
||||
faces.push_back(face);
|
||||
}
|
||||
|
||||
return faces;
|
||||
}
|
||||
|
||||
void removeVertex(const int index, std::vector<Face>& faces, std::vector<Vertex>& vertices) const
|
||||
{
|
||||
vertices.erase(vertices.begin() + index);
|
||||
|
||||
for (auto &face : faces)
|
||||
{
|
||||
if (face.a > index) --face.a;
|
||||
if (face.b > index) --face.b;
|
||||
if (face.c > index) --face.c;
|
||||
}
|
||||
}
|
||||
|
||||
void filterSurfaceVertices(std::vector<Face>& faces, std::vector<Vertex>& vertices) const
|
||||
{
|
||||
for (auto i = 0; i < int(vertices.size()); ++i)
|
||||
{
|
||||
auto referenced = false;
|
||||
|
||||
for (const auto &face : faces)
|
||||
{
|
||||
if (face.a == i || face.b == i || face.c == i)
|
||||
{
|
||||
referenced = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!referenced)
|
||||
{
|
||||
this->removeVertex(i--, faces, vertices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseStaticModel(Game::GfxStaticModelDrawInst* model)
|
||||
{
|
||||
for (unsigned char i = 0; i < model->model->numsurfs; ++i)
|
||||
{
|
||||
this->getFaceList(model->model->materialHandles[i]);
|
||||
}
|
||||
|
||||
const auto* lod = &model->model->lodInfo[model->model->numLods - 1];
|
||||
|
||||
const auto baseIndex = this->vertices_.size() + 1;
|
||||
const auto surfIndex = lod->surfIndex;
|
||||
|
||||
assert(lod->modelSurfs->numsurfs <= model->model->numsurfs);
|
||||
|
||||
for (unsigned short i = 0; i < lod->modelSurfs->numsurfs; ++i)
|
||||
{
|
||||
// TODO: Something is still wrong about the models. Probably baseTriIndex and baseVertIndex might help
|
||||
|
||||
const auto* surface = &lod->modelSurfs->surfs[i];
|
||||
auto faces = this->parseSurfaceFaces(surface);
|
||||
auto vertices = this->parseSurfaceVertices(surface, model->placement);
|
||||
this->filterSurfaceVertices(faces, vertices);
|
||||
|
||||
auto& f = this->getFaceList(model->model->materialHandles[i + surfIndex]);
|
||||
|
||||
for (const auto& vertex : vertices)
|
||||
{
|
||||
this->vertices_.push_back(vertex);
|
||||
}
|
||||
|
||||
for (auto face : faces)
|
||||
{
|
||||
face.a += baseIndex;
|
||||
face.b += baseIndex;
|
||||
face.c += baseIndex;
|
||||
f.indices.push_back(std::move(face));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseStaticModels()
|
||||
{
|
||||
Logger::Print("Parsing static models...\n");
|
||||
|
||||
for (unsigned i = 0u; i < this->world_->dpvs.smodelCount; ++i)
|
||||
{
|
||||
this->parseStaticModel(this->world_->dpvs.smodelDrawInsts + i);
|
||||
}
|
||||
}
|
||||
|
||||
void write()
|
||||
{
|
||||
this->object_ = File(Utils::String::VA("raw/mapdump/%s/%s.obj", this->world_->baseName, this->world_->baseName));
|
||||
this->material_ = File(Utils::String::VA("raw/mapdump/%s/%s.mtl", this->world_->baseName, this->world_->baseName));
|
||||
|
||||
this->object_.append("# Generated by IW4x\n");
|
||||
this->object_.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
this->object_.append(Utils::String::VA("o %s\n", this->world_->baseName));
|
||||
this->object_.append(Utils::String::VA("mtllib %s.mtl\n\n", this->world_->baseName));
|
||||
|
||||
this->material_.append("# IW4x MTL File\n");
|
||||
this->material_.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
|
||||
this->writeVertices();
|
||||
this->writeFaces();
|
||||
|
||||
Logger::Print("Writing files...\n");
|
||||
|
||||
this->object_ = {};
|
||||
this->material_ = {};
|
||||
}
|
||||
|
||||
void writeVertices()
|
||||
{
|
||||
Logger::Print("Writing vertices...\n");
|
||||
this->object_.append("# Vertices\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("v %.6f %.6f %.6f\n", vertex.coordinate[0], vertex.coordinate[1], vertex.coordinate[2]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing texture coordinates...\n");
|
||||
this->object_.append("\n# Texture coordinates\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("vt %.6f %.6f\n", vertex.texture[0], vertex.texture[1]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing normals...\n");
|
||||
this->object_.append("\n# Normals\n");
|
||||
|
||||
for (const auto& vertex : this->vertices_)
|
||||
{
|
||||
this->object_.append(Utils::String::VA("vn %.6f %.6f %.6f\n", vertex.normal[0], vertex.normal[1], vertex.normal[2]));
|
||||
}
|
||||
|
||||
this->object_.append("\n");
|
||||
}
|
||||
|
||||
void writeMaterial(Game::Material* material)
|
||||
{
|
||||
std::string name = material->info.name;
|
||||
|
||||
const auto pos = name.find_last_of('/');
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
this->object_.append(Utils::String::VA("usemtl %s\n", name.data()));
|
||||
this->object_.append("s off\n");
|
||||
|
||||
Game::GfxImage *image = nullptr;
|
||||
|
||||
for (char l = 0; l < material->textureCount; ++l)
|
||||
{
|
||||
if (material->textureTable[l].nameStart == 'c' && material->textureTable[l].nameEnd == 'p')
|
||||
{
|
||||
image = material->textureTable[l].u.image; // Hopefully our colorMap
|
||||
}
|
||||
}
|
||||
|
||||
if (!image)
|
||||
{
|
||||
Logger::Print("Failed to get color map for material: %s\n", material->info.name);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This is still wrong.
|
||||
if (image->mapType == 5 && false)
|
||||
{
|
||||
for (auto i = 0; i < 6; ++i)
|
||||
{
|
||||
IDirect3DSurface9* surface = nullptr;
|
||||
image->texture.cubemap->GetCubeMapSurface(D3DCUBEMAP_FACES(i), 0, &surface);
|
||||
|
||||
if (surface)
|
||||
{
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s_%i.png", this->world_->baseName, image->name, i);
|
||||
D3DXSaveSurfaceToFileA(_name.data(), D3DXIFF_PNG, surface, nullptr, nullptr);
|
||||
surface->Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", this->world_->baseName, image->name);
|
||||
D3DXSaveTextureToFileA(_name.data(), D3DXIFF_PNG, image->texture.map, nullptr);
|
||||
}
|
||||
|
||||
this->material_.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
|
||||
this->material_.append("Ka 1.0000 1.0000 1.0000\n");
|
||||
this->material_.append("Kd 1.0000 1.0000 1.0000\n");
|
||||
this->material_.append("illum 1\n");
|
||||
this->material_.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
|
||||
this->material_.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
|
||||
}
|
||||
|
||||
void writeFaces()
|
||||
{
|
||||
Logger::Print("Writing faces...\n");
|
||||
Utils::IO::CreateDir(Utils::String::VA("raw/mapdump/%s/textures", this->world_->baseName));
|
||||
|
||||
this->material_.append(Utils::String::VA("# Material count: %d\n", this->faces_.size()));
|
||||
|
||||
this->object_.append("# Faces\n");
|
||||
|
||||
for (const auto& material : this->facesOrder_)
|
||||
{
|
||||
this->writeMaterial(material);
|
||||
|
||||
const auto& faces = this->getFaceList(material);
|
||||
for (const auto& index : faces.indices)
|
||||
{
|
||||
const int a = index.a;
|
||||
const int b = index.b;
|
||||
const int c = index.c;
|
||||
|
||||
this->object_.append(Utils::String::VA("f %d/%d/%d %d/%d/%d %d/%d/%d\n", a, a, a, b, b, b, c, c, c));
|
||||
}
|
||||
|
||||
this->object_.append("\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
MapDump::MapDump()
|
||||
{
|
||||
Command::Add("dumpmap", [](Command::Params*)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::GfxWorld* world = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [](Game::XAssetHeader header, void* world)
|
||||
{
|
||||
*reinterpret_cast<Game::GfxWorld**>(world) = header.gfxWorld;
|
||||
}, &world, false);
|
||||
|
||||
if (world)
|
||||
{
|
||||
MapDumper dumper(world);
|
||||
dumper.dump();
|
||||
|
||||
Logger::Print("Map '%s' exported!\n", world->baseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("No map loaded, unable to dump anything!\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
10
src/Components/Modules/MapDump.hpp
Normal file
10
src/Components/Modules/MapDump.hpp
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Components
|
||||
{
|
||||
class MapDump : public Component
|
||||
{
|
||||
public:
|
||||
MapDump();
|
||||
};
|
||||
}
|
@ -524,140 +524,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
// Credit to SE2Dev, as we shouldn't share the code, keep that in debug mode!
|
||||
void Maps::ExportMap(Game::GfxWorld* world)
|
||||
{
|
||||
Utils::Memory::Allocator allocator;
|
||||
if (!world) return;
|
||||
|
||||
Logger::Print("Exporting '%s'...\n", world->baseName);
|
||||
|
||||
std::string mtl;
|
||||
mtl.append("# IW4x MTL File\n");
|
||||
mtl.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
|
||||
std::string map;
|
||||
map.append("# Generated by IW4x\n");
|
||||
map.append("# Credit to SE2Dev for his D3DBSP Tool\n");
|
||||
map.append(Utils::String::VA("o %s\n", world->baseName));
|
||||
map.append(Utils::String::VA("mtllib %s.mtl\n\n", world->baseName));
|
||||
|
||||
Logger::Print("Writing vertices...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
float x = world->draw.vd.vertices[i].xyz[1];
|
||||
float y = world->draw.vd.vertices[i].xyz[2];
|
||||
float z = world->draw.vd.vertices[i].xyz[0];
|
||||
|
||||
map.append(Utils::String::VA("v %.6f %.6f %.6f\n", x, y, z));
|
||||
}
|
||||
|
||||
map.append("\n");
|
||||
|
||||
Logger::Print("Writing texture coordinates...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
map.append(Utils::String::VA("vt %.6f %.6f\n", world->draw.vd.vertices[i].texCoord[0], -world->draw.vd.vertices[i].texCoord[1]));
|
||||
}
|
||||
|
||||
Logger::Print("Writing normals...\n");
|
||||
for (unsigned int i = 0; i < world->draw.vertexCount; ++i)
|
||||
{
|
||||
Game::vec3_t normal;
|
||||
Game::Vec3UnpackUnitVec(world->draw.vd.vertices[i].normal, &normal);
|
||||
map.append(Utils::String::VA("vn %.6f %.6f %.6f\n", normal[0], normal[1], normal[2]));
|
||||
}
|
||||
|
||||
map.append("\n");
|
||||
|
||||
Logger::Print("Searching materials...\n");
|
||||
int materialCount = 0;
|
||||
Game::Material** materials = allocator.allocateArray<Game::Material*>(world->dpvs.staticSurfaceCount);
|
||||
|
||||
for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
bool isNewMat = true;
|
||||
|
||||
for (int j = 0; j < materialCount; ++j)
|
||||
{
|
||||
if (world->dpvs.surfaces[i].material == materials[j])
|
||||
{
|
||||
isNewMat = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isNewMat)
|
||||
{
|
||||
materials[materialCount++] = world->dpvs.surfaces[i].material;
|
||||
}
|
||||
}
|
||||
|
||||
Utils::IO::CreateDir(Utils::String::VA("raw/mapdump/%s/textures", world->baseName));
|
||||
mtl.append(Utils::String::VA("# Material Count: %d\n", materialCount));
|
||||
|
||||
Logger::Print("Exporting materials and faces...\n");
|
||||
for (int m = 0; m < materialCount; ++m)
|
||||
{
|
||||
std::string name = materials[m]->info.name;
|
||||
|
||||
auto pos = name.find_last_of("/");
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
name = name.substr(pos + 1);
|
||||
}
|
||||
|
||||
map.append(Utils::String::VA("\nusemtl %s\n", name.data()));
|
||||
map.append("s off\n");
|
||||
|
||||
Game::GfxImage* image = materials[m]->textureTable[0].u.image;
|
||||
|
||||
for (char l = 0; l < materials[m]->textureCount; ++l)
|
||||
{
|
||||
if (materials[m]->textureTable[l].nameStart == 'c')
|
||||
{
|
||||
if (materials[m]->textureTable[l].nameEnd == 'p')
|
||||
{
|
||||
image = materials[m]->textureTable[l].u.image; // Hopefully our colorMap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", world->baseName, image->name);
|
||||
D3DXSaveTextureToFile(std::wstring(_name.begin(), _name.end()).data(), D3DXIFF_PNG, image->texture.map, NULL);
|
||||
|
||||
mtl.append(Utils::String::VA("\nnewmtl %s\n", name.data()));
|
||||
mtl.append("Ka 1.0000 1.0000 1.0000\n");
|
||||
mtl.append("Kd 1.0000 1.0000 1.0000\n");
|
||||
mtl.append("illum 1\n");
|
||||
mtl.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name));
|
||||
mtl.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name));
|
||||
|
||||
for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i)
|
||||
{
|
||||
if (world->dpvs.surfaces[i].material != materials[m])
|
||||
continue;
|
||||
|
||||
int vertOffset = world->dpvs.surfaces[i].tris.firstVertex + 1;//+1 cus obj starts at 1
|
||||
int indexOffset = world->dpvs.surfaces[i].tris.baseIndex;
|
||||
for (unsigned short j = 0; j < world->dpvs.surfaces[i].tris.triCount; ++j)
|
||||
{
|
||||
int a = world->draw.indices[indexOffset + j * 3 + 0] + vertOffset;
|
||||
int b = world->draw.indices[indexOffset + j * 3 + 1] + vertOffset;
|
||||
int c = world->draw.indices[indexOffset + j * 3 + 2] + vertOffset;
|
||||
|
||||
map.append(Utils::String::VA("f %d/%d/%d %d/%d/%d %d/%d/%d\n", a, a, a, b, b, b, c, c, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Logger::Print("Writing final files...\n");
|
||||
Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.mtl", world->baseName, world->baseName), mtl);
|
||||
Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.obj", world->baseName, world->baseName), map);
|
||||
}
|
||||
#endif
|
||||
|
||||
void Maps::AddDlc(Maps::DLC dlc)
|
||||
{
|
||||
for (auto& pack : Maps::DlcPacks)
|
||||
@ -1001,33 +867,6 @@ namespace Components
|
||||
//Maps::AddDependency("co_hunted", "mp_storm");
|
||||
//Maps::AddDependency("mp_shipment", "mp_shipment_long");
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
Command::Add("dumpmap", [](Command::Params*)
|
||||
{
|
||||
if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled())
|
||||
{
|
||||
Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::GfxWorld* world = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [](Game::XAssetHeader header, void* world)
|
||||
{
|
||||
*reinterpret_cast<Game::GfxWorld**>(world) = header.gfxWorld;
|
||||
}, &world, false);
|
||||
|
||||
if (world)
|
||||
{
|
||||
Maps::ExportMap(world);
|
||||
Logger::Print("Map '%s' exported!\n", world->baseName);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::Print("No map loaded, unable to dump anything!\n");
|
||||
}
|
||||
});
|
||||
#endif
|
||||
|
||||
// Allow hiding specific smodels
|
||||
Utils::Hook(0x50E67C, Maps::HideModelStub, HOOK_CALL).install()->quick();
|
||||
|
||||
|
@ -100,10 +100,6 @@ namespace Components
|
||||
static void AddDlc(DLC dlc);
|
||||
static void UpdateDlcStatus();
|
||||
|
||||
#if defined(DEBUG) && defined(ENABLE_DXSDK)
|
||||
static void ExportMap(Game::GfxWorld* world);
|
||||
#endif
|
||||
|
||||
static void PrepareUsermap(const char* mapname);
|
||||
static void SpawnServerStub();
|
||||
static void LoadMapLoadscreenStub();
|
||||
|
@ -683,6 +683,43 @@ namespace Game
|
||||
return atoi(StringTable_Lookup(rankTable, 0, maxrank, 7));
|
||||
}
|
||||
|
||||
void Vec3Normalize(vec3_t& vec)
|
||||
{
|
||||
const auto length = std::sqrt(std::pow(vec[0], 2) + std::pow(vec[1], 2) + std::pow(vec[2], 2));
|
||||
vec[0] /= length;
|
||||
vec[1] /= length;
|
||||
vec[2] /= length;
|
||||
}
|
||||
|
||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out)
|
||||
{
|
||||
unsigned int v3; // xmm1_4
|
||||
|
||||
if (LOWORD(in.packed))
|
||||
v3 = ((in.packed & 0x8000) << 16) | (((((in.packed & 0x3FFF) << 14) - (~(LOWORD(in.packed) << 14) & 0x10000000)) ^ 0x80000001) >> 1);
|
||||
else
|
||||
v3 = 0;
|
||||
|
||||
(*out)[0] = *reinterpret_cast<float*>(&v3);
|
||||
|
||||
if (HIWORD(in.packed))
|
||||
v3 = ((HIWORD(in.packed) & 0x8000) << 16) | (((((HIWORD(in.packed) & 0x3FFF) << 14)
|
||||
- (~(HIWORD(in.packed) << 14) & 0x10000000)) ^ 0x80000001) >> 1);
|
||||
else
|
||||
v3 = 0;
|
||||
|
||||
(*out)[1] = *reinterpret_cast<float*>(&v3);
|
||||
}
|
||||
|
||||
void MatrixVecMultiply(const float (& mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution)
|
||||
{
|
||||
vec3_t res;
|
||||
res[0] = mulMat[0][0] * mulVec[0] + mulMat[1][0] * mulVec[1] + mulMat[2][0] * mulVec[2];
|
||||
res[1] = mulMat[0][1] * mulVec[0] + mulMat[1][1] * mulVec[1] + mulMat[2][1] * mulVec[2];
|
||||
res[2] = mulMat[0][2] * mulVec[0] + mulMat[1][2] * mulVec[1] + mulMat[2][2] * mulVec[2];
|
||||
std::memmove(&solution[0], &res[0], sizeof(res));
|
||||
}
|
||||
|
||||
void SortWorldSurfaces(GfxWorld* world)
|
||||
{
|
||||
DWORD* specular1 = reinterpret_cast<DWORD*>(0x69F105C);
|
||||
|
@ -885,6 +885,10 @@ namespace Game
|
||||
|
||||
void Image_Setup(GfxImage* image, unsigned int width, unsigned int height, unsigned int depth, unsigned int flags, _D3DFORMAT format);
|
||||
|
||||
void Vec3Normalize(vec3_t& vec);
|
||||
void Vec2UnpackTexCoords(const PackedTexCoords in, vec2_t* out);
|
||||
void MatrixVecMultiply(const float(&mulMat)[3][3], const vec3_t& mulVec, vec3_t& solution);
|
||||
|
||||
void SortWorldSurfaces(GfxWorld* world);
|
||||
void R_AddDebugLine(float* color, float* v1, float* v2);
|
||||
void R_AddDebugString(float *color, float *pos, float scale, const char *str);
|
||||
|
@ -45,10 +45,8 @@
|
||||
|
||||
#pragma warning(pop)
|
||||
|
||||
#ifdef ENABLE_DXSDK
|
||||
#include <d3dx9tex.h>
|
||||
#pragma comment(lib, "D3dx9.lib")
|
||||
#endif
|
||||
|
||||
// Usefull for debugging
|
||||
template <size_t S> class Sizer { };
|
||||
|
Loading…
Reference in New Issue
Block a user