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/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/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/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>
|
</root>
|
||||||
</artifact>
|
</artifact>
|
||||||
</component>
|
</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.reflect" level="project" />
|
||||||
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" />
|
<orderEntry type="library" name="jetbrains.kotlin.test" level="project" />
|
||||||
<orderEntry type="library" name="io.github.classgraph" level="project" />
|
<orderEntry type="library" name="io.github.classgraph" level="project" />
|
||||||
|
<orderEntry type="library" name="apache.commons.math3" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</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)
|
weatherbox.initWith(weatherDict["generic01"]!!, 7200L)
|
||||||
|
|
||||||
// TEST FILL WITH RANDOM VALUES
|
// TEST FILL WITH RANDOM VALUES
|
||||||
(0..4).map { takeUniformRand(0f..1f) }.let {
|
(0..6).map { takeUniformRand(0f..1f) }.let {
|
||||||
weatherbox.windDir.pM1 = it[0]
|
weatherbox.windDir.pM2 = it[1]
|
||||||
weatherbox.windDir.p0 = it[1]
|
weatherbox.windDir.pM1 = it[2]
|
||||||
weatherbox.windDir.p1 = it[2]
|
weatherbox.windDir.p0 = it[3]
|
||||||
weatherbox.windDir.p2 = it[3]
|
weatherbox.windDir.p1 = it[4]
|
||||||
weatherbox.windDir.p3 = it[4]
|
weatherbox.windDir.p2 = it[5]
|
||||||
|
weatherbox.windDir.p3 = it[6]
|
||||||
}
|
}
|
||||||
(0..4).map { takeUniformRand(-1f..1f) }.let {
|
(0..6).map { takeUniformRand(-1f..1f) }.let {
|
||||||
weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[0])
|
weatherbox.windSpeed.pM2 = currentWeather.getRandomWindSpeed(it[1])
|
||||||
weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[1])
|
weatherbox.windSpeed.pM1 = currentWeather.getRandomWindSpeed(it[2])
|
||||||
weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[2])
|
weatherbox.windSpeed.p0 = currentWeather.getRandomWindSpeed(it[3])
|
||||||
weatherbox.windSpeed.p2 = currentWeather.getRandomWindSpeed(it[3])
|
weatherbox.windSpeed.p1 = currentWeather.getRandomWindSpeed(it[4])
|
||||||
weatherbox.windSpeed.p3 = 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)
|
Vector3(x, y, z)
|
||||||
}
|
}
|
||||||
-1, 3, 7 -> { // z = 0
|
-1, 3, 7 -> { // z = 0
|
||||||
val z = 0.1f
|
|
||||||
val posXscr = FastMath.interpolateLinear(rr, -halfCloudSize, App.scr.width + halfCloudSize)
|
val posXscr = FastMath.interpolateLinear(rr, -halfCloudSize, App.scr.width + halfCloudSize)
|
||||||
|
val z = WeatherObjectCloud.worldYtoWorldZforScreenYof0(y)
|
||||||
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, Z_LIM)
|
val x = WeatherObjectCloud.screenXtoWorldX(posXscr, Z_LIM)
|
||||||
Vector3(x, y, z)
|
Vector3(x, y, z)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import com.badlogic.gdx.graphics.g2d.TextureRegion
|
|||||||
import com.badlogic.gdx.math.Vector3
|
import com.badlogic.gdx.math.Vector3
|
||||||
import com.jme3.math.FastMath
|
import com.jme3.math.FastMath
|
||||||
import net.torvald.terrarum.App
|
import net.torvald.terrarum.App
|
||||||
import net.torvald.terrarum.App.printdbg
|
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
@@ -70,9 +69,7 @@ class WeatherObjectCloud(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val w = App.scr.halfwf
|
private val vecMult = Vector3(1f, 1f, 1f / (4f * H))
|
||||||
private val h = App.scr.hf * 0.5f
|
|
||||||
private val vecMult = Vector3(1f, 1f, 1f / (4f * h))
|
|
||||||
|
|
||||||
private fun roundRgbGamma(x: Float): Int {
|
private fun roundRgbGamma(x: Float): Int {
|
||||||
return RGB_GAMMA_TABLE.mapIndexed { i, f -> (f - x).absoluteValue to i }.minBy { it.first }.second
|
return RGB_GAMMA_TABLE.mapIndexed { i, f -> (f - x).absoluteValue to i }.minBy { it.first }.second
|
||||||
@@ -118,10 +115,10 @@ class WeatherObjectCloud(
|
|||||||
get() {
|
get() {
|
||||||
val x = posX - texture.regionWidth * scale * 0.5f
|
val x = posX - texture.regionWidth * scale * 0.5f
|
||||||
val y = posY - texture.regionHeight * scale
|
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 drawX = (x + W * (z-1)) / z
|
||||||
val drawY = (y + h * (z-1)) / z
|
val drawY = (y + H * (z-1)) / z
|
||||||
val drawScale = scale / z
|
val drawScale = scale / z
|
||||||
|
|
||||||
return Vector3(drawX, drawY, drawScale)
|
return Vector3(drawX, drawY, drawScale)
|
||||||
@@ -137,9 +134,9 @@ class WeatherObjectCloud(
|
|||||||
val y = posY - texture.regionHeight * scale
|
val y = posY - texture.regionHeight * scale
|
||||||
val z = posZ // must be larger than 0
|
val z = posZ // must be larger than 0
|
||||||
|
|
||||||
val drawXL = (xL + w * (z-1)) / z
|
val drawXL = (xL + W * (z-1)) / z
|
||||||
val drawXR = (xR + w * (z-1)) / z
|
val drawXR = (xR + W * (z-1)) / z
|
||||||
val drawY = (y + h * (z-1)) / z
|
val drawY = (y + H * (z-1)) / z
|
||||||
|
|
||||||
return Vector3(drawXL, drawY, drawXR)
|
return Vector3(drawXL, drawY, drawXR)
|
||||||
}
|
}
|
||||||
@@ -151,9 +148,9 @@ class WeatherObjectCloud(
|
|||||||
val y = posY - texture.regionHeight * scale
|
val y = posY - texture.regionHeight * scale
|
||||||
val z = FastMath.interpolateLinear(posZ / ALPHA_ROLLOFF_Z, ALPHA_ROLLOFF_Z / 4f, ALPHA_ROLLOFF_Z)
|
val z = FastMath.interpolateLinear(posZ / ALPHA_ROLLOFF_Z, ALPHA_ROLLOFF_Z / 4f, ALPHA_ROLLOFF_Z)
|
||||||
|
|
||||||
val drawXL = (xL + w * (z-1)) / z
|
val drawXL = (xL + W * (z-1)) / z
|
||||||
val drawXR = (xR + w * (z-1)) / z
|
val drawXR = (xR + W * (z-1)) / z
|
||||||
val drawY = (y + h * (z-1)) / z
|
val drawY = (y + H * (z-1)) / z
|
||||||
|
|
||||||
return Vector3(drawXL, drawY, drawXR)
|
return Vector3(drawXL, drawY, drawXR)
|
||||||
}
|
}
|
||||||
@@ -162,7 +159,19 @@ class WeatherObjectCloud(
|
|||||||
override fun compareTo(other: WeatherObjectCloud): Int = (other.posZ - this.posZ).sign.toInt()
|
override fun compareTo(other: WeatherObjectCloud): Int = (other.posZ - this.posZ).sign.toInt()
|
||||||
|
|
||||||
companion object {
|
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 ALPHA_ROLLOFF_Z = 64f
|
||||||
const val OLD_AGE_DECAY = 4000f
|
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.floorToInt
|
||||||
import net.torvald.terrarum.gameworld.GameWorld
|
import net.torvald.terrarum.gameworld.GameWorld
|
||||||
import net.torvald.terrarum.gameworld.fmod
|
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 java.util.*
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.pow
|
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
|
// - 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)
|
protected lateinit var polynomial: PolynomialSplineFunction
|
||||||
open fun valueAt(x: Float) = when (x.floorToInt()) {
|
protected val interpolator = AkimaSplineInterpolator()
|
||||||
-2 -> interpolate(x + 2, pM2,pM1, p0, p1)
|
|
||||||
-1 -> interpolate(x + 1, pM1, p0, p1, p2)
|
open val value: Float; get() = valueAt(x)
|
||||||
0 -> interpolate(x - 0, p0, p1, p2, p3)
|
open fun valueAt(x: Float) = polynomial.value(x + 1.0).toFloat()
|
||||||
1 -> interpolate(x - 1, p1, p2, p3, p3)
|
|
||||||
2 -> interpolate(x - 2, p2, p3, p3, p3)
|
open protected fun updatePolynomials() {
|
||||||
3 -> interpolate(x - 3, p3, p3, p3, p3)
|
polynomial = interpolator.interpolate(
|
||||||
else -> throw IllegalArgumentException()
|
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) {
|
open fun update(xdelta: Float, next: () -> Float) {
|
||||||
|
if (!::polynomial.isInitialized) updatePolynomials()
|
||||||
|
|
||||||
synchronized(WeatherMixer.RNG) {
|
synchronized(WeatherMixer.RNG) {
|
||||||
x += xdelta
|
x += xdelta
|
||||||
while (x >= 1.0) {
|
while (x >= 1.0) {
|
||||||
@@ -114,25 +120,10 @@ open class WeatherStateBox(
|
|||||||
p1 = p2
|
p1 = p2
|
||||||
p2 = p3
|
p2 = p3
|
||||||
p3 = next()
|
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,
|
p2: Float = 0f,
|
||||||
p3: Float = 0f,
|
p3: Float = 0f,
|
||||||
) : WeatherStateBox(x, pM2, pM1, p0, p1, p2, p3) {
|
) : 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 pM2 = pM2
|
||||||
var pM1 = pM1
|
var pM1 = pM1
|
||||||
var p0 = p0
|
var p0 = p0
|
||||||
@@ -157,55 +148,50 @@ class WeatherDirBox(
|
|||||||
var p2 = p2
|
var p2 = p2
|
||||||
var p3 = p3
|
var p3 = p3
|
||||||
|
|
||||||
if (x < -2f) {
|
|
||||||
if (pM1 - pM2 > 2f) {
|
if (pM1 - pM2 > 2f) {
|
||||||
pM2 -= 4f
|
pM2 -= 4f
|
||||||
pM1 -= 4f
|
pM1 -= 4f
|
||||||
p0 -= 4f
|
p0 -= 4f
|
||||||
p1 -= 4f
|
p1 -= 4f
|
||||||
p2 -= 4f
|
p2 -= 4f
|
||||||
p3 -= 4f
|
p3 -= 4f
|
||||||
}
|
}
|
||||||
else if (pM1 - pM2 < -2f) {
|
else if (pM1 - pM2 < -2f) {
|
||||||
pM2 += 4f
|
pM2 += 4f
|
||||||
pM1 += 4f
|
pM1 += 4f
|
||||||
p0 += 4f
|
p0 += 4f
|
||||||
p1 += 4f
|
p1 += 4f
|
||||||
p2 += 4f
|
p2 += 4f
|
||||||
p3 += 4f
|
p3 += 4f
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < -1f) {
|
if (pM1 - pM2 > 2f) {
|
||||||
if (pM1 - pM2 > 2f) {
|
pM1 -= 4f
|
||||||
pM1 -= 4f
|
p0 -= 4f
|
||||||
p0 -= 4f
|
p1 -= 4f
|
||||||
p1 -= 4f
|
p2 -= 4f
|
||||||
p2 -= 4f
|
p3 -= 4f
|
||||||
p3 -= 4f
|
}
|
||||||
}
|
else if (pM1 - pM2 < -2f) {
|
||||||
else if (pM1 - pM2 < -2f) {
|
pM1 += 4f
|
||||||
pM1 += 4f
|
p0 += 4f
|
||||||
p0 += 4f
|
p1 += 4f
|
||||||
p1 += 4f
|
p2 += 4f
|
||||||
p2 += 4f
|
p3 += 4f
|
||||||
p3 += 4f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x < 0f) {
|
if (p0 - pM1 > 2f) {
|
||||||
if (p0 - pM1 > 2f) {
|
p0 -= 4f
|
||||||
p0 -= 4f
|
p1 -= 4f
|
||||||
p1 -= 4f
|
p2 -= 4f
|
||||||
p2 -= 4f
|
p3 -= 4f
|
||||||
p3 -= 4f
|
}
|
||||||
}
|
else if (p0 - pM1 < -2f) {
|
||||||
else if (p0 - pM1 < -2f) {
|
p0 += 4f
|
||||||
p0 += 4f
|
p1 += 4f
|
||||||
p1 += 4f
|
p2 += 4f
|
||||||
p2 += 4f
|
p3 += 4f
|
||||||
p3 += 4f
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p1 - p0 > 2f) {
|
if (p1 - p0 > 2f) {
|
||||||
@@ -235,25 +221,9 @@ class WeatherDirBox(
|
|||||||
p3 += 4f
|
p3 += 4f
|
||||||
}
|
}
|
||||||
|
|
||||||
return when (x.floorToInt()) {
|
polynomial = interpolator.interpolate(
|
||||||
-2 -> interpolate2(x + 2, pM2,pM1, p0, p1)
|
doubleArrayOf(-2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0),
|
||||||
-1 -> interpolate2(x + 1, pM1, p0, p1, p2)
|
doubleArrayOf(pM2.toDouble(), pM1.toDouble(), p0.toDouble(), p1.toDouble(), p2.toDouble(), p3.toDouble(), p3.toDouble())
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user