From 41bad6c97c60918af640e8d4c558316edd1bd9c7 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Sat, 1 Jun 2024 21:32:27 +1000 Subject: [PATCH 1/4] fix MaterialDatabase::radiantLight null deref when material/matmod ID does not exist --- source/game/StarLiquidsDatabase.hpp | 12 +++++------- source/game/StarMaterialDatabase.hpp | 8 ++++---- source/game/StarWorldClient.cpp | 6 ++---- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/source/game/StarLiquidsDatabase.hpp b/source/game/StarLiquidsDatabase.hpp index 707be70..fa319c0 100644 --- a/source/game/StarLiquidsDatabase.hpp +++ b/source/game/StarLiquidsDatabase.hpp @@ -89,14 +89,12 @@ inline LiquidSettingsConstPtr LiquidsDatabase::liquidSettings(LiquidId liquidId) } inline Vec3F LiquidsDatabase::radiantLight(LiquidLevel level) const { - if (level.liquid >= m_settings.size()) - return Vec3F(); + if (level.liquid < m_settings.size()) { + if (auto const& settings = m_settings[level.liquid]) + return settings->radiantLightLevel * level.level; + } - auto const& settings = m_settings[level.liquid]; - if (!settings) - return Vec3F(); - - return settings->radiantLightLevel * level.level; + return Vec3F(); } } diff --git a/source/game/StarMaterialDatabase.hpp b/source/game/StarMaterialDatabase.hpp index 8e4f350..b890e30 100644 --- a/source/game/StarMaterialDatabase.hpp +++ b/source/game/StarMaterialDatabase.hpp @@ -239,13 +239,13 @@ inline Vec3F MaterialDatabase::radiantLight(MaterialId materialId, ModId modId) Vec3F radiantLight; if (materialId < m_materials.size()) { auto const& mat = m_materials[materialId]; - if (mat->materialRenderProfile) + if (mat && mat->materialRenderProfile) radiantLight += mat->materialRenderProfile->radiantLight; } if (modId < m_mods.size()) { - auto const& mat = m_mods[modId]; - if (mat->modRenderProfile) - radiantLight += mat->modRenderProfile->radiantLight; + auto const& mod = m_mods[modId]; + if (mod && mod->modRenderProfile) + radiantLight += mod->modRenderProfile->radiantLight; } return radiantLight; } diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp index 91f1d39..ef5daa2 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -1631,6 +1631,7 @@ RpcPromise WorldClient::interact(InteractRequest const& request) } void WorldClient::lightingTileGather() { + int64_t start = Time::monotonicMicroseconds(); Vec3F environmentLight = m_sky->environmentLight().toRgbF(); float undergroundLevel = m_worldTemplate->undergroundLevel(); auto liquidsDatabase = Root::singleton().liquidsDatabase(); @@ -1638,7 +1639,6 @@ void WorldClient::lightingTileGather() { // Each column in tileEvalColumns is guaranteed to be no larger than the sector size. - size_t lights = 0; m_tileArray->tileEvalColumns(m_lightingCalculator.calculationRegion(), [&](Vec2I const& pos, ClientTile const* column, size_t ySize) { size_t baseIndex = m_lightingCalculator.baseIndexFor(pos); for (size_t y = 0; y < ySize; ++y) { @@ -1655,12 +1655,10 @@ void WorldClient::lightingTileGather() { if (tile.backgroundLightTransparent && pos[1] + y > undergroundLevel) light += environmentLight; } - if (light.max() > 0.0f) - ++lights; m_lightingCalculator.setCellIndex(baseIndex + y, light, !tile.foregroundLightTransparent); } }); - LogMap::set("client_render_world_async_light_tiles", toString(lights)); + LogMap::set("client_render_world_async_light_gather", strf(u8"{:05d}\u00b5s", Time::monotonicMicroseconds() - start)); } void WorldClient::lightingCalc() { From 986db9cfc5a1c9015503b5366c65a377f8c8a259 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Sun, 2 Jun 2024 14:02:16 +1000 Subject: [PATCH 2/4] further MaterialDatabase checks --- source/game/StarMaterialDatabase.hpp | 18 ++++++++++++------ source/game/StarTileDrawer.cpp | 1 - source/rendering/StarTilePainter.cpp | 1 - 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/source/game/StarMaterialDatabase.hpp b/source/game/StarMaterialDatabase.hpp index b890e30..f20a3e2 100644 --- a/source/game/StarMaterialDatabase.hpp +++ b/source/game/StarMaterialDatabase.hpp @@ -188,15 +188,21 @@ private: }; inline MaterialRenderProfileConstPtr MaterialDatabase::materialRenderProfile(MaterialId materialId) const { - if (materialId >= m_materials.size()) - return {}; - return m_materials[materialId]->materialRenderProfile; + if (materialId < m_materials.size()) { + if (auto const& mat = m_materials[materialId]) + return mat->materialRenderProfile; + } + + return {}; } inline MaterialRenderProfileConstPtr MaterialDatabase::modRenderProfile(ModId modId) const { - if (modId >= m_mods.size()) - return {}; - return m_mods[modId]->modRenderProfile; + if (modId < m_mods.size()) { + if (auto const& mod = m_mods[modId]) + return mod->modRenderProfile; + } + + return {}; } inline bool MaterialDatabase::foregroundLightTransparent(MaterialId materialId) const { diff --git a/source/game/StarTileDrawer.cpp b/source/game/StarTileDrawer.cpp index 3ac8e48..70cd7d0 100644 --- a/source/game/StarTileDrawer.cpp +++ b/source/game/StarTileDrawer.cpp @@ -106,7 +106,6 @@ bool TileDrawer::produceTerrainDrawables(Drawables& drawables, }; auto materialRenderProfile = materialDatabase->materialRenderProfile(material); - auto modRenderProfile = materialDatabase->modRenderProfile(mod); if (materialRenderProfile) { diff --git a/source/rendering/StarTilePainter.cpp b/source/rendering/StarTilePainter.cpp index 58d9d28..f9cff34 100644 --- a/source/rendering/StarTilePainter.cpp +++ b/source/rendering/StarTilePainter.cpp @@ -277,7 +277,6 @@ bool TilePainter::produceTerrainPrimitives(HashMapmaterialRenderProfile(material); - auto modRenderProfile = materialDatabase->modRenderProfile(mod); if (materialRenderProfile) { From 68bd850fe73819c75841c5b6147255caf16c6ed9 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Sun, 2 Jun 2024 14:31:25 +1000 Subject: [PATCH 3/4] scale planetNameOffset by interface scale that parameter was definitely not tested for other interface scales --- source/frontend/StarMainInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/source/frontend/StarMainInterface.cpp b/source/frontend/StarMainInterface.cpp index 9f0bc0d..f9e8d57 100644 --- a/source/frontend/StarMainInterface.cpp +++ b/source/frontend/StarMainInterface.cpp @@ -714,6 +714,7 @@ void MainInterface::update(float dt) { worldName = worldTemplate->worldName(); if (!worldName.empty()) { + m_planetText->parent()->setPosition(Vec2I(Vec2F(m_config->planetNameOffset) * (2.f / interfaceScale()))); m_planetText->setText(strf(m_config->planetNameFormatString.utf8Ptr(), worldName)); m_paneManager.displayRegisteredPane(MainInterfacePanes::PlanetText); } From add17da988f4518c451abc4d0bacf536071e1c0b Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Sun, 2 Jun 2024 22:37:52 +1000 Subject: [PATCH 4/4] directive-based color variants for materials --- source/core/StarDirectives.hpp | 2 +- source/game/StarMaterialDatabase.cpp | 24 ++++++++++++-- source/game/StarMaterialDatabase.hpp | 2 ++ source/game/StarMaterialRenderProfile.cpp | 8 +++-- source/game/StarMaterialRenderProfile.hpp | 4 ++- source/game/StarTileDrawer.cpp | 38 ++++++++++++++++------- source/rendering/StarTilePainter.cpp | 33 ++++++++++++-------- 7 files changed, 80 insertions(+), 31 deletions(-) diff --git a/source/core/StarDirectives.hpp b/source/core/StarDirectives.hpp index e6b0a6e..9290649 100644 --- a/source/core/StarDirectives.hpp +++ b/source/core/StarDirectives.hpp @@ -121,7 +121,7 @@ private: void buildString(String& string, const DirectivesGroup& directives) const; List m_directives; - size_t m_count; + size_t m_count = 0; }; template <> diff --git a/source/game/StarMaterialDatabase.cpp b/source/game/StarMaterialDatabase.cpp index 6363c5d..99ce32e 100644 --- a/source/game/StarMaterialDatabase.cpp +++ b/source/game/StarMaterialDatabase.cpp @@ -402,11 +402,31 @@ ItemDescriptor MaterialDatabase::modItemDrop(ModId modId) const { return {}; } +MaterialColorVariant MaterialDatabase::materialColorVariants(MaterialId materialId) const { + if (isRealMaterial(materialId)) { + auto const& matInfo = getMaterialInfo(materialId); + if (matInfo->materialRenderProfile) + return matInfo->materialRenderProfile->colorVariants; + } + + return 0; +} + +MaterialColorVariant MaterialDatabase::modColorVariants(ModId modId) const { + if (isRealMod(modId)) { + auto const& modInfo = getModInfo(modId); + if (modInfo->modRenderProfile) + return modInfo->modRenderProfile->colorVariants; + } + + return 0; +} + bool MaterialDatabase::isMultiColor(MaterialId materialId) const { if (isRealMaterial(materialId)) { auto const& matInfo = getMaterialInfo(materialId); if (matInfo->materialRenderProfile) - return matInfo->materialRenderProfile->multiColor; + return matInfo->materialRenderProfile->colorVariants > 0; } return false; @@ -568,7 +588,7 @@ shared_ptr const& MaterialDatabase::getMat shared_ptr const& MaterialDatabase::getModInfo(ModId modId) const { if (modId >= m_mods.size() || !m_mods[modId]) - throw MaterialException(strf("No such modId id: {}\n", modId)); + throw MaterialException(strf("No such mod id: {}\n", modId)); else return m_mods[modId]; } diff --git a/source/game/StarMaterialDatabase.hpp b/source/game/StarMaterialDatabase.hpp index f20a3e2..1d7d709 100644 --- a/source/game/StarMaterialDatabase.hpp +++ b/source/game/StarMaterialDatabase.hpp @@ -69,6 +69,8 @@ public: ItemDescriptor materialItemDrop(MaterialId materialId) const; ItemDescriptor modItemDrop(ModId modId) const; + MaterialColorVariant materialColorVariants(MaterialId materialId) const; + MaterialColorVariant modColorVariants(ModId modId) const; bool isMultiColor(MaterialId materialId) const; bool foregroundLightTransparent(MaterialId materialId) const; bool backgroundLightTransparent(MaterialId materialId) const; diff --git a/source/game/StarMaterialRenderProfile.cpp b/source/game/StarMaterialRenderProfile.cpp index 975e7fa..1272e83 100644 --- a/source/game/StarMaterialRenderProfile.cpp +++ b/source/game/StarMaterialRenderProfile.cpp @@ -80,7 +80,9 @@ MaterialRenderProfile parseMaterialRenderProfile(Json const& spec, String const& bool lightTransparent = spec.getBool("lightTransparent", false); profile.foregroundLightTransparent = spec.getBool("foregroundLightTransparent", lightTransparent); profile.backgroundLightTransparent = spec.getBool("backgroundLightTransparent", lightTransparent); - profile.multiColor = spec.getBool("multiColored", false); + profile.colorVariants = spec.getBool("multiColored", false) ? spec.getUInt("colorVariants", MaxMaterialColorVariant) : 0; + for (auto& entry : spec.getArray("colorDirectives", JsonArray())) + profile.colorDirectives.append(entry.toString()); profile.occludesBehind = spec.getBool("occludesBelow", true); profile.zLevel = spec.getUInt("zLevel", 0); profile.radiantLight = Color::rgb(jsonToVec3B(spec.get("radiantLight", JsonArray{0, 0, 0}))).toRgbF(); @@ -126,8 +128,8 @@ MaterialRenderProfile parseMaterialRenderProfile(Json const& spec, String const& auto flipTextureCoordinates = [imageHeight]( RectF const& rect) { return RectF::withSize(Vec2F(rect.xMin(), imageHeight - rect.yMax()), rect.size()); }; for (unsigned v = 0; v < variants; ++v) { - if (profile.multiColor) { - for (MaterialColorVariant c = 0; c <= MaxMaterialColorVariant; ++c) { + if (profile.colorVariants > 0) { + for (MaterialColorVariant c = 0; c <= profile.colorVariants; ++c) { RectF textureRect = RectF::withSize(texturePosition + variantStride * v + colorStride * c, textureSize); renderPiece->variants[c].append(flipTextureCoordinates(textureRect)); } diff --git a/source/game/StarMaterialRenderProfile.hpp b/source/game/StarMaterialRenderProfile.hpp index 658cd89..f721cdb 100644 --- a/source/game/StarMaterialRenderProfile.hpp +++ b/source/game/StarMaterialRenderProfile.hpp @@ -6,6 +6,7 @@ #include "StarMultiArray.hpp" #include "StarGameTypes.hpp" #include "StarTileDamage.hpp" +#include "StarDirectives.hpp" namespace Star { @@ -96,11 +97,12 @@ struct MaterialRenderProfile { MaterialRenderMatchList mainMatchList; List> crackingFrames; List> protectedFrames; + List colorDirectives; Json ruleProperties; bool foregroundLightTransparent; bool backgroundLightTransparent; - bool multiColor; + uint8_t colorVariants; bool occludesBehind; uint32_t zLevel; Vec3F radiantLight; diff --git a/source/game/StarTileDrawer.cpp b/source/game/StarTileDrawer.cpp index 70cd7d0..2fa5051 100644 --- a/source/game/StarTileDrawer.cpp +++ b/source/game/StarTileDrawer.cpp @@ -65,7 +65,7 @@ bool TileDrawer::produceTerrainDrawables(Drawables& drawables, MaterialId material = EmptyMaterialId; MaterialHue materialHue = 0; - MaterialColorVariant materialColorVariant = 0; + MaterialColorVariant colorVariant = 0; ModId mod = NoModId; MaterialHue modHue = 0; float damageLevel = 0.0f; @@ -77,7 +77,7 @@ bool TileDrawer::produceTerrainDrawables(Drawables& drawables, if (terrainLayer == TerrainLayer::Background) { material = tile.background; materialHue = tile.backgroundHueShift; - materialColorVariant = tile.backgroundColorVariant; + colorVariant = tile.backgroundColorVariant; mod = tile.backgroundMod; modHue = tile.backgroundModHueShift; damageLevel = byteToFloat(tile.backgroundDamageLevel); @@ -86,7 +86,7 @@ bool TileDrawer::produceTerrainDrawables(Drawables& drawables, } else { material = tile.foreground; materialHue = tile.foregroundHueShift; - materialColorVariant = tile.foregroundColorVariant; + colorVariant = tile.foregroundColorVariant; mod = tile.foregroundMod; modHue = tile.foregroundModHueShift; damageLevel = byteToFloat(tile.foregroundDamageLevel); @@ -99,10 +99,14 @@ bool TileDrawer::produceTerrainDrawables(Drawables& drawables, if ((isBlock && terrainLayer == TerrainLayer::Midground) || (!isBlock && terrainLayer == TerrainLayer::Foreground)) return false; - auto getPieceImage = [](MaterialRenderPieceConstPtr const& piece, Box const& box, MaterialHue hue) -> String { - return hue == 0 + auto getPieceImage = [](MaterialRenderPieceConstPtr const& piece, Box const& box, MaterialHue hue, Directives const* directives) -> AssetPath { + AssetPath image = hue == 0 ? strf("{}?crop={};{};{};{}", piece->texture, box.xMin(), box.yMin(), box.xMax(), box.yMax()) : strf("{}?crop={};{};{};{}?hueshift={}", piece->texture, box.xMin(), box.yMin(), box.xMax(), box.yMax(), materialHueToDegrees(hue)); + if (directives) + image.directives += *directives; + + return image; }; auto materialRenderProfile = materialDatabase->materialRenderProfile(material); @@ -110,31 +114,43 @@ bool TileDrawer::produceTerrainDrawables(Drawables& drawables, if (materialRenderProfile) { occlude = materialRenderProfile->occludesBehind; - + auto materialColorVariant = materialRenderProfile->colorVariants > 0 ? colorVariant % materialRenderProfile->colorVariants : 0; uint32_t variance = staticRandomU32(renderData.geometry.xwrap(pos[0]) + offset[0], pos[1] + offset[1], (int)variantLayer.value(terrainLayer), "main"); auto& drawList = drawables[materialZLevel(materialRenderProfile->zLevel, material, materialHue, materialColorVariant)]; MaterialPieceResultList pieces; determineMatchingPieces(pieces, &occlude, materialDatabase, materialRenderProfile->mainMatchList, renderData, pos, terrainLayer == TerrainLayer::Background ? TileLayer::Background : TileLayer::Foreground, false); + Directives const* directives = materialRenderProfile->colorDirectives.empty() + ? nullptr + : &materialRenderProfile->colorDirectives.wrap(materialColorVariant); for (auto const& piecePair : pieces) { - auto& variant = piecePair.first->variants.get(materialColorVariant).wrap(variance); - auto image = getPieceImage(piecePair.first, variant, materialHue); + auto variant = piecePair.first->variants.ptr(materialColorVariant); + if (!variant) variant = piecePair.first->variants.ptr(0); + if (!variant) continue; + auto& textureCoords = variant->wrap(variance); + auto image = getPieceImage(piecePair.first, textureCoords, materialHue, directives); drawList.emplace_back(Drawable::makeImage(image, scale, false, piecePair.second * scale + Vec2F(pos), color)); } } if (modRenderProfile) { - auto modColorVariant = modRenderProfile->multiColor ? materialColorVariant : 0; + auto modColorVariant = modRenderProfile->colorVariants > 0 ? colorVariant % modRenderProfile->colorVariants : 0; uint32_t variance = staticRandomU32(renderData.geometry.xwrap(pos[0]), pos[1], (int)variantLayer.value(terrainLayer), "mod"); auto& drawList = drawables[modZLevel(modRenderProfile->zLevel, mod, modHue, modColorVariant)]; MaterialPieceResultList pieces; determineMatchingPieces(pieces, &occlude, materialDatabase, modRenderProfile->mainMatchList, renderData, pos, terrainLayer == TerrainLayer::Background ? TileLayer::Background : TileLayer::Foreground, true); + Directives const* directives = modRenderProfile->colorDirectives.empty() + ? nullptr + : &modRenderProfile->colorDirectives.wrap(modColorVariant); for (auto const& piecePair : pieces) { - auto& variant = piecePair.first->variants.get(modColorVariant).wrap(variance); - auto image = getPieceImage(piecePair.first, variant, modHue); + auto variant = piecePair.first->variants.ptr(modColorVariant); + if (!variant) variant = piecePair.first->variants.ptr(0); + if (!variant) continue; + auto& textureCoords = variant->wrap(variance); + auto image = getPieceImage(piecePair.first, textureCoords, modHue, directives); drawList.emplace_back(Drawable::makeImage(image, scale, false, piecePair.second * scale + Vec2F(pos), color)); } } diff --git a/source/rendering/StarTilePainter.cpp b/source/rendering/StarTilePainter.cpp index f9cff34..eef54b6 100644 --- a/source/rendering/StarTilePainter.cpp +++ b/source/rendering/StarTilePainter.cpp @@ -230,7 +230,7 @@ bool TilePainter::produceTerrainPrimitives(HashMappieceId, hue, mod), [&](auto const&) { - String texture; - if (hue == 0) - texture = piece->texture; - else - texture = strf("{}?hueshift={}", piece->texture, materialHueToDegrees(hue)); + AssetPath texture = (hue == 0) ? piece->texture : strf("{}?hueshift={}", piece->texture, materialHueToDegrees(hue)); + + if (directives) + texture.directives += *directives; return m_textureGroup->create(*assets->image(texture)); }); @@ -281,16 +280,20 @@ bool TilePainter::produceTerrainPrimitives(HashMapoccludesBehind; - + auto materialColorVariant = materialRenderProfile->colorVariants > 0 ? colorVariant % materialRenderProfile->colorVariants : 0; uint32_t variance = staticRandomU32(renderData.geometry.xwrap(pos[0]), pos[1], (int)terrainLayer, "main"); auto& quadList = primitives[materialZLevel(materialRenderProfile->zLevel, material, materialHue, materialColorVariant)]; MaterialPieceResultList pieces; determineMatchingPieces(pieces, &occlude, materialDatabase, materialRenderProfile->mainMatchList, renderData, pos, terrainLayer == TerrainLayer::Background ? TileLayer::Background : TileLayer::Foreground, false); + Directives const* directives = materialRenderProfile->colorDirectives.empty() + ? nullptr + : &materialRenderProfile->colorDirectives.wrap(materialColorVariant); for (auto const& piecePair : pieces) { - TexturePtr texture = getPieceTexture(material, piecePair.first, materialHue, false); + TexturePtr texture = getPieceTexture(material, piecePair.first, materialHue, directives, false); auto variant = piecePair.first->variants.ptr(materialColorVariant); + if (!variant) variant = piecePair.first->variants.ptr(0); if (!variant) continue; RectF textureCoords = variant->wrap(variance); RectF worldCoords = RectF::withSize(piecePair.second / TilePixels + Vec2F(pos), textureCoords.size() / TilePixels); @@ -308,16 +311,20 @@ bool TilePainter::produceTerrainPrimitives(HashMapmultiColor ? materialColorVariant : 0; + auto modColorVariant = modRenderProfile->colorVariants > 0 ? colorVariant % modRenderProfile->colorVariants : 0; uint32_t variance = staticRandomU32(renderData.geometry.xwrap(pos[0]), pos[1], (int)terrainLayer, "mod"); auto& quadList = primitives[modZLevel(modRenderProfile->zLevel, mod, modHue, modColorVariant)]; MaterialPieceResultList pieces; determineMatchingPieces(pieces, &occlude, materialDatabase, modRenderProfile->mainMatchList, renderData, pos, terrainLayer == TerrainLayer::Background ? TileLayer::Background : TileLayer::Foreground, true); + Directives const* directives = modRenderProfile->colorDirectives.empty() + ? nullptr + : &modRenderProfile->colorDirectives.wrap(modColorVariant); for (auto const& piecePair : pieces) { - auto texture = getPieceTexture(mod, piecePair.first, modHue, true); + auto texture = getPieceTexture(mod, piecePair.first, modHue, directives, true); auto variant = piecePair.first->variants.ptr(modColorVariant); + if (!variant) variant = piecePair.first->variants.ptr(0); if (!variant) continue; auto& textureCoords = variant->wrap(variance); RectF worldCoords = RectF::withSize(piecePair.second / TilePixels + Vec2F(pos), textureCoords.size() / TilePixels);