diff --git a/assets/opensb/binds/opensb.binds b/assets/opensb/binds/opensb.binds index 73ba73b..c52385a 100644 --- a/assets/opensb/binds/opensb.binds +++ b/assets/opensb/binds/opensb.binds @@ -33,15 +33,15 @@ "group": "building", "name": "Cycle Material Collision" }, - "materialRadiusGrow": { + "buildingRadiusGrow": { "default": [], "group": "building", - "name": "Grow Material Radius" + "name": "Grow Building Radius" }, - "materialRadiusShrink": { + "buildingRadiusShrink": { "default": [], "group": "building", - "name": "Shrink Material Radius" + "name": "Shrink Building Radius" } } } diff --git a/assets/opensb/sfx/tools/matradiusgrow.wav b/assets/opensb/sfx/tools/buildradiusgrow.wav similarity index 100% rename from assets/opensb/sfx/tools/matradiusgrow.wav rename to assets/opensb/sfx/tools/buildradiusgrow.wav diff --git a/assets/opensb/sfx/tools/matradiusshrink.wav b/assets/opensb/sfx/tools/buildradiusshrink.wav similarity index 100% rename from assets/opensb/sfx/tools/matradiusshrink.wav rename to assets/opensb/sfx/tools/buildradiusshrink.wav diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 995325e..19ea9e0 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -170,9 +170,9 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController) AudioFormat audioFormat = appController->enableAudio(); m_mainMixer = make_shared(audioFormat.sampleRate, audioFormat.channels); - m_mainMixer->setVolume(0.5); + m_worldPainter = make_shared(); m_guiContext = make_shared(m_mainMixer->mixer(), appController); m_input = make_shared(); m_voice = make_shared(appController); @@ -644,7 +644,6 @@ void ClientApplication::changeState(MainAppState newState) { m_titleScreen->stopMusic(); - m_worldPainter = make_shared(); m_mainInterface = make_shared(m_universeClient, m_worldPainter, m_cinematicOverlay); m_universeClient->setLuaCallbacks("interface", LuaBindings::makeInterfaceCallbacks(m_mainInterface.get())); m_universeClient->startLua(); diff --git a/source/game/CMakeLists.txt b/source/game/CMakeLists.txt index c229000..4886c48 100644 --- a/source/game/CMakeLists.txt +++ b/source/game/CMakeLists.txt @@ -144,6 +144,7 @@ SET (star_game_HEADERS StarTenantDatabase.hpp StarTerrainDatabase.hpp StarTileDamage.hpp + StarTileDrawer.hpp StarTileModification.hpp StarTileSectorArray.hpp StarTilesetDatabase.hpp @@ -404,6 +405,7 @@ SET (star_game_SOURCES StarTenantDatabase.cpp StarTerrainDatabase.cpp StarTileDamage.cpp + StarTileDrawer.cpp StarTileModification.cpp StarTilesetDatabase.cpp StarToolUser.cpp diff --git a/source/game/StarItemDrop.cpp b/source/game/StarItemDrop.cpp index a9dbe0a..c97df6a 100644 --- a/source/game/StarItemDrop.cpp +++ b/source/game/StarItemDrop.cpp @@ -8,6 +8,7 @@ #include "StarWorld.hpp" #include "StarDataStreamExtra.hpp" #include "StarPlayer.hpp" +#include "StarMaterialItem.hpp" namespace Star { @@ -231,9 +232,10 @@ void ItemDrop::update(float dt, uint64_t) { m_mode.set(Mode::Dead); if (m_mode.get() <= Mode::Available && m_ageItemsTimer.elapsedTime() > m_ageItemsEvery) { - Root::singleton().itemDatabase()->ageItem(m_item, m_ageItemsTimer.elapsedTime()); - m_itemDescriptor.set(m_item->descriptor()); - updateCollisionPoly(); + if (Root::singleton().itemDatabase()->ageItem(m_item, m_ageItemsTimer.elapsedTime())) { + m_itemDescriptor.set(m_item->descriptor()); + updateCollisionPoly(); + } m_ageItemsTimer.setElapsedTime(0.0); } } else { @@ -261,7 +263,7 @@ void ItemDrop::update(float dt, uint64_t) { } bool ItemDrop::shouldDestroy() const { - return m_mode.get() == Mode::Dead; + return m_mode.get() == Mode::Dead || m_item->empty(); } void ItemDrop::render(RenderCallback* renderCallback) { @@ -294,7 +296,13 @@ void ItemDrop::render(RenderCallback* renderCallback) { } if (!m_drawables) { - m_drawables = m_item->dropDrawables(); + if (auto mat = as(m_item.get())) { + m_drawables = mat->generatedPreview(Vec2I(position().floor())); + m_overForeground = true; + } + else + m_drawables = m_item->dropDrawables(); + if (Directives dropDirectives = m_config.getString("directives", "")) { for (auto& drawable : *m_drawables) { if (drawable.isImage()) @@ -302,7 +310,7 @@ void ItemDrop::render(RenderCallback* renderCallback) { } } } - EntityRenderLayer renderLayer = m_mode.get() == Mode::Taken ? RenderLayerForegroundTile : RenderLayerItemDrop; + EntityRenderLayer renderLayer = m_mode.get() == Mode::Taken || m_overForeground ? RenderLayerForegroundTile : RenderLayerItemDrop; Vec2F dropPosition = position(); for (Drawable drawable : *m_drawables) { drawable.position += dropPosition; @@ -405,11 +413,16 @@ ItemDrop::ItemDrop() { m_ageItemsEvery = m_config.getDouble("ageItemsEvery", 10); m_eternal = false; + m_overForeground = false; } void ItemDrop::updateCollisionPoly() { - m_boundBox = Drawable::boundBoxAll(m_item->dropDrawables(), true); - m_boundBox.rangeSetIfEmpty(RectF{-0.5, -0.5, 0.5, 0.5}); + if (auto mat = as(m_item.get())) + m_boundBox = RectF{ -0.5, -0.5, 0.5, 0.5 }; + else { + m_boundBox = Drawable::boundBoxAll(m_item->dropDrawables(), true); + m_boundBox.rangeSetIfEmpty(RectF{ -0.5, -0.5, 0.5, 0.5 }); + } MovementParameters parameters; parameters.collisionPoly = PolyF(collisionArea()); m_movementController.applyParameters(parameters); diff --git a/source/game/StarItemDrop.hpp b/source/game/StarItemDrop.hpp index a806209..7b76cd7 100644 --- a/source/game/StarItemDrop.hpp +++ b/source/game/StarItemDrop.hpp @@ -123,6 +123,7 @@ private: GameTimer m_intangibleTimer; EpochTimer m_ageItemsTimer; + bool m_overForeground; Maybe> m_drawables; }; diff --git a/source/game/StarTileDrawer.cpp b/source/game/StarTileDrawer.cpp new file mode 100644 index 0000000..3ac8e48 --- /dev/null +++ b/source/game/StarTileDrawer.cpp @@ -0,0 +1,311 @@ +#include "StarTileDrawer.hpp" +#include "StarLexicalCast.hpp" +#include "StarJsonExtra.hpp" +#include "StarXXHash.hpp" +#include "StarMaterialDatabase.hpp" +#include "StarLiquidsDatabase.hpp" +#include "StarAssets.hpp" +#include "StarRoot.hpp" + +namespace Star { + +RenderTile TileDrawer::DefaultRenderTile{ + NullMaterialId, + NoModId, + NullMaterialId, + NoModId, + 0, + 0, + DefaultMaterialColorVariant, + TileDamageType::Protected, + 0, + 0, + 0, + DefaultMaterialColorVariant, + TileDamageType::Protected, + 0, + EmptyLiquidId, + 0 +}; + +TileDrawer* TileDrawer::s_singleton; + +TileDrawer* TileDrawer::singletonPtr() { + return s_singleton; +} + +TileDrawer& TileDrawer::singleton() { + if (!s_singleton) + throw StarException("TileDrawer::singleton() called with no TileDrawer instance available"); + else + return *s_singleton; +} + +TileDrawer::TileDrawer() { + auto assets = Root::singleton().assets(); + + m_backgroundLayerColor = jsonToColor(assets->json("/rendering.config:backgroundLayerColor")).toRgba(); + m_foregroundLayerColor = jsonToColor(assets->json("/rendering.config:foregroundLayerColor")).toRgba(); + m_liquidDrawLevels = jsonToVec2F(assets->json("/rendering.config:liquidDrawLevels")); + s_singleton = this; +} + +TileDrawer::~TileDrawer() { + if (s_singleton == this) + s_singleton = nullptr; +} + +bool TileDrawer::produceTerrainDrawables(Drawables& drawables, + TerrainLayer terrainLayer, Vec2I const& pos, WorldRenderData const& renderData, float scale, Vec2I offset, Maybe variantLayer) { + auto& root = Root::singleton(); + auto assets = Root::singleton().assets(); + auto materialDatabase = root.materialDatabase(); + + RenderTile const& tile = getRenderTile(renderData, pos); + + MaterialId material = EmptyMaterialId; + MaterialHue materialHue = 0; + MaterialColorVariant materialColorVariant = 0; + ModId mod = NoModId; + MaterialHue modHue = 0; + float damageLevel = 0.0f; + TileDamageType damageType = TileDamageType::Protected; + Color color; + + bool occlude = false; + + if (terrainLayer == TerrainLayer::Background) { + material = tile.background; + materialHue = tile.backgroundHueShift; + materialColorVariant = tile.backgroundColorVariant; + mod = tile.backgroundMod; + modHue = tile.backgroundModHueShift; + damageLevel = byteToFloat(tile.backgroundDamageLevel); + damageType = tile.backgroundDamageType; + color = Color::rgba(m_backgroundLayerColor); + } else { + material = tile.foreground; + materialHue = tile.foregroundHueShift; + materialColorVariant = tile.foregroundColorVariant; + mod = tile.foregroundMod; + modHue = tile.foregroundModHueShift; + damageLevel = byteToFloat(tile.foregroundDamageLevel); + damageType = tile.foregroundDamageType; + color = Color::rgba(m_foregroundLayerColor); + } + + // render non-block colliding things in the midground + bool isBlock = BlockCollisionSet.contains(materialDatabase->materialCollisionKind(material)); + 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 + ? 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)); + }; + + auto materialRenderProfile = materialDatabase->materialRenderProfile(material); + + auto modRenderProfile = materialDatabase->modRenderProfile(mod); + + if (materialRenderProfile) { + occlude = materialRenderProfile->occludesBehind; + + 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); + for (auto const& piecePair : pieces) { + auto& variant = piecePair.first->variants.get(materialColorVariant).wrap(variance); + auto image = getPieceImage(piecePair.first, variant, materialHue); + drawList.emplace_back(Drawable::makeImage(image, scale, false, piecePair.second * scale + Vec2F(pos), color)); + } + } + + if (modRenderProfile) { + auto modColorVariant = modRenderProfile->multiColor ? materialColorVariant : 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); + for (auto const& piecePair : pieces) { + auto& variant = piecePair.first->variants.get(modColorVariant).wrap(variance); + auto image = getPieceImage(piecePair.first, variant, modHue); + drawList.emplace_back(Drawable::makeImage(image, scale, false, piecePair.second * scale + Vec2F(pos), color)); + } + } + + if (materialRenderProfile && damageLevel > 0 && isBlock) { + auto& drawList = drawables[damageZLevel()]; + auto const& crackingImage = materialRenderProfile->damageImage(damageLevel, damageType); + + drawList.emplace_back(Drawable::makeImage(crackingImage.first, scale, false, crackingImage.second * scale + Vec2F(pos), color)); + } + + return occlude; +} + +WorldRenderData& TileDrawer::renderData() { + return m_tempRenderData; +} + +MutexLocker TileDrawer::lockRenderData() { + return MutexLocker(m_tempRenderDataMutex); +} + +RenderTile const& TileDrawer::getRenderTile(WorldRenderData const& renderData, Vec2I const& worldPos) { + Vec2I arrayPos = renderData.geometry.diff(worldPos, renderData.tileMinPosition); + + Vec2I size = Vec2I(renderData.tiles.size()); + if (arrayPos[0] >= 0 && arrayPos[1] >= 0 && arrayPos[0] < size[0] && arrayPos[1] < size[1]) + return renderData.tiles(Vec2S(arrayPos)); + + return DefaultRenderTile; +} + +TileDrawer::QuadZLevel TileDrawer::materialZLevel(uint32_t zLevel, MaterialId material, MaterialHue hue, MaterialColorVariant colorVariant) { + QuadZLevel quadZLevel = 0; + quadZLevel |= (uint64_t)colorVariant; + quadZLevel |= (uint64_t)hue << 8; + quadZLevel |= (uint64_t)material << 16; + quadZLevel |= (uint64_t)zLevel << 32; + return quadZLevel; +} + +TileDrawer::QuadZLevel TileDrawer::modZLevel(uint32_t zLevel, ModId mod, MaterialHue hue, MaterialColorVariant colorVariant) { + QuadZLevel quadZLevel = 0; + quadZLevel |= (uint64_t)colorVariant; + quadZLevel |= (uint64_t)hue << 8; + quadZLevel |= (uint64_t)mod << 16; + quadZLevel |= (uint64_t)zLevel << 32; + quadZLevel |= (uint64_t)1 << 63; + return quadZLevel; +} + +TileDrawer::QuadZLevel TileDrawer::damageZLevel() { + return (uint64_t)(-1); +} + +bool TileDrawer::determineMatchingPieces(MaterialPieceResultList& resultList, bool* occlude, MaterialDatabaseConstPtr const& materialDb, MaterialRenderMatchList const& matchList, + WorldRenderData const& renderData, Vec2I const& basePos, TileLayer layer, bool isMod) { + RenderTile const& tile = getRenderTile(renderData, basePos); + + auto matchSetMatches = [&](MaterialRenderMatchConstPtr const& match) -> bool { + if (match->requiredLayer && *match->requiredLayer != layer) + return false; + + if (match->matchPoints.empty()) + return true; + + bool matchValid = match->matchJoin == MaterialJoinType::All; + for (auto const& matchPoint : match->matchPoints) { + auto const& neighborTile = getRenderTile(renderData, basePos + matchPoint.position); + + bool neighborShadowing = false; + if (layer == TileLayer::Background) { + if (auto profile = materialDb->materialRenderProfile(neighborTile.foreground)) + neighborShadowing = !profile->foregroundLightTransparent; + } + + MaterialHue baseHue = layer == TileLayer::Foreground ? tile.foregroundHueShift : tile.backgroundHueShift; + MaterialHue neighborHue = layer == TileLayer::Foreground ? neighborTile.foregroundHueShift : neighborTile.backgroundHueShift; + MaterialHue baseModHue = layer == TileLayer::Foreground ? tile.foregroundModHueShift : tile.backgroundModHueShift; + MaterialHue neighborModHue = layer == TileLayer::Foreground ? neighborTile.foregroundModHueShift : neighborTile.backgroundModHueShift; + MaterialId baseMaterial = layer == TileLayer::Foreground ? tile.foreground : tile.background; + MaterialId neighborMaterial = layer == TileLayer::Foreground ? neighborTile.foreground : neighborTile.background; + ModId baseMod = layer == TileLayer::Foreground ? tile.foregroundMod : tile.backgroundMod; + ModId neighborMod = layer == TileLayer::Foreground ? neighborTile.foregroundMod : neighborTile.backgroundMod; + + bool rulesValid = matchPoint.rule->join == MaterialJoinType::All; + for (auto const& ruleEntry : matchPoint.rule->entries) { + bool valid = true; + if (isMod) { + if (ruleEntry.rule.is()) { + valid = neighborMod == NoModId; + } else if (ruleEntry.rule.is()) { + valid = isConnectableMaterial(neighborMaterial); + } else if (ruleEntry.rule.is()) { + valid = neighborShadowing; + } else if (auto equalsSelf = ruleEntry.rule.ptr()) { + valid = neighborMod == baseMod; + if (equalsSelf->matchHue) + valid = valid && baseModHue == neighborModHue; + } else if (auto equalsId = ruleEntry.rule.ptr()) { + valid = neighborMod == equalsId->id; + } else if (auto propertyEquals = ruleEntry.rule.ptr()) { + if (auto profile = materialDb->modRenderProfile(neighborMod)) + valid = profile->ruleProperties.get(propertyEquals->propertyName, Json()) == propertyEquals->compare; + else + valid = false; + } + } else { + if (ruleEntry.rule.is()) { + valid = neighborMaterial == EmptyMaterialId; + } else if (ruleEntry.rule.is()) { + valid = isConnectableMaterial(neighborMaterial); + } else if (ruleEntry.rule.is()) { + valid = neighborShadowing; + } else if (auto equalsSelf = ruleEntry.rule.ptr()) { + valid = neighborMaterial == baseMaterial; + if (equalsSelf->matchHue) + valid = valid && baseHue == neighborHue; + } else if (auto equalsId = ruleEntry.rule.ptr()) { + valid = neighborMaterial == equalsId->id; + } else if (auto propertyEquals = ruleEntry.rule.ptr()) { + if (auto profile = materialDb->materialRenderProfile(neighborMaterial)) + valid = profile->ruleProperties.get(propertyEquals->propertyName) == propertyEquals->compare; + else + valid = false; + } + } + if (ruleEntry.inverse) + valid = !valid; + + if (matchPoint.rule->join == MaterialJoinType::All) { + rulesValid = valid && rulesValid; + if (!rulesValid) + break; + } else { + rulesValid = valid || rulesValid; + } + } + + if (match->matchJoin == MaterialJoinType::All) { + matchValid = matchValid && rulesValid; + if (!matchValid) + return matchValid; + } else { + matchValid = matchValid || rulesValid; + } + } + return matchValid; + }; + + bool subMatchResult = false; + for (auto const& match : matchList) { + if (matchSetMatches(match)) { + if (match->occlude) + *occlude = match->occlude.get(); + + subMatchResult = true; + + for (auto const& piecePair : match->resultingPieces) + resultList.append({piecePair.first, piecePair.second}); + + if (determineMatchingPieces(resultList, occlude, materialDb, match->subMatches, renderData, basePos, layer, isMod) && match->haltOnSubMatch) + break; + + if (match->haltOnMatch) + break; + } + } + + return subMatchResult; +} + +} \ No newline at end of file diff --git a/source/game/StarTileDrawer.hpp b/source/game/StarTileDrawer.hpp new file mode 100644 index 0000000..907908a --- /dev/null +++ b/source/game/StarTileDrawer.hpp @@ -0,0 +1,77 @@ +#ifndef STAR_TILE_DRAWER_HPP +#define STAR_TILE_DRAWER_HPP + +#include "StarTtlCache.hpp" +#include "StarWorldRenderData.hpp" +#include "StarMaterialRenderProfile.hpp" +#include "StarDrawable.hpp" + +namespace Star { + +STAR_CLASS(Assets); +STAR_CLASS(MaterialDatabase); +STAR_CLASS(TileDrawer); + +class TileDrawer { +public: + typedef uint64_t QuadZLevel; + typedef HashMap> Drawables; + + typedef size_t MaterialRenderPieceIndex; + typedef List> MaterialPieceResultList; + + enum class TerrainLayer { Background, Midground, Foreground }; + + static RenderTile DefaultRenderTile; + + static TileDrawer* singletonPtr(); + static TileDrawer& singleton(); + + TileDrawer(); + ~TileDrawer(); + + bool produceTerrainDrawables(Drawables& drawables, TerrainLayer terrainLayer, Vec2I const& pos, + WorldRenderData const& renderData, float scale = 1.0f, Vec2I variantOffset = {}, Maybe variantLayer = {}); + + WorldRenderData& renderData(); + MutexLocker lockRenderData(); + + template + static void forEachRenderTile(WorldRenderData const& renderData, RectI const& worldCoordRange, Function&& function); +private: + friend class TilePainter; + + static TileDrawer* s_singleton; + + static RenderTile const& getRenderTile(WorldRenderData const& renderData, Vec2I const& worldPos); + + static QuadZLevel materialZLevel(uint32_t zLevel, MaterialId material, MaterialHue hue, MaterialColorVariant colorVariant); + static QuadZLevel modZLevel(uint32_t zLevel, ModId mod, MaterialHue hue, MaterialColorVariant colorVariant); + static QuadZLevel damageZLevel(); + + static bool determineMatchingPieces(MaterialPieceResultList& resultList, bool* occlude, MaterialDatabaseConstPtr const& materialDb, MaterialRenderMatchList const& matchList, + WorldRenderData const& renderData, Vec2I const& basePos, TileLayer layer, bool isMod); + + Vec4B m_backgroundLayerColor; + Vec4B m_foregroundLayerColor; + Vec2F m_liquidDrawLevels; + + WorldRenderData m_tempRenderData; + Mutex m_tempRenderDataMutex; +}; + +template +void TileDrawer::forEachRenderTile(WorldRenderData const& renderData, RectI const& worldCoordRange, Function&& function) { + RectI indexRect = RectI::withSize(renderData.geometry.diff(worldCoordRange.min(), renderData.tileMinPosition), worldCoordRange.size()); + indexRect.limit(RectI::withSize(Vec2I(0, 0), Vec2I(renderData.tiles.size()))); + + if (!indexRect.isEmpty()) { + renderData.tiles.forEach(Array2S(indexRect.min()), Array2S(indexRect.size()), [&](Array2S const& index, RenderTile const& tile) { + return function(worldCoordRange.min() + (Vec2I(index) - indexRect.min()), tile); + }); + } +} + +} + +#endif \ No newline at end of file diff --git a/source/game/StarToolUser.cpp b/source/game/StarToolUser.cpp index 7208c6e..dd6f5ff 100644 --- a/source/game/StarToolUser.cpp +++ b/source/game/StarToolUser.cpp @@ -536,9 +536,9 @@ void ToolUser::render(RenderCallback* renderCallback, bool inToolRange, bool shi } if (auto pri = as(m_primaryHandItem.get())) - renderCallback->addTilePreviews(pri->preview(shifting)); + renderCallback->addTilePreviews(pri->previewTiles(shifting)); else if (auto alt = as(m_altHandItem.get())) - renderCallback->addTilePreviews(alt->preview(shifting)); + renderCallback->addTilePreviews(alt->previewTiles(shifting)); if (auto ren = as(m_primaryHandItem.get())) ren->render(renderCallback, renderLayer); diff --git a/source/game/interfaces/StarPreviewTileTool.hpp b/source/game/interfaces/StarPreviewTileTool.hpp index dfc53bf..12a4b2b 100644 --- a/source/game/interfaces/StarPreviewTileTool.hpp +++ b/source/game/interfaces/StarPreviewTileTool.hpp @@ -12,7 +12,7 @@ namespace Star { class PreviewTileTool { public: virtual ~PreviewTileTool() {} - virtual List preview(bool shifting) const = 0; + virtual List previewTiles(bool shifting) const = 0; }; } diff --git a/source/game/items/StarLiquidItem.cpp b/source/game/items/StarLiquidItem.cpp index 08e202f..2e887aa 100644 --- a/source/game/items/StarLiquidItem.cpp +++ b/source/game/items/StarLiquidItem.cpp @@ -85,7 +85,7 @@ float LiquidItem::liquidQuantity() const { return m_quantity; } -List LiquidItem::preview(bool shifting) const { +List LiquidItem::previewTiles(bool shifting) const { List result; if (initialized()) { auto liquid = liquidId(); diff --git a/source/game/items/StarLiquidItem.hpp b/source/game/items/StarLiquidItem.hpp index 03a73cd..b4c4cfc 100644 --- a/source/game/items/StarLiquidItem.hpp +++ b/source/game/items/StarLiquidItem.hpp @@ -28,7 +28,7 @@ public: LiquidId liquidId() const; float liquidQuantity() const; - List preview(bool shifting) const override; + List previewTiles(bool shifting) const override; bool canPlace(bool shifting) const; bool canPlaceAtTile(Vec2I pos) const; diff --git a/source/game/items/StarMaterialItem.cpp b/source/game/items/StarMaterialItem.cpp index f2f15da..2740eb7 100644 --- a/source/game/items/StarMaterialItem.cpp +++ b/source/game/items/StarMaterialItem.cpp @@ -8,6 +8,7 @@ #include "StarWorldClient.hpp" #include "StarWorldTemplate.hpp" #include "StarInput.hpp" +#include "StarTileDrawer.hpp" namespace Star { @@ -17,6 +18,7 @@ MaterialItem::MaterialItem(Json const& config, String const& directory, Json con : Item(config, directory, settings), FireableItem(config), BeamItem(config) { m_material = config.getInt("materialId"); m_materialHueShift = materialHueFromDegrees(instanceValue("materialHueShift", 0).toFloat()); + auto materialDatabase = Root::singleton().materialDatabase(); if (materialHueShift() != MaterialHue()) { auto drawables = iconDrawables(); @@ -36,7 +38,6 @@ MaterialItem::MaterialItem(Json const& config, String const& directory, Json con m_blockRadius = instanceValue("blockRadius", defaultParameters.getFloat("blockRadius")).toFloat(); m_altBlockRadius = instanceValue("altBlockRadius", defaultParameters.getFloat("altBlockRadius")).toFloat(); - auto materialDatabase = Root::singleton().materialDatabase(); auto multiplace = instanceValue("allowMultiplace", BlockCollisionSet.contains(materialDatabase->materialCollisionKind(m_material))); if (multiplace.isType(Json::Type::Bool)) m_multiplace = multiplace.toBool(); @@ -96,16 +97,16 @@ void MaterialItem::update(float dt, FireMode fireMode, bool shifting, HashSetaddSound("/sfx/tools/cyclematcollision.ogg", 1.0f, Random::randf(0.9f, 1.1f)); } - if (auto presses = input.bindDown("opensb", "materialRadiusGrow")) { + if (auto presses = input.bindDown("opensb", "buildingRadiusGrow")) { m_blockRadius = min(BlockRadiusLimit, int(m_blockRadius + *presses)); setInstanceValue("blockRadius", m_blockRadius); - owner()->addSound("/sfx/tools/matradiusgrow.wav", 1.0f, 1.0f + m_blockRadius / BlockRadiusLimit); + owner()->addSound("/sfx/tools/buildradiusgrow.wav", 1.0f, 1.0f + m_blockRadius / BlockRadiusLimit); } - if (auto presses = input.bindDown("opensb", "materialRadiusShrink")) { + if (auto presses = input.bindDown("opensb", "buildingRadiusShrink")) { m_blockRadius = max(1, int(m_blockRadius - *presses)); setInstanceValue("blockRadius", m_blockRadius); - owner()->addSound("/sfx/tools/matradiusshrink.wav", 1.0f, 1.0f + m_blockRadius / BlockRadiusLimit); + owner()->addSound("/sfx/tools/buildradiusshrink.wav", 1.0f, 1.0f + m_blockRadius / BlockRadiusLimit); } } else { @@ -148,6 +149,14 @@ void MaterialItem::render(RenderCallback* renderCallback, EntityRenderLayer rend } } +List MaterialItem::preview(PlayerPtr const&) const { + return generatedPreview(); +} + +List MaterialItem::dropDrawables() const { + return generatedPreview(); +} + List MaterialItem::nonRotatedDrawables() const { return beamDrawables(canPlace(m_shifting)); } @@ -218,6 +227,47 @@ MaterialId MaterialItem::materialId() const { return m_material; } +List const& MaterialItem::generatedPreview(Vec2I position) const { + if (!m_generatedPreviewCache) { + if (TileDrawer* tileDrawer = TileDrawer::singletonPtr()) { + auto locker = tileDrawer->lockRenderData(); + WorldRenderData& renderData = tileDrawer->renderData(); + renderData.geometry = WorldGeometry(3, 3); + renderData.tiles.resize({ 3, 3 }); + renderData.tiles.fill(TileDrawer::DefaultRenderTile); + renderData.tileMinPosition = { 0, 0 }; + RenderTile& tile = renderData.tiles.at({ 1, 1 }); + tile.foreground = m_material; + tile.foregroundHueShift = m_materialHueShift; + tile.foregroundColorVariant = 0; + + List drawables; + TileDrawer::Drawables tileDrawables; + bool isBlock = BlockCollisionSet.contains(Root::singleton().materialDatabase()->materialCollisionKind(m_material)); + TileDrawer::TerrainLayer layer = isBlock ? TileDrawer::TerrainLayer::Foreground : TileDrawer::TerrainLayer::Midground; + for (int x = 0; x != 3; ++x) { + for (int y = 0; y != 3; ++y) + tileDrawer->produceTerrainDrawables(tileDrawables, layer, { x, y }, renderData, 1.0f / TilePixels, position - Vec2I(1, 1)); + } + + locker.unlock(); + for (auto& index : tileDrawables.keys()) + drawables.appendAll(tileDrawables.take(index)); + + auto boundBox = Drawable::boundBoxAll(drawables, true); + if (!boundBox.isEmpty()) { + for (auto& drawable : drawables) + drawable.translate(-boundBox.center()); + } + + m_generatedPreviewCache.emplace(move(drawables)); + } + else + m_generatedPreviewCache.emplace(iconDrawables()); + } + return *m_generatedPreviewCache; +} + float MaterialItem::calcRadius(bool shifting) const { if (!multiplaceEnabled()) return 1; @@ -270,7 +320,7 @@ TileCollisionOverride& MaterialItem::collisionOverride() { return m_collisionOverride; } -List MaterialItem::preview(bool shifting) const { +List MaterialItem::previewTiles(bool shifting) const { List result; if (initialized()) { Color lightColor = Color::rgba(owner()->favoriteColor()); diff --git a/source/game/items/StarMaterialItem.hpp b/source/game/items/StarMaterialItem.hpp index 05f9e08..363d248 100644 --- a/source/game/items/StarMaterialItem.hpp +++ b/source/game/items/StarMaterialItem.hpp @@ -7,13 +7,15 @@ #include "StarEntityRendering.hpp" #include "StarPreviewTileTool.hpp" #include "StarRenderableItem.hpp" +#include "StarPreviewableItem.hpp" #include "StarCollisionBlock.hpp" namespace Star { STAR_CLASS(MaterialItem); +STAR_CLASS(Player); -class MaterialItem : public Item, public FireableItem, public PreviewTileTool, public RenderableItem, public BeamItem { +class MaterialItem : public Item, public FireableItem, public PreviewTileTool, public RenderableItem, public PreviewableItem, public BeamItem { public: MaterialItem(Json const& config, String const& directory, Json const& settings); virtual ~MaterialItem() {} @@ -25,6 +27,8 @@ public: void update(float dt, FireMode fireMode, bool shifting, HashSet const& moves) override; void render(RenderCallback* renderCallback, EntityRenderLayer renderLayer) override; + virtual List preview(PlayerPtr const& viewer = {}) const override; + virtual List dropDrawables() const override; List nonRotatedDrawables() const override; void fire(FireMode mode, bool shifting, bool edgeTriggered) override; @@ -40,7 +44,8 @@ public: float& altBlockRadius(); TileCollisionOverride& collisionOverride(); - List preview(bool shifting) const override; + List previewTiles(bool shifting) const override; + List const& generatedPreview(Vec2I position = {}) const; private: float calcRadius(bool shifting) const; List& tileArea(float radius, Vec2F const& position) const; @@ -60,6 +65,8 @@ private: mutable Vec2F m_lastTileAreaOriginCache; mutable float m_lastTileAreaRadiusCache; mutable List m_tileAreasCache; + + mutable Maybe> m_generatedPreviewCache; }; } diff --git a/source/game/items/StarTools.cpp b/source/game/items/StarTools.cpp index 602e554..7faaeae 100644 --- a/source/game/items/StarTools.cpp +++ b/source/game/items/StarTools.cpp @@ -360,7 +360,7 @@ void BeamMiningTool::setEnd(EndType) { m_endType = EndType::Object; } -List BeamMiningTool::preview(bool shifting) const { +List BeamMiningTool::previewTiles(bool shifting) const { List result; auto ownerp = owner(); auto worldp = world(); @@ -636,7 +636,7 @@ void PaintingBeamTool::update(float dt, FireMode fireMode, bool shifting, HashSe FireableItem::update(dt, fireMode, shifting, moves); } -List PaintingBeamTool::preview(bool shifting) const { +List PaintingBeamTool::previewTiles(bool shifting) const { List result; auto ownerp = owner(); auto worldp = world(); diff --git a/source/game/items/StarTools.hpp b/source/game/items/StarTools.hpp index fbe79a0..75cdcaf 100644 --- a/source/game/items/StarTools.hpp +++ b/source/game/items/StarTools.hpp @@ -150,7 +150,7 @@ public: List drawables() const override; virtual void setEnd(EndType type) override; - virtual List preview(bool shifting) const override; + virtual List previewTiles(bool shifting) const override; virtual List nonRotatedDrawables() const override; virtual void fire(FireMode mode, bool shifting, bool edgeTriggered) override; @@ -219,7 +219,7 @@ public: void setEnd(EndType type) override; void update(float dt, FireMode fireMode, bool shifting, HashSet const& moves) override; - List preview(bool shifting) const override; + List previewTiles(bool shifting) const override; void init(ToolUserEntity* owner, ToolHand hand) override; List nonRotatedDrawables() const override; void fire(FireMode mode, bool shifting, bool edgeTriggered) override; diff --git a/source/rendering/StarTilePainter.cpp b/source/rendering/StarTilePainter.cpp index 40b3de6..de49910 100644 --- a/source/rendering/StarTilePainter.cpp +++ b/source/rendering/StarTilePainter.cpp @@ -6,10 +6,11 @@ #include "StarLiquidsDatabase.hpp" #include "StarAssets.hpp" #include "StarRoot.hpp" +#include "StarTileDrawer.hpp" namespace Star { -TilePainter::TilePainter(RendererPtr renderer) { +TilePainter::TilePainter(RendererPtr renderer) : TileDrawer() { m_renderer = move(renderer); m_textureGroup = m_renderer->createTextureGroup(TextureGroupSize::Large); @@ -24,10 +25,6 @@ TilePainter::TilePainter(RendererPtr renderer) { m_textureCache.setTimeToLive(assets->json("/rendering.config:textureTimeout").toInt()); - m_backgroundLayerColor = jsonToColor(assets->json("/rendering.config:backgroundLayerColor")).toRgba(); - m_foregroundLayerColor = jsonToColor(assets->json("/rendering.config:foregroundLayerColor")).toRgba(); - m_liquidDrawLevels = jsonToVec2F(assets->json("/rendering.config:liquidDrawLevels")); - for (auto const& liquid : root.liquidsDatabase()->allLiquidSettings()) { m_liquids.set(liquid->id, LiquidInfo{ m_renderer->createTexture(*assets->image(liquid->config.getString("texture")), TextureAddressing::Wrap), @@ -152,57 +149,6 @@ TilePainter::ChunkHash TilePainter::liquidChunkHash(WorldRenderData& renderData, return XXH3_64bits(buffer.ptr(), buffer.size()); } -TilePainter::QuadZLevel TilePainter::materialZLevel(uint32_t zLevel, MaterialId material, MaterialHue hue, MaterialColorVariant colorVariant) { - QuadZLevel quadZLevel = 0; - quadZLevel |= (uint64_t)colorVariant; - quadZLevel |= (uint64_t)hue << 8; - quadZLevel |= (uint64_t)material << 16; - quadZLevel |= (uint64_t)zLevel << 32; - return quadZLevel; -} - -TilePainter::QuadZLevel TilePainter::modZLevel(uint32_t zLevel, ModId mod, MaterialHue hue, MaterialColorVariant colorVariant) { - QuadZLevel quadZLevel = 0; - quadZLevel |= (uint64_t)colorVariant; - quadZLevel |= (uint64_t)hue << 8; - quadZLevel |= (uint64_t)mod << 16; - quadZLevel |= (uint64_t)zLevel << 32; - quadZLevel |= (uint64_t)1 << 63; - return quadZLevel; -} - -TilePainter::QuadZLevel TilePainter::damageZLevel() { - return (uint64_t)(-1); -} - -RenderTile const& TilePainter::getRenderTile(WorldRenderData const& renderData, Vec2I const& worldPos) { - Vec2I arrayPos = renderData.geometry.diff(worldPos, renderData.tileMinPosition); - - Vec2I size = Vec2I(renderData.tiles.size()); - if (arrayPos[0] >= 0 && arrayPos[1] >= 0 && arrayPos[0] < size[0] && arrayPos[1] < size[1]) - return renderData.tiles(Vec2S(arrayPos)); - - static RenderTile defaultRenderTile = { - NullMaterialId, - NoModId, - NullMaterialId, - NoModId, - 0, - 0, - DefaultMaterialColorVariant, - TileDamageType::Protected, - 0, - 0, - 0, - DefaultMaterialColorVariant, - TileDamageType::Protected, - 0, - EmptyLiquidId, - 0 - }; - return defaultRenderTile; -} - void TilePainter::renderTerrainChunks(WorldCamera const& camera, TerrainLayer terrainLayer) { Map> zOrderBuffers; for (auto const& chunk : m_pendingTerrainChunks) { @@ -285,7 +231,7 @@ bool TilePainter::produceTerrainPrimitives(HashMapmaterialCollisionKind(material)); - if ((isBlock && terrainLayer == TerrainLayer::Midground) || (!isBlock && terrainLayer == TerrainLayer::Foreground)) + if (terrainLayer == (isBlock ? TerrainLayer::Midground : TerrainLayer::Foreground)) return false; auto getPieceTexture = [this, assets](MaterialId material, MaterialRenderPieceConstPtr const& piece, MaterialHue hue, bool mod) { @@ -423,7 +369,7 @@ void TilePainter::produceLiquidPrimitives(HashMap(), move(liquid.texture), + primitives[tile.liquidId].emplace_back(std::in_place_type_t(), liquid.texture, worldRect.min(), texRect.min(), Vec2F(worldRect.xMax(), worldRect.yMin()), Vec2F(texRect.xMax(), texRect.yMin()), worldRect.max(), texRect.max(), @@ -431,123 +377,6 @@ void TilePainter::produceLiquidPrimitives(HashMap bool { - if (match->requiredLayer && *match->requiredLayer != layer) - return false; - - if (match->matchPoints.empty()) - return true; - - bool matchValid = match->matchJoin == MaterialJoinType::All; - for (auto const& matchPoint : match->matchPoints) { - auto const& neighborTile = getRenderTile(renderData, basePos + matchPoint.position); - - bool neighborShadowing = false; - if (layer == TileLayer::Background) { - if (auto profile = materialDb->materialRenderProfile(neighborTile.foreground)) - neighborShadowing = !profile->foregroundLightTransparent; - } - - MaterialHue baseHue = layer == TileLayer::Foreground ? tile.foregroundHueShift : tile.backgroundHueShift; - MaterialHue neighborHue = layer == TileLayer::Foreground ? neighborTile.foregroundHueShift : neighborTile.backgroundHueShift; - MaterialHue baseModHue = layer == TileLayer::Foreground ? tile.foregroundModHueShift : tile.backgroundModHueShift; - MaterialHue neighborModHue = layer == TileLayer::Foreground ? neighborTile.foregroundModHueShift : neighborTile.backgroundModHueShift; - MaterialId baseMaterial = layer == TileLayer::Foreground ? tile.foreground : tile.background; - MaterialId neighborMaterial = layer == TileLayer::Foreground ? neighborTile.foreground : neighborTile.background; - ModId baseMod = layer == TileLayer::Foreground ? tile.foregroundMod : tile.backgroundMod; - ModId neighborMod = layer == TileLayer::Foreground ? neighborTile.foregroundMod : neighborTile.backgroundMod; - - bool rulesValid = matchPoint.rule->join == MaterialJoinType::All; - for (auto const& ruleEntry : matchPoint.rule->entries) { - bool valid = true; - if (isMod) { - if (ruleEntry.rule.is()) { - valid = neighborMod == NoModId; - } else if (ruleEntry.rule.is()) { - valid = isConnectableMaterial(neighborMaterial); - } else if (ruleEntry.rule.is()) { - valid = neighborShadowing; - } else if (auto equalsSelf = ruleEntry.rule.ptr()) { - valid = neighborMod == baseMod; - if (equalsSelf->matchHue) - valid = valid && baseModHue == neighborModHue; - } else if (auto equalsId = ruleEntry.rule.ptr()) { - valid = neighborMod == equalsId->id; - } else if (auto propertyEquals = ruleEntry.rule.ptr()) { - if (auto profile = materialDb->modRenderProfile(neighborMod)) - valid = profile->ruleProperties.get(propertyEquals->propertyName, Json()) == propertyEquals->compare; - else - valid = false; - } - } else { - if (ruleEntry.rule.is()) { - valid = neighborMaterial == EmptyMaterialId; - } else if (ruleEntry.rule.is()) { - valid = isConnectableMaterial(neighborMaterial); - } else if (ruleEntry.rule.is()) { - valid = neighborShadowing; - } else if (auto equalsSelf = ruleEntry.rule.ptr()) { - valid = neighborMaterial == baseMaterial; - if (equalsSelf->matchHue) - valid = valid && baseHue == neighborHue; - } else if (auto equalsId = ruleEntry.rule.ptr()) { - valid = neighborMaterial == equalsId->id; - } else if (auto propertyEquals = ruleEntry.rule.ptr()) { - if (auto profile = materialDb->materialRenderProfile(neighborMaterial)) - valid = profile->ruleProperties.get(propertyEquals->propertyName) == propertyEquals->compare; - else - valid = false; - } - } - if (ruleEntry.inverse) - valid = !valid; - - if (matchPoint.rule->join == MaterialJoinType::All) { - rulesValid = valid && rulesValid; - if (!rulesValid) - break; - } else { - rulesValid = valid || rulesValid; - } - } - - if (match->matchJoin == MaterialJoinType::All) { - matchValid = matchValid && rulesValid; - if (!matchValid) - return matchValid; - } else { - matchValid = matchValid || rulesValid; - } - } - return matchValid; - }; - - bool subMatchResult = false; - for (auto const& match : matchList) { - if (matchSetMatches(match)) { - if (match->occlude) - *occlude = match->occlude.get(); - - subMatchResult = true; - - for (auto const& piecePair : match->resultingPieces) - resultList.append({piecePair.first, piecePair.second}); - - if (determineMatchingPieces(resultList, occlude, materialDb, match->subMatches, renderData, basePos, layer, isMod) && match->haltOnSubMatch) - break; - - if (match->haltOnMatch) - break; - } - } - - return subMatchResult; -} - float TilePainter::liquidDrawLevel(float liquidLevel) const { return clamp((liquidLevel - m_liquidDrawLevels[0]) / (m_liquidDrawLevels[1] - m_liquidDrawLevels[0]), 0.0f, 1.0f); } diff --git a/source/rendering/StarTilePainter.hpp b/source/rendering/StarTilePainter.hpp index 9e21850..3126a16 100644 --- a/source/rendering/StarTilePainter.hpp +++ b/source/rendering/StarTilePainter.hpp @@ -6,6 +6,7 @@ #include "StarMaterialRenderProfile.hpp" #include "StarRenderer.hpp" #include "StarWorldCamera.hpp" +#include "StarTileDrawer.hpp" namespace Star { @@ -13,7 +14,7 @@ STAR_CLASS(Assets); STAR_CLASS(MaterialDatabase); STAR_CLASS(TilePainter); -class TilePainter { +class TilePainter : public TileDrawer { public: // The rendered tiles are split and cached in chunks of RenderChunkSize x // RenderChunkSize. This means that, around the border, there may be as many @@ -62,13 +63,10 @@ private: typedef HashMap> TerrainChunk; typedef HashMap LiquidChunk; - typedef size_t MaterialRenderPieceIndex; typedef tuple MaterialPieceTextureKey; typedef String AssetTextureKey; typedef Variant TextureKey; - typedef List> MaterialPieceResultList; - struct TextureKeyHash { size_t operator()(TextureKey const& key) const; }; @@ -80,15 +78,6 @@ private: static ChunkHash terrainChunkHash(WorldRenderData& renderData, Vec2I chunkIndex); static ChunkHash liquidChunkHash(WorldRenderData& renderData, Vec2I chunkIndex); - static QuadZLevel materialZLevel(uint32_t zLevel, MaterialId material, MaterialHue hue, MaterialColorVariant colorVariant); - static QuadZLevel modZLevel(uint32_t zLevel, ModId mod, MaterialHue hue, MaterialColorVariant colorVariant); - static QuadZLevel damageZLevel(); - - static RenderTile const& getRenderTile(WorldRenderData const& renderData, Vec2I const& worldPos); - - template - static void forEachRenderTile(WorldRenderData const& renderData, RectI const& worldCoordRange, Function&& function); - void renderTerrainChunks(WorldCamera const& camera, TerrainLayer terrainLayer); shared_ptr getTerrainChunk(WorldRenderData& renderData, Vec2I chunkIndex); @@ -98,17 +87,10 @@ private: TerrainLayer terrainLayer, Vec2I const& pos, WorldRenderData const& renderData); void produceLiquidPrimitives(HashMap>& primitives, Vec2I const& pos, WorldRenderData const& renderData); - bool determineMatchingPieces(MaterialPieceResultList& resultList, bool* occlude, MaterialDatabaseConstPtr const& materialDb, MaterialRenderMatchList const& matchList, - WorldRenderData const& renderData, Vec2I const& basePos, TileLayer layer, bool isMod); - float liquidDrawLevel(float liquidLevel) const; List m_liquids; - Vec4B m_backgroundLayerColor; - Vec4B m_foregroundLayerColor; - Vec2F m_liquidDrawLevels; - RendererPtr m_renderer; TextureGroupPtr m_textureGroup; @@ -123,18 +105,6 @@ private: Vec2F m_cameraPan; }; -template -void TilePainter::forEachRenderTile(WorldRenderData const& renderData, RectI const& worldCoordRange, Function&& function) { - RectI indexRect = RectI::withSize(renderData.geometry.diff(worldCoordRange.min(), renderData.tileMinPosition), worldCoordRange.size()); - indexRect.limit(RectI::withSize(Vec2I(0, 0), Vec2I(renderData.tiles.size()))); - - if (!indexRect.isEmpty()) { - renderData.tiles.forEach(Array2S(indexRect.min()), Array2S(indexRect.size()), [&](Array2S const& index, RenderTile const& tile) { - return function(worldCoordRange.min() + (Vec2I(index) - indexRect.min()), tile); - }); - } -} - } #endif