431a9c00a5
On Linux and macOS, using Clang to compile OpenStarbound produces about 400 MB worth of warnings during the build, making the compiler output unreadable and slowing the build down considerably. 99% of the warnings were unqualified uses of std::move and std::forward, which are now all properly qualified. Fixed a few other minor warnings about non-virtual destructors and some uses of std::move preventing copy elision on temporary objects. Most remaining warnings are now unused parameters.
263 lines
9.1 KiB
C++
263 lines
9.1 KiB
C++
#include "StarParallax.hpp"
|
|
#include "StarLexicalCast.hpp"
|
|
#include "StarJsonExtra.hpp"
|
|
#include "StarRoot.hpp"
|
|
#include "StarImageMetadataDatabase.hpp"
|
|
#include "StarRandom.hpp"
|
|
#include "StarAssets.hpp"
|
|
#include "StarDataStreamExtra.hpp"
|
|
|
|
namespace Star {
|
|
|
|
ParallaxLayer::ParallaxLayer() {
|
|
timeOfDayCorrelation = "";
|
|
zLevel = 0;
|
|
verticalOrigin = 0;
|
|
speed = { 0, 0 };
|
|
unlit = false;
|
|
lightMapped = false;
|
|
fadePercent = 0;
|
|
directives = "";
|
|
alpha = 1.0f;
|
|
}
|
|
|
|
ParallaxLayer::ParallaxLayer(Json const& store) : ParallaxLayer() {
|
|
textures = jsonToStringList(store.get("textures"));
|
|
directives = store.getString("directives");
|
|
parallaxValue = jsonToVec2F(store.get("parallaxValue"));
|
|
repeat = jsonToVec2B(store.get("repeat"));
|
|
tileLimitTop = store.optFloat("tileLimitTop");
|
|
tileLimitBottom = store.optFloat("tileLimitBottom");
|
|
verticalOrigin = store.getFloat("verticalOrigin");
|
|
zLevel = store.getFloat("zLevel");
|
|
parallaxOffset = jsonToVec2F(store.get("parallaxOffset"));
|
|
timeOfDayCorrelation = store.getString("timeOfDayCorrelation");
|
|
speed = { store.getFloat("speed"), store.getFloat("speedY", 0.0f) };
|
|
unlit = store.getBool("unlit");
|
|
lightMapped = store.getBool("lightMapped");
|
|
fadePercent = store.getFloat("fadePercent");
|
|
}
|
|
|
|
Json ParallaxLayer::store() const {
|
|
return JsonObject{
|
|
{"textures", jsonFromStringList(textures)},
|
|
{"directives", directives.string()},
|
|
{"parallaxValue", jsonFromVec2F(parallaxValue)},
|
|
{"repeat", jsonFromVec2B(repeat)},
|
|
{"tileLimitTop", jsonFromMaybe(tileLimitTop)},
|
|
{"tileLimitBottom", jsonFromMaybe(tileLimitBottom)},
|
|
{"verticalOrigin", verticalOrigin},
|
|
{"zLevel", zLevel},
|
|
{"parallaxOffset", jsonFromVec2F(parallaxOffset)},
|
|
{"timeOfDayCorrelation", timeOfDayCorrelation},
|
|
{"speed", speed[0]},
|
|
{"speedY", speed[1]},
|
|
{"unlit", unlit},
|
|
{"lightMapped", lightMapped},
|
|
{"fadePercent", fadePercent}};
|
|
}
|
|
|
|
void ParallaxLayer::addImageDirectives(Directives const& newDirectives) {
|
|
if (newDirectives) { // TODO: Move to Directives +=
|
|
if (directives) {
|
|
String dirString = directives.string();
|
|
|
|
auto& newString = newDirectives.shared->string;
|
|
if (!newString.empty()) {
|
|
if (newString.utf8().front() != '?')
|
|
dirString += "?";
|
|
dirString += newString;
|
|
}
|
|
|
|
directives = std::move(dirString);
|
|
}
|
|
else
|
|
directives = newDirectives;
|
|
}
|
|
}
|
|
|
|
void ParallaxLayer::fadeToSkyColor(Color skyColor) {
|
|
if (fadePercent > 0) {
|
|
String fade = "fade=" + skyColor.toHex().slice(0, 6) + "=" + toString(fadePercent);
|
|
addImageDirectives(fade);
|
|
}
|
|
}
|
|
|
|
DataStream& operator>>(DataStream& ds, ParallaxLayer& parallaxLayer) {
|
|
ds.read(parallaxLayer.textures);
|
|
ds.read(parallaxLayer.directives);
|
|
ds.read(parallaxLayer.alpha);
|
|
ds.read(parallaxLayer.parallaxValue);
|
|
ds.read(parallaxLayer.repeat);
|
|
ds.read(parallaxLayer.tileLimitTop);
|
|
ds.read(parallaxLayer.tileLimitBottom);
|
|
ds.read(parallaxLayer.verticalOrigin);
|
|
ds.read(parallaxLayer.zLevel);
|
|
ds.read(parallaxLayer.parallaxOffset);
|
|
ds.read(parallaxLayer.timeOfDayCorrelation);
|
|
ds.read(parallaxLayer.speed);
|
|
ds.read(parallaxLayer.unlit);
|
|
ds.read(parallaxLayer.lightMapped);
|
|
ds.read(parallaxLayer.fadePercent);
|
|
return ds;
|
|
}
|
|
|
|
DataStream& operator<<(DataStream& ds, ParallaxLayer const& parallaxLayer) {
|
|
ds.write(parallaxLayer.textures);
|
|
ds.write(parallaxLayer.directives);
|
|
ds.write(parallaxLayer.alpha);
|
|
ds.write(parallaxLayer.parallaxValue);
|
|
ds.write(parallaxLayer.repeat);
|
|
ds.write(parallaxLayer.tileLimitTop);
|
|
ds.write(parallaxLayer.tileLimitBottom);
|
|
ds.write(parallaxLayer.verticalOrigin);
|
|
ds.write(parallaxLayer.zLevel);
|
|
ds.write(parallaxLayer.parallaxOffset);
|
|
ds.write(parallaxLayer.timeOfDayCorrelation);
|
|
ds.write(parallaxLayer.speed);
|
|
ds.write(parallaxLayer.unlit);
|
|
ds.write(parallaxLayer.lightMapped);
|
|
ds.write(parallaxLayer.fadePercent);
|
|
return ds;
|
|
}
|
|
|
|
Parallax::Parallax(String const& assetFile,
|
|
uint64_t seed,
|
|
float verticalOrigin,
|
|
float hueShift,
|
|
Maybe<TreeVariant> parallaxTreeVariant) {
|
|
m_seed = seed;
|
|
m_verticalOrigin = verticalOrigin;
|
|
m_parallaxTreeVariant = parallaxTreeVariant;
|
|
m_hueShift = hueShift;
|
|
m_imageDirectory = "/parallax/images/";
|
|
|
|
auto assets = Root::singleton().assets();
|
|
|
|
Json config = assets->json(assetFile);
|
|
|
|
m_verticalOrigin += config.getFloat("verticalOrigin", 0);
|
|
|
|
RandomSource rnd(m_seed);
|
|
|
|
for (auto layerSettings : config.getArray("layers")) {
|
|
String kind = layerSettings.getString("kind");
|
|
|
|
float frequency = layerSettings.getFloat("frequency", 1.0);
|
|
if (rnd.randf() > frequency)
|
|
continue;
|
|
|
|
buildLayer(layerSettings, kind);
|
|
}
|
|
|
|
// sort with highest Z level first
|
|
stableSort(m_layers, [](ParallaxLayer const& a, ParallaxLayer const& b) { return a.zLevel > b.zLevel; });
|
|
}
|
|
|
|
Parallax::Parallax(Json const& store) {
|
|
m_seed = store.getUInt("seed");
|
|
m_verticalOrigin = store.getFloat("verticalOrigin");
|
|
if (auto treeVariant = store.opt("parallaxTreeVariant"))
|
|
m_parallaxTreeVariant = TreeVariant(*treeVariant);
|
|
m_hueShift = store.getFloat("hueShift");
|
|
m_imageDirectory = store.getString("imageDirectory");
|
|
m_layers = store.getArray("layers").transformed(construct<ParallaxLayer>());
|
|
|
|
stableSort(m_layers, [](ParallaxLayer const& a, ParallaxLayer const& b) { return a.zLevel > b.zLevel; });
|
|
}
|
|
|
|
Json Parallax::store() const {
|
|
return JsonObject{{"seed", m_seed},
|
|
{"verticalOrigin", m_verticalOrigin},
|
|
{"parallaxTreeVariant", m_parallaxTreeVariant ? m_parallaxTreeVariant->toJson() : Json()},
|
|
{"hueShift", m_hueShift},
|
|
{"imageDirectory", m_imageDirectory},
|
|
{"layers", m_layers.transformed(mem_fn(&ParallaxLayer::store))}};
|
|
}
|
|
|
|
void Parallax::fadeToSkyColor(Color const& skyColor) {
|
|
for (auto& layer : m_layers) {
|
|
layer.fadeToSkyColor(skyColor);
|
|
}
|
|
}
|
|
|
|
ParallaxLayers const& Parallax::layers() const {
|
|
return m_layers;
|
|
}
|
|
|
|
void Parallax::buildLayer(Json const& layerSettings, String const& kind) {
|
|
bool isFoliage = kind.beginsWith("foliage/");
|
|
bool isStem = kind.beginsWith("stem/");
|
|
|
|
String texPath = m_imageDirectory + kind + "/";
|
|
if (isFoliage) {
|
|
// If our tree type has no foliage, don't construct this layer
|
|
if (!m_parallaxTreeVariant || !m_parallaxTreeVariant->foliageSettings.getBool("parallaxFoliage", false))
|
|
return;
|
|
|
|
texPath = m_parallaxTreeVariant->foliageDirectory + "parallax/" + kind.replace("foliage/", "") + "/";
|
|
} else if (isStem) {
|
|
if (!m_parallaxTreeVariant)
|
|
return;
|
|
|
|
texPath = m_parallaxTreeVariant->stemDirectory + "parallax/" + kind.replace("stem/", "") + "/";
|
|
}
|
|
|
|
ParallaxLayer layer;
|
|
RandomSource rnd(m_seed + m_layers.size());
|
|
auto imgMetadata = Root::singleton().imageMetadataDatabase();
|
|
|
|
int baseCount = layerSettings.getInt("baseCount", 1);
|
|
int base = rnd.randInt(baseCount - 1) + 1;
|
|
layer.textures.append(AssetPath::relativeTo(texPath, "base/" + toString<int>(base) + ".png"));
|
|
|
|
int modCount = layerSettings.getInt("modCount", 0);
|
|
int mod = rnd.randInt(modCount);
|
|
if (mod != 0)
|
|
layer.textures.append(AssetPath::relativeTo(texPath, "mod/" + toString<int>(mod) + ".png"));
|
|
|
|
if (layerSettings.get("parallax").type() == Json::Type::Array)
|
|
layer.parallaxValue = jsonToVec2F(layerSettings.get("parallax"));
|
|
else
|
|
layer.parallaxValue = Vec2F::filled(layerSettings.getFloat("parallax"));
|
|
|
|
bool repeatY = layerSettings.getBool("repeatY", false);
|
|
layer.repeat = {true, repeatY};
|
|
if (repeatY) {
|
|
layer.tileLimitTop = layerSettings.optFloat("tileLimitTop");
|
|
layer.tileLimitBottom = layerSettings.optFloat("tileLimitBottom");
|
|
}
|
|
|
|
layer.verticalOrigin = m_verticalOrigin;
|
|
layer.zLevel = layer.parallaxValue.sum();
|
|
auto offset = layerSettings.getArray("offset", { 0, 0 });
|
|
layer.parallaxOffset = { offset[0].toFloat(), offset[1].toFloat() }; // shift from bottom left to horizon level in the image
|
|
if (!layerSettings.getBool("noRandomOffset", false))
|
|
layer.parallaxOffset[0] += rnd.randInt(imgMetadata->imageSize(layer.textures[0])[0]);
|
|
layer.timeOfDayCorrelation = layerSettings.getString("timeOfDayCorrelation", "");
|
|
auto minSpeed = layerSettings.get("minSpeed", 0.0f);
|
|
auto maxSpeed = layerSettings.get("maxSpeed", 0.0f);
|
|
if (!minSpeed.isType(Json::Type::Array))
|
|
minSpeed = JsonArray{ minSpeed, 0.0f };
|
|
if (!maxSpeed.isType(Json::Type::Array))
|
|
maxSpeed = JsonArray{ maxSpeed, 0.0f };
|
|
layer.speed = { rnd.randf(minSpeed.getFloat(0), maxSpeed.getFloat(0)),
|
|
rnd.randf(minSpeed.getFloat(1), maxSpeed.getFloat(1)) };
|
|
layer.unlit = layerSettings.getBool("unlit", false);
|
|
layer.lightMapped = layerSettings.getBool("lightMapped", false);
|
|
|
|
layer.addImageDirectives(layerSettings.getString("directives", ""));
|
|
if (isFoliage)
|
|
layer.addImageDirectives(String(strf("hueshift={}", m_parallaxTreeVariant->foliageHueShift)));
|
|
else if (isStem)
|
|
layer.addImageDirectives(String(strf("hueshift={}", m_parallaxTreeVariant->stemHueShift)));
|
|
else if (!layerSettings.getBool("nohueshift", false))
|
|
layer.addImageDirectives(String(strf("hueshift={}", m_hueShift)));
|
|
|
|
layer.fadePercent = layerSettings.getFloat("fadePercent", 0);
|
|
|
|
m_layers.append(layer);
|
|
}
|
|
|
|
}
|