IW4OF integration (#772)
Co-authored-by: Louvenarde <louve@louve.systems> Co-authored-by: Roxanne <roxanne@thegamebakers.com> Co-authored-by: FutureRave <edoardo.sanguineti222@gmail.com>
This commit is contained in:
parent
ffd7a59f5e
commit
f409b881e2
89
.gitmodules
vendored
89
.gitmodules
vendored
@ -1,43 +1,46 @@
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
branch = develop
|
||||
[submodule "deps/libtommath"]
|
||||
path = deps/libtommath
|
||||
url = https://github.com/libtom/libtommath.git
|
||||
branch = develop
|
||||
[submodule "deps/libtomcrypt"]
|
||||
path = deps/libtomcrypt
|
||||
url = https://github.com/libtom/libtomcrypt.git
|
||||
branch = develop
|
||||
[submodule "deps/pdcurses"]
|
||||
path = deps/pdcurses
|
||||
url = https://github.com/wmcbrine/PDCurses.git
|
||||
branch = master
|
||||
[submodule "deps/mongoose"]
|
||||
path = deps/mongoose
|
||||
url = https://github.com/cesanta/mongoose.git
|
||||
branch = 7.9
|
||||
[submodule "deps/protobuf"]
|
||||
path = deps/protobuf
|
||||
url = https://github.com/google/protobuf.git
|
||||
branch = 3.20.x
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/dxsdk"]
|
||||
path = deps/dxsdk
|
||||
url = https://github.com/devKlausS/dxsdk.git
|
||||
[submodule "deps/GSL"]
|
||||
path = deps/GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "deps/nlohmannjson"]
|
||||
path = deps/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
branch = v3.11.2
|
||||
[submodule "deps/discord-rpc"]
|
||||
path = deps/discord-rpc
|
||||
url = https://github.com/discord/discord-rpc.git
|
||||
[submodule "deps/rapidjson"]
|
||||
path = deps/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson.git
|
||||
[submodule "deps/zlib"]
|
||||
path = deps/zlib
|
||||
url = https://github.com/madler/zlib.git
|
||||
branch = develop
|
||||
[submodule "deps/libtommath"]
|
||||
path = deps/libtommath
|
||||
url = https://github.com/libtom/libtommath.git
|
||||
branch = develop
|
||||
[submodule "deps/libtomcrypt"]
|
||||
path = deps/libtomcrypt
|
||||
url = https://github.com/libtom/libtomcrypt.git
|
||||
branch = develop
|
||||
[submodule "deps/pdcurses"]
|
||||
path = deps/pdcurses
|
||||
url = https://github.com/wmcbrine/PDCurses.git
|
||||
branch = master
|
||||
[submodule "deps/mongoose"]
|
||||
path = deps/mongoose
|
||||
url = https://github.com/cesanta/mongoose.git
|
||||
branch = 7.9
|
||||
[submodule "deps/protobuf"]
|
||||
path = deps/protobuf
|
||||
url = https://github.com/google/protobuf.git
|
||||
branch = 3.20.x
|
||||
[submodule "deps/udis86"]
|
||||
path = deps/udis86
|
||||
url = https://github.com/vmt/udis86.git
|
||||
[submodule "deps/dxsdk"]
|
||||
path = deps/dxsdk
|
||||
url = https://github.com/devKlausS/dxsdk.git
|
||||
[submodule "deps/GSL"]
|
||||
path = deps/GSL
|
||||
url = https://github.com/microsoft/GSL.git
|
||||
[submodule "deps/nlohmannjson"]
|
||||
path = deps/json
|
||||
url = https://github.com/nlohmann/json.git
|
||||
branch = v3.11.2
|
||||
[submodule "deps/discord-rpc"]
|
||||
path = deps/discord-rpc
|
||||
url = https://github.com/discord/discord-rpc.git
|
||||
[submodule "deps/rapidjson"]
|
||||
path = deps/rapidjson
|
||||
url = https://github.com/Tencent/rapidjson.git
|
||||
[submodule "deps/iw4-open-formats"]
|
||||
path = deps/iw4-open-formats
|
||||
url = https://github.com/XLabsProject/iw4-open-formats.git
|
||||
|
1
deps/iw4-open-formats
vendored
Submodule
1
deps/iw4-open-formats
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 619302b5c30f372f3b22576d4553f5af637a6b10
|
44
deps/premake/iw4-open-formats.lua
vendored
Normal file
44
deps/premake/iw4-open-formats.lua
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
iw4_open_formats = {
|
||||
source = path.join(dependencies.basePath, "iw4-open-formats"),
|
||||
}
|
||||
|
||||
function iw4_open_formats.import()
|
||||
links "iw4-open-formats"
|
||||
|
||||
iw4_open_formats.includes()
|
||||
end
|
||||
|
||||
function iw4_open_formats.includes()
|
||||
includedirs {
|
||||
path.join(iw4_open_formats.source, "include")
|
||||
}
|
||||
end
|
||||
|
||||
function iw4_open_formats.project()
|
||||
project "iw4-open-formats"
|
||||
language "C++"
|
||||
|
||||
iw4_open_formats.includes()
|
||||
|
||||
pchheader "std_include.hpp"
|
||||
pchsource (path.join(iw4_open_formats.source, "src/iw4-of/std_include.cpp"))
|
||||
|
||||
files {
|
||||
path.join(iw4_open_formats.source, "src/iw4-of/**.hpp"),
|
||||
path.join(iw4_open_formats.source, "src/iw4-of/**.cpp"),
|
||||
}
|
||||
|
||||
includedirs {
|
||||
path.join(iw4_open_formats.source, "src/iw4-of"),
|
||||
path.join(iw4_open_formats.source, "include"),
|
||||
}
|
||||
|
||||
libtomcrypt.includes()
|
||||
libtommath.includes()
|
||||
rapidjson.includes()
|
||||
zlib.includes()
|
||||
|
||||
kind "StaticLib"
|
||||
end
|
||||
|
||||
table.insert(dependencies, iw4_open_formats)
|
@ -7,52 +7,7 @@ namespace Assets
|
||||
{
|
||||
void IComWorld::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 mapFile(std::format("comworld/{}.iw4xComWorld", name));
|
||||
|
||||
if (mapFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer());
|
||||
|
||||
__int64 magic = reader.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xComW", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading comworld '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
int version = reader.read<int>();
|
||||
if (version != IW4X_COMMAP_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading comworld '{}' failed, expected version is {}, but it was {}!", name, IW4X_COMMAP_VERSION, version);
|
||||
}
|
||||
|
||||
Game::ComWorld* asset = reader.readObject<Game::ComWorld>();
|
||||
header->comWorld = asset;
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->primaryLights)
|
||||
{
|
||||
asset->primaryLights = reader.readArray<Game::ComPrimaryLight>(asset->primaryLightCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
|
||||
{
|
||||
Game::ComPrimaryLight* light = &asset->primaryLights[i];
|
||||
|
||||
if (light->defName)
|
||||
{
|
||||
light->defName = reader.readCString();
|
||||
Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LIGHT_DEF, light->defName, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
header->comWorld = builder->getIW4OfApi()->read<Game::ComWorld>(Game::XAssetType::ASSET_TYPE_COMWORLD, _name);
|
||||
}
|
||||
|
||||
void IComWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -1,14 +1,12 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IFxEffectDef.hpp"
|
||||
|
||||
#define IW4X_FX_VERSION 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IFxEffectDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->data) this->loadEfx(header, name, builder); // Check if we have an editor fx
|
||||
if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game
|
||||
if (!header->data) this->loadFromIW4OF(header, name, builder); // Check if we need to import a new one into the game
|
||||
if (!header->data /*&& !builder->isPrimaryAsset()*/) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
}
|
||||
|
||||
@ -62,139 +60,9 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
|
||||
void IFxEffectDef::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
void IFxEffectDef::loadFromIW4OF(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File fxFile(std::format("fx/{}.iw4xFx", name));
|
||||
|
||||
if (fxFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader buffer(builder->getAllocator(), fxFile.getBuffer());
|
||||
|
||||
__int64 magic = buffer.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xFx ", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
int version = buffer.read<int>();
|
||||
if (version != IW4X_FX_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading fx '{}' failed, expected version is {}, but it was {}!", name, IW4X_FX_VERSION, version);
|
||||
}
|
||||
|
||||
Game::FxEffectDef* asset = buffer.readObject<Game::FxEffectDef>();
|
||||
header->fx = asset;
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = buffer.readCString();
|
||||
}
|
||||
|
||||
if (asset->elemDefs)
|
||||
{
|
||||
asset->elemDefs = buffer.readArray<Game::FxElemDef>(asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot);
|
||||
|
||||
for (int i = 0; i < (asset->elemDefCountEmission + asset->elemDefCountLooping + asset->elemDefCountOneShot); ++i)
|
||||
{
|
||||
Game::FxElemDef* elemDef = &asset->elemDefs[i];
|
||||
|
||||
if (elemDef->velSamples)
|
||||
{
|
||||
elemDef->velSamples = buffer.readArray<Game::FxElemVelStateSample>(elemDef->velIntervalCount + 1);
|
||||
}
|
||||
|
||||
if (elemDef->visSamples)
|
||||
{
|
||||
elemDef->visSamples = buffer.readArray<Game::FxElemVisStateSample>(elemDef->visStateIntervalCount + 1);
|
||||
}
|
||||
|
||||
// Save_FxElemDefVisuals
|
||||
{
|
||||
if (elemDef->elemType == Game::FX_ELEM_TYPE_DECAL)
|
||||
{
|
||||
if (elemDef->visuals.markArray)
|
||||
{
|
||||
elemDef->visuals.markArray = buffer.readArray<Game::FxElemMarkVisuals>(elemDef->visualCount);
|
||||
|
||||
for (char j = 0; j < elemDef->visualCount; ++j)
|
||||
{
|
||||
if (elemDef->visuals.markArray[j].materials[0])
|
||||
{
|
||||
elemDef->visuals.markArray[j].materials[0] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material;
|
||||
}
|
||||
|
||||
if (elemDef->visuals.markArray[j].materials[1])
|
||||
{
|
||||
elemDef->visuals.markArray[j].materials[1] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, buffer.readString(), builder).material;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (elemDef->visualCount > 1)
|
||||
{
|
||||
if (elemDef->visuals.array)
|
||||
{
|
||||
elemDef->visuals.array = buffer.readArray<Game::FxElemVisuals>(elemDef->visualCount);
|
||||
|
||||
for (char j = 0; j < elemDef->visualCount; ++j)
|
||||
{
|
||||
this->loadFxElemVisuals(&elemDef->visuals.array[j], elemDef->elemType, builder, &buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this->loadFxElemVisuals(&elemDef->visuals.instance, elemDef->elemType, builder, &buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if (elemDef->effectOnImpact.handle)
|
||||
{
|
||||
elemDef->effectOnImpact.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString(), builder).fx;
|
||||
}
|
||||
|
||||
if (elemDef->effectOnDeath.handle)
|
||||
{
|
||||
elemDef->effectOnDeath.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString(), builder).fx;
|
||||
}
|
||||
|
||||
if (elemDef->effectEmitted.handle)
|
||||
{
|
||||
elemDef->effectEmitted.handle = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, buffer.readString(), builder).fx;
|
||||
}
|
||||
|
||||
// Save_FxElemExtendedDefPtr
|
||||
{
|
||||
|
||||
if (elemDef->elemType == Game::FX_ELEM_TYPE_TRAIL)
|
||||
{
|
||||
// Save_FxTrailDef
|
||||
{
|
||||
if (elemDef->extended.trailDef)
|
||||
{
|
||||
Game::FxTrailDef* trailDef = buffer.readObject<Game::FxTrailDef>();
|
||||
elemDef->extended.trailDef = trailDef;
|
||||
|
||||
if (trailDef->verts)
|
||||
{
|
||||
trailDef->verts = buffer.readArray<Game::FxTrailVertex>(trailDef->vertCount);
|
||||
}
|
||||
|
||||
if (trailDef->inds)
|
||||
{
|
||||
trailDef->inds = buffer.readArray<unsigned short>(trailDef->indCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (elemDef->extended.trailDef)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Fx element of type {} has traildef, that's impossible?\n", elemDef->elemType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
header->fx = builder->getIW4OfApi()->read<Game::FxEffectDef>(Game::XAssetType::ASSET_TYPE_FX, name);
|
||||
}
|
||||
|
||||
void IFxEffectDef::loadEfx(Game::XAssetHeader* /*header*/, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
@ -270,7 +138,7 @@ namespace Assets
|
||||
// TODO: Convert editor fx to real fx
|
||||
}
|
||||
#else
|
||||
(name);
|
||||
(void)name;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ namespace Assets
|
||||
|
||||
void loadEfx(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);
|
||||
void loadFromIW4OF(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
|
||||
void loadFxElemVisuals(Game::FxElemVisuals* visuals, char elemType, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader);
|
||||
};
|
||||
|
@ -187,6 +187,19 @@ namespace Assets
|
||||
}
|
||||
|
||||
void IFxWorld::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (!header->fxWorld) loadFromDisk(header, name, builder);
|
||||
if (!header->fxWorld) generate(header, name, builder);
|
||||
|
||||
assert(header->fxWorld);
|
||||
}
|
||||
|
||||
void IFxWorld::loadFromDisk(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
header->fxWorld = builder->getIW4OfApi()->read<Game::FxWorld>(Game::XAssetType::ASSET_TYPE_FXWORLD, _name);
|
||||
}
|
||||
|
||||
void IFxWorld::generate(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;
|
||||
@ -194,7 +207,7 @@ namespace Assets
|
||||
// Generate
|
||||
map = builder->getAllocator()->allocate<Game::FxWorld>();
|
||||
map->name = builder->getAllocator()->duplicateString(name);
|
||||
|
||||
|
||||
// No glass for you!
|
||||
ZeroMemory(&map->glassSys, sizeof(map->glassSys));
|
||||
|
||||
|
@ -10,5 +10,7 @@ namespace Assets
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
void generate(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -80,115 +80,6 @@ namespace Assets
|
||||
|
||||
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;
|
||||
}
|
||||
header->gameWorldMp = builder->getIW4OfApi()->read<Game::GameWorldMp>(Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP, _name);
|
||||
}
|
||||
}
|
||||
|
@ -1,150 +1,11 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IGfxImage.hpp"
|
||||
|
||||
#define IW4X_IMG_VERSION "0"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IGfxImage::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::GfxImage* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_IMAGE, name.data()).image;
|
||||
if (image && name[0] != '*') return;
|
||||
|
||||
image = builder->getAllocator()->allocate<Game::GfxImage>();
|
||||
if (!image)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to allocate GfxImage structure!");
|
||||
return;
|
||||
}
|
||||
|
||||
image->name = builder->getAllocator()->duplicateString(name);
|
||||
image->semantic = Game::TextureSemantic::TS_COLOR_MAP;
|
||||
|
||||
const char* tempName = image->name;
|
||||
if (tempName[0] == '*') tempName++;
|
||||
|
||||
Components::FileSystem::File imageFile(std::format("images/{}.iw4xImage", tempName));
|
||||
if (imageFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer());
|
||||
|
||||
__int64 magic = reader.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xImg" IW4X_IMG_VERSION, 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading image '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
image->mapType = reader.read<char>();
|
||||
image->semantic = reader.read<Game::TextureSemantic>();
|
||||
image->category = reader.read<char>();
|
||||
|
||||
int dataLength = reader.read<int>();
|
||||
image->cardMemory.platform[0] = dataLength;
|
||||
image->cardMemory.platform[1] = dataLength;
|
||||
|
||||
Game::GfxImageLoadDefIW3 loadDef;
|
||||
image->texture.loadDef = reinterpret_cast<Game::GfxImageLoadDef*>(reader.readArray<char>(dataLength + 16));
|
||||
std::memcpy(&loadDef, image->texture.loadDef, sizeof(loadDef));
|
||||
|
||||
image->texture.loadDef->levelCount = loadDef.levelCount;
|
||||
image->texture.loadDef->flags = loadDef.flags;
|
||||
image->texture.loadDef->format = loadDef.format;
|
||||
image->texture.loadDef->resourceSize = loadDef.resourceSize;
|
||||
ZeroMemory(image->texture.loadDef->pad, 3);
|
||||
|
||||
if (image->texture.loadDef->resourceSize != dataLength)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Resource size doesn't match the data length ({})!\n", name);
|
||||
}
|
||||
|
||||
image->width = loadDef.dimensions[0];
|
||||
image->height = loadDef.dimensions[1];
|
||||
image->depth = loadDef.dimensions[2];
|
||||
|
||||
image->delayLoadPixels = true;
|
||||
|
||||
header->image = image;
|
||||
}
|
||||
else if (name[0] != '*')
|
||||
{
|
||||
char nameBuffer[MAX_PATH]{};
|
||||
Components::Materials::FormatImagePath(nameBuffer, sizeof(nameBuffer), 0, 0, name.data());
|
||||
Components::FileSystem::File iwi(nameBuffer);
|
||||
|
||||
if (!iwi.exists())
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Loading image '{}' failed!", iwi.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
auto iwiBuffer = iwi.getBuffer();
|
||||
|
||||
const Game::GfxImageFileHeader* iwiHeader = reinterpret_cast<const Game::GfxImageFileHeader*>(iwiBuffer.data());
|
||||
|
||||
if (std::memcmp(iwiHeader->tag, "IWi", 3) && iwiHeader->version == 8)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Image is not a valid IWi!");
|
||||
return;
|
||||
}
|
||||
|
||||
image->mapType = Game::MAPTYPE_2D;
|
||||
image->cardMemory.platform[0] = iwiHeader->fileSizeForPicmip[0] - 32;
|
||||
image->cardMemory.platform[1] = iwiHeader->fileSizeForPicmip[0] - 32;
|
||||
|
||||
image->texture.loadDef = builder->getAllocator()->allocate<Game::GfxImageLoadDef>();
|
||||
if (!image->texture.loadDef)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to allocate GfxImageLoadDef structure!");
|
||||
return;
|
||||
}
|
||||
|
||||
image->texture.loadDef->flags = iwiHeader->flags;
|
||||
image->texture.loadDef->levelCount = 0;
|
||||
|
||||
image->width = iwiHeader->dimensions[0];
|
||||
image->height = iwiHeader->dimensions[1];
|
||||
image->depth = iwiHeader->dimensions[2];
|
||||
|
||||
switch (iwiHeader->format)
|
||||
{
|
||||
case Game::IMG_FORMAT_BITMAP_RGBA:
|
||||
{
|
||||
image->texture.loadDef->format = 21;
|
||||
break;
|
||||
}
|
||||
|
||||
case Game::IMG_FORMAT_BITMAP_RGB:
|
||||
{
|
||||
image->texture.loadDef->format = 20;
|
||||
break;
|
||||
}
|
||||
|
||||
case Game::IMG_FORMAT_DXT1:
|
||||
{
|
||||
image->texture.loadDef->format = 0x31545844;
|
||||
break;
|
||||
}
|
||||
|
||||
case Game::IMG_FORMAT_DXT3:
|
||||
{
|
||||
image->texture.loadDef->format = 0x33545844;
|
||||
break;
|
||||
}
|
||||
|
||||
case Game::IMG_FORMAT_DXT5:
|
||||
{
|
||||
image->texture.loadDef->format = 0x35545844;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
header->image = image;
|
||||
}
|
||||
header->image = builder->getIW4OfApi()->read<Game::GfxImage>(Game::XAssetType::ASSET_TYPE_IMAGE, name);
|
||||
}
|
||||
|
||||
void IGfxImage::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -1,44 +1,11 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IGfxLightDef.hpp"
|
||||
|
||||
#define IW4X_LIGHT_VERSION "0"
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IGfxLightDef::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File mapFile(std::format("lights/{}.iw4xLight", name));
|
||||
|
||||
if (mapFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(7);
|
||||
if (std::memcmp(magic, "IW4xLit", 7))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading light '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_LIGHT_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading light '{}' failed, expected version is {}, but it was {}!", name, IW4X_LIGHT_VERSION, version);
|
||||
}
|
||||
|
||||
Game::GfxLightDef* asset = reader.readObject<Game::GfxLightDef>();
|
||||
header->lightDef = asset;
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->attenuation.image)
|
||||
{
|
||||
asset->attenuation.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image;
|
||||
}
|
||||
}
|
||||
header->lightDef = builder->getIW4OfApi()->read<Game::GfxLightDef>(Game::XAssetType::ASSET_TYPE_LIGHT_DEF, name);
|
||||
}
|
||||
|
||||
void IGfxLightDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -1,450 +1,16 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IGfxWorld.hpp"
|
||||
|
||||
#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)
|
||||
{
|
||||
if (asset->sortedSurfIndex)
|
||||
{
|
||||
asset->sortedSurfIndex = reader->readArray<unsigned short>(asset->staticSurfaceCount + asset->staticSurfaceCountNoDecal);
|
||||
}
|
||||
|
||||
if (asset->smodelInsts)
|
||||
{
|
||||
asset->smodelInsts = reader->readArray<Game::GfxStaticModelInst>(asset->smodelCount);
|
||||
}
|
||||
|
||||
if (asset->surfaces)
|
||||
{
|
||||
asset->surfaces = reader->readArray<Game::GfxSurface>(world->surfaceCount);
|
||||
|
||||
for (unsigned int i = 0; i < world->surfaceCount; ++i)
|
||||
{
|
||||
Game::GfxSurface* surface = &asset->surfaces[i];
|
||||
|
||||
if (surface->material)
|
||||
{
|
||||
world->dpvs.surfaces[i].material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader->readString(), builder).material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->surfacesBounds)
|
||||
{
|
||||
asset->surfacesBounds = reader->readArray<Game::GfxSurfaceBounds>(world->surfaceCount);
|
||||
}
|
||||
|
||||
if (asset->smodelDrawInsts)
|
||||
{
|
||||
asset->smodelDrawInsts = reader->readArray<Game::GfxStaticModelDrawInst>(asset->smodelCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->smodelCount; ++i)
|
||||
{
|
||||
Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i];
|
||||
|
||||
if (model->model)
|
||||
{
|
||||
auto name = reader->readString();
|
||||
|
||||
model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, name, builder).model;
|
||||
|
||||
assert(model->model);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IGfxWorld::loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader)
|
||||
{
|
||||
if (asset->reflectionProbes)
|
||||
{
|
||||
asset->reflectionProbes = reader->readArray<Game::GfxImage*>(asset->reflectionProbeCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i)
|
||||
{
|
||||
asset->reflectionProbes[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->reflectionProbeOrigins)
|
||||
{
|
||||
asset->reflectionProbeOrigins = reader->readArray<Game::GfxReflectionProbe>(asset->reflectionProbeCount);
|
||||
}
|
||||
|
||||
if (asset->lightmaps)
|
||||
{
|
||||
asset->lightmaps = reader->readArray<Game::GfxLightmapArray>(asset->lightmapCount);
|
||||
|
||||
for (int i = 0; i < asset->lightmapCount; ++i)
|
||||
{
|
||||
Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i];
|
||||
|
||||
if (lightmapArray->primary)
|
||||
{
|
||||
lightmapArray->primary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
|
||||
}
|
||||
|
||||
if (lightmapArray->secondary)
|
||||
{
|
||||
lightmapArray->secondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->lightmapOverridePrimary)
|
||||
{
|
||||
asset->lightmapOverridePrimary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
|
||||
}
|
||||
|
||||
if (asset->lightmapOverrideSecondary)
|
||||
{
|
||||
asset->lightmapOverrideSecondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString(), builder).image;
|
||||
}
|
||||
|
||||
// saveGfxWorldVertexData
|
||||
{
|
||||
if (asset->vd.vertices)
|
||||
{
|
||||
asset->vd.vertices = reader->readArray<Game::GfxWorldVertex>(asset->vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
// saveGfxWorldVertexLayerData
|
||||
{
|
||||
if (asset->vld.data)
|
||||
{
|
||||
// no align for char
|
||||
asset->vld.data = reader->readArray<char>(asset->vertexLayerDataSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->indices)
|
||||
{
|
||||
asset->indices = reader->readArray<unsigned short>(asset->indexCount);
|
||||
}
|
||||
}
|
||||
|
||||
void IGfxWorld::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 mapFile(std::format("gfxworld/{}.iw4xGfxWorld", name));
|
||||
|
||||
if (mapFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer());
|
||||
|
||||
__int64 magic = reader.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xGfxW", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
int version = reader.read<int>();
|
||||
if (version != IW4X_GFXMAP_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading gfxworld '{}' failed, expected version is {}, but it was {}!", name, IW4X_GFXMAP_VERSION, version);
|
||||
}
|
||||
|
||||
Game::GfxWorld* asset = reader.readObject<Game::GfxWorld>();
|
||||
header->gfxWorld = asset;
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->baseName)
|
||||
{
|
||||
asset->baseName = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->skies)
|
||||
{
|
||||
asset->skies = reader.readArray<Game::GfxSky>(asset->skyCount);
|
||||
|
||||
for (int i = 0; i < asset->skyCount; ++i)
|
||||
{
|
||||
Game::GfxSky* sky = &asset->skies[i];
|
||||
|
||||
if (sky->skyStartSurfs)
|
||||
{
|
||||
sky->skyStartSurfs = reader.readArray<int>(sky->skySurfCount);
|
||||
}
|
||||
|
||||
if (sky->skyImage)
|
||||
{
|
||||
sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GfxWorldDpvsPlanes
|
||||
{
|
||||
if (asset->dpvsPlanes.planes)
|
||||
{
|
||||
void* oldPtr = asset->dpvsPlanes.planes;
|
||||
asset->dpvsPlanes.planes = reader.readArray<Game::cplane_s>(asset->planeCount);
|
||||
|
||||
if (builder->getAllocator()->isPointerMapped(oldPtr))
|
||||
{
|
||||
asset->dpvsPlanes.planes = builder->getAllocator()->getPointer<Game::cplane_s>(oldPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->getAllocator()->mapPointer(oldPtr, asset->dpvsPlanes.planes);
|
||||
Components::Logger::Print("GfxWorld dpvs planes not mapped. This shouldn't happen. Make sure to load the ClipMap first!\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->dpvsPlanes.nodes)
|
||||
{
|
||||
asset->dpvsPlanes.nodes = reader.readArray<unsigned short>(asset->nodeCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int cellCount = asset->dpvsPlanes.cellCount;
|
||||
|
||||
if (asset->aabbTreeCounts)
|
||||
{
|
||||
asset->aabbTreeCounts = reader.readArray<Game::GfxCellTreeCount>(cellCount);
|
||||
}
|
||||
|
||||
if (asset->aabbTrees)
|
||||
{
|
||||
asset->aabbTrees = reader.readArray<Game::GfxCellTree>(cellCount);
|
||||
|
||||
for (int i = 0; i < cellCount; ++i)
|
||||
{
|
||||
Game::GfxCellTree* cellTree = &asset->aabbTrees[i];
|
||||
|
||||
if (cellTree->aabbTree)
|
||||
{
|
||||
cellTree->aabbTree = reader.readArray<Game::GfxAabbTree>(asset->aabbTreeCounts[i].aabbTreeCount);
|
||||
|
||||
for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j)
|
||||
{
|
||||
Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j];
|
||||
|
||||
if (aabbTree->smodelIndexes)
|
||||
{
|
||||
unsigned short* oldPointer = aabbTree->smodelIndexes;
|
||||
if(builder->getAllocator()->isPointerMapped(oldPointer))
|
||||
{
|
||||
// We still have to read it
|
||||
reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
|
||||
|
||||
aabbTree->smodelIndexes = builder->getAllocator()->getPointer<unsigned short>(oldPointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
aabbTree->smodelIndexes = reader.readArray<unsigned short>(aabbTree->smodelIndexCount);
|
||||
|
||||
for (unsigned short k = 0; k < aabbTree->smodelIndexCount; ++k)
|
||||
{
|
||||
builder->getAllocator()->mapPointer(&oldPointer[k], &aabbTree->smodelIndexes[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->cells)
|
||||
{
|
||||
asset->cells = reader.readArray<Game::GfxCell>(cellCount);
|
||||
|
||||
for (int i = 0; i < cellCount; ++i)
|
||||
{
|
||||
Game::GfxCell* cell = &asset->cells[i];
|
||||
|
||||
if (cell->portals)
|
||||
{
|
||||
cell->portals = reader.readArray<Game::GfxPortal>(cell->portalCount);
|
||||
|
||||
for (int j = 0; j < cell->portalCount; ++j)
|
||||
{
|
||||
Game::GfxPortal* portal = &cell->portals[j];
|
||||
|
||||
if (portal->vertices)
|
||||
{
|
||||
portal->vertices = reader.readArray<Game::vec3_t>(portal->vertexCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cell->reflectionProbes)
|
||||
{
|
||||
cell->reflectionProbes = reader.readArray<char>(cell->reflectionProbeCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->loadGfxWorldDraw(&asset->draw, builder, &reader);
|
||||
|
||||
// GfxLightGrid
|
||||
{
|
||||
if (asset->lightGrid.rowDataStart)
|
||||
{
|
||||
asset->lightGrid.rowDataStart = reader.readArray<unsigned short>((asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset->lightGrid.rowAxis]) + 1);
|
||||
}
|
||||
|
||||
if (asset->lightGrid.rawRowData)
|
||||
{
|
||||
asset->lightGrid.rawRowData = reader.readArray<char>(asset->lightGrid.rawRowDataSize);
|
||||
}
|
||||
|
||||
if (asset->lightGrid.entries)
|
||||
{
|
||||
asset->lightGrid.entries = reader.readArray<Game::GfxLightGridEntry>(asset->lightGrid.entryCount);
|
||||
}
|
||||
|
||||
if (asset->lightGrid.colors)
|
||||
{
|
||||
asset->lightGrid.colors = reader.readArray<Game::GfxLightGridColors>(asset->lightGrid.colorCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->models)
|
||||
{
|
||||
asset->models = reader.readArray<Game::GfxBrushModel>(asset->modelCount);
|
||||
}
|
||||
|
||||
if (asset->materialMemory)
|
||||
{
|
||||
asset->materialMemory = reader.readArray<Game::MaterialMemory>(asset->materialMemoryCount);
|
||||
|
||||
for (int i = 0; i < asset->materialMemoryCount; ++i)
|
||||
{
|
||||
Game::MaterialMemory* materialMemory = &asset->materialMemory[i];
|
||||
|
||||
if (materialMemory->material)
|
||||
{
|
||||
materialMemory->material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->sun.spriteMaterial)
|
||||
{
|
||||
asset->sun.spriteMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material;
|
||||
}
|
||||
|
||||
if (asset->sun.flareMaterial)
|
||||
{
|
||||
asset->sun.flareMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material;
|
||||
}
|
||||
|
||||
if (asset->outdoorImage)
|
||||
{
|
||||
asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image;
|
||||
}
|
||||
|
||||
if (asset->primaryLightCount > 0)
|
||||
{
|
||||
Utils::Stream::ClearPointer(&asset->primaryLightEntityShadowVis);
|
||||
}
|
||||
|
||||
if (asset->dpvsDyn.dynEntClientCount[0] > 0)
|
||||
{
|
||||
Utils::Stream::ClearPointer(&asset->sceneDynModel);
|
||||
Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[0]);
|
||||
Utils::Stream::ClearPointer(&asset->nonSunPrimaryLightForModelDynEnt);
|
||||
}
|
||||
|
||||
if (asset->dpvsDyn.dynEntClientCount[1] > 0)
|
||||
{
|
||||
Utils::Stream::ClearPointer(&asset->sceneDynBrush);
|
||||
Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[1]);
|
||||
}
|
||||
|
||||
if (asset->shadowGeom)
|
||||
{
|
||||
asset->shadowGeom = reader.readArray<Game::GfxShadowGeometry>(asset->primaryLightCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
|
||||
{
|
||||
Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i];
|
||||
|
||||
if (shadowGeometry->sortedSurfIndex)
|
||||
{
|
||||
shadowGeometry->sortedSurfIndex = reader.readArray<unsigned short>(shadowGeometry->surfaceCount);
|
||||
}
|
||||
|
||||
if (shadowGeometry->smodelIndex)
|
||||
{
|
||||
shadowGeometry->smodelIndex = reader.readArray<unsigned short>(shadowGeometry->smodelCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->lightRegion)
|
||||
{
|
||||
asset->lightRegion = reader.readArray<Game::GfxLightRegion>(asset->primaryLightCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
|
||||
{
|
||||
Game::GfxLightRegion* lightRegion = &asset->lightRegion[i];
|
||||
|
||||
if (lightRegion->hulls)
|
||||
{
|
||||
lightRegion->hulls = reader.readArray<Game::GfxLightRegionHull>(lightRegion->hullCount);
|
||||
|
||||
for (unsigned int j = 0; j < lightRegion->hullCount; ++j)
|
||||
{
|
||||
Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j];
|
||||
|
||||
if (lightRegionHull->axis)
|
||||
{
|
||||
lightRegionHull->axis = reader.readArray<Game::GfxLightRegionAxis>(lightRegionHull->axisCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->loadGfxWorldDpvsStatic(asset, &asset->dpvs, builder, &reader);
|
||||
|
||||
// Obsolete, IW3 has no support for that
|
||||
if (asset->heroOnlyLights)
|
||||
{
|
||||
asset->heroOnlyLights = reader.readArray<Game::GfxHeroOnlyLight>(asset->heroOnlyLightCount);
|
||||
}
|
||||
}
|
||||
header->gfxWorld = builder->getIW4OfApi()->read<Game::GfxWorld>(Game::XAssetType::ASSET_TYPE_GFXWORLD, _name);
|
||||
}
|
||||
|
||||
void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Game::GfxWorld* asset = header.gfxWorld;
|
||||
|
||||
auto* asset = header.gfxWorld;
|
||||
if (asset->draw.reflectionProbes)
|
||||
{
|
||||
for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i)
|
||||
@ -455,7 +21,7 @@ namespace Assets
|
||||
|
||||
if (asset->draw.lightmaps)
|
||||
{
|
||||
for (int i = 0; i < asset->draw.lightmapCount; ++i)
|
||||
for (auto i = 0; i < asset->draw.lightmapCount; ++i)
|
||||
{
|
||||
if (asset->draw.lightmaps[i].primary)
|
||||
{
|
||||
@ -599,7 +165,7 @@ namespace Assets
|
||||
{
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
Game::GfxImage** imageDest = buffer->dest<Game::GfxImage*>();
|
||||
auto** imageDest = buffer->dest<Game::GfxImage*>();
|
||||
buffer->saveArray(asset->reflectionProbes, asset->reflectionProbeCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i)
|
||||
@ -648,13 +214,13 @@ namespace Assets
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
Game::GfxLightmapArray* lightmapArrayDestTable = buffer->dest<Game::GfxLightmapArray>();
|
||||
auto* lightmapArrayDestTable = buffer->dest<Game::GfxLightmapArray>();
|
||||
buffer->saveArray(asset->lightmaps, asset->lightmapCount);
|
||||
|
||||
for (int i = 0; i < asset->lightmapCount; ++i)
|
||||
{
|
||||
Game::GfxLightmapArray* lightmapArrayDest = &lightmapArrayDestTable[i];
|
||||
Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i];
|
||||
auto* lightmapArrayDest = &lightmapArrayDestTable[i];
|
||||
auto* lightmapArray = &asset->lightmaps[i];
|
||||
|
||||
if (lightmapArray->primary)
|
||||
{
|
||||
@ -854,13 +420,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxSurface");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxSurface* destSurfaceTable = buffer->dest<Game::GfxSurface>();
|
||||
auto* destSurfaceTable = buffer->dest<Game::GfxSurface>();
|
||||
buffer->saveArray(asset->surfaces, world->surfaceCount);
|
||||
|
||||
for (unsigned int i = 0; i < world->surfaceCount; ++i)
|
||||
{
|
||||
Game::GfxSurface* surface = &asset->surfaces[i];
|
||||
Game::GfxSurface* destSurface = &destSurfaceTable[i];
|
||||
auto* surface = &asset->surfaces[i];
|
||||
auto* destSurface = &destSurfaceTable[i];
|
||||
|
||||
if (surface->material)
|
||||
{
|
||||
@ -890,13 +456,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxStaticModelDrawInst");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxStaticModelDrawInst* destModelTable = buffer->dest<Game::GfxStaticModelDrawInst>();
|
||||
auto* destModelTable = buffer->dest<Game::GfxStaticModelDrawInst>();
|
||||
buffer->saveArray(asset->smodelDrawInsts, asset->smodelCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->smodelCount; ++i)
|
||||
{
|
||||
Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i];
|
||||
Game::GfxStaticModelDrawInst* destModel = &destModelTable[i];
|
||||
auto* model = &asset->smodelDrawInsts[i];
|
||||
auto* destModel = &destModelTable[i];
|
||||
|
||||
if (model->model)
|
||||
{
|
||||
@ -986,8 +552,8 @@ namespace Assets
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
SaveLogEnter("GfxWorld");
|
||||
|
||||
Game::GfxWorld* asset = header.gfxWorld;
|
||||
Game::GfxWorld* dest = buffer->dest<Game::GfxWorld>();
|
||||
auto* asset = header.gfxWorld;
|
||||
auto* dest = buffer->dest<Game::GfxWorld>();
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
@ -1012,13 +578,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxSky");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxSky* destSkyTable = buffer->dest<Game::GfxSky>();
|
||||
auto* destSkyTable = buffer->dest<Game::GfxSky>();
|
||||
buffer->saveArray(asset->skies, asset->skyCount);
|
||||
|
||||
for (int i = 0; i < asset->skyCount; ++i)
|
||||
{
|
||||
Game::GfxSky* destSky = &destSkyTable[i];
|
||||
Game::GfxSky* sky = &asset->skies[i];
|
||||
auto* destSky = &destSkyTable[i];
|
||||
auto* sky = &asset->skies[i];
|
||||
|
||||
if (sky->skyStartSurfs)
|
||||
{
|
||||
@ -1059,13 +625,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxCellTree");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_128);
|
||||
Game::GfxCellTree* destCellTreeTable = buffer->dest<Game::GfxCellTree>();
|
||||
auto* destCellTreeTable = buffer->dest<Game::GfxCellTree>();
|
||||
buffer->saveArray(asset->aabbTrees, cellCount);
|
||||
|
||||
for (int i = 0; i < cellCount; ++i)
|
||||
{
|
||||
Game::GfxCellTree* destCellTree = &destCellTreeTable[i];
|
||||
Game::GfxCellTree* cellTree = &asset->aabbTrees[i];
|
||||
auto* destCellTree = &destCellTreeTable[i];
|
||||
auto* cellTree = &asset->aabbTrees[i];
|
||||
|
||||
if (cellTree->aabbTree)
|
||||
{
|
||||
@ -1073,7 +639,7 @@ namespace Assets
|
||||
SaveLogEnter("GfxAabbTree");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxAabbTree* destAabbTreeTable = buffer->dest<Game::GfxAabbTree>();
|
||||
auto* destAabbTreeTable = buffer->dest<Game::GfxAabbTree>();
|
||||
buffer->saveArray(cellTree->aabbTree, asset->aabbTreeCounts[i].aabbTreeCount);
|
||||
|
||||
// ok this one is based on some assumptions because the actual count is this
|
||||
@ -1083,8 +649,8 @@ namespace Assets
|
||||
|
||||
for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j)
|
||||
{
|
||||
Game::GfxAabbTree* destAabbTree = &destAabbTreeTable[j];
|
||||
Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j];
|
||||
auto* destAabbTree = &destAabbTreeTable[j];
|
||||
auto* aabbTree = &cellTree->aabbTree[j];
|
||||
|
||||
if (aabbTree->smodelIndexes)
|
||||
{
|
||||
@ -1122,13 +688,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxCell");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxCell* destCellTable = buffer->dest<Game::GfxCell>();
|
||||
auto* destCellTable = buffer->dest<Game::GfxCell>();
|
||||
buffer->saveArray(asset->cells, cellCount);
|
||||
|
||||
for (int i = 0; i < cellCount; ++i)
|
||||
{
|
||||
Game::GfxCell* destCell = &destCellTable[i];
|
||||
Game::GfxCell* cell = &asset->cells[i];
|
||||
auto* destCell = &destCellTable[i];
|
||||
auto* cell = &asset->cells[i];
|
||||
|
||||
if (cell->portals)
|
||||
{
|
||||
@ -1136,13 +702,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxPortal");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxPortal* destPortalTable = buffer->dest<Game::GfxPortal>();
|
||||
auto* destPortalTable = buffer->dest<Game::GfxPortal>();
|
||||
buffer->saveArray(cell->portals, cell->portalCount);
|
||||
|
||||
for (int j = 0; j < cell->portalCount; ++j)
|
||||
{
|
||||
Game::GfxPortal* destPortal = &destPortalTable[j];
|
||||
Game::GfxPortal* portal = &cell->portals[j];
|
||||
auto* destPortal = &destPortalTable[j];
|
||||
auto* portal = &cell->portals[j];
|
||||
|
||||
if (portal->vertices)
|
||||
{
|
||||
@ -1189,13 +755,13 @@ namespace Assets
|
||||
SaveLogEnter("MaterialMemory");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::MaterialMemory* destMaterialMemoryTable = buffer->dest<Game::MaterialMemory>();
|
||||
auto* destMaterialMemoryTable = buffer->dest<Game::MaterialMemory>();
|
||||
buffer->saveArray(asset->materialMemory, asset->materialMemoryCount);
|
||||
|
||||
for (int i = 0; i < asset->materialMemoryCount; ++i)
|
||||
{
|
||||
Game::MaterialMemory* destMaterialMemory = &destMaterialMemoryTable[i];
|
||||
Game::MaterialMemory* materialMemory = &asset->materialMemory[i];
|
||||
auto* destMaterialMemory = &destMaterialMemoryTable[i];
|
||||
auto* materialMemory = &asset->materialMemory[i];
|
||||
|
||||
if (materialMemory->material)
|
||||
{
|
||||
@ -1284,13 +850,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxShadowGeometry");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxShadowGeometry* destShadowGeometryTable = buffer->dest<Game::GfxShadowGeometry>();
|
||||
auto* destShadowGeometryTable = buffer->dest<Game::GfxShadowGeometry>();
|
||||
buffer->saveArray(asset->shadowGeom, asset->primaryLightCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
|
||||
{
|
||||
Game::GfxShadowGeometry* destShadowGeometry = &destShadowGeometryTable[i];
|
||||
Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i];
|
||||
auto* destShadowGeometry = &destShadowGeometryTable[i];
|
||||
auto* shadowGeometry = &asset->shadowGeom[i];
|
||||
|
||||
if (shadowGeometry->sortedSurfIndex)
|
||||
{
|
||||
@ -1317,13 +883,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxLightRegion");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxLightRegion* destLightRegionTable = buffer->dest<Game::GfxLightRegion>();
|
||||
auto* destLightRegionTable = buffer->dest<Game::GfxLightRegion>();
|
||||
buffer->saveArray(asset->lightRegion, asset->primaryLightCount);
|
||||
|
||||
for (unsigned int i = 0; i < asset->primaryLightCount; ++i)
|
||||
{
|
||||
Game::GfxLightRegion* destLightRegion = &destLightRegionTable[i];
|
||||
Game::GfxLightRegion* lightRegion = &asset->lightRegion[i];
|
||||
auto* destLightRegion = &destLightRegionTable[i];
|
||||
auto* lightRegion = &asset->lightRegion[i];
|
||||
|
||||
if (lightRegion->hulls)
|
||||
{
|
||||
@ -1331,13 +897,13 @@ namespace Assets
|
||||
SaveLogEnter("GfxLightRegionHull");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
Game::GfxLightRegionHull* destLightRegionHullTable = buffer->dest<Game::GfxLightRegionHull>();
|
||||
auto* destLightRegionHullTable = buffer->dest<Game::GfxLightRegionHull>();
|
||||
buffer->saveArray(lightRegion->hulls, lightRegion->hullCount);
|
||||
|
||||
for (unsigned int j = 0; j < lightRegion->hullCount; ++j)
|
||||
{
|
||||
Game::GfxLightRegionHull* destLightRegionHull = &destLightRegionHullTable[j];
|
||||
Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j];
|
||||
auto* destLightRegionHull = &destLightRegionHullTable[j];
|
||||
auto* lightRegionHull = &lightRegion->hulls[j];
|
||||
|
||||
if (lightRegionHull->axis)
|
||||
{
|
||||
@ -1373,7 +939,6 @@ namespace Assets
|
||||
Utils::Stream::ClearPointer(&dest->heroOnlyLights);
|
||||
}
|
||||
|
||||
//buffer->setPointerAssertion(false);
|
||||
buffer->popBlock();
|
||||
SaveLogExit();
|
||||
}
|
||||
|
@ -18,8 +18,5 @@ namespace Assets
|
||||
void savesunflare_t(Game::sunflare_t* asset, Game::sunflare_t* dest, Components::ZoneBuilder::Zone* builder);
|
||||
void saveGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Game::GfxWorldDpvsStatic* dest, int planeCount, Components::ZoneBuilder::Zone* builder);
|
||||
void saveGfxWorldDpvsDynamic(Game::GfxWorldDpvsDynamic* asset, Game::GfxWorldDpvsDynamic* dest, int cellCount, Components::ZoneBuilder::Zone* builder);
|
||||
|
||||
void loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader);
|
||||
void loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader);
|
||||
};
|
||||
}
|
||||
|
@ -5,106 +5,16 @@ namespace Assets
|
||||
{
|
||||
void ILoadedSound::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File soundFile(std::format("loaded_sound/{}", name));
|
||||
if (!soundFile.exists())
|
||||
{
|
||||
header->loadSnd = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).loadSnd;
|
||||
return;
|
||||
}
|
||||
|
||||
Game::LoadedSound* sound = builder->getAllocator()->allocate<Game::LoadedSound>();
|
||||
if (!sound)
|
||||
{
|
||||
Components::Logger::Print("Error allocating memory for sound structure!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::LoadedSound* reference = nullptr;
|
||||
if (!reference) reference = Game::DB_FindXAssetHeader(Game::ASSET_TYPE_LOADED_SOUND, "weapons/c4_detpack/c4_drop_dirt1.wav").loadSnd;
|
||||
|
||||
std::memcpy(sound, reference, sizeof(Game::LoadedSound));
|
||||
sound->sound.data = nullptr;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), soundFile.getBuffer());
|
||||
|
||||
unsigned int chunkIDBuffer = reader.read<unsigned int>();
|
||||
if (chunkIDBuffer != 0x46464952) // RIFF
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int chunkSize = reader.read<unsigned int>();
|
||||
|
||||
unsigned int format = reader.read<unsigned int>();
|
||||
if (format != 0x45564157) // WAVE
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, header is invalid!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
while (!sound->sound.data && !reader.end())
|
||||
{
|
||||
chunkIDBuffer = reader.read<unsigned int>();
|
||||
chunkSize = reader.read<unsigned int>();
|
||||
|
||||
switch (chunkIDBuffer)
|
||||
{
|
||||
case 0x20746D66: // fmt
|
||||
if (chunkSize >= 16)
|
||||
{
|
||||
sound->sound.info.format = reader.read<short>();
|
||||
if (sound->sound.info.format != 1 && sound->sound.info.format != 17)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, invalid format!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
sound->sound.info.channels = reader.read<short>();
|
||||
sound->sound.info.rate = reader.read<int>();
|
||||
sound->sound.info.samples = reader.read<int>();
|
||||
sound->sound.info.block_size = reader.read<short>();
|
||||
sound->sound.info.bits = reader.read<short>();
|
||||
|
||||
// skip any extra parameters
|
||||
if (chunkSize > 16)
|
||||
{
|
||||
reader.seekRelative(chunkSize - 16);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x61746164: // data
|
||||
sound->sound.info.data_len = chunkSize;
|
||||
sound->sound.data = reader.readArray<char>(chunkSize);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (chunkSize > 0)
|
||||
{
|
||||
reader.seekRelative(chunkSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sound->sound.info.data_ptr)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading sound '{}' failed, invalid format!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
sound->name = builder->getAllocator()->duplicateString(name);
|
||||
header->loadSnd = sound;
|
||||
header->loadSnd = builder->getIW4OfApi()->read<Game::LoadedSound>(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, name);
|
||||
}
|
||||
|
||||
void ILoadedSound::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
AssertSize(Game::LoadedSound, 44);
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::LoadedSound* asset = header.loadSnd;
|
||||
Game::LoadedSound* dest = buffer->dest<Game::LoadedSound>();
|
||||
auto* buffer = builder->getBuffer();
|
||||
auto* asset = header.loadSnd;
|
||||
auto* dest = buffer->dest<Game::LoadedSound>();
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
|
@ -5,55 +5,7 @@ namespace Assets
|
||||
{
|
||||
void IMapEnts::load(Game::XAssetHeader* header, const std::string& _name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
std::string name = _name;
|
||||
Utils::String::Replace(name, "maps/", "");
|
||||
Utils::String::Replace(name, "mp/", "");
|
||||
Utils::String::Replace(name, ".d3dbsp", "");
|
||||
|
||||
Components::FileSystem::File ents(std::format("mapents/{}.ents", name));
|
||||
if (ents.exists())
|
||||
{
|
||||
Game::MapEnts* entites = builder->getAllocator()->allocate<Game::MapEnts>();
|
||||
Game::MapEnts* orgEnts = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).mapEnts;
|
||||
|
||||
// TODO: Get rid of that
|
||||
if (!orgEnts)
|
||||
{
|
||||
orgEnts = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, "maps/iw4_credits.d3dbsp").mapEnts;
|
||||
|
||||
if (!orgEnts)
|
||||
{
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_MAP_ENTS, [](Game::XAssetHeader header, void* mapEnts)
|
||||
{
|
||||
if (!*reinterpret_cast<void**>(mapEnts))
|
||||
{
|
||||
*reinterpret_cast<Game::MapEnts**>(mapEnts) = header.mapEnts;
|
||||
}
|
||||
}, &orgEnts, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (orgEnts)
|
||||
{
|
||||
std::memcpy(entites, orgEnts, sizeof Game::MapEnts);
|
||||
}
|
||||
else
|
||||
{
|
||||
entites->stageCount = 1;
|
||||
entites->stages = builder->getAllocator()->allocate<Game::Stage>();
|
||||
entites->stages[0].name = "stage 0";
|
||||
entites->stages[0].triggerIndex = 0x400;
|
||||
entites->stages[0].sunPrimaryLightIndex = 0x1;
|
||||
}
|
||||
|
||||
std::string entityString = ents.getBuffer();
|
||||
|
||||
entites->name = builder->getAllocator()->duplicateString(std::format("maps/mp/{}.d3dbsp", name));
|
||||
entites->entityString = builder->getAllocator()->duplicateString(entityString);
|
||||
entites->numEntityChars = entityString.size() + 1;
|
||||
|
||||
header->mapEnts = entites;
|
||||
}
|
||||
header->mapEnts = builder->getIW4OfApi()->read<Game::MapEnts>(Game::XAssetType::ASSET_TYPE_MAP_ENTS, _name);
|
||||
}
|
||||
|
||||
void IMapEnts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -1,730 +1,18 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterial.hpp"
|
||||
|
||||
#include <Utils/Json.hpp>
|
||||
|
||||
#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 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->loadFromDisk(header, name, builder); // Check if we want to load a material from disk
|
||||
if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one
|
||||
assert(header->data);
|
||||
}
|
||||
|
||||
|
||||
void IMaterial::loadJson(Game::XAssetHeader* header, const std::string& name, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||
void IMaterial::loadFromDisk(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, 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, 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)
|
||||
{
|
||||
static const char* techsetSuffix[] =
|
||||
{
|
||||
"_lin",
|
||||
"_add_lin",
|
||||
"_replace",
|
||||
"_eyeoffset",
|
||||
|
||||
"_blend",
|
||||
"_blend_nofog",
|
||||
"_add",
|
||||
"_nofog",
|
||||
"_nocast",
|
||||
|
||||
"_add_lin_nofog",
|
||||
};
|
||||
|
||||
Components::FileSystem::File materialFile(std::format("materials/{}.iw4xMaterial", name));
|
||||
if (!materialFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(7);
|
||||
if (std::memcmp(magic, "IW4xMat", 7) != 0)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading material '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
std::string version;
|
||||
version.push_back(reader.read<char>());
|
||||
if (version != IW4X_MAT_BIN_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>();
|
||||
|
||||
if (asset->info.name)
|
||||
{
|
||||
asset->info.name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->techniqueSet)
|
||||
{
|
||||
std::string techsetName = reader.readString();
|
||||
if (!techsetName.empty() && techsetName.front() == ',') techsetName.erase(techsetName.begin());
|
||||
asset->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techsetName, builder).techniqueSet;
|
||||
|
||||
if (!asset->techniqueSet)
|
||||
{
|
||||
// Workaround for effect techsets having _nofog suffix
|
||||
std::string suffix;
|
||||
if (Utils::String::StartsWith(techsetName, "effect_") && Utils::String::EndsWith(techsetName, "_nofog"))
|
||||
{
|
||||
suffix = "_nofog";
|
||||
Utils::String::Replace(techsetName, suffix, "");
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(techsetSuffix); ++i)
|
||||
{
|
||||
Game::MaterialTechniqueSet* techsetPtr = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, (techsetName + techsetSuffix[i] + suffix), builder).techniqueSet;
|
||||
|
||||
if (techsetPtr)
|
||||
{
|
||||
asset->techniqueSet = techsetPtr;
|
||||
|
||||
if (asset->techniqueSet->name[0] == ',') continue; // Try to find a better one
|
||||
Components::Logger::Print("Techset '{}' has been mapped to '{}'\n", techsetName, asset->techniqueSet->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Components::Logger::Print("Techset {} exists with the same name in iw4, and was mapped 1:1 with {}\n", techsetName, asset->techniqueSet->name);
|
||||
}
|
||||
|
||||
if (!asset->techniqueSet)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Missing techset: '{}' not found", techsetName);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->textureTable)
|
||||
{
|
||||
asset->textureTable = reader.readArray<Game::MaterialTextureDef>(asset->textureCount);
|
||||
|
||||
for (char i = 0; i < asset->textureCount; ++i)
|
||||
{
|
||||
Game::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
||||
|
||||
if (textureDef->semantic == Game::TextureSemantic::TS_WATER_MAP)
|
||||
{
|
||||
if (textureDef->u.water)
|
||||
{
|
||||
Game::water_t* water = reader.readObject<Game::water_t>();
|
||||
textureDef->u.water = water;
|
||||
|
||||
// Save_water_t
|
||||
if (water->H0)
|
||||
{
|
||||
water->H0 = reader.readArray<Game::complex_s>(water->M * water->N);
|
||||
}
|
||||
|
||||
if (water->wTerm)
|
||||
{
|
||||
water->wTerm = reader.readArray<float>(water->M * water->N);
|
||||
}
|
||||
|
||||
if (water->image)
|
||||
{
|
||||
water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (textureDef->u.image)
|
||||
{
|
||||
textureDef->u.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString(), builder).image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->constantTable)
|
||||
{
|
||||
asset->constantTable = reader.readArray<Game::MaterialConstantDef>(asset->constantCount);
|
||||
}
|
||||
|
||||
if (asset->stateBitsTable)
|
||||
{
|
||||
asset->stateBitsTable = reader.readArray<Game::GfxStateBits>(asset->stateBitsCount);
|
||||
}
|
||||
|
||||
header->material = asset;
|
||||
|
||||
static thread_local bool replacementFound;
|
||||
replacementFound = false;
|
||||
|
||||
// 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)
|
||||
{
|
||||
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);
|
||||
|
||||
if (!replacementFound)
|
||||
{
|
||||
auto techsetMatches = [](Game::Material* m1, Game::Material* m2)
|
||||
{
|
||||
Game::MaterialTechniqueSet* t1 = m1->techniqueSet;
|
||||
Game::MaterialTechniqueSet* t2 = m2->techniqueSet;
|
||||
if (!t1 || !t2) return false;
|
||||
if (t1->remappedTechniqueSet && t2->remappedTechniqueSet && std::string(t1->remappedTechniqueSet->name) == t2->remappedTechniqueSet->name) return true;
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(t1->techniques); ++i)
|
||||
{
|
||||
if (!t1->techniques[i] && !t2->techniques[i]) continue;;
|
||||
if (!t1->techniques[i] || !t2->techniques[i]) return false;
|
||||
|
||||
// Apparently, this is really not that important
|
||||
//if (t1->techniques[i]->flags != t2->techniques[i]->flags) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
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))
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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 (const auto itr = techSetCorrespondance.find(techName); itr != techSetCorrespondance.end())
|
||||
{
|
||||
auto& iw4TechSetName = itr->second;
|
||||
auto* iw4TechSet = Game::DB_FindXAssetEntry(Game::ASSET_TYPE_TECHNIQUE_SET, iw4TechSetName.data());
|
||||
|
||||
if (iw4TechSet)
|
||||
{
|
||||
Game::DB_EnumXAssetEntries(Game::XAssetType::ASSET_TYPE_MATERIAL, [asset, iw4TechSet](Game::XAssetEntry* entry)
|
||||
{
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
asset->info.sortKey = header.material->info.sortKey;
|
||||
asset->techniqueSet = iw4TechSet->asset.header.techniqueSet;
|
||||
|
||||
// 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);
|
||||
|
||||
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
|
||||
{
|
||||
Components::Logger::Print("Could not find any loaded techset with iw4 name {} for iw3 techset {}\n", iw4TechSetName, asset->techniqueSet->name);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reader.end())
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Material data left!");
|
||||
}
|
||||
|
||||
/*char baseIndex = 0;
|
||||
for (char i = 0; i < asset->stateBitsCount; ++i)
|
||||
{
|
||||
auto stateBits = asset->stateBitsTable[i];
|
||||
if (stateBits.loadBits[0] == 0x18128812 &&
|
||||
stateBits.loadBits[1] == 0xD) // Seems to be like a default stateBit causing a 'generic' initialization
|
||||
{
|
||||
baseIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; ++i)
|
||||
{
|
||||
if (!asset->techniqueSet->techniques[i] && asset->stateBitsEntry[i] != -1)
|
||||
{
|
||||
asset->stateBitsEntry[i] = -1;
|
||||
}
|
||||
|
||||
if (asset->techniqueSet->techniques[i] && asset->stateBitsEntry[i] == -1)
|
||||
{
|
||||
asset->stateBitsEntry[i] = baseIndex;
|
||||
}
|
||||
}*/
|
||||
header->material = builder->getIW4OfApi()->read<Game::Material>(Game::XAssetType::ASSET_TYPE_MATERIAL, name);
|
||||
}
|
||||
|
||||
void IMaterial::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
|
@ -10,11 +10,7 @@ namespace Assets
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
void loadJson(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 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,8 +1,6 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IMaterialPixelShader.hpp"
|
||||
|
||||
#define GFX_RENDERER_SHADER_SM3 0
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
|
||||
@ -19,21 +17,7 @@ namespace Assets
|
||||
|
||||
void IMaterialPixelShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File psFile(std::format("ps/{}.cso", name));
|
||||
if (!psFile.exists()) return;
|
||||
|
||||
auto buff = psFile.getBuffer();
|
||||
auto programSize = buff.size() / 4;
|
||||
Game::MaterialPixelShader* asset = builder->getAllocator()->allocate<Game::MaterialPixelShader>();
|
||||
|
||||
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->pixelShader = asset;
|
||||
header->pixelShader = builder->getIW4OfApi()->read<Game::MaterialPixelShader>(Game::XAssetType::ASSET_TYPE_PIXELSHADER, name);
|
||||
}
|
||||
|
||||
void IMaterialPixelShader::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -11,6 +11,8 @@ namespace Assets
|
||||
{
|
||||
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
|
||||
|
||||
assert(header->data);
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadNative(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* /*builder*/)
|
||||
@ -18,198 +20,9 @@ namespace Assets
|
||||
header->techniqueSet = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).techniqueSet;
|
||||
}
|
||||
|
||||
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/{}.iw4x.json", name));
|
||||
if (!techFile.exists())
|
||||
{
|
||||
*tech = nullptr;
|
||||
|
||||
Components::Logger::Warning(Game::CON_CHANNEL_DONT_FILTER, "Missing technique '{}'\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json technique;
|
||||
|
||||
try
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
unsigned short flags = static_cast<unsigned short>(Utils::Json::ReadFlags(technique["flags"].get<std::string>(), sizeof(short)));
|
||||
|
||||
if (technique["passArray"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t passArray = technique["passArray"];
|
||||
|
||||
Game::MaterialTechnique* asset = (Game::MaterialTechnique*)builder->getAllocator()->allocateArray<unsigned char>(sizeof(Game::MaterialTechnique) + (sizeof(Game::MaterialPass) * (passArray.size() - 1)));
|
||||
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->flags = flags;
|
||||
asset->passCount = static_cast<unsigned short>(passArray.size());
|
||||
|
||||
Game::MaterialPass* passes = builder->getAllocator()->allocateArray<Game::MaterialPass>(asset->passCount);
|
||||
std::memcpy(asset->passArray, passes, sizeof(Game::MaterialPass) * asset->passCount);
|
||||
|
||||
for (unsigned short i = 0; i < asset->passCount; i++)
|
||||
{
|
||||
Game::MaterialPass* pass = &asset->passArray[i];
|
||||
auto jsonPass = passArray[i];
|
||||
|
||||
if (jsonPass["vertexDeclaration"].is_string())
|
||||
{
|
||||
auto declName = jsonPass["vertexDeclaration"].get<std::string>();
|
||||
pass->vertexDecl = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_VERTEXDECL, declName, builder).vertexDecl;
|
||||
}
|
||||
|
||||
if (jsonPass["vertexShader"].is_string())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File tsFile(std::format("techsets/{}.iw4x.json", name));
|
||||
if (!tsFile.exists()) return;
|
||||
|
||||
nlohmann::json techset;
|
||||
|
||||
try
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
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 = builder->getAllocator()->allocate<Game::MaterialTechniqueSet>();
|
||||
|
||||
if (asset == nullptr)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading techset '{}' failed, allocation failed!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
if (techset["name"].is_string())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
header->techniqueSet = builder->getIW4OfApi()->read<Game::MaterialTechniqueSet>(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, name);
|
||||
}
|
||||
|
||||
void IMaterialTechniqueSet::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -18,32 +18,7 @@ namespace Assets
|
||||
|
||||
void IMaterialVertexDeclaration::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File declFile(std::format("decl/{}.iw4xDECL", name));
|
||||
if (!declFile.exists()) return;
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), declFile.getBuffer());
|
||||
|
||||
char* magic = reader.readArray<char>(8);
|
||||
if (std::memcmp(magic, "IW4xDECL", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading vertex declaration '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
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 {:d}!",
|
||||
name, IW4X_TECHSET_VERSION, version);
|
||||
}
|
||||
|
||||
Game::MaterialVertexDeclaration* asset = reader.readObject<Game::MaterialVertexDeclaration>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
header->vertexDecl = asset;
|
||||
header->vertexDecl = builder->getIW4OfApi()->read<Game::MaterialVertexDeclaration>(Game::XAssetType::ASSET_TYPE_VERTEXDECL, name);
|
||||
}
|
||||
|
||||
void IMaterialVertexDeclaration::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -18,20 +18,7 @@ namespace Assets
|
||||
|
||||
void IMaterialVertexShader::loadBinary(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File vsFile(std::format("vs/{}.cso", name));
|
||||
if (!vsFile.exists()) return;
|
||||
|
||||
auto buff = vsFile.getBuffer();
|
||||
auto programSize = buff.size() / 4;
|
||||
Game::MaterialVertexShader* asset = builder->getAllocator()->allocate<Game::MaterialVertexShader>();
|
||||
|
||||
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;
|
||||
header->vertexShader = builder->getIW4OfApi()->read<Game::MaterialVertexShader>(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, name);
|
||||
}
|
||||
|
||||
void IMaterialVertexShader::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -7,9 +7,9 @@ namespace Assets
|
||||
{
|
||||
AssertSize(Game::PhysPreset, 44);
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::PhysPreset* asset = header.physPreset;
|
||||
Game::PhysPreset* dest = buffer->dest<Game::PhysPreset>();
|
||||
auto* buffer = builder->getBuffer();
|
||||
auto* asset = header.physPreset;
|
||||
auto* dest = buffer->dest<Game::PhysPreset>();
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
@ -28,4 +28,14 @@ namespace Assets
|
||||
|
||||
buffer->popBlock();
|
||||
}
|
||||
|
||||
void IPhysPreset::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
loadFromDisk(header, name, builder);
|
||||
}
|
||||
|
||||
void IPhysPreset::loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
header->physPreset = builder->getIW4OfApi()->read<Game::PhysPreset>(Game::XAssetType::ASSET_TYPE_PHYSPRESET, name);
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,7 @@ namespace Assets
|
||||
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_PHYSPRESET; }
|
||||
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
void loadFromDisk(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder);
|
||||
};
|
||||
}
|
||||
|
@ -7,38 +7,7 @@ namespace Assets
|
||||
{
|
||||
void IRawFile::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File rawFile(name);
|
||||
|
||||
if (!rawFile.exists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto* asset = builder->getAllocator()->allocate<Game::RawFile>();
|
||||
if (!asset)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
asset->name = builder->getAllocator()->duplicateString(name);
|
||||
asset->len = static_cast<int>(rawFile.getBuffer().size());
|
||||
|
||||
const auto compressedData = Utils::Compression::ZLib::Compress(rawFile.getBuffer());
|
||||
// Only save the compressed buffer if we gained space
|
||||
if (compressedData.size() < rawFile.getBuffer().size())
|
||||
{
|
||||
asset->buffer = builder->getAllocator()->allocateArray<char>(compressedData.size());
|
||||
std::memcpy(const_cast<char*>(asset->buffer), compressedData.data(), compressedData.size());
|
||||
asset->compressedLen = static_cast<int>(compressedData.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
asset->buffer = builder->getAllocator()->allocateArray<char>(rawFile.getBuffer().size() + 1);
|
||||
std::memcpy(const_cast<char*>(asset->buffer), rawFile.getBuffer().data(), rawFile.getBuffer().size());
|
||||
asset->compressedLen = 0;
|
||||
}
|
||||
|
||||
header->rawfile = asset;
|
||||
header->rawfile = builder->getIW4OfApi()->read<Game::RawFile>(Game::XAssetType::ASSET_TYPE_RAWFILE, name);
|
||||
}
|
||||
|
||||
void IRawFile::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -7,9 +7,9 @@ namespace Assets
|
||||
{
|
||||
AssertSize(Game::SndCurve, 136);
|
||||
|
||||
Utils::Stream* buffer = builder->getBuffer();
|
||||
Game::SndCurve* asset = header.sndCurve;
|
||||
Game::SndCurve* dest = buffer->dest<Game::SndCurve>();
|
||||
auto* buffer = builder->getBuffer();
|
||||
auto* asset = header.sndCurve;
|
||||
auto* dest = buffer->dest<Game::SndCurve>();
|
||||
buffer->save(asset);
|
||||
|
||||
buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL);
|
||||
@ -22,4 +22,9 @@ namespace Assets
|
||||
|
||||
buffer->popBlock();
|
||||
}
|
||||
|
||||
void ISndCurve::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
header->sndCurve = builder->getIW4OfApi()->read<Game::SndCurve>(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, name);
|
||||
}
|
||||
}
|
||||
|
@ -8,5 +8,6 @@ namespace Assets
|
||||
Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_SOUND_CURVE; }
|
||||
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
};
|
||||
}
|
||||
|
@ -1,108 +1,11 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IXAnimParts.hpp"
|
||||
|
||||
#define IW4X_ANIM_VERSION 1
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IXAnimParts::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File animFile(std::format("xanim/{}.iw4xAnim", name));
|
||||
|
||||
if (animFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer());
|
||||
|
||||
__int64 magic = reader.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xAnim", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
int version = reader.read<int>();
|
||||
if (version != IW4X_ANIM_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, expected version is {}, but it was {}!", name, IW4X_ANIM_VERSION, version);
|
||||
}
|
||||
|
||||
Game::XAnimParts* xanim = reader.readArray<Game::XAnimParts>();
|
||||
|
||||
if (xanim)
|
||||
{
|
||||
if (xanim->name)
|
||||
{
|
||||
xanim->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (xanim->names)
|
||||
{
|
||||
xanim->names = builder->getAllocator()->allocateArray<unsigned short>(xanim->boneCount[Game::PART_TYPE_ALL]);
|
||||
for (int i = 0; i < xanim->boneCount[Game::PART_TYPE_ALL]; ++i)
|
||||
{
|
||||
xanim->names[i] = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (xanim->notify)
|
||||
{
|
||||
xanim->notify = reader.readArray<Game::XAnimNotifyInfo>(xanim->notifyCount);
|
||||
|
||||
for (int i = 0; i < xanim->notifyCount; ++i)
|
||||
{
|
||||
xanim->notify[i].name = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (xanim->dataByte)
|
||||
{
|
||||
xanim->dataByte = reader.readArray<char>(xanim->dataByteCount);
|
||||
}
|
||||
|
||||
if (xanim->dataShort)
|
||||
{
|
||||
xanim->dataShort = reader.readArray<short>(xanim->dataShortCount);
|
||||
}
|
||||
|
||||
if (xanim->dataInt)
|
||||
{
|
||||
xanim->dataInt = reader.readArray<int>(xanim->dataIntCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataByte)
|
||||
{
|
||||
xanim->randomDataByte = reader.readArray<char>(xanim->randomDataByteCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataShort)
|
||||
{
|
||||
xanim->randomDataShort = reader.readArray<short>(xanim->randomDataShortCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataInt)
|
||||
{
|
||||
xanim->randomDataInt = reader.readArray<int>(xanim->randomDataIntCount);
|
||||
}
|
||||
|
||||
if (xanim->indices.data)
|
||||
{
|
||||
if (xanim->numframes < 256)
|
||||
{
|
||||
xanim->indices._1 = reader.readArray<char>(xanim->indexCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
xanim->indices._2 = reader.readArray<unsigned short>(xanim->indexCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reader.end())
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading animation '{}' failed, remaining raw data found!", name);
|
||||
}
|
||||
|
||||
header->parts = xanim;
|
||||
}
|
||||
}
|
||||
header->parts = builder->getIW4OfApi()->read<Game::XAnimParts>(Game::XAssetType::ASSET_TYPE_XANIMPARTS, name);
|
||||
}
|
||||
|
||||
void IXAnimParts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
|
@ -1,298 +1,30 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IXModel.hpp"
|
||||
|
||||
#define IW4X_MODEL_VERSION 8
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IXModel::loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader)
|
||||
{
|
||||
if (entry->nodes)
|
||||
{
|
||||
entry->nodes = reader->readArrayOnce<Game::XSurfaceCollisionNode>(entry->nodeCount);
|
||||
}
|
||||
|
||||
if (entry->leafs)
|
||||
{
|
||||
entry->leafs = reader->readArrayOnce<Game::XSurfaceCollisionLeaf>(entry->leafCount);
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, [[maybe_unused]] Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (surf->vertInfo.vertsBlend)
|
||||
{
|
||||
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->readArrayOnce<Game::GfxPackedVertex>(surf->vertCount);
|
||||
}
|
||||
|
||||
// Save_XRigidVertListArray
|
||||
if (surf->vertList)
|
||||
{
|
||||
surf->vertList = reader->readArrayOnce<Game::XRigidVertList>(surf->vertListCount);
|
||||
|
||||
for (unsigned int i = 0; i < surf->vertListCount; ++i)
|
||||
{
|
||||
Game::XRigidVertList* rigidVertList = &surf->vertList[i];
|
||||
|
||||
if (rigidVertList->collisionTree)
|
||||
{
|
||||
rigidVertList->collisionTree = reader->readObject<Game::XSurfaceCollisionTree>();
|
||||
this->loadXSurfaceCollisionTree(rigidVertList->collisionTree, reader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Access index block
|
||||
if (surf->triIndices)
|
||||
{
|
||||
surf->triIndices = reader->readArrayOnce<unsigned short>(surf->triCount * 3);
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader->readCString();
|
||||
}
|
||||
|
||||
if (asset->surfs)
|
||||
{
|
||||
asset->surfs = reader->readArrayOnce<Game::XSurface>(asset->numsurfs);
|
||||
|
||||
for (int i = 0; i < asset->numsurfs; ++i)
|
||||
{
|
||||
this->loadXSurface(&asset->surfs[i], reader, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IXModel::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File modelFile(std::format("xmodel/{}.iw4xModel", name));
|
||||
header->model = builder->getIW4OfApi()->read<Game::XModel>(Game::XAssetType::ASSET_TYPE_XMODEL, name);
|
||||
|
||||
if (!builder->isPrimaryAsset() && (!Components::ZoneBuilder::ZBPreferDiskAssets.get<bool>() || !modelFile.exists()))
|
||||
if (header->model)
|
||||
{
|
||||
header->model = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).model;
|
||||
if (header->model) return;
|
||||
}
|
||||
|
||||
|
||||
if (modelFile.exists())
|
||||
{
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), modelFile.getBuffer());
|
||||
|
||||
__int64 magic = reader.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xModl", 8))
|
||||
// ???
|
||||
if (header->model->physCollmap)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, header is invalid!", name);
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { header->model->physCollmap });
|
||||
}
|
||||
|
||||
int version = reader.read<int>();
|
||||
if (version != IW4X_MODEL_VERSION)
|
||||
if (header->model->physPreset)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, expected version is {}, but it was {}!", name, IW4X_MODEL_VERSION, version);
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { header->model->physPreset });
|
||||
}
|
||||
|
||||
Game::XModel* asset = reader.readObject<Game::XModel>();
|
||||
|
||||
if (asset->name)
|
||||
for (size_t i = 0; i < header->model->numLods; i++)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
const auto& info = header->model->lodInfo[i];
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, { info.modelSurfs });
|
||||
}
|
||||
|
||||
if (asset->boneNames)
|
||||
{
|
||||
asset->boneNames = builder->getAllocator()->allocateArray<unsigned short>(asset->numBones);
|
||||
|
||||
for (char i = 0; i < asset->numBones; ++i)
|
||||
{
|
||||
asset->boneNames[i] = static_cast<std::uint16_t>(Game::SL_GetString(reader.readCString(), 0));
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->parentList)
|
||||
{
|
||||
asset->parentList = reader.readArrayOnce<unsigned char>(asset->numBones - asset->numRootBones);
|
||||
}
|
||||
|
||||
if (asset->quats)
|
||||
{
|
||||
asset->quats = reader.readArrayOnce<short>((asset->numBones - asset->numRootBones) * 4);
|
||||
}
|
||||
|
||||
if (asset->trans)
|
||||
{
|
||||
asset->trans = reader.readArrayOnce<float>((asset->numBones - asset->numRootBones) * 3);
|
||||
}
|
||||
|
||||
if (asset->partClassification)
|
||||
{
|
||||
asset->partClassification = reader.readArrayOnce<unsigned char>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->baseMat)
|
||||
{
|
||||
asset->baseMat = reader.readArrayOnce<Game::DObjAnimMat>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->materialHandles)
|
||||
{
|
||||
asset->materialHandles = reader.readArray<Game::Material*>(asset->numsurfs);
|
||||
|
||||
for (unsigned char i = 0; i < asset->numsurfs; ++i)
|
||||
{
|
||||
if (asset->materialHandles[i])
|
||||
{
|
||||
asset->materialHandles[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save_XModelLodInfoArray
|
||||
{
|
||||
for (unsigned int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (asset->lodInfo[i].modelSurfs)
|
||||
{
|
||||
asset->lodInfo[i].modelSurfs = reader.readObject<Game::XModelSurfs>();
|
||||
this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader, builder);
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODEL_SURFS, { asset->lodInfo[i].modelSurfs });
|
||||
|
||||
asset->lodInfo[i].surfs = asset->lodInfo[i].modelSurfs->surfs;
|
||||
|
||||
// Zero that for now, it breaks the models.
|
||||
// TODO: Figure out how that can be converted
|
||||
asset->lodInfo[i].smcBaseIndexPlusOne = 0;
|
||||
asset->lodInfo[i].smcSubIndexMask = 0;
|
||||
asset->lodInfo[i].smcBucket = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save_XModelCollSurfArray
|
||||
if (asset->collSurfs)
|
||||
{
|
||||
asset->collSurfs = reader.readArray<Game::XModelCollSurf_s>(asset->numCollSurfs);
|
||||
|
||||
for (int i = 0; i < asset->numCollSurfs; ++i)
|
||||
{
|
||||
Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i];
|
||||
|
||||
if (collSurf->collTris)
|
||||
{
|
||||
collSurf->collTris = reader.readArray<Game::XModelCollTri_s>(collSurf->numCollTris);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->boneInfo)
|
||||
{
|
||||
asset->boneInfo = reader.readArray<Game::XBoneInfo>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->physPreset)
|
||||
{
|
||||
asset->physPreset = reader.readObject<Game::PhysPreset>();
|
||||
|
||||
if (asset->physPreset->name)
|
||||
{
|
||||
asset->physPreset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->physPreset->sndAliasPrefix)
|
||||
{
|
||||
asset->physPreset->sndAliasPrefix = reader.readCString();
|
||||
}
|
||||
|
||||
// This is an experiment, ak74 fails though
|
||||
if (asset->name == "weapon_ak74u"s)
|
||||
{
|
||||
asset->physPreset = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
Game::PhysPreset* preset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset->name, builder).physPreset;
|
||||
if (preset)
|
||||
{
|
||||
asset->physPreset = preset;
|
||||
}
|
||||
else
|
||||
{
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->physCollmap)
|
||||
{
|
||||
Game::PhysCollmap* collmap = reader.readObject<Game::PhysCollmap>();
|
||||
asset->physCollmap = collmap;
|
||||
|
||||
if (collmap->name)
|
||||
{
|
||||
collmap->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (collmap->geoms)
|
||||
{
|
||||
collmap->geoms = reader.readArray<Game::PhysGeomInfo>(collmap->count);
|
||||
|
||||
for (unsigned int i = 0; i < collmap->count; ++i)
|
||||
{
|
||||
Game::PhysGeomInfo* geom = &collmap->geoms[i];
|
||||
|
||||
if (geom->brushWrapper)
|
||||
{
|
||||
Game::BrushWrapper* brush = reader.readObject<Game::BrushWrapper>();
|
||||
geom->brushWrapper = brush;
|
||||
{
|
||||
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)
|
||||
{
|
||||
Game::cbrushside_t* side = &brush->brush.sides[j];
|
||||
|
||||
// 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)
|
||||
{
|
||||
brush->planes = reader.readArray<Game::cplane_s>(brush->brush.numsides);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSCOLLMAP, { asset->physCollmap });
|
||||
// asset->physCollmap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!reader.end())
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading model '{}' failed, remaining raw data found!", name);
|
||||
}
|
||||
|
||||
header->model = asset;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,11 +10,5 @@ namespace Assets
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
|
||||
private:
|
||||
std::map<void*, void*> triIndicies;
|
||||
void loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder);
|
||||
void loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader, Components::ZoneBuilder::Zone* builder);
|
||||
void loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader);
|
||||
};
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
#include <STDInclude.hpp>
|
||||
#include "IclipMap_t.hpp"
|
||||
|
||||
#define IW4X_CLIPMAP_VERSION 2
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
void IclipMap_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
@ -37,7 +35,7 @@ namespace Assets
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
// not sure if this is needed but both brushside and brushedge need it and it can't hurt
|
||||
for (int i = 0; i < asset->planeCount; ++i)
|
||||
for (size_t i = 0; i < asset->planeCount; ++i)
|
||||
{
|
||||
builder->storePointer(&asset->planes[i]);
|
||||
buffer->save(&asset->planes[i]);
|
||||
@ -192,7 +190,13 @@ namespace Assets
|
||||
SaveLogEnter("cLeafBrush_t");
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_2);
|
||||
buffer->saveArray(asset->leafbrushes, asset->numLeafBrushes);
|
||||
|
||||
for (size_t i = 0; i < asset->numLeafBrushes; i++)
|
||||
{
|
||||
builder->storePointer(&asset->leafbrushes[i]);
|
||||
buffer->saveObject(asset->leafbrushes[i]);
|
||||
}
|
||||
|
||||
Utils::Stream::ClearPointer(&dest->leafbrushes);
|
||||
|
||||
SaveLogExit();
|
||||
@ -213,6 +217,9 @@ namespace Assets
|
||||
{
|
||||
if (node[i].data.leaf.brushes)
|
||||
{
|
||||
assert(node[i].data.leaf.brushes >= asset->leafbrushes);
|
||||
assert(node[i].data.leaf.brushes < asset->leafbrushes + sizeof(unsigned short) * asset->numLeafBrushes);
|
||||
|
||||
if (builder->hasPointer(node[i].data.leaf.brushes))
|
||||
{
|
||||
node[i].data.leaf.brushes = builder->getPointer(node[i].data.leaf.brushes);
|
||||
@ -278,7 +285,7 @@ namespace Assets
|
||||
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
for (int i = 0; i < asset->borderCount; ++i)
|
||||
for (size_t i = 0; i < asset->borderCount; ++i)
|
||||
{
|
||||
builder->storePointer(&asset->borders[i]);
|
||||
buffer->save(&asset->borders[i]);
|
||||
@ -564,381 +571,13 @@ namespace Assets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder->loadAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, asset);
|
||||
}
|
||||
|
||||
void IclipMap_t::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 clipFile(std::format("clipmap/{}.iw4xClipMap", name));
|
||||
if (!clipFile.exists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Game::clipMap_t* clipMap = builder->getAllocator()->allocate<Game::clipMap_t>();
|
||||
if (!clipMap)
|
||||
{
|
||||
Components::Logger::Print("Error allocating memory for clipMap_t structure!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::clipMap_t* orgClipMap = nullptr;
|
||||
Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, [](Game::XAssetHeader header, void* clipMap)
|
||||
{
|
||||
if (!*reinterpret_cast<void**>(clipMap))
|
||||
{
|
||||
*reinterpret_cast<Game::clipMap_t**>(clipMap) = header.clipMap;
|
||||
}
|
||||
}, &orgClipMap, false);
|
||||
|
||||
if (orgClipMap) std::memcpy(clipMap, orgClipMap, sizeof Game::clipMap_t);
|
||||
|
||||
Utils::Stream::Reader reader(builder->getAllocator(), clipFile.getBuffer());
|
||||
|
||||
__int64 magic = reader.read<__int64>();
|
||||
if (std::memcmp(&magic, "IW4xClip", 8))
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading clipMap_t '{}' failed, header is invalid!", name);
|
||||
}
|
||||
|
||||
int version = reader.read<int>();
|
||||
if (version > IW4X_CLIPMAP_VERSION)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Reading clipmap '{}' failed, expected version is {}, but it was {}!", name, IW4X_CLIPMAP_VERSION, version);
|
||||
}
|
||||
|
||||
clipMap->name = reader.readCString();
|
||||
|
||||
clipMap->planeCount = reader.read<int>();
|
||||
clipMap->numStaticModels = reader.read<int>();
|
||||
clipMap->numMaterials = reader.read<int>();
|
||||
clipMap->numBrushSides = reader.read<int>();
|
||||
clipMap->numBrushEdges = reader.read<int>();
|
||||
clipMap->numNodes = reader.read<int>();
|
||||
clipMap->numLeafs = reader.read<int>();
|
||||
clipMap->leafbrushNodesCount = reader.read<int>();
|
||||
clipMap->numLeafBrushes = reader.read<int>();
|
||||
clipMap->numLeafSurfaces = reader.read<int>();
|
||||
clipMap->vertCount = reader.read<int>();
|
||||
clipMap->triCount = reader.read<int>();
|
||||
clipMap->borderCount = reader.read<int>();
|
||||
clipMap->partitionCount = reader.read<int>();
|
||||
clipMap->aabbTreeCount = reader.read<int>();
|
||||
clipMap->numSubModels = reader.read<int>();
|
||||
clipMap->numBrushes = reader.read<short>();
|
||||
clipMap->dynEntCount[0] = reader.read<unsigned __int16>();
|
||||
clipMap->dynEntCount[1] = reader.read<unsigned __int16>();
|
||||
|
||||
if (clipMap->planeCount)
|
||||
{
|
||||
void* oldPtr = reader.read<void*>();
|
||||
clipMap->planes = reader.readArray<Game::cplane_s>(clipMap->planeCount);
|
||||
|
||||
if (builder->getAllocator()->isPointerMapped(oldPtr))
|
||||
{
|
||||
clipMap->planes = builder->getAllocator()->getPointer<Game::cplane_s>(oldPtr);
|
||||
Components::Logger::Print("ClipMap dpvs planes already mapped. This shouldn't happen. Make sure to load the ClipMap before the GfxWorld!\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->getAllocator()->mapPointer(oldPtr, clipMap->planes);
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->numStaticModels)
|
||||
{
|
||||
clipMap->staticModelList = builder->getAllocator()->allocateArray<Game::cStaticModel_s>(clipMap->numStaticModels);
|
||||
for (unsigned int i = 0; i < clipMap->numStaticModels; ++i)
|
||||
{
|
||||
std::string modelName = reader.readString();
|
||||
if (modelName != "NONE"s)
|
||||
{
|
||||
clipMap->staticModelList[i].xmodel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, modelName, builder).model;
|
||||
}
|
||||
float* buf = reader.readArray<float>(18);
|
||||
memcpy(&clipMap->staticModelList[i].origin, buf, sizeof(float) * 18);
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->numMaterials)
|
||||
{
|
||||
clipMap->materials = builder->getAllocator()->allocateArray<Game::ClipMaterial>(clipMap->numMaterials);
|
||||
for (unsigned int j = 0; j < clipMap->numMaterials; ++j)
|
||||
{
|
||||
clipMap->materials[j].name = reader.readArray<char>(64);
|
||||
clipMap->materials[j].surfaceFlags = reader.read<int>();
|
||||
clipMap->materials[j].contents = reader.read<int>();
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->numBrushSides)
|
||||
{
|
||||
clipMap->brushsides = builder->getAllocator()->allocateArray<Game::cbrushside_t>(clipMap->numBrushSides);
|
||||
for (unsigned int i = 0; i < clipMap->numBrushSides; ++i)
|
||||
{
|
||||
int planeIndex = reader.read<int>();
|
||||
if (planeIndex < 0 || planeIndex >= clipMap->planeCount)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "invalid plane index");
|
||||
return;
|
||||
}
|
||||
clipMap->brushsides[i].plane = &clipMap->planes[planeIndex];
|
||||
clipMap->brushsides[i].materialNum = static_cast<unsigned short>(reader.read<int>()); // materialNum
|
||||
clipMap->brushsides[i].firstAdjacentSideOffset = static_cast<char>(reader.read<short>()); // firstAdjacentSide
|
||||
clipMap->brushsides[i].edgeCount = reader.read<char>(); // edgeCount
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->numBrushEdges)
|
||||
{
|
||||
clipMap->brushEdges = reader.readArray<char>(clipMap->numBrushEdges);
|
||||
}
|
||||
|
||||
if (clipMap->numNodes)
|
||||
{
|
||||
clipMap->nodes = builder->getAllocator()->allocateArray<Game::cNode_t>(clipMap->numNodes);
|
||||
for (unsigned int i = 0; i < clipMap->numNodes; ++i)
|
||||
{
|
||||
int planeIndex = reader.read<int>();
|
||||
if (planeIndex < 0 || planeIndex >= clipMap->planeCount)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "invalid plane index\n");
|
||||
return;
|
||||
}
|
||||
clipMap->nodes[i].plane = &clipMap->planes[planeIndex];
|
||||
clipMap->nodes[i].children[0] = reader.read<short>();
|
||||
clipMap->nodes[i].children[1] = reader.read<short>();
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->numLeafs)
|
||||
{
|
||||
clipMap->leafs = reader.readArray<Game::cLeaf_t>(clipMap->numLeafs);
|
||||
}
|
||||
|
||||
if (clipMap->leafbrushNodesCount)
|
||||
{
|
||||
clipMap->leafbrushNodes = builder->getAllocator()->allocateArray<Game::cLeafBrushNode_s>(clipMap->leafbrushNodesCount);
|
||||
for (unsigned int i = 0; i < clipMap->leafbrushNodesCount; ++i)
|
||||
{
|
||||
clipMap->leafbrushNodes[i] = reader.read<Game::cLeafBrushNode_s>();
|
||||
|
||||
if (clipMap->leafbrushNodes[i].leafBrushCount > 0)
|
||||
{
|
||||
clipMap->leafbrushNodes[i].data.leaf.brushes = reader.readArray<unsigned short>(clipMap->leafbrushNodes[i].leafBrushCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->numLeafBrushes)
|
||||
{
|
||||
clipMap->leafbrushes = reader.readArray<unsigned short>(clipMap->numLeafBrushes);
|
||||
}
|
||||
|
||||
if (clipMap->numLeafSurfaces)
|
||||
{
|
||||
clipMap->leafsurfaces = reader.readArray<unsigned int>(clipMap->numLeafSurfaces);
|
||||
}
|
||||
|
||||
if (clipMap->vertCount)
|
||||
{
|
||||
clipMap->verts = reader.readArray<Game::vec3_t>(clipMap->vertCount);
|
||||
}
|
||||
|
||||
if (clipMap->triCount)
|
||||
{
|
||||
clipMap->triIndices = reader.readArray<unsigned short>(clipMap->triCount * 3);
|
||||
clipMap->triEdgeIsWalkable = reader.readArray<char>(4 * ((3 * clipMap->triCount + 31) >> 5));
|
||||
}
|
||||
|
||||
if (clipMap->borderCount)
|
||||
{
|
||||
clipMap->borders = reader.readArray<Game::CollisionBorder>(clipMap->borderCount);
|
||||
}
|
||||
|
||||
if (clipMap->partitionCount)
|
||||
{
|
||||
clipMap->partitions = builder->getAllocator()->allocateArray<Game::CollisionPartition>(clipMap->partitionCount);
|
||||
for (int i = 0; i < clipMap->partitionCount; ++i)
|
||||
{
|
||||
clipMap->partitions[i].triCount = reader.read<char>();
|
||||
clipMap->partitions[i].borderCount = reader.read<char>();
|
||||
clipMap->partitions[i].firstTri = reader.read<int>();
|
||||
|
||||
if (clipMap->partitions[i].borderCount > 0)
|
||||
{
|
||||
int index = reader.read<int>();
|
||||
if (index < 0 || index > clipMap->borderCount)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "invalid border index\n");
|
||||
return;
|
||||
}
|
||||
clipMap->partitions[i].borders = &clipMap->borders[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clipMap->aabbTreeCount)
|
||||
{
|
||||
clipMap->aabbTrees = reader.readArray<Game::CollisionAabbTree>(clipMap->aabbTreeCount);
|
||||
}
|
||||
|
||||
if (clipMap->numSubModels)
|
||||
{
|
||||
clipMap->cmodels = reader.readArray<Game::cmodel_t>(clipMap->numSubModels);
|
||||
}
|
||||
|
||||
if (clipMap->numBrushes)
|
||||
{
|
||||
clipMap->brushes = builder->getAllocator()->allocateArray<Game::cbrush_t>(clipMap->numBrushes);
|
||||
memset(clipMap->brushes, 0, sizeof(Game::cbrush_t) * clipMap->numBrushes);
|
||||
for (int i = 0; i < clipMap->numBrushes; ++i)
|
||||
{
|
||||
clipMap->brushes[i].numsides = reader.read<unsigned int>() & 0xFFFF; // todo: check for overflow here
|
||||
if (clipMap->brushes[i].numsides > 0)
|
||||
{
|
||||
auto index = reader.read<unsigned int>();
|
||||
if (index < 0 || index > clipMap->numBrushSides)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "invalid side index\n");
|
||||
return;
|
||||
}
|
||||
clipMap->brushes[i].sides = &clipMap->brushsides[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
clipMap->brushes[i].sides = nullptr;
|
||||
}
|
||||
|
||||
auto index = reader.read<unsigned int>();
|
||||
if (index > clipMap->numBrushEdges)
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "invalid edge index\n");
|
||||
return;
|
||||
}
|
||||
clipMap->brushes[i].baseAdjacentSide = &clipMap->brushEdges[index];
|
||||
|
||||
char* tmp = reader.readArray<char>(12);
|
||||
memcpy(&clipMap->brushes[i].axialMaterialNum, tmp, 12);
|
||||
|
||||
//todo check for overflow
|
||||
for (int r = 0; r < 2; ++r)
|
||||
{
|
||||
for (int c = 0; c < 3; ++c)
|
||||
{
|
||||
clipMap->brushes[i].firstAdjacentSideOffsets[r][c] = reader.read<short>() & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
tmp = reader.readArray<char>(6);
|
||||
memcpy(&clipMap->brushes[i].edgeCount, tmp, 6);
|
||||
}
|
||||
|
||||
clipMap->brushBounds = reader.readArray<Game::Bounds>(clipMap->numBrushes);
|
||||
clipMap->brushContents = reader.readArray<int>(clipMap->numBrushes);
|
||||
}
|
||||
|
||||
for (int x = 0; x < 2; ++x)
|
||||
{
|
||||
if (clipMap->dynEntCount[x])
|
||||
{
|
||||
clipMap->dynEntDefList[x] = builder->getAllocator()->allocateArray<Game::DynEntityDef>(clipMap->dynEntCount[x]);
|
||||
for (int i = 0; i < clipMap->dynEntCount[x]; ++i)
|
||||
{
|
||||
clipMap->dynEntDefList[x][i].type = reader.read<Game::DynEntityType>();
|
||||
clipMap->dynEntDefList[x][i].pose = reader.read<Game::GfxPlacement>();
|
||||
std::string tempName = reader.readString();
|
||||
if (tempName != "NONE"s)
|
||||
{
|
||||
clipMap->dynEntDefList[x][i].xModel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, tempName, builder).model;
|
||||
}
|
||||
|
||||
clipMap->dynEntDefList[x][i].brushModel = reader.read<short>();
|
||||
clipMap->dynEntDefList[x][i].physicsBrushModel = reader.read<short>();
|
||||
|
||||
tempName = reader.readString();
|
||||
if (tempName != "NONE"s)
|
||||
{
|
||||
clipMap->dynEntDefList[x][i].destroyFx = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, tempName, builder).fx;
|
||||
}
|
||||
|
||||
tempName = reader.readString();
|
||||
if (tempName != "NONE"s)
|
||||
{
|
||||
clipMap->dynEntDefList[x][i].physPreset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, tempName, builder).physPreset;
|
||||
}
|
||||
|
||||
clipMap->dynEntDefList[x][i].health = reader.read<int>();
|
||||
clipMap->dynEntDefList[x][i].mass = reader.read<Game::PhysMass>();
|
||||
clipMap->dynEntDefList[x][i].contents = reader.read<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clipMap->smodelNodeCount = reader.read<unsigned short>();
|
||||
clipMap->smodelNodes = reader.readArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
|
||||
|
||||
clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, std::format("maps/mp/{}.d3dbsp", name), builder).mapEnts;
|
||||
|
||||
// add triggers to mapEnts
|
||||
if (version >= 2) {
|
||||
if (clipMap->numSubModels > 0) {
|
||||
clipMap->mapEnts->trigger.count = clipMap->numSubModels;
|
||||
clipMap->mapEnts->trigger.hullCount = clipMap->numSubModels;
|
||||
|
||||
Game::TriggerHull* hulls = builder->getAllocator()->allocateArray<Game::TriggerHull>(clipMap->mapEnts->trigger.hullCount);
|
||||
Game::TriggerModel* models = builder->getAllocator()->allocateArray<Game::TriggerModel>(clipMap->mapEnts->trigger.count);
|
||||
|
||||
for (unsigned int i = 0; i < clipMap->numSubModels; ++i)
|
||||
{
|
||||
models[i] = reader.read<Game::TriggerModel>();
|
||||
hulls[i] = reader.read<Game::TriggerHull>();
|
||||
}
|
||||
|
||||
size_t slabCount = reader.read<size_t>();
|
||||
clipMap->mapEnts->trigger.slabCount = slabCount;
|
||||
Game::TriggerSlab* slabs = builder->getAllocator()->allocateArray<Game::TriggerSlab>(clipMap->mapEnts->trigger.slabCount);
|
||||
for (unsigned int i = 0; i < clipMap->mapEnts->trigger.slabCount; i++) {
|
||||
slabs[i] = reader.read<Game::TriggerSlab>();
|
||||
}
|
||||
|
||||
|
||||
clipMap->mapEnts->trigger.models = &models[0];
|
||||
clipMap->mapEnts->trigger.hulls = &hulls[0];
|
||||
clipMap->mapEnts->trigger.slabs = &slabs[0];
|
||||
}
|
||||
}
|
||||
|
||||
clipMap->checksum = reader.read<int>();
|
||||
|
||||
// This mustn't be null and has to have at least 1 'valid' entry.
|
||||
if (!clipMap->smodelNodeCount || !clipMap->smodelNodes)
|
||||
{
|
||||
clipMap->smodelNodeCount = 1;
|
||||
clipMap->smodelNodes = builder->getAllocator()->allocateArray<Game::SModelAabbNode>(clipMap->smodelNodeCount);
|
||||
|
||||
clipMap->smodelNodes[0].bounds.halfSize[0] = -131072.000f;
|
||||
clipMap->smodelNodes[0].bounds.halfSize[1] = -131072.000f;
|
||||
clipMap->smodelNodes[0].bounds.halfSize[2] = -131072.000f;
|
||||
}
|
||||
|
||||
// These mustn't be null, but they don't need to be valid.
|
||||
for (int i = 0; i < 2 && clipMap->dynEntCount[i]; ++i)
|
||||
{
|
||||
Utils::Stream::ClearPointer(&clipMap->dynEntPoseList[i]);
|
||||
Utils::Stream::ClearPointer(&clipMap->dynEntClientList[i]);
|
||||
Utils::Stream::ClearPointer(&clipMap->dynEntCollList[i]);
|
||||
}
|
||||
|
||||
if (!reader.end())
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Clipmap data left!");
|
||||
}
|
||||
|
||||
header->clipMap = clipMap;
|
||||
header->clipMap = builder->getIW4OfApi()->read<Game::clipMap_t>(Game::XAssetType::ASSET_TYPE_CLIPMAP_MP, _name);
|
||||
assert(header->data);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ namespace Assets
|
||||
void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override;
|
||||
void load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder) override;
|
||||
static void dump(Game::XAssetHeader /*header*/);
|
||||
|
||||
private:
|
||||
class SModelQuadtree
|
||||
|
@ -7,351 +7,7 @@ namespace Assets
|
||||
{
|
||||
void Isnd_alias_list_t::load(Game::XAssetHeader* header, const std::string& name, Components::ZoneBuilder::Zone* builder)
|
||||
{
|
||||
Components::FileSystem::File aliasFile(std::format("sounds/{}.json", name));
|
||||
if (!aliasFile.exists())
|
||||
{
|
||||
header->sound = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).sound;
|
||||
return;
|
||||
}
|
||||
|
||||
auto* aliasList = builder->getAllocator()->allocate<Game::snd_alias_list_t>();
|
||||
if (!aliasList)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Failed to allocate memory for sound alias structure!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
nlohmann::json infoData;
|
||||
try
|
||||
{
|
||||
infoData = nlohmann::json::parse(aliasFile.getBuffer());
|
||||
}
|
||||
catch (const nlohmann::json::parse_error& ex)
|
||||
{
|
||||
Components::Logger::PrintError(Game::CON_CHANNEL_ERROR, "Json Parse Error: {}\n", ex.what());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
nlohmann::json aliasesContainer = infoData["head"];
|
||||
nlohmann::json::array_t aliases = aliasesContainer;
|
||||
|
||||
aliasList->count = aliases.size();
|
||||
|
||||
// Allocate
|
||||
aliasList->head = builder->getAllocator()->allocateArray<Game::snd_alias_t>(aliasList->count);
|
||||
if (!aliasList->head)
|
||||
{
|
||||
Components::Logger::Print("Error allocating memory for sound alias structure!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
aliasList->aliasName = builder->getAllocator()->duplicateString(name);
|
||||
|
||||
for (size_t i = 0; i < aliasList->count; i++)
|
||||
{
|
||||
nlohmann::json head = aliasesContainer[i];
|
||||
|
||||
if (!infoData.is_object())
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to load sound {}!", name);
|
||||
return;
|
||||
}
|
||||
|
||||
aliasList->head->soundFile = builder->getAllocator()->allocate<Game::SoundFile>();
|
||||
if (!aliasList->head->soundFile)
|
||||
{
|
||||
Components::Logger::Print("Error allocating memory for sound alias structure!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Game::snd_alias_t* alias = aliasList->head;
|
||||
|
||||
// try and parse everything and if it fails then fail for the whole file
|
||||
auto type = head["type"];
|
||||
auto subtitle = head["subtitle"];
|
||||
auto secondaryAliasName = head["secondaryAliasName"];
|
||||
auto chainAliasName = head["chainAliasName"];
|
||||
auto soundFile = head["soundFile"];
|
||||
auto sequence = head["sequence"];
|
||||
auto volMin = head["volMin"];
|
||||
auto volMax = head["volMax"];
|
||||
auto pitchMin = head["pitchMin"];
|
||||
auto pitchMax = head["pitchMax"];
|
||||
auto distMin = head["distMin"];
|
||||
auto distMax = head["distMax"];
|
||||
auto flags = head["flags"];
|
||||
auto slavePercentage = head["slavePercentage"];
|
||||
auto probability = head["probability"];
|
||||
auto lfePercentage = head["lfePercentage"];
|
||||
auto centerPercentage = head["centerPercentage"];
|
||||
auto startDelay = head["startDelay"];
|
||||
auto volumeFalloffCurve = head["volumeFalloffCurve"];
|
||||
auto envelopMin = head["envelopMin"];
|
||||
auto envelopMax = head["envelopMax"];
|
||||
auto envelopPercentage = head["envelopPercentage"];
|
||||
auto speakerMap = head["speakerMap"];
|
||||
auto aliasName = head["aliasName"];
|
||||
|
||||
// Fix casing
|
||||
if (soundFile.is_null())
|
||||
{
|
||||
soundFile = head["soundfile"];
|
||||
|
||||
Components::Logger::Print("Fixed casing on {}\n", name);
|
||||
}
|
||||
|
||||
if (type.is_null() || soundFile.is_null())
|
||||
{
|
||||
Components::Logger::Print("Type is {}\n", type.dump());
|
||||
Components::Logger::Print("SoundFile is {}\n", soundFile.dump());
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}! Each alias must have at least a type and a soundFile\n", name);
|
||||
return;
|
||||
}
|
||||
|
||||
#define CHECK(x, type) (x.is_##type##() || x.is_null())
|
||||
|
||||
// TODO: actually support all of those properties
|
||||
if (!CHECK(type, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "type", Utils::Json::TypeToString(type.type()), type.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(subtitle, string))
|
||||
{
|
||||
Components::Logger::Print("{} is not string but {} ({})\n", "subtitle", Utils::Json::TypeToString(subtitle.type()), subtitle.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(aliasName, string))
|
||||
{
|
||||
Components::Logger::Print("{} is not string but {} ({})\n", "aliasName", Utils::Json::TypeToString(aliasName.type()), aliasName.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(secondaryAliasName, string))
|
||||
{
|
||||
Components::Logger::Print("{} is not string but {} ({})\n", "secondaryAliasName", Utils::Json::TypeToString(secondaryAliasName.type()), secondaryAliasName.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(chainAliasName, string))
|
||||
{
|
||||
Components::Logger::Print("{} is not string but {} ({})\n", "chainAliasName", Utils::Json::TypeToString(chainAliasName.type()), chainAliasName.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(soundFile, string))
|
||||
{
|
||||
Components::Logger::Print("{} is not string but {} ({})\n", "soundFile", Utils::Json::TypeToString(soundFile.type()), soundFile.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(sequence, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "sequence", Utils::Json::TypeToString(sequence.type()), sequence.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(volMin, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "volMin", Utils::Json::TypeToString(volMin.type()), volMin.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(volMax, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "volMax", Utils::Json::TypeToString(volMax.type()), volMax.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(pitchMin, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "pitchMin", Utils::Json::TypeToString(pitchMin.type()), pitchMin.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(pitchMax, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ()\n", "pitchMax", Utils::Json::TypeToString(pitchMax.type()), pitchMax.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(probability, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({}))\n", "probability", Utils::Json::TypeToString(probability.type()), probability.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(lfePercentage, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "lfePercentage", Utils::Json::TypeToString(lfePercentage.type()), lfePercentage.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(centerPercentage, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "centerPercentage", Utils::Json::TypeToString(centerPercentage.type()), centerPercentage.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(startDelay, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "startDelay", Utils::Json::TypeToString(startDelay.type()), startDelay.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(volumeFalloffCurve, string))
|
||||
{
|
||||
Components::Logger::Print("{}s is not string but {} ({})\n", "volumeFalloffCurve", Utils::Json::TypeToString(volumeFalloffCurve.type()), volumeFalloffCurve.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(envelopMin, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "envelopMin", Utils::Json::TypeToString(envelopMin.type()), envelopMin.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(envelopMax, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "envelopMax", Utils::Json::TypeToString(envelopMax.type()), envelopMax.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(envelopPercentage, number))
|
||||
{
|
||||
Components::Logger::Print("{} is not number but {} ({})\n", "envelopPercentage", Utils::Json::TypeToString(envelopPercentage.type()), envelopPercentage.dump());
|
||||
}
|
||||
|
||||
if (!CHECK(speakerMap, object))
|
||||
{
|
||||
Components::Logger::Print("{} is not object but {} ({})\n", "speakerMap", Utils::Json::TypeToString(speakerMap.type()), speakerMap.dump());
|
||||
}
|
||||
|
||||
|
||||
if (CHECK(type, number) && CHECK(aliasName, string) && CHECK(subtitle, string) && CHECK(secondaryAliasName, string) && CHECK(chainAliasName, string) &&
|
||||
CHECK(soundFile, string) && CHECK(sequence, number) && CHECK(volMin, number) && CHECK(volMax, number) && CHECK(pitchMin, number) &&
|
||||
CHECK(pitchMax, number) && CHECK(distMin, number) && CHECK(distMax, number) && CHECK(flags, number) && CHECK(slavePercentage, number) &&
|
||||
CHECK(probability, number) && CHECK(lfePercentage, number) && CHECK(centerPercentage, number) && CHECK(startDelay, number) &&
|
||||
CHECK(volumeFalloffCurve, string) && CHECK(envelopMin, number) && CHECK(envelopMax, number) && CHECK(envelopPercentage, number) &&
|
||||
CHECK(speakerMap, object))
|
||||
{
|
||||
|
||||
alias->soundFile->exists = true;
|
||||
alias->aliasName = builder->getAllocator()->duplicateString(aliasName.get<std::string>());
|
||||
|
||||
if (subtitle.is_string())
|
||||
{
|
||||
alias->subtitle = builder->getAllocator()->duplicateString(subtitle.get<std::string>());
|
||||
}
|
||||
if (secondaryAliasName.is_string())
|
||||
{
|
||||
alias->secondaryAliasName = builder->getAllocator()->duplicateString(secondaryAliasName.get<std::string>());
|
||||
}
|
||||
if (chainAliasName.is_string())
|
||||
{
|
||||
alias->chainAliasName = builder->getAllocator()->duplicateString(chainAliasName.get<std::string>());
|
||||
}
|
||||
|
||||
alias->sequence = sequence.get<int>();
|
||||
alias->volMin = volMin.get<float>();
|
||||
alias->volMax = volMax.get<float>();
|
||||
alias->pitchMin = pitchMin.get<float>();
|
||||
alias->pitchMax = pitchMax.get<float>();
|
||||
alias->distMin = distMin.get<float>();
|
||||
alias->distMax = distMax.get<float>();
|
||||
alias->flags = flags.get<int>();
|
||||
alias->___u15.slavePercentage = slavePercentage.get<float>();
|
||||
alias->probability = probability.get<float>();
|
||||
alias->lfePercentage = lfePercentage.get<float>();
|
||||
alias->centerPercentage = centerPercentage.get<float>();
|
||||
alias->startDelay = startDelay.get<int>();
|
||||
alias->envelopMin = envelopMin.get<float>();
|
||||
alias->envelopMax = envelopMax.get<float>();
|
||||
alias->envelopPercentage = envelopPercentage.get<float>();
|
||||
|
||||
// Speaker map object
|
||||
if (!speakerMap.is_null())
|
||||
{
|
||||
alias->speakerMap = builder->getAllocator()->allocate<Game::SpeakerMap>();
|
||||
if (!alias->speakerMap)
|
||||
{
|
||||
Components::Logger::Print("Error allocating memory for speakermap in sound alias{}!\n", alias->aliasName);
|
||||
return;
|
||||
}
|
||||
|
||||
alias->speakerMap->name = builder->getAllocator()->duplicateString(speakerMap["name"].get<std::string>());
|
||||
alias->speakerMap->isDefault = speakerMap["isDefault"].get<bool>();
|
||||
|
||||
if (speakerMap["channelMaps"].is_array())
|
||||
{
|
||||
nlohmann::json::array_t channelMaps = speakerMap["channelMaps"];
|
||||
|
||||
assert(channelMaps.size() <= 4);
|
||||
|
||||
// channelMapIndex should never exceed 1
|
||||
for (size_t channelMapIndex = 0; channelMapIndex < 2; channelMapIndex++)
|
||||
{
|
||||
// subChannelIndex should never exceed 1
|
||||
for (size_t subChannelIndex = 0; subChannelIndex < 2; subChannelIndex++)
|
||||
{
|
||||
nlohmann::json channelMap = channelMaps[channelMapIndex * 2 + subChannelIndex]; // 0-3
|
||||
|
||||
nlohmann::json::array_t speakers = channelMap["speakers"];
|
||||
|
||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount = speakers.size();
|
||||
|
||||
for (size_t speakerIndex = 0; speakerIndex < alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakerCount; speakerIndex++)
|
||||
{
|
||||
auto speaker = speakers[speakerIndex];
|
||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[0] = speaker["levels0"].get<float>();
|
||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].levels[1] = speaker["levels1"].get<float>();
|
||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].numLevels = speaker["numLevels"].get<int>();
|
||||
alias->speakerMap->channelMaps[channelMapIndex][subChannelIndex].speakers[speakerIndex].speaker = speaker["speaker"].get<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (volumeFalloffCurve.is_string())
|
||||
{
|
||||
auto fallOffCurve = volumeFalloffCurve.get<std::string>();
|
||||
|
||||
if (fallOffCurve.empty())
|
||||
{
|
||||
fallOffCurve = "$default";
|
||||
}
|
||||
|
||||
auto curve = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, fallOffCurve, builder).sndCurve;
|
||||
alias->volumeFalloffCurve = curve;
|
||||
}
|
||||
|
||||
if (static_cast<Game::snd_alias_type_t>(type.get<int>()) == Game::snd_alias_type_t::SAT_LOADED) // Loaded
|
||||
{
|
||||
alias->soundFile->type = Game::SAT_LOADED;
|
||||
alias->soundFile->u.loadSnd = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, soundFile.get<std::string>(), builder).loadSnd;
|
||||
}
|
||||
else if (static_cast<Game::snd_alias_type_t>(type.get<int>()) == Game::snd_alias_type_t::SAT_STREAMED) // Streamed
|
||||
{
|
||||
alias->soundFile->type = Game::SAT_STREAMED;
|
||||
|
||||
std::string streamedFile = soundFile.get<std::string>();
|
||||
std::string directory = ""s;
|
||||
int split = streamedFile.find_last_of('/');
|
||||
|
||||
if (split >= 0)
|
||||
{
|
||||
directory = streamedFile.substr(0, split);
|
||||
streamedFile = streamedFile.substr(split+1);
|
||||
}
|
||||
|
||||
alias->soundFile->u.streamSnd.filename.info.raw.dir = builder->getAllocator()->duplicateString(directory.c_str());
|
||||
alias->soundFile->u.streamSnd.filename.info.raw.name = builder->getAllocator()->duplicateString(streamedFile.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}! Invalid sound type {}\n", name, type.get<std::string>());
|
||||
return;
|
||||
}
|
||||
|
||||
aliasList->head[i] = *alias;
|
||||
}
|
||||
else
|
||||
{
|
||||
Components::Logger::Error(Game::ERR_FATAL, "Failed to parse sound {}!\n", name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
header->sound = aliasList;
|
||||
|
||||
#undef CHECK
|
||||
|
||||
header->sound = builder->getIW4OfApi()->read<Game::snd_alias_list_t>(Game::XAssetType::ASSET_TYPE_SOUND, name);
|
||||
}
|
||||
|
||||
void Isnd_alias_list_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder)
|
||||
@ -392,8 +48,16 @@ namespace Assets
|
||||
|
||||
if (asset->aliasName)
|
||||
{
|
||||
buffer->saveString(builder->getAssetName(this->getType(), asset->aliasName));
|
||||
Utils::Stream::ClearPointer(&dest->aliasName);
|
||||
if (builder->hasPointer(asset->aliasName))
|
||||
{
|
||||
dest->aliasName = builder->getPointer(asset->aliasName);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->storePointer(asset->aliasName);
|
||||
buffer->saveString(asset->aliasName);
|
||||
Utils::Stream::ClearPointer(&dest->aliasName);
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->head)
|
||||
@ -419,8 +83,16 @@ namespace Assets
|
||||
|
||||
if (alias->aliasName)
|
||||
{
|
||||
buffer->saveString(alias->aliasName);
|
||||
Utils::Stream::ClearPointer(&destAlias->aliasName);
|
||||
if (builder->hasPointer(alias->aliasName))
|
||||
{
|
||||
destAlias->aliasName = builder->getPointer(alias->aliasName);
|
||||
}
|
||||
else
|
||||
{
|
||||
builder->storePointer(alias->aliasName);
|
||||
buffer->saveString(alias->aliasName);
|
||||
Utils::Stream::ClearPointer(&destAlias->aliasName);
|
||||
}
|
||||
}
|
||||
|
||||
if (alias->subtitle)
|
||||
@ -467,7 +139,7 @@ namespace Assets
|
||||
{
|
||||
if (alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED)
|
||||
{
|
||||
destSoundFile->u.loadSnd = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->u.loadSnd).loadSnd;
|
||||
destSoundFile->u.loadSnd = builder->saveSubAsset(Game::ASSET_TYPE_LOADED_SOUND, alias->soundFile->u.loadSnd).loadSnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -494,7 +166,7 @@ namespace Assets
|
||||
|
||||
if (alias->volumeFalloffCurve)
|
||||
{
|
||||
destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve;
|
||||
destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve;
|
||||
}
|
||||
|
||||
if (alias->speakerMap)
|
||||
|
@ -159,10 +159,11 @@ namespace Components
|
||||
|
||||
// show error
|
||||
pushad
|
||||
push eax
|
||||
push ebx
|
||||
push edx
|
||||
call R_TextureFromCodeError
|
||||
add esp, 8
|
||||
add esp, 0xC
|
||||
popad
|
||||
|
||||
// go back
|
||||
|
@ -19,8 +19,6 @@ namespace Components
|
||||
volatile bool ZoneBuilder::CommandThreadTerminate = false;
|
||||
std::thread ZoneBuilder::CommandThread;
|
||||
|
||||
Dvar::Var ZoneBuilder::ZBPreferDiskAssets;
|
||||
|
||||
ZoneBuilder::Zone::Zone(const std::string& name) : indexStart(0), externalSize(0),
|
||||
// Reserve 100MB by default.
|
||||
// That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory.
|
||||
@ -28,8 +26,12 @@ namespace Components
|
||||
// Side note: if you need a fastfile larger than 100MB, you're doing it wrong-
|
||||
// Well, decompressed maps can get way larger than 100MB, so let's increase that.
|
||||
buffer(0xC800000),
|
||||
zoneName(name), dataMap("zone_source/" + name + ".csv"), branding{nullptr}, assetDepth(0)
|
||||
zoneName(name),
|
||||
dataMap("zone_source/" + name + ".csv"),
|
||||
branding{nullptr},
|
||||
assetDepth(0)
|
||||
{
|
||||
this->initializeIW4OfApi();
|
||||
}
|
||||
|
||||
ZoneBuilder::Zone::~Zone()
|
||||
@ -98,6 +100,11 @@ namespace Components
|
||||
return &this->memAllocator;
|
||||
}
|
||||
|
||||
iw4of::api* ZoneBuilder::Zone::getIW4OfApi()
|
||||
{
|
||||
return &iw4ofApi;
|
||||
}
|
||||
|
||||
void ZoneBuilder::Zone::Zone::build()
|
||||
{
|
||||
if (!this->dataMap.isValid())
|
||||
@ -750,6 +757,49 @@ namespace Components
|
||||
return header;
|
||||
}
|
||||
|
||||
void ZoneBuilder::Zone::initializeIW4OfApi()
|
||||
{
|
||||
iw4of::params_t params;
|
||||
|
||||
params.find_other_asset = [this](int type, const std::string& name) -> void*
|
||||
{
|
||||
return AssetHandler::FindAssetForZone(static_cast<Game::XAssetType>(type), name, this).data;
|
||||
};
|
||||
|
||||
params.fs_read_file = [](const std::string& filename) -> std::string
|
||||
{
|
||||
auto file = FileSystem::File(filename);
|
||||
if (file.exists())
|
||||
{
|
||||
return file.getBuffer();
|
||||
}
|
||||
|
||||
return {};
|
||||
};
|
||||
|
||||
params.store_in_string_table = [](const std::string& text) -> unsigned int
|
||||
{
|
||||
return Game::SL_GetString(text.data(), 0);
|
||||
};
|
||||
|
||||
params.print = [](iw4of::params_t::print_type t, const std::string& message) -> void
|
||||
{
|
||||
switch (t)
|
||||
{
|
||||
case iw4of::params_t::P_ERR:
|
||||
Logger::PrintError(Game::CON_CHANNEL_ERROR, "{}", message);
|
||||
break;
|
||||
case iw4of::params_t::P_WARN:
|
||||
Logger::Print("{}", message);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
params.work_directory = (*Game::fs_basepath)->current.string;
|
||||
|
||||
this->iw4ofApi = iw4of::api{ params };
|
||||
}
|
||||
|
||||
int ZoneBuilder::StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image)
|
||||
{
|
||||
size_t size = 16 + (*loadDef)->resourceSize;
|
||||
@ -1590,9 +1640,6 @@ namespace Components
|
||||
Logger::Print("{}\n", nlohmann::json(images).dump());
|
||||
Logger::Print("------------------- END IWI DUMP -------------------\n");
|
||||
});
|
||||
|
||||
// True by default, but can be put to zero for backward compatibility if needed
|
||||
ZoneBuilder::ZBPreferDiskAssets = Dvar::Register<bool>("zb_prefer_disk_assets", true, Game::DVAR_NONE, "Should ZoneBuilder prefer in-memory assets (requirements) or disk assets when both are present?");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <api.hpp>
|
||||
|
||||
#define XFILE_MAGIC_UNSIGNED 0x3030317566665749
|
||||
#define XFILE_VERSION 276
|
||||
|
||||
@ -38,6 +40,7 @@ namespace Components
|
||||
|
||||
Utils::Stream* getBuffer();
|
||||
Utils::Memory::Allocator* getAllocator();
|
||||
iw4of::api* getIW4OfApi();
|
||||
|
||||
bool hasPointer(const void* pointer);
|
||||
void storePointer(const void* pointer);
|
||||
@ -87,11 +90,14 @@ namespace Components
|
||||
|
||||
void addBranding();
|
||||
|
||||
void initializeIW4OfApi();
|
||||
|
||||
uint32_t safeGetPointer(const void* pointer);
|
||||
|
||||
int indexStart;
|
||||
unsigned int externalSize;
|
||||
Utils::Stream buffer;
|
||||
iw4of::api iw4ofApi;
|
||||
|
||||
std::string zoneName;
|
||||
Utils::CSV dataMap;
|
||||
@ -131,7 +137,6 @@ namespace Components
|
||||
static std::vector<std::pair<Game::XAssetType, std::string>> EndAssetTrace();
|
||||
|
||||
static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, const std::string& name, Zone* builder);
|
||||
static Dvar::Var ZBPreferDiskAssets;
|
||||
|
||||
private:
|
||||
static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image);
|
||||
|
@ -882,8 +882,8 @@ namespace Game
|
||||
{
|
||||
float normal[3];
|
||||
float dist;
|
||||
char type;
|
||||
char pad[3];
|
||||
unsigned char type;
|
||||
unsigned char pad[3];
|
||||
};
|
||||
|
||||
struct cbrushside_t
|
||||
@ -896,13 +896,13 @@ namespace Game
|
||||
|
||||
struct cbrush_t
|
||||
{
|
||||
unsigned __int16 numsides;
|
||||
unsigned __int16 glassPieceIndex;
|
||||
unsigned short numsides;
|
||||
unsigned short glassPieceIndex;
|
||||
cbrushside_t* sides;
|
||||
char* baseAdjacentSide;
|
||||
__int16 axialMaterialNum[2][3];
|
||||
char firstAdjacentSideOffsets[2][3];
|
||||
char edgeCount[2][3];
|
||||
unsigned char* baseAdjacentSide;
|
||||
unsigned short axialMaterialNum[2][3];
|
||||
unsigned char firstAdjacentSideOffsets[2][3];
|
||||
unsigned char edgeCount[2][3];
|
||||
};
|
||||
|
||||
struct BrushWrapper
|
||||
@ -2614,6 +2614,13 @@ namespace Game
|
||||
SASYS_COUNT = 0x3,
|
||||
};
|
||||
|
||||
struct SoundFile
|
||||
{
|
||||
char type;
|
||||
char exists;
|
||||
SoundFileRef u;
|
||||
};
|
||||
|
||||
struct MSSSpeakerLevels
|
||||
{
|
||||
int speaker;
|
||||
@ -2634,11 +2641,24 @@ namespace Game
|
||||
MSSChannelMap channelMaps[2][2];
|
||||
};
|
||||
|
||||
struct SoundFile
|
||||
union SoundAliasFlags
|
||||
{
|
||||
char type;
|
||||
char exists;
|
||||
SoundFileRef u;
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4201)
|
||||
struct
|
||||
{
|
||||
unsigned int looping : 1; // & 1 / 0x1 / 0000 0000 0000 0001
|
||||
unsigned int isMaster : 1; // & 2 / 0x2 / 0000 0000 0000 0010
|
||||
unsigned int isSlave : 1; // & 4 / 0x4 / 0000 0000 0000 0100
|
||||
unsigned int fullDryLevel : 1; // & 8 / 0x8 / 0000 0000 0000 1000
|
||||
unsigned int noWetLevel : 1; // & 16 / 0x10 / 0000 0000 0001 0000
|
||||
unsigned int unknown : 1; // & 32 / 0x20 / 0000 0000 0010 0000
|
||||
unsigned int unk_is3D : 1; // & 64 / 0x40 / 0000 0000 0100 0000 // CONFIRMED IW4 IW5
|
||||
unsigned int type : 2; // & 384 / 0x180 / 0000 0001 1000 0000 // CONFIRMED IW4 IW5
|
||||
unsigned int channel : 6; // & 32256 / 0x7E00 / 0111 1110 0000 0000 // CONFIRMED IW4 IW5
|
||||
};
|
||||
#pragma warning(pop)
|
||||
unsigned int intValue;
|
||||
};
|
||||
|
||||
struct SndCurve
|
||||
@ -2664,7 +2684,7 @@ namespace Game
|
||||
float distMin;
|
||||
float distMax;
|
||||
float velocityMin;
|
||||
int flags;
|
||||
SoundAliasFlags flags;
|
||||
union
|
||||
{
|
||||
float slavePercentage;
|
||||
@ -2817,7 +2837,7 @@ namespace Game
|
||||
|
||||
struct cLeafBrushNode_s
|
||||
{
|
||||
char axis;
|
||||
unsigned char axis;
|
||||
__int16 leafBrushCount;
|
||||
int contents;
|
||||
cLeafBrushNodeData_t data;
|
||||
@ -2834,9 +2854,9 @@ namespace Game
|
||||
|
||||
struct CollisionPartition
|
||||
{
|
||||
char triCount;
|
||||
char borderCount;
|
||||
char firstVertSegment;
|
||||
unsigned char triCount;
|
||||
unsigned char borderCount;
|
||||
unsigned char firstVertSegment;
|
||||
int firstTri;
|
||||
CollisionBorder* borders;
|
||||
};
|
||||
@ -3173,7 +3193,7 @@ namespace Game
|
||||
{
|
||||
const char* name;
|
||||
int isInUse;
|
||||
int planeCount;
|
||||
unsigned int planeCount;
|
||||
cplane_s* planes;
|
||||
unsigned int numStaticModels;
|
||||
cStaticModel_s* staticModelList;
|
||||
@ -3182,7 +3202,7 @@ namespace Game
|
||||
unsigned int numBrushSides;
|
||||
cbrushside_t* brushsides;
|
||||
unsigned int numBrushEdges;
|
||||
char* brushEdges;
|
||||
unsigned char* brushEdges;
|
||||
unsigned int numNodes;
|
||||
cNode_t* nodes;
|
||||
unsigned int numLeafs;
|
||||
@ -3194,15 +3214,15 @@ namespace Game
|
||||
unsigned int numLeafSurfaces;
|
||||
unsigned int* leafsurfaces;
|
||||
unsigned int vertCount;
|
||||
float(*verts)[3];
|
||||
int triCount;
|
||||
vec3_t* verts;
|
||||
unsigned int triCount;
|
||||
unsigned __int16* triIndices;
|
||||
char* triEdgeIsWalkable;
|
||||
int borderCount;
|
||||
unsigned char* triEdgeIsWalkable;
|
||||
unsigned int borderCount;
|
||||
CollisionBorder* borders;
|
||||
int partitionCount;
|
||||
CollisionPartition* partitions;
|
||||
int aabbTreeCount;
|
||||
unsigned int aabbTreeCount;
|
||||
CollisionAabbTree* aabbTrees;
|
||||
unsigned int numSubModels;
|
||||
cmodel_t* cmodels;
|
||||
@ -3574,9 +3594,9 @@ namespace Game
|
||||
float texCoordOrigin[2];
|
||||
unsigned int supportMask;
|
||||
float areaX2;
|
||||
char defIndex;
|
||||
char vertCount;
|
||||
char fanDataCount;
|
||||
unsigned char defIndex;
|
||||
unsigned char vertCount;
|
||||
unsigned char fanDataCount;
|
||||
char pad[1];
|
||||
};
|
||||
|
||||
|
@ -62,4 +62,12 @@ namespace Utils::Json
|
||||
return input.to_ulong();
|
||||
}
|
||||
|
||||
Game::Bounds ReadBounds(const nlohmann::json& value)
|
||||
{
|
||||
Game::Bounds bounds{};
|
||||
CopyArray(bounds.midPoint, value["midPoint"]);
|
||||
CopyArray(bounds.halfSize, value["halfSize"]);
|
||||
|
||||
return bounds;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <json.hpp>
|
||||
|
||||
namespace Utils::Json
|
||||
{
|
||||
std::string TypeToString(nlohmann::json::value_t type);
|
||||
|
||||
unsigned long ReadFlags(std::string binaryFlags, size_t size);
|
||||
unsigned long ReadFlags(const std::string binaryFlags, size_t size);
|
||||
|
||||
Game::Bounds ReadBounds(const nlohmann::json& value);
|
||||
|
||||
template <typename T> void CopyArray(T* destination, const nlohmann::json& json_member, size_t count = 0)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
count = json_member.size();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
destination[i] = json_member[i].get<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ namespace Utils::Maths
|
||||
return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
|
||||
}
|
||||
|
||||
void VectorSubtract(float va[3], float vb[3], float out[3])
|
||||
void VectorSubtract(const float va[3], const float vb[3], float out[3])
|
||||
{
|
||||
out[0] = va[0] - vb[0];
|
||||
out[1] = va[1] - vb[1];
|
||||
@ -35,7 +35,7 @@ namespace Utils::Maths
|
||||
out[2] = v[2] * scale;
|
||||
}
|
||||
|
||||
float Vec3SqrDistance(float v1[3], float v2[3])
|
||||
float Vec3SqrDistance(const float v1[3], const float v2[3])
|
||||
{
|
||||
float out[3];
|
||||
|
||||
|
@ -7,9 +7,9 @@ namespace Utils::Maths
|
||||
constexpr auto VectorNegate(float x[3]) { x[0] = -x[0]; x[1] = -x[1]; x[2] = -x[2]; }
|
||||
|
||||
float DotProduct(float v1[3], float v2[3]);
|
||||
void VectorSubtract(float va[3], float vb[3], float out[3]);
|
||||
void VectorSubtract(const float va[3], const float vb[3], float out[3]);
|
||||
void VectorAdd(float va[3], float vb[3], float out[3]);
|
||||
void VectorCopy(float in[3], float out[3]);
|
||||
void VectorScale(float v[3], float scale, float out[3]);
|
||||
float Vec3SqrDistance(float v1[3], float v2[3]);
|
||||
float Vec3SqrDistance(const float v1[3], const float v2[3]);
|
||||
}
|
||||
|
@ -31,12 +31,11 @@ namespace Utils
|
||||
|
||||
void* Stream::Reader::read(size_t size, std::size_t count)
|
||||
{
|
||||
size_t bytes = size * count;
|
||||
auto bytes = size * count;
|
||||
|
||||
if ((this->position_ + bytes) <= this->buffer_.size())
|
||||
{
|
||||
void* buffer = this->allocator_->allocate(bytes);
|
||||
|
||||
auto* buffer = this->allocator_->allocate(bytes);
|
||||
std::memcpy(buffer, this->buffer_.data() + this->position_, bytes);
|
||||
this->position_ += bytes;
|
||||
|
||||
@ -84,7 +83,7 @@ namespace Utils
|
||||
|
||||
bool Stream::Reader::hasPointer(void* pointer) const
|
||||
{
|
||||
return this->pointerMap_.find(pointer) != this->pointerMap_.end();
|
||||
return this->pointerMap_.contains(pointer);
|
||||
}
|
||||
|
||||
Stream::Stream() : ptrAssertion(false), criticalSectionState(0)
|
||||
@ -159,7 +158,7 @@ namespace Utils
|
||||
return this->at();
|
||||
}
|
||||
|
||||
auto data = this->data();
|
||||
auto* data = this->data();
|
||||
|
||||
if (this->isCriticalSection() && this->length() + (size * count) > this->capacity())
|
||||
{
|
||||
|
@ -12,6 +12,9 @@
|
||||
|
||||
namespace Utils
|
||||
{
|
||||
constexpr auto POINTER = 255;
|
||||
constexpr auto FOLLOWING = 254;
|
||||
|
||||
class Stream
|
||||
{
|
||||
private:
|
||||
@ -42,9 +45,6 @@ namespace Utils
|
||||
|
||||
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)
|
||||
{
|
||||
@ -65,6 +65,7 @@ namespace Utils
|
||||
auto filePosition = position_;
|
||||
auto data = readArray<T>(count);
|
||||
allocator_->mapPointer(reinterpret_cast<void*>(filePosition), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
default:
|
||||
@ -123,11 +124,13 @@ namespace Utils
|
||||
Stream(size_t size);
|
||||
~Stream();
|
||||
|
||||
std::unordered_map<void*, size_t> dataPointers;
|
||||
|
||||
[[nodiscard]] std::size_t length() const;
|
||||
[[nodiscard]] std::size_t capacity() const;
|
||||
|
||||
char* save(const void * _str, std::size_t size, std::size_t count = 1);
|
||||
char* save(Game::XFILE_BLOCK_TYPES stream, const void * _str, std::size_t size, std::size_t count);
|
||||
char* save(const void * str, std::size_t size, std::size_t count = 1);
|
||||
char* save(Game::XFILE_BLOCK_TYPES stream, const void * str, std::size_t size, std::size_t count);
|
||||
char* save(Game::XFILE_BLOCK_TYPES stream, int value, std::size_t count);
|
||||
|
||||
template <typename T> char* save(T* object)
|
||||
@ -135,6 +138,38 @@ namespace Utils
|
||||
return saveArray<T>(object, 1);
|
||||
}
|
||||
|
||||
template <typename T> char* saveObject(T value)
|
||||
{
|
||||
return saveArray(&value, 1);
|
||||
}
|
||||
|
||||
template <typename T> void saveArrayIfNotExisting(T* data, size_t count)
|
||||
{
|
||||
if (const auto itr = dataPointers.find(data); itr != dataPointers.end())
|
||||
{
|
||||
saveByte(POINTER);
|
||||
saveObject(itr->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
saveByte(FOLLOWING);
|
||||
dataPointers.insert_or_assign(reinterpret_cast<void*>(data), length());
|
||||
saveArray(data, count);
|
||||
}
|
||||
}
|
||||
|
||||
char* save(int value, size_t count = 1)
|
||||
{
|
||||
auto ret = this->length();
|
||||
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
this->save(&value, 4, 1);
|
||||
}
|
||||
|
||||
return this->data() + ret;
|
||||
}
|
||||
|
||||
template <typename T> char* saveArray(T* array, std::size_t count)
|
||||
{
|
||||
return save(array, sizeof(T), count);
|
||||
|
Loading…
Reference in New Issue
Block a user