some light cleanup, datatype: light level (inlined)

This commit is contained in:
Moritz Zwerger 2025-03-08 14:37:32 +01:00
parent d6223cf873
commit f0d70af460
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
29 changed files with 289 additions and 175 deletions

View File

@ -31,7 +31,7 @@ import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.registries.shapes.voxel.AbstractVoxelShape
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkLight
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition

View File

@ -22,7 +22,7 @@ import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.biome.WorldBiomes
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkLight
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition

View File

@ -31,6 +31,7 @@ object DebugOptions {
const val FORCE_CHECK_UPDATES = false
const val VERIFY_COORDINATES = true
const val VERIFY_LIGHT_LEVEL = true
// Add a test to ensure that all options are disabled!!!
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2024 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -22,8 +22,8 @@ import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.data.text.BaseComponent
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.world.chunk.light.SectionLight.Companion.BLOCK_LIGHT_MASK
import de.bixilon.minosoft.data.world.chunk.light.SectionLight.Companion.SKY_LIGHT_MASK
import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.protocol.network.session.play.PlaySession
open class CropBlock(resourceLocation: ResourceLocation, registries: Registries, data: Map<String, Any>) : PlantBlock(resourceLocation, registries, data), BlockWawlaProvider {
@ -35,14 +35,11 @@ open class CropBlock(resourceLocation: ResourceLocation, registries: Registries,
override fun getWawlaInformation(session: PlaySession, target: BlockTarget): ChatComponent {
val light = session.world.getLight(target.blockPosition)
val blockLight = light and BLOCK_LIGHT_MASK
val skyLight = (light and SKY_LIGHT_MASK) shr 4
val component = BaseComponent("Light: ")
component += if (blockLight < MIN_LIGHT_LEVEL) "§4$blockLight§r" else "§a$blockLight§r"
component += TextComponent(light.block).color(if (light.block < MIN_LIGHT_LEVEL) ChatColors.RED else ChatColors.GREEN)
component += " ($skyLight)"
component += " (${light.sky})"
return component
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.data.text.formatting.TextFormattable
import org.checkerframework.common.value.qual.IntRange
// TODO: JvmInline value class
class RGBColor(val rgba: Int) : TextFormattable {
val ansi: String get() = ANSI.rgb(red, green, blue)

View File

@ -28,8 +28,8 @@ import de.bixilon.minosoft.data.world.audio.WorldAudioPlayer
import de.bixilon.minosoft.data.world.biome.WorldBiomes
import de.bixilon.minosoft.data.world.border.WorldBorder
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkLightUtil.hasSkyLight
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.chunk.manager.ChunkManager
import de.bixilon.minosoft.data.world.difficulty.WorldDifficulty
import de.bixilon.minosoft.data.world.entities.WorldEntities
@ -177,17 +177,17 @@ class World(
return !iterator.hasCollisions(entity, aabb, fluids)
}
fun getLight(position: BlockPosition): Int {
return chunks[position.chunkPosition]?.light?.get(position.inChunkPosition) ?: 0x00
fun getLight(position: BlockPosition): LightLevel {
return chunks[position.chunkPosition]?.light?.get(position.inChunkPosition) ?: LightLevel.EMPTY
}
fun getBrightness(position: BlockPosition): Float {
val light = getLight(position)
var level = light and SectionLight.BLOCK_LIGHT_MASK
val level = getLight(position)
var max = level.block
if (dimension.hasSkyLight()) {
level = maxOf(level, light and SectionLight.SKY_LIGHT_MASK shr 4)
max = maxOf(max, level.sky)
}
return dimension.ambientLight[level]
return dimension.ambientLight[max]
}
fun recalculateLight(heightmap: Boolean = false) {

View File

@ -16,7 +16,7 @@ import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.entities.block.BlockEntity
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
import de.bixilon.minosoft.data.world.chunk.light.section.SectionLight
import de.bixilon.minosoft.data.world.container.SectionDataProvider
import de.bixilon.minosoft.data.world.container.biome.BiomeSectionDataProvider
import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.blocks.types.entity.BlockWithEntity
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.light.ChunkLight
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.chunk.update.block.ChunkLocalBlockUpdate
import de.bixilon.minosoft.data.world.chunk.update.block.SingleBlockUpdate

View File

@ -19,7 +19,7 @@ import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.blocks.types.entity.BlockWithEntity
import de.bixilon.minosoft.data.world.biome.source.BiomeSource
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.light.LightArray
import de.bixilon.minosoft.data.world.chunk.light.types.LightArray
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition

View File

@ -0,0 +1,24 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.data.world.chunk.light
import de.bixilon.minosoft.config.DebugOptions
object LightUtil {
inline fun assertLight(condition: Boolean) {
if (!DebugOptions.VERIFY_LIGHT_LEVEL) return
if (!condition) throw AssertionError("Invalid light level!")
}
}

View File

@ -11,13 +11,14 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.section
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.positions.InSectionPosition
abstract class AbstractSectionLight {
open var update = false
abstract operator fun get(position: InSectionPosition): Byte
abstract operator fun get(position: InSectionPosition): LightLevel
}

View File

@ -11,7 +11,7 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.section
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
@ -19,7 +19,10 @@ import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.heightmap.FixedHeightmap
import de.bixilon.minosoft.data.world.chunk.heightmap.LightHeightmap
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkLightUtil.hasSkyLight
import de.bixilon.minosoft.data.world.chunk.light.section.border.BottomSectionLight
import de.bixilon.minosoft.data.world.chunk.light.section.border.TopSectionLight
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.chunk.update.AbstractWorldUpdate
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkLightUpdate
@ -31,8 +34,8 @@ class ChunkLight(val chunk: Chunk) {
private val session = chunk.session
val heightmap = if (chunk.world.dimension.hasSkyLight()) LightHeightmap(chunk) else FixedHeightmap.MAX_VALUE
val bottom = BorderSectionLight(false, chunk)
val top = BorderSectionLight(true, chunk)
val bottom = BottomSectionLight(chunk)
val top = TopSectionLight(chunk)
val sky = ChunkSkyLight(this)
@ -101,19 +104,19 @@ class ChunkLight(val chunk: Chunk) {
}
operator fun get(position: InChunkPosition): Int {
operator fun get(position: InChunkPosition): LightLevel {
val sectionHeight = position.sectionHeight
val inSection = position.inSectionPosition
val light = when (sectionHeight) {
chunk.minSection - 1 -> bottom[inSection].toInt()
chunk.maxSection + 1 -> return top[inSection].toInt() or SectionLight.SKY_LIGHT_MASK // top has always sky=15
else -> chunk[sectionHeight]?.light?.get(inSection)?.toInt() ?: 0x00
} and 0xFF
chunk.minSection - 1 -> bottom[inSection]
chunk.maxSection + 1 -> return top[inSection].with(sky = LightLevel.MAX_LEVEL) // top has always sky=15; TODO: only if dimension has skylight?
else -> chunk[sectionHeight]?.light?.get(inSection) ?: LightLevel.EMPTY
}
if (position.y >= heightmap[position]) {
// set sky=15
return light or SectionLight.SKY_LIGHT_MASK
return light.with(sky = LightLevel.MAX_LEVEL)
}
return light
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -11,7 +11,7 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.section
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties

View File

@ -11,10 +11,10 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.section
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkLightUtil.hasSkyLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight

View File

@ -11,13 +11,15 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.section
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.light.TransparentProperty
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.light.ChunkSkyLight.Companion.NEIGHBOUR_TRACE_LEVEL
import de.bixilon.minosoft.data.world.chunk.light.section.ChunkSkyLight.Companion.NEIGHBOUR_TRACE_LEVEL
import de.bixilon.minosoft.data.world.chunk.light.types.LightArray
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
@ -54,7 +56,7 @@ class SectionLight(
private fun startDecreaseTrace(position: InSectionPosition) {
// that is kind of hacky, but far easier and kind of faster
val light = this.light[position].toInt() and BLOCK_LIGHT_MASK
val light = this.light[position].block
decreaseLight(position, light, true) // just clear the light
decreaseLight(position, light, false) // increase the light in all sections
@ -132,13 +134,13 @@ class SectionLight(
}
// get block or next luminance level
val blockSkyLight = this.light[position].toInt()
val currentLight = blockSkyLight and BLOCK_LIGHT_MASK // we just care about block light
val level = this.light[position]
val currentLight = level.block // we just care about block light
if (currentLight >= nextLuminance) {
// light is already higher, no need to trace
return
}
this.light[position] = ((blockSkyLight and SKY_LIGHT_MASK) or nextLuminance) // keep the sky light set
this.light[position] = level.with(block = nextLuminance) // keep the sky light set
if (!update) {
update = true
}
@ -269,13 +271,13 @@ class SectionLight(
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
val totalY = baseY + y
neighbours[Directions.O_WEST]?.light?.get(InSectionPosition(ProtocolDefinition.SECTION_MAX_Z, y, z))?.toInt()?.let { light ->
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(InSectionPosition(0, y, z), it - 1, Directions.EAST) }
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(InSectionPosition(0, y, z), it - 1, Directions.EAST, totalY) }
neighbours[Directions.O_WEST]?.light?.get(InSectionPosition(ProtocolDefinition.SECTION_MAX_Z, y, z))?.let { light ->
light.block.let { if (it > 1) traceBlockIncrease(InSectionPosition(0, y, z), it - 1, Directions.EAST) }
light.sky.let { if (it > 1) traceSkyLightIncrease(InSectionPosition(0, y, z), it - 1, Directions.EAST, totalY) }
}
neighbours[Directions.O_EAST]?.light?.get(InSectionPosition(0, y, z))?.toInt()?.let { light ->
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(InSectionPosition(ProtocolDefinition.SECTION_MAX_X, y, z), it - 1, Directions.WEST) }
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(InSectionPosition(ProtocolDefinition.SECTION_MAX_X, y, z), it - 1, Directions.WEST, totalY) }
neighbours[Directions.O_EAST]?.light?.get(InSectionPosition(0, y, z))?.let { light ->
light.block.let { if (it > 1) traceBlockIncrease(InSectionPosition(ProtocolDefinition.SECTION_MAX_X, y, z), it - 1, Directions.WEST) }
light.sky.let { if (it > 1) traceSkyLightIncrease(InSectionPosition(ProtocolDefinition.SECTION_MAX_X, y, z), it - 1, Directions.WEST, totalY) }
}
}
}
@ -284,13 +286,13 @@ class SectionLight(
private fun propagateZ(baseY: Int, neighbours: Array<ChunkSection?>, x: Int) {
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
val totalY = baseY + y
neighbours[Directions.O_NORTH]?.light?.get(InSectionPosition(x, y, ProtocolDefinition.SECTION_MAX_Z))?.toInt()?.let { light ->
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(InSectionPosition(x, y, 0), it - 1, Directions.SOUTH) }
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, y, 0), it - 1, Directions.SOUTH, totalY) }
neighbours[Directions.O_NORTH]?.light?.get(InSectionPosition(x, y, ProtocolDefinition.SECTION_MAX_Z))?.let { light ->
light.block.let { if (it > 1) traceBlockIncrease(InSectionPosition(x, y, 0), it - 1, Directions.SOUTH) }
light.sky.let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, y, 0), it - 1, Directions.SOUTH, totalY) }
}
neighbours[Directions.O_SOUTH]?.light?.get(InSectionPosition(x, y, 0))?.toInt()?.let { light ->
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(InSectionPosition(x, y, ProtocolDefinition.SECTION_MAX_Z), it - 1, Directions.NORTH) }
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, y, ProtocolDefinition.SECTION_MAX_Z), it - 1, Directions.NORTH, totalY) }
neighbours[Directions.O_SOUTH]?.light?.get(InSectionPosition(x, y, 0))?.let { light ->
light.block.let { if (it > 1) traceBlockIncrease(InSectionPosition(x, y, ProtocolDefinition.SECTION_MAX_Z), it - 1, Directions.NORTH) }
light.sky.let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, y, ProtocolDefinition.SECTION_MAX_Z), it - 1, Directions.NORTH, totalY) }
}
}
}
@ -298,13 +300,13 @@ class SectionLight(
private fun propagateY(neighbours: Array<ChunkSection?>, x: Int, baseY: Int) {
// ToDo: Border light
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
neighbours[Directions.O_DOWN]?.light?.get(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z))?.toInt()?.let { light ->
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(InSectionPosition(x, 0, z), it - 1, Directions.UP) }
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, 0, z), it - 1, Directions.UP, baseY + 0) } // ToDo: Is that possible?
neighbours[Directions.O_DOWN]?.light?.get(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z))?.let { light ->
light.block.let { if (it > 1) traceBlockIncrease(InSectionPosition(x, 0, z), it - 1, Directions.UP) }
light.sky.let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, 0, z), it - 1, Directions.UP, baseY + 0) } // ToDo: Is that possible?
}
neighbours[Directions.O_UP]?.light?.get(InSectionPosition(x, 0, z))?.toInt()?.let { light ->
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z), it - 1, Directions.DOWN) }
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z), it - 1, Directions.DOWN, baseY + ProtocolDefinition.SECTION_MAX_Y) }
neighbours[Directions.O_UP]?.light?.get(InSectionPosition(x, 0, z))?.let { light ->
light.block.let { if (it > 1) traceBlockIncrease(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z), it - 1, Directions.DOWN) }
light.sky.let { if (it > 1) traceSkyLightIncrease(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z), it - 1, Directions.DOWN, baseY + ProtocolDefinition.SECTION_MAX_Y) }
}
}
}
@ -317,8 +319,8 @@ class SectionLight(
return
}
val chunkNeighbours = chunk.neighbours.neighbours
val currentLight = this[position].toInt()
if (((currentLight and SKY_LIGHT_MASK) shr 4) >= nextLevel) {
val light = this[position]
if (light.sky >= nextLevel) {
return
}
@ -333,7 +335,7 @@ class SectionLight(
val neighbours = this.section.neighbours ?: return
this.light[position] = ((currentLight and BLOCK_LIGHT_MASK) or (nextLevel shl 4)).toByte()
this.light[position] = light.with(sky = nextLevel)
if (!update) {
update = true
@ -411,7 +413,7 @@ class SectionLight(
val neighbours = this.section.neighbours ?: return
this.light[position] = ((this[position].toInt() and BLOCK_LIGHT_MASK) or (ProtocolDefinition.MAX_LIGHT_LEVEL_I shl 4)).toByte()
this.light[position] = this.light[position].with(sky = ProtocolDefinition.MAX_LIGHT_LEVEL_I)
if (!update) {
update = true
@ -434,59 +436,46 @@ class SectionLight(
fun propagateFromNeighbours(position: InSectionPosition) {
val neighbours = section.neighbours ?: return
// TODO: those 2 values are boxed in wrapper classes (slow!)
var skyLight = 0
var blockLight = 0
fun pushLight(light: Byte) {
val nextSkyLight = light.toInt() and SKY_LIGHT_MASK shr 4
if (nextSkyLight > skyLight) {
skyLight = nextSkyLight
}
val nextBlockLight = light.toInt() and BLOCK_LIGHT_MASK
if (nextBlockLight > blockLight) {
blockLight = nextBlockLight
}
}
var level = LightLevel(0, 0)
// ToDo: check if light can exit block at side or can enter block at neighbour
if (position.x > 0) {
pushLight(this[position.minusX()])
level = level.max(this[position.minusX()])
} else {
neighbours[Directions.O_WEST]?.light?.get(position.with(x = ProtocolDefinition.SECTION_MAX_X))?.let { pushLight(it) }
neighbours[Directions.O_WEST]?.light?.get(position.with(x = ProtocolDefinition.SECTION_MAX_X))?.let { level = level.max(it) }
}
if (position.x < ProtocolDefinition.SECTION_MAX_X) {
pushLight(this[position.plusX()])
level = level.max(this[position.plusX()])
} else {
neighbours[Directions.O_EAST]?.light?.get(position.with(x = 0))?.let { pushLight(it) }
neighbours[Directions.O_EAST]?.light?.get(position.with(x = 0))?.let { level = level.max(it) }
}
if (position.y > 0) {
pushLight(this[position.minusY()])
level = level.max(this[position.minusY()])
} else {
neighbours[Directions.O_DOWN]?.light?.get(position.with(y = ProtocolDefinition.SECTION_MAX_Y))?.let { pushLight(it) }
neighbours[Directions.O_DOWN]?.light?.get(position.with(y = ProtocolDefinition.SECTION_MAX_Y))?.let { level = level.max(it) }
}
if (position.y < ProtocolDefinition.SECTION_MAX_Y) {
pushLight(this[position.plusY()])
level = level.max(this[position.plusY()])
} else {
neighbours[Directions.O_UP]?.light?.get(position.with(y = 0))?.let { pushLight(it) }
neighbours[Directions.O_UP]?.light?.get(position.with(y = 0))?.let { level = level.max(it) }
}
if (position.z > 0) {
pushLight(this[position.minusZ()])
level = level.max(this[position.minusZ()])
} else {
neighbours[Directions.O_NORTH]?.light?.get(position.with(z = ProtocolDefinition.SECTION_MAX_Z))?.let { pushLight(it) }
neighbours[Directions.O_NORTH]?.light?.get(position.with(z = ProtocolDefinition.SECTION_MAX_Z))?.let { level = level.max(it) }
}
if (position.z < ProtocolDefinition.SECTION_MAX_Z) {
pushLight(this[position.plusZ()])
level = level.max(this[position.plusZ()])
} else {
neighbours[Directions.O_SOUTH]?.light?.get(position.with(z = 0))?.let { pushLight(it) }
neighbours[Directions.O_SOUTH]?.light?.get(position.with(z = 0))?.let { level = level.max(it) }
}
traceBlockIncrease(position, blockLight - 1, null)
traceBlockIncrease(position, level.block - 1, null)
val totalY = section.height * ProtocolDefinition.SECTION_HEIGHT_Y + position.y
section.chunk.let {
@ -495,16 +484,11 @@ class SectionLight(
if (!it.neighbours.complete) return@let
val minHeight = it.light.sky.getNeighbourMinHeight(chunkNeighbours, position.x, position.z)
if (totalY > minHeight) {
skyLight = ProtocolDefinition.MAX_LIGHT_LEVEL_I
level = level.with(sky = ProtocolDefinition.MAX_LIGHT_LEVEL_I)
}
}
traceSkyLightIncrease(position, skyLight - 1, null, totalY)
traceSkyLightIncrease(position, level.sky - 1, null, totalY)
}
override fun get(position: InSectionPosition) = light[position]
companion object {
const val BLOCK_LIGHT_MASK = 0x0F
const val SKY_LIGHT_MASK = 0xF0
}
}

View File

@ -11,45 +11,47 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.section.border
import de.bixilon.kutil.array.ArrayUtil.getFirst
import de.bixilon.kutil.array.ArrayUtil.getLast
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.section.AbstractSectionLight
import de.bixilon.minosoft.data.world.chunk.light.types.LightArray
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import java.util.*
class BorderSectionLight(
val top: Boolean,
abstract class BorderSectionLight(
val chunk: Chunk,
) : AbstractSectionLight() {
val light = ByteArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z)
override fun get(position: InSectionPosition): Byte {
if ((top && position.y == 0) || (!top && position.y == ProtocolDefinition.SECTION_MAX_Y)) {
return light[position.xz]
}
return 0x00
protected abstract fun getNearestSection(): ChunkSection?
protected abstract fun Chunk.getBorderLight(): BorderSectionLight
protected inline operator fun get(index: Int) = LightLevel(this.light[index])
protected inline operator fun set(index: Int, value: LightLevel) {
this.light[index] = value.index
}
private fun updateY() {
// we can not further increase the light
if (top) {
chunk.sections.getLast()?.light?.apply { if (!update) update = true }
} else {
chunk.sections.getFirst()?.light?.apply { if (!update) update = true }
}
val section = getNearestSection()
section?.light?.apply { if (!update) update = true }
}
fun traceBlockIncrease(x: Int, z: Int, nextLuminance: Int) {
val index = z shl 4 or x
val currentLight = light[index].toInt() and SectionLight.BLOCK_LIGHT_MASK
if (currentLight >= nextLuminance) {
val currentLight = this[index]
if (currentLight.block >= nextLuminance) {
// light is already higher, no need to trace
return
}
this.light[index] = ((this.light[index].toInt() and SectionLight.SKY_LIGHT_MASK) or nextLuminance).toByte()
this[index] = currentLight.with(block = nextLuminance)
if (!update) {
update = true
@ -61,7 +63,7 @@ class BorderSectionLight(
}
val neighbourLuminance = nextLuminance - 1
if (top) {
if (this is TopSectionLight) { // TODO: slow check
chunk.sections.getLast()?.light?.traceBlockIncrease(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z), neighbourLuminance, Directions.DOWN)
} else {
chunk.sections.getFirst()?.light?.traceBlockIncrease(InSectionPosition(x, 0, z), neighbourLuminance, Directions.UP)
@ -90,19 +92,15 @@ class BorderSectionLight(
}
}
private fun Chunk.getBorderLight(): BorderSectionLight {
return if (top) light.top else light.bottom
}
fun traceSkyIncrease(x: Int, z: Int, nextLevel: Int) {
// TODO: check heightmap
val index = z shl 4 or x
val light = light[index].toInt()
if ((light and SectionLight.SKY_LIGHT_MASK shr 4) >= nextLevel) {
val light = this[index]
if (light.sky >= nextLevel) {
// light is already higher, no need to trace
return
}
this.light[index] = ((light and SectionLight.BLOCK_LIGHT_MASK) or (nextLevel shl 4)).toByte()
this[index] = light.with(sky = nextLevel)
if (!update) {
update = true
@ -114,7 +112,7 @@ class BorderSectionLight(
}
val neighbourLevel = nextLevel - 1
if (top) {
if (this is TopSectionLight) { // TOOD: slow check
chunk.sections.getLast()?.light?.traceSkyLightIncrease(InSectionPosition(x, ProtocolDefinition.SECTION_MAX_Y, z), neighbourLevel, Directions.DOWN, chunk.maxSection * ProtocolDefinition.SECTION_HEIGHT_Y + ProtocolDefinition.SECTION_MAX_Y)
} else {
chunk.sections.getFirst()?.light?.traceSkyLightIncrease(InSectionPosition(x, 0, z), neighbourLevel, Directions.UP, chunk.minSection * ProtocolDefinition.SECTION_HEIGHT_Y)
@ -170,9 +168,7 @@ class BorderSectionLight(
}
fun reset() {
for (i in light.indices) {
light[i] = 0x00
}
Arrays.fill(this.light, 0x00)
}
fun update(array: LightArray) {

View File

@ -0,0 +1,33 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.data.world.chunk.light.section.border
import de.bixilon.kutil.array.ArrayUtil.getFirst
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
class BottomSectionLight(
chunk: Chunk,
) : BorderSectionLight(chunk) {
override fun get(position: InSectionPosition): LightLevel {
if (position.y != ProtocolDefinition.SECTION_MAX_Y) return LightLevel.EMPTY
return LightLevel(this.light[position.xz])
}
override fun getNearestSection() = chunk.sections.getFirst()
override fun Chunk.getBorderLight() = this.light.bottom
}

View File

@ -0,0 +1,32 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.data.world.chunk.light.section.border
import de.bixilon.kutil.array.ArrayUtil.getLast
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.positions.InSectionPosition
class TopSectionLight(
chunk: Chunk,
) : BorderSectionLight(chunk) {
override fun get(position: InSectionPosition): LightLevel {
if (position.y != 0) return LightLevel.EMPTY
return LightLevel(this.light[position.xz])
}
override fun getNearestSection() = chunk.sections.getLast()
override fun Chunk.getBorderLight() = this.light.top
}

View File

@ -11,7 +11,7 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.world.chunk.light
package de.bixilon.minosoft.data.world.chunk.light.types
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
@ -20,13 +20,9 @@ import java.util.*
@JvmInline
value class LightArray(inline val array: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION)) {
inline operator fun get(position: InSectionPosition) = array[position.index]
inline operator fun set(position: InSectionPosition, value: Byte) {
array[position.index] = value
}
inline operator fun set(position: InSectionPosition, value: Int) {
array[position.index] = value.toByte()
inline operator fun get(position: InSectionPosition) = LightLevel(array[position.index])
inline operator fun set(position: InSectionPosition, value: LightLevel) {
array[position.index] = value.index
}
inline fun clear() = Arrays.fill(array, 0.toByte())

View File

@ -0,0 +1,51 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.data.world.chunk.light.types
import de.bixilon.minosoft.data.world.chunk.light.LightUtil.assertLight
@JvmInline
value class LightLevel(val index: Byte) {
constructor(block: Int, sky: Int) : this(((block shl BLOCK_SHIFT) or (sky shl SKY_SHIFT)).toByte()) {
assertLight(block >= MIN_LEVEL)
assertLight(block <= MAX_LEVEL)
assertLight(sky >= MIN_LEVEL)
assertLight(sky <= MAX_LEVEL)
}
inline val block: Int get() = (index.toInt() ushr BLOCK_SHIFT) and BLOCK_MASK
inline val sky: Int get() = (index.toInt() ushr SKY_SHIFT) and SKY_MASK
inline fun with(block: Int = this.block, sky: Int = this.sky) = LightLevel(block, sky)
inline fun max(other: LightLevel) = LightLevel(maxOf(block, other.block), maxOf(sky, other.sky))
companion object {
const val BLOCK_SHIFT = 0
const val BLOCK_MASK = 0x0F
const val SKY_SHIFT = 4
const val SKY_MASK = 0x0F
const val MIN_LEVEL = 0
const val MAX_LEVEL = 15
val EMPTY = LightLevel(0, 0)
val MAX = LightLevel(0, 0)
}
}

View File

@ -162,7 +162,7 @@ class FluidSectionMesher(
val light = chunk.light[InChunkPosition(x, position.y, z)]
addFluidVertices(meshToUse, positions, texturePositions, texture, tint, light)
addFluidVertices(meshToUse, positions, texturePositions, texture, tint, light.index.toInt())
rendered = true
}
// ToDo: Sides: Minecraft uses (for water) an overlay texture (with cullface) that is used, when the face fits to a non opaque block
@ -226,7 +226,7 @@ class FluidSectionMesher(
val meshToUse = mesh[model.flowing.transparency]
val fluidLight = chunk.light[InChunkPosition(x, offsetY + y, z)]
addFluidVertices(meshToUse, positions, texturePositions, model.flowing, tint, fluidLight)
addFluidVertices(meshToUse, positions, texturePositions, model.flowing, tint, fluidLight.index.toInt())
rendered = true
}

View File

@ -28,7 +28,7 @@ import de.bixilon.minosoft.data.registries.blocks.types.fluid.FluidBlock
import de.bixilon.minosoft.data.registries.blocks.types.properties.offset.OffsetBlock
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.SectionLight.Companion.SKY_LIGHT_MASK
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
@ -109,15 +109,15 @@ class SolidSectionMesher(
setX(neighbourBlocks, inChunk, neighbours, light, neighbourChunks, section, chunk)
val maxHeight = chunk.light.heightmap[inSection.xz]
light[SELF_LIGHT_INDEX] = section.light[inSection]
light[SELF_LIGHT_INDEX] = section.light[inSection].index
if (position.y > maxHeight) {
light[O_UP] = (light[O_UP].toInt() or SKY_LIGHT_MASK).toByte()
light[O_UP] = (light[O_UP].toInt() or MAX_SKY_LIGHT).toByte()
}
if (position.y >= maxHeight) {
light[SELF_LIGHT_INDEX] = (light[SELF_LIGHT_INDEX].toInt() or SKY_LIGHT_MASK).toByte()
light[SELF_LIGHT_INDEX] = (light[SELF_LIGHT_INDEX].toInt() or MAX_SKY_LIGHT).toByte()
}
if (position.y - 1 >= maxHeight) {
light[O_DOWN] = (light[O_DOWN].toInt() or SKY_LIGHT_MASK).toByte()
light[O_DOWN] = (light[O_DOWN].toInt() or MAX_SKY_LIGHT).toByte()
}
// TODO: cull neighbours
@ -154,21 +154,21 @@ class SolidSectionMesher(
neighbourBlocks[O_DOWN] = bedrock
} else {
neighbourBlocks[O_DOWN] = neighbours[O_DOWN]?.blocks?.let { it[position.with(y = ProtocolDefinition.SECTION_MAX_Y)] }
light[O_DOWN] = (if (lowest) chunk.light.bottom else neighbours[O_DOWN]?.light)?.get(position.with(y = ProtocolDefinition.SECTION_MAX_Y)) ?: 0x00
light[O_DOWN] = (if (lowest) chunk.light.bottom else neighbours[O_DOWN]?.light)?.get(position.with(y = ProtocolDefinition.SECTION_MAX_Y))?.index ?: 0x00
}
} else {
neighbourBlocks[O_DOWN] = section.blocks[position.minusY()]
light[O_DOWN] = section.light[position.minusY()]
light[O_DOWN] = section.light[position.minusY()].index
}
}
fun checkUp(highest: Boolean, position: InSectionPosition, neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, light: ByteArray, section: ChunkSection, chunk: Chunk) {
if (position.y == ProtocolDefinition.SECTION_MAX_Y) {
neighbourBlocks[O_UP] = neighbours[O_UP]?.blocks?.let { it[position.with(y = 0)] }
light[O_UP] = (if (highest) chunk.light.top else neighbours[O_UP]?.light)?.get(position.with(y = 0)) ?: 0x00
light[O_UP] = (if (highest) chunk.light.top else neighbours[O_UP]?.light)?.get(position.with(y = 0))?.index ?: 0x00
} else {
neighbourBlocks[O_UP] = section.blocks[position.plusY()]
light[O_UP] = section.light[position.plusY()]
light[O_UP] = section.light[position.plusY()].index
}
}
@ -202,14 +202,15 @@ class SolidSectionMesher(
private inline fun setNeighbour(blocks: Array<BlockState?>, position: InChunkPosition, light: ByteArray, section: ChunkSection?, chunk: Chunk?, direction: Int) {
val inSection = position.inSectionPosition
blocks[direction] = section?.blocks?.let { it[inSection] }
var level = section?.light?.get(inSection) ?: 0x00
var level = section?.light?.get(inSection)?.index ?: 0x00
if (chunk != null && position.y >= chunk.light.heightmap[position.xz]) {
level = (level.toInt() or SKY_LIGHT_MASK).toByte() // set sky light to 0x0F
level = (level.toInt() or MAX_SKY_LIGHT).toByte() // set sky light to 0x0F
}
light[direction] = level
}
companion object {
const val SELF_LIGHT_INDEX = 6 // after all directions
const val MAX_SKY_LIGHT = LightLevel.SKY_MASK shl LightLevel.SKY_SHIFT
}
}

View File

@ -19,6 +19,7 @@ import de.bixilon.kutil.math.interpolation.Interpolator
import de.bixilon.minosoft.data.entities.entities.Entity
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.ColorUtil
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.gui.rendering.entities.EntitiesRenderer
import de.bixilon.minosoft.gui.rendering.entities.easteregg.EntityEasterEggs.FLIP_ROTATION
import de.bixilon.minosoft.gui.rendering.entities.easteregg.EntityEasterEggs.isFlipped
@ -89,17 +90,17 @@ abstract class EntityRenderer<E : Entity>(
this.distance = (entity.renderInfo.eyePosition - renderer.session.camera.entity.renderInfo.eyePosition).length2()
}
private fun getCurrentLight(): Int {
var light = entity.physics.positionInfo.chunk?.light?.get(entity.physics.positionInfo.position.inChunkPosition) ?: return 0xFF
private fun getCurrentLight(): LightLevel {
var light = with(entity.physics.positionInfo) { chunk?.light?.get(position.inChunkPosition) } ?: return LightLevel.MAX
if (entity.isOnFire) {
light = light or 0xF0
light = light.with(block = LightLevel.MAX_LEVEL)
}
return light
}
protected open fun updateLight(delta: Float) {
if (this.light.delta >= 1.0f) {
val rgb = renderer.context.light.map.buffer[getCurrentLight()]
val rgb = renderer.context.light.map.buffer[getCurrentLight().index.toInt()]
this.light.push(rgb)
}
this.light.add(delta, 0.1f)

View File

@ -32,7 +32,6 @@ import de.bixilon.minosoft.data.text.BaseComponent
import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
import de.bixilon.minosoft.gui.rendering.entities.EntitiesRenderer
import de.bixilon.minosoft.gui.rendering.events.ResizeWindowEvent
@ -299,7 +298,7 @@ class DebugHUDElement(guiRenderer: GUIRenderer) : Element(guiRenderer), Layouted
this@DebugWorldInfo += AutoTextElement(guiRenderer, 1) { BaseComponent("Sky properties ", entity.session.world.dimension.effects) }
this@DebugWorldInfo += AutoTextElement(guiRenderer, 1) { BaseComponent("Biome ", biome) }
this@DebugWorldInfo += AutoTextElement(guiRenderer, 1) { with(entity.session.world.getLight(entity.renderInfo.eyePosition.blockPosition)) { BaseComponent("Light block=", (this and SectionLight.BLOCK_LIGHT_MASK), ", sky=", ((this and SectionLight.SKY_LIGHT_MASK) shr 4)) } }
this@DebugWorldInfo += AutoTextElement(guiRenderer, 1) { with(entity.session.world.getLight(entity.renderInfo.eyePosition.blockPosition)) { BaseComponent("Light block=", this.block, ", sky=", this.sky) } }
this@DebugWorldInfo += AutoTextElement(guiRenderer, 1) { BaseComponent("Fully loaded: ", chunk.neighbours.complete) }
lastChunk.value = chunk

View File

@ -17,7 +17,7 @@ import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.minosoft.data.registries.particle.data.ParticleData
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
import de.bixilon.minosoft.data.world.chunk.light.types.LightLevel
import de.bixilon.minosoft.gui.rendering.particle.types.Particle
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.blockPosition
import de.bixilon.minosoft.protocol.network.session.play.PlaySession
@ -34,26 +34,20 @@ abstract class RenderParticle(session: PlaySession, position: Vec3d, velocity: V
this.light = retrieveLight()
}
private fun retrieveLight(): Int {
private fun retrieveLight(): LightLevel {
val aabb = aabb + position
var maxBlockLight = emittingLight
var maxSkyLight = 0
var maxLevel = LightLevel.EMPTY.with(emittingLight)
val chunkPosition = position.blockPosition.chunkPosition
val chunk = getChunk() ?: return maxBlockLight
val chunk = getChunk() ?: return maxLevel
for (position in aabb.positions()) {
val next = chunk.neighbours.traceChunk(position.chunkPosition - chunkPosition)
val light = next?.light?.get(position.inChunkPosition) ?: SectionLight.SKY_LIGHT_MASK
if (light and SectionLight.BLOCK_LIGHT_MASK > maxBlockLight) {
maxBlockLight = light and SectionLight.BLOCK_LIGHT_MASK
}
if (light and SectionLight.SKY_LIGHT_MASK > maxSkyLight) {
maxSkyLight = light and SectionLight.SKY_LIGHT_MASK
}
val light = next?.light?.get(position.inChunkPosition) ?: LightLevel(0, LightLevel.MAX_LEVEL) // No chunk is given, assume there is light (otherwise particle might looks badly dark)
maxLevel = maxLevel.max(light)
}
return maxBlockLight or maxSkyLight
return maxLevel
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2024 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -32,6 +32,6 @@ abstract class TextureParticle(session: PlaySession, position: Vec3d, velocity:
when {
texture.transparency == TextureTransparencies.TRANSLUCENT || color.alpha != 255 -> translucentMesh
else -> mesh
}.addVertex(getCameraPosition(time), scale, texture, color, light = light)
}.addVertex(getCameraPosition(time), scale, texture, color, light = light.index.toInt())
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2024 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -32,6 +32,6 @@ abstract class AdvancedTextureParticle(session: PlaySession, position: Vec3d, ve
when {
texture.transparency == TextureTransparencies.TRANSLUCENT || color.alpha != 255 -> translucentMesh
else -> mesh
}.addVertex(getCameraPosition(time), scale, texture, color, minUV.array, maxUV.array, light)
}.addVertex(getCameraPosition(time), scale, texture, color, minUV.array, maxUV.array, light.index.toInt())
}
}

View File

@ -24,7 +24,7 @@ import de.bixilon.minosoft.data.world.biome.source.XZBiomeArray
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.chunk.ChunkPrototype
import de.bixilon.minosoft.data.world.chunk.light.LightArray
import de.bixilon.minosoft.data.world.chunk.light.types.LightArray
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.container.palette.PalettedContainerReader
import de.bixilon.minosoft.data.world.container.palette.palettes.BiomePaletteFactory

View File

@ -15,7 +15,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk.light
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.world.chunk.chunk.ChunkPrototype
import de.bixilon.minosoft.data.world.chunk.light.LightArray
import de.bixilon.minosoft.data.world.chunk.light.types.LightArray
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_1_16