outsource skybox color interpolation

This commit is contained in:
Moritz Zwerger 2023-11-17 07:47:09 +01:00
parent 239595986f
commit 33d84068e6
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
12 changed files with 377 additions and 352 deletions

View File

@ -13,7 +13,7 @@
package de.bixilon.minosoft.data.entities.entities
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kutil.random.RandomUtil.nextLong
import de.bixilon.kutil.random.RandomUtil.nextInt
import de.bixilon.minosoft.data.entities.EntityRotation
import de.bixilon.minosoft.data.entities.data.EntityData
import de.bixilon.minosoft.data.registries.entities.EntityFactory
@ -21,10 +21,9 @@ import de.bixilon.minosoft.data.registries.entities.EntityType
import de.bixilon.minosoft.data.registries.identified.Namespaces.minecraft
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil
class LightningBolt(connection: PlayConnection, entityType: EntityType, data: EntityData, position: Vec3d) : Entity(connection, entityType, data, position, EntityRotation(0.0f, 0.0f)) {
val duration = random.nextLong(100, 250)
val duration = random.nextInt(100, 250)
companion object : EntityFactory<LightningBolt> {
override val identifier: ResourceLocation = minecraft("lightning_bolt")

View File

@ -13,8 +13,8 @@
package de.bixilon.minosoft.data.text.formatting.color
import de.bixilon.kotlinglm.GLM.PIf
import de.bixilon.kutil.math.Trigonometry.sin
import kotlin.math.PI
object ColorInterpolation {
@ -46,7 +46,7 @@ object ColorInterpolation {
return end
}
return interpolateColor((sin(delta * PI.toFloat() / 2.0f)), start, end)
return interpolateColor((sin(delta * PIf / 2.0f)), start, end)
}
private fun interpolateColor(delta: Float, start: RGBColor, end: RGBColor): RGBColor {

View File

@ -17,13 +17,13 @@ import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec3.swizzle.xz
import de.bixilon.kutil.avg.FloatAverage
import de.bixilon.kutil.math.Trigonometry.sin
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.config.profile.profiles.rendering.camera.shaking.ShakingC
import de.bixilon.minosoft.gui.rendering.camera.Camera
import de.bixilon.minosoft.gui.rendering.renderer.drawable.Drawable
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.Z
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import kotlin.math.sin
class CameraShaking(
private val camera: Camera,

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.light.updater.normal
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.math.MathConstants.PIf
import de.bixilon.kutil.math.Trigonometry.sin
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
@ -32,7 +33,6 @@ import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
/**
* Updates the lightmap similar to vanilla
@ -136,7 +136,7 @@ class NormalLightmapUpdater(
var color = interpolateLinear(baseBrightness, THUNDER_BASE, THUNDER_BRIGHT) * baseBrightness * brightness * 0.3f
skyRenderer?.let { color = interpolateLinear(brightness * 5.0f + 0.5f, color, it.box.calculateLightingStrike(color)) }
skyRenderer?.let { color = interpolateLinear(brightness * 5.0f + 0.5f, color, it.box.color.calculateLightingStrike(color)) }
return interpolateLinear(thunder, base, color)
}

View File

@ -13,8 +13,9 @@
package de.bixilon.minosoft.gui.rendering.particle.types.render.texture.simple
import de.bixilon.kotlinglm.GLM
import de.bixilon.kotlinglm.GLM.PIf
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kutil.math.Trigonometry.sin
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.registries.particle.data.ParticleData
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
@ -22,7 +23,6 @@ import de.bixilon.minosoft.gui.rendering.particle.ParticleFactory
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.EMPTY
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import kotlin.math.sin
class NoteParticle(connection: PlayConnection, position: Vec3d, colorModifier: Float, data: ParticleData? = null) : SimpleTextureParticle(connection, position, Vec3d.EMPTY, data) {
@ -33,7 +33,7 @@ class NoteParticle(connection: PlayConnection, position: Vec3d, colorModifier: F
this.velocity.y += 0.2
fun getColor(offset: Float): Float {
return maxOf(0.0f, sin((colorModifier + offset) * 2 * GLM.PIf) * 0.65f + 0.35f)
return maxOf(0.0f, sin((colorModifier + offset) * 2.0f * PIf) * 0.65f + 0.35f)
}
this.color = RGBColor(

View File

@ -0,0 +1,239 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.sky.box
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.kutil.math.MathConstants.PIf
import de.bixilon.kutil.math.Trigonometry.sin
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.data.world.time.DayPhases
import de.bixilon.minosoft.data.world.time.MoonPhases
import de.bixilon.minosoft.data.world.time.WorldTime
import de.bixilon.minosoft.data.world.weather.WorldWeather
import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateLinear
import kotlin.math.abs
import kotlin.math.pow
class SkyboxColor(
val sky: SkyRenderer,
) {
private var lastStrike = -1L
private var strikeDuration = -1
private var baseColor: RGBColor? = null
var color: RGBColor? = null
private set
private fun calculateBiomeAvg(average: (Biome) -> RGBColor?): RGBColor? {
var radius = sky.profile.biomeRadius
radius *= radius
var red = 0
var green = 0
var blue = 0
var count = 0
val entity = sky.connection.camera.entity
val eyePosition = entity.renderInfo.eyePosition
val chunk = entity.physics.positionInfo.chunk ?: return null
val offset = Vec3i(eyePosition)
val dimension = sky.connection.world.dimension
val yRange: IntRange
if (dimension.supports3DBiomes) {
if (offset.y - radius < dimension.minY) {
offset.y = dimension.minY
yRange = IntRange(0, radius)
} else if (offset.y + radius > dimension.maxY) {
offset.y = dimension.maxY
yRange = IntRange(-radius, 0)
} else {
yRange = IntRange(-radius, radius)
}
} else {
yRange = 0..1
}
for (xOffset in -radius..radius) {
for (yOffset in yRange) {
for (zOffset in -radius..radius) {
if (xOffset * xOffset + yOffset * yOffset + zOffset * zOffset > radius) {
continue
}
val x = offset.x + xOffset
val y = offset.y + yOffset
val z = offset.z + zOffset
val neighbour = chunk.neighbours.trace(Vec2i(x shr 4, z shr 4)) ?: continue
val biome = neighbour.getBiome(x and 0x0F, y, z and 0x0F) ?: continue
count++
val color = average(biome) ?: continue
red += color.red
green += color.green
blue += color.blue
}
}
}
if (count == 0) {
return null
}
return RGBColor(red / count, green / count, blue / count)
}
fun calculateLightingStrike(original: Vec3): Vec3 {
val duration = this.strikeDuration
val delta = millis() - lastStrike
if (delta > duration) {
return original
}
val progress = delta / duration.toFloat()
val sine = abs(sin(progress * PIf * (duration / 80).toInt()))
return interpolateLinear(sine, original, Vec3(1.0f))
}
private fun calculateThunder(time: WorldTime, rain: Float, thunder: Float): Vec3? {
val rainColor = calculateRain(time, rain) ?: return null
val brightness = minOf(rainColor.length() * 2, 1.0f)
val thunderColor = interpolateLinear(brightness / 8, THUNDER_BASE_COLOR, rainColor)
thunderColor *= brightness
return calculateLightingStrike(interpolateLinear(thunder, rainColor, thunderColor))
}
private fun calculateRain(time: WorldTime, rain: Float): Vec3? {
val clearColor = calculateClear(time) ?: return null
val brightness = minOf(clearColor.length(), 1.0f)
val rainColor = interpolateLinear(brightness / 8, RAIN_BASE_COLOR, clearColor)
rainColor *= brightness
return interpolateLinear(rain, clearColor, rainColor)
}
private fun calculateSunrise(progress: Float, moon: MoonPhases): Vec3? {
val night = calculateNight(1.0f, moon) ?: return null
val day = calculateDaytime(0.0f) ?: return null
val baseColor = interpolateLinear(progress, night, day)
var color = Vec3(baseColor)
// make a bit more red/yellow
val delta = (abs(progress - 0.5f) * 2.0f)
val sine = maxOf(sin(delta.pow(2) * PIf / 2.0f), 0.6f)
color = interpolateLinear(sine, SUNRISE_BASE_COLOR, color)
color = interpolateLinear(sky.box.intensity, baseColor, color)
return color
}
private fun calculateDaytime(progress: Float): Vec3? {
val base = this.baseColor?.toVec3() ?: return null
return interpolateLinear((abs(progress - 0.5f) * 2.0f).pow(2), base, base * 0.9f)
}
private fun calculateSunset(progress: Float, moon: MoonPhases): Vec3? {
val night = calculateNight(0.0f, moon) ?: return null
val day = calculateDaytime(1.0f) ?: return null
val baseColor = interpolateLinear(progress, day, night)
var color = Vec3(baseColor)
// make a bit more red
val delta = (abs(progress - 0.5f) * 2.0f)
val sine = maxOf(sin(delta.pow(3) * PIf / 2.0f), 0.4f)
color = interpolateLinear(sine, SUNSET_BASE_COLOR, color)
color = interpolateLinear(sky.box.intensity, baseColor, color)
return color
}
private fun calculateNight(progress: Float, moon: MoonPhases): Vec3? {
val base = this.baseColor?.toVec3() ?: return null
base *= 0.1
return interpolateLinear((abs(progress - 0.5f) * 2.0f), NIGHT_BASE_COLOR, base) * moon.light
}
private fun calculateClear(time: WorldTime): Vec3? {
return when (time.phase) {
DayPhases.SUNRISE -> calculateSunrise(time.progress, time.moonPhase)
DayPhases.DAY -> calculateDaytime(time.progress)
DayPhases.SUNSET -> calculateSunset(time.progress, time.moonPhase)
DayPhases.NIGHT -> calculateNight(time.progress, time.moonPhase)
}
}
fun update(weather: WorldWeather, time: WorldTime): RGBColor? {
if (weather.thunder > 0.0f) {
return calculateThunder(time, weather.rain, weather.thunder)?.let { RGBColor(it) }
}
if (weather.raining) {
return calculateRain(time, weather.rain)?.let { RGBColor(it) }
}
return calculateClear(time)?.let { RGBColor(it) }
}
fun update(): RGBColor? {
val properties = sky.effects
val time = sky.time
if (properties.fixedTexture != null) {
// sky is a texture, no color (e.g. end)
return null
}
if (!properties.daylightCycle) {
// no daylight cycle (e.g. nether)
return calculateBiomeAvg { it.fogColor } // ToDo: Optimize
}
// TODO: Check if wither is present
var weather = sky.context.connection.world.weather
if (!properties.weather) {
weather = WorldWeather.SUNNY
}
return update(weather, time)
}
fun onStrike(duration: Int) {
lastStrike = millis()
strikeDuration = duration
}
fun updateBase() {
baseColor = calculateBiomeAvg(Biome::skyColor)
}
companion object {
private val THUNDER_BASE_COLOR = Vec3(0.16f, 0.18f, 0.21f)
private val RAIN_BASE_COLOR = Vec3(0.39f, 0.45f, 0.54f)
private val SUNRISE_BASE_COLOR = Vec3(0.95f, 0.78f, 0.56f)
private val SUNSET_BASE_COLOR = Vec3(0.95f, 0.68f, 0.56f)
private val NIGHT_BASE_COLOR = Vec3(0.02f, 0.04f, 0.09f)
}
}

View File

@ -13,69 +13,40 @@
package de.bixilon.minosoft.gui.rendering.sky.box
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.kutil.hash.HashUtil.murmur64
import de.bixilon.kutil.math.MathConstants.PIf
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.kutil.observer.DataObserver.Companion.observed
import de.bixilon.kutil.observer.set.SetObserver.Companion.observeSet
import de.bixilon.kutil.random.RandomUtil.nextFloat
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.data.entities.entities.LightningBolt
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.dimension.effects.DefaultDimensionEffects
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.data.text.formatting.color.RGBColor.Companion.asColor
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.update.WorldUpdateEvent
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkCreateUpdate
import de.bixilon.minosoft.data.world.chunk.update.chunk.NeighbourChangeUpdate
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.inChunkPosition
import de.bixilon.minosoft.data.world.time.DayPhases
import de.bixilon.minosoft.data.world.time.WorldTime
import de.bixilon.minosoft.data.world.weather.WorldWeather
import de.bixilon.minosoft.gui.rendering.events.CameraPositionChangeEvent
import de.bixilon.minosoft.gui.rendering.sky.SkyChildRenderer
import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateLinear
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.blockPosition
import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen
import java.util.*
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
class SkyboxRenderer(
private val sky: SkyRenderer,
) : SkyChildRenderer {
val color = SkyboxColor(sky)
private val textureCache: MutableMap<ResourceLocation, Texture> = mutableMapOf()
private val colorShader = sky.renderSystem.createShader(minosoft("sky/skybox")) { SkyboxColorShader(it) }
private val textureShader = sky.renderSystem.createShader(minosoft("sky/skybox/texture")) { SkyboxTextureShader(it) }
private val colorMesh = SkyboxMesh(sky.context)
private val textureMesh = SkyboxTextureMesh(sky.context)
private var updateColor = true
private var updateTexture = true
private var updateMatrix = true
private var color: RGBColor by observed(ChatColors.BLUE)
private var time: WorldTime = sky.context.connection.world.time
private var lastStrike = -1L
private var strikeDuration = -1L
private var cameraPosition: Vec3i? = null
private var chunkPosition: ChunkPosition? = null
private var chunk: Chunk? = null
private var baseColor: RGBColor? = null
private var day = -1L
var intensity = 1.0f
@ -85,44 +56,22 @@ class SkyboxRenderer(
init {
sky::matrix.observe(this) { updateMatrix = true }
this::color.observe(this) { updateColor = true }
// ToDo: Sync with lightmap, lightnings, etc
sky.context.connection.world.entities::entities.observeSet(this) {
val lightnings = it.adds.filterIsInstance<LightningBolt>()
if (lightnings.isEmpty()) return@observeSet
lastStrike = millis()
strikeDuration = lightnings.maxOf(LightningBolt::duration)
}
sky.context.connection.events.listen<CameraPositionChangeEvent> {
val blockPosition = it.newPosition.blockPosition
if (blockPosition == this.cameraPosition) {
return@listen
}
this.cameraPosition = blockPosition
val chunkPosition = blockPosition.chunkPosition
if (chunkPosition != this.chunkPosition) {
this.chunkPosition = chunkPosition
this.chunk = sky.context.connection.world.chunks[chunkPosition]
}
recalculateBaseColor()
color.onStrike(lightnings.maxOf(LightningBolt::duration))
}
sky.context.connection.events.listen<WorldUpdateEvent> {
if (it.update !is NeighbourChangeUpdate && it.update !is ChunkCreateUpdate) return@listen
if (it.update.chunkPosition != chunkPosition) return@listen
if (!it.update.chunk.neighbours.complete) {
return@listen
}
if (this.chunk != it.update.chunk) {
this.chunk = it.update.chunk
}
recalculateBaseColor()
if (!it.update.chunk.neighbours.complete) return@listen
color.updateBase()
}
sky.context.connection.events.listen<CameraPositionChangeEvent> {
color.updateBase()
}
}
private fun recalculateBaseColor() {
baseColor = calculateBiomeAvg(Biome::skyColor)
}
override fun onTimeUpdate(time: WorldTime) {
@ -150,10 +99,7 @@ class SkyboxRenderer(
}
private fun updateColorShader() {
if (updateColor) {
colorShader.skyColor = color
updateColor = false
}
colorShader.skyColor = color.color ?: DEFAULT_SKY_COLOR
if (updateMatrix) {
colorShader.skyViewProjectionMatrix = sky.matrix
updateMatrix = false
@ -173,7 +119,7 @@ class SkyboxRenderer(
}
override fun updateAsync() {
color = calculateSkyColor() ?: DEFAULT_SKY_COLOR
color.update()
}
override fun draw() {
@ -193,185 +139,7 @@ class SkyboxRenderer(
}
}
private fun calculateBiomeAvg(average: (Biome) -> RGBColor?): RGBColor? {
var radius = sky.profile.biomeRadius
radius *= radius
var red = 0
var green = 0
var blue = 0
var count = 0
val cameraPosition = this.cameraPosition?.inChunkPosition ?: return null
val offset = Vec3i(cameraPosition)
val chunk = this.chunk ?: return null
val dimension = sky.connection.world.dimension
val yRange: IntRange
if (dimension.supports3DBiomes) {
if (offset.y - radius < dimension.minY) {
offset.y = dimension.minY
yRange = IntRange(0, radius)
} else if (offset.y + radius > dimension.maxY) {
offset.y = dimension.maxY
yRange = IntRange(-radius, 0)
} else {
yRange = IntRange(-radius, radius)
}
} else {
yRange = 0..1
}
for (xOffset in -radius..radius) {
for (yOffset in yRange) {
for (zOffset in -radius..radius) {
if (xOffset * xOffset + yOffset * yOffset + zOffset * zOffset > radius) {
continue
}
val x = offset.x + xOffset
val y = offset.y + yOffset
val z = offset.z + zOffset
val neighbour = chunk.neighbours.trace(Vec2i(x shr 4, z shr 4)) ?: continue
val biome = neighbour.getBiome(x and 0x0F, y, z and 0x0F) ?: continue
count++
val color = average(biome) ?: continue
red += color.red
green += color.green
blue += color.blue
}
}
}
if (count == 0) {
return null
}
return RGBColor(red / count, green / count, blue / count)
}
fun calculateLightingStrike(original: Vec3): Vec3 {
val duration = this.strikeDuration
val delta = millis() - lastStrike
if (delta > duration) {
return original
}
val progress = delta / duration.toFloat()
val sine = abs(sin(progress * PIf * (duration / 80).toInt()))
return interpolateLinear(sine, original, Vec3(1.0f))
}
private fun calculateThunder(time: WorldTime, rain: Float, thunder: Float): Vec3? {
val rainColor = calculateRain(time, rain) ?: return null
val brightness = minOf(rainColor.length() * 2, 1.0f)
val thunderColor = interpolateLinear(brightness / 8, THUNDER_BASE_COLOR, rainColor)
thunderColor *= brightness
return calculateLightingStrike(interpolateLinear(thunder, rainColor, thunderColor))
}
private fun calculateRain(time: WorldTime, rain: Float): Vec3? {
val clearColor = calculateClear(time) ?: return null
val brightness = minOf(clearColor.length(), 1.0f)
val rainColor = interpolateLinear(brightness / 8, RAIN_BASE_COLOR, clearColor)
rainColor *= brightness
return interpolateLinear(rain, clearColor, rainColor)
}
private fun calculateSunrise(progress: Float): Vec3? {
val night = calculateNight(1.0f) ?: return null
val day = calculateDaytime(0.0f) ?: return null
val baseColor = interpolateLinear(progress, night, day)
var color = Vec3(baseColor)
// make a bit more red/yellow
val delta = (abs(progress - 0.5f) * 2.0f)
val sine = maxOf(sin(delta.pow(2) * PI.toFloat() / 2.0f), 0.6f)
color = interpolateLinear(sine, SUNRISE_BASE_COLOR, color)
color = interpolateLinear(intensity, baseColor, color)
return color
}
private fun calculateDaytime(progress: Float): Vec3? {
val base = this.baseColor?.toVec3() ?: return null
return interpolateLinear((abs(progress - 0.5f) * 2.0f).pow(2), base, base * 0.9f)
}
private fun calculateSunset(progress: Float): Vec3? {
val night = calculateNight(0.0f) ?: return null
val day = calculateDaytime(1.0f) ?: return null
val baseColor = interpolateLinear(progress, day, night)
var color = Vec3(baseColor)
// make a bit more red
val delta = (abs(progress - 0.5f) * 2.0f)
val sine = maxOf(sin(delta.pow(3) * PI.toFloat() / 2.0f), 0.4f)
color = interpolateLinear(sine, SUNSET_BASE_COLOR, color)
color = interpolateLinear(intensity, baseColor, color)
return color
}
private fun calculateNight(progress: Float): Vec3? {
val base = this.baseColor?.toVec3() ?: return null
base *= 0.1
return interpolateLinear((abs(progress - 0.5f) * 2.0f), NIGHT_BASE_COLOR, base) * time.moonPhase.light
}
private fun calculateClear(time: WorldTime): Vec3? {
return when (time.phase) {
DayPhases.SUNRISE -> calculateSunrise(time.progress)
DayPhases.DAY -> calculateDaytime(time.progress)
DayPhases.SUNSET -> calculateSunset(time.progress)
DayPhases.NIGHT -> calculateNight(time.progress)
}
}
private fun calculateSkyColor(): RGBColor? {
val properties = sky.effects
val time = time
if (properties.fixedTexture != null) {
// sky is a texture, no color (e.g. end)
return null
}
if (!properties.daylightCycle) {
// no daylight cycle (e.g. nether)
return calculateBiomeAvg { it.fogColor } // ToDo: Optimize
}
// TODO: Check if wither is present
var weather = sky.context.connection.world.weather
if (!properties.weather) {
weather = WorldWeather.SUNNY
}
if (weather.thunder > 0.0f) {
return calculateThunder(time, weather.rain, weather.thunder)?.let { RGBColor(it) }
}
if (weather.raining) {
return calculateRain(time, weather.rain)?.let { RGBColor(it) }
}
return calculateClear(time)?.let { RGBColor(it) }
}
companion object {
private val DEFAULT_SKY_COLOR = "#ecff89".asColor()
private val THUNDER_BASE_COLOR = Vec3(0.16f, 0.18f, 0.21f)
private val RAIN_BASE_COLOR = Vec3(0.39f, 0.45f, 0.54f)
private val SUNRISE_BASE_COLOR = Vec3(0.95f, 0.78f, 0.56f)
private val SUNSET_BASE_COLOR = Vec3(0.95f, 0.68f, 0.56f)
private val NIGHT_BASE_COLOR = Vec3(0.02f, 0.04f, 0.09f)
}
}

View File

@ -0,0 +1,109 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.sky.clouds
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.math.MathConstants.PIf
import de.bixilon.kutil.math.Trigonometry.sin
import de.bixilon.minosoft.data.world.time.DayPhases
import de.bixilon.minosoft.data.world.time.MoonPhases
import de.bixilon.minosoft.data.world.time.WorldTime
import de.bixilon.minosoft.data.world.weather.WorldWeather
import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateLinear
import kotlin.math.abs
import kotlin.math.pow
class CloudColor(
val sky: SkyRenderer,
) {
private fun normal(time: WorldTime): Vec3 {
return when (time.phase) {
DayPhases.DAY -> day(time.progress)
DayPhases.NIGHT -> night(time.progress, time.moonPhase)
DayPhases.SUNRISE -> sunrise(time.progress, time.moonPhase)
DayPhases.SUNSET -> sunset(time.progress, time.moonPhase)
}
}
private fun rain(time: WorldTime, rain: Float, thunder: Float): Vec3 {
val normal = normal(time)
val brightness = normal.length()
var color = RAIN_COLOR
color = color * maxOf(1.0f - thunder, 0.4f)
return interpolateLinear(maxOf(rain, thunder), normal, color) * brightness * 0.8f
}
private fun day(progress: Float): Vec3 {
return interpolateLinear((abs(progress - 0.5f) * 2).pow(2), DAY_COLOR, DAY_COLOR * 0.8f)
}
private fun night(progress: Float, moon: MoonPhases): Vec3 {
return interpolateLinear((abs(progress - 0.5f) * 2).pow(2), NIGHT_COLOR, DAY_COLOR * 0.2f) * moon.light
}
private fun sunrise(progress: Float, moon: MoonPhases): Vec3 {
val night = night(1.0f, moon)
val day = day(0.0f)
val base = interpolateLinear(progress, night, day)
var color = Vec3(base)
val sine = maxOf(sin((abs(progress - 0.5f) * 2.0f).pow(2) * PIf / 2.0f), 0.4f)
color = interpolateLinear(sine, SUNRISE_COLOR, color)
color = interpolateLinear(sky.box.intensity, base, color)
return color
}
private fun sunset(progress: Float, moon: MoonPhases): Vec3 {
val day = day(1.0f)
val night = night(0.0f, moon)
val base = interpolateLinear(progress, day, night)
var color = Vec3(base)
val sine = maxOf(sin((abs(progress - 0.5f) * 2.0f).pow(3) * PIf / 2.0f), 0.1f)
color = interpolateLinear(sine, SUNSET_COLOR, color)
color = interpolateLinear(sky.box.intensity, base, color)
return color
}
private fun calculate(weather: WorldWeather, time: WorldTime): Vec3 {
if (sky.effects.weather && (weather.rain > 0.0f || weather.thunder > 0.0f)) {
return rain(time, weather.rain, weather.thunder)
}
return normal(time)
}
fun calculate(): Vec3 {
return calculate(sky.connection.world.weather, sky.time)
}
companion object {
private val RAIN_COLOR = Vec3(0.31f, 0.35f, 0.40f)
private val SUNRISE_COLOR = Vec3(0.85f, 0.68f, 0.36f)
private val DAY_COLOR = Vec3(0.95f, 0.97f, 0.97f)
private val SUNSET_COLOR = Vec3(1.0f, 0.75f, 0.55f)
private val NIGHT_COLOR = Vec3(0.08f, 0.13f, 0.18f)
}
}

View File

@ -14,16 +14,10 @@
package de.bixilon.minosoft.gui.rendering.sky.clouds
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec4.Vec4
import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.world.time.DayPhases
import de.bixilon.minosoft.data.world.time.MoonPhases
import de.bixilon.minosoft.data.world.time.WorldTime
import de.bixilon.minosoft.data.world.weather.WorldWeather
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.renderer.renderer.AsyncRenderer
import de.bixilon.minosoft.gui.rendering.renderer.renderer.RendererBuilder
@ -33,27 +27,22 @@ import de.bixilon.minosoft.gui.rendering.sky.SkyRenderer
import de.bixilon.minosoft.gui.rendering.system.base.RenderSystem
import de.bixilon.minosoft.gui.rendering.system.base.layer.RenderLayer
import de.bixilon.minosoft.gui.rendering.system.base.settings.RenderSettings
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.interpolateLinear
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnectionStates
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.sin
class CloudRenderer(
private val sky: SkyRenderer,
val connection: PlayConnection,
override val context: RenderContext,
) : WorldRenderer, AsyncRenderer {
private val color = CloudColor(sky)
override val layers = LayerSettings()
override val renderSystem: RenderSystem = context.system
val shader = renderSystem.createShader(minosoft("sky/clouds")) { CloudShader(it) }
val matrix = CloudMatrix()
private val cloudLayers: MutableList<CloudLayer> = mutableListOf()
private var position = Vec2i(Int.MIN_VALUE)
private var color: Vec3 = Vec3.EMPTY
private var maxDistance = 0.0f
private var baseHeight = 0
private var nextLayers = 0
@ -169,74 +158,6 @@ class CloudRenderer(
}
}
private fun calculateDay(progress: Float): Vec3 {
return interpolateLinear((abs(progress - 0.5f) * 2).pow(2), DAY_COLOR, DAY_COLOR * 0.8f)
}
private fun calculateNight(progress: Float, moon: MoonPhases): Vec3 {
return interpolateLinear((abs(progress - 0.5f) * 2).pow(2), NIGHT_COLOR, DAY_COLOR * 0.2f) * moon.light
}
private fun calculateSunrise(progress: Float, moon: MoonPhases): Vec3 {
val night = calculateNight(1.0f, moon)
val day = calculateDay(0.0f)
val base = interpolateLinear(progress, night, day)
var color = Vec3(base)
val sine = maxOf(sin((abs(progress - 0.5f) * 2.0f).pow(2) * PI.toFloat() / 2.0f), 0.4f)
color = interpolateLinear(sine, SUNRISE_COLOR, color)
color = interpolateLinear(sky.box.intensity, base, color)
return color
}
fun calculateSunset(progress: Float, moon: MoonPhases): Vec3 {
val day = calculateDay(1.0f)
val night = calculateNight(0.0f, moon)
val base = interpolateLinear(progress, day, night)
var color = Vec3(base)
val sine = maxOf(sin((abs(progress - 0.5f) * 2.0f).pow(3) * PI.toFloat() / 2.0f), 0.1f)
color = interpolateLinear(sine, SUNSET_COLOR, color)
color = interpolateLinear(sky.box.intensity, base, color)
return color
}
private fun calculateNormal(time: WorldTime): Vec3 {
return when (time.phase) {
DayPhases.DAY -> calculateDay(time.progress)
DayPhases.NIGHT -> calculateNight(time.progress, time.moonPhase)
DayPhases.SUNRISE -> calculateSunrise(time.progress, time.moonPhase)
DayPhases.SUNSET -> calculateSunset(time.progress, time.moonPhase)
}
}
private fun calculateRainColor(time: WorldTime, rain: Float, thunder: Float): Vec3 {
val normal = calculateNormal(time)
val brightness = normal.length()
var color = RAIN_COLOR
color = color * maxOf(1.0f - thunder, 0.4f)
return interpolateLinear(maxOf(rain, thunder), normal, color) * brightness * 0.8f
}
private fun calculateCloudsColor(): Vec3 {
var weather = connection.world.weather
if (!sky.effects.weather) {
weather = WorldWeather.SUNNY
}
val time = sky.time
if (weather.rain > 0.0f || weather.thunder > 0.0f) {
return calculateRainColor(time, weather.rain, weather.thunder)
}
return calculateNormal(time)
}
private fun setYOffset() {
val y = (context.connection.camera.entity.renderInfo.eyePosition.y - context.camera.offset.offset.y).toFloat()
@ -249,11 +170,7 @@ class CloudRenderer(
}
private fun draw() {
val color = calculateCloudsColor()
if (color != this.color) {
shader.cloudsColor = Vec4(color, 1.0f)
this.color = color
}
shader.cloudsColor = color.calculate()
setYOffset()
@ -268,12 +185,6 @@ class CloudRenderer(
}
companion object : RendererBuilder<CloudRenderer> {
private val RAIN_COLOR = Vec3(0.31f, 0.35f, 0.40f)
private val SUNRISE_COLOR = Vec3(0.85f, 0.68f, 0.36f)
private val DAY_COLOR = Vec3(0.95f, 0.97f, 0.97f)
private val SUNSET_COLOR = Vec3(1.0f, 0.75f, 0.55f)
private val NIGHT_COLOR = Vec3(0.08f, 0.13f, 0.18f)
override fun build(connection: PlayConnection, context: RenderContext): CloudRenderer? {
val sky = context.renderer[SkyRenderer] ?: return null

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -15,13 +15,12 @@ package de.bixilon.minosoft.gui.rendering.sky.clouds
import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec4.Vec4
import de.bixilon.minosoft.gui.rendering.camera.FogManager
import de.bixilon.minosoft.gui.rendering.shader.Shader
import de.bixilon.minosoft.gui.rendering.shader.types.FogShader
import de.bixilon.minosoft.gui.rendering.shader.types.ViewProjectionShader
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
import de.bixilon.minosoft.gui.rendering.util.vec.vec4.Vec4Util.EMPTY
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.EMPTY
class CloudShader(
override val native: NativeShader,
@ -30,7 +29,7 @@ class CloudShader(
override var cameraPosition: Vec3 by cameraPosition()
override var fog: FogManager by fog()
var cloudsColor by uniform("uCloudsColor", Vec4.EMPTY)
var cloudsColor by uniform("uCloudsColor", Vec3.EMPTY)
var offset by uniform("uOffset", 0.0f)
var yOffset by uniform("uYOffset", 0.0f)
}

View File

@ -18,6 +18,8 @@ import de.bixilon.kotlinglm.mat4x4.Mat4
import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec4.Vec4
import de.bixilon.kotlinglm.vec4.swizzle.xyz
import de.bixilon.kutil.math.MathConstants.PIf
import de.bixilon.kutil.math.Trigonometry.sin
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.world.time.DayPhases
import de.bixilon.minosoft.data.world.time.WorldTime
@ -27,9 +29,7 @@ import de.bixilon.minosoft.gui.rendering.sky.planet.SunRenderer
import de.bixilon.minosoft.gui.rendering.system.base.BlendingFunctions
import de.bixilon.minosoft.gui.rendering.system.base.RenderingCapabilities
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3Util.Z
import kotlin.math.PI
import kotlin.math.abs
import kotlin.math.sin
class SunScatterRenderer(
private val sky: SkyRenderer,
@ -60,7 +60,7 @@ class SunScatterRenderer(
private fun calculateIntensity(progress: Float): Float {
val delta = (abs(progress) * 2.0f)
return maxOf(sin(delta * PI.toFloat() / 2.0f), 0.5f)
return maxOf(sin(delta * PIf / 2.0f), 0.5f)
}
private fun calculateSunPosition(): Vec3 {

View File

@ -13,6 +13,7 @@
package de.bixilon.minosoft.gui.rendering.util.vec.vec3
import de.bixilon.kotlinglm.GLM.PIf
import de.bixilon.kotlinglm.func.cos
import de.bixilon.kotlinglm.func.rad
import de.bixilon.kotlinglm.func.sin
@ -28,7 +29,6 @@ import de.bixilon.kutil.math.simple.FloatMath.floor
import de.bixilon.kutil.primitive.FloatUtil.toFloat
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import kotlin.math.PI
object Vec3Util {
private val EMPTY_INSTANCE = Vec3.EMPTY
@ -159,7 +159,7 @@ object Vec3Util {
return end
}
val sineDelta = sin(delta * PI.toFloat() / 2.0f)
val sineDelta = sin(delta * PIf / 2.0f)
fun interpolate(start: Float, end: Float): Float {
return start + sineDelta * (end - start)