From decee490712856cfd71afcefcd9ab9d841efad67 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Sat, 18 Jun 2022 11:44:27 +0200 Subject: [PATCH] basic block light engine --- .../data/registries/blocks/BlockState.kt | 3 + .../de/bixilon/minosoft/data/world/Chunk.kt | 13 +++- .../minosoft/data/world/ChunkSection.kt | 78 ++++++++++++++++++- .../world/container/SectionDataProvider.kt | 4 +- 4 files changed, 92 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/data/registries/blocks/BlockState.kt b/src/main/java/de/bixilon/minosoft/data/registries/blocks/BlockState.kt index 47658c2ac..4c400d5cc 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/blocks/BlockState.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/blocks/BlockState.kt @@ -16,6 +16,7 @@ import de.bixilon.kutil.cast.CastUtil.unsafeCast import de.bixilon.kutil.json.JsonUtil.toJsonObject import de.bixilon.kutil.primitive.BooleanUtil.toBoolean import de.bixilon.kutil.primitive.FloatUtil.toFloat +import de.bixilon.kutil.primitive.IntUtil.toInt import de.bixilon.minosoft.data.registries.ResourceLocation import de.bixilon.minosoft.data.registries.VoxelShape import de.bixilon.minosoft.data.registries.blocks.properties.BlockProperties @@ -34,6 +35,7 @@ data class BlockState( val hardness: Float, val requiresTool: Boolean, val isSolid: Boolean, + val luminance: Byte, ) { var blockModel: BakedBlockModel? = null @@ -131,6 +133,7 @@ data class BlockState( hardness = data["hardness"]?.toFloat() ?: 1.0f, requiresTool = data["requires_tool"]?.toBoolean() ?: material.soft, isSolid = data["solid_render"]?.toBoolean() ?: false, + luminance = data["luminance"]?.toInt()?.toByte() ?: 0, ) } diff --git a/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt index ebe330849..db4a90001 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/Chunk.kt @@ -78,8 +78,17 @@ class Chunk( fun set(x: Int, y: Int, z: Int, blockState: BlockState?, blockEntity: BlockEntity? = null) { val section = getOrPut(y.sectionHeight) ?: return - section.blocks[x, y.inSectionHeight, z] = blockState - section.blockEntities[x, y.inSectionHeight, z] = blockEntity // ToDo + val inSectionHeight = y.inSectionHeight + val previous = section.blocks.set(x, inSectionHeight, z, blockState) + section.blockEntities[x, inSectionHeight, z] = blockEntity // ToDo + + val previousLuminance = previous?.luminance ?: 0 + val luminance = blockState?.luminance ?: 0 + if (luminance > previousLuminance) { + section.onLightIncrease(x, inSectionHeight, z, luminance) + } else if (previousLuminance > luminance) { + section.onLightDecrease(x, inSectionHeight, z, luminance) + } } operator fun set(position: Vec3i, blockState: BlockState?) = set(position.x, position.y, position.z, blockState) diff --git a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt index cb8cf80d5..2ccc864b1 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/ChunkSection.kt @@ -28,11 +28,16 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition * Collection of 16x16x16 blocks */ class ChunkSection( - var blocks: BlockSectionDataProvider, + blocks: BlockSectionDataProvider, var biomes: SectionDataProvider = SectionDataProvider(checkSize = false), var blockEntities: SectionDataProvider = SectionDataProvider(checkSize = false), - var light: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION) { 0xFF.toByte() }, // packed (skyLight: 0xF0, blockLight: 0x0F) + var light: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION) { 0x00.toByte() }, // packed (skyLight: 0xF0, blockLight: 0x0F) ) { + var blocks = blocks + set(value) { + field = value + recalculateLight() + } fun tick(connection: PlayConnection, chunkPosition: Vec2i, sectionHeight: Int) { if (blockEntities.isEmpty) { @@ -98,4 +103,73 @@ class ChunkSection( } this.biomes.setData(biomes.unsafeCast()) } + + fun onLightDecrease(x: Int, y: Int, z: Int, luminance: Byte) { + // ToDo: make faster, set light to 0 and trace to next night increase. then backtrace + recalculateLight() + } + + fun onLightIncrease(x: Int, y: Int, z: Int, luminance: Byte) { + traceLightIncrease(x, y, z, luminance) + } + + + private fun traceLightIncrease(x: Int, y: Int, z: Int, nextLuminance: Byte) { + val index = getIndex(x, y, z) + val block = blocks.unsafeGet(index) + if (block != null && block.luminance == 0.toByte() && block.isSolid) { + // light can not pass through the block + return + } + // get block or next luminance level + var luminance = nextLuminance + val currentLuminance = block?.luminance ?: nextLuminance + if (currentLuminance > luminance) { + luminance = currentLuminance + } + val currentLight = light[index].toInt() and 0x0F // we just care about block light + if (currentLight >= luminance) { + // light is already higher, no need to trace + return + } + light[index] = luminance + + val neighbourLuminance = (luminance - 1).toByte() + if (neighbourLuminance == 0.toByte()) { + // luminance is 1, we can not further increase the light + return + } + + // ToDo: check neighbours and trace there + if (x > 0) traceLightIncrease(x - 1, y, z, neighbourLuminance) + if (x < ProtocolDefinition.SECTION_MAX_X) traceLightIncrease(x + 1, y, z, neighbourLuminance) + if (y > 0) traceLightIncrease(x, y - 1, z, neighbourLuminance) + if (y < ProtocolDefinition.SECTION_MAX_Y) traceLightIncrease(x, y + 1, z, neighbourLuminance) + if (z > 0) traceLightIncrease(x, y, z - 1, neighbourLuminance) + if (z < ProtocolDefinition.SECTION_MAX_Y) traceLightIncrease(x, y, z + 1, neighbourLuminance) + } + + + fun recalculateLight() { + // clear light + for (index in light.indices) { + light[index] = 0x00.toByte() + } + + blocks.acquire() + for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) { + for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) { + for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) { + val index = getIndex(x, y, z) + val luminance = blocks.unsafeGet(index)?.luminance ?: continue + if (luminance == 0.toByte()) { + // block is not emitting light, ignore it + continue + } + traceLightIncrease(x, y, z, luminance) + } + } + } + blocks.release() + } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt index c42d5ad5e..16d2bf5d8 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/SectionDataProvider.kt @@ -126,8 +126,8 @@ open class SectionDataProvider( return get(y shl 8 or (z shl 4) or x) } - operator fun set(x: Int, y: Int, z: Int, value: T) { - set(y shl 8 or (z shl 4) or x, value) + operator fun set(x: Int, y: Int, z: Int, value: T): T? { + return set(y shl 8 or (z shl 4) or x, value) } open operator fun set(index: Int, value: T): T? {