#include #include "imaterial.hpp" #include "itechniqueset.hpp" #include #include #include #include #include #include #include #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(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::XAssetType::ASSET_TYPE_IMAGE, water->image); } constexpr unsigned long BUFF_SIZE = 0xFFFF; // 65KB // Save_water_t if (water->H0) { auto ptr = reinterpret_cast(water->H0); auto buffer = std::vector(ptr, ptr + water->M * water->N * sizeof(native::complex_s)); auto b64 = local_allocator.allocate_array(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(water->wTerm); auto buffer = std::vector(ptr, ptr + water->M * water->N * sizeof(float)); auto b64 = local_allocator.allocate_array(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::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(); 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() != IW4X_MAT_VERSION) { print_error( "Invalid material json version for {}, expected {} and got {}\n", name, IW4X_MAT_VERSION, materialJson["version"].Get()); return nullptr; } try { asset->info.name = local_allocator.duplicate_string(materialJson["name"].Get()); asset->info.gameFlags = static_cast(utils::json::read_flags(materialJson["gameFlags"].Get(), sizeof(char))); asset->info.sortKey = materialJson["sortKey"].Get(); // * We do techset later * // asset->info.textureAtlasRowCount = materialJson["textureAtlasRowCount"].Get(); asset->info.textureAtlasColumnCount = materialJson["textureAtlasColumnCount"].Get(); asset->info.surfaceTypeBits = static_cast(utils::json::read_flags(materialJson["surfaceTypeBits"].Get(), sizeof(int))); asset->info.hashIndex = materialJson["hashIndex"].Get(); asset->cameraRegion = materialJson["cameraRegion"].Get(); } 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(); asset->info.drawSurf.fields.hasGfxEntIndex = materialJson["gfxDrawSurface"]["hasGfxEntIndex"].Get(); asset->info.drawSurf.fields.materialSortedIndex = materialJson["gfxDrawSurface"]["materialSortedIndex"].Get(); asset->info.drawSurf.fields.objectId = materialJson["gfxDrawSurface"]["objectId"].Get(); asset->info.drawSurf.fields.prepass = materialJson["gfxDrawSurface"]["prepass"].Get(); asset->info.drawSurf.fields.primarySortKey = materialJson["gfxDrawSurface"]["primarySortKey"].Get(); asset->info.drawSurf.fields.reflectionProbeIndex = materialJson["gfxDrawSurface"]["reflectionProbeIndex"].Get(); asset->info.drawSurf.fields.sceneLightIndex = materialJson["gfxDrawSurface"]["sceneLightIndex"].Get(); asset->info.drawSurf.fields.surfType = materialJson["gfxDrawSurface"]["surfType"].Get(); asset->info.drawSurf.fields.unused = materialJson["gfxDrawSurface"]["unused"].Get(); asset->info.drawSurf.fields.useHeroLighting = materialJson["gfxDrawSurface"]["useHeroLighting"].Get(); } asset->stateFlags = static_cast(utils::json::read_flags(materialJson["stateFlags"].Get(), sizeof(char))); if (materialJson.HasMember("textureTable") && materialJson["textureTable"].IsArray()) { const auto& textureTable = materialJson["textureTable"]; asset->textureCount = static_cast(textureTable.Size()); asset->textureTable = local_allocator.allocate_array(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(); textureDef->samplerState = textureJson["samplerState"].Get(); textureDef->nameStart = textureJson["nameStart"].Get(); textureDef->nameEnd = textureJson["nameEnd"].Get(); textureDef->nameHash = textureJson["nameHash"].Get(); if (textureDef->semantic == native::TextureSemantic::TS_WATER_MAP) { native::water_t* water = local_allocator.allocate(); if (textureJson["water"].IsObject()) { auto& waterJson = textureJson["water"]; if (waterJson["image"].IsString()) { auto imageName = waterJson["image"].Get(); water->image = find(native::XAssetType::ASSET_TYPE_IMAGE, imageName.data()); water->image->semantic = native::TextureSemantic::TS_WATER_MAP; } water->amplitude = waterJson["amplitude"].Get(); water->M = waterJson["M"].Get(); water->N = waterJson["N"].Get(); water->Lx = waterJson["Lx"].Get(); water->Lz = waterJson["Lz"].Get(); water->gravity = waterJson["gravity"].Get(); water->windvel = waterJson["windvel"].Get(); 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(); auto predictedSize = static_cast(std::ceilf((h064.size() / 4.f) * 3.f)); assert(predictedSize >= idealSize); auto h0 = reinterpret_cast(local_allocator.allocate(predictedSize)); [[maybe_unused]] auto h0Result = base64_decode(h064.data(), h064.size(), reinterpret_cast(h0), &predictedSize); assert(h0Result == CRYPT_OK); water->H0 = h0; /// WTerm auto wTerm64 = waterJson["wTerm"].Get(); auto predictedWTermSize = static_cast(std::ceilf((wTerm64.size() / 4.f) * 3.f)); auto wTerm = reinterpret_cast(local_allocator.allocate(predictedWTermSize)); [[maybe_unused]] auto wTermResult = base64_decode(wTerm64.data(), wTerm64.size(), reinterpret_cast(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(); textureDef->u.image = find(native::XAssetType::ASSET_TYPE_IMAGE, image_name); textureDef->u.image->semantic = static_cast(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(); } } if (materialJson.HasMember("stateBitsTable") && materialJson["stateBitsTable"].IsArray()) { const auto& array = materialJson["stateBitsTable"]; asset->stateBitsCount = static_cast(array.Size()); asset->stateBitsTable = local_allocator.allocate_array(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() #define READ_BOOL_LB_FROM_JSON(x) bool x = jsonStateBitEntry[#x].Get() 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(); const auto cullFace = jsonStateBitEntry["cullFace"].Get(); 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(constants.Size()); auto table = local_allocator.allocate_array(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::copy(constantName.begin(), constantName.end(), entry->name); entry->nameHash = constant["nameHash"].Get(); } asset->constantTable = table; } if (materialJson["techniqueSet"].IsString()) { const std::string techsetName = materialJson["techniqueSet"].Get(); asset->techniqueSet = find(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 imaterial::get_child_assets(const native::XAssetHeader& header) const { auto result = std::vector(); 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& 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