diff --git a/ReadMe.md b/ReadMe.md index 1a1379d2b..3b1766e58 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/DirectedProperty.kt b/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/DirectedProperty.kt index dd9882a29..a61179b1b 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/DirectedProperty.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/DirectedProperty.kt @@ -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)] } diff --git a/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/LightProperties.kt b/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/LightProperties.kt index 6e98c9ba4..a63463a84 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/LightProperties.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/LightProperties.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/TransparentProperty.kt b/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/TransparentProperty.kt index bd7243774..501ad693e 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/TransparentProperty.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/blocks/light/TransparentProperty.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/Chunk.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/Chunk.kt index eb07919f0..373996ea4 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/Chunk.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/Chunk.kt @@ -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? = 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 + } + + } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt index a6d38e1de..39cf89884 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/light/SectionLight.kt @@ -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) } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/window/GLFWWindow.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/window/GLFWWindow.kt index 8fc0949ed..01a3ebc81 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/window/GLFWWindow.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/window/GLFWWindow.kt @@ -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) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/world/preparer/cull/SolidCullSectionPreparer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/world/preparer/cull/SolidCullSectionPreparer.kt index 41d0c84fe..854e55454 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/world/preparer/cull/SolidCullSectionPreparer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/world/preparer/cull/SolidCullSectionPreparer.kt @@ -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() } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java index 12446fed7..0e84b04fc 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/ProtocolDefinition.java @@ -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;