v1.4.4
This commit is contained in:
commit
9c94d113d3
10260 changed files with 1237388 additions and 0 deletions
170
source/game/StarMaterialRenderProfile.cpp
Normal file
170
source/game/StarMaterialRenderProfile.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue