From 4d02696bc50aff013a01aa91f6a317e89b607a7f Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Thu, 27 Jul 2023 23:06:23 +0200 Subject: [PATCH] refactor heightmap --- .../world/chunk/light/GeneralHeightmapTest.kt | 41 +++--- .../data/world/chunk/light/SkyLightTraceIT.kt | 59 +++++++++ .../chunk/light/place/SkyLightPlaceIT.kt | 2 +- .../de/bixilon/minosoft/data/world/World.kt | 2 +- .../minosoft/data/world/chunk/chunk/Chunk.kt | 4 +- .../chunk/heightmap/AbstractHeightmap.kt | 26 ++++ .../world/chunk/heightmap/FixedHeightmap.kt | 30 +++++ .../data/world/chunk/heightmap/Heightmap.kt | 117 +++++++++++++++++ .../world/chunk/heightmap/LightHeightmap.kt | 63 +++++++++ .../data/world/chunk/light/ChunkLight.kt | 122 +----------------- 10 files changed, 326 insertions(+), 140 deletions(-) create mode 100644 src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/SkyLightTraceIT.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/AbstractHeightmap.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/FixedHeightmap.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/Heightmap.kt create mode 100644 src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/LightHeightmap.kt diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/GeneralHeightmapTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/GeneralHeightmapTest.kt index 0390fc61d..15a369502 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/GeneralHeightmapTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/GeneralHeightmapTest.kt @@ -14,6 +14,8 @@ package de.bixilon.minosoft.data.world.chunk.light import de.bixilon.kotlinglm.vec3.Vec3i +import de.bixilon.minosoft.data.registries.blocks.GlassTest0 +import de.bixilon.minosoft.data.registries.blocks.LeavesTest0 import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0 import de.bixilon.minosoft.data.world.chunk.ChunkTestingUtil.createChunkWithNeighbours import de.bixilon.minosoft.data.world.chunk.chunk.Chunk @@ -25,15 +27,6 @@ import org.testng.annotations.Test @Test(groups = ["light"], dependsOnGroups = ["block"]) class GeneralHeightmapTest { - fun testMaxHeightEast() { - val chunk: Chunk = createChunkWithNeighbours() - chunk[Vec3i(2, 10, 3)] = StoneTest0.state - chunk[Vec3i(3, 11, 2)] = StoneTest0.state - chunk[Vec3i(3, 12, 4)] = StoneTest0.state - chunk[Vec3i(4, 13, 3)] = StoneTest0.state - assertEquals(chunk.light.getNeighbourMaxHeight(chunk.neighbours.get()!!, 3, 3), 14) - } - fun testMinHeightEast() { val chunk: Chunk = createChunkWithNeighbours() chunk[Vec3i(2, 10, 3)] = StoneTest0.state @@ -43,16 +36,6 @@ class GeneralHeightmapTest { assertEquals(chunk.light.getNeighbourMinHeight(chunk.neighbours.get()!!, 3, 3), 11) } - fun testMaxHeightNeighbourEast() { - val chunk: Chunk = createChunkWithNeighbours() - val neighbours = chunk.neighbours.get()!! - chunk[Vec3i(14, 10, 3)] = StoneTest0.state - chunk[Vec3i(15, 11, 2)] = StoneTest0.state - chunk[Vec3i(15, 12, 4)] = StoneTest0.state - neighbours[ChunkNeighbours.EAST][Vec3i(0, 13, 3)] = StoneTest0.state - assertEquals(chunk.light.getNeighbourMaxHeight(neighbours, 15, 3), 14) - } - fun testMinHeightNeighbourEast() { val chunk: Chunk = createChunkWithNeighbours() val neighbours = chunk.neighbours.get()!! @@ -62,6 +45,24 @@ class GeneralHeightmapTest { neighbours[ChunkNeighbours.EAST][Vec3i(0, 10, 3)] = StoneTest0.state assertEquals(chunk.light.getNeighbourMinHeight(neighbours, 15, 3), 11) } - // TODO: Test other directions + + fun `top of the world and not passing`() { + val chunk: Chunk = createChunkWithNeighbours() + chunk[Vec3i(2, 255, 3)] = StoneTest0.state + assertEquals(chunk.light.heightmap[2, 3], 256) + } + + fun `top of the world and entering`() { + val chunk: Chunk = createChunkWithNeighbours() + chunk[Vec3i(2, 255, 3)] = LeavesTest0.state + assertEquals(chunk.light.heightmap[2, 3], 255) + } + + fun `top of the world and passing`() { + val chunk: Chunk = createChunkWithNeighbours() + chunk[Vec3i(2, 255, 3)] = GlassTest0.state + assertEquals(chunk.light.heightmap[2, 3], Int.MIN_VALUE) + } + } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/SkyLightTraceIT.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/SkyLightTraceIT.kt new file mode 100644 index 000000000..50418fd20 --- /dev/null +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/SkyLightTraceIT.kt @@ -0,0 +1,59 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.chunk.light + +import de.bixilon.kotlinglm.vec3.Vec3i +import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0 +import de.bixilon.minosoft.data.world.chunk.light.LightTestUtil.assertLight +import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil.createConnection +import org.testng.annotations.Test + + +@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8, priority = 1000) +class SkyLightTraceIT { + + fun `check level below block`() { + val world = createConnection(3, light = true).world + world[Vec3i(8, 10, 8)] = StoneTest0.state + world.assertLight(8, 9, 8, 0xD0) + } + + fun `heightmap optimization west, upper block set`() { + val world = createConnection(3, light = true).world + world[Vec3i(8, 10, 8)] = StoneTest0.state + world[Vec3i(7, 12, 8)] = StoneTest0.state + world.assertLight(7, 11, 8, 0xD0) + } + + fun `heightmap optimization east, upper block set`() { + val world = createConnection(3, light = true).world + world[Vec3i(8, 10, 8)] = StoneTest0.state + world[Vec3i(9, 12, 8)] = StoneTest0.state + world.assertLight(9, 11, 8, 0xD0) + } + + fun `heightmap optimization north, upper block set`() { + val world = createConnection(3, light = true).world + world[Vec3i(8, 10, 8)] = StoneTest0.state + world[Vec3i(8, 12, 7)] = StoneTest0.state + world.assertLight(8, 11, 7, 0xD0) + } + + fun `heightmap optimization south, upper block set`() { + val world = createConnection(3, light = true).world + world[Vec3i(8, 10, 8)] = StoneTest0.state + world[Vec3i(8, 12, 9)] = StoneTest0.state + world.assertLight(8, 11, 9, 0xD0) + } +} diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/place/SkyLightPlaceIT.kt b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/place/SkyLightPlaceIT.kt index 34e9d7317..131ff0d67 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/place/SkyLightPlaceIT.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/data/world/chunk/light/place/SkyLightPlaceIT.kt @@ -23,7 +23,7 @@ import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil.c import org.testng.annotations.Test -@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8, priority = -100) +@Test(groups = ["light"], dependsOnGroups = ["block"], threadPoolSize = 8, priority = 1000) class SkyLightPlaceIT { fun aboveBlock() { 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 9d5d8a3fa..b1d7fe616 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/World.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/World.kt @@ -228,7 +228,7 @@ class World( reset += { chunk.light.reset() } calculate += { if (heightmap) { - chunk.light.recalculateHeightmap() + chunk.light.heightmap.recalculate() } chunk.light.calculate() } 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 6c6053ca7..ed2c01de4 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 @@ -62,7 +62,7 @@ class Chunk( init { - light.recalculateHeightmap() + light.heightmap.recalculate() } @Deprecated("neighbours.complete", ReplaceWith("neighbours.complete")) @@ -162,7 +162,7 @@ class Chunk( if (executed.isEmpty()) { return lock.unlock() } - light.recalculateHeightmap() + light.heightmap.recalculate() light.recalculate() for (section in sections) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/AbstractHeightmap.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/AbstractHeightmap.kt new file mode 100644 index 000000000..abb57c339 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/AbstractHeightmap.kt @@ -0,0 +1,26 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.chunk.heightmap + +import de.bixilon.minosoft.data.registries.blocks.state.BlockState + +interface AbstractHeightmap { + + fun recalculate() + + operator fun get(x: Int, z: Int): Int + operator fun get(index: Int): Int + + fun onBlockChange(x: Int, y: Int, z: Int, next: BlockState?) +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/FixedHeightmap.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/FixedHeightmap.kt new file mode 100644 index 000000000..0381936bc --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/FixedHeightmap.kt @@ -0,0 +1,30 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.chunk.heightmap + +import de.bixilon.minosoft.data.registries.blocks.state.BlockState + +class FixedHeightmap(val value: Int) : AbstractHeightmap { + + override fun recalculate() = Unit + + override fun get(x: Int, z: Int) = value + override fun get(index: Int) = value + + override fun onBlockChange(x: Int, y: Int, z: Int, next: BlockState?) = Unit + + companion object { + val MAX_VALUE = FixedHeightmap(Int.MAX_VALUE) + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/Heightmap.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/Heightmap.kt new file mode 100644 index 000000000..7a8825d9d --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/Heightmap.kt @@ -0,0 +1,117 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.chunk.heightmap + +import de.bixilon.minosoft.data.registries.blocks.state.BlockState +import de.bixilon.minosoft.data.world.chunk.chunk.Chunk +import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition + +abstract class Heightmap(protected val chunk: Chunk) : AbstractHeightmap { + protected val heightmap = IntArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z) { Int.MIN_VALUE } + + override fun get(index: Int) = heightmap[index] + override fun get(x: Int, z: Int) = heightmap[(z shl 4) or x] + + + protected abstract fun passes(state: BlockState): HeightmapPass + protected abstract fun onHeightmapUpdate(x: Int, z: Int, previous: Int, now: Int) + + + override fun recalculate() { + chunk.lock.lock() + val maxY = chunk.maxSection * ProtocolDefinition.SECTION_HEIGHT_Y + + for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) { + for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) { + trace(x, maxY, z, false) + } + } + chunk.lock.unlock() + } + + + private fun trace(x: Int, startY: Int, z: Int, notify: Boolean) { + val sections = chunk.sections + + var y = Int.MIN_VALUE + + sectionLoop@ for (sectionIndex in (startY.sectionHeight - chunk.minSection) downTo 0) { + if (sectionIndex >= sections.size) { + // starting from above world + continue + } + val section = sections[sectionIndex] ?: continue + if (section.blocks.isEmpty) continue + + section.acquire() + for (sectionY in ProtocolDefinition.SECTION_MAX_Y downTo 0) { + val state = section.blocks[x, sectionY, z] ?: continue + val pass = passes(state) + if (pass == HeightmapPass.PASSES) continue + + y = (sectionIndex + chunk.minSection) * ProtocolDefinition.SECTION_HEIGHT_Y + sectionY + 1 + if (pass == HeightmapPass.ABOVE) y++ + + section.release() + break@sectionLoop + } + section.release() + } + val index = (z shl 4) or x + val previous = heightmap[index] + + if (previous == y) return + + heightmap[index] = y + + if (notify) { + onHeightmapUpdate(x, z, previous, y) + } + } + + + override fun onBlockChange(x: Int, y: Int, z: Int, next: BlockState?) { + chunk.lock.lock() + val index = (z shl 4) or x + + val current = heightmap[index] + + if (current > y + 1) { + // our block is/was not the highest, ignore everything + chunk.lock.unlock() + return + } + if (next == null) { + trace(x, y, z, true) + chunk.lock.unlock() + return + } + + when (passes(next)) { + HeightmapPass.ABOVE -> heightmap[index] = y + 1 + HeightmapPass.IN -> heightmap[index] = y + HeightmapPass.PASSES -> Unit + } + + chunk.lock.unlock() + } + + protected enum class HeightmapPass { + ABOVE, + IN, + PASSES, + ; + } +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/LightHeightmap.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/LightHeightmap.kt new file mode 100644 index 000000000..49cf5a3b0 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/heightmap/LightHeightmap.kt @@ -0,0 +1,63 @@ +/* + * Minosoft + * Copyright (C) 2020-2023 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.data.world.chunk.heightmap + +import de.bixilon.minosoft.data.direction.Directions +import de.bixilon.minosoft.data.registries.blocks.state.BlockState +import de.bixilon.minosoft.data.world.chunk.chunk.Chunk +import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight + +class LightHeightmap(chunk: Chunk) : Heightmap(chunk) { + + + override fun recalculate() { + super.recalculate() + chunk.light.calculateSkylight() + } + + override fun onHeightmapUpdate(x: Int, z: Int, previous: Int, now: Int) { + if (previous > now) { + // block is lower + return chunk.light.startSkylightFloodFill(x, z) + } + // block is now higher + // ToDo: Neighbours + val sections = chunk.sections + val maxIndex = previous.sectionHeight - chunk.minSection + val minIndex = now.sectionHeight - chunk.minSection + chunk.light.bottom.reset() + for (index in maxIndex downTo minIndex) { + val section = sections[index] ?: continue + section.light.reset() + } + for (index in maxIndex downTo minIndex) { + val section = sections[index] ?: continue + section.light.calculate() + } + chunk.light.calculateSkylight() + } + + + override fun passes(state: BlockState): HeightmapPass { + val light = state.block.getLightProperties(state) + if (!light.skylightEnters) return HeightmapPass.ABOVE + + if (!light.filtersSkylight && light.propagatesLight(Directions.DOWN)) { + // can go through block + return HeightmapPass.PASSES + } + + return HeightmapPass.IN + } +} 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/ChunkLight.kt index 63ded434d..a48ff7d50 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/ChunkLight.kt @@ -20,6 +20,8 @@ import de.bixilon.minosoft.data.registries.blocks.state.BlockState import de.bixilon.minosoft.data.registries.dimension.DimensionProperties 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.neighbours.ChunkNeighbours import de.bixilon.minosoft.data.world.chunk.update.AbstractWorldUpdate import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkLightUpdate @@ -29,7 +31,7 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition class ChunkLight(private val chunk: Chunk) { private val connection = chunk.connection - val heightmap = IntArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z) { if (chunk.world.dimension.canSkylight()) Int.MIN_VALUE else Int.MAX_VALUE } + val heightmap = if (chunk.world.dimension.canSkylight()) LightHeightmap(chunk) else FixedHeightmap.MAX_VALUE val bottom = BorderSectionLight(false, chunk) val top = BorderSectionLight(true, chunk) @@ -39,10 +41,7 @@ class ChunkLight(private val chunk: Chunk) { if (!chunk.world.dimension.light) { return } - val heightmapIndex = (z shl 4) or x - val previous = heightmap[heightmapIndex] - recalculateHeightmap(x, y, z, next) - onHeightmapUpdate(x, y, z, previous, heightmap[heightmapIndex]) + heightmap.onBlockChange(x, y, z, next) val neighbours = chunk.neighbours.get() ?: return @@ -180,118 +179,8 @@ class ChunkLight(private val chunk: Chunk) { } } - fun recalculateHeightmap() { - if (!chunk.world.dimension.canSkylight()) { - return - } - chunk.lock.lock() - val maxY = chunk.maxSection * ProtocolDefinition.SECTION_HEIGHT_Y - for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) { - for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) { - checkHeightmapY(x, maxY, z) - } - } - chunk.lock.unlock() - calculateSkylight() - } - - private fun checkHeightmapY(x: Int, startY: Int, z: Int) { - val sections = chunk.sections - - var y = Int.MIN_VALUE - - sectionLoop@ for (sectionIndex in (startY.sectionHeight - chunk.minSection) downTo 0) { - if (sectionIndex >= sections.size) { - // starting from above world - continue - } - val section = sections[sectionIndex] ?: continue - if (section.blocks.isEmpty) continue - - section.acquire() - for (sectionY in ProtocolDefinition.SECTION_MAX_Y downTo 0) { - val state = section.blocks[x, sectionY, z] ?: continue - val light = state.block.getLightProperties(state) - - if (light.skylightEnters && !light.filtersSkylight && light.propagatesLight(Directions.DOWN)) { - // can go through block - continue - } - y = (sectionIndex + chunk.minSection) * ProtocolDefinition.SECTION_HEIGHT_Y + sectionY - if (!light.skylightEnters) { - y++ - } - section.release() - break@sectionLoop - } - section.release() - } - val heightmapIndex = (z shl 4) or x - heightmap[heightmapIndex] = y - } - - private fun onHeightmapUpdate(x: Int, y: Int, z: Int, previous: Int, now: Int) { - if (previous == now) { - return - } - - if (previous < y) { - // block is now higher - // ToDo: Neighbours - val sections = chunk.sections - val maxIndex = previous.sectionHeight - chunk.minSection - val minIndex = now.sectionHeight - chunk.minSection - bottom.reset() - for (index in maxIndex downTo minIndex) { - val section = sections[index] ?: continue - section.light.reset() - } - for (index in maxIndex downTo minIndex) { - val section = sections[index] ?: continue - section.light.calculate() - } - calculateSkylight() - } else if (previous > y && chunk.world.dimension.canSkylight()) { - // block is lower - startSkylightFloodFill(x, z) - } - } - - private fun recalculateHeightmap(x: Int, y: Int, z: Int, blockState: BlockState?) { - if (!chunk.world.dimension.canSkylight()) { - return - } - chunk.lock.lock() - val index = (z shl 4) or x - - val current = heightmap[index] - - if (current > y + 1) { - // our block is/was not the highest, ignore everything - chunk.lock.unlock() - return - } - if (blockState == null) { - checkHeightmapY(x, y, z) - chunk.lock.unlock() - return - } - - // we are the highest block now - // check if light can pass - val light = blockState.block.getLightProperties(blockState) - if (!light.skylightEnters) { - heightmap[index] = y + 1 - } else if (light.filtersSkylight || !light.propagatesLight(Directions.DOWN)) { - heightmap[index] = y - } - - chunk.lock.unlock() - return - } - - private fun calculateSkylight() { + fun calculateSkylight() { if (!chunk.world.dimension.canSkylight() || !chunk.neighbours.complete) { // no need to calculate it return @@ -400,6 +289,7 @@ class ChunkLight(private val chunk: Chunk) { } } + @Deprecated("heightmap") inline fun getMaxHeight(x: Int, z: Int): Int { return heightmap[(z shl 4) or x] }