mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 20:31:51 +09:00
cloud stage has more depth
This commit is contained in:
@@ -21,5 +21,6 @@
|
||||
"altLow": 1080, "altHigh": 1800
|
||||
}
|
||||
},
|
||||
"atmoTurbidity": 3.5
|
||||
"atmoTurbidity": 3.5,
|
||||
"shaderVibrancy": [1.0, 1.0]
|
||||
}
|
||||
@@ -27,5 +27,6 @@
|
||||
}
|
||||
},
|
||||
"atmoTurbidity": 3.5,
|
||||
"shaderVibrancy": [1.0, 1.0],
|
||||
"__comment__": "Make a texture for altocumulus so that this weather can be realised with less than 1000 sprites"
|
||||
}
|
||||
@@ -8,7 +8,7 @@
|
||||
"cloudGammaVariance": [0.0, 0.0],
|
||||
"windSpeed": 10.45,
|
||||
"windSpeedVariance": 0.5,
|
||||
"windSpeedDamping": 0.0,
|
||||
"windSpeedDamping": 0.5,
|
||||
"clouds": {
|
||||
"cumulus": {
|
||||
"filename": "cloud_normal.png", "tw": 1024, "th": 512, "probability": 0.1,
|
||||
@@ -26,5 +26,6 @@
|
||||
"altLow": 1100, "altHigh": 1500
|
||||
}
|
||||
},
|
||||
"atmoTurbidity": 9.5
|
||||
"atmoTurbidity": 9.5,
|
||||
"shaderVibrancy": [0.93, 0.9]
|
||||
}
|
||||
Binary file not shown.
@@ -13,6 +13,7 @@ import net.torvald.terrarum.App.IS_DEVELOPMENT_BUILD
|
||||
import net.torvald.terrarum.gamecontroller.KeyToggler
|
||||
import net.torvald.terrarum.ui.BasicDebugInfoWindow
|
||||
import net.torvald.terrarum.ui.Toolkit
|
||||
import net.torvald.terrarum.weather.WeatherMixer
|
||||
|
||||
/**
|
||||
* Must be called by the App Loader
|
||||
@@ -236,6 +237,13 @@ object TerrarumPostProcessor : Disposable {
|
||||
else
|
||||
shaderPostNoDither
|
||||
|
||||
val (vo, vg) = INGAME.world.weatherbox.let {
|
||||
if (it.currentWeather.identifier == "titlescreen")
|
||||
1f to 1f
|
||||
else
|
||||
it.currentVibrancy.x to it.currentVibrancy.y
|
||||
}
|
||||
|
||||
App.getCurrentDitherTex().bind(1)
|
||||
fbo.colorBufferTexture.bind(0)
|
||||
|
||||
@@ -245,6 +253,7 @@ object TerrarumPostProcessor : Disposable {
|
||||
shader.setUniformi("rnd", rng.nextInt(8192), rng.nextInt(8192))
|
||||
shader.setUniformi("u_pattern", 1)
|
||||
shader.setUniformf("quant", shaderQuant[App.getConfigInt("displaycolourdepth")] ?: 255f)
|
||||
shader.setUniformf("vibrancy", 1f, vo, vg, 1f)
|
||||
shader.setUniformMatrix4fv("swizzler", swizzler, rng.nextInt(24), 16*4)
|
||||
App.fullscreenQuad.render(shader, GL20.GL_TRIANGLES)
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ open class GameWorld(
|
||||
var weatherbox = Weatherbox()
|
||||
|
||||
init {
|
||||
weatherbox.initWith(WeatherMixer.weatherDict["generic01"]!!, 7200L)
|
||||
weatherbox.initWith(WeatherMixer.weatherDict["generic01"]!!, 3600L)
|
||||
val currentWeather = weatherbox.currentWeather
|
||||
// TEST FILL WITH RANDOM VALUES
|
||||
(0..6).map { WeatherMixer.takeUniformRand(0f..1f) }.let {
|
||||
@@ -165,12 +165,13 @@ open class GameWorld(
|
||||
weatherbox.windDir.p3 = it[6]
|
||||
}
|
||||
(0..6).map { WeatherMixer.takeUniformRand(-1f..1f) }.let {
|
||||
weatherbox.windSpeed.pM2 = currentWeather.getRandomWindSpeed(it[0], it[1])
|
||||
weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[1], it[2])
|
||||
weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[2], it[3])
|
||||
weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[3], it[4])
|
||||
weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(it[4], it[5])
|
||||
weatherbox.windSpeed.p3 = currentWeather.getRandomWindSpeed(it[5], it[6])
|
||||
val pM3 = currentWeather.getRandomWindSpeed(it[1])
|
||||
weatherbox.windSpeed.pM2 = currentWeather.getRandomWindSpeed(pM3, it[1])
|
||||
weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(weatherbox.windSpeed.pM2, it[2])
|
||||
weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(weatherbox.windSpeed.pM1, it[3])
|
||||
weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(weatherbox.windSpeed.p0, it[4])
|
||||
weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(weatherbox.windSpeed.p1, it[5])
|
||||
weatherbox.windSpeed.p3 = currentWeather.getRandomWindSpeed(weatherbox.windSpeed.p2, it[6])
|
||||
}
|
||||
|
||||
// the savegame loader will overwrite whatever the initial value we have here
|
||||
|
||||
@@ -177,6 +177,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
printdbg(this, "Demo world not found, using empty world")
|
||||
}
|
||||
|
||||
this.world = demoWorld
|
||||
|
||||
// set initial time to summer
|
||||
demoWorld.worldTime.addTime(WorldTime.DAY_LENGTH * 32)
|
||||
@@ -213,7 +214,7 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) {
|
||||
|
||||
IngameRenderer.setRenderedWorld(demoWorld)
|
||||
WeatherMixer.internalReset(this)
|
||||
WeatherMixer.titleScreenInitWeather(this.world.weatherbox)
|
||||
WeatherMixer.titleScreenInitWeather(demoWorld.weatherbox)
|
||||
|
||||
|
||||
// load a half-gradient texture that would be used throughout the titlescreen and its sub UIs
|
||||
|
||||
@@ -359,8 +359,10 @@ class BasicDebugInfoWindow : UICanvas() {
|
||||
val schedYstart = App.scr.height - 140 - 120 - 13f * (weatherbox.weatherSchedule.size + 3)
|
||||
App.fontSmallNumbers.draw(batch, "$ccY== WeatherSched [${weatherbox.weatherSchedule.size}] ==", drawXf, schedYstart)
|
||||
weatherbox.weatherSchedule.forEachIndexed { index, weather ->
|
||||
val sek = if (index == 0) weatherbox.updateAkku else 0
|
||||
App.fontSmallNumbers.draw(batch, "$ccY${weather.weather.identifier} $ccG${weather.duration - sek}", drawXf, schedYstart + 13 * (index + 1))
|
||||
val sek = if (index == 1) weatherbox.updateAkku else 0
|
||||
val cc1 = if (index == 0) ccK else ccY
|
||||
val cc2 = if (index == 0) ccK else ccG
|
||||
App.fontSmallNumbers.draw(batch, "$cc1${weather.weather.identifier} $cc2${weather.duration - sek}", drawXf, schedYstart + 13 * (index + 1))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,18 +29,26 @@ data class BaseModularWeather(
|
||||
val cloudGamma: Vector2,
|
||||
val cloudGammaVariance: Vector2,
|
||||
var clouds: List<CloudProps>, // sorted by CloudProps.probability
|
||||
val shaderVibrancy: FloatArray,
|
||||
|
||||
val mixFrom: String? = null,
|
||||
val mixPercentage: Double? = null,
|
||||
) {
|
||||
|
||||
|
||||
/**
|
||||
* @param rnd random number between -1 and +1
|
||||
*/
|
||||
fun getRandomWindSpeed(old: Float, rnd: Float): Float {
|
||||
val v = 1f + rnd.absoluteValue * windSpeedVariance
|
||||
val r = if (rnd < 0) windSpeed / v else windSpeed * v
|
||||
return FastMath.interpolateLinear(windSpeedDamping, old, r)
|
||||
return FastMath.interpolateLinear(1f - windSpeedDamping, old, r)
|
||||
}
|
||||
|
||||
fun getRandomWindSpeed(rnd: Float): Float {
|
||||
val v = 1f + rnd.absoluteValue * windSpeedVariance
|
||||
val r = if (rnd < 0) windSpeed / v else windSpeed * v
|
||||
return r
|
||||
}
|
||||
|
||||
fun getRandomCloudGamma(rnd1: Float, rnd2: Float): Vector2 {
|
||||
|
||||
@@ -64,7 +64,8 @@ internal object WeatherMixer : RNGConsumer {
|
||||
0f,
|
||||
Vector2(1f, 1f),
|
||||
Vector2(0f, 0f),
|
||||
listOf()
|
||||
listOf(),
|
||||
floatArrayOf(1f, 1f)
|
||||
)
|
||||
|
||||
override val RNG = HQRNG()
|
||||
@@ -169,7 +170,7 @@ internal object WeatherMixer : RNGConsumer {
|
||||
weatherDB[weather.classification]!!.add(weather)
|
||||
}
|
||||
|
||||
weatherDict["titlescreen"] = weatherDB[WEATHER_GENERIC]!![0].copy(windSpeed = 1f)
|
||||
weatherDict["titlescreen"] = weatherDB[WEATHER_GENERIC]!![0].copy(identifier = "titlescreen", windSpeed = 1f)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,7 +460,8 @@ internal object WeatherMixer : RNGConsumer {
|
||||
}
|
||||
|
||||
private fun initClouds(currentWeather: BaseModularWeather) {
|
||||
val hCloudSize = 1024f
|
||||
clouds.clear()
|
||||
cloudsSpawned = 0
|
||||
// multiplier is an empirical value that depends on the 'rZ'
|
||||
// it does converge at ~6, but having it as an initial state does not make it stay converged
|
||||
repeat((currentWeather.cloudChance * 1.333f).ceilToInt()) {
|
||||
@@ -731,13 +733,6 @@ internal object WeatherMixer : RNGConsumer {
|
||||
val skyboxInJson = JSON.getString("skyboxGradColourMap")
|
||||
val lightbox = JSON.getString("daylightClut")
|
||||
|
||||
val skybox = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}"))
|
||||
val daylight = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}"))
|
||||
|
||||
val identifier = JSON.getString("identifier")
|
||||
val classification = JSON.getString("classification")
|
||||
|
||||
|
||||
val cloudsMap = ArrayList<CloudProps>()
|
||||
val clouds = JSON["clouds"]
|
||||
clouds.forEachSiblings { name, json ->
|
||||
@@ -755,13 +750,12 @@ internal object WeatherMixer : RNGConsumer {
|
||||
|
||||
|
||||
|
||||
|
||||
return BaseModularWeather(
|
||||
identifier = identifier,
|
||||
identifier = JSON.getString("identifier"),
|
||||
json = JSON,
|
||||
skyboxGradColourMap = skybox,
|
||||
daylightClut = daylight,
|
||||
classification = classification,
|
||||
skyboxGradColourMap = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${skyboxInJson}")),
|
||||
daylightClut = GdxColorMap(ModMgr.getGdxFile(modname, "$pathToImage/${lightbox}")),
|
||||
classification = JSON.getString("classification"),
|
||||
cloudChance = JSON.getFloat("cloudChance"),
|
||||
windSpeed = JSON.getFloat("windSpeed"),
|
||||
windSpeedVariance = JSON.getFloat("windSpeedVariance"),
|
||||
@@ -769,6 +763,7 @@ internal object WeatherMixer : RNGConsumer {
|
||||
cloudGamma = JSON["cloudGamma"].asFloatArray().let { Vector2(it[0], it[1]) },
|
||||
cloudGammaVariance = JSON["cloudGammaVariance"].asFloatArray().let { Vector2(it[0], it[1]) },
|
||||
clouds = cloudsMap,
|
||||
shaderVibrancy = JSON["shaderVibrancy"].asFloatArray()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -5,12 +5,11 @@ import com.badlogic.gdx.graphics.g2d.SpriteBatch
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion
|
||||
import com.badlogic.gdx.math.Vector3
|
||||
import com.jme3.math.FastMath
|
||||
import com.jme3.math.FastMath.PI
|
||||
import com.jme3.math.FastMath.sin
|
||||
import net.torvald.terrarum.App
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
import kotlin.math.*
|
||||
|
||||
/**
|
||||
* Created by minjaesong on 2023-08-21.
|
||||
@@ -47,7 +46,7 @@ class WeatherObjectCloud(
|
||||
scl(world.worldTime.timeDelta.toFloat())
|
||||
)
|
||||
|
||||
eigenAlpha = if (posZ < 1f) posZ.pow(0.5f) else -((posZ - 1f) / ALPHA_ROLLOFF_Z) + 1f
|
||||
eigenAlpha = if (posZ < 1f) posZ.pow(0.5f) else cosh((posZ - CLOUD_STAGE_DEPTH) / (0.75636f * CLOUD_STAGE_DEPTH)) - 1f //-((posZ - 1f) / CLOUD_STAGE_DEPTH) + 1f
|
||||
|
||||
val alphaMult = if (life < NEWBORN_GROWTH_TIME)
|
||||
life / NEWBORN_GROWTH_TIME
|
||||
@@ -60,13 +59,13 @@ class WeatherObjectCloud(
|
||||
|
||||
|
||||
val lrCoord = screenCoordBottomLRforDespawnCalculation
|
||||
if (lrCoord.x > WeatherMixer.oobMarginR || lrCoord.z < WeatherMixer.oobMarginL || posZ !in 0.0001f..ALPHA_ROLLOFF_Z + 1f || alpha < 0f) {
|
||||
if (lrCoord.x > WeatherMixer.oobMarginR || lrCoord.z < WeatherMixer.oobMarginL || posZ !in 0.0001f..CLOUD_STAGE_DEPTH + 1f || alpha < 0f) {
|
||||
flagToDespawn = true
|
||||
|
||||
despawnCode = if (lrCoord.x > WeatherMixer.oobMarginR) "OUT_OF_SCREEN_RIGHT"
|
||||
else if (lrCoord.z < WeatherMixer.oobMarginL) "OUT_OF_SCREEN_LEFT"
|
||||
else if (posZ < 0.0001f) "OUT_OF_SCREEN_TOO_CLOSE"
|
||||
else if (posZ > ALPHA_ROLLOFF_Z + 1f) "OUT_OF_SCREEN_TOO_FAR"
|
||||
else if (posZ > CLOUD_STAGE_DEPTH + 1f) "OUT_OF_SCREEN_TOO_FAR"
|
||||
else if (life >= lifespan + OLD_AGE_DECAY) "OLD_AGE"
|
||||
else if (alpha < 0f) "ALPHA_BELOW_ZERO"
|
||||
else "UNKNOWN"
|
||||
@@ -180,6 +179,7 @@ class WeatherObjectCloud(
|
||||
fun worldYtoWorldZforScreenYof0(y: Float) = 1f - (y / H) // rearrange screenCoord equations to derive this eq :p
|
||||
|
||||
const val ALPHA_ROLLOFF_Z = 64f
|
||||
const val CLOUD_STAGE_DEPTH = 256f
|
||||
const val OLD_AGE_DECAY = 5000f
|
||||
const val NEWBORN_GROWTH_TIME = 1000f
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package net.torvald.terrarum.weather
|
||||
|
||||
import com.badlogic.gdx.math.Vector2
|
||||
import com.jme3.math.FastMath
|
||||
import net.torvald.terrarum.App.printdbg
|
||||
import net.torvald.terrarum.floorToInt
|
||||
import net.torvald.terrarum.gameworld.GameWorld
|
||||
import net.torvald.terrarum.gameworld.fmod
|
||||
import org.apache.commons.math3.analysis.UnivariateFunction
|
||||
import org.apache.commons.math3.analysis.interpolation.NevilleInterpolator
|
||||
import org.apache.commons.math3.analysis.interpolation.SplineInterpolator
|
||||
import java.util.*
|
||||
|
||||
data class WeatherSchedule(val weather: BaseModularWeather = WeatherMixer.DEFAULT_WEATHER, val duration: Long = 3600)
|
||||
@@ -37,25 +36,37 @@ class Weatherbox {
|
||||
val windDir = WeatherDirBox() // 0 .. 1.0
|
||||
val windSpeed = WeatherStateBox() // 0 .. arbitrarily large number
|
||||
|
||||
val weatherSchedule: MutableList<WeatherSchedule> = mutableListOf<WeatherSchedule>()
|
||||
val currentWeather: BaseModularWeather
|
||||
val weatherSchedule = ArrayList<WeatherSchedule>()
|
||||
val oldWeather: BaseModularWeather
|
||||
get() = weatherSchedule[0].weather
|
||||
val currentWeatherDuration: Long
|
||||
val currentWeather: BaseModularWeather
|
||||
get() = weatherSchedule[1].weather
|
||||
// val nextWeather: BaseModularWeather
|
||||
// get() = weatherSchedule[2].weather
|
||||
val oldWeatherDuration: Long
|
||||
get() = weatherSchedule[0].duration
|
||||
val currentWeatherDuration: Long
|
||||
get() = weatherSchedule[1].duration
|
||||
|
||||
@Transient val currentVibrancy = Vector2(1f, 1f)
|
||||
val weatherBlend: Float
|
||||
get() = updateAkku.toFloat() / currentWeatherDuration
|
||||
|
||||
fun initWith(initWeather: BaseModularWeather, duration: Long) {
|
||||
weatherSchedule.clear()
|
||||
weatherSchedule.add(WeatherSchedule(initWeather, duration))
|
||||
weatherSchedule.add(WeatherSchedule(initWeather, duration))
|
||||
}
|
||||
|
||||
var updateAkku = 0L; private set
|
||||
|
||||
fun update(world: GameWorld) {
|
||||
updateShaderParams()
|
||||
updateWind(world)
|
||||
|
||||
if (updateAkku >= currentWeatherDuration) {
|
||||
// TODO add more random weathers
|
||||
if (weatherSchedule.size == 1) {
|
||||
while (weatherSchedule.size < 3) {
|
||||
val newName = if (currentWeather.identifier == "generic01") "overcast01" else "generic01"
|
||||
val newDuration = 7200L
|
||||
weatherSchedule.add(WeatherSchedule(WeatherMixer.weatherDict[newName]!!, newDuration))
|
||||
@@ -64,7 +75,10 @@ class Weatherbox {
|
||||
}
|
||||
|
||||
// subtract akku by old currentWeatherDuration
|
||||
updateAkku -= weatherSchedule.removeAt(0).duration
|
||||
// printdbg(this, "Dequeueing a weather")
|
||||
|
||||
weatherSchedule.removeAt(0)
|
||||
updateAkku -= oldWeatherDuration
|
||||
}
|
||||
|
||||
updateAkku += world.worldTime.timeDelta
|
||||
@@ -76,6 +90,15 @@ class Weatherbox {
|
||||
}
|
||||
windDir.update( world.worldTime.timeDelta / WIND_DIR_TIME_UNIT) { RNG.nextFloat() * 4f }
|
||||
}
|
||||
|
||||
private fun updateShaderParams() {
|
||||
val (co, cg) = oldWeather.shaderVibrancy
|
||||
val (no, ng) = currentWeather.shaderVibrancy
|
||||
currentVibrancy.set(
|
||||
FastMath.interpolateLinear(weatherBlend * 2, co, no),
|
||||
FastMath.interpolateLinear(weatherBlend * 2, cg, ng),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ const mat4 swizzler = mat4(
|
||||
);
|
||||
uniform float quant = 255.0; // 64 steps -> 63.0; 256 steps -> 255.0
|
||||
|
||||
vec4 quantVec = vec4(quant);
|
||||
vec4 invQuant = vec4(1.0 / quant);
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
const vec2 boolean = vec2(0.0, 1.0);
|
||||
@@ -54,12 +57,12 @@ const mat4 ycocg_to_rgb = mat4(
|
||||
|
||||
|
||||
vec4 nearestColour(vec4 inColor) {
|
||||
return floor(vec4(quant) * inColor) * vec4(1.0 / quant);
|
||||
return floor(quantVec * inColor) * invQuant;
|
||||
}
|
||||
|
||||
vec4 getDitherredDot(vec4 inColor) {
|
||||
vec4 bayerThreshold = swizzler * vec4(matrixNormaliser + texture(u_pattern, (gl_FragCoord.xy + rnd) * patternsize));
|
||||
return nearestColour(bayerThreshold * vec4(1.0 / quant) + inColor);
|
||||
return nearestColour(fma(bayerThreshold, invQuant, inColor));
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user