diff --git a/assets/clut/hosek/datasetXYZ1.bin b/assets/clut/hosek/datasetXYZ1.bin deleted file mode 100644 index 819912ce5..000000000 Binary files a/assets/clut/hosek/datasetXYZ1.bin and /dev/null differ diff --git a/assets/clut/hosek/datasetXYZ2.bin b/assets/clut/hosek/datasetXYZ2.bin deleted file mode 100644 index c15de128e..000000000 Binary files a/assets/clut/hosek/datasetXYZ2.bin and /dev/null differ diff --git a/assets/clut/hosek/datasetXYZ3.bin b/assets/clut/hosek/datasetXYZ3.bin deleted file mode 100644 index fc448abd6..000000000 Binary files a/assets/clut/hosek/datasetXYZ3.bin and /dev/null differ diff --git a/assets/clut/hosek/datasetXYZRad1.bin b/assets/clut/hosek/datasetXYZRad1.bin deleted file mode 100644 index 0f8e5d170..000000000 Binary files a/assets/clut/hosek/datasetXYZRad1.bin and /dev/null differ diff --git a/assets/clut/hosek/datasetXYZRad2.bin b/assets/clut/hosek/datasetXYZRad2.bin deleted file mode 100644 index c7d2bdc77..000000000 Binary files a/assets/clut/hosek/datasetXYZRad2.bin and /dev/null differ diff --git a/assets/clut/hosek/datasetXYZRad3.bin b/assets/clut/hosek/datasetXYZRad3.bin deleted file mode 100644 index bc30e27c1..000000000 Binary files a/assets/clut/hosek/datasetXYZRad3.bin and /dev/null differ diff --git a/src/net/torvald/parametricsky/datasets/DatasetCIEXYZ.kt b/src/net/torvald/parametricsky/datasets/DatasetCIEXYZ.kt index 0d7da948d..993b2348d 100644 --- a/src/net/torvald/parametricsky/datasets/DatasetCIEXYZ.kt +++ b/src/net/torvald/parametricsky/datasets/DatasetCIEXYZ.kt @@ -107,13 +107,13 @@ import kotlin.test.assertEquals object DatasetCIEXYZ { - val datasetXYZ1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZ1.bin") - val datasetXYZ2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZ2.bin") - val datasetXYZ3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZ3.bin") + val datasetXYZ1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZ1.bin") + val datasetXYZ2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZ2.bin") + val datasetXYZ3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZ3.bin") - val datasetXYZRad1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZRad1.bin") - val datasetXYZRad2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZRad2.bin") - val datasetXYZRad3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetXYZRad3.bin") + val datasetXYZRad1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZRad1.bin") + val datasetXYZRad2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZRad2.bin") + val datasetXYZRad3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetXYZRad3.bin") init { assertEquals(1080, datasetXYZ2.size, "Dataset size mismatch: expected 1080, got ${datasetXYZ2.size}") diff --git a/src/net/torvald/parametricsky/datasets/DatasetRGB.kt b/src/net/torvald/parametricsky/datasets/DatasetRGB.kt index 92a704aa5..e2d6bf07a 100644 --- a/src/net/torvald/parametricsky/datasets/DatasetRGB.kt +++ b/src/net/torvald/parametricsky/datasets/DatasetRGB.kt @@ -107,13 +107,13 @@ import kotlin.test.assertEquals object DatasetRGB { - val datasetRGB1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGB1.bin") - val datasetRGB2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGB2.bin") - val datasetRGB3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGB3.bin") + val datasetRGB1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGB1.bin") + val datasetRGB2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGB2.bin") + val datasetRGB3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGB3.bin") - val datasetRGBRad1 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGBRad1.bin") - val datasetRGBRad2 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGBRad2.bin") - val datasetRGBRad3 = DatasetOp.readDatasetFromFile("./assets/clut/hosek/datasetRGBRad3.bin") + val datasetRGBRad1 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGBRad1.bin") + val datasetRGBRad2 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGBRad2.bin") + val datasetRGBRad3 = DatasetOp.readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRGBRad3.bin") init { assertEquals(1080, datasetRGB2.size, "Dataset size mismatch: expected 1080, got ${datasetRGB2.size}") diff --git a/src/net/torvald/parametricsky/datasets/DatasetSpectral.kt b/src/net/torvald/parametricsky/datasets/DatasetSpectral.kt index d5cba8ef6..1a39e2dd5 100644 --- a/src/net/torvald/parametricsky/datasets/DatasetSpectral.kt +++ b/src/net/torvald/parametricsky/datasets/DatasetSpectral.kt @@ -108,41 +108,41 @@ import kotlin.test.assertEquals object DatasetSpectral { - val dataset320 = readDatasetFromFile("./assets/clut/hosek/dataset320.bin") - val dataset360 = readDatasetFromFile("./assets/clut/hosek/dataset360.bin") - val dataset400 = readDatasetFromFile("./assets/clut/hosek/dataset400.bin") - val dataset440 = readDatasetFromFile("./assets/clut/hosek/dataset440.bin") - val dataset480 = readDatasetFromFile("./assets/clut/hosek/dataset480.bin") - val dataset520 = readDatasetFromFile("./assets/clut/hosek/dataset520.bin") - val dataset560 = readDatasetFromFile("./assets/clut/hosek/dataset560.bin") - val dataset600 = readDatasetFromFile("./assets/clut/hosek/dataset600.bin") - val dataset640 = readDatasetFromFile("./assets/clut/hosek/dataset640.bin") - val dataset680 = readDatasetFromFile("./assets/clut/hosek/dataset680.bin") - val dataset720 = readDatasetFromFile("./assets/clut/hosek/dataset720.bin") + val dataset320 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset320.bin") + val dataset360 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset360.bin") + val dataset400 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset400.bin") + val dataset440 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset440.bin") + val dataset480 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset480.bin") + val dataset520 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset520.bin") + val dataset560 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset560.bin") + val dataset600 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset600.bin") + val dataset640 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset640.bin") + val dataset680 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset680.bin") + val dataset720 = readDatasetFromFile("./work_files/skylight/hosek_model_source/dataset720.bin") - val datasetRad320 = readDatasetFromFile("./assets/clut/hosek/datasetRad320.bin") - val datasetRad360 = readDatasetFromFile("./assets/clut/hosek/datasetRad360.bin") - val datasetRad400 = readDatasetFromFile("./assets/clut/hosek/datasetRad400.bin") - val datasetRad440 = readDatasetFromFile("./assets/clut/hosek/datasetRad440.bin") - val datasetRad480 = readDatasetFromFile("./assets/clut/hosek/datasetRad480.bin") - val datasetRad520 = readDatasetFromFile("./assets/clut/hosek/datasetRad520.bin") - val datasetRad560 = readDatasetFromFile("./assets/clut/hosek/datasetRad560.bin") - val datasetRad600 = readDatasetFromFile("./assets/clut/hosek/datasetRad600.bin") - val datasetRad640 = readDatasetFromFile("./assets/clut/hosek/datasetRad640.bin") - val datasetRad680 = readDatasetFromFile("./assets/clut/hosek/datasetRad680.bin") - val datasetRad720 = readDatasetFromFile("./assets/clut/hosek/datasetRad720.bin") + val datasetRad320 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad320.bin") + val datasetRad360 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad360.bin") + val datasetRad400 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad400.bin") + val datasetRad440 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad440.bin") + val datasetRad480 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad480.bin") + val datasetRad520 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad520.bin") + val datasetRad560 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad560.bin") + val datasetRad600 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad600.bin") + val datasetRad640 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad640.bin") + val datasetRad680 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad680.bin") + val datasetRad720 = readDatasetFromFile("./work_files/skylight/hosek_model_source/datasetRad720.bin") - val solarDataset320 = readDatasetFromFile("./assets/clut/hosek/solarDataset320.bin") - val solarDataset360 = readDatasetFromFile("./assets/clut/hosek/solarDataset360.bin") - val solarDataset400 = readDatasetFromFile("./assets/clut/hosek/solarDataset400.bin") - val solarDataset440 = readDatasetFromFile("./assets/clut/hosek/solarDataset440.bin") - val solarDataset480 = readDatasetFromFile("./assets/clut/hosek/solarDataset480.bin") - val solarDataset520 = readDatasetFromFile("./assets/clut/hosek/solarDataset520.bin") - val solarDataset560 = readDatasetFromFile("./assets/clut/hosek/solarDataset560.bin") - val solarDataset600 = readDatasetFromFile("./assets/clut/hosek/solarDataset600.bin") - val solarDataset640 = readDatasetFromFile("./assets/clut/hosek/solarDataset640.bin") - val solarDataset680 = readDatasetFromFile("./assets/clut/hosek/solarDataset680.bin") - val solarDataset720 = readDatasetFromFile("./assets/clut/hosek/solarDataset720.bin") + val solarDataset320 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset320.bin") + val solarDataset360 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset360.bin") + val solarDataset400 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset400.bin") + val solarDataset440 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset440.bin") + val solarDataset480 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset480.bin") + val solarDataset520 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset520.bin") + val solarDataset560 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset560.bin") + val solarDataset600 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset600.bin") + val solarDataset640 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset640.bin") + val solarDataset680 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset680.bin") + val solarDataset720 = readDatasetFromFile("./work_files/skylight/hosek_model_source/solarDataset720.bin") init { assertEquals(1080, dataset600.size, "Dataset size mismatch - expected 1080, got ${dataset600.size}") diff --git a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt index a9aa10a84..6efbcd786 100644 --- a/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt +++ b/src/net/torvald/terrarum/modulebasegame/TitleScreen.kt @@ -241,7 +241,8 @@ class TitleScreen(batch: FlippingSpriteBatch) : IngameInstance(batch) { uiContainer.add(uiRemoCon) CommandDict // invoke - Skybox // invoke + Skybox.loadlut() // invoke +// Skybox.initiate() // invoke the lengthy calculation // TODO add console here diff --git a/src/net/torvald/terrarum/modulebasegame/clut/GenerateSkyboxTextureAtlas.kt b/src/net/torvald/terrarum/modulebasegame/clut/GenerateSkyboxTextureAtlas.kt new file mode 100644 index 000000000..0c8104edd --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/clut/GenerateSkyboxTextureAtlas.kt @@ -0,0 +1,102 @@ +package net.torvald.terrarum.modulebasegame.clut + +import net.torvald.colourutil.CIEXYZ +import net.torvald.colourutil.toColor +import net.torvald.colourutil.toRGB +import net.torvald.parametricsky.ArHosekSkyModel +import net.torvald.terrarum.abs +import net.torvald.terrarum.modulebasegame.clut.Skybox.coerceInSmoothly +import net.torvald.terrarum.modulebasegame.clut.Skybox.mapCircle +import net.torvald.terrarum.modulebasegame.clut.Skybox.scaleToFit +import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI +import net.torvald.terrarum.serialise.toLittle +import java.io.File +import kotlin.math.PI +import kotlin.math.pow +import kotlin.math.roundToInt + +/** + * Created by minjaesong on 2023-08-01. + */ +fun main() { + // y: increasing turbidity (1.0 .. 10.0, in steps of 0.333) + // x: elevations (-75 .. 75 in steps of 1, then albedo of [0.1, 0.3, 0.5, 0.7, 0.9]) + val texh = Skybox.gradSize * Skybox.turbCnt + val texw = 2 * Skybox.elevCnt * 5 + val TGA_HEADER_SIZE = 18 + + val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26) + // write header + byteArrayOf( + 0, // ID field + 0, // colour map (none) + 2, // colour type (unmapped RGB) + 0,0,0,0,0, // colour map spec (empty) + 0,0, // x origin (0) + 0,0, // y origin (0) + (texw and 255).toByte(),(texw.ushr(8) and 255).toByte(), // width + (texh and 255).toByte(),(texh.ushr(8) and 255).toByte(), // height + 32, // bits-per-pixel (8bpp RGBA) + 8 // image descriptor + ).forEachIndexed { i,b -> bytes[i] = b } + // write footer + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000TRUEVISION-XFILE\u002E\u0000".forEachIndexed { i, c -> bytes[18 + texw * texh * 4 + i] = + c.code.toByte() + } + + println("Generating texture atlas ($texw x $texh)...") + + // write pixels + for (albedo0 in 0 until Skybox.albedoCnt) { + val albedo = Skybox.albedos[albedo0] + println("Albedo=$albedo") + for (turb0 in 0 until Skybox.turbCnt) { + val turbidity = Skybox.turbiditiesD[turb0] + println("....... Turbidity=$turbidity") + for (elev0 in 0 until Skybox.elevCnt) { + val elevationDeg = Skybox.elevationsD[elev0] + val elevationRad = Math.toRadians(elevationDeg) +// println("... Elevation: $elevationDeg") + + val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs()) + + for (yp in 0 until Skybox.gradSize) { + val yi = yp - 3 + val xf = -elevationDeg / 90.0 + var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) + + // experiments visualisation: https://www.desmos.com/calculator/5crifaekwa +// if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333) +// if (elevationDeg < 0) yf *= -2.0 * asin(xf - 1.0) / PI + if (elevationDeg < 0) yf *= Skybox.superellipsoidDecay(1.0 / 3.0, xf) + val theta = yf * HALF_PI + // vertical angle, where 0 is zenith, ±90 is ground (which is odd) + +// println("$yp\t$theta") + + val xyz = CIEXYZ( + ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 0).toFloat(), + ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 1).toFloat(), + ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, HALF_PI, 2).toFloat() + ) + val xyz2 = xyz.scaleToFit(elevationDeg) + val rgb = xyz2.toRGB().toColor() + val colour = rgb.toIntBits().toLittle() + + val imgOffX = 2 * (albedo0 * Skybox.elevCnt + elev0) + val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp) + val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) + for (i in 0..7) { + bytes[fileOffset + i] = colour[bytesLut[i]] + } + } + } + } + } + + println("Atlas generation done!") + + File("./assets/mods/basegame/weathers/main_skybox.tga").writeBytes(bytes) +} + +private val bytesLut = arrayOf(2,1,0,3,2,1,0,3) // For some reason BGRA order is what makes it work \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/clut/Skybox.kt b/src/net/torvald/terrarum/modulebasegame/clut/Skybox.kt index 4342b1717..28436404a 100644 --- a/src/net/torvald/terrarum/modulebasegame/clut/Skybox.kt +++ b/src/net/torvald/terrarum/modulebasegame/clut/Skybox.kt @@ -2,6 +2,7 @@ package net.torvald.terrarum.modulebasegame.clut import com.badlogic.gdx.graphics.Pixmap import com.badlogic.gdx.graphics.Texture +import com.badlogic.gdx.graphics.g2d.TextureRegion import com.badlogic.gdx.utils.Disposable import com.jme3.math.FastMath import net.torvald.colourutil.CIEXYZ @@ -10,8 +11,10 @@ import net.torvald.colourutil.toRGB import net.torvald.parametricsky.ArHosekSkyModel import net.torvald.terrarum.App import net.torvald.terrarum.App.printdbg +import net.torvald.terrarum.ModMgr import net.torvald.terrarum.abs import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import kotlin.math.* /** @@ -21,29 +24,39 @@ object Skybox : Disposable { const val gradSize = 64 - private val gradTexBinLowAlbedo: Array - private val gradTexBinHighAlbedo: Array + private lateinit var gradTexBinLowAlbedo: Array + private lateinit var gradTexBinHighAlbedo: Array - operator fun get(elevationDeg: Double, turbidity: Double, highAlbedo: Boolean = false): Texture { -// if (elevationDeg !in elevationsD) { -// throw IllegalArgumentException("Elevation not in ±75° (got $elevationDeg)") -// } -// if (turbidity !in turbiditiesD) { -// throw IllegalArgumentException("Turbidity not in 1..10 (got $turbidity)") -// } + private lateinit var tex: Texture + private lateinit var texRegions: TextureRegionPack - val elev = elevationDeg.coerceIn(elevationsD).toInt() - elevations.first - val turb = ((turbidity.coerceIn(turbiditiesD) - turbiditiesD.start) / (turbidities.step / 10.0)).toInt() + fun loadlut() { + tex = Texture(ModMgr.getGdxFile("basegame", "weathers/main_skybox.png")) + tex.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) + texRegions = TextureRegionPack(tex, 2, gradSize - 2, 0, 2, 0, 1) + } -// printdbg(this, "$elevationDeg $turbidity ; $elev $turb") + // use internal LUT + /*operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion { + val elev = elevationDeg.coerceIn(-75.0, 75.0).times(2.0).roundToInt().plus(150) + val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(3.0).roundToInt() + val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(5.0).roundToInt() + return gradTexBinLowAlbedo[elev * turbCnt + turb] + }*/ - return (if (highAlbedo) gradTexBinHighAlbedo else gradTexBinLowAlbedo)[elev * turbCnt + turb] + // use external LUT + operator fun get(elevationDeg: Double, turbidity: Double, albedo: Double): TextureRegion { + val elev = elevationDeg.coerceIn(-75.0, 75.0).roundToInt().plus(75) + val turb = turbidity.coerceIn(1.0, 10.0).minus(1.0).times(3.0).roundToInt() + val alb = albedo.coerceIn(0.1, 0.9).minus(0.1).times(5.0).roundToInt() + //printdbg(this, "elev $elevationDeg->$elev; turb $turbidity->$turb; alb $albedo->$alb") + return texRegions.get(alb * elevCnt + elev, turb) } private fun Float.scaleFun() = (1f - 1f / 2f.pow(this/6f)) * 0.97f - private fun CIEXYZ.scaleToFit(elevationDeg: Double): CIEXYZ { + internal fun CIEXYZ.scaleToFit(elevationDeg: Double): CIEXYZ { return if (elevationDeg >= 0) { CIEXYZ( this.X.scaleFun(), @@ -67,20 +80,21 @@ object Skybox : Disposable { } } - private val elevations = (-75..75) //zw 151 - private val elevationsD = (elevations.first.toDouble() .. elevations.last.toDouble()) - private val turbidityStep = 5 - private val turbidities = (1_0..10_0 step turbidityStep) // (100 / turbidityStep) - 1 - private val turbiditiesD = (turbidities.first / 10.0..turbidities.last / 10.0) - private val elevCnt = elevations.count() - private val turbCnt = turbidities.count() - private val albedoLow = 0.1 - private val albedoHight = 0.8 // for theoretical "winter wonderland"? - private val gamma = HALF_PI + val elevations = (0..150) // + val elevationsD = elevations.map { -75.0 + it } // -75, -74, -73, ..., 74, 75 // (specifically using whole number of angles because angle units any finer than 1.0 would make "hack" sunsut happen too fast) + val turbidities = (0..27) // 1, 1.333, 1.666, 2, 2,333, ... , 10.0 + val turbiditiesD = turbidities.map { 1.0 + it / 3.0 } + val albedos = arrayOf(0.1, 0.3, 0.5, 0.7, 0.9) + val elevCnt = elevations.count() + val turbCnt = turbidities.count() + val albedoCnt = albedos.size + val albedoLow = 0.1 + val albedoHight = 0.8 // for theoretical "winter wonderland"? + val gamma = HALF_PI - private fun Double.mapCircle() = sin(HALF_PI * this) + internal fun Double.mapCircle() = sin(HALF_PI * this) - init { + internal fun initiate() { printdbg(this, "Initialising skybox model") gradTexBinLowAlbedo = getTexturmaps(albedoLow) @@ -97,7 +111,7 @@ object Skybox : Disposable { * @param q polynomial degree. 2+. Larger value means sharper transition around the point p * @param x the 'x' value of the function, as in `y=f(x)`. 0.0..1.0 */ - private fun polynomialDecay(p: Double, q: Int, x: Double): Double { + internal fun polynomialDecay(p: Double, q: Int, x: Double): Double { val sign = if (q % 2 == 1) -1 else 1 val a1 = -1.0 / p val a2 = 1.0 / (1.0 - p) @@ -108,7 +122,7 @@ object Skybox : Disposable { sign * a2.pow(q - 1.0) * (x - 1.0).pow(q) } - private fun polynomialDecay2(p: Double, q: Int, x: Double): Double { + internal fun polynomialDecay2(p: Double, q: Int, x: Double): Double { val sign = if (q % 2 == 1) 1 else -1 val a1 = -1.0 / p val a2 = 1.0 / (1.0 - p) @@ -119,11 +133,11 @@ object Skybox : Disposable { sign * a2.pow(q - 1.0) * (x - 1.0).pow(q) + 1.0 } - private fun superellipsoidDecay(p: Double, x: Double): Double { + internal fun superellipsoidDecay(p: Double, x: Double): Double { return 1.0 - (1.0 - (1.0 - x).pow(1.0 / p)).pow(p) } - private fun Double.coerceInSmoothly(low: Double, high: Double): Double { + internal fun Double.coerceInSmoothly(low: Double, high: Double): Double { val x = this.coerceIn(low, high) val x2 = ((x - low) * (high - low).pow(-1.0)) // return FastMath.interpolateLinear(polynomialDecay2(0.5, 2, x2), low, high) @@ -133,25 +147,26 @@ object Skybox : Disposable { /** * To get the idea what the fuck is going on here, please refer to https://www.desmos.com/calculator/snqglcu2wl */ - private fun smoothLinear(p: Double, x0: Double): Double { + internal fun smoothLinear(p: Double, x0: Double): Double { val x = x0 - 0.5 - val t = 0.5 * sqrt(1.0 - 2.0 * p) + val p1 = sqrt(1.0 - 2.0 * p) + val t = 0.5 * p1 val y0 = if (x < -t) (1.0 / p) * (x + 0.5).pow(2) - 0.5 else if (x > t) -(1.0 / p) * (x - 0.5).pow(2) + 0.5 else - x * 2.0 / (1.0 + sqrt(1.0 - 2.0 * p)) + x * 2.0 / (1.0 + p1) return y0 + 0.5 } - private fun getTexturmaps(albedo: Double): Array { + private fun getTexturmaps(albedo: Double): Array { return Array(elevCnt * turbCnt) { - val elevationDeg = (it / turbCnt).plus(elevations.first).toDouble() + val elevationDeg = elevationsD[it / turbCnt] val elevationRad = Math.toRadians(elevationDeg) - val turbidity = 1.0 + (it % turbCnt) / (10.0 / turbidityStep) + val turbidity = turbiditiesD[it % turbCnt] val state = ArHosekSkyModel.arhosek_xyz_skymodelstate_alloc_init(turbidity, albedo, elevationRad.abs()) val pixmap = Pixmap(1, gradSize, Pixmap.Format.RGBA8888) @@ -161,15 +176,17 @@ object Skybox : Disposable { for (yp in 0 until gradSize) { val yi = yp - 3 val xf = -elevationDeg / 90.0 - var yf = (yi / 58.0).coerceInSmoothly(0.0, 0.95) + var yf = (yi / 58.0).coerceIn(0.0, 1.0).mapCircle().coerceInSmoothly(0.0, 0.95) // experiments visualisation: https://www.desmos.com/calculator/5crifaekwa // if (elevationDeg < 0) yf *= 1.0 - pow(xf, 0.333) // if (elevationDeg < 0) yf *= -2.0 * asin(xf - 1.0) / PI if (elevationDeg < 0) yf *= superellipsoidDecay(1.0 / 3.0, xf) - val theta = (yf.mapCircle() * HALF_PI) + val theta = yf * HALF_PI // vertical angle, where 0 is zenith, ±90 is ground (which is odd) +// println("$yp\t$theta") + val xyz = CIEXYZ( ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 0).toFloat(), ArHosekSkyModel.arhosek_tristim_skymodel_radiance(state, theta, gamma, 1).toFloat(), @@ -187,12 +204,13 @@ object Skybox : Disposable { it.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) } pixmap.dispose() - texture + TextureRegion(texture) } } override fun dispose() { - gradTexBinLowAlbedo.forEach { it.dispose() } - gradTexBinHighAlbedo.forEach { it.dispose() } + if (::gradTexBinLowAlbedo.isInitialized) gradTexBinLowAlbedo.forEach { it.texture.dispose() } + if (::gradTexBinHighAlbedo.isInitialized) gradTexBinHighAlbedo.forEach { it.texture.dispose() } + if (::tex.isInitialized) tex.dispose() } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/weather/WeatherMixer.kt b/src/net/torvald/terrarum/weather/WeatherMixer.kt index a6de8e962..33df34a6d 100644 --- a/src/net/torvald/terrarum/weather/WeatherMixer.kt +++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt @@ -152,7 +152,7 @@ internal object WeatherMixer : RNGConsumer { } var turbidity = 4.0; private set - private var gH = (4f/3f) * App.scr.height + private var gH = 1.4f * App.scr.height // private var gH = 0.8f * App.scr.height internal var parallaxPos = 0f; private set @@ -195,13 +195,17 @@ internal object WeatherMixer : RNGConsumer { gdxBlendNormalStraightAlpha() - val degThis = if (timeNow < HALF_DAY) solarElev.floorToDouble() else solarElev.ceilToDouble() + val degThis = if (timeNow < HALF_DAY) + solarElev.floorToDouble() + else + solarElev.ceilToDouble() val degNext = degThis + if (timeNow < HALF_DAY) 1 else -1 // Skybox.get has internal coerceIn val thisTurbidity = forceTurbidity ?: turbidity - val texture1 = Skybox[degThis, thisTurbidity] - val texture2 = Skybox[degNext, thisTurbidity] + // TODO trilinear with (deg, turb, alb) + val texture1 = Skybox[degThis, thisTurbidity, 0.1] + val texture2 = Skybox[degNext, thisTurbidity, 0.1] val lerpScale = (if (timeNow < HALF_DAY) solarElev - degThis else -(solarElev - degThis)).toFloat() // println("degThis=$degThis, degNext=$degNext, lerp=$lerpScale") @@ -210,10 +214,10 @@ internal object WeatherMixer : RNGConsumer { batch.inUse { batch.shader = null batch.color = Color.WHITE - batch.draw(texture1, 0f, gradY, App.scr.wf, gH) + batch.draw(texture1, -App.scr.halfwf, gradY, 2f * App.scr.wf, gH) batch.color = Color(1f, 1f, 1f, lerpScale) - batch.draw(texture2, 0f, gradY, App.scr.wf, gH) + batch.draw(texture2, -App.scr.halfwf, gradY, 2f * App.scr.wf, gH) batch.color = Color.WHITE }