mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-13 09:26:11 -04:00
refactor heightmap
This commit is contained in:
parent
ebb5b8a04e
commit
4d02696bc5
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.chunk.light
|
||||
|
||||
import de.bixilon.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)
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -228,7 +228,7 @@ class World(
|
||||
reset += { chunk.light.reset() }
|
||||
calculate += {
|
||||
if (heightmap) {
|
||||
chunk.light.recalculateHeightmap()
|
||||
chunk.light.heightmap.recalculate()
|
||||
}
|
||||
chunk.light.calculate()
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.chunk.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?)
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.chunk.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)
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.chunk.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,
|
||||
;
|
||||
}
|
||||
}
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
|
||||
*/
|
||||
|
||||
package de.bixilon.minosoft.data.world.chunk.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
|
||||
}
|
||||
}
|
@ -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]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user