756 lines
34 KiB
C++
756 lines
34 KiB
C++
#include <std_include.hpp>
|
|
|
|
#include "imaterial.hpp"
|
|
|
|
#include "itechniqueset.hpp"
|
|
|
|
#include <utils/io.hpp>
|
|
#include <utils/stream.hpp>
|
|
#include <utils/string.hpp>
|
|
#include <utils/json.hpp>
|
|
|
|
#include <rapidjson/document.h>
|
|
#include <rapidjson/prettywriter.h>
|
|
#include <rapidjson/stringbuffer.h>
|
|
|
|
#define IW4X_MAT_VERSION 1
|
|
|
|
namespace iw4of::interfaces
|
|
{
|
|
bool imaterial::write_internal(const native::XAssetHeader& header) const
|
|
{
|
|
auto asset = header.material;
|
|
|
|
utils::memory::allocator str_duplicator;
|
|
rapidjson::Document output(rapidjson::kObjectType);
|
|
auto& allocator = output.GetAllocator();
|
|
|
|
output.AddMember("version", IW4X_MAT_VERSION, allocator);
|
|
|
|
output.AddMember("name", RAPIDJSON_STR(asset->info.name), allocator);
|
|
|
|
const auto gameFlagsStr = std::format("{:08b}", asset->info.gameFlags);
|
|
output.AddMember("gameFlags", RAPIDJSON_STR(gameFlagsStr.c_str()), allocator);
|
|
|
|
const auto stateFlags = std::format("{:08b}", static_cast<char>(asset->stateFlags));
|
|
output.AddMember("stateFlags", RAPIDJSON_STR(stateFlags.c_str()), allocator);
|
|
|
|
#define SAME_NAME_JSON_MEMBER(x) output.AddMember(#x, asset->info.x, allocator)
|
|
|
|
SAME_NAME_JSON_MEMBER(sortKey);
|
|
|
|
std::string techsetName;
|
|
if (asset->techniqueSet)
|
|
{
|
|
output.AddMember("techniqueSet", RAPIDJSON_STR(asset->techniqueSet->name), allocator);
|
|
assets->write(native::ASSET_TYPE_TECHNIQUE_SET, asset->techniqueSet);
|
|
}
|
|
|
|
SAME_NAME_JSON_MEMBER(textureAtlasRowCount);
|
|
SAME_NAME_JSON_MEMBER(textureAtlasColumnCount);
|
|
|
|
const auto surfaceTypeBits = std::format("{:032b}", asset->info.surfaceTypeBits);
|
|
output.AddMember("surfaceTypeBits", RAPIDJSON_STR(surfaceTypeBits.c_str()), allocator);
|
|
|
|
rapidjson::Value textureTable(rapidjson::kArrayType);
|
|
|
|
if (asset->textureTable)
|
|
{
|
|
for (char i = 0; i < asset->textureCount; ++i)
|
|
{
|
|
auto textureDef = &asset->textureTable[i];
|
|
rapidjson::Value textureJson(rapidjson::kObjectType);
|
|
|
|
textureJson.AddMember("nameStart", textureDef->nameStart, allocator);
|
|
textureJson.AddMember("nameEnd", textureDef->nameEnd, allocator);
|
|
textureJson.AddMember("nameHash", textureDef->nameHash, allocator);
|
|
textureJson.AddMember("samplerState", textureDef->samplerState,
|
|
allocator); // $6961E030A9677F7C86FC6FF9B5901495
|
|
textureJson.AddMember("semantic", textureDef->semantic, allocator);
|
|
|
|
if (textureDef->semantic == native::TS_WATER_MAP)
|
|
{
|
|
auto water = textureDef->u.water;
|
|
|
|
if (water)
|
|
{
|
|
rapidjson::Value waterJson(rapidjson::kObjectType);
|
|
|
|
if (water->image)
|
|
{
|
|
waterJson.AddMember("image", RAPIDJSON_STR(water->image->name), allocator);
|
|
assets->write<native::GfxImage>(native::XAssetType::ASSET_TYPE_IMAGE, water->image);
|
|
}
|
|
|
|
constexpr unsigned long BUFF_SIZE = 0xFFFF; // 65KB
|
|
|
|
// Save_water_t
|
|
if (water->H0)
|
|
{
|
|
auto ptr = reinterpret_cast<uint8_t*>(water->H0);
|
|
auto buffer = std::vector<uint8_t>(ptr, ptr + water->M * water->N * sizeof(native::complex_s));
|
|
|
|
auto b64 = local_allocator.allocate_array<char>(BUFF_SIZE);
|
|
unsigned long buffLength = BUFF_SIZE;
|
|
|
|
[[maybe_unused]] auto result = base64_encode(&buffer.front(), buffer.size(), b64, &buffLength);
|
|
assert(result == CRYPT_OK);
|
|
|
|
waterJson.AddMember("H0", RAPIDJSON_STR(str_duplicator.duplicate_string(b64)), allocator);
|
|
}
|
|
|
|
if (water->wTerm)
|
|
{
|
|
auto ptr = reinterpret_cast<uint8_t*>(water->wTerm);
|
|
auto buffer = std::vector<uint8_t>(ptr, ptr + water->M * water->N * sizeof(float));
|
|
|
|
auto b64 = local_allocator.allocate_array<char>(BUFF_SIZE);
|
|
unsigned long buffLength = BUFF_SIZE;
|
|
|
|
[[maybe_unused]] auto result = base64_encode(&buffer.front(), buffer.size(), b64, &buffLength);
|
|
assert(result == CRYPT_OK);
|
|
|
|
waterJson.AddMember("wTerm", RAPIDJSON_STR(str_duplicator.duplicate_string(b64)), allocator);
|
|
}
|
|
|
|
#define SAME_NAME_WATER_MEMBER(x) waterJson.AddMember(#x, water->x, allocator)
|
|
|
|
SAME_NAME_WATER_MEMBER(M);
|
|
SAME_NAME_WATER_MEMBER(N);
|
|
SAME_NAME_WATER_MEMBER(Lx);
|
|
SAME_NAME_WATER_MEMBER(Lz);
|
|
SAME_NAME_WATER_MEMBER(gravity);
|
|
SAME_NAME_WATER_MEMBER(windvel);
|
|
waterJson.AddMember("winddir", utils::json::make_json_array(water->winddir, 2, allocator), allocator);
|
|
|
|
SAME_NAME_WATER_MEMBER(amplitude);
|
|
waterJson.AddMember("codeConstant", utils::json::make_json_array(water->codeConstant, 4, allocator), allocator);
|
|
|
|
textureJson.AddMember("water", waterJson, allocator);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (textureDef->u.image)
|
|
{
|
|
textureJson.AddMember("image", RAPIDJSON_STR(textureDef->u.image->name), allocator);
|
|
assets->write<native::GfxImage>(native::XAssetType::ASSET_TYPE_IMAGE, textureDef->u.image);
|
|
}
|
|
else
|
|
{
|
|
// This can't happen! It will crash the game
|
|
assert(false);
|
|
print_error("Null/missing image for material {}", header.material->info.name);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
textureTable.PushBack(textureJson, allocator);
|
|
}
|
|
}
|
|
|
|
output.AddMember("textureTable", textureTable, allocator);
|
|
rapidjson::Value gfxDrawSurface(rapidjson::kObjectType);
|
|
|
|
#define SAME_NAME_GFXDRAWSURF_MEMBER(x) gfxDrawSurface.AddMember(#x, asset->info.drawSurf.fields.##x##, allocator)
|
|
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(objectId);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(reflectionProbeIndex);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(hasGfxEntIndex);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(customIndex);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(materialSortedIndex);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(prepass);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(useHeroLighting);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(sceneLightIndex);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(surfType);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(primarySortKey);
|
|
SAME_NAME_GFXDRAWSURF_MEMBER(unused);
|
|
|
|
output.AddMember("gfxDrawSurface", gfxDrawSurface, allocator);
|
|
|
|
output.AddMember("hashIndex", 0, allocator);
|
|
|
|
rapidjson::Value stateBitsEntry(rapidjson::kArrayType);
|
|
for (size_t i = 0; i < 48; i++)
|
|
{
|
|
stateBitsEntry.PushBack(asset->stateBitsEntry[i], allocator);
|
|
}
|
|
|
|
output.AddMember("stateBitsEntry", stateBitsEntry, allocator);
|
|
output.AddMember("cameraRegion", asset->cameraRegion, allocator);
|
|
|
|
if (asset->constantTable)
|
|
{
|
|
rapidjson::Value constantTable(rapidjson::kArrayType);
|
|
|
|
for (char i = 0; i < asset->constantCount; ++i)
|
|
{
|
|
native::MaterialConstantDef constantDef;
|
|
std::memcpy(&constantDef, &asset->constantTable[i], sizeof native::MaterialConstantDef);
|
|
|
|
rapidjson::Value constantDefJson(rapidjson::kObjectType);
|
|
|
|
constantDefJson.AddMember("nameHash", constantDef.nameHash, allocator);
|
|
constantDefJson.AddMember("literal", utils::json::make_json_array(constantDef.literal, 4, allocator), allocator);
|
|
|
|
std::string constantDefName = constantDef.name;
|
|
constantDefName = constantDefName.substr(0, 12);
|
|
|
|
constantDefJson.AddMember("name", RAPIDJSON_STR(str_duplicator.duplicate_string(constantDefName.c_str())), allocator);
|
|
|
|
constantTable.PushBack(constantDefJson, allocator);
|
|
}
|
|
|
|
output.AddMember("constantTable", constantTable, allocator);
|
|
}
|
|
|
|
if (asset->stateBitsTable)
|
|
{
|
|
output.AddMember("stateBitsTable", statebits_to_json_array(asset->stateBitsTable, asset->stateBitsCount, allocator), allocator);
|
|
}
|
|
|
|
// Write to disk
|
|
rapidjson::StringBuffer buff;
|
|
rapidjson::PrettyWriter<
|
|
/*typename OutputStream */ rapidjson::StringBuffer,
|
|
/*typename SourceEncoding*/ rapidjson::UTF8<>,
|
|
/*typename TargetEncoding*/ rapidjson::UTF8<>,
|
|
/*typename StackAllocator*/ rapidjson::CrtAllocator,
|
|
/*unsigned writeFlags*/ rapidjson::kWriteNanAndInfNullFlag | rapidjson::kWriteNanAndInfFlag>
|
|
writer(buff);
|
|
writer.SetFormatOptions(rapidjson::PrettyFormatOptions::kFormatSingleLineArray);
|
|
|
|
output.Accept(writer);
|
|
|
|
utils::io::write_file(get_work_path(header).string(), buff.GetString());
|
|
return true;
|
|
}
|
|
|
|
void* interfaces::imaterial::read_internal(const std::string& name) const
|
|
{
|
|
auto path = get_work_path(name).string();
|
|
|
|
if (!utils::io::file_exists(path)) return nullptr;
|
|
|
|
native::Material* asset = local_allocator.allocate<native::Material>();
|
|
|
|
rapidjson::Document materialJson;
|
|
try
|
|
{
|
|
auto contents = utils::io::read_file(path);
|
|
materialJson.Parse(contents.data());
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
print_error("Invalid material json for {} (broken json {})\n", name, e.what());
|
|
}
|
|
|
|
if (!materialJson.IsObject())
|
|
{
|
|
print_error("Invalid material json for {} (Is it zonebuilder format?)\n", name);
|
|
return nullptr;
|
|
}
|
|
|
|
if (materialJson["version"].Get<int32_t>() != IW4X_MAT_VERSION)
|
|
{
|
|
print_error(
|
|
"Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_VERSION, materialJson["version"].Get<std::string>());
|
|
return nullptr;
|
|
}
|
|
|
|
try
|
|
{
|
|
asset->info.name = local_allocator.duplicate_string(materialJson["name"].Get<std::string>());
|
|
asset->info.gameFlags = static_cast<char>(utils::json::read_flags(materialJson["gameFlags"].Get<std::string>(), sizeof(char)));
|
|
|
|
asset->info.sortKey = materialJson["sortKey"].Get<char>();
|
|
// * We do techset later * //
|
|
asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].Get<uint8_t>();
|
|
asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].Get<uint8_t>();
|
|
asset->info.surfaceTypeBits =
|
|
static_cast<uint32_t>(utils::json::read_flags(materialJson["surfaceTypeBits"].Get<std::string>(), sizeof(int)));
|
|
asset->info.hashIndex = materialJson["hashIndex"].Get<uint16_t>();
|
|
asset->cameraRegion = materialJson["cameraRegion"].Get<char>();
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
print_error("Invalid material json for {} (broken json {})\n", name, e.what());
|
|
return nullptr;
|
|
}
|
|
|
|
if (materialJson.HasMember("gfxDrawSurface") && materialJson["gfxDrawSurface"].IsObject())
|
|
{
|
|
asset->info.drawSurf.fields.customIndex = materialJson["gfxDrawSurface"]["customIndex"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].Get<int64_t>();
|
|
asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].Get<int64_t>();
|
|
}
|
|
|
|
asset->stateFlags = static_cast<char>(utils::json::read_flags(materialJson["stateFlags"].Get<std::string>(), sizeof(char)));
|
|
|
|
if (materialJson.HasMember("textureTable") && materialJson["textureTable"].IsArray())
|
|
{
|
|
const auto& textureTable = materialJson["textureTable"];
|
|
asset->textureCount = static_cast<uint8_t>(textureTable.Size());
|
|
asset->textureTable = local_allocator.allocate_array<native::MaterialTextureDef>(asset->textureCount);
|
|
|
|
for (size_t i = 0; i < textureTable.Size(); i++)
|
|
{
|
|
auto& textureJson = textureTable[i];
|
|
if (textureJson.IsObject())
|
|
{
|
|
native::MaterialTextureDef* textureDef = &asset->textureTable[i];
|
|
textureDef->semantic = textureJson["semantic"].Get<uint8_t>();
|
|
textureDef->samplerState = textureJson["samplerState"].Get<char>();
|
|
textureDef->nameStart = textureJson["nameStart"].Get<char>();
|
|
textureDef->nameEnd = textureJson["nameEnd"].Get<char>();
|
|
textureDef->nameHash = textureJson["nameHash"].Get<uint32_t>();
|
|
|
|
if (textureDef->semantic == native::TextureSemantic::TS_WATER_MAP)
|
|
{
|
|
native::water_t* water = local_allocator.allocate<native::water_t>();
|
|
|
|
if (textureJson["water"].IsObject())
|
|
{
|
|
auto& waterJson = textureJson["water"];
|
|
|
|
if (waterJson["image"].IsString())
|
|
{
|
|
auto imageName = waterJson["image"].Get<std::string>();
|
|
|
|
water->image = find<native::GfxImage>(native::XAssetType::ASSET_TYPE_IMAGE, imageName.data());
|
|
water->image->semantic = native::TextureSemantic::TS_WATER_MAP;
|
|
}
|
|
|
|
water->amplitude = waterJson["amplitude"].Get<float>();
|
|
water->M = waterJson["M"].Get<int32_t>();
|
|
water->N = waterJson["N"].Get<int32_t>();
|
|
water->Lx = waterJson["Lx"].Get<float>();
|
|
water->Lz = waterJson["Lz"].Get<float>();
|
|
water->gravity = waterJson["gravity"].Get<float>();
|
|
water->windvel = waterJson["windvel"].Get<float>();
|
|
|
|
utils::json::copy_array(water->winddir, waterJson["winddir"], 2);
|
|
utils::json::copy_array(water->codeConstant, waterJson["codeConstant"], 4);
|
|
|
|
/// H0
|
|
[[maybe_unused]] auto idealSize = water->M * water->N * sizeof(native::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<native::complex_s*>(local_allocator.allocate(predictedSize));
|
|
|
|
[[maybe_unused]] auto h0Result = base64_decode(h064.data(), h064.size(), reinterpret_cast<uint8_t*>(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*>(local_allocator.allocate(predictedWTermSize));
|
|
|
|
[[maybe_unused]] auto wTermResult =
|
|
base64_decode(wTerm64.data(), wTerm64.size(), reinterpret_cast<uint8_t*>(wTerm), &predictedWTermSize);
|
|
|
|
assert(wTermResult == CRYPT_OK);
|
|
water->wTerm = wTerm;
|
|
}
|
|
|
|
textureDef->u.water = water;
|
|
}
|
|
else
|
|
{
|
|
textureDef->u.image = nullptr;
|
|
if (textureJson["image"].IsString())
|
|
{
|
|
const auto& image_name = textureJson["image"].Get<std::string>();
|
|
textureDef->u.image = find<native::GfxImage>(native::XAssetType::ASSET_TYPE_IMAGE, image_name);
|
|
textureDef->u.image->semantic = static_cast<native::TextureSemantic>(textureDef->semantic);
|
|
|
|
assert(textureDef->u.image);
|
|
assert(textureDef->u.image->semantic >= native::TextureSemantic::TS_2D);
|
|
assert(textureDef->u.image->semantic <= native::TextureSemantic::TS_DISPLACEMENT_MAP);
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Statebits
|
|
if (materialJson.HasMember("stateBitsEntry") && materialJson["stateBitsEntry"].IsArray())
|
|
{
|
|
const auto& stateBitsEntry = materialJson["stateBitsEntry"];
|
|
|
|
for (size_t i = 0; i < std::min(stateBitsEntry.Size(), 48u); i++)
|
|
{
|
|
asset->stateBitsEntry[i] = stateBitsEntry[i].Get<char>();
|
|
}
|
|
}
|
|
|
|
if (materialJson.HasMember("stateBitsTable") && materialJson["stateBitsTable"].IsArray())
|
|
{
|
|
const auto& array = materialJson["stateBitsTable"];
|
|
asset->stateBitsCount = static_cast<uint8_t>(array.Size());
|
|
|
|
asset->stateBitsTable = local_allocator.allocate_array<native::GfxStateBits>(array.Size());
|
|
|
|
size_t statebitTableIndex = 0;
|
|
for (size_t i = 0; i < array.Size(); i++)
|
|
{
|
|
const auto& jsonStateBitEntry = array[i];
|
|
auto stateBit = &asset->stateBitsTable[statebitTableIndex++];
|
|
|
|
uint32_t loadbits0 = 0;
|
|
uint32_t loadbits1 = 0;
|
|
|
|
#define READ_INT_LB_FROM_JSON(x) uint32_t x = jsonStateBitEntry[#x].Get<uint32_t>()
|
|
#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 << native::GFXS0_SRCBLEND_RGB_SHIFT;
|
|
loadbits0 |= dstBlendRgb << native::GFXS0_DSTBLEND_RGB_SHIFT;
|
|
loadbits0 |= blendOpRgb << native::GFXS0_BLENDOP_RGB_SHIFT;
|
|
loadbits0 |= srcBlendAlpha << native::GFXS0_SRCBLEND_ALPHA_SHIFT;
|
|
loadbits0 |= dstBlendAlpha << native::GFXS0_DSTBLEND_ALPHA_SHIFT;
|
|
loadbits0 |= blendOpAlpha << native::GFXS0_BLENDOP_ALPHA_SHIFT;
|
|
|
|
if (depthTest == -1)
|
|
{
|
|
loadbits1 |= native::GFXS1_DEPTHTEST_DISABLE;
|
|
}
|
|
else
|
|
{
|
|
loadbits1 |= depthTest << native::GFXS1_DEPTHTEST_SHIFT;
|
|
}
|
|
|
|
loadbits1 |= polygonOffset << native::GFXS1_POLYGON_OFFSET_SHIFT;
|
|
|
|
if (alphaTest == "disable")
|
|
{
|
|
loadbits0 |= native::GFXS0_ATEST_DISABLE;
|
|
}
|
|
else if (alphaTest == ">0")
|
|
{
|
|
loadbits0 |= native::GFXS0_ATEST_GT_0;
|
|
}
|
|
else if (alphaTest == "<128")
|
|
{
|
|
loadbits0 |= native::GFXS0_ATEST_LT_128;
|
|
}
|
|
else if (alphaTest == ">=128")
|
|
{
|
|
loadbits0 |= native::GFXS0_ATEST_GE_128;
|
|
}
|
|
else
|
|
{
|
|
print_error("Invalid alphatest loadbit0 '{}' in material {}\n", alphaTest, name);
|
|
return nullptr;
|
|
}
|
|
|
|
if (cullFace == "none")
|
|
{
|
|
loadbits0 |= native::GFXS0_CULL_NONE;
|
|
}
|
|
else if (cullFace == "back")
|
|
{
|
|
loadbits0 |= native::GFXS0_CULL_BACK;
|
|
}
|
|
else if (cullFace == "front")
|
|
{
|
|
loadbits0 |= native::GFXS0_CULL_FRONT;
|
|
}
|
|
else
|
|
{
|
|
print_error("Invalid cullFace loadbit0 '{}' in material {}\n", cullFace, name);
|
|
return nullptr;
|
|
}
|
|
|
|
if (gammaWrite)
|
|
{
|
|
loadbits0 |= native::GFXS0_GAMMAWRITE;
|
|
}
|
|
|
|
if (colorWriteAlpha)
|
|
{
|
|
loadbits0 |= native::GFXS0_COLORWRITE_ALPHA;
|
|
}
|
|
|
|
if (colorWriteRgb)
|
|
{
|
|
loadbits0 |= native::GFXS0_COLORWRITE_RGB;
|
|
}
|
|
|
|
if (polymodeLine)
|
|
{
|
|
loadbits0 |= native::GFXS0_POLYMODE_LINE;
|
|
}
|
|
|
|
if (depthWrite)
|
|
{
|
|
loadbits1 |= native::GFXS1_DEPTHWRITE;
|
|
}
|
|
if (stencilFrontEnabled)
|
|
{
|
|
loadbits1 |= native::GFXS1_STENCIL_FRONT_ENABLE;
|
|
}
|
|
if (stencilBackEnabled)
|
|
{
|
|
loadbits1 |= native::GFXS1_STENCIL_BACK_ENABLE;
|
|
}
|
|
|
|
loadbits1 |= stencilFrontPass << native::GFXS1_STENCIL_FRONT_PASS_SHIFT;
|
|
loadbits1 |= stencilFrontFail << native::GFXS1_STENCIL_FRONT_FAIL_SHIFT;
|
|
loadbits1 |= stencilFrontZFail << native::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT;
|
|
loadbits1 |= stencilFrontFunc << native::GFXS1_STENCIL_FRONT_FUNC_SHIFT;
|
|
loadbits1 |= stencilBackPass << native::GFXS1_STENCIL_BACK_PASS_SHIFT;
|
|
loadbits1 |= stencilBackFail << native::GFXS1_STENCIL_BACK_FAIL_SHIFT;
|
|
loadbits1 |= stencilBackZFail << native::GFXS1_STENCIL_BACK_ZFAIL_SHIFT;
|
|
loadbits1 |= stencilBackFunc << native::GFXS1_STENCIL_BACK_FUNC_SHIFT;
|
|
|
|
stateBit->loadBits[0] = loadbits0;
|
|
stateBit->loadBits[1] = loadbits1;
|
|
}
|
|
|
|
// Constant table
|
|
if (materialJson.HasMember("constantTable") && materialJson["constantTable"].IsArray())
|
|
{
|
|
const auto& constants = materialJson["constantTable"];
|
|
asset->constantCount = static_cast<char>(constants.Size());
|
|
auto table = local_allocator.allocate_array<native::MaterialConstantDef>(asset->constantCount);
|
|
|
|
for (size_t constantIndex = 0; constantIndex < asset->constantCount; constantIndex++)
|
|
{
|
|
auto& constant = constants[constantIndex];
|
|
auto entry = &table[constantIndex];
|
|
|
|
utils::json::copy_array(entry->literal, constant["literal"], 4);
|
|
|
|
auto constantName = constant["name"].Get<std::string>();
|
|
std::copy(constantName.begin(), constantName.end(), entry->name);
|
|
|
|
entry->nameHash = constant["nameHash"].Get<uint32_t>();
|
|
}
|
|
|
|
asset->constantTable = table;
|
|
}
|
|
|
|
if (materialJson["techniqueSet"].IsString())
|
|
{
|
|
const std::string techsetName = materialJson["techniqueSet"].Get<std::string>();
|
|
asset->techniqueSet = find<native::MaterialTechniqueSet>(native::ASSET_TYPE_TECHNIQUE_SET, techsetName);
|
|
|
|
if (asset->techniqueSet == nullptr)
|
|
{
|
|
assert(false);
|
|
print_error("Could not find technique set {} for material {}", techsetName, name);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return asset;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
std::vector<native::XAsset> imaterial::get_child_assets(const native::XAssetHeader& header) const
|
|
{
|
|
auto result = std::vector<native::XAsset>();
|
|
|
|
auto asset = header.material;
|
|
|
|
if (asset->techniqueSet)
|
|
{
|
|
result.push_back({native::ASSET_TYPE_TECHNIQUE_SET, asset->techniqueSet});
|
|
}
|
|
|
|
if (asset->textureTable)
|
|
{
|
|
for (char i = 0; i < asset->textureCount; ++i)
|
|
{
|
|
auto textureDef = &asset->textureTable[i];
|
|
|
|
if (textureDef->semantic == native::TS_WATER_MAP)
|
|
{
|
|
auto water = textureDef->u.water;
|
|
|
|
if (water)
|
|
{
|
|
if (water->image)
|
|
{
|
|
result.push_back({native::XAssetType::ASSET_TYPE_IMAGE, water->image});
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (textureDef->u.image)
|
|
{
|
|
result.push_back({native::XAssetType::ASSET_TYPE_IMAGE, textureDef->u.image});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
std::filesystem::path interfaces::imaterial::get_file_name(const std::string& basename) const
|
|
{
|
|
return std::format("{}.iw4x.json", basename);
|
|
}
|
|
|
|
rapidjson::Value imaterial::statebits_to_json_array(native::GfxStateBits* stateBits, uint8_t count,
|
|
rapidjson::MemoryPoolAllocator<rapidjson::CrtAllocator>& allocator) const
|
|
{
|
|
rapidjson::Value arr(rapidjson::kArrayType);
|
|
|
|
for (auto index = 0u; index < count; index++)
|
|
{
|
|
const auto& entry = stateBits[index];
|
|
|
|
const auto srcBlendRgb = (entry.flags.loadbit0 & native::GFXS0_SRCBLEND_RGB_MASK) >> native::GFXS0_SRCBLEND_RGB_SHIFT;
|
|
const auto dstBlendRgb = (entry.flags.loadbit0 & native::GFXS0_DSTBLEND_RGB_MASK) >> native::GFXS0_DSTBLEND_RGB_SHIFT;
|
|
const auto blendOpRgb = (entry.flags.loadbit0 & native::GFXS0_BLENDOP_RGB_MASK) >> native::GFXS0_BLENDOP_RGB_SHIFT;
|
|
const auto srcBlendAlpha = (entry.flags.loadbit0 & native::GFXS0_SRCBLEND_ALPHA_MASK) >> native::GFXS0_SRCBLEND_ALPHA_SHIFT;
|
|
const auto dstBlendAlpha = (entry.flags.loadbit0 & native::GFXS0_DSTBLEND_ALPHA_MASK) >> native::GFXS0_DSTBLEND_ALPHA_SHIFT;
|
|
const auto blendOpAlpha = (entry.flags.loadbit0 & native::GFXS0_BLENDOP_ALPHA_MASK) >> native::GFXS0_BLENDOP_ALPHA_SHIFT;
|
|
const auto depthTest = (entry.flags.loadbit1 & native::GFXS1_DEPTHTEST_DISABLE)
|
|
? -1
|
|
: (entry.flags.loadbit1 & native::GFXS1_DEPTHTEST_MASK) >> native::GFXS1_DEPTHTEST_SHIFT;
|
|
const auto polygonOffset = (entry.flags.loadbit1 & native::GFXS1_POLYGON_OFFSET_MASK) >> native::GFXS1_POLYGON_OFFSET_SHIFT;
|
|
|
|
const auto* alphaTest = "disable";
|
|
if ((entry.flags.loadbit0 & native::GFXS0_ATEST_MASK) == native::GFXS0_ATEST_GE_128)
|
|
{
|
|
alphaTest = ">=128";
|
|
}
|
|
else if ((entry.flags.loadbit0 & native::GFXS0_ATEST_MASK) == native::GFXS0_ATEST_GT_0)
|
|
{
|
|
alphaTest = ">0";
|
|
}
|
|
else if ((entry.flags.loadbit0 & native::GFXS0_ATEST_MASK) == native::GFXS0_ATEST_LT_128)
|
|
{
|
|
alphaTest = "<128";
|
|
}
|
|
else
|
|
{
|
|
assert(entry.flags.loadbit0 & native::GFXS0_ATEST_DISABLE);
|
|
}
|
|
|
|
const auto* cullFace = "none";
|
|
if ((entry.flags.loadbit0 & native::GFXS0_CULL_MASK) == native::GFXS0_CULL_BACK)
|
|
{
|
|
cullFace = "back";
|
|
}
|
|
else if ((entry.flags.loadbit0 & native::GFXS0_CULL_MASK) == native::GFXS0_CULL_FRONT)
|
|
{
|
|
cullFace = "front";
|
|
}
|
|
else
|
|
{
|
|
assert((entry.flags.loadbit0 & native::GFXS0_CULL_MASK) == native::GFXS0_CULL_NONE);
|
|
}
|
|
|
|
rapidjson::Value stateBitEntry(rapidjson::kObjectType);
|
|
|
|
const auto colorWriteRgb = entry.flags.loadbit0 & native::GFXS0_COLORWRITE_RGB ? true : false;
|
|
const auto colorWriteAlpha = entry.flags.loadbit0 & native::GFXS0_COLORWRITE_ALPHA ? true : false;
|
|
const auto polymodeLine = entry.flags.loadbit0 & native::GFXS0_POLYMODE_LINE ? true : false;
|
|
const auto gammaWrite = entry.flags.loadbit0 & native::GFXS0_GAMMAWRITE ? true : false;
|
|
const auto depthWrite = (entry.flags.loadbit1 & native::GFXS1_DEPTHWRITE) ? true : false;
|
|
const auto stencilFrontEnabled = (entry.flags.loadbit1 & native::GFXS1_STENCIL_FRONT_ENABLE) ? true : false;
|
|
const auto stencilBackEnabled = (entry.flags.loadbit1 & native::GFXS1_STENCIL_BACK_ENABLE) ? true : false;
|
|
const auto stencilFrontPass = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_FRONT_PASS_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilFrontFail = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_FRONT_FAIL_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilFrontZFail = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_FRONT_ZFAIL_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilFrontFunc = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_FRONT_FUNC_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilBackPass = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_BACK_PASS_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilBackFail = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_BACK_FAIL_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilBackZFail = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_BACK_ZFAIL_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
const auto stencilBackFunc = (entry.flags.loadbit1 >> native::GFXS1_STENCIL_BACK_FUNC_SHIFT) & native::GFXS_STENCILOP_MASK;
|
|
|
|
#define ADD_TO_JSON(x) stateBitEntry.AddMember(#x, x, allocator)
|
|
#define ADD_TO_JSON_STR(x) stateBitEntry.AddMember(#x, RAPIDJSON_STR(x), allocator)
|
|
|
|
ADD_TO_JSON_STR(alphaTest);
|
|
ADD_TO_JSON(blendOpAlpha);
|
|
ADD_TO_JSON(blendOpRgb);
|
|
ADD_TO_JSON(colorWriteAlpha);
|
|
ADD_TO_JSON(colorWriteRgb);
|
|
ADD_TO_JSON_STR(cullFace);
|
|
ADD_TO_JSON(depthTest);
|
|
ADD_TO_JSON(depthWrite);
|
|
ADD_TO_JSON(dstBlendAlpha);
|
|
ADD_TO_JSON(dstBlendRgb);
|
|
ADD_TO_JSON(gammaWrite);
|
|
ADD_TO_JSON(polygonOffset);
|
|
ADD_TO_JSON(polymodeLine);
|
|
ADD_TO_JSON(srcBlendRgb);
|
|
ADD_TO_JSON(srcBlendAlpha);
|
|
ADD_TO_JSON(stencilBackEnabled);
|
|
ADD_TO_JSON(stencilBackFail);
|
|
ADD_TO_JSON(stencilBackFunc);
|
|
ADD_TO_JSON(stencilBackPass);
|
|
ADD_TO_JSON(stencilBackZFail);
|
|
ADD_TO_JSON(stencilFrontEnabled);
|
|
ADD_TO_JSON(stencilFrontFail);
|
|
ADD_TO_JSON(stencilFrontFunc);
|
|
ADD_TO_JSON(stencilFrontPass);
|
|
ADD_TO_JSON(stencilFrontZFail);
|
|
|
|
#if DEBUG
|
|
stateBitEntry.AddMember("check0", entry.flags.loadbit0, allocator);
|
|
stateBitEntry.AddMember("check1", entry.flags.loadbit1, allocator);
|
|
#endif
|
|
|
|
arr.PushBack(stateBitEntry, allocator);
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
} // namespace iw4of::interfaces
|