mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-03-07 12:21:52 +09:00
akima interpolation to the WeatherBox
This commit is contained in:
1
.idea/artifacts/TerrarumBuild.xml
generated
1
.idea/artifacts/TerrarumBuild.xml
generated
@@ -87,6 +87,7 @@
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/regex-22.3.1-edit.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/js-22.3.1-edit.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/graal-sdk-22.3.1.jar" path-in-jar="/" />
|
||||
<element id="extracted-dir" path="$PROJECT_DIR$/lib/commons-math3-3.6.1.jar" path-in-jar="/" />
|
||||
</root>
|
||||
</artifact>
|
||||
</component>
|
||||
14
.idea/libraries/apache_commons_math3.xml
generated
Normal file
14
.idea/libraries/apache_commons_math3.xml
generated
Normal file
@@ -0,0 +1,14 @@
|
||||
<component name="libraryTable">
|
||||
<library name="apache.commons.math3" type="repository">
|
||||
<properties maven-id="org.apache.commons:commons-math3:3.6.1" />
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-math3-3.6.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-math3-3.6.1-javadoc.jar!/" />
|
||||
</JAVADOC>
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-math3-3.6.1-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
@@ -25,5 +25,6 @@
|
||||
<orderEntry type="library" name="jetbrains.kotlin.reflect" level="project" />
|
||||
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" />
|
||||
<orderEntry type="library" name="io.github.classgraph" level="project" />
|
||||
<orderEntry type="library" name="apache.commons.math3" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
BIN
lib/commons-math3-3.6.1-javadoc.jar
LFS
Normal file
BIN
lib/commons-math3-3.6.1-javadoc.jar
LFS
Normal file
Binary file not shown.
BIN
lib/commons-math3-3.6.1-sources.jar
LFS
Normal file
BIN
lib/commons-math3-3.6.1-sources.jar
LFS
Normal file
Binary file not shown.
BIN
lib/commons-math3-3.6.1.jar
LFS
Normal file
BIN
lib/commons-math3-3.6.1.jar
LFS
Normal file
Binary file not shown.
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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())
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user