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

summaryrefslogtreecommitdiff
path: root/source/game/StarMaterialRenderProfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/game/StarMaterialRenderProfile.cpp')
-rw-r--r--source/game/StarMaterialRenderProfile.cpp170
1 files changed, 170 insertions, 0 deletions
diff --git a/source/game/StarMaterialRenderProfile.cpp b/source/game/StarMaterialRenderProfile.cpp
new file mode 100644
index 0000000..39012f1
--- /dev/null
+++ b/source/game/StarMaterialRenderProfile.cpp
@@ -0,0 +1,170 @@
+#include "StarMaterialRenderProfile.hpp"
+#include "StarLexicalCast.hpp"
+#include "StarJsonExtra.hpp"
+#include "StarAssets.hpp"
+#include "StarImageMetadataDatabase.hpp"
+#include "StarRoot.hpp"
+
+namespace Star {
+
+EnumMap<MaterialJoinType> const MaterialJoinTypeNames = {
+ {MaterialJoinType::All, "All"}, {MaterialJoinType::Any, "Any"},
+};
+
+MaterialRenderMatchList parseMaterialRenderMatchList(Json const& matchSpec, RuleMap const& ruleMap, PieceMap const& pieceMap, MatchMap const& matchMap) {
+ MaterialRenderMatchList matchList;
+
+ for (auto const& matchConfig : matchSpec.toArray()) {
+ MaterialRenderMatchPtr match = make_shared<MaterialRenderMatch>();
+
+ Json matchPointList = JsonArray();
+ if (auto matchAllPoints = matchConfig.opt("matchAllPoints")) {
+ matchPointList = *matchAllPoints;
+ match->matchJoin = MaterialJoinType::All;
+ } else if (auto matchAnyPoints = matchConfig.opt("matchAnyPoints")) {
+ matchPointList = *matchAnyPoints;
+ match->matchJoin = MaterialJoinType::Any;
+ }
+
+ for (auto const& matchPointConfig : matchPointList.iterateArray()) {
+ MaterialMatchPoint matchPoint;
+ matchPoint.position = jsonToVec2I(matchPointConfig.get(0));
+ if (abs(matchPoint.position[0]) > MaterialRenderProfileMaxNeighborDistance || abs(matchPoint.position[1]) > MaterialRenderProfileMaxNeighborDistance)
+ throw MaterialRenderProfileException(strf("Match position %s outside of maximum rule distance %s",
+ matchPoint.position, MaterialRenderProfileMaxNeighborDistance));
+ matchPoint.rule = ruleMap.get(matchPointConfig.getString(1));
+ match->matchPoints.append(move(matchPoint));
+ }
+
+ for (auto const& piece : matchConfig.getArray("pieces", {}))
+ match->resultingPieces.append({pieceMap.get(piece.getString(0)), jsonToVec2F(piece.get(1))});
+
+ auto subMatches = matchConfig.get("subMatches", {});
+ if (subMatches.isType(Json::Type::String))
+ match->subMatches = matchMap.get(subMatches.toString());
+ else if (!subMatches.isNull())
+ match->subMatches = parseMaterialRenderMatchList(subMatches, ruleMap, pieceMap, matchMap);
+
+ match->requiredLayer = matchConfig.optString("requiredLayer").apply(bind(&EnumMap<TileLayer>::getLeft, &TileLayerNames, _1));
+ match->haltOnMatch = matchConfig.getBool("haltOnMatch", false);
+ match->haltOnSubMatch = matchConfig.getBool("haltOnSubMatch", false);
+
+ match->occlude = matchConfig.optBool("occlude");
+
+ matchList.append(match);
+ }
+
+ return matchList;
+}
+
+String MaterialRenderProfile::pieceImage(String const& pieceName, unsigned variant, MaterialColorVariant colorVariant, MaterialHue hueShift) const {
+ auto const& piece = pieces.get(pieceName);
+
+ String texture = piece->texture;
+ if (hueShift != MaterialHue())
+ texture = strf("%s?hueshift=%s", texture, materialHueToDegrees(hueShift));
+
+ auto const& rect = piece->variants.get(colorVariant).wrap(variant);
+ return strf("%s?crop=%s;%s;%s;%s", texture, rect.xMin(), rect.yMin(), rect.xMax(), rect.yMax());
+}
+
+pair<String, Vec2F> const& MaterialRenderProfile::damageImage(float damageLevel, TileDamageType damageType) const {
+ if (damageType == TileDamageType::Protected)
+ return protectedFrames.at(clamp<unsigned>(damageLevel * crackingFrames.size(), 0, crackingFrames.size() - 1));
+ return crackingFrames.at(clamp<unsigned>(damageLevel * crackingFrames.size(), 0, crackingFrames.size() - 1));
+}
+
+MaterialRenderProfile parseMaterialRenderProfile(Json const& spec, String const& relativePath) {
+ MaterialRenderProfile profile;
+
+ 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.occludesBehind = spec.getBool("occludesBelow", true);
+ profile.zLevel = spec.getUInt("zLevel", 0);
+ profile.radiantLight = Color::rgb(jsonToVec3B(spec.get("radiantLight", JsonArray{0, 0, 0}))).toRgbF();
+
+ profile.representativePiece = spec.getString("representativePiece");
+
+ for (auto const& pair : spec.get("rules").iterateObject()) {
+ auto rule = make_shared<MaterialRule>();
+ rule->join = MaterialJoinTypeNames.getLeft(pair.second.getString("join", "all"));
+ for (auto const& ruleEntry : pair.second.getArray("entries", {})) {
+ bool inverse = ruleEntry.getBool("inverse", false);
+ String type = ruleEntry.getString("type");
+ if (type.equalsIgnoreCase("Connects")) {
+ rule->entries.append({MaterialRule::RuleConnects(), inverse});
+ } else if (type.equalsIgnoreCase("Shadows")) {
+ rule->entries.append({MaterialRule::RuleShadows(), inverse});
+ } else if (type.equalsIgnoreCase("EqualsSelf")) {
+ rule->entries.append({MaterialRule::RuleEqualsSelf{ruleEntry.getBool("matchHue", false)}, inverse});
+ } else if (type.equalsIgnoreCase("EqualsId")) {
+ rule->entries.append({MaterialRule::RuleEqualsId{(uint16_t)ruleEntry.getUInt("id")}, inverse});
+ } else if (type.equalsIgnoreCase("PropertyEquals")) {
+ rule->entries.append({MaterialRule::RulePropertyEquals{ruleEntry.getString("propertyName"), ruleEntry.get("propertyValue")}, inverse});
+ }
+ }
+ profile.rules[pair.first] = move(rule);
+ }
+
+ for (auto const& pair : spec.get("pieces").iterateObject()) {
+ auto renderPiece = make_shared<MaterialRenderPiece>();
+ renderPiece->pieceId = profile.pieces.size();
+
+ renderPiece->texture = AssetPath::relativeTo(relativePath, pair.second.getString("texture", spec.getString("texture")));
+ unsigned variants = pair.second.getUInt("variants", spec.getUInt("variants", 1));
+
+ Vec2F textureSize = jsonToVec2F(pair.second.get("textureSize"));
+ Vec2F texturePosition = jsonToVec2F(pair.second.get("texturePosition"));
+ Vec2F variantStride = jsonToVec2F(pair.second.get("variantStride", JsonArray{0, 0}));
+ Vec2F colorStride = jsonToVec2F(pair.second.get("colorStride", JsonArray{0, 0}));
+
+ // Need to flip texture coordinates because material rendering configs
+ // assume top down image coordinates
+ unsigned imageHeight = Root::singleton().imageMetadataDatabase()->imageSize(renderPiece->texture)[1];
+ 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) {
+ RectF textureRect = RectF::withSize(texturePosition + variantStride * v + colorStride * c, textureSize);
+ renderPiece->variants[c].append(flipTextureCoordinates(textureRect));
+ }
+ } else {
+ RectF textureRect = RectF::withSize(texturePosition + variantStride * v, textureSize);
+ renderPiece->variants[DefaultMaterialColorVariant].append(flipTextureCoordinates(textureRect));
+ }
+ }
+
+ profile.pieces[pair.first] = move(renderPiece);
+ }
+
+ for (auto const& pair : spec.get("matches").iterateArray())
+ profile.matches[pair.getString(0)] = parseMaterialRenderMatchList(pair.get(1), profile.rules, profile.pieces, profile.matches);
+
+ profile.mainMatchList = profile.matches.get("main");
+
+ // TODO: Completely hard-coded for now
+ profile.crackingFrames = {
+ {"/tiles/blockdamage.png:1", Vec2F(0, 0)},
+ {"/tiles/blockdamage.png:2", Vec2F(0, 0)},
+ {"/tiles/blockdamage.png:3", Vec2F(0, 0)},
+ {"/tiles/blockdamage.png:4", Vec2F(0, 0)},
+ {"/tiles/blockdamage.png:5", Vec2F(0, 0)}
+ };
+
+ profile.protectedFrames = {
+ {"/tiles/blockprotection.png:1", Vec2F(0, 0)},
+ {"/tiles/blockprotection.png:2", Vec2F(0, 0)},
+ {"/tiles/blockprotection.png:3", Vec2F(0, 0)},
+ {"/tiles/blockprotection.png:4", Vec2F(0, 0)},
+ {"/tiles/blockprotection.png:5", Vec2F(0, 0)}
+ };
+
+ profile.ruleProperties = spec.get("ruleProperties", JsonObject());
+
+ return profile;
+}
+
+}