Big zonebuilder update (#427)
This commit is contained in:
parent
f6b66fdc0d
commit
f91fa0d0b2
@ -257,7 +257,7 @@ namespace Components
|
||||
|
||||
for (int k = 0; k < (pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount); ++k)
|
||||
{
|
||||
if (pass->args[k].type == D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_CONSTINT)
|
||||
if (pass->args[k].type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
|
||||
{
|
||||
if (pass->args[k].u.codeConst.index == -28132)
|
||||
{
|
||||
|
@ -391,7 +391,7 @@ namespace Assets
|
||||
|
||||
switch (elemType)
|
||||
{
|
||||
case 7:
|
||||
case Game::FX_ELEM_TYPE_MODEL:
|
||||
{
|
||||
if (visuals->model)
|
||||
{
|
||||
@ -401,11 +401,11 @@ namespace Assets
|
||||
break;
|
||||
}
|
||||
|
||||
case 8:
|
||||
case 9:
|
||||
case Game::FX_ELEM_TYPE_OMNI_LIGHT:
|
||||
case Game::FX_ELEM_TYPE_SPOT_LIGHT:
|
||||
break;
|
||||
|
||||
case 0xA:
|
||||
case Game::FX_ELEM_TYPE_SOUND:
|
||||
{
|
||||
if (visuals->soundName)
|
||||
{
|
||||
@ -416,7 +416,7 @@ namespace Assets
|
||||
break;
|
||||
}
|
||||
|
||||
case 0xC:
|
||||
case Game::FX_ELEM_TYPE_RUNNER:
|
||||
{
|
||||
if (visuals->effectDef.handle)
|
||||
{
|
||||
@ -491,7 +491,7 @@ namespace Assets
|
||||
|
||||
// Save_FxElemDefVisuals
|
||||
{
|
||||
if (elemDef->elemType == 11)
|
||||
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
|
||||
{
|
||||
if (elemDef->visuals.markArray)
|
||||
{
|
||||
@ -501,7 +501,7 @@ namespace Assets
|
||||
Game::FxElemMarkVisuals* destMarkArray = buffer->dest<Game::FxElemMarkVisuals>();
|
||||
buffer->saveArray(elemDef->visuals.markArray, elemDef->visualCount);
|
||||
|
||||
for (char j = 0; j < elemDef->visualCount; ++j)
|
||||
for (auto j = 0; j < elemDef->visualCount; ++j)
|
||||
{
|
||||
if (elemDef->visuals.markArray[j].materials[0])
|
||||
{
|
||||
@ -563,7 +563,7 @@ namespace Assets
|
||||
{
|
||||
AssertSize(Game::FxElemExtendedDefPtr, 4);
|
||||
|
||||
if (elemDef->elemType == 3)
|
||||
if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL)
|
||||
{
|
||||
// Save_FxTrailDef
|
||||
{
|
||||
@ -597,7 +597,7 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (elemDef->elemType == 6)
|
||||
else if (elemDef->elemType == Game::FX_ELEM_TYPE_SPARK_FOUNTAIN)
|
||||
{
|
||||
if (elemDef->extended.sparkFountainDef)
|
||||
{
|
||||
|
@ -186,11 +186,20 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
|
||||
void IFxWorld::load(Game::XAssetHeader* /*header*/, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::FxWorld* map = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FXWORLD, name.data()).fxWorld;
|
||||
if (map) return;
|
||||
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Missing fx_map {}... you can't make them yet you idiot.", name);
|
||||
// Generate
|
||||
map = builder->getAllocator()->allocate<Game::FxWorld>();
|
||||
map->name = builder->getAllocator()->duplicateString(name);
|
||||
|
||||
// No glass for you!
|
||||
ZeroMemory(&map->glassSys, sizeof(map->glassSys));
|
||||
|
||||
map->glassSys.firstFreePiece = 0xFFFF; // That's how rust has it (no glass)
|
||||
|
||||
header->fxWorld = map;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IGameWorldMp.hpp"
|
||||
|
||||
#define IW4X_GAMEWORLD_VERSION 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
@ -75,4 +77,118 @@ namespace Assets
|
||||
|
||||
buffer->popBlock();
|
||||
}
|
||||
|
||||
void IGameWorldMp::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
std::string name = _name;
|
||||
Utils::String::Replace(name, "maps/mp/", "");
|
||||
Utils::String::Replace(name, ".d3dbsp", "");
|
||||
|
||||
Components::FileSystem::File gameWorld(std::format("gameworld/{}.iw4x.json", name));
|
||||
|
||||
if (gameWorld.exists())
|
||||
{
|
||||
nlohmann::json gameWorldJson;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
gameWorldJson = nlohmann::json::parse(gameWorld.getBuffer());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid JSON for gameworld {}! {}", name, e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
auto* asset = builder->getAllocator()->allocate<Game::GameWorldMp>();
|
||||
|
||||
if (!gameWorldJson.is_object())
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorldMp json for {}\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
auto version = gameWorldJson["version"].is_number() ? gameWorldJson["version"].get<int>() : 0;
|
||||
if (version != IW4X_GAMEWORLD_VERSION)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid GameWorld json version for {}, expected {} and got {}\n", name, IW4X_GAMEWORLD_VERSION, version);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gameWorldJson["name"].is_string())
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Missing gameworld name! on {}\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
asset->name = builder->getAllocator()->duplicateString(gameWorldJson["name"].get<std::string>());
|
||||
auto glassData = builder->getAllocator()->allocate<Game::G_GlassData>();
|
||||
|
||||
if (gameWorldJson["glassData"].is_object())
|
||||
{
|
||||
auto jsonGlassData = gameWorldJson["glassData"];
|
||||
|
||||
try
|
||||
{
|
||||
glassData->damageToDestroy = jsonGlassData["damageToDestroy"].get<unsigned short>();
|
||||
glassData->damageToWeaken = jsonGlassData["damageToWeaken"].get<unsigned short>();
|
||||
|
||||
if (jsonGlassData["glassNames"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t glassNames = jsonGlassData["glassNames"];
|
||||
glassData->glassNameCount = glassNames.size();
|
||||
glassData->glassNames = builder->getAllocator()->allocateArray<Game::G_GlassName>(glassData->glassNameCount);
|
||||
|
||||
for (size_t i = 0; i < glassData->glassNameCount; i++)
|
||||
{
|
||||
auto jsonGlassName = glassNames[i];
|
||||
glassData->glassNames[i].nameStr = builder->getAllocator()->duplicateString(jsonGlassName["nameStr"]);
|
||||
|
||||
glassData->glassNames[i].name = jsonGlassName["name"].get<unsigned short>();
|
||||
|
||||
if (jsonGlassName["piecesIndices"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t jsonPiecesIndices = jsonGlassName["piecesIndices"];
|
||||
glassData->glassNames[i].pieceCount = static_cast<unsigned short>(jsonPiecesIndices.size());
|
||||
|
||||
for (size_t j = 0; j < glassData->glassNames[i].pieceCount; j++)
|
||||
{
|
||||
glassData->glassNames[i].pieceIndices[j] = jsonPiecesIndices[j].get<unsigned short>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gameWorldJson["glassPieces"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t glassPieces = gameWorldJson["glassPieces"];
|
||||
glassData->pieceCount = glassPieces.size();
|
||||
glassData->glassPieces = builder->getAllocator()->allocateArray<Game::G_GlassPiece>(glassData->pieceCount);
|
||||
|
||||
for (size_t i = 0; i < glassData->pieceCount; i++)
|
||||
{
|
||||
glassData->glassPieces[i].collapseTime = glassPieces[i]["collapseTime"].get<unsigned short>();
|
||||
glassData->glassPieces[i].damageTaken = glassPieces[i]["damageTaken"].get<unsigned short>();
|
||||
glassData->glassPieces[i].lastStateChangeTime = glassPieces[i]["lastStateChangeTime"].get<int>();
|
||||
glassData->glassPieces[i].impactDir = glassPieces[i]["impactDir"].get<char>();
|
||||
|
||||
nlohmann::json::array_t jsonPos = glassPieces[i]["impactPos"];
|
||||
glassData->glassPieces[i].impactPos[0] = jsonPos[0].get<char>();
|
||||
glassData->glassPieces[i].impactPos[1] = jsonPos[1].get<char>();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const nlohmann::json::exception& e)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Malformed GameWorldMp json for {} ({})\n", name, e.what());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
asset->g_glassData = glassData;
|
||||
|
||||
header->gameWorldMp = asset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,6 @@ namespace Assets
|
||||
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP; }
|
||||
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
};
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ namespace Assets
|
||||
|
||||
for (char j = 0; j < 5; ++j)
|
||||
{
|
||||
builder->mapScriptString(&(&node->constant.targetname)[j]);
|
||||
builder->mapScriptString((&node->constant.targetname)[j]);
|
||||
}
|
||||
|
||||
if (node->constant.Links)
|
||||
|
@ -18,7 +18,7 @@ namespace Assets
|
||||
}
|
||||
|
||||
image->name = builder->getAllocator()->duplicateString(name);
|
||||
image->semantic = 2;
|
||||
image->semantic = Game::TextureSemantic::TS_COLOR_MAP;
|
||||
|
||||
const char* tempName = image->name;
|
||||
if (tempName[0] == '*') tempName++;
|
||||
@ -35,7 +35,7 @@ namespace Assets
|
||||
}
|
||||
|
||||
image->mapType = reader.read<char>();
|
||||
image->semantic = reader.read<char>();
|
||||
image->semantic = reader.read<Game::TextureSemantic>();
|
||||
image->category = reader.read<char>();
|
||||
|
||||
int dataLength = reader.read<int>();
|
||||
|
@ -3,6 +3,27 @@
|
||||
|
||||
#define IW4X_GFXMAP_VERSION 1
|
||||
|
||||
// The xmodel vehicle_small_hatch_green_destructible_mp causes EXTREME lag
|
||||
// when placed in the world, for reasons unknown.
|
||||
//
|
||||
// Something happens with the SModelSurfIterator which makes it load garbage
|
||||
// as an XSurface in the middle of otherwise valid surfaces. This bug is very
|
||||
// easy to reproduce with an empty map and just this car in the middle
|
||||
//
|
||||
// As of know we do not know why the iterator corruption occurs or what causes
|
||||
// it. It doesn't seem linked to the SModel, nor to the materials or techsets,
|
||||
// nor to the sortkeys, nor to the tilemode, boneinfo, and so on. So for now
|
||||
// and to make it work for majority of users, we just swap the car. (no, using
|
||||
// the identical car from iw4's favela_escape doesn't work either!)
|
||||
//
|
||||
// Two other models have this problem: ch_apartment_9story_noentry_02 and
|
||||
// ch_apartment_5story_noentry_01
|
||||
// But these exist in mp_vacant in slightly different versions, and can be
|
||||
// swapped safely by deleting the two .iw4XModel files and requiring mp_vacant
|
||||
// or a minimal zone containing just these two models.
|
||||
//
|
||||
#define SWAP_GREEN_VEHICLE_XMODEL 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
|
||||
@ -47,7 +68,11 @@ namespace Assets
|
||||
|
||||
if (model->model)
|
||||
{
|
||||
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, reader->readString().data(), builder).model;
|
||||
auto name = reader->readString();
|
||||
|
||||
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name.data(), builder).model;
|
||||
|
||||
assert(model->model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -120,17 +120,8 @@ namespace Assets
|
||||
|
||||
if (asset->sound.data)
|
||||
{
|
||||
if (builder->hasPointer(asset->sound.data))
|
||||
{
|
||||
dest->sound.data = builder->getPointer(asset->sound.data);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->storePointer(asset->sound.data);
|
||||
|
||||
buffer->saveArray(asset->sound.data, asset->sound.info.data_len);
|
||||
Utils::Stream::ClearPointer(&dest->sound.data);
|
||||
}
|
||||
buffer->saveArray(asset->sound.data, asset->sound.info.data_len);
|
||||
Utils::Stream::ClearPointer(&dest->sound.data);
|
||||
}
|
||||
|
||||
buffer->popBlock();
|
||||
|
@ -1,15 +1,439 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterial.hpp"
|
||||
|
||||
#define IW4X_MAT_VERSION "1"
|
||||
#define IW4X_MAT_BIN_VERSION "1"
|
||||
#define IW4X_MAT_JSON_VERSION 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
const std::unordered_map<std::string, std::string> techSetCorrespondance =
|
||||
{
|
||||
{"effect", "effect_blend"},
|
||||
{"effect", "effect_blend"},
|
||||
{"effect_nofog", "effect_blend_nofog"},
|
||||
{"effect_zfeather", "effect_zfeather_blend"},
|
||||
{"effect_zfeather_falloff", "effect_zfeather_falloff_add"},
|
||||
{"effect_zfeather_nofog", "effect_zfeather_add_nofog"},
|
||||
|
||||
{"wc_unlit_add", "wc_unlit_add_lin"},
|
||||
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
|
||||
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
|
||||
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin"},
|
||||
{"wc_unlit", "wc_unlit_replace_lin"},
|
||||
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
||||
{"wc_unlit_blend", "wc_unlit_blend_lin"},
|
||||
{"wc_unlit_replace", "wc_unlit_replace_lin"},
|
||||
{"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" },
|
||||
|
||||
{"mc_unlit_replace", "mc_unlit_replace_lin"},
|
||||
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
||||
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
|
||||
{"mc_unlit_alphatest", "mc_unlit_blend_lin"},
|
||||
{"mc_effect_nofog", "mc_effect_blend_nofog"},
|
||||
{"mc_effect_falloff_add_nofog", "mc_effect_falloff_add_nofog_eyeoffset"},
|
||||
};
|
||||
|
||||
void IMaterial::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadJson(header, name, builder); // Check if we want to override materials
|
||||
if (!header->data) this->loadJson(header, name, builder); // Check if we want to load a material from disk
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we want to load a material from disk (binary format)
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
}
|
||||
|
||||
|
||||
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File materialInfo(std::format("materials/{}.iw4x.json", name));
|
||||
|
||||
if (!materialInfo.exists()) return;
|
||||
|
||||
Game::Material* asset = builder->getAllocator()->allocate<Game::Material>();
|
||||
|
||||
|
||||
nlohmann::json materialJson;
|
||||
try
|
||||
{
|
||||
materialJson = nlohmann::json::parse(materialInfo.getBuffer());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
Components::Logger::Print("Invalid material json for {} (broken json {})\n", name, e.what());
|
||||
}
|
||||
|
||||
if (!materialJson.is_object())
|
||||
{
|
||||
Components::Logger::Print("Invalid material json for {} (Is it zonebuilder format?)\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (materialJson["version"].get<int>() != IW4X_MAT_JSON_VERSION)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_JSON_VERSION, materialJson["version"].get<std::string>());
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
asset->info.name = builder->getAllocator()->duplicateString(materialJson["name"].get<std::string>());
|
||||
asset->info.gameFlags = static_cast<char>(Utils::Json::ReadFlags(materialJson["gameFlags"].get<std::string>(), sizeof(char)));
|
||||
|
||||
asset->info.sortKey = materialJson["sortKey"].get<char>();
|
||||
// * We do techset later * //
|
||||
asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].get<unsigned char>();
|
||||
asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].get<unsigned char>();
|
||||
asset->info.surfaceTypeBits = static_cast<unsigned int>(Utils::Json::ReadFlags(materialJson["surfaceTypeBits"].get<std::string>(), sizeof(int)));
|
||||
asset->info.hashIndex = materialJson["hashIndex"].get<unsigned short>();
|
||||
asset->cameraRegion = materialJson["cameraRegion"].get<char>();
|
||||
}
|
||||
catch (const nlohmann::json::exception& e)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid material json for {} (broken json {})\n", name, e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
if (materialJson["gfxDrawSurface"].is_object())
|
||||
{
|
||||
asset->info.drawSurf.fields.customIndex = materialJson["gfxDrawSurface"]["customIndex"].get<long long>();
|
||||
asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].get<long long>();
|
||||
asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].get<long long>();
|
||||
asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].get<long long>();
|
||||
asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].get<long long>();
|
||||
asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].get<long long>();
|
||||
asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].get<long long>();
|
||||
asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].get<long long>();
|
||||
asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].get<long long>();
|
||||
asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].get<long long>();
|
||||
asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].get<long long>();
|
||||
}
|
||||
|
||||
asset->stateFlags = static_cast<char>(Utils::Json::ReadFlags(materialJson["stateFlags"].get<std::string>(), sizeof(char)));
|
||||
|
||||
if (materialJson["textureTable"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t textureTable = materialJson["textureTable"];
|
||||
asset->textureCount = static_cast<unsigned char>(textureTable.size());
|
||||
asset->textureTable = builder->getAllocator()->allocateArray<Game::MaterialTextureDef>(asset->textureCount);
|
||||
|
||||
for (size_t i = 0; i < textureTable.size(); i++)
|
||||
{
|
||||
auto& textureJson = textureTable[i];
|
||||
if (textureJson.is_object())
|
||||
{
|
||||
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
||||
textureDef->semantic = textureJson["semantic"].get<Game::TextureSemantic>();
|
||||
textureDef->samplerState = textureJson["samplerState"].get<char>();
|
||||
textureDef->nameStart = textureJson["nameStart"].get<char>();
|
||||
textureDef->nameEnd = textureJson["nameEnd"].get<char>();
|
||||
textureDef->nameHash = textureJson["nameHash"].get<unsigned int>();
|
||||
|
||||
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||
{
|
||||
Game::water_t* water = builder->getAllocator()->allocate<Game::water_t>();
|
||||
|
||||
if (textureJson["water"].is_object())
|
||||
{
|
||||
auto& waterJson = textureJson["water"];
|
||||
|
||||
if (waterJson["image"].is_string())
|
||||
{
|
||||
auto imageName = waterJson["image"].get<std::string>();
|
||||
|
||||
water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, imageName.data(), builder).image;
|
||||
}
|
||||
|
||||
water->amplitude = waterJson["amplitude"].get<float>();
|
||||
water->M = waterJson["M"].get<int>();
|
||||
water->N = waterJson["N"].get<int>();
|
||||
water->Lx = waterJson["Lx"].get<float>();
|
||||
water->Lz = waterJson["Lz"].get<float>();
|
||||
water->gravity = waterJson["gravity"].get<float>();
|
||||
water->windvel = waterJson["windvel"].get<float>();
|
||||
|
||||
auto winddir = waterJson["winddir"].get<std::vector<float>>();
|
||||
if (winddir.size() == 2)
|
||||
{
|
||||
std::copy(winddir.begin(), winddir.end(), water->winddir);
|
||||
}
|
||||
|
||||
auto codeConstant = waterJson["codeConstant"].get<std::vector<float>>();
|
||||
|
||||
if (codeConstant.size() == 4)
|
||||
{
|
||||
std::copy(codeConstant.begin(), codeConstant.end(), water->codeConstant);
|
||||
}
|
||||
|
||||
/// H0
|
||||
[[maybe_unused]] auto idealSize = water->M * water->N * sizeof(Game::complex_s);
|
||||
auto h064 = waterJson["H0"].get<std::string>();
|
||||
auto predictedSize = static_cast<unsigned long>(std::ceilf((h064.size() / 4.f) * 3.f));
|
||||
assert(predictedSize >= idealSize);
|
||||
|
||||
auto h0 = reinterpret_cast<Game::complex_s*>(builder->getAllocator()->allocate(predictedSize));
|
||||
|
||||
[[maybe_unused]] auto h0Result = base64_decode(
|
||||
h064.data(),
|
||||
h064.size(),
|
||||
reinterpret_cast<unsigned char*>(h0),
|
||||
&predictedSize
|
||||
);
|
||||
|
||||
assert(h0Result == CRYPT_OK);
|
||||
water->H0 = h0;
|
||||
|
||||
/// WTerm
|
||||
auto wTerm64 = waterJson["wTerm"].get<std::string>();
|
||||
auto predictedWTermSize = static_cast<unsigned long>(std::ceilf((wTerm64.size() / 4.f) * 3.f));
|
||||
|
||||
auto wTerm = reinterpret_cast<float*>(builder->getAllocator()->allocate(predictedWTermSize));
|
||||
|
||||
[[maybe_unused]] auto wTermResult = base64_decode(
|
||||
wTerm64.data(),
|
||||
wTerm64.size(),
|
||||
reinterpret_cast<unsigned char*>(wTerm),
|
||||
&predictedWTermSize
|
||||
);
|
||||
|
||||
assert(wTermResult == CRYPT_OK);
|
||||
water->wTerm = wTerm;
|
||||
}
|
||||
|
||||
textureDef->u.water = water;
|
||||
}
|
||||
else
|
||||
{
|
||||
textureDef->u.image = nullptr;
|
||||
if (textureJson["image"].is_string())
|
||||
{
|
||||
textureDef->u.image = Components::AssetHandler::FindAssetForZone
|
||||
(
|
||||
Game::XAssetType::ASSET_TYPE_IMAGE,
|
||||
textureJson["image"].get<std::string>(),
|
||||
builder
|
||||
).image;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Statebits
|
||||
if (materialJson["stateBitsEntry"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t stateBitsEntry = materialJson["stateBitsEntry"];
|
||||
|
||||
for (size_t i = 0; i < std::min(stateBitsEntry.size(), 48u); i++)
|
||||
{
|
||||
asset->stateBitsEntry[i] = stateBitsEntry[i].get<char>();
|
||||
}
|
||||
}
|
||||
|
||||
if (materialJson["stateBitsTable"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t array = materialJson["stateBitsTable"];
|
||||
asset->stateBitsCount = static_cast<unsigned char>(array.size());
|
||||
|
||||
asset->stateBitsTable = builder->getAllocator()->allocateArray<Game::GfxStateBits>(array.size());
|
||||
|
||||
size_t statebitTableIndex = 0;
|
||||
for (auto& jsonStateBitEntry : array)
|
||||
{
|
||||
auto stateBit = &asset->stateBitsTable[statebitTableIndex++];
|
||||
|
||||
unsigned int loadbits0 = 0;
|
||||
unsigned int loadbits1 = 0;
|
||||
|
||||
#define READ_INT_LB_FROM_JSON(x) unsigned int x = jsonStateBitEntry[#x].get<unsigned int>()
|
||||
#define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].get<bool>()
|
||||
|
||||
READ_INT_LB_FROM_JSON(srcBlendRgb);
|
||||
READ_INT_LB_FROM_JSON(dstBlendRgb);
|
||||
READ_INT_LB_FROM_JSON(blendOpRgb);
|
||||
READ_INT_LB_FROM_JSON(srcBlendAlpha);
|
||||
READ_INT_LB_FROM_JSON(dstBlendAlpha);
|
||||
READ_INT_LB_FROM_JSON(blendOpAlpha);
|
||||
READ_INT_LB_FROM_JSON(depthTest);
|
||||
READ_INT_LB_FROM_JSON(polygonOffset);
|
||||
|
||||
const auto alphaTest = jsonStateBitEntry["alphaTest"].get<std::string>();
|
||||
const auto cullFace = jsonStateBitEntry["cullFace"].get<std::string>();
|
||||
|
||||
READ_BOOL_LB_FROM_JSON(colorWriteRgb);
|
||||
READ_BOOL_LB_FROM_JSON(colorWriteAlpha);
|
||||
READ_BOOL_LB_FROM_JSON(polymodeLine);
|
||||
|
||||
READ_BOOL_LB_FROM_JSON(gammaWrite);
|
||||
READ_BOOL_LB_FROM_JSON(depthWrite);
|
||||
READ_BOOL_LB_FROM_JSON(stencilFrontEnabled);
|
||||
READ_BOOL_LB_FROM_JSON(stencilBackEnabled);
|
||||
|
||||
READ_INT_LB_FROM_JSON(stencilFrontPass);
|
||||
READ_INT_LB_FROM_JSON(stencilFrontFail);
|
||||
READ_INT_LB_FROM_JSON(stencilFrontZFail);
|
||||
READ_INT_LB_FROM_JSON(stencilFrontFunc);
|
||||
READ_INT_LB_FROM_JSON(stencilBackPass);
|
||||
READ_INT_LB_FROM_JSON(stencilBackFail);
|
||||
READ_INT_LB_FROM_JSON(stencilBackZFail);
|
||||
READ_INT_LB_FROM_JSON(stencilBackFunc);
|
||||
|
||||
loadbits0 |= srcBlendRgb << Game::GFXS0_SRCBLEND_RGB_SHIFT;
|
||||
loadbits0 |= dstBlendRgb << Game::GFXS0_DSTBLEND_RGB_SHIFT;
|
||||
loadbits0 |= blendOpRgb << Game::GFXS0_BLENDOP_RGB_SHIFT;
|
||||
loadbits0 |= srcBlendAlpha << Game::GFXS0_SRCBLEND_ALPHA_SHIFT;
|
||||
loadbits0 |= dstBlendAlpha << Game::GFXS0_DSTBLEND_ALPHA_SHIFT;
|
||||
loadbits0 |= blendOpAlpha << Game::GFXS0_BLENDOP_ALPHA_SHIFT;
|
||||
|
||||
if (depthTest == -1)
|
||||
{
|
||||
loadbits1 |= Game::GFXS1_DEPTHTEST_DISABLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadbits1 |= depthTest << Game::GFXS1_DEPTHTEST_SHIFT;
|
||||
}
|
||||
|
||||
loadbits1 |= polygonOffset << Game::GFXS1_POLYGON_OFFSET_SHIFT;
|
||||
|
||||
if (alphaTest == "disable")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_ATEST_DISABLE;
|
||||
}
|
||||
else if (alphaTest == ">0")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_ATEST_GT_0;
|
||||
}
|
||||
else if (alphaTest == "<128")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_ATEST_LT_128;
|
||||
}
|
||||
else if (alphaTest == ">=128")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_ATEST_GE_128;
|
||||
}
|
||||
else
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid alphatest loadbit0 '{}' in material {}\n", alphaTest, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cullFace == "none")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_CULL_NONE;
|
||||
}
|
||||
else if (cullFace == "back")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_CULL_BACK;
|
||||
}
|
||||
else if (cullFace == "front")
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_CULL_FRONT;
|
||||
}
|
||||
else
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Invalid cullFace loadbit0 '{}' in material {}\n", cullFace, name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gammaWrite)
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_GAMMAWRITE;
|
||||
}
|
||||
|
||||
if (colorWriteAlpha)
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_COLORWRITE_ALPHA;
|
||||
}
|
||||
|
||||
if (colorWriteRgb)
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_COLORWRITE_RGB;
|
||||
}
|
||||
|
||||
if (polymodeLine)
|
||||
{
|
||||
loadbits0 |= Game::GFXS0_POLYMODE_LINE;
|
||||
}
|
||||
|
||||
if (depthWrite)
|
||||
{
|
||||
loadbits1 |= Game::GFXS1_DEPTHWRITE;
|
||||
}
|
||||
if (stencilFrontEnabled)
|
||||
{
|
||||
loadbits1 |= Game::GFXS1_STENCIL_FRONT_ENABLE;
|
||||
}
|
||||
if (stencilBackEnabled)
|
||||
{
|
||||
loadbits1 |= Game::GFXS1_STENCIL_BACK_ENABLE;
|
||||
}
|
||||
|
||||
loadbits1 |= stencilFrontPass << Game::GFXS1_STENCIL_FRONT_PASS_SHIFT;
|
||||
loadbits1 |= stencilFrontFail << Game::GFXS1_STENCIL_FRONT_FAIL_SHIFT;
|
||||
loadbits1 |= stencilFrontZFail << Game::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT;
|
||||
loadbits1 |= stencilFrontFunc << Game::GFXS1_STENCIL_FRONT_FUNC_SHIFT;
|
||||
loadbits1 |= stencilBackPass << Game::GFXS1_STENCIL_BACK_PASS_SHIFT;
|
||||
loadbits1 |= stencilBackFail << Game::GFXS1_STENCIL_BACK_FAIL_SHIFT;
|
||||
loadbits1 |= stencilBackZFail << Game::GFXS1_STENCIL_BACK_ZFAIL_SHIFT;
|
||||
loadbits1 |= stencilBackFunc << Game::GFXS1_STENCIL_BACK_FUNC_SHIFT;
|
||||
|
||||
stateBit->loadBits[0] = loadbits0;
|
||||
stateBit->loadBits[1] = loadbits1;
|
||||
}
|
||||
|
||||
// Constant table
|
||||
if (materialJson["constantTable"].is_array())
|
||||
{
|
||||
|
||||
nlohmann::json::array_t constants = materialJson["constantTable"];
|
||||
asset->constantCount = static_cast<char>(constants.size());
|
||||
auto table = builder->getAllocator()->allocateArray<Game::MaterialConstantDef>(asset->constantCount);
|
||||
|
||||
for (size_t constantIndex = 0; constantIndex < asset->constantCount; constantIndex++)
|
||||
{
|
||||
auto& constant = constants[constantIndex];
|
||||
auto entry = &table[constantIndex];
|
||||
|
||||
auto litVec = constant["literal"].get<std::vector<float>>();
|
||||
std::copy(litVec.begin(), litVec.end(), entry->literal);
|
||||
|
||||
auto constantName = constant["name"].get<std::string>();
|
||||
std::copy(constantName.begin(), constantName.end(), entry->name);
|
||||
|
||||
entry->nameHash = constant["nameHash"].get<unsigned int>();
|
||||
}
|
||||
|
||||
asset->constantTable = table;
|
||||
}
|
||||
|
||||
if (materialJson["techniqueSet"].is_string())
|
||||
{
|
||||
const std::string techsetName = materialJson["techniqueSet"].get<std::string>();
|
||||
asset->techniqueSet = findWorkingTechset(techsetName, asset, builder);
|
||||
|
||||
if (asset->techniqueSet == nullptr)
|
||||
{
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
header->material = asset;
|
||||
}
|
||||
}
|
||||
|
||||
Game::MaterialTechniqueSet* IMaterial::findWorkingTechset(std::string techsetName, [[maybe_unused]] Game::Material* material, Components::ZoneBuilder::Zone* builder) const
|
||||
{
|
||||
Game::MaterialTechniqueSet* techset;
|
||||
|
||||
// Pass 1: Identical techset (1:1)
|
||||
techset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName.data(), builder).techniqueSet;
|
||||
if (techset != nullptr)
|
||||
{
|
||||
return techset;
|
||||
}
|
||||
|
||||
// We do no more cause we use CoD4 techset and they should always be present
|
||||
// If one day we want to go back to mw2 fallback we can add extra steps here!
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void IMaterial::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
@ -30,29 +454,6 @@ namespace Assets
|
||||
"_add_lin_nofog",
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, std::string> techSetCorrespondance =
|
||||
{
|
||||
{"effect", "effect_blend"},
|
||||
{"effect", "effect_blend"},
|
||||
{"effect_nofog", "effect_blend_nofog"},
|
||||
{"effect_zfeather", "effect_zfeather_blend"},
|
||||
|
||||
{"wc_unlit_add", "wc_unlit_add_lin"},
|
||||
{"wc_unlit_distfalloff", "wc_unlit_distfalloff_replace"},
|
||||
{"wc_unlit_multiply", "wc_unlit_multiply_lin"},
|
||||
{"wc_unlit_falloff_add", "wc_unlit_falloff_add_lin_ua"},
|
||||
{"wc_unlit", "wc_unlit_replace_lin"},
|
||||
{"wc_unlit_alphatest", "wc_unlit_blend_lin"},
|
||||
{"wc_unlit_blend", "wc_unlit_blend_lin_ua"},
|
||||
{"wc_unlit_replace", "wc_unlit_replace_lin"},
|
||||
{"wc_unlit_nofog", "wc_unlit_replace_lin_nofog_nocast" },
|
||||
|
||||
{"mc_unlit_replace", "mc_unlit_replace_lin"},
|
||||
{"mc_unlit_nofog", "mc_unlit_blend_nofog_ua"},
|
||||
{"mc_unlit", "mc_unlit_replace_lin_nocast"},
|
||||
{"mc_unlit_alphatest", "mc_unlit_blend_lin"}
|
||||
};
|
||||
|
||||
Components::FileSystem::File materialFile(std::format("materials/{}.iw4xMaterial", name));
|
||||
if (!materialFile.exists()) return;
|
||||
|
||||
@ -66,9 +467,9 @@ namespace Assets
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_MAT_VERSION)
|
||||
if (version != IW4X_MAT_BIN_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_VERSION, version);
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, expected version is {}, but it was {}!", name, IW4X_MAT_BIN_VERSION, version);
|
||||
}
|
||||
|
||||
auto* asset = reader.readObject<Game::Material>();
|
||||
@ -127,7 +528,7 @@ namespace Assets
|
||||
{
|
||||
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
||||
|
||||
if (textureDef->semantic == SEMANTIC_WATER_MAP)
|
||||
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||
{
|
||||
if (textureDef->u.water)
|
||||
{
|
||||
@ -175,29 +576,29 @@ namespace Assets
|
||||
|
||||
// Find correct sortkey by comparing techsets
|
||||
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset](Game::XAssetEntry* entry)
|
||||
{
|
||||
if (!replacementFound)
|
||||
{
|
||||
Game::XAssetHeader header = entry->asset.header;
|
||||
|
||||
const char* name = asset->techniqueSet->name;
|
||||
if (name[0] == ',') ++name;
|
||||
|
||||
if (std::string(name) == header.material->techniqueSet->name)
|
||||
if (!replacementFound)
|
||||
{
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
Game::XAssetHeader header = entry->asset.header;
|
||||
|
||||
// This is temp, as nobody has time to fix materials
|
||||
asset->stateBitsCount = header.material->stateBitsCount;
|
||||
asset->stateBitsTable = header.material->stateBitsTable;
|
||||
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
||||
asset->constantCount = header.material->constantCount;
|
||||
asset->constantTable = header.material->constantTable;
|
||||
Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name);
|
||||
replacementFound = true;
|
||||
const char* name = asset->techniqueSet->name;
|
||||
if (name[0] == ',') ++name;
|
||||
|
||||
if (std::string(name) == header.material->techniqueSet->name)
|
||||
{
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
|
||||
// This is temp, as nobody has time to fix materials
|
||||
asset->stateBitsCount = header.material->stateBitsCount;
|
||||
asset->stateBitsTable = header.material->stateBitsTable;
|
||||
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, ARRAYSIZE(asset->stateBitsEntry));
|
||||
asset->constantCount = header.material->constantCount;
|
||||
asset->constantTable = header.material->constantTable;
|
||||
Components::Logger::Print("For {}, copied constants & statebits from {}\n", asset->info.name, header.material->info.name);
|
||||
replacementFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}, false);
|
||||
|
||||
if (!replacementFound)
|
||||
{
|
||||
@ -222,70 +623,72 @@ namespace Assets
|
||||
|
||||
|
||||
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, techsetMatches](Game::XAssetEntry* entry)
|
||||
{
|
||||
if (!replacementFound)
|
||||
{
|
||||
Game::XAssetHeader header = entry->asset.header;
|
||||
|
||||
if (techsetMatches(header.material, asset))
|
||||
if (!replacementFound)
|
||||
{
|
||||
Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name);
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
replacementFound = true;
|
||||
Game::XAssetHeader header = entry->asset.header;
|
||||
|
||||
if (techsetMatches(header.material, asset))
|
||||
{
|
||||
Components::Logger::Print("Material {} with techset {} has been mapped to {}\n", asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name);
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
replacementFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}, false);
|
||||
}
|
||||
|
||||
if (!replacementFound && asset->techniqueSet)
|
||||
{
|
||||
Components::Logger::Print("No replacement found for material {} with techset {}\n", asset->info.name, asset->techniqueSet->name);
|
||||
std::string techName = asset->techniqueSet->name;
|
||||
if (techSetCorrespondance.contains(techName))
|
||||
if (const auto itr = techSetCorrespondance.find(techName); itr != techSetCorrespondance.end())
|
||||
{
|
||||
auto iw4TechSetName = techSetCorrespondance[techName];
|
||||
Game::XAssetEntry* iw4TechSet = Game::DB_FindXAssetEntry(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
|
||||
auto& iw4TechSetName = itr->second;
|
||||
auto* iw4TechSet = Game::DB_FindXAssetEntry(Game::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
|
||||
|
||||
if (iw4TechSet)
|
||||
if (iw4TechSet)
|
||||
{
|
||||
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry)
|
||||
{
|
||||
if (!replacementFound)
|
||||
{
|
||||
Game::XAssetHeader header = entry->asset.header;
|
||||
|
||||
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet
|
||||
&& std::string(header.material->info.name).find("icon") == std::string::npos) // Yeah this has a tendency to fuck up a LOT of transparent materials
|
||||
if (!replacementFound)
|
||||
{
|
||||
Game::XAssetHeader header = entry->asset.header;
|
||||
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
|
||||
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
|
||||
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
|
||||
// Yeah this has a tendency to fuck up a LOT of transparent materials
|
||||
if (header.material->techniqueSet == iw4TechSet->asset.header.techniqueSet && std::string(header.material->info.name).find("icon") != std::string::npos)
|
||||
{
|
||||
Components::Logger::Print("Material {} with techset {} has been mapped to {} (last chance!), taking the sort key of material {}\n",
|
||||
asset->info.name, asset->techniqueSet->name, header.material->techniqueSet->name, header.material->info.name);
|
||||
|
||||
// this is terrible!
|
||||
asset->stateBitsCount = header.material->stateBitsCount;
|
||||
asset->stateBitsTable = header.material->stateBitsTable;
|
||||
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
||||
asset->constantCount = header.material->constantCount;
|
||||
asset->constantTable = header.material->constantTable;
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
|
||||
|
||||
replacementFound = true;
|
||||
// this is terrible!
|
||||
asset->stateBitsCount = header.material->stateBitsCount;
|
||||
asset->stateBitsTable = header.material->stateBitsTable;
|
||||
std::memcpy(asset->stateBitsEntry, header.material->stateBitsEntry, 48);
|
||||
asset->constantCount = header.material->constantCount;
|
||||
asset->constantTable = header.material->constantTable;
|
||||
|
||||
replacementFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
}, false);
|
||||
|
||||
if (!replacementFound)
|
||||
if (!replacementFound)
|
||||
{
|
||||
Components::Logger::Print("Could not find any loaded material with techset {} (in replacement of {}), so I cannot set the sortkey for material {}\n", iw4TechSetName, asset->techniqueSet->name, asset->info.name);
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
Components::Logger::Print("Could not find any loaded techset with iw4 name {} for iw3 techset {}\n", iw4TechSetName, asset->techniqueSet->name);
|
||||
}
|
||||
}
|
||||
else
|
||||
else
|
||||
{
|
||||
Components::Logger::Print("Could not match iw3 techset {} with any of the techsets I know! This is a critical error, there's a good chance the map will not be playable.\n", techName);
|
||||
}
|
||||
@ -327,15 +730,6 @@ namespace Assets
|
||||
header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material;
|
||||
}
|
||||
|
||||
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File materialInfo(std::format("materials/{}.json", name));
|
||||
|
||||
if (!materialInfo.exists()) return;
|
||||
|
||||
header->material = nullptr;
|
||||
}
|
||||
|
||||
void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::Material* asset = header.material;
|
||||
@ -351,7 +745,7 @@ namespace Assets
|
||||
{
|
||||
if (asset->textureTable[i].u.image)
|
||||
{
|
||||
if (asset->textureTable[i].semantic == SEMANTIC_WATER_MAP)
|
||||
if (asset->textureTable[i].semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||
{
|
||||
if (asset->textureTable[i].u.water->image)
|
||||
{
|
||||
@ -411,7 +805,7 @@ namespace Assets
|
||||
auto* destTextureDef = &destTextureTable[i];
|
||||
auto* textureDef = &asset->textureTable[i];
|
||||
|
||||
if (textureDef->semantic == SEMANTIC_WATER_MAP)
|
||||
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||
{
|
||||
AssertSize(Game::water_t, 68);
|
||||
|
||||
|
@ -13,5 +13,8 @@ namespace Assets
|
||||
void loadJson(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
|
||||
private:
|
||||
Game::MaterialTechniqueSet* findWorkingTechset(const std::string techsetName, Game::Material* material, Components::ZoneBuilder::Zone* builder) const;
|
||||
};
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterialPixelShader.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
#define GFX_RENDERER_SHADER_SM3 0
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
|
||||
void IMaterialPixelShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
}
|
||||
|
||||
void IMaterialPixelShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
@ -19,36 +19,19 @@ namespace Assets
|
||||
|
||||
void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File psFile(std::format("ps/{}.iw4xPS", name));
|
||||
Components::FileSystem::File psFile(std::format("ps/{}.cso", name));
|
||||
if (!psFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), psFile.getBuffer());
|
||||
auto buff = psFile.getBuffer();
|
||||
auto programSize = buff.size() / 4;
|
||||
Game::MaterialPixelShader* asset = builder->getAllocator()->allocate<Game::MaterialPixelShader>();
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xPIXL", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading pixel shader '{}' failed, header is invalid!", name);
|
||||
}
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3;
|
||||
asset->prog.loadDef.programSize = static_cast<unsigned short>(programSize);
|
||||
asset->prog.loadDef.program = builder->getAllocator()->allocateArray<unsigned int>(programSize);
|
||||
memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size());
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL,
|
||||
"Reading pixel shader '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
|
||||
}
|
||||
|
||||
Game::MaterialPixelShader* asset = reader.readObject<Game::MaterialPixelShader>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->prog.loadDef.program)
|
||||
{
|
||||
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
|
||||
}
|
||||
|
||||
header->pixelShader = asset;
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterialTechniqueSet.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
#define IW4X_TECHSET_VERSION 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMaterialTechniqueSet::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadFromDisk(header, name, builder); // Check if we need to import a new one into the game
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
@ -16,132 +16,202 @@ namespace Assets
|
||||
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
void IMaterialTechniqueSet::loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::MaterialPass, 20);
|
||||
|
||||
Components::FileSystem::File techFile(std::format("techniques/{}.iw4xTech", name));
|
||||
if (!techFile.exists()) {
|
||||
Components::FileSystem::File techFile(std::format("techniques/{}.iw4x.json", name));
|
||||
if (!techFile.exists())
|
||||
{
|
||||
*tech = nullptr;
|
||||
|
||||
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), techFile.getBuffer());
|
||||
nlohmann::json technique;
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xTECH", 8))
|
||||
try
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading technique '{}' failed, header is invalid!", name);
|
||||
technique = nlohmann::json::parse(techFile.getBuffer());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
int version = technique["version"].get<int>();
|
||||
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL,
|
||||
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version.data());
|
||||
"Reading technique '{}' failed, expected version is {}, but it was {}!", name, IW4X_TECHSET_VERSION, version);
|
||||
}
|
||||
|
||||
unsigned short flags = reader.read<unsigned short>();
|
||||
unsigned short passCount = reader.read<unsigned short>();
|
||||
unsigned short flags = static_cast<unsigned short>(Utils::Json::ReadFlags(technique["flags"].get<std::string>(), sizeof(short)));
|
||||
|
||||
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passCount - 1)));
|
||||
if (technique["passArray"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t passArray = technique["passArray"];
|
||||
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->flags = flags;
|
||||
asset->passCount = passCount;
|
||||
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1)));
|
||||
|
||||
Game::MaterialPass* passes = reader.readArray<Game::MaterialPass>(passCount);
|
||||
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * passCount);
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->flags = flags;
|
||||
asset->passCount = static_cast<unsigned short>(passArray.size());
|
||||
|
||||
for (unsigned short i = 0; i < asset->passCount; i++)
|
||||
{
|
||||
Game::MaterialPass* pass = &asset->passArray[i];
|
||||
Game::MaterialPass* passes = builder->getAllocator()->allocateArray<Game::MaterialPass>(asset->passCount);
|
||||
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount);
|
||||
|
||||
if (pass->vertexDecl)
|
||||
for (unsigned short i = 0; i < asset->passCount; i++)
|
||||
{
|
||||
const char* declName = reader.readCString();
|
||||
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
|
||||
}
|
||||
Game::MaterialPass* pass = &asset->passArray[i];
|
||||
auto jsonPass = passArray[i];
|
||||
|
||||
if (pass->vertexShader)
|
||||
{
|
||||
const char* vsName = reader.readCString();
|
||||
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
|
||||
|
||||
}
|
||||
|
||||
if (pass->pixelShader)
|
||||
{
|
||||
const char* psName = reader.readCString();
|
||||
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
|
||||
}
|
||||
|
||||
pass->args = reader.readArray<Game::MaterialShaderArgument>(pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount);
|
||||
|
||||
for (int j = 0; j < pass->perPrimArgCount + pass->perObjArgCount + pass->stableArgCount; j++)
|
||||
{
|
||||
if (pass->args[j].type == 1 || pass->args[j].type == 7)
|
||||
if (jsonPass["vertexDeclaration"].is_string())
|
||||
{
|
||||
pass->args[j].u.literalConst = reader.readArray<float>(4);
|
||||
auto declName = jsonPass["vertexDeclaration"].get<std::string>();
|
||||
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
|
||||
}
|
||||
|
||||
if (pass->args[j].type == 3 || pass->args[j].type == 5)
|
||||
if (jsonPass["vertexShader"].is_string())
|
||||
{
|
||||
pass->args[j].u.codeConst.index = *reader.readObject<unsigned short>();
|
||||
pass->args[j].u.codeConst.firstRow = *reader.readObject<unsigned char>();
|
||||
pass->args[j].u.codeConst.rowCount = *reader.readObject<unsigned char>();
|
||||
auto vsName = jsonPass["vertexShader"].get<std::string>();
|
||||
pass->vertexShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, vsName, builder).vertexShader;
|
||||
}
|
||||
|
||||
if (jsonPass["pixelShader"].is_string())
|
||||
{
|
||||
auto psName = jsonPass["pixelShader"].get<std::string>();
|
||||
pass->pixelShader = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PIXELSHADER, psName, builder).pixelShader;
|
||||
}
|
||||
|
||||
pass->perPrimArgCount = jsonPass["perPrimArgCount"].get<char>();
|
||||
pass->perObjArgCount = jsonPass["perObjArgCount"].get<char>();
|
||||
pass->stableArgCount = jsonPass["stableArgCount"].get<char>();
|
||||
pass->customSamplerFlags = jsonPass["customSamplerFlags"].get<char>();
|
||||
|
||||
|
||||
if (jsonPass["arguments"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t jsonAguments = jsonPass["arguments"];
|
||||
|
||||
pass->args = builder->getAllocator()->allocateArray<Game::MaterialShaderArgument>(jsonAguments.size());
|
||||
|
||||
for (size_t j = 0; j < jsonAguments.size(); j++)
|
||||
{
|
||||
auto jsonArgument = jsonAguments[j];
|
||||
Game::MaterialShaderArgument* argument = &pass->args[j];
|
||||
|
||||
argument->type = jsonArgument["type"].get<Game::MaterialShaderArgumentType>();
|
||||
argument->dest = jsonArgument["dest"].get<unsigned short>();
|
||||
|
||||
if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_VERTEX_CONST ||
|
||||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_LITERAL_PIXEL_CONST)
|
||||
{
|
||||
argument->u.literalConst = builder->getAllocator()->allocateArray<float>(4);
|
||||
|
||||
auto literals = jsonArgument["literals"].get<std::vector<float>>();
|
||||
std::copy(literals.begin(), literals.end(), argument->u.literalConst);
|
||||
}
|
||||
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST ||
|
||||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST)
|
||||
{
|
||||
if (jsonArgument["codeConst"].is_object())
|
||||
{
|
||||
auto codeConst = jsonArgument["codeConst"];
|
||||
|
||||
argument->u.codeConst.index = codeConst["index"].get<unsigned short>();
|
||||
argument->u.codeConst.firstRow = codeConst["firstRow"].get<unsigned char>();
|
||||
argument->u.codeConst.rowCount = codeConst["rowCount"].get<unsigned char>();
|
||||
}
|
||||
}
|
||||
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_SAMPLER ||
|
||||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_VERTEX_CONST ||
|
||||
argument->type == Game::MaterialShaderArgumentType::MTL_ARG_MATERIAL_PIXEL_CONST)
|
||||
{
|
||||
argument->u.nameHash = jsonArgument["nameHash"].get<unsigned int>();
|
||||
}
|
||||
else if (argument->type == Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_SAMPLER)
|
||||
{
|
||||
argument->u.codeSampler = jsonArgument["codeSampler"].get<unsigned int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*tech = asset;
|
||||
}
|
||||
|
||||
*tech = asset;
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4xTS", name));
|
||||
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name));
|
||||
if (!tsFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), tsFile.getBuffer());
|
||||
nlohmann::json techset;
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xTSET", 8))
|
||||
try
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, header is invalid!", name);
|
||||
techset = nlohmann::json::parse(tsFile.getBuffer());
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, file is messed up! {}", name, e.what());
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
auto version = techset["version"].get<int>();
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, expected version is {}, but it was {}!",
|
||||
name, IW4X_TECHSET_VERSION, version);
|
||||
}
|
||||
|
||||
Game::MaterialTechniqueSet* asset = reader.readObject<Game::MaterialTechniqueSet>();
|
||||
Game::MaterialTechniqueSet* asset = builder->getAllocator()->allocate<Game::MaterialTechniqueSet>();
|
||||
|
||||
if (asset->name)
|
||||
if (asset == nullptr)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; i++)
|
||||
if (techset["name"].is_string())
|
||||
{
|
||||
if (asset->techniques[i])
|
||||
asset->name = builder->getAllocator()->duplicateString(techset["name"].get<std::string>());
|
||||
}
|
||||
|
||||
asset->hasBeenUploaded = techset["hasBeenUploaded"].get<bool>();
|
||||
asset->worldVertFormat = techset["worldVertFormat"].get<char>();
|
||||
|
||||
|
||||
if (techset["remappedTechniqueSet"].is_string())
|
||||
{
|
||||
auto remapped = techset["remappedTechniqueSet"].get<std::string>();
|
||||
|
||||
if (remapped != asset->name)
|
||||
{
|
||||
const char* techName = reader.readCString();
|
||||
this->loadBinaryTechnique(&asset->techniques[i], techName, builder);
|
||||
builder->loadAssetByName(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, remapped, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (techset["techniques"].is_object())
|
||||
{
|
||||
for (int i = 0; i < Game::TECHNIQUE_COUNT; i++)
|
||||
{
|
||||
auto technique = techset["techniques"].at(std::to_string(i));
|
||||
|
||||
if (technique.is_string())
|
||||
{
|
||||
this->loadTechniqueFromDisk(&asset->techniques[i], technique.get<std::string>(), builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header->techniqueSet = asset;
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
{
|
||||
Game::MaterialTechniqueSet* asset = header.techniqueSet;
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(Game::MaterialTechniqueSet::techniques); ++i)
|
||||
@ -177,8 +247,10 @@ namespace Assets
|
||||
AssertSize(Game::MaterialTechniqueSet, 204);
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
|
||||
Game::MaterialTechniqueSet* asset = header.techniqueSet;
|
||||
Game::MaterialTechniqueSet* dest = buffer->dest<Game::MaterialTechniqueSet>();
|
||||
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
|
@ -12,8 +12,8 @@ namespace Assets
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
void loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
|
||||
void loadBinaryTechnique(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void loadTechniqueFromDisk(Game::MaterialTechnique** tech, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterialVertexDeclaration.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
#define IW4X_TECHSET_VERSION 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMaterialVertexDeclaration::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
}
|
||||
|
||||
void IMaterialVertexDeclaration::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
@ -29,12 +29,11 @@ namespace Assets
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
auto version = reader.read<char>();
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {}!",
|
||||
name, IW4X_TECHSET_VERSION, version.data());
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, expected version is {}, but it was {:d}!",
|
||||
name, IW4X_TECHSET_VERSION, version);
|
||||
}
|
||||
|
||||
Game::MaterialVertexDeclaration* asset = reader.readObject<Game::MaterialVertexDeclaration>();
|
||||
|
@ -1,14 +1,14 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterialVertexShader.hpp"
|
||||
|
||||
#define IW4X_TECHSET_VERSION "0"
|
||||
#define GFX_RENDERER_SHADER_SM3 0
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IMaterialVertexShader::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
}
|
||||
|
||||
void IMaterialVertexShader::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
@ -18,36 +18,18 @@ namespace Assets
|
||||
|
||||
void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File vsFile(std::format("vs/{}.iw4xVS", name));
|
||||
Components::FileSystem::File vsFile(std::format("vs/{}.cso", name));
|
||||
if (!vsFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), vsFile.getBuffer());
|
||||
auto buff = vsFile.getBuffer();
|
||||
auto programSize = buff.size() / 4;
|
||||
Game::MaterialVertexShader* asset = builder->getAllocator()->allocate<Game::MaterialVertexShader>();
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xVERT", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_TECHSET_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex shader '{}' failed, expected version is {}, but it was {}!",
|
||||
name, IW4X_TECHSET_VERSION, version);
|
||||
}
|
||||
|
||||
Game::MaterialVertexShader* asset = reader.readObject<Game::MaterialVertexShader>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->prog.loadDef.program)
|
||||
{
|
||||
asset->prog.loadDef.program = reader.readArray<unsigned int>(asset->prog.loadDef.programSize);
|
||||
}
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->prog.loadDef.loadForRenderer = GFX_RENDERER_SHADER_SM3;
|
||||
asset->prog.loadDef.programSize = static_cast<unsigned short>(programSize);
|
||||
asset->prog.loadDef.program = builder->getAllocator()->allocateArray<unsigned int>(programSize);
|
||||
memcpy_s(asset->prog.loadDef.program, buff.size(), buff.data(), buff.size());
|
||||
|
||||
header->vertexShader = asset;
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ namespace Assets
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackSoundMapKeys, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
builder->mapScriptString(scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackSoundMapKeys);
|
||||
@ -292,7 +292,7 @@ namespace Assets
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackSoundMapValues, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
builder->mapScriptString(scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackSoundMapValues);
|
||||
@ -304,7 +304,7 @@ namespace Assets
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackRumbleMapKeys, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
builder->mapScriptString(scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapKeys);
|
||||
@ -316,7 +316,7 @@ namespace Assets
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(def->notetrackRumbleMapValues, 16);
|
||||
for (int i = 0; i < 16; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
builder->mapScriptString(scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notetrackRumbleMapValues);
|
||||
@ -725,7 +725,7 @@ namespace Assets
|
||||
unsigned short* scriptStringTable = buffer->dest<unsigned short>();
|
||||
buffer->saveArray(asset->hideTags, 32);
|
||||
for (int i = 0; i < 32; i++) {
|
||||
builder->mapScriptString(&scriptStringTable[i]);
|
||||
builder->mapScriptString(scriptStringTable[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->hideTags);
|
||||
|
@ -264,7 +264,7 @@ namespace Assets
|
||||
|
||||
for (char i = 0; i < asset->boneCount[Game::PART_TYPE_ALL]; ++i)
|
||||
{
|
||||
builder->mapScriptString(&destTagnames[i]);
|
||||
builder->mapScriptString(destTagnames[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->names);
|
||||
@ -280,7 +280,7 @@ namespace Assets
|
||||
|
||||
for (char i = 0; i < asset->notifyCount; ++i)
|
||||
{
|
||||
builder->mapScriptString(&destNotetracks[i].name);
|
||||
builder->mapScriptString(destNotetracks[i].name);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->notify);
|
||||
|
@ -1,7 +1,7 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IXModel.hpp"
|
||||
|
||||
#define IW4X_MODEL_VERSION 5
|
||||
#define IW4X_MODEL_VERSION 8
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
@ -9,32 +9,32 @@ namespace Assets
|
||||
{
|
||||
if (entry->nodes)
|
||||
{
|
||||
entry->nodes = reader->readArray<Game::XSurfaceCollisionNode>(entry->nodeCount);
|
||||
entry->nodes = reader->readArrayOnce<Game::XSurfaceCollisionNode>(entry->nodeCount);
|
||||
}
|
||||
|
||||
if (entry->leafs)
|
||||
{
|
||||
entry->leafs = reader->readArray<Game::XSurfaceCollisionLeaf>(entry->leafCount);
|
||||
entry->leafs = reader->readArrayOnce<Game::XSurfaceCollisionLeaf>(entry->leafCount);
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
||||
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (surf->vertInfo.vertsBlend)
|
||||
{
|
||||
surf->vertInfo.vertsBlend = reader->readArray<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
||||
surf->vertInfo.vertsBlend = reader->readArrayOnce<unsigned short>(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
||||
}
|
||||
|
||||
// Access vertex block
|
||||
if (surf->verts0)
|
||||
{
|
||||
surf->verts0 = reader->readArray<Game::GfxPackedVertex>(surf->vertCount);
|
||||
surf->verts0 = reader->readArrayOnce<Game::GfxPackedVertex>(surf->vertCount);
|
||||
}
|
||||
|
||||
// Save_XRigidVertListArray
|
||||
if (surf->vertList)
|
||||
{
|
||||
surf->vertList = reader->readArray<Game::XRigidVertList>(surf->vertListCount);
|
||||
surf->vertList = reader->readArrayOnce<Game::XRigidVertList>(surf->vertListCount);
|
||||
|
||||
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
||||
{
|
||||
@ -51,17 +51,7 @@ namespace Assets
|
||||
// Access index block
|
||||
if (surf->triIndices)
|
||||
{
|
||||
void* oldPtr = surf->triIndices;
|
||||
surf->triIndices = reader->readArray<unsigned short>(surf->triCount * 3);
|
||||
|
||||
if (builder->getAllocator()->isPointerMapped(oldPtr))
|
||||
{
|
||||
surf->triIndices = builder->getAllocator()->getPointer<unsigned short>(oldPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->getAllocator()->mapPointer(oldPtr, surf->triIndices);
|
||||
}
|
||||
surf->triIndices = reader->readArrayOnce<unsigned short>(surf->triCount * 3);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +64,7 @@ namespace Assets
|
||||
|
||||
if (asset->surfs)
|
||||
{
|
||||
asset->surfs = reader->readArray<Game::XSurface>(asset->numsurfs);
|
||||
asset->surfs = reader->readArrayOnce<Game::XSurface>(asset->numsurfs);
|
||||
|
||||
for (int i = 0; i < asset->numsurfs; ++i)
|
||||
{
|
||||
@ -110,11 +100,6 @@ namespace Assets
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version);
|
||||
}
|
||||
|
||||
if (version == 4)
|
||||
{
|
||||
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Model '{}' is in legacy format, please update it!\n", name);
|
||||
}
|
||||
|
||||
Game::XModel* asset = reader.readObject<Game::XModel>();
|
||||
|
||||
if (asset->name)
|
||||
@ -134,27 +119,27 @@ namespace Assets
|
||||
|
||||
if (asset->parentList)
|
||||
{
|
||||
asset->parentList = reader.readArray<char>(asset->numBones - asset->numRootBones);
|
||||
asset->parentList = reader.readArrayOnce<unsigned char>(asset->numBones - asset->numRootBones);
|
||||
}
|
||||
|
||||
if (asset->quats)
|
||||
{
|
||||
asset->quats = reader.readArray<short>((asset->numBones - asset->numRootBones) * 4);
|
||||
asset->quats = reader.readArrayOnce<short>((asset->numBones - asset->numRootBones) * 4);
|
||||
}
|
||||
|
||||
if (asset->trans)
|
||||
{
|
||||
asset->trans = reader.readArray<float>((asset->numBones - asset->numRootBones) * 3);
|
||||
asset->trans = reader.readArrayOnce<float>((asset->numBones - asset->numRootBones) * 3);
|
||||
}
|
||||
|
||||
if (asset->partClassification)
|
||||
{
|
||||
asset->partClassification = reader.readArray<char>(asset->numBones);
|
||||
asset->partClassification = reader.readArrayOnce<unsigned char>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->baseMat)
|
||||
{
|
||||
asset->baseMat = reader.readArray<Game::DObjAnimMat>(asset->numBones);
|
||||
asset->baseMat = reader.readArrayOnce<Game::DObjAnimMat>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->materialHandles)
|
||||
@ -172,7 +157,7 @@ namespace Assets
|
||||
|
||||
// Save_XModelLodInfoArray
|
||||
{
|
||||
for (int i = 0; i < 4; ++i)
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (asset->lodInfo[i].modelSurfs)
|
||||
{
|
||||
@ -247,60 +232,53 @@ namespace Assets
|
||||
|
||||
if (asset->physCollmap)
|
||||
{
|
||||
if (version == 4)
|
||||
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
|
||||
asset->physCollmap = collmap;
|
||||
|
||||
if (collmap->name)
|
||||
{
|
||||
asset->physCollmap = nullptr;
|
||||
collmap->name = reader.readCString();
|
||||
}
|
||||
else
|
||||
|
||||
if (collmap->geoms)
|
||||
{
|
||||
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
|
||||
asset->physCollmap = collmap;
|
||||
collmap->geoms = reader.readArray<Game::PhysGeomInfo>(collmap->count);
|
||||
|
||||
if (collmap->name)
|
||||
for (unsigned int i = 0; i < collmap->count; ++i)
|
||||
{
|
||||
collmap->name = reader.readCString();
|
||||
}
|
||||
Game::PhysGeomInfo* geom = &collmap->geoms[i];
|
||||
|
||||
if (collmap->geoms)
|
||||
{
|
||||
collmap->geoms = reader.readArray<Game::PhysGeomInfo>(collmap->count);
|
||||
|
||||
for (unsigned int i = 0; i < collmap->count; ++i)
|
||||
if (geom->brushWrapper)
|
||||
{
|
||||
Game::PhysGeomInfo* geom = &collmap->geoms[i];
|
||||
|
||||
if (geom->brushWrapper)
|
||||
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
|
||||
geom->brushWrapper = brush;
|
||||
{
|
||||
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
|
||||
geom->brushWrapper = brush;
|
||||
if (brush->brush.sides)
|
||||
{
|
||||
if (brush->brush.sides)
|
||||
brush->brush.sides = reader.readArray<Game::cbrushside_t>(brush->brush.numsides);
|
||||
for (unsigned short j = 0; j < brush->brush.numsides; ++j)
|
||||
{
|
||||
brush->brush.sides = reader.readArray<Game::cbrushside_t>(brush->brush.numsides);
|
||||
for (unsigned short j = 0; j < brush->brush.numsides; ++j)
|
||||
{
|
||||
Game::cbrushside_t* side = &brush->brush.sides[j];
|
||||
Game::cbrushside_t* side = &brush->brush.sides[j];
|
||||
|
||||
// TODO: Add pointer support
|
||||
if (side->plane)
|
||||
{
|
||||
side->plane = reader.readObject<Game::cplane_s>();
|
||||
}
|
||||
// TODO: Add pointer support
|
||||
if (side->plane)
|
||||
{
|
||||
side->plane = reader.readObject<Game::cplane_s>();
|
||||
}
|
||||
}
|
||||
|
||||
if (brush->brush.baseAdjacentSide)
|
||||
{
|
||||
brush->brush.baseAdjacentSide = reader.readArray<char>(brush->totalEdgeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add pointer support
|
||||
if (brush->planes)
|
||||
if (brush->brush.baseAdjacentSide)
|
||||
{
|
||||
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides);
|
||||
brush->brush.baseAdjacentSide = reader.readArray<char>(brush->totalEdgeCount);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add pointer support
|
||||
if (brush->planes)
|
||||
{
|
||||
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,7 +364,7 @@ namespace Assets
|
||||
|
||||
for (char i = 0; i < asset->numBones; ++i)
|
||||
{
|
||||
builder->mapScriptString(&destBoneNames[i]);
|
||||
builder->mapScriptString(destBoneNames[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->boneNames);
|
||||
@ -394,37 +372,77 @@ namespace Assets
|
||||
|
||||
if (asset->parentList)
|
||||
{
|
||||
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
|
||||
Utils::Stream::ClearPointer(&dest->parentList);
|
||||
if (builder->hasPointer(asset->parentList))
|
||||
{
|
||||
dest->parentList = builder->getPointer(asset->parentList);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->storePointer(asset->parentList);
|
||||
buffer->save(asset->parentList, asset->numBones - asset->numRootBones);
|
||||
Utils::Stream::ClearPointer(&dest->parentList);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->quats)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
|
||||
Utils::Stream::ClearPointer(&dest->quats);
|
||||
if (builder->hasPointer(asset->quats))
|
||||
{
|
||||
dest->quats = builder->getPointer(asset->quats);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
builder->storePointer(asset->quats);
|
||||
buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4);
|
||||
Utils::Stream::ClearPointer(&dest->quats);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->trans)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
|
||||
Utils::Stream::ClearPointer(&dest->trans);
|
||||
if (builder->hasPointer(asset->trans))
|
||||
{
|
||||
dest->trans = builder->getPointer(asset->trans);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
builder->storePointer(asset->trans);
|
||||
buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3);
|
||||
Utils::Stream::ClearPointer(&dest->trans);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->partClassification)
|
||||
{
|
||||
buffer->save(asset->partClassification, asset->numBones);
|
||||
Utils::Stream::ClearPointer(&dest->partClassification);
|
||||
if (builder->hasPointer(asset->partClassification))
|
||||
{
|
||||
dest->partClassification = builder->getPointer(asset->partClassification);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->storePointer(asset->partClassification);
|
||||
buffer->save(asset->partClassification, asset->numBones);
|
||||
Utils::Stream::ClearPointer(&dest->partClassification);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->baseMat)
|
||||
{
|
||||
AssertSize(Game::DObjAnimMat, 32);
|
||||
if (builder->hasPointer(asset->baseMat))
|
||||
{
|
||||
dest->baseMat = builder->getPointer(asset->baseMat);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
builder->storePointer(asset->baseMat);
|
||||
buffer->saveArray(asset->baseMat, asset->numBones);
|
||||
Utils::Stream::ClearPointer(&dest->baseMat);
|
||||
}
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
buffer->saveArray(asset->baseMat, asset->numBones);
|
||||
Utils::Stream::ClearPointer(&dest->baseMat);
|
||||
}
|
||||
|
||||
if (asset->materialHandles)
|
||||
|
@ -37,9 +37,18 @@ namespace Assets
|
||||
|
||||
if (surf->vertInfo.vertsBlend)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
||||
Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend);
|
||||
if (builder->hasPointer(surf->vertInfo.vertsBlend))
|
||||
{
|
||||
destSurf->vertInfo.vertsBlend = builder->getPointer(surf->vertInfo.vertsBlend);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
builder->storePointer(surf->vertInfo.vertsBlend);
|
||||
buffer->saveArray(surf->vertInfo.vertsBlend, surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7));
|
||||
Utils::Stream::ClearPointer(&destSurf->vertInfo.vertsBlend);
|
||||
}
|
||||
}
|
||||
|
||||
// Access vertex block
|
||||
@ -48,9 +57,17 @@ namespace Assets
|
||||
{
|
||||
AssertSize(Game::GfxPackedVertex, 32);
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_16);
|
||||
buffer->saveArray(surf->verts0, surf->vertCount);
|
||||
Utils::Stream::ClearPointer(&destSurf->verts0);
|
||||
if (builder->hasPointer(surf->verts0))
|
||||
{
|
||||
destSurf->verts0 = builder->getPointer(surf->verts0);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_16);
|
||||
builder->storePointer(surf->verts0);
|
||||
buffer->saveArray(surf->verts0, surf->vertCount);
|
||||
Utils::Stream::ClearPointer(&destSurf->verts0);
|
||||
}
|
||||
}
|
||||
buffer->popBlock();
|
||||
|
||||
@ -59,25 +76,40 @@ namespace Assets
|
||||
{
|
||||
AssertSize(Game::XRigidVertList, 12);
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
Game::XRigidVertList* destCt = buffer->dest<Game::XRigidVertList>();
|
||||
buffer->saveArray(surf->vertList, surf->vertListCount);
|
||||
|
||||
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
||||
if (builder->hasPointer(surf->vertList))
|
||||
{
|
||||
Game::XRigidVertList* destRigidVertList = &destCt[i];
|
||||
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
|
||||
|
||||
if (rigidVertList->collisionTree)
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder);
|
||||
Utils::Stream::ClearPointer(&destRigidVertList->collisionTree);
|
||||
}
|
||||
destSurf->vertList = builder->getPointer(surf->vertList);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
builder->storePointer(surf->vertList);
|
||||
|
||||
Utils::Stream::ClearPointer(&destSurf->vertList);
|
||||
Game::XRigidVertList* destCt = buffer->dest<Game::XRigidVertList>();
|
||||
buffer->saveArray(surf->vertList, surf->vertListCount);
|
||||
|
||||
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
||||
{
|
||||
Game::XRigidVertList* destRigidVertList = &destCt[i];
|
||||
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
|
||||
|
||||
if (rigidVertList->collisionTree)
|
||||
{
|
||||
if (builder->hasPointer(rigidVertList->collisionTree))
|
||||
{
|
||||
destRigidVertList->collisionTree = builder->getPointer(rigidVertList->collisionTree);
|
||||
}
|
||||
else {
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
builder->storePointer(rigidVertList->collisionTree);
|
||||
this->saveXSurfaceCollisionTree(rigidVertList->collisionTree, builder);
|
||||
Utils::Stream::ClearPointer(&destRigidVertList->collisionTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&destSurf->vertList);
|
||||
}
|
||||
}
|
||||
|
||||
// Access index block
|
||||
@ -89,6 +121,7 @@ namespace Assets
|
||||
else
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_16);
|
||||
builder->storePointer(surf->triIndices);
|
||||
buffer->saveArray(surf->triIndices, surf->triCount * 3);
|
||||
Utils::Stream::ClearPointer(&destSurf->triIndices);
|
||||
}
|
||||
|
@ -564,7 +564,7 @@ namespace Components
|
||||
bool Maps::IsCustomMap()
|
||||
{
|
||||
Game::GfxWorld*& gameWorld = *reinterpret_cast<Game::GfxWorld**>(0x66DEE94);
|
||||
if(gameWorld) return gameWorld->checksum == 0xDEADBEEF;
|
||||
if (gameWorld) return (gameWorld->checksum & 0xFFFF0000) == 0xC0D40000;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -619,7 +619,7 @@ namespace Components
|
||||
|
||||
for (unsigned int i = 0; i < gameWorld->dpvs.smodelCount; ++i)
|
||||
{
|
||||
if (gameWorld->dpvs.smodelDrawInsts[i].model->name == model)
|
||||
if (model == "all"s || gameWorld->dpvs.smodelDrawInsts[i].model->name == model)
|
||||
{
|
||||
gameWorld->dpvs.smodelVisData[0][i] = 0;
|
||||
gameWorld->dpvs.smodelVisData[1][i] = 0;
|
||||
@ -641,86 +641,6 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* Maps::GetDistortionDvar()
|
||||
{
|
||||
Game::dvar_t*& r_distortion = *reinterpret_cast<Game::dvar_t**>(0x69F0DCC);
|
||||
|
||||
if(Maps::IsCustomMap())
|
||||
{
|
||||
static Game::dvar_t noDistortion;
|
||||
ZeroMemory(&noDistortion, sizeof noDistortion);
|
||||
return &noDistortion;
|
||||
}
|
||||
|
||||
return r_distortion;
|
||||
}
|
||||
|
||||
__declspec(naked) void Maps::SetDistortionStub()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
pushad
|
||||
call Maps::GetDistortionDvar
|
||||
|
||||
mov [esp + 20h], eax
|
||||
popad
|
||||
|
||||
pop eax
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
Game::dvar_t* Maps::GetSpecularDvar()
|
||||
{
|
||||
Game::dvar_t*& r_specular = *reinterpret_cast<Game::dvar_t**>(0x69F0D94);
|
||||
static Game::dvar_t* r_specularCustomMaps = Game::Dvar_RegisterBool("r_specularCustomMaps", false, Game::DVAR_ARCHIVE, "Allows shaders to use phong specular lighting on custom maps");
|
||||
|
||||
if (Maps::IsCustomMap())
|
||||
{
|
||||
if (!r_specularCustomMaps->current.enabled)
|
||||
{
|
||||
static Game::dvar_t noSpecular;
|
||||
ZeroMemory(&noSpecular, sizeof noSpecular);
|
||||
return &noSpecular;
|
||||
}
|
||||
}
|
||||
|
||||
return r_specular;
|
||||
}
|
||||
|
||||
__declspec(naked) void Maps::SetSpecularStub1()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
pushad
|
||||
call Maps::GetSpecularDvar
|
||||
|
||||
mov [esp + 20h], eax
|
||||
popad
|
||||
|
||||
pop eax
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void Maps::SetSpecularStub2()
|
||||
{
|
||||
__asm
|
||||
{
|
||||
push eax
|
||||
pushad
|
||||
call Maps::GetSpecularDvar
|
||||
|
||||
mov [esp + 20h], eax
|
||||
popad
|
||||
|
||||
pop edx
|
||||
retn
|
||||
}
|
||||
}
|
||||
|
||||
void Maps::G_SpawnTurretHook(Game::gentity_s* ent, int unk, int unk2)
|
||||
{
|
||||
if (Maps::CurrentMainZone == "mp_backlot_sh"s || Maps::CurrentMainZone == "mp_con_spring"s ||
|
||||
@ -842,16 +762,6 @@ namespace Components
|
||||
// Allow loading raw suns
|
||||
Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick();
|
||||
|
||||
// Disable distortion on custom maps
|
||||
//Utils::Hook(0x50AA47, Maps::SetDistortionStub, HOOK_CALL).install()->quick();
|
||||
|
||||
// Disable speculars on custom maps
|
||||
Utils::Hook(0x525EA6, Maps::SetSpecularStub1, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x51FBC7, Maps::SetSpecularStub2, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x522A2E, Maps::SetSpecularStub2, HOOK_CALL).install()->quick();
|
||||
Utils::Hook::Nop(0x51FBCC, 1);
|
||||
Utils::Hook::Nop(0x522A33, 1);
|
||||
|
||||
// Intercept map loading for usermap initialization
|
||||
Utils::Hook(0x6245E3, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
||||
Utils::Hook(0x62493E, Maps::SpawnServerStub, HOOK_CALL).install()->quick();
|
||||
|
@ -755,18 +755,6 @@ namespace Components
|
||||
Menus::CustomMenus.push_back(menu);
|
||||
}
|
||||
|
||||
void Menus::RegisterCustomMenusHook()
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x401700)(); // call original load functions
|
||||
|
||||
#ifdef _DEBUG
|
||||
for (int i = 0; i < Game::uiContext->menuCount; i++)
|
||||
{
|
||||
OutputDebugStringA(Utils::String::VA("%s\n", Game::uiContext->Menus[i]->window.name));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Menus::Menus()
|
||||
{
|
||||
if (ZoneBuilder::IsEnabled())
|
||||
@ -786,9 +774,6 @@ namespace Components
|
||||
// Don't open connect menu
|
||||
//Utils::Hook::Nop(0x428E48, 5);
|
||||
|
||||
// register custom menufiles if they exist
|
||||
Utils::Hook(0x4A58C3, Menus::RegisterCustomMenusHook, HOOK_CALL).install()->quick();
|
||||
|
||||
// Use the connect menu open call to update server motds
|
||||
Utils::Hook(0x428E48, []()
|
||||
{
|
||||
|
@ -56,8 +56,6 @@ namespace Components
|
||||
|
||||
static void RemoveMenuFromContext(Game::UiContext* dc, Game::menuDef_t* menu);
|
||||
|
||||
static void RegisterCustomMenusHook();
|
||||
|
||||
// Ugly!
|
||||
static int KeywordHash(char* key);
|
||||
};
|
||||
|
@ -14,6 +14,7 @@ namespace Components
|
||||
Dvar::Var Renderer::r_drawModelNames;
|
||||
Dvar::Var Renderer::r_drawAABBTrees;
|
||||
Dvar::Var Renderer::r_playerDrawDebugDistance;
|
||||
Dvar::Var Renderer::r_forceTechnique;
|
||||
|
||||
float cyan[4] = { 0.0f, 0.5f, 0.5f, 1.0f };
|
||||
float red[4] = { 1.0f, 0.0f, 0.0f, 1.0f };
|
||||
@ -171,6 +172,32 @@ namespace Components
|
||||
return Utils::Hook::Call<int(int, float, float, const char*, Game::vec4_t*, int)>(0x005033E0)(a1, a2, a3, Utils::String::VA("%s (^3%s^7)", mat->info.name, mat->techniqueSet->name), color, a6);
|
||||
}
|
||||
|
||||
void ListSamplers()
|
||||
{
|
||||
static auto* source = reinterpret_cast<Game::GfxCmdBufSourceState*>(0x6CAF080);
|
||||
|
||||
Game::Font_s* font = Game::R_RegisterFont("fonts/smallFont", 0);
|
||||
auto height = Game::R_TextHeight(font);
|
||||
auto scale = 1.0f;
|
||||
float color[4] = {0.0f, 1.0f, 0.0f, 1.0f};
|
||||
|
||||
for (std::size_t i = 0; i < 27; ++i)
|
||||
{
|
||||
if (source->input.codeImages[i] == nullptr)
|
||||
{
|
||||
color[0] = 1.f;
|
||||
}
|
||||
else
|
||||
{
|
||||
color[0] = 0.f;
|
||||
}
|
||||
|
||||
std::stringstream str;
|
||||
str << std::format("{}/{:#X} => ", i, i) << (source->input.codeImages[i] == nullptr ? "---" : source->input.codeImages[i]->name) << " " << std::to_string(source->input.codeImageSamplerStates[i]);
|
||||
Game::R_AddCmdDrawText(str.str().data(), std::numeric_limits<int>::max(), font, 15.0f, (height * scale + 1) * (i + 1) + 14.0f, scale, scale, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::DebugDrawTriggers()
|
||||
{
|
||||
if (!r_drawTriggers.get<bool>()) return;
|
||||
@ -326,21 +353,15 @@ namespace Components
|
||||
// Static models
|
||||
for (size_t i = 0; i < world->dpvs.smodelCount; i++)
|
||||
{
|
||||
auto staticModel = world->dpvs.smodelDrawInsts[i];
|
||||
auto staticModel = &world->dpvs.smodelDrawInsts[i];
|
||||
Game::Bounds* b = &world->dpvs.smodelInsts[i].bounds;
|
||||
|
||||
if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel.placement.origin) < sqrDist)
|
||||
if (Utils::Maths::Vec3SqrDistance(playerPosition, staticModel->placement.origin) < sqrDist)
|
||||
{
|
||||
if (staticModel.model)
|
||||
if (staticModel->model)
|
||||
{
|
||||
Game::Bounds b = staticModel.model->bounds;
|
||||
b.midPoint[0] += staticModel.placement.origin[0];
|
||||
b.midPoint[1] += staticModel.placement.origin[1];
|
||||
b.midPoint[2] += staticModel.placement.origin[2];
|
||||
b.halfSize[0] *= staticModel.placement.scale;
|
||||
b.halfSize[1] *= staticModel.placement.scale;
|
||||
b.halfSize[2] *= staticModel.placement.scale;
|
||||
|
||||
Game::R_AddDebugBounds(staticModelsColor, &b);
|
||||
Game::R_AddDebugBounds(staticModelsColor, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -447,6 +468,29 @@ namespace Components
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::ForceTechnique()
|
||||
{
|
||||
auto forceTechnique = r_forceTechnique.get<int>();
|
||||
|
||||
if (forceTechnique > 0)
|
||||
{
|
||||
Utils::Hook::Set(0x6FABDF4, forceTechnique);
|
||||
}
|
||||
}
|
||||
|
||||
int Renderer::FixSunShadowPartitionSize(Game::GfxCamera* camera, Game::GfxSunShadowMapMetrics* mapMetrics, Game::GfxSunShadow* sunShadow, Game::GfxSunShadowClip* clip, float* partitionFraction)
|
||||
{
|
||||
auto result = Utils::Hook::Call<int(Game::GfxCamera*, Game::GfxSunShadowMapMetrics*, Game::GfxSunShadow*, Game::GfxSunShadowClip*, float*)>(0x5463B0)(camera, mapMetrics, sunShadow, clip, partitionFraction);
|
||||
|
||||
if (Maps::IsCustomMap())
|
||||
{
|
||||
// Fixes shadowmap viewport which fixes pixel adjustment shadowmap bug - partly, because the real problem lies within the way CoD4 shaders are programmed
|
||||
sunShadow->partition[Game::SunShadowPartition::R_SUNSHADOW_FAR].viewportParms.viewport = sunShadow->partition[Game::SunShadowPartition::R_SUNSHADOW_NEAR].viewportParms.viewport;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Renderer::Renderer()
|
||||
{
|
||||
if (Dedicated::IsEnabled()) return;
|
||||
@ -460,9 +504,14 @@ namespace Components
|
||||
DebugDrawModelBoundingBoxes();
|
||||
DebugDrawSceneModelCollisions();
|
||||
DebugDrawTriggers();
|
||||
ForceTechnique();
|
||||
}
|
||||
}, Scheduler::Pipeline::RENDERER);
|
||||
|
||||
// COD4 Map Fixes
|
||||
// The day map porting is perfect we should be able to remove these
|
||||
Utils::Hook(0x546A09, FixSunShadowPartitionSize, HOOK_CALL).install()->quick();
|
||||
|
||||
// Log broken materials
|
||||
Utils::Hook(0x0054CAAA, Renderer::StoreGfxBufContextPtrStub1, HOOK_JUMP).install()->quick();
|
||||
Utils::Hook(0x0054CF8D, Renderer::StoreGfxBufContextPtrStub2, HOOK_JUMP).install()->quick();
|
||||
@ -510,6 +559,7 @@ namespace Components
|
||||
Renderer::r_drawModelNames = Game::Dvar_RegisterEnum("r_drawModelNames", values, 0, Game::DVAR_CHEAT, "Draw all model names");
|
||||
Renderer::r_drawAABBTrees = Game::Dvar_RegisterBool("r_drawAabbTrees", false, Game::DVAR_CHEAT, "Draw aabb trees");
|
||||
Renderer::r_playerDrawDebugDistance = Game::Dvar_RegisterInt("r_drawDebugDistance", 1000, 0, 50000, Game::DVAR_ARCHIVE, "r_draw debug functions draw distance, relative to the player");
|
||||
Renderer::r_forceTechnique = Game::Dvar_RegisterInt("r_forceTechnique", 0, 0, 14, Game::DVAR_NONE, "Force a base technique on the renderer");
|
||||
}, Scheduler::Pipeline::MAIN);
|
||||
}
|
||||
|
||||
|
@ -39,6 +39,9 @@ namespace Components
|
||||
static void DebugDrawModelBoundingBoxes();
|
||||
static void DebugDrawModelNames();
|
||||
static void DebugDrawAABBTrees();
|
||||
static void ForceTechnique();
|
||||
|
||||
static int FixSunShadowPartitionSize(Game::GfxCamera* camera, Game::GfxSunShadowMapMetrics* mapMetrics, Game::GfxSunShadow* sunShadow, Game::GfxSunShadowClip* clip, float* partitionFraction);
|
||||
|
||||
static Utils::Signal<Renderer::Callback> EndRecoverDeviceSignal;
|
||||
static Utils::Signal<Renderer::Callback> BeginRecoverDeviceSignal;
|
||||
@ -52,5 +55,6 @@ namespace Components
|
||||
static Dvar::Var r_drawModelNames;
|
||||
static Dvar::Var r_drawAABBTrees;
|
||||
static Dvar::Var r_playerDrawDebugDistance;
|
||||
static Dvar::Var r_forceTechnique;
|
||||
};
|
||||
}
|
||||
|
@ -225,6 +225,9 @@ namespace Components
|
||||
// Sanitize name for empty assets
|
||||
if (name[0] == ',') name.erase(name.begin());
|
||||
|
||||
// Fix forward slashes for FXEffectDef (and probably other assets)
|
||||
std::replace(name.begin(), name.end(), '\\', '/');
|
||||
|
||||
if (this->findAsset(type, name) != -1 || this->findSubAsset(type, name).data) return true;
|
||||
|
||||
if (type == Game::XAssetType::ASSET_TYPE_INVALID || type >= Game::XAssetType::ASSET_TYPE_COUNT)
|
||||
@ -245,6 +248,9 @@ namespace Components
|
||||
asset.type = type;
|
||||
asset.header = assetHeader;
|
||||
|
||||
// Handle script strings
|
||||
AssetHandler::ZoneMark(asset, this);
|
||||
|
||||
if (isSubAsset)
|
||||
{
|
||||
this->loadedSubAssets.push_back(asset);
|
||||
@ -254,8 +260,6 @@ namespace Components
|
||||
this->loadedAssets.push_back(asset);
|
||||
}
|
||||
|
||||
// Handle script strings
|
||||
AssetHandler::ZoneMark(asset, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -447,38 +451,30 @@ namespace Components
|
||||
Utils::Stream::ClearPointer(&zoneHeader.assetList.assets);
|
||||
|
||||
// Increment ScriptStrings count (for empty script string) if available
|
||||
if (!this->scriptStrings.empty())
|
||||
{
|
||||
zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1;
|
||||
Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings);
|
||||
}
|
||||
zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1;
|
||||
Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings);
|
||||
|
||||
// Write header
|
||||
this->buffer.save(&zoneHeader, sizeof(Game::ZoneHeader));
|
||||
this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); // Push main stream onto the stream stack
|
||||
|
||||
// Write ScriptStrings, if available
|
||||
if (!this->scriptStrings.empty())
|
||||
this->buffer.saveNull(4);
|
||||
// Empty script string?
|
||||
// This actually represents a NULL string, but as scriptString.
|
||||
// So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString.
|
||||
// That's the reason why the count is incremented by 1, if scriptStrings are available.
|
||||
|
||||
// Write ScriptString pointer table
|
||||
for (std::size_t i = 0; i < this->scriptStrings.size(); ++i)
|
||||
{
|
||||
this->buffer.saveNull(4);
|
||||
// Empty script string?
|
||||
// This actually represents a NULL string, but as scriptString.
|
||||
// So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString.
|
||||
// That's the reason why the count is incremented by 1, if scriptStrings are available.
|
||||
this->buffer.saveMax(4);
|
||||
}
|
||||
|
||||
// Write ScriptString pointer table
|
||||
for (std::size_t i = 0; i < this->scriptStrings.size(); ++i)
|
||||
{
|
||||
this->buffer.saveMax(4);
|
||||
}
|
||||
|
||||
this->buffer.align(Utils::Stream::ALIGN_4);
|
||||
|
||||
// Write ScriptStrings
|
||||
for (auto ScriptString : this->scriptStrings)
|
||||
{
|
||||
this->buffer.saveString(ScriptString.data());
|
||||
}
|
||||
// Write ScriptStrings
|
||||
for (auto ScriptString : this->scriptStrings)
|
||||
{
|
||||
this->buffer.saveString(ScriptString.data());
|
||||
}
|
||||
|
||||
// Align buffer (4 bytes) to get correct offsets for pointers
|
||||
@ -530,7 +526,7 @@ namespace Components
|
||||
constexpr auto* data = "Built using the IW4x Zone:B:uilder Version 4";
|
||||
auto dataLen = std::strlen(data); // + 1 is added by the save code
|
||||
|
||||
this->branding = { this->zoneName.data(), 0, static_cast<int>(dataLen), data };
|
||||
this->branding = {this->zoneName.data(), 0, static_cast<int>(dataLen), data};
|
||||
|
||||
if (this->findAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, this->branding.name) != -1)
|
||||
{
|
||||
@ -641,9 +637,9 @@ namespace Components
|
||||
}
|
||||
|
||||
// Remap a scriptString to it's corresponding value in the local scriptString table.
|
||||
void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex)
|
||||
void ZoneBuilder::Zone::mapScriptString(unsigned short& gameIndex)
|
||||
{
|
||||
*gameIndex = 0xFFFF & this->scriptStringMap[*gameIndex];
|
||||
gameIndex = 0xFFFF & this->scriptStringMap[gameIndex];
|
||||
}
|
||||
|
||||
// Store a new name for a given asset
|
||||
@ -1006,6 +1002,19 @@ namespace Components
|
||||
return "";
|
||||
}
|
||||
|
||||
void ZoneBuilder::ReallocateLoadedSounds(void*& data, [[maybe_unused]] void* a2)
|
||||
{
|
||||
assert(data);
|
||||
auto* sound = Utils::Hook::Get<Game::MssSound*>(0x112AE04);
|
||||
auto length = sound->info.data_len;
|
||||
auto allocatedSpace = Utils::Memory::AllocateArray<char>(length);
|
||||
memcpy_s(allocatedSpace, length, data, length);
|
||||
|
||||
data = allocatedSpace;
|
||||
sound->data = allocatedSpace;
|
||||
sound->info.data_ptr = allocatedSpace;
|
||||
}
|
||||
|
||||
ZoneBuilder::ZoneBuilder()
|
||||
{
|
||||
// ReSharper disable CppStaticAssertFailure
|
||||
@ -1048,7 +1057,7 @@ namespace Components
|
||||
//Utils::Hook::Nop(0x5BB632, 5);
|
||||
|
||||
// Don't load sounds
|
||||
//Utils::Hook::Set<BYTE>(0x413430, 0xC3);
|
||||
Utils::Hook(0x492EFC, ReallocateLoadedSounds, HOOK_CALL).install()->quick();
|
||||
|
||||
// Don't display errors when assets are missing (we might manually build those)
|
||||
Utils::Hook::Nop(0x5BB3F2, 5);
|
||||
@ -1228,6 +1237,41 @@ namespace Components
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader asset, [[maybe_unused]] const std::string& name, [[maybe_unused]] bool* restrict)
|
||||
{
|
||||
if (type != Game::ASSET_TYPE_SOUND)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto sound = asset.sound;
|
||||
|
||||
for (size_t i = 0; i < sound->count; i++)
|
||||
{
|
||||
auto thisSound = sound->head[i];
|
||||
|
||||
if (thisSound.soundFile->type == Game::SAT_LOADED)
|
||||
{
|
||||
if (thisSound.soundFile->u.loadSnd->sound.data == nullptr)
|
||||
{
|
||||
// ouch
|
||||
// This should never happen and will cause a memory leak
|
||||
// Let's change it to a streamed sound instead
|
||||
thisSound.soundFile->type = Game::SAT_STREAMED;
|
||||
|
||||
auto virtualPath = std::filesystem::path(thisSound.soundFile->u.loadSnd->name);
|
||||
|
||||
thisSound.soundFile->u.streamSnd.filename.info.raw.name = Utils::Memory::DuplicateString(virtualPath.filename().string());
|
||||
|
||||
auto dir = virtualPath.remove_filename().string();
|
||||
dir = dir.substr(0, dir.size() - 1); // remove /
|
||||
thisSound.soundFile->u.streamSnd.filename.info.raw.dir = Utils::Memory::DuplicateString(dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Command::Add("buildtechsets", [](Command::Params*)
|
||||
{
|
||||
Utils::IO::CreateDir("zone_source/techsets");
|
||||
|
@ -60,7 +60,7 @@ namespace Components
|
||||
int findScriptString(const std::string& str);
|
||||
void addRawAsset(Game::XAssetType type, void* ptr);
|
||||
|
||||
void mapScriptString(unsigned short* gameIndex);
|
||||
void mapScriptString(unsigned short& gameIndex);
|
||||
|
||||
void renameAsset(Game::XAssetType type, const std::string& asset, const std::string& newName);
|
||||
std::string getAssetName(Game::XAssetType type, const std::string& asset);
|
||||
@ -138,6 +138,7 @@ namespace Components
|
||||
static void ReleaseTexture(Game::XAssetHeader header);
|
||||
|
||||
static std::string FindMaterialByTechnique(const std::string& name);
|
||||
static void ReallocateLoadedSounds(void*& data, void* a2);
|
||||
|
||||
static int __stdcall EntryPoint(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/);
|
||||
static void HandleError(Game::errorParm_t code, const char* fmt, ...);
|
||||
|
@ -1456,7 +1456,7 @@ namespace Components
|
||||
{
|
||||
Game::MaterialShaderArgument* arg = &argument[i];
|
||||
|
||||
if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT)
|
||||
if (arg->type != Game::MaterialShaderArgumentType::MTL_ARG_CODE_VERTEX_CONST && arg->type != Game::MaterialShaderArgumentType::MTL_ARG_CODE_PIXEL_CONST)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -1730,7 +1730,7 @@ namespace Components
|
||||
{
|
||||
Game::GfxImageLoadDef* texture;
|
||||
char mapType;
|
||||
char semantic;
|
||||
Game::TextureSemantic semantic;
|
||||
char category;
|
||||
char flags;
|
||||
int cardMemory;
|
||||
|
@ -323,6 +323,7 @@ namespace Game
|
||||
PhysPreset*** varPhysPresetPtr = reinterpret_cast<PhysPreset***>(0x112B378);
|
||||
MaterialPass** varMaterialPass = reinterpret_cast<MaterialPass**>(0x112A960);
|
||||
snd_alias_list_t*** varsnd_alias_list_name = reinterpret_cast<snd_alias_list_t***>(0x112AF38);
|
||||
MaterialVertexShader** varMaterialVertexShader = reinterpret_cast<MaterialVertexShader**>(0x112B338);
|
||||
|
||||
FxElemField* s_elemFields = reinterpret_cast<FxElemField*>(0x73B848);
|
||||
|
||||
|
@ -646,6 +646,7 @@ namespace Game
|
||||
extern PhysPreset*** varPhysPresetPtr;
|
||||
extern MaterialPass** varMaterialPass;
|
||||
extern snd_alias_list_t*** varsnd_alias_list_name;
|
||||
extern MaterialVertexShader** varMaterialVertexShader;
|
||||
|
||||
extern FxElemField* s_elemFields;
|
||||
|
||||
|
1230
src/Game/Structs.hpp
1230
src/Game/Structs.hpp
File diff suppressed because it is too large
Load Diff
@ -11,8 +11,15 @@ namespace Utils::Compression
|
||||
// Make sure the buffer is large enough
|
||||
if (length < 100) length *= 10;
|
||||
|
||||
char* buffer = allocator.allocateArray<char>(length);
|
||||
if (compress2(reinterpret_cast<Bytef*>(buffer), &length, reinterpret_cast<Bytef*>(const_cast<char*>(data.data())), data.size(), Z_BEST_COMPRESSION) != Z_OK)
|
||||
auto* buffer = allocator.allocateArray<char>(length);
|
||||
|
||||
#ifdef _DEBUG
|
||||
constexpr auto compression = Z_NO_COMPRESSION;
|
||||
#else
|
||||
constexpr auto compression = Z_BEST_COMPRESSION;
|
||||
#endif
|
||||
|
||||
if (compress2((Bytef*)(buffer), &length, (const Bytef*)(data.data()), data.size(), compression) != Z_OK)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
@ -34,13 +41,13 @@ namespace Utils::Compression
|
||||
int ret;
|
||||
Memory::Allocator allocator;
|
||||
|
||||
std::uint8_t* dest = allocator.allocateArray<uint8_t>(CHUNK);
|
||||
const char* dataPtr = data.data();
|
||||
auto* dest = allocator.allocateArray<std::uint8_t>(CHUNK);
|
||||
const auto* dataPtr = data.data();
|
||||
|
||||
do
|
||||
{
|
||||
stream.avail_in = std::min(static_cast<size_t>(CHUNK), data.size() - (dataPtr - data.data()));
|
||||
stream.next_in = reinterpret_cast<const uint8_t*>(dataPtr);
|
||||
stream.avail_in = std::min(static_cast<std::size_t>(CHUNK), data.size() - (dataPtr - data.data()));
|
||||
stream.next_in = reinterpret_cast<const std::uint8_t*>(dataPtr);
|
||||
dataPtr += stream.avail_in;
|
||||
|
||||
do
|
||||
|
@ -35,7 +35,9 @@ namespace Utils
|
||||
{
|
||||
const auto& model = itr->second;
|
||||
|
||||
if (!model.empty() && model[0] != '*' && model[0] != '?') // Skip brushmodels
|
||||
if (!model.empty() && model[0] != '*' && model[0] != '?' && // Skip brushmodels
|
||||
model != "com_plasticcase_green_big_us_dirt"s // Team zones
|
||||
)
|
||||
{
|
||||
if (std::find(models.begin(), models.end(), model) == models.end())
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include <bitset>
|
||||
|
||||
namespace Utils::Json
|
||||
{
|
||||
@ -31,4 +32,32 @@ namespace Utils::Json
|
||||
return "null";
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long ReadFlags(const std::string binaryFlags, size_t size)
|
||||
{
|
||||
std::bitset<64> input;
|
||||
const auto binarySize = size * 8;
|
||||
|
||||
if (binaryFlags.size() > binarySize)
|
||||
{
|
||||
Components::Logger::Print("Flag {} might not be properly translated, it seems to contain an error (invalid length)\n", binaryFlags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto i = binarySize - 1;
|
||||
for (char bit : binaryFlags)
|
||||
{
|
||||
if (i < 0)
|
||||
{
|
||||
Components::Logger::Print("Flag {} might not be properly translated, it seems to contain an error (invalid length)\n", binaryFlags);
|
||||
break;
|
||||
}
|
||||
|
||||
bool isOne = bit == '1';
|
||||
input.set(i--, isOne);
|
||||
}
|
||||
|
||||
return input.to_ulong();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,4 +3,6 @@
|
||||
namespace Utils::Json
|
||||
{
|
||||
std::string TypeToString(nlohmann::json::value_t type);
|
||||
|
||||
unsigned long ReadFlags(const std::string binaryFlags, size_t size);
|
||||
}
|
||||
|
@ -39,10 +39,44 @@ namespace Utils
|
||||
{
|
||||
return readArray<T>(1);
|
||||
}
|
||||
|
||||
template <typename T> T* readArrayOnce(std::size_t count = 1)
|
||||
{
|
||||
constexpr auto POINTER = 255;
|
||||
constexpr auto FOLLOWING = 254;
|
||||
|
||||
auto b = static_cast<unsigned char>(readByte());
|
||||
switch (b)
|
||||
{
|
||||
case POINTER:
|
||||
{
|
||||
auto ptr = read<int>();
|
||||
auto* voidPtr = reinterpret_cast<void*>(ptr);
|
||||
|
||||
if (allocator->isPointerMapped(voidPtr))
|
||||
{
|
||||
return allocator->getPointer<T>(voidPtr);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Bad data: missing ptr");
|
||||
}
|
||||
case FOLLOWING:
|
||||
{
|
||||
auto filePosition = position;
|
||||
auto data = readArray<T>(count);
|
||||
allocator->mapPointer(reinterpret_cast<void*>(filePosition), data);
|
||||
return data;
|
||||
}
|
||||
default:
|
||||
throw std::runtime_error("Bad data");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> T* readArray(std::size_t count = 1)
|
||||
{
|
||||
return static_cast<T*>(this->read(sizeof(T), count));
|
||||
}
|
||||
|
||||
template <typename T> T read()
|
||||
{
|
||||
T obj;
|
||||
|
Loading…
x
Reference in New Issue
Block a user