fix heightmap calculation, calculate skylight

This commit is contained in:
Bixilon 2022-09-18 16:53:00 +02:00
parent 958c19b4d2
commit c537e674a0
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
9 changed files with 88 additions and 61 deletions

View File

@ -132,8 +132,9 @@ Support for macOS is a delicate topic. Let's say it works for now, but it is not
5. Build and run Minosoft with `mvn clean verify exec:java`. If any errors occur, feel free to open an issue. In this early stage it might be helpful to delete its configuration files
6. (Optional) Build a fat jar with `mvn package`. You'll find the jar with all dependencies in `target/`. Then you don't need to recompile everytime
Using IntelliJ IDEA for building or developing is strongly recommended. There you have features
like build caching. You might need to increase heap memory for the compiler (`File` -> `Settings` -> `Build, Execution and Compiler` -> `Compiler` -> `Shared build process heap size`).
Using IntelliJ IDEA for building or developing is strongly recommended. There you have features like build caching.
If you run into errors, please ensure you have the latest version of it.
You might need to increase heap memory for the compiler (`File` -> `Settings` -> `Build, Execution and Compiler` -> `Compiler` -> `Shared build process heap size`).
Allow at least 1500 MBytes.
## Code mirrors

View File

@ -19,12 +19,9 @@ import de.bixilon.minosoft.data.registries.VoxelShape
import de.bixilon.minosoft.data.registries.blocks.cube.CubeDirections
class DirectedProperty(private val directions: BooleanArray) : LightProperties {
override val propagatesSkylight: Boolean = propagatesLight(Directions.UP, Directions.DOWN)
override fun propagatesSkylight(from: Directions, to: Directions): Boolean {
return directions[CubeDirections.getIndex(from, to)]
}
override fun propagatesBlockLight(from: Directions, to: Directions): Boolean {
override fun propagatesLight(from: Directions, to: Directions): Boolean {
return directions[CubeDirections.getIndex(from, to)]
}

View File

@ -16,10 +16,9 @@ package de.bixilon.minosoft.data.registries.blocks.light
import de.bixilon.minosoft.data.direction.Directions
interface LightProperties {
val propagatesBlockLight: Boolean get() = false
val propagatesLight: Boolean get() = false
val propagatesSkylight: Boolean get() = false
fun propagatesSkylight(from: Directions, to: Directions): Boolean = propagatesSkylight
fun propagatesBlockLight(from: Directions, to: Directions): Boolean = propagatesBlockLight
fun propagatesLight(from: Directions, to: Directions): Boolean = propagatesLight
}

View File

@ -14,7 +14,7 @@
package de.bixilon.minosoft.data.registries.blocks.light
object TransparentProperty : LightProperties {
override val propagatesBlockLight: Boolean
override val propagatesLight: Boolean
get() = true
override val propagatesSkylight: Boolean
get() = true

View File

@ -58,7 +58,7 @@ class Chunk(
var blocksInitialized = false // All block data was received
var biomesInitialized = false // All biome data is initialized (aka. cache built, or similar)
val heightmap = IntArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z)
val skylightHeightmap = IntArray(ProtocolDefinition.SECTION_WIDTH_X * ProtocolDefinition.SECTION_WIDTH_Z)
var neighbours: Array<Chunk>? = null
@Synchronized set(value) {
@ -304,7 +304,7 @@ class Chunk(
return topLight[index].toInt() or 0xF0 // top has always sky=15
}
var light = this[sectionHeight]?.light?.get(index)?.toInt() ?: 0x00
if (y >= heightmap[heightmapIndex]) {
if (y >= skylightHeightmap[heightmapIndex]) {
// set sky=15
light = light or 0xF0
}
@ -435,7 +435,7 @@ class Chunk(
val maxY = highestSection * ProtocolDefinition.SECTION_HEIGHT_Y
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
z@ for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
checkHeightmapY(x, maxY, z)
}
}
@ -444,37 +444,31 @@ class Chunk(
private fun checkHeightmapY(x: Int, startY: Int, z: Int) {
val minY = lowestSection * ProtocolDefinition.SECTION_HEIGHT_Y
var y = startY
var sectionHeight = y.sectionHeight
var section: ChunkSection? = this[sectionHeight]
while (y > minY) {
val nextSectionHeight = y.sectionHeight
if (sectionHeight != nextSectionHeight) {
sectionHeight = nextSectionHeight
section = this[sectionHeight]
}
if (section == null) {
y -= ProtocolDefinition.SECTION_HEIGHT_Y
continue
}
val block = section.blocks[x, y.inSectionHeight, z]
if (block == null || !block.isSolid) {
y--
continue
}
val sections = sections ?: return
heightmap[(z shl 4) or x] = y
return
var y = minY
sectionLoop@ for (sectionIndex in (sections.size - 1) downTo 0) {
val section = sections[sectionIndex] ?: continue
for (sectionY in ProtocolDefinition.SECTION_MAX_Y downTo 0) {
val light = section.blocks[x, sectionY, z]?.lightProperties ?: continue
if (light.propagatesSkylight) {
continue
}
y = (sectionIndex + lowestSection) * ProtocolDefinition.SECTION_HEIGHT_Y + sectionY
break@sectionLoop
}
}
heightmap[(z shl 4) or x] = minY
skylightHeightmap[(z shl 4) or x] = y
}
@Synchronized
private fun updateHeightmap(x: Int, y: Int, z: Int, place: Boolean) {
val index = (z shl 4) or x
val current = heightmap[index]
val current = skylightHeightmap[index]
if (current > y) {
// our block is/was not the highest, ignore everything
@ -483,7 +477,7 @@ class Chunk(
if (current < y) {
if (place) {
// we are the highest block now
heightmap[index] = y
skylightHeightmap[index] = y
}
return
}
@ -497,41 +491,73 @@ class Chunk(
}
private fun recalculateSkylight() {
println("Chunk: $chunkPosition")
if (world.dimension?.hasSkyLight != true) {
// no need to calculate it
return
}
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
calculateSkylight(x, z)
traceSkylightDown(x, z)
}
}
for (x in 1 until ProtocolDefinition.SECTION_WIDTH_X - 1) {
for (z in 1 until ProtocolDefinition.SECTION_WIDTH_Z - 1) {
startSkylightFloodFill(x, z)
}
}
}
private fun calculateSkylight(x: Int, z: Int) {
private fun traceSkylightDown(x: Int, z: Int) {
val heightmapIndex = (z shl 4) or x
val maxHeight = heightmap[heightmapIndex]
val maxHeight = skylightHeightmap[heightmapIndex]
// ToDo: only update changed ones
for (sectionHeight in highestSection - 1 downTo maxHeight.sectionHeight + 1) {
val section = sections?.get(sectionHeight - lowestSection) ?: continue
section.light.update = true
}
val maxSection = sections?.get(maxHeight.sectionHeight - lowestSection)
if (maxSection != null) {
for (y in ProtocolDefinition.SECTION_MAX_Y downTo maxHeight.inSectionHeight + 2) {
for (y in ProtocolDefinition.SECTION_MAX_Y downTo maxHeight.inSectionHeight) {
val index = (y shl 8) or heightmapIndex
val block = maxSection.blocks[index]?.lightProperties
if (block != null && (!block.propagatesSkylight || block.propagatesSkylight(Directions.UP, Directions.DOWN))) {
if (block != null && !block.propagatesSkylight) {
continue
}
maxSection.light.light[index] = (maxSection.light.light[index].toInt() and 0x0F or 0xF0).toByte()
}
maxSection.light.update = true
// ToDo: This must run for every block
maxSection.light.traceSkylight(x, maxHeight.inSectionHeight + 1, z, ProtocolDefinition.LIGHT_LEVELS - 1, Directions.UP, true)
}
}
private fun startSkylightFloodFill(x: Int, z: Int) {
val heightmapIndex = (z shl 4) or x
val maxHeight = skylightHeightmap[heightmapIndex]
for (sectionHeight in highestSection - 1 downTo maxHeight.sectionHeight + 1) {
val section = sections?.get(sectionHeight - lowestSection) ?: continue
section.light.update = true
for (y in ProtocolDefinition.SECTION_MAX_Y downTo 0) {
section.light.traceSkylight(x, y, z, ProtocolDefinition.MAX_LIGHT_LEVEL.toInt(), Directions.UP, true)
}
}
val maxSection = sections?.get(maxHeight.sectionHeight - lowestSection)
if (maxSection != null) {
for (y in ProtocolDefinition.SECTION_MAX_Y downTo maxHeight.inSectionHeight) {
val index = (y shl 8) or heightmapIndex
val block = maxSection.blocks[index]?.lightProperties
if (block != null && !block.propagatesSkylight) {
continue
}
maxSection.light.traceSkylight(x, y, z, ProtocolDefinition.MAX_LIGHT_LEVEL.toInt(), Directions.UP, true)
}
maxSection.light.update = true
}
}
}

View File

@ -29,7 +29,7 @@ class SectionLight(
val previousLuminance = previous?.luminance ?: 0
val luminance = now?.luminance ?: 0
if (previousLuminance == luminance && previous?.lightProperties?.propagatesBlockLight == now?.lightProperties?.propagatesBlockLight && previous?.lightProperties?.propagatesSkylight == now?.lightProperties?.propagatesSkylight) {
if (previousLuminance == luminance && previous?.lightProperties?.propagatesLight == now?.lightProperties?.propagatesLight && previous?.lightProperties?.propagatesSkylight == now?.lightProperties?.propagatesSkylight) {
// no change for light data
return
}
@ -91,7 +91,7 @@ class SectionLight(
val index = getIndex(x, y, z)
val block = section.blocks.unsafeGet(index)
val blockLuminance = block?.luminance ?: 0
if (block != null && !block.lightProperties.propagatesBlockLight && blockLuminance == 0) {
if (block != null && !block.lightProperties.propagatesLight && blockLuminance == 0) {
// light can not pass through the block
return
}
@ -128,7 +128,7 @@ class SectionLight(
val neighbourLuminance = nextLuminance - 1
if (source == null || (source != Directions.DOWN && block?.lightProperties?.propagatesBlockLight(source, Directions.DOWN) != false)) {
if (source == null || (source != Directions.DOWN && block?.lightProperties?.propagatesLight(source, Directions.DOWN) != false)) {
if (y > 0) {
traceIncrease(x, y - 1, z, neighbourLuminance, Directions.UP)
} else if (section.sectionHeight == section.chunk?.lowestSection) {
@ -137,7 +137,7 @@ class SectionLight(
(neighbours[Directions.O_DOWN] ?: section.chunk?.getOrPut(section.sectionHeight - 1, false))?.light?.traceIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, neighbourLuminance, Directions.UP)
}
}
if (source == null || (source != Directions.UP && block?.lightProperties?.propagatesBlockLight(source, Directions.UP) != false)) {
if (source == null || (source != Directions.UP && block?.lightProperties?.propagatesLight(source, Directions.UP) != false)) {
if (y < ProtocolDefinition.SECTION_MAX_Y) {
traceIncrease(x, y + 1, z, neighbourLuminance, Directions.DOWN)
} else if (section.sectionHeight == section.chunk?.highestSection) {
@ -147,28 +147,28 @@ class SectionLight(
}
}
if (source == null || (source != Directions.NORTH && block?.lightProperties?.propagatesBlockLight(source, Directions.NORTH) != false)) {
if (source == null || (source != Directions.NORTH && block?.lightProperties?.propagatesLight(source, Directions.NORTH) != false)) {
if (z > 0) {
traceIncrease(x, y, z - 1, neighbourLuminance, Directions.SOUTH)
} else {
(neighbours[Directions.O_NORTH] ?: section.chunk?.neighbours?.get(ChunkNeighbours.NORTH)?.getOrPut(section.sectionHeight - 1, false))?.light?.traceIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance, Directions.SOUTH)
}
}
if (source == null || (source != Directions.SOUTH && block?.lightProperties?.propagatesBlockLight(source, Directions.SOUTH) != false)) {
if (source == null || (source != Directions.SOUTH && block?.lightProperties?.propagatesLight(source, Directions.SOUTH) != false)) {
if (z < ProtocolDefinition.SECTION_MAX_Y) {
traceIncrease(x, y, z + 1, neighbourLuminance, Directions.NORTH)
} else {
(neighbours[Directions.O_SOUTH] ?: section.chunk?.neighbours?.get(ChunkNeighbours.SOUTH)?.getOrPut(section.sectionHeight, false))?.light?.traceIncrease(x, y, 0, neighbourLuminance, Directions.NORTH)
}
}
if (source == null || (source != Directions.WEST && block?.lightProperties?.propagatesBlockLight(source, Directions.WEST) != false)) {
if (source == null || (source != Directions.WEST && block?.lightProperties?.propagatesLight(source, Directions.WEST) != false)) {
if (x > 0) {
traceIncrease(x - 1, y, z, neighbourLuminance, Directions.EAST)
} else {
(neighbours[Directions.O_WEST] ?: section.chunk?.neighbours?.get(ChunkNeighbours.WEST)?.getOrPut(section.sectionHeight, false))?.light?.traceIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, neighbourLuminance, Directions.EAST)
}
}
if (source == null || (source != Directions.EAST && block?.lightProperties?.propagatesBlockLight(source, Directions.EAST) != false)) {
if (source == null || (source != Directions.EAST && block?.lightProperties?.propagatesLight(source, Directions.EAST) != false)) {
if (x < ProtocolDefinition.SECTION_MAX_X) {
traceIncrease(x + 1, y, z, neighbourLuminance, Directions.WEST)
} else {
@ -248,29 +248,29 @@ class SectionLight(
val light = section.blocks[index]?.lightProperties
if (light != null && !light.propagatesSkylight) {
if (light != null && !light.propagatesLight) {
return
}
this.light[index] = ((currentLight and BLOCK_LIGHT_MASK) or (nextLevel shl 4)).toByte()
val nextNeighbourLevel = nextLevel - 1
// ToDo: Neighbours
if (y > 0 && (light == null || light.propagatesSkylight(direction, Directions.DOWN))) {
if (y > 0 && (light == null || light.propagatesLight(direction, Directions.DOWN))) {
traceSkylight(x, y - 1, z, nextNeighbourLevel, Directions.DOWN, false)
}
if (y < ProtocolDefinition.SECTION_MAX_Y && (light == null || light.propagatesSkylight(direction, Directions.UP))) {
if (y < ProtocolDefinition.SECTION_MAX_Y && (light == null || light.propagatesLight(direction, Directions.UP))) {
traceSkylight(x, y + 1, z, nextNeighbourLevel, Directions.UP, false)
}
if (z > 0 && (light == null || light.propagatesSkylight(direction, Directions.NORTH))) {
if (z > 0 && (light == null || light.propagatesLight(direction, Directions.NORTH))) {
traceSkylight(x, y, z - 1, nextNeighbourLevel, Directions.NORTH, false)
}
if (z < ProtocolDefinition.SECTION_MAX_Z && (light == null || light.propagatesSkylight(direction, Directions.SOUTH))) {
if (z < ProtocolDefinition.SECTION_MAX_Z && (light == null || light.propagatesLight(direction, Directions.SOUTH))) {
traceSkylight(x, y, z + 1, nextNeighbourLevel, Directions.SOUTH, false)
}
if (x > 0 && (light == null || light.propagatesSkylight(direction, Directions.WEST))) {
if (x > 0 && (light == null || light.propagatesLight(direction, Directions.WEST))) {
traceSkylight(x - 1, y, z, nextNeighbourLevel, Directions.WEST, false)
}
if (x < ProtocolDefinition.SECTION_MAX_X && (light == null || light.propagatesSkylight(direction, Directions.EAST))) {
if (x < ProtocolDefinition.SECTION_MAX_X && (light == null || light.propagatesLight(direction, Directions.EAST))) {
traceSkylight(x + 1, y, z, nextNeighbourLevel, Directions.EAST, false)
}
}

View File

@ -171,6 +171,9 @@ class GLFWWindow(
initLatch.await() // wait for async glfw init
glfwDefaultWindowHints()
if (renderWindow.preferQuads) {
// yes, this is dirty. for using a geometry shader we need 3.3+. The thing is 3.3+ does not allow us to use GL_QUAD.
// we can still bind it to a lower version and use features that need a more recent version of opengl.
// most drivers allow us to do this, if not it'll crash
setOpenGLVersion(3, 0, false)
} else {
setOpenGLVersion(3, 3, true)

View File

@ -91,7 +91,7 @@ class SolidCullSectionPreparer(
light[SELF_LIGHT_INDEX] = sectionLight[index]
position = Vec3i(offsetX + x, offsetY + y, offsetZ + z)
val maxHeight = chunk.heightmap[baseIndex]
val maxHeight = chunk.skylightHeightmap[baseIndex]
if (position.y >= maxHeight) {
light[SELF_LIGHT_INDEX] = (light[SELF_LIGHT_INDEX].toInt() or 0xF0).toByte()
}
@ -209,7 +209,7 @@ class SolidCullSectionPreparer(
val neighbourIndex = y shl 8 or nextBaseIndex
neighbourBlocks[ordinal] = blocks?.unsafeGet(neighbourIndex)
light[ordinal] = sectionLight[neighbourIndex]
if (position.y > chunk.heightmap[nextBaseIndex]) {
if (position.y > chunk.skylightHeightmap[nextBaseIndex]) {
light[ordinal] = (light[ordinal].toInt() or 0xF0).toByte()
}
}

View File

@ -89,6 +89,7 @@ public final class ProtocolDefinition {
public static final float TICKS_PER_DAYf = (float) TICKS_PER_DAY;
public static final byte LIGHT_LEVELS = 16;
public static final byte MAX_LIGHT_LEVEL = LIGHT_LEVELS - 1;
static {
InetAddress inetAddress;