diff --git a/assets/clut/skyboxavr.png b/assets/clut/skyboxavr.png new file mode 100644 index 000000000..5f7f2615e --- /dev/null +++ b/assets/clut/skyboxavr.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:924af7adbd863fefb11ebb3a0a1a9292e98a36b105f6c69bd07700eb906d5c3e +size 39510 diff --git a/assets/mods/basegame/weathers/clut_daylight.tga b/assets/mods/basegame/weathers/clut_daylight.tga index 24d27da41..222403a4b 100644 --- a/assets/mods/basegame/weathers/clut_daylight.tga +++ b/assets/mods/basegame/weathers/clut_daylight.tga @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fb7162449515ae86d941504eccc7bb8974a9d7e72de00d6928d3a8f1a73b6c2 -size 3618 +oid sha256:d2085a2519ccbf653275a7cec2e4d9fb71167df2042e6a5e96a2cb51dea92fca +size 2418 diff --git a/src/net/torvald/terrarum/GdxColorMap.kt b/src/net/torvald/terrarum/GdxColorMap.kt index 72d9fa7a0..6a73e0e39 100644 --- a/src/net/torvald/terrarum/GdxColorMap.kt +++ b/src/net/torvald/terrarum/GdxColorMap.kt @@ -73,6 +73,10 @@ class GdxColorMap { fun get(x: Int, y: Int): Color = dataGdxColor[y * width + x] operator fun get(x: Int): Color = if (is2D) throw UnsupportedOperationException("This is 2D color map") else dataGdxColor[x] + fun getCvec(x: Int, y: Int): Cvec = dataCvec[y * width + x] + fun getCvec(x: Int): Cvec = if (is2D) throw UnsupportedOperationException("This is 2D color map") else dataCvec[x] + + fun getRaw(x: Int, y: Int): RGBA8888 = dataRaw[y * width + x] fun getRaw(x: Int): RGBA8888 = if (is2D) throw UnsupportedOperationException("This is 2D color map") else dataRaw[x] diff --git a/src/net/torvald/terrarum/clut/GenerateSkyboxTextureAtlas.kt b/src/net/torvald/terrarum/clut/GenerateSkyboxTextureAtlas.kt index ddf73b9e4..9eb66153a 100644 --- a/src/net/torvald/terrarum/clut/GenerateSkyboxTextureAtlas.kt +++ b/src/net/torvald/terrarum/clut/GenerateSkyboxTextureAtlas.kt @@ -10,27 +10,30 @@ import net.torvald.terrarum.clut.Skybox.mapCircle import net.torvald.terrarum.clut.Skybox.scaleToFit import net.torvald.terrarum.modulebasegame.worldgenerator.HALF_PI import net.torvald.terrarum.serialise.toLittle +import net.torvald.terrarum.serialise.toUint +import net.torvald.terrarum.sqrt import java.io.File import kotlin.math.PI import kotlin.math.cos +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 = Skybox.elevCnt * Skybox.albedoCnt * 2 - val TGA_HEADER_SIZE = 18 +class GenerateSkyboxTextureAtlas { - val bytes = ByteArray(TGA_HEADER_SIZE + texw * texh * 4 + 26) - - fun generateStrip(gammaPair: Int, albedo: Double, turbidity: Double, elevationDeg: Double, writefun: (Int, Int, Byte) -> Unit) { + fun generateStrip( + gammaPair: Int, + albedo: Double, + turbidity: Double, + elevationDeg: Double, + writefun: (Int, Int, Byte) -> Unit + ) { val elevationRad = Math.toRadians(elevationDeg) /*val gamma = if (gammaPair == 0) HALF_PI else { - Math.toRadians(180 + 114 + 24 * cos(PI * elevationDeg / 40)) - }*/ + Math.toRadians(180 + 114 + 24 * cos(PI * elevationDeg / 40)) + }*/ val gamma = Math.toRadians(115 + 25 * cos(PI * elevationDeg / 40)) + (gammaPair * PI) // println("... Elevation: $elevationDeg") @@ -66,53 +69,147 @@ fun main() { } } - // 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() - } + // 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 TGA_HEADER_SIZE = 18 + val texh = Skybox.gradSize * Skybox.turbCnt + val texh2 = Skybox.turbCnt + val texw = Skybox.elevCnt * Skybox.albedoCnt * 2 + val bytesSize = texw * texh + val bytes2Size = texw * texh2 + val bytes = ByteArray(TGA_HEADER_SIZE + bytesSize * 4 + 26) + val bytes2 = ByteArray(TGA_HEADER_SIZE + texw * bytes2Size * 4 + 26) - println("Generating texture atlas ($texw x $texh)...") + fun generateMainFile() { + // 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, 0, 0, // unused for modern purposes + (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 (32bpp, bottom-left origin) + ).forEachIndexed { i, b -> bytes[i] = b } + // write footer + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000TRUEVISION-XFILE\u002E\u0000".forEachIndexed { i, c -> + bytes[TGA_HEADER_SIZE + bytesSize * 4 + i] = + c.code.toByte() + } - // write pixels - for (gammaPair in 0..1) { + println("Generating texture atlas ($texw x $texh)...") - 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) { - var elevationDeg = Skybox.elevationsD[elev0] - if (elevationDeg == 0.0) elevationDeg = 0.5 // dealing with the edge case - generateStrip(gammaPair, albedo, turbidity, elevationDeg) { yp, i, colour -> - val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair - val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp) - val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) - bytes[fileOffset + i] = colour + // write pixels + for (gammaPair in 0..1) { + + 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) { + var elevationDeg = Skybox.elevationsD[elev0] + if (elevationDeg == 0.0) elevationDeg = 0.5 // dealing with the edge case + generateStrip(gammaPair, albedo, turbidity, elevationDeg) { yp, i, colour -> + val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair + val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp) + val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) + bytes[fileOffset + i] = colour + } } } } } + + println("Atlas generation done!") + File("./assets/clut/skybox.tga").writeBytes(bytes) } - println("Atlas generation done!") + private val gradSizes = (0 until Skybox.gradSize) - File("./assets/clut/skybox.tga").writeBytes(bytes) + private fun getByte(gammaPair: Int, albedo0: Int, turb0: Int, elev0: Int, yp: Int, channel: Int): Byte { + val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair + val imgOffY = texh - 1 - (Skybox.gradSize * turb0 + yp) + val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) + return bytes[fileOffset + channel] + } + + fun generateCloudColourmap() { + if (bytes[TGA_HEADER_SIZE].toInt() == 0) throw IllegalStateException("Atlas not generated") + + // 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, 0, 0, // unused for modern purposes + (texw and 255).toByte(), (texw.ushr(8) and 255).toByte(), // width + (texh2 and 255).toByte(), (texh2.ushr(8) and 255).toByte(), // height + 32, // bits-per-pixel (8bpp RGBA) + 8 // image descriptor (32bpp, bottom-left origin) + ).forEachIndexed { i, b -> bytes2[i] = b } + // write footer + "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000TRUEVISION-XFILE\u002E\u0000".forEachIndexed { i, c -> + bytes2[TGA_HEADER_SIZE + bytes2Size * 4 + i] = + c.code.toByte() + } + + println("Generating cloud colour atlas ($texw x $texh2)...") + + for (gammaPair in 0..1) { + + 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 pixelValueAvrB = (gradSizes.sumOf { getByte(gammaPair, albedo0, turb0, elev0, it, 0).toUint() }.toDouble() / Skybox.gradSize).div(255.0).srgbLinearise().times(255.0).roundToInt().toByte() + val pixelValueAvrG = (gradSizes.sumOf { getByte(gammaPair, albedo0, turb0, elev0, it, 1).toUint() }.toDouble() / Skybox.gradSize).div(255.0).srgbLinearise().times(255.0).roundToInt().toByte() + val pixelValueAvrR = (gradSizes.sumOf { getByte(gammaPair, albedo0, turb0, elev0, it, 2).toUint() }.toDouble() / Skybox.gradSize).div(255.0).srgbLinearise().times(255.0).roundToInt().toByte() + val pixelValueAvrA = (gradSizes.sumOf { getByte(gammaPair, albedo0, turb0, elev0, it, 3).toUint() }.toDouble() / Skybox.gradSize).div(255.0).srgbLinearise().times(255.0).roundToInt().toByte() + + val colour = arrayOf(pixelValueAvrB, pixelValueAvrG, pixelValueAvrR, pixelValueAvrA) + + val imgOffX = albedo0 * Skybox.elevCnt + elev0 + Skybox.elevCnt * Skybox.albedoCnt * gammaPair + val imgOffY = texh2 - 1 - turb0 + val fileOffset = TGA_HEADER_SIZE + 4 * (imgOffY * texw + imgOffX) + + for (i in 0..3) { + bytes2[fileOffset + i] = colour[i] + } + + } + } + } + } + + println("Colourmap generation done!") + File("./assets/clut/skyboxavr.tga").writeBytes(bytes2) + } + + + fun invoke() { + generateMainFile() + generateCloudColourmap() + } + + private fun Double.srgbLinearise(): Double { + return if (this > 0.0031308) + 1.055 * this.pow(1 / 2.4) - 0.055 + else + this * 12.92 + } + + private val bytesLut = arrayOf(2, 1, 0, 3, 2, 1, 0, 3) // For some reason BGRA order is what makes it work } - -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 +fun main() { + GenerateSkyboxTextureAtlas().invoke() +} \ 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 fd5bfdd5b..8bf4582d1 100644 --- a/src/net/torvald/terrarum/weather/WeatherMixer.kt +++ b/src/net/torvald/terrarum/weather/WeatherMixer.kt @@ -20,6 +20,8 @@ import net.torvald.terrarum.gameworld.WorldTime import net.torvald.terrarum.gameworld.WorldTime.Companion.DAY_LENGTH import net.torvald.terrarum.RNGConsumer import net.torvald.terrarum.clut.Skybox +import net.torvald.terrarum.clut.Skybox.elevCnt +import net.torvald.terrarum.spriteassembler.ADPropertyObject import net.torvald.terrarum.utils.JsonFetcher import net.torvald.terrarum.utils.forEachSiblings import net.torvald.terrarum.weather.WeatherObjectCloud.Companion.ALPHA_ROLLOFF_Z @@ -94,7 +96,7 @@ internal object WeatherMixer : RNGConsumer { var forceTurbidity: Double? = null // doesn't work if the png is in greyscale/indexed mode - val starmapTex: TextureRegion = TextureRegion(Texture(Gdx.files.internal("./assets/graphics/astrum.png"))).also { + val starmapTex: TextureRegion = TextureRegion(Texture(Gdx.files.internal("assets/graphics/astrum.png"))).also { it.texture.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear) it.texture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat) } @@ -105,7 +107,6 @@ internal object WeatherMixer : RNGConsumer { private var astrumOffX = 0f private var astrumOffY = 0f - // Clouds are merely a response to the current Weatherbox status // private val clouds = SortedArrayList() @@ -115,6 +116,10 @@ internal object WeatherMixer : RNGConsumer { val cloudSpawnMax: Int get() = 256 shl (App.getConfigInt("maxparticles") / 256) + + private val skyboxavr = GdxColorMap(Gdx.files.internal("assets/clut/skyboxavr.png")) + + override fun loadFromSave(ingame: IngameInstance, s0: Long, s1: Long) { super.loadFromSave(ingame, s0, s1) internalReset(s0, s1) @@ -498,6 +503,10 @@ internal object WeatherMixer : RNGConsumer { private var turbidity1 = 1.0 /** Interpolated value, controlled by the weatherbox */ var turbidity = 1.0; private set + + /** Controlled by todo: something that monitors ground tile compisition */ + var albedo = 1.0; private set + private var gH = 1.8f * App.scr.height // private var gH = 0.8f * App.scr.height @@ -520,6 +529,10 @@ internal object WeatherMixer : RNGConsumer { batch.color = Color.WHITE } + /** + * Dependent on the `drawSkybox(camera, batch, world)` for the `cloudDrawColour` + * + */ private fun drawClouds(batch: SpriteBatch) { batch.inUse { _ -> batch.shader = shaderClouds @@ -546,11 +559,9 @@ internal object WeatherMixer : RNGConsumer { val daylightClut = currentWeather.daylightClut // calculate global light val moonSize = (-(2.0 * world.worldTime.moonPhase - 1.0).abs() + 1.0).toFloat() - val globalLightBySun: Cvec = getGradientColour2(daylightClut, solarElev, timeNow, GradientColourMode.DAYLIGHT) + val globalLightBySun: Cvec = getGradientColour2(daylightClut, solarElev, timeNow) val globalLightByMoon: Cvec = moonlightMax * moonSize - val cloudCol = getGradientColour2(daylightClut, solarElev, timeNow, GradientColourMode.CLOUD_COLOUR) globalLightNow.set(globalLightBySun max globalLightByMoon) - cloudDrawColour.set(cloudCol max globalLightByMoon) /* (copied from the shader source) UV mapping coord.y @@ -574,16 +585,27 @@ internal object WeatherMixer : RNGConsumer { val mornNoonBlend = (1f/4000f * (timeNow - 43200) + 0.5f).coerceIn(0f, 1f) // 0.0 at T41200; 0.5 at T43200; 1.0 at T45200; turbidity0 = (world.weatherbox.oldWeather.json.getDouble("atmoTurbidity") + turbidityCoeff * 2.5).coerceIn(1.0, 10.0) - turbidity1 = (currentWeather.json.getDouble("atmoTurbidity") + turbidityCoeff * 2.5).coerceIn(1.0, 10.0) + turbidity1 = (currentWeather.json.getDouble("atmoTurbidity") + turbidityCoeff * 2.5).coerceIn(1.0, 10.0) turbidity = FastMath.interpolateLinear(oldNewBlend.toDouble(), turbidity0, turbidity1) - val oldTurbidity = forceTurbidity ?: turbidity0 val thisTurbidity = forceTurbidity ?: turbidity1 + albedo = 0.3 // TODO() depends on the ground tile composition + val oldAlbedo = forceTurbidity ?: turbidity0 + val thisAlbedo = forceTurbidity ?: turbidity1 + + + + + val cloudCol1 = getGradientCloud(skyboxavr, solarElev, mornNoonBlend.toDouble(), turbidity, albedo) + cloudDrawColour.set(lerp(0.5, cloudCol1, globalLightNow)) + + + val gradY = -(gH - App.scr.height) * ((parallax + 1f) / 2f) - val (tex, uvs, turbTihsBlend, albThisBlend, turbOldBlend, albOldBlend) = Skybox.getUV(solarElev, oldTurbidity, 0.3, thisTurbidity, 0.3) + val (tex, uvs, turbTihsBlend, albThisBlend, turbOldBlend, albOldBlend) = Skybox.getUV(solarElev, oldTurbidity, oldAlbedo, thisTurbidity, thisAlbedo) starmapTex.texture.bind(1) Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0) // so that batch that comes next will bind any tex to it @@ -672,7 +694,7 @@ internal object WeatherMixer : RNGConsumer { return Cvec(newCol) } - fun getGradientColour2(colorMap: GdxColorMap, solarAngleInDeg: Double, timeOfDay: Int, mode: GradientColourMode): Cvec { + fun getGradientColour2(colorMap: GdxColorMap, solarAngleInDeg: Double, timeOfDay: Int): Cvec { val pNowRaw = (solarAngleInDeg + 75.0) / 150.0 * colorMap.width val pStartRaw = pNowRaw.floorToInt() @@ -695,27 +717,80 @@ internal object WeatherMixer : RNGConsumer { var scale = (pNowRaw - pStartRaw).toFloat() if (timeOfDay >= HALF_DAY) scale = 1f - scale - return when (mode) { - GradientColourMode.DAYLIGHT -> { - val colourThisRGB = colorMap.get(pSx, pSy) - val colourNextRGB = colorMap.get(pNx, pNy) - val colourThisUV = colorMap.get(pSx, pSy + 2) - val colourNextUV = colorMap.get(pNx, pNy + 2) + val colourThisRGB = colorMap.get(pSx, pSy) + val colourNextRGB = colorMap.get(pNx, pNy) + val colourThisUV = colorMap.get(pSx, pSy + 2) + val colourNextUV = colorMap.get(pNx, pNy + 2) - val newColRGB = colourThisRGB.cpy().lerp(colourNextRGB, scale)//CIELuvUtil.getGradient(scale, colourThis, colourNext) - val newColUV = colourThisUV.cpy().lerp(colourNextUV, scale)//CIELuvUtil.getGradient(scale, colourThis, colourNext) + val newColRGB = colourThisRGB.cpy().lerp(colourNextRGB, scale)//CIELuvUtil.getGradient(scale, colourThis, colourNext) + val newColUV = colourThisUV.cpy().lerp(colourNextUV, scale)//CIELuvUtil.getGradient(scale, colourThis, colourNext) - Cvec(newColRGB, newColUV.r) - } - GradientColourMode.CLOUD_COLOUR -> { - val colourThisRGB = colorMap.get(pSx, pSy + 4) - val colourNextRGB = colorMap.get(pNx, pNy + 4) + return Cvec(newColRGB, newColUV.r) + } - val newColRGB = colourThisRGB.cpy().lerp(colourNextRGB, scale)//CIELuvUtil.getGradient(scale, colourThis, colourNext) + fun getGradientCloud(colorMap: GdxColorMap, solarAngleInDeg: Double, mornNoonBlend: Double, turbidity: Double, albedo: Double): Cvec { + // fine-grained + val angleX1 = solarAngleInDeg.toInt() + 75 + val angleX2 = (angleX1 + 1).coerceAtMost(150) + val ax = solarAngleInDeg % 1.0 + // fine-grained + val turbY = turbidity.coerceIn(Skybox.turbiditiesD.first(), Skybox.turbiditiesD.last()).minus(1.0).times(Skybox.turbDivisor) + val turbY1 = turbY.floorToInt() + val turbY2 = (turbY1).coerceAtMost(Skybox.turbCnt - 1) + val tx = turbY - turbY1 + // coarse-grained + val albX = albedo.coerceIn(Skybox.albedos.first(), Skybox.albedos.last()).times(5.0) * Skybox.elevCnt // 0*151..5*151 + val albX1 = albX.floorToInt() + val albX2 = (albX1 + elevCnt).coerceAtMost(5 * Skybox.elevCnt) + val bx = albX - albX1 - Cvec(newColRGB) - } - } + val a1t1b1A = colorMap.getCvec(albX1 * elevCnt + angleX1, turbY1) + val a2t1b1A = colorMap.getCvec(albX1 * elevCnt + angleX2, turbY1) + val a1t2b1A = colorMap.getCvec(albX1 * elevCnt + angleX1, turbY2) + val a2t2b1A = colorMap.getCvec(albX1 * elevCnt + angleX2, turbY2) + val a1t1b2A = colorMap.getCvec(albX2 * elevCnt + angleX1, turbY1) + val a2t1b2A = colorMap.getCvec(albX2 * elevCnt + angleX2, turbY1) + val a1t2b2A = colorMap.getCvec(albX2 * elevCnt + angleX1, turbY2) + val a2t2b2A = colorMap.getCvec(albX2 * elevCnt + angleX2, turbY2) + + val a1t1b1B = colorMap.getCvec(albX1 * elevCnt + angleX1 + Skybox.albedoCnt * elevCnt, turbY1) + val a2t1b1B = colorMap.getCvec(albX1 * elevCnt + angleX2 + Skybox.albedoCnt * elevCnt, turbY1) + val a1t2b1B = colorMap.getCvec(albX1 * elevCnt + angleX1 + Skybox.albedoCnt * elevCnt, turbY2) + val a2t2b1B = colorMap.getCvec(albX1 * elevCnt + angleX2 + Skybox.albedoCnt * elevCnt, turbY2) + val a1t1b2B = colorMap.getCvec(albX2 * elevCnt + angleX1 + Skybox.albedoCnt * elevCnt, turbY1) + val a2t1b2B = colorMap.getCvec(albX2 * elevCnt + angleX2 + Skybox.albedoCnt * elevCnt, turbY1) + val a1t2b2B = colorMap.getCvec(albX2 * elevCnt + angleX1 + Skybox.albedoCnt * elevCnt, turbY2) + val a2t2b2B = colorMap.getCvec(albX2 * elevCnt + angleX2 + Skybox.albedoCnt * elevCnt, turbY2) + + val t1b1A = lerp(ax, a1t1b1A, a2t1b1A) + val t2b1A = lerp(ax, a1t2b1A, a2t2b1A) + val t1b2A = lerp(ax, a1t1b2A, a2t1b2A) + val t2b2A = lerp(ax, a1t2b2A, a2t2b2A) + + val b1A = lerp(tx, t1b1A, t2b1A) + val b2A = lerp(tx, t1b2A, t2b2A) + + val A = lerp(bx, b1A, b2A) + + val t1b1B = lerp(ax, a1t1b1B, a2t1b1B) + val t2b1B = lerp(ax, a1t2b1B, a2t2b1B) + val t1b2B = lerp(ax, a1t1b2B, a2t1b2B) + val t2b2B = lerp(ax, a1t2b2B, a2t2b2B) + + val b1B = lerp(tx, t1b1B, t2b1B) + val b2B = lerp(tx, t1b2B, t2b2B) + + val B = lerp(bx, b1B, b2B) + + return lerp(mornNoonBlend, A, B) + } + + private fun lerp(x: Double, c1: Cvec, c2: Cvec): Cvec { + val r = (((1.0 - x) * c1.r) + (x * c2.r)).toFloat() + val g = (((1.0 - x) * c1.g) + (x * c2.g)).toFloat() + val b = (((1.0 - x) * c1.b) + (x * c2.b)).toFloat() + val a = (((1.0 - x) * c1.a) + (x * c2.a)).toFloat() + return Cvec(r, g, b, a) } fun getWeatherList(classification: String) = weatherDB[classification]!!