From f0d70af460286e9f0e6501b08c6fd2867ffe4495 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Sat, 8 Mar 2025 14:37:32 +0100 Subject: [PATCH] some light cleanup, datatype: light level (inlined) --- .../data/world/chunk/LightTestingUtil.kt | 2 +- .../data/world/chunk/LightTestingUtil.kt | 2 +- .../bixilon/minosoft/config/DebugOptions.kt | 1 + .../blocks/types/pixlyzer/plant/CropBlock.kt | 13 +-- .../data/text/formatting/color/RGBColor.kt | 3 +- .../de/bixilon/minosoft/data/world/World.kt | 16 +-- .../minosoft/data/world/chunk/ChunkSection.kt | 2 +- .../minosoft/data/world/chunk/chunk/Chunk.kt | 2 +- .../data/world/chunk/chunk/ChunkPrototype.kt | 2 +- .../data/world/chunk/light/LightUtil.kt | 24 ++++ .../{ => section}/AbstractSectionLight.kt | 5 +- .../chunk/light/{ => section}/ChunkLight.kt | 23 ++-- .../light/{ => section}/ChunkLightUtil.kt | 4 +- .../light/{ => section}/ChunkSkyLight.kt | 4 +- .../chunk/light/{ => section}/SectionLight.kt | 110 ++++++++---------- .../border}/BorderSectionLight.kt | 52 ++++----- .../section/border/BottomSectionLight.kt | 33 ++++++ .../light/section/border/TopSectionLight.kt | 32 +++++ .../chunk/light/{ => types}/LightArray.kt | 12 +- .../world/chunk/light/types/LightLevel.kt | 51 ++++++++ .../chunk/mesher/FluidSectionMesher.kt | 4 +- .../chunk/mesher/SolidSectionMesher.kt | 23 ++-- .../entities/renderer/EntityRenderer.kt | 9 +- .../elements/other/debug/DebugHUDElement.kt | 3 +- .../particle/types/render/RenderParticle.kt | 20 ++-- .../types/render/texture/TextureParticle.kt | 4 +- .../advanced/AdvancedTextureParticle.kt | 4 +- .../packets/s2c/play/block/chunk/ChunkUtil.kt | 2 +- .../s2c/play/block/chunk/light/LightUtil.kt | 2 +- 29 files changed, 289 insertions(+), 175 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightUtil.kt rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => section}/AbstractSectionLight.kt (82%) rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => section}/ChunkLight.kt (87%) rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => section}/ChunkLightUtil.kt (90%) rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => section}/ChunkSkyLight.kt (98%) rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => section}/SectionLight.kt (81%) rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => section/border}/BorderSectionLight.kt (82%) create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BottomSectionLight.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/TopSectionLight.kt rename src/main/java/de/bixilon/minosoft/data/world/chunk/light/{ => types}/LightArray.kt (79%) create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightLevel.kt diff --git a/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt b/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt index 597d31bc1..c04ad3b31 100644 --- a/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt +++ b/src/benchmark/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt @@ -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 diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt index 4d5c8e1f1..5b53eac64 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/LightTestingUtil.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/config/DebugOptions.kt b/src/main/java/de/bixilon/minosoft/config/DebugOptions.kt index 70e12fd75..1d98b1fc9 100644 --- a/src/main/java/de/bixilon/minosoft/config/DebugOptions.kt +++ b/src/main/java/de/bixilon/minosoft/config/DebugOptions.kt @@ -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!!! } diff --git a/src/main/java/de/bixilon/minosoft/data/registries/blocks/types/pixlyzer/plant/CropBlock.kt b/src/main/java/de/bixilon/minosoft/data/registries/blocks/types/pixlyzer/plant/CropBlock.kt index 44a381602..b5fe40f5f 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/blocks/types/pixlyzer/plant/CropBlock.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/blocks/types/pixlyzer/plant/CropBlock.kt @@ -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) : 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 } diff --git a/src/main/java/de/bixilon/minosoft/data/text/formatting/color/RGBColor.kt b/src/main/java/de/bixilon/minosoft/data/text/formatting/color/RGBColor.kt index b355b4293..2f456d2a4 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/formatting/color/RGBColor.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/formatting/color/RGBColor.kt @@ -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) diff --git a/src/main/java/de/bixilon/minosoft/data/world/World.kt b/src/main/java/de/bixilon/minosoft/data/world/World.kt index b4d822bc1..8f2b82f65 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/World.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/World.kt @@ -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) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt index 97686e82c..534db8b13 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/ChunkSection.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt index fa9457a1e..68d6536ec 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/Chunk.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt index 116fc1db8..70a52a9fc 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/chunk/ChunkPrototype.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightUtil.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightUtil.kt new file mode 100644 index 000000000..f7fb9f397 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightUtil.kt @@ -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 . + * + * 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!") + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/AbstractSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt similarity index 82% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/AbstractSectionLight.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt index 88182ae65..35b941ac4 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/AbstractSectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt similarity index 87% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt index 2b9adc560..9dbee799d 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLightUtil.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLightUtil.kt similarity index 90% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLightUtil.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLightUtil.kt index e21de1190..b80fb905e 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkLightUtil.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLightUtil.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkSkyLight.kt similarity index 98% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkSkyLight.kt index 2853a9c29..1bd0bf653 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/ChunkSkyLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkSkyLight.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt similarity index 81% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt index a49ba1620..29ebeb9c3 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt @@ -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, 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, 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 - } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt similarity index 82% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt index 3538dbc0f..64287575e 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/BorderSectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt @@ -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) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BottomSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BottomSectionLight.kt new file mode 100644 index 000000000..38fed9691 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BottomSectionLight.kt @@ -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 . + * + * 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 +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/TopSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/TopSectionLight.kt new file mode 100644 index 000000000..2f9076473 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/TopSectionLight.kt @@ -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 . + * + * 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 +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightArray.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightArray.kt similarity index 79% rename from src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightArray.kt rename to src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightArray.kt index 7c6e42d16..256c295a9 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/LightArray.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightArray.kt @@ -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()) diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightLevel.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightLevel.kt new file mode 100644 index 000000000..8399dc24c --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/types/LightLevel.kt @@ -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 . + * + * 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) + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt index 7bc8bde54..dad08af50 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/FluidSectionMesher.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt index d1c68d687..c1b7c0b09 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt @@ -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, neighbours: Array, 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, 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 } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/EntityRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/EntityRenderer.kt index 6084db362..8ceb053ab 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/EntityRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/entities/renderer/EntityRenderer.kt @@ -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( 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) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt index 5ec58d0a9..76c53aa88 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/other/debug/DebugHUDElement.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/RenderParticle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/RenderParticle.kt index afb0b1f63..1cbf9a2f3 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/RenderParticle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/RenderParticle.kt @@ -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 } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt index 80b0878d0..6e8195202 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/TextureParticle.kt @@ -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()) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt index 83d4aecdf..eea8b5355 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/advanced/AdvancedTextureParticle.kt @@ -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()) } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt index d82413ec3..9e82fb6da 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/ChunkUtil.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/light/LightUtil.kt b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/light/LightUtil.kt index c7794fae0..aa4d909f0 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/light/LightUtil.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/s2c/play/block/chunk/light/LightUtil.kt @@ -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