diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt index 303a93171..6b961f144 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/AbstractSectionLight.kt @@ -23,10 +23,12 @@ interface AbstractSectionLight { operator fun get(position: InSectionPosition): LightLevel + fun traceFrom(direction: Directions) fun traceFrom(position: InSectionPosition, direction: Directions) fun clear() fun propagate() + fun calculate() fun update(array: LightArray) diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt index d878d0f87..1ae07350c 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/ChunkLight.kt @@ -25,6 +25,7 @@ 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.positions.InChunkPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition +import de.bixilon.minosoft.data.world.positions.SectionHeight import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.* @@ -48,11 +49,7 @@ class ChunkLight(val chunk: Chunk) { val sectionHeight = position.sectionHeight val inSection = position.inSectionPosition - val light = when (sectionHeight) { - chunk.minSection - 1 -> bottom[inSection] - chunk.maxSection + 1 -> top[inSection] - else -> chunk[sectionHeight]?.light?.get(inSection) ?: LightLevel.EMPTY - } + val light = chunk.light[sectionHeight]?.get(inSection) ?: LightLevel.EMPTY if (position.y >= heightmap[position]) { return light.with(sky = LightLevel.MAX_LEVEL) @@ -70,7 +67,7 @@ class ChunkLight(val chunk: Chunk) { fun calculate() { for (section in chunk.sections) { - section?.light?.calculateBlocks() + section?.light?.calculateBlock() } calculateSky() } @@ -147,4 +144,10 @@ class ChunkLight(val chunk: Chunk) { traceSkyDown(position, maxOf(bottomY, chunk.minSection - 1), topY) } } + + operator fun get(height: SectionHeight) = when { + height < chunk.minSection -> bottom + height > chunk.maxSection -> top + else -> chunk[height]?.light + } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt index d6f7924a7..809be9c44 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/SectionLight.kt @@ -13,6 +13,7 @@ package de.bixilon.minosoft.data.world.chunk.light.section +import de.bixilon.kutil.exception.Broken import de.bixilon.minosoft.data.Axes import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.registries.blocks.light.LightProperties @@ -23,6 +24,7 @@ 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.light.types.LightLevel.Companion.MAX_LEVEL import de.bixilon.minosoft.data.world.chunk.update.chunk.SectionLightUpdate +import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight @@ -40,7 +42,7 @@ class SectionLight( when { luminance > previousLuminance -> onIncrease(position, luminance) - luminance < previousLuminance -> onDecrease(position) + luminance < previousLuminance -> onDecrease(position, previousLuminance) } } @@ -58,6 +60,8 @@ class SectionLight( if (position.z > 0) traceFrom(position.minusZ(), Directions.NORTH) else getNeighbour(Directions.SOUTH)?.traceFrom(position.with(z = SECTION_MAX_Z), Directions.NORTH) if (position.z < SECTION_MAX_Z) traceFrom(position.plusZ(), Directions.SOUTH) else getNeighbour(Directions.NORTH)?.traceFrom(position.with(z = 0), Directions.SOUTH) + + // TODO: This just handles increase in light (i.e. breaking a block), what if a block is placed? } fun onBlockChange(position: InSectionPosition, previous: BlockState?, state: BlockState?) { @@ -69,8 +73,37 @@ class SectionLight( trace(position, LightLevel(block = luminance, sky = this[position].sky), null) } - private fun onDecrease(position: InSectionPosition) { - // TODO: Trace until next light increase (or level 0), set all those levels to 0 and then force trace all blocks in range (also from neighbours). + private fun onDecrease(position: InSectionPosition, luminance: Int) { + // TODO: Optimize (trace in direction and then propagate from edges and trace back again) + + val minX = (position.x - luminance).sectionHeight + val maxX = (position.x + luminance).sectionHeight + + val minY = (position.y - luminance).sectionHeight + val maxY = (position.y + luminance).sectionHeight + + val minZ = (position.z - luminance).sectionHeight + val maxZ = (position.z + luminance).sectionHeight + + for (x in minX..maxX) { + for (z in minZ..maxZ) { + val chunk = section.chunk.neighbours.traceChunk(ChunkPosition(x, z)) ?: continue + for (y in minY..maxY) { + val neighbour = chunk.light[section.height + y] ?: continue + neighbour.clear() + } + } + } + for (x in minX..maxX) { + for (z in minZ..maxZ) { + val chunk = section.chunk.neighbours.traceChunk(ChunkPosition(x, z)) ?: continue + for (y in minY..maxY) { + val neighbour = chunk.light[section.height + y] ?: continue + neighbour.propagate() + neighbour.calculate() + } + } + } } private inline fun getOrPut(direction: Directions): SectionLight? { @@ -231,7 +264,7 @@ class SectionLight( event = true } - fun calculateBlocks() { + fun calculateBlock() { if (section.blocks.isEmpty) return val min = section.blocks.minPosition val max = section.blocks.maxPosition @@ -263,12 +296,15 @@ class SectionLight( } } - protected fun getNeighbour(direction: Directions): AbstractSectionLight? { - return when (direction) { - Directions.UP -> if (section.height == section.chunk.maxSection) section.chunk.light.top else section.chunk[section.height + 1]?.light - Directions.DOWN -> if (section.height == section.chunk.minSection) section.chunk.light.bottom else section.chunk[section.height - 1]?.light - else -> section.chunk.neighbours[direction]?.get(section.height)?.light - } + override fun calculate() { + calculateSky() + calculateBlock() + } + + protected fun getNeighbour(direction: Directions) = when (direction) { + Directions.UP -> if (section.height == section.chunk.maxSection) section.chunk.light.top else section.chunk[section.height + 1]?.light + Directions.DOWN -> if (section.height == section.chunk.minSection) section.chunk.light.bottom else section.chunk[section.height - 1]?.light + else -> section.chunk.neighbours[direction]?.get(section.height)?.light } private fun propagateVertical() { @@ -290,23 +326,20 @@ class SectionLight( } } + override fun traceFrom(direction: Directions) { + if (direction.axis != Axes.Y) Broken() + for (xz in 0 until SECTION_WIDTH_X * SECTION_WIDTH_Z) { + traceFrom(InSectionPosition(xz), direction) + } + } + private fun propagateHorizontal() { val below = if (section.height == section.chunk.minSection) section.chunk.light.bottom else section.chunk[section.height - 1]?.light val above = if (section.height == section.chunk.maxSection) section.chunk.light.top else section.chunk[section.height + 1]?.light // TODO: merge both? - if (below != null) { - for (xz in 0 until SECTION_WIDTH_X * SECTION_WIDTH_Z) { - val position = InSectionPosition(xz).with(y = SECTION_MAX_Y) - below.traceFrom(position, Directions.UP) - } - } - if (above != null) { - for (xz in 0 until SECTION_WIDTH_X * SECTION_WIDTH_Z) { - val position = InSectionPosition(xz).with(y = 0) - above.traceFrom(position, Directions.DOWN) - } - } + below?.traceFrom(Directions.UP) + above?.traceFrom(Directions.DOWN) } override fun propagate() { diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt index fc43e2bcb..99c602769 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/section/border/BorderSectionLight.kt @@ -107,6 +107,8 @@ abstract class BorderSectionLight( return null } + override fun calculate() = Unit // TODO: bottom sky light + protected fun propagateVertical() { for (x in 0 until SECTION_MAX_X) { chunk.neighbours[Directions.NORTH]?.light?.bottom?.traceFrom(InSectionPosition(x, 0, SECTION_MAX_Z), Directions.SOUTH) 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 index c8f1923f2..b00bd8408 100644 --- 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 @@ -13,6 +13,7 @@ package de.bixilon.minosoft.data.world.chunk.light.section.border +import de.bixilon.kutil.exception.Broken import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.light.types.LightArray @@ -31,6 +32,18 @@ class BottomSectionLight( override fun Chunk.getBorderLight() = this.light.bottom + override fun traceFrom(direction: Directions) { + if (direction != Directions.UP) Broken() + val above = chunk[chunk.minSection]?.light ?: return + for (xz in 0 until SECTION_WIDTH_X * SECTION_WIDTH_Z) { + val position = InSectionPosition(xz) + val current = LightLevel(light[xz]) + if (current.block <= 1) return + + val next = current.decrease() + above.trace(position.with(y = 0), next, Directions.UP) + } + } override fun trace(position: InSectionPosition, level: LightLevel) { var level = level 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 index e8394f375..493eb4ae2 100644 --- 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 @@ -13,6 +13,7 @@ package de.bixilon.minosoft.data.world.chunk.light.section.border +import de.bixilon.kutil.exception.Broken import de.bixilon.minosoft.data.direction.Directions import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.light.types.LightArray @@ -31,6 +32,19 @@ class TopSectionLight( override fun Chunk.getBorderLight() = this.light.top + override fun traceFrom(direction: Directions) { + if (direction != Directions.DOWN) Broken() + val above = chunk[chunk.maxSection]?.light ?: return + for (xz in 0 until SECTION_WIDTH_X * SECTION_WIDTH_Z) { + val position = InSectionPosition(xz) + val current = LightLevel(light[xz]) + if (current.block <= 1) return + + val next = current.decrease() + above.trace(position.with(y = SECTION_MAX_Y), next, Directions.DOWN) + } + } + override fun trace(position: InSectionPosition, level: LightLevel) { val current = LightLevel(light[position.xz]) if (current.block >= level.block) return