diff --git a/.idea/artifacts/TerrarumBuild.xml b/.idea/artifacts/TerrarumBuild.xml
index 3f1077feb..9f3fb9511 100644
--- a/.idea/artifacts/TerrarumBuild.xml
+++ b/.idea/artifacts/TerrarumBuild.xml
@@ -87,6 +87,7 @@
+
\ No newline at end of file
diff --git a/.idea/libraries/apache_commons_math3.xml b/.idea/libraries/apache_commons_math3.xml
new file mode 100644
index 000000000..9287a3b9f
--- /dev/null
+++ b/.idea/libraries/apache_commons_math3.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TerrarumBuild.iml b/TerrarumBuild.iml
index 0bad4ebf2..1095d70ee 100644
--- a/TerrarumBuild.iml
+++ b/TerrarumBuild.iml
@@ -25,5 +25,6 @@
+
\ No newline at end of file
diff --git a/lib/commons-math3-3.6.1-javadoc.jar b/lib/commons-math3-3.6.1-javadoc.jar
new file mode 100644
index 000000000..bca7959af
--- /dev/null
+++ b/lib/commons-math3-3.6.1-javadoc.jar
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:10ed884e29ca17cb59d6940a49e6ffe72b87a59078c53f07c2a173ed325bca34
+size 6840211
diff --git a/lib/commons-math3-3.6.1-sources.jar b/lib/commons-math3-3.6.1-sources.jar
new file mode 100644
index 000000000..f0424fe21
--- /dev/null
+++ b/lib/commons-math3-3.6.1-sources.jar
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e2ff85a3c360d56c51a7021614a194f3fbaf224054642ac535016f118322934d
+size 2514690
diff --git a/lib/commons-math3-3.6.1.jar b/lib/commons-math3-3.6.1.jar
new file mode 100644
index 000000000..b113fef7f
--- /dev/null
+++ b/lib/commons-math3-3.6.1.jar
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:1e56d7b058d28b65abd256b8458e3885b674c1d588fa43cd7d1cbb9c7ef2b308
+size 2213560
diff --git a/src/net/torvald/terrarum/weather/WeatherMixer.kt b/src/net/torvald/terrarum/weather/WeatherMixer.kt
index 9eccc76bd..a85bd817b 100644
--- a/src/net/torvald/terrarum/weather/WeatherMixer.kt
+++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt
@@ -149,19 +149,21 @@ internal object WeatherMixer : RNGConsumer {
weatherbox.initWith(weatherDict["generic01"]!!, 7200L)
// TEST FILL WITH RANDOM VALUES
- (0..4).map { takeUniformRand(0f..1f) }.let {
- weatherbox.windDir.pM1 = it[0]
- weatherbox.windDir.p0 = it[1]
- weatherbox.windDir.p1 = it[2]
- weatherbox.windDir.p2 = it[3]
- weatherbox.windDir.p3 = it[4]
+ (0..6).map { takeUniformRand(0f..1f) }.let {
+ weatherbox.windDir.pM2 = it[1]
+ weatherbox.windDir.pM1 = it[2]
+ weatherbox.windDir.p0 = it[3]
+ weatherbox.windDir.p1 = it[4]
+ weatherbox.windDir.p2 = it[5]
+ weatherbox.windDir.p3 = it[6]
}
- (0..4).map { takeUniformRand(-1f..1f) }.let {
- weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[0])
- weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[1])
- weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[2])
- weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(it[3])
- weatherbox.windSpeed.p3 = currentWeather.getRandomWindSpeed(it[4])
+ (0..6).map { takeUniformRand(-1f..1f) }.let {
+ weatherbox.windSpeed.pM2 = currentWeather.getRandomWindSpeed(it[1])
+ weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[2])
+ weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[3])
+ weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[4])
+ weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(it[5])
+ weatherbox.windSpeed.p3 = currentWeather.getRandomWindSpeed(it[6])
}
}
@@ -405,8 +407,8 @@ internal object WeatherMixer : RNGConsumer {
Vector3(x, y, z)
}
-1, 3, 7 -> { // z = 0
- val z = 0.1f
val posXscr = FastMath.interpolateLinear(rr, -halfCloudSize, App.scr.width + halfCloudSize)
+ val z = WeatherObjectCloud.worldYtoWorldZforScreenYof0(y)
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, Z_LIM)
Vector3(x, y, z)
}
diff --git a/src/net/torvald/terrarum/weather/WeatherObjectCloud.kt b/src/net/torvald/terrarum/weather/WeatherObjectCloud.kt
index 9ea40951a..5ec20c6d1 100644
--- a/src/net/torvald/terrarum/weather/WeatherObjectCloud.kt
+++ b/src/net/torvald/terrarum/weather/WeatherObjectCloud.kt
@@ -6,7 +6,6 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion
import com.badlogic.gdx.math.Vector3
import com.jme3.math.FastMath
import net.torvald.terrarum.App
-import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.gameworld.GameWorld
import kotlin.math.absoluteValue
import kotlin.math.pow
@@ -70,9 +69,7 @@ class WeatherObjectCloud(
}
}
- private val w = App.scr.halfwf
- private val h = App.scr.hf * 0.5f
- private val vecMult = Vector3(1f, 1f, 1f / (4f * h))
+ private val vecMult = Vector3(1f, 1f, 1f / (4f * H))
private fun roundRgbGamma(x: Float): Int {
return RGB_GAMMA_TABLE.mapIndexed { i, f -> (f - x).absoluteValue to i }.minBy { it.first }.second
@@ -118,10 +115,10 @@ class WeatherObjectCloud(
get() {
val x = posX - texture.regionWidth * scale * 0.5f
val y = posY - texture.regionHeight * scale
- val z = posZ // must be at least 1.0
+ val z = posZ // must be larger than 0
- val drawX = (x + w * (z-1)) / z
- val drawY = (y + h * (z-1)) / z
+ val drawX = (x + W * (z-1)) / z
+ val drawY = (y + H * (z-1)) / z
val drawScale = scale / z
return Vector3(drawX, drawY, drawScale)
@@ -137,9 +134,9 @@ class WeatherObjectCloud(
val y = posY - texture.regionHeight * scale
val z = posZ // must be larger than 0
- val drawXL = (xL + w * (z-1)) / z
- val drawXR = (xR + w * (z-1)) / z
- val drawY = (y + h * (z-1)) / z
+ val drawXL = (xL + W * (z-1)) / z
+ val drawXR = (xR + W * (z-1)) / z
+ val drawY = (y + H * (z-1)) / z
return Vector3(drawXL, drawY, drawXR)
}
@@ -151,9 +148,9 @@ class WeatherObjectCloud(
val y = posY - texture.regionHeight * scale
val z = FastMath.interpolateLinear(posZ / ALPHA_ROLLOFF_Z, ALPHA_ROLLOFF_Z / 4f, ALPHA_ROLLOFF_Z)
- val drawXL = (xL + w * (z-1)) / z
- val drawXR = (xR + w * (z-1)) / z
- val drawY = (y + h * (z-1)) / z
+ val drawXL = (xL + W * (z-1)) / z
+ val drawXR = (xR + W * (z-1)) / z
+ val drawY = (y + H * (z-1)) / z
return Vector3(drawXL, drawY, drawXR)
}
@@ -162,7 +159,19 @@ class WeatherObjectCloud(
override fun compareTo(other: WeatherObjectCloud): Int = (other.posZ - this.posZ).sign.toInt()
companion object {
- fun screenXtoWorldX(screenX: Float, z: Float) = screenX * z - App.scr.halfwf * (z - 1f)
+ private val W = App.scr.halfwf
+ private val H = App.scr.hf * 0.5f
+
+ /**
+ * Given screen-x and world-z position, calculates a world-x position that would make the cloud appear at the given screen-x position
+ */
+ fun screenXtoWorldX(screenX: Float, z: Float) = screenX * z - W * (z - 1f) // rearrange screenCoord equations to derive this eq :p
+
+ /**
+ * Given a world-y position, calculates a world-z position that would put the cloud to screen-y of zero
+ */
+ fun worldYtoWorldZforScreenYof0(y: Float) = 1f - (y / H) // rearrange screenCoord equations to derive this eq :p
+
const val ALPHA_ROLLOFF_Z = 64f
const val OLD_AGE_DECAY = 4000f
diff --git a/src/net/torvald/terrarum/weather/Weatherbox.kt b/src/net/torvald/terrarum/weather/Weatherbox.kt
index e2630cc57..be831207a 100644
--- a/src/net/torvald/terrarum/weather/Weatherbox.kt
+++ b/src/net/torvald/terrarum/weather/Weatherbox.kt
@@ -5,6 +5,8 @@ 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.interpolation.AkimaSplineInterpolator
+import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction
import java.util.*
import kotlin.math.max
import kotlin.math.pow
@@ -92,18 +94,22 @@ open class WeatherStateBox(
// - removing p4 and beyond: for faster response to the changing weather schedule and make the forecasting less accurate like irl
) {
- open val value: Float; get() = interpolate(x, p0, p1, p2, p3)
- open fun valueAt(x: Float) = when (x.floorToInt()) {
- -2 -> interpolate(x + 2, pM2,pM1, p0, p1)
- -1 -> interpolate(x + 1, pM1, p0, p1, p2)
- 0 -> interpolate(x - 0, p0, p1, p2, p3)
- 1 -> interpolate(x - 1, p1, p2, p3, p3)
- 2 -> interpolate(x - 2, p2, p3, p3, p3)
- 3 -> interpolate(x - 3, p3, p3, p3, p3)
- else -> throw IllegalArgumentException()
+ protected lateinit var polynomial: PolynomialSplineFunction
+ protected val interpolator = AkimaSplineInterpolator()
+
+ open val value: Float; get() = valueAt(x)
+ open fun valueAt(x: Float) = polynomial.value(x + 1.0).toFloat()
+
+ open protected fun updatePolynomials() {
+ polynomial = interpolator.interpolate(
+ doubleArrayOf(-2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0),
+ doubleArrayOf(pM2.toDouble(), pM1.toDouble(), p0.toDouble(), p1.toDouble(), p2.toDouble(), p3.toDouble(), p3.toDouble())
+ )
}
open fun update(xdelta: Float, next: () -> Float) {
+ if (!::polynomial.isInitialized) updatePolynomials()
+
synchronized(WeatherMixer.RNG) {
x += xdelta
while (x >= 1.0) {
@@ -114,25 +120,10 @@ open class WeatherStateBox(
p1 = p2
p2 = p3
p3 = next()
-
-
-// p3 = p4
-// p4 = p5
-// p5 = next()
}
+ updatePolynomials()
}
}
- protected fun interpolate(u: Float, p0: Float, p1: Float, p2: Float, p3: Float): Float {
- val T = FastMath.interpolateLinear(u, p1, p2).div(max(p0, p3).coerceAtLeast(1f)).toDouble().coerceIn(0.0, 0.5)
-// if (u == x) printdbg(this, "u=$u, p1=$p1, p2=$p2; T=$T")
-
- val c1 = p1.toDouble()
- val c2 = -1.0 * T * p0 + T * p2
- val c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3
- val c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3
-
- return (((c4 * u + c3) * u + c2) * u + c1).toFloat()
- }
}
/**
@@ -147,9 +138,9 @@ class WeatherDirBox(
p2: Float = 0f,
p3: Float = 0f,
) : WeatherStateBox(x, pM2, pM1, p0, p1, p2, p3) {
- override val value; get() = valueAt(x)
+ override fun valueAt(x: Float) = polynomial.value(x + 1.0).plus(2.0).fmod(4.0).minus(2.0).toFloat()
- override fun valueAt(x: Float): Float {
+ override fun updatePolynomials() {
var pM2 = pM2
var pM1 = pM1
var p0 = p0
@@ -157,55 +148,50 @@ class WeatherDirBox(
var p2 = p2
var p3 = p3
- if (x < -2f) {
- if (pM1 - pM2 > 2f) {
- pM2 -= 4f
- pM1 -= 4f
- p0 -= 4f
- p1 -= 4f
- p2 -= 4f
- p3 -= 4f
- }
- else if (pM1 - pM2 < -2f) {
- pM2 += 4f
- pM1 += 4f
- p0 += 4f
- p1 += 4f
- p2 += 4f
- p3 += 4f
- }
+
+ if (pM1 - pM2 > 2f) {
+ pM2 -= 4f
+ pM1 -= 4f
+ p0 -= 4f
+ p1 -= 4f
+ p2 -= 4f
+ p3 -= 4f
+ }
+ else if (pM1 - pM2 < -2f) {
+ pM2 += 4f
+ pM1 += 4f
+ p0 += 4f
+ p1 += 4f
+ p2 += 4f
+ p3 += 4f
}
- if (x < -1f) {
- if (pM1 - pM2 > 2f) {
- pM1 -= 4f
- p0 -= 4f
- p1 -= 4f
- p2 -= 4f
- p3 -= 4f
- }
- else if (pM1 - pM2 < -2f) {
- pM1 += 4f
- p0 += 4f
- p1 += 4f
- p2 += 4f
- p3 += 4f
- }
+ if (pM1 - pM2 > 2f) {
+ pM1 -= 4f
+ p0 -= 4f
+ p1 -= 4f
+ p2 -= 4f
+ p3 -= 4f
+ }
+ else if (pM1 - pM2 < -2f) {
+ pM1 += 4f
+ p0 += 4f
+ p1 += 4f
+ p2 += 4f
+ p3 += 4f
}
- if (x < 0f) {
- if (p0 - pM1 > 2f) {
- p0 -= 4f
- p1 -= 4f
- p2 -= 4f
- p3 -= 4f
- }
- else if (p0 - pM1 < -2f) {
- p0 += 4f
- p1 += 4f
- p2 += 4f
- p3 += 4f
- }
+ if (p0 - pM1 > 2f) {
+ p0 -= 4f
+ p1 -= 4f
+ p2 -= 4f
+ p3 -= 4f
+ }
+ else if (p0 - pM1 < -2f) {
+ p0 += 4f
+ p1 += 4f
+ p2 += 4f
+ p3 += 4f
}
if (p1 - p0 > 2f) {
@@ -235,25 +221,9 @@ class WeatherDirBox(
p3 += 4f
}
- return when (x.floorToInt()) {
- -2 -> interpolate2(x + 2, pM2,pM1, p0, p1)
- -1 -> interpolate2(x + 1, pM1, p0, p1, p2)
- 0 -> interpolate2(x - 0, p0, p1, p2, p3)
- 1 -> interpolate2(x - 1, p1, p2, p3, p3)
- 2 -> interpolate2(x - 2, p2, p3, p3, p3)
- 3 -> interpolate2(x - 3, p3, p3, p3, p3)
- else -> throw IllegalArgumentException()
- }.plus(2f).fmod(4f).minus(2f)
- }
-
- private fun interpolate2(u: Float, p0: Float, p1: Float, p2: Float, p3: Float): Float {
- val T = 0.5
-
- val c1 = p1.toDouble()
- val c2 = -1.0 * T * p0 + T * p2
- val c3 = 2 * T * p0 + (T - 3) * p1 + (3 - 2 * T) * p2 + -T * p3
- val c4 = -T * p0 + (2 - T) * p1 + (T - 2) * p2 + T * p3
-
- return (((c4 * u + c3) * u + c2) * u + c1).toFloat()
+ polynomial = interpolator.interpolate(
+ doubleArrayOf(-2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0),
+ doubleArrayOf(pM2.toDouble(), pM1.toDouble(), p0.toDouble(), p1.toDouble(), p2.toDouble(), p3.toDouble(), p3.toDouble())
+ )
}
}
\ No newline at end of file