Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarTileDrawer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarTileDrawer.cpp')
-rw-r--r--source/game/StarTileDrawer.cpp311
1 files changed, 311 insertions, 0 deletions
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<TerrainLayer> 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<float, 2> 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<MaterialRule::RuleEmpty>()) {
+ valid = neighborMod == NoModId;
+ } else if (ruleEntry.rule.is<MaterialRule::RuleConnects>()) {
+ valid = isConnectableMaterial(neighborMaterial);
+ } else if (ruleEntry.rule.is<MaterialRule::RuleShadows>()) {
+ valid = neighborShadowing;
+ } else if (auto equalsSelf = ruleEntry.rule.ptr<MaterialRule::RuleEqualsSelf>()) {
+ valid = neighborMod == baseMod;
+ if (equalsSelf->matchHue)
+ valid = valid && baseModHue == neighborModHue;
+ } else if (auto equalsId = ruleEntry.rule.ptr<MaterialRule::RuleEqualsId>()) {
+ valid = neighborMod == equalsId->id;
+ } else if (auto propertyEquals = ruleEntry.rule.ptr<MaterialRule::RulePropertyEquals>()) {
+ if (auto profile = materialDb->modRenderProfile(neighborMod))
+ valid = profile->ruleProperties.get(propertyEquals->propertyName, Json()) == propertyEquals->compare;
+ else
+ valid = false;
+ }
+ } else {
+ if (ruleEntry.rule.is<MaterialRule::RuleEmpty>()) {
+ valid = neighborMaterial == EmptyMaterialId;
+ } else if (ruleEntry.rule.is<MaterialRule::RuleConnects>()) {
+ valid = isConnectableMaterial(neighborMaterial);
+ } else if (ruleEntry.rule.is<MaterialRule::RuleShadows>()) {
+ valid = neighborShadowing;
+ } else if (auto equalsSelf = ruleEntry.rule.ptr<MaterialRule::RuleEqualsSelf>()) {
+ valid = neighborMaterial == baseMaterial;
+ if (equalsSelf->matchHue)
+ valid = valid && baseHue == neighborHue;
+ } else if (auto equalsId = ruleEntry.rule.ptr<MaterialRule::RuleEqualsId>()) {
+ valid = neighborMaterial == equalsId->id;
+ } else if (auto propertyEquals = ruleEntry.rule.ptr<MaterialRule::RulePropertyEquals>()) {
+ 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