diff options
Diffstat (limited to 'source/game/StarCelestialGraphics.cpp')
-rw-r--r-- | source/game/StarCelestialGraphics.cpp | 288 |
1 files changed, 288 insertions, 0 deletions
diff --git a/source/game/StarCelestialGraphics.cpp b/source/game/StarCelestialGraphics.cpp new file mode 100644 index 0000000..3e0c02e --- /dev/null +++ b/source/game/StarCelestialGraphics.cpp @@ -0,0 +1,288 @@ +#include "StarCelestialGraphics.hpp" +#include "StarJsonExtra.hpp" +#include "StarLexicalCast.hpp" +#include "StarFormat.hpp" +#include "StarImageProcessing.hpp" +#include "StarCelestialDatabase.hpp" +#include "StarParallax.hpp" +#include "StarRoot.hpp" +#include "StarBiomeDatabase.hpp" +#include "StarTerrainDatabase.hpp" +#include "StarLiquidsDatabase.hpp" +#include "StarAssets.hpp" + +namespace Star { + +List<pair<String, float>> CelestialGraphics::drawSystemPlanetaryObject(CelestialParameters const& parameters) { + return {{parameters.getParameter("smallImage").toString(), parameters.getParameter("smallImageScale").toFloat()}}; +} + +List<pair<String, float>> CelestialGraphics::drawSystemCentralBody(CelestialParameters const& parameters) { + return {{parameters.getParameter("image").toString(), parameters.getParameter("imageScale").toFloat()}}; +} + +List<pair<String, float>> CelestialGraphics::drawWorld( + CelestialParameters const& celestialParameters, Maybe<CelestialParameters> const& overrideShadowParameters) { + auto& root = Root::singleton(); + auto assets = root.assets(); + auto liquidsDatabase = root.liquidsDatabase(); + + CelestialParameters shadowParameters = overrideShadowParameters.value(celestialParameters); + + String type = celestialParameters.getParameter("worldType").toString(); + + List<pair<String, float>> layers; + + if (type == "Terrestrial") { + auto terrestrialParameters = as<TerrestrialWorldParameters>(celestialParameters.visitableParameters()); + if (!terrestrialParameters) + return {}; + + auto gfxConfig = jsonMerge(assets->json("/celestial.config:terrestrialGraphics").get("default"), + assets->json("/celestial.config:terrestrialGraphics").get(terrestrialParameters->typeName, JsonObject())); + + auto liquidImages = gfxConfig.getString("liquidImages", ""); + auto baseImages = gfxConfig.getString("baseImages", ""); + auto shadowImages = gfxConfig.getString("shadowImages", ""); + auto baseCount = gfxConfig.getInt("baseCount", 0); + auto dynamicsImages = gfxConfig.getString("dynamicsImages", ""); + float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat(); + + // If the planet has water, then draw the corresponding water image as the + // base layer, otherwise use the bottom most mask image. + if (terrestrialParameters->primarySurfaceLiquid != EmptyLiquidId && !liquidImages.empty()) { + String liquidBaseImage = liquidImages.replace("<liquid>", liquidsDatabase->liquidName(terrestrialParameters->primarySurfaceLiquid)); + layers.append({move(liquidBaseImage), imageScale}); + } else { + if (baseCount > 0) { + String baseLayer = strf("%s?hueshift=%s", baseImages.replace("<biome>", + terrestrialParameters->primaryBiome).replace("<num>", toString(baseCount)), terrestrialParameters->hueShift); + layers.append({move(baseLayer), imageScale}); + } + } + + // Then draw all the biome layers + for (int i = 0; i < baseCount; ++i) { + String baseImage = baseImages.replace("<num>", toString(baseCount - i)); + String hueShiftString, dynamicMaskString; + if (!dynamicsImages.empty()) + dynamicMaskString = "?addmask=" + dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange(gfxConfig.getArray("dynamicsRange"), i).toInt())); + if (terrestrialParameters->hueShift != 0) + hueShiftString = strf("?hueshift=%s", terrestrialParameters->hueShift); + String layer = baseImage + hueShiftString + dynamicMaskString; + layers.append({move(layer), imageScale}); + } + + if (!shadowImages.empty()) { + String shadow = shadowImages.replace("<num>", toString(shadowParameters.randomizeParameterRange(gfxConfig.getArray("shadowNumber")).toInt())); + layers.append({move(shadow), imageScale}); + } + + } else if (type == "Asteroids") { + String maskImages = celestialParameters.getParameter("maskImages").toString(); + int maskCount = celestialParameters.getParameter("masks").toInt(); + String dynamicsImages = celestialParameters.getParameter("dynamicsImages").toString(); + float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat(); + + for (int i = 0; i < maskCount; ++i) { + String biomeMaskBase = maskImages.replace("<num>", toString(maskCount - i)); + String dynamicMask = dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange("dynamicsRange", i).toInt())); + String layer = strf("%s?addmask=%s", biomeMaskBase, dynamicMask); + layers.append({move(layer), imageScale}); + } + + } else if (type == "FloatingDungeon") { + String image = celestialParameters.getParameter("image").toString(); + float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat(); + layers.append({move(image), imageScale}); + + if (!celestialParameters.getParameter("dynamicsImages").toString().empty()) { + String dynamicsImages = celestialParameters.getParameter("dynamicsImages", "").toString(); + String dynamicsImage = dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange("dynamicsRange").toInt())); + layers.append({move(dynamicsImage), imageScale}); + } + + } else if (type == "GasGiant") { + auto gfxConfig = assets->json("/celestial.config:gasGiantGraphics"); + + auto baseImage = gfxConfig.getString("baseImage", ""); + auto shadowImages = gfxConfig.getString("shadowImages", ""); + auto dynamicsImages = gfxConfig.getString("dynamicsImages", ""); + auto overlayImages = gfxConfig.getString("overlayImages", ""); + auto overlayCount = gfxConfig.getInt("overlayCount", 0); + float imageScale = celestialParameters.getParameter("imageScale", 1.0f).toFloat(); + + float hueShift = celestialParameters.randomizeParameterRange(gfxConfig.getArray("primaryHueShiftRange")).toFloat(); + if (!baseImage.empty()) + layers.append({strf("%s?hueshift=%s", baseImage, hueShift), imageScale}); + + if (!overlayImages.empty()) { + for (int i = 0; i < overlayCount; ++i) { + hueShift += celestialParameters.randomizeParameterRange(gfxConfig.getArray("hueShiftOffsetRange")).toFloat(); + String maskImage = dynamicsImages.replace("<num>", toString(celestialParameters.randomizeParameterRange(gfxConfig.getArray("dynamicsRange"), i).toInt())); + String overlayImage = overlayImages.replace("<num>", toString(i)); + layers.append({strf("%s?hueshift=%s?addmask=%s", overlayImage, hueShift, maskImage), imageScale}); + } + } + + if (!shadowImages.empty()) { + String shadow = shadowImages.replace("<num>", toString(shadowParameters.randomizeParameterRange(gfxConfig.getArray("shadowNumber")).toInt())); + layers.append({move(shadow), imageScale}); + } + } + + return layers; +} + +List<pair<String, String>> CelestialGraphics::worldHorizonImages(CelestialParameters const& celestialParameters) { + auto& root = Root::singleton(); + auto assets = root.assets(); + auto liquidsDatabase = root.liquidsDatabase(); + + auto getLR = [](String const& base) -> pair<String, String> { + return pair<String, String>(base.replace("<selector>", "l"), base.replace("<selector>", "r")); + }; + + String type = celestialParameters.getParameter("worldType").toString(); + + List<pair<String, String>> res; + + if (type == "Terrestrial") { + auto terrestrialParameters = as<TerrestrialWorldParameters>(celestialParameters.visitableParameters()); + if (!terrestrialParameters) + return {}; + + auto gfxConfig = jsonMerge(assets->json("/celestial.config:terrestrialHorizonGraphics").get("default"), + assets->json("/celestial.config:terrestrialHorizonGraphics").get(terrestrialParameters->typeName, JsonObject())); + + String baseImages = gfxConfig.getString("baseImages"); + String atmoTextures = gfxConfig.getString("atmosphereTextures"); + String shadowTextures = gfxConfig.getString("shadowTextures"); + String maskTextures = gfxConfig.getString("maskTextures"); + String liquidTextures = gfxConfig.getString("liquidTextures"); + auto numMasks = jsonToVec2I(gfxConfig.get("maskRange")); + auto maskPerPlanetRange = jsonToVec2I(gfxConfig.get("maskPerPlanetRange")); + + auto biomeHueShift = "?" + imageOperationToString(HueShiftImageOperation::hueShiftDegrees(terrestrialParameters->hueShift)); + + if (terrestrialParameters->primarySurfaceLiquid != EmptyLiquidId) { + auto seed = celestialParameters.seed(); + RandomSource rand(seed); + + int numPlanetMasks = rand.randInt(maskPerPlanetRange[0], maskPerPlanetRange[1]); + List<int> masks; + for (int i = 0; i < numPlanetMasks; ++i) + masks.append(rand.randInt(numMasks[0], numMasks[1])); + + String liquidBase = liquidTextures.replace("<liquid>", liquidsDatabase->liquidName(terrestrialParameters->primarySurfaceLiquid)); + res.append(getLR(liquidBase)); + + StringList planetMaskListL; + StringList planetMaskListR; + for (auto m : masks) { + String base = maskTextures.replace("<mask>", toString(m)); + auto lr = getLR(base); + planetMaskListL.append(lr.first); + planetMaskListR.append(lr.second); + } + + String leftMask, rightMask; + if (!planetMaskListL.empty()) + leftMask = "?" + imageOperationToString(AlphaMaskImageOperation{AlphaMaskImageOperation::Additive, planetMaskListL, {0, 0}}); + if (!planetMaskListR.empty()) + rightMask = "?" + imageOperationToString(AlphaMaskImageOperation{AlphaMaskImageOperation::Additive, planetMaskListR, {0, 0}}); + + auto toAppend = getLR(baseImages + biomeHueShift); + res.append({toAppend.first + leftMask, toAppend.second + rightMask}); + } else { + res.append(getLR(baseImages + biomeHueShift)); + } + + if (celestialParameters.getParameter("atmosphere", true).toBool()) + res.append(getLR(atmoTextures)); + + res.append(getLR(shadowTextures)); + + } else if (type == "Asteroids") { + res.append(getLR(assets->json("/celestial.config:asteroidsHorizons").toString())); + + } else if (type == "FloatingDungeon") { + auto dungeonParameters = as<FloatingDungeonWorldParameters>(celestialParameters.visitableParameters()); + auto dungeonHorizons = assets->json("/celestial.config:floatingDungeonHorizons"); + if (dungeonHorizons.contains(dungeonParameters->primaryDungeon)) + res.append(getLR(dungeonHorizons.get(dungeonParameters->primaryDungeon).toString())); + } + + return res; +} + +int CelestialGraphics::worldRadialPosition(CelestialParameters const& parameters) { + if (parameters.coordinate().isPlanetaryBody()) + return staticRandomU32(parameters.seed(), "RadialNumber") % planetRadialPositions(); + if (parameters.coordinate().isSatelliteBody()) + return staticRandomU32(parameters.seed(), "RadialNumber") % satelliteRadialPositions(); + return 0; +} + +int CelestialGraphics::planetRadialPositions() { + return Root::singleton().assets()->json("/celestial.config:planetRadialSlots").toInt(); +} + +int CelestialGraphics::satelliteRadialPositions() { + return Root::singleton().assets()->json("/celestial.config:satelliteRadialSlots").toInt(); +} + +List<pair<String, float>> CelestialGraphics::drawSystemTwinkle(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& system, double time) { + auto parameters = celestialDatabase->parameters(system); + if (!parameters) + return {}; + + auto assets = Root::singleton().assets(); + + int twinkleFrameCount = assets->json("/celestial.config:twinkleFrames").toInt(); + float twinkleScale = assets->json("/celestial.config:twinkleScale").toFloat(); + String twinkleFrameset = parameters->getParameter("twinkleFrames").toString(); + float twinkleTime = parameters->randomizeParameterRange("twinkleTime").toFloat(); + String twinkleBackground = parameters->getParameter("twinkleBackground").toString(); + + String twinkleFrame = strf("%s:%s", twinkleFrameset, (int)(std::fmod<double>(time / twinkleTime, 1.0f) * twinkleFrameCount)); + + return {{move(twinkleBackground), 1.0f}, {move(twinkleFrame), twinkleScale}}; +} + +List<pair<String, float>> CelestialGraphics::drawSystemPlanetaryObject(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) { + if (auto params = celestialDatabase->parameters(coordinate)) + return drawSystemPlanetaryObject(params.take()); + return {}; +} + +List<pair<String, float>> CelestialGraphics::drawSystemCentralBody(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) { + if (auto params = celestialDatabase->parameters(coordinate)) + return drawSystemCentralBody(params.take()); + return {}; +} + +List<pair<String, float>> CelestialGraphics::drawWorld(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) { + auto params = celestialDatabase->parameters(coordinate); + if (!params) + return {}; + + if (coordinate.isSatelliteBody()) + return drawWorld(params.take(), celestialDatabase->parameters(coordinate.parent())); + else + return drawWorld(params.take()); +} + +List<pair<String, String>> CelestialGraphics::worldHorizonImages(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) { + if (auto params = celestialDatabase->parameters(coordinate)) + return worldHorizonImages(params.take()); + return {}; +} + +int CelestialGraphics::worldRadialPosition(CelestialDatabasePtr celestialDatabase, CelestialCoordinate const& coordinate) { + if (auto params = celestialDatabase->parameters(coordinate)) + return worldRadialPosition(params.take()); + return 0; +} + +} |