mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-14 09:56:37 -04:00
Merge branch 'improve-light'
This commit is contained in:
commit
cfd1207cdd
@ -222,7 +222,7 @@ testing {
|
|||||||
options {
|
options {
|
||||||
val options = this as TestNGOptions
|
val options = this as TestNGOptions
|
||||||
options.preserveOrder = true
|
options.preserveOrder = true
|
||||||
// options.excludeGroups("input", "font", "command", "registry", "biome", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "block", "physics", "light", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "world_renderer", "rendering")
|
// options.excludeGroups("input", "font", "command", "registry", "biome", "version", "fluid", "world", "raycasting", "pixlyzer", "item", "physics", "packet", "container", "item_stack", "signature", "private_key", "interaction", "item_digging", "world_renderer", "rendering")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import de.bixilon.minosoft.data.registries.blocks.light.OpaqueProperty
|
|||||||
import de.bixilon.minosoft.data.registries.blocks.settings.BlockSettings
|
import de.bixilon.minosoft.data.registries.blocks.settings.BlockSettings
|
||||||
import de.bixilon.minosoft.data.registries.blocks.state.AdvancedBlockState
|
import de.bixilon.minosoft.data.registries.blocks.state.AdvancedBlockState
|
||||||
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
||||||
|
import de.bixilon.minosoft.data.registries.blocks.state.manager.SimpleStateManager
|
||||||
import de.bixilon.minosoft.data.registries.blocks.types.Block
|
import de.bixilon.minosoft.data.registries.blocks.types.Block
|
||||||
import de.bixilon.minosoft.data.registries.blocks.types.TestBlock
|
import de.bixilon.minosoft.data.registries.blocks.types.TestBlock
|
||||||
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
|
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
|
||||||
@ -109,8 +110,7 @@ object ChunkTestingUtil {
|
|||||||
fun createBlock(name: String, luminance: Int, lightProperties: LightProperties): Block {
|
fun createBlock(name: String, luminance: Int, lightProperties: LightProperties): Block {
|
||||||
val block = TestBlock(minosoft(name), BlockSettings())
|
val block = TestBlock(minosoft(name), BlockSettings())
|
||||||
val state = AdvancedBlockState(block, properties = emptyMap(), collisionShape = AbstractVoxelShape.EMPTY, outlineShape = AbstractVoxelShape.EMPTY, luminance = luminance, lightProperties = lightProperties, solidRenderer = true)
|
val state = AdvancedBlockState(block, properties = emptyMap(), collisionShape = AbstractVoxelShape.EMPTY, outlineShape = AbstractVoxelShape.EMPTY, luminance = luminance, lightProperties = lightProperties, solidRenderer = true)
|
||||||
block::states.javaField!!.forceSet(block, setOf(state))
|
block::states.forceSet(SimpleStateManager(state))
|
||||||
block.states::default.javaField!!.forceSet(block, state)
|
|
||||||
|
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
@ -79,9 +79,10 @@ internal class LightBenchmark {
|
|||||||
for (index in 0 until 256) {
|
for (index in 0 until 256) {
|
||||||
highest.unsafeSet(index or (0x0F shl 8), solid)
|
highest.unsafeSet(index or (0x0F shl 8), solid)
|
||||||
}
|
}
|
||||||
|
|
||||||
var totalPlace = 0L
|
var totalPlace = 0L
|
||||||
var totalBreak = 0L
|
var totalBreak = 0L
|
||||||
val benchmark = BenchmarkUtil.benchmark(10000) {
|
val benchmark = BenchmarkUtil.benchmark(100000) {
|
||||||
totalBreak += measureNanoTime { chunk[7, 255, 7] = null }
|
totalBreak += measureNanoTime { chunk[7, 255, 7] = null }
|
||||||
totalPlace += measureNanoTime { chunk[7, 255, 7] = solid }
|
totalPlace += measureNanoTime { chunk[7, 255, 7] = solid }
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ object WorldTestUtil {
|
|||||||
if (chunk == null) {
|
if (chunk == null) {
|
||||||
chunk = this.chunks[chunkPosition] ?: continue
|
chunk = this.chunks[chunkPosition] ?: continue
|
||||||
} else if (chunk.chunkPosition != chunkPosition) {
|
} else if (chunk.chunkPosition != chunkPosition) {
|
||||||
chunk = chunk.traceChunk(chunkPosition - chunk.chunkPosition) ?: continue
|
chunk = chunk.neighbours.trace(chunkPosition - chunk.chunkPosition) ?: continue
|
||||||
}
|
}
|
||||||
for (y in start.y..end.y) {
|
for (y in start.y..end.y) {
|
||||||
val section = chunk.getOrPut(y.sectionHeight) ?: continue
|
val section = chunk.getOrPut(y.sectionHeight) ?: continue
|
||||||
@ -96,7 +96,7 @@ object WorldTestUtil {
|
|||||||
for (z in (start.z shr 4)..(end.z shr 4)) {
|
for (z in (start.z shr 4)..(end.z shr 4)) {
|
||||||
val chunkPosition = Vec2i(x, z)
|
val chunkPosition = Vec2i(x, z)
|
||||||
chunk = if (chunk != null) {
|
chunk = if (chunk != null) {
|
||||||
chunk.traceChunk(chunkPosition - chunk.chunkPosition) ?: continue
|
chunk.neighbours.trace(chunkPosition - chunk.chunkPosition) ?: continue
|
||||||
} else {
|
} else {
|
||||||
this.chunks[chunkPosition] ?: continue
|
this.chunks[chunkPosition] ?: continue
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
package de.bixilon.minosoft.data.world.chunk.light
|
package de.bixilon.minosoft.data.world.chunk.light
|
||||||
|
|
||||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
|
import de.bixilon.minosoft.data.registries.blocks.GlassTest0
|
||||||
|
import de.bixilon.minosoft.data.registries.blocks.StairsTest0
|
||||||
import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0
|
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.ChunkTestingUtil.createChunkWithNeighbours
|
||||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||||
@ -25,32 +27,13 @@ import org.testng.annotations.Test
|
|||||||
@Test(groups = ["light"], dependsOnGroups = ["block"])
|
@Test(groups = ["light"], dependsOnGroups = ["block"])
|
||||||
class GeneralHeightmapTest {
|
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() {
|
fun testMinHeightEast() {
|
||||||
val chunk: Chunk = createChunkWithNeighbours()
|
val chunk: Chunk = createChunkWithNeighbours()
|
||||||
chunk[Vec3i(2, 10, 3)] = StoneTest0.state
|
chunk[Vec3i(2, 10, 3)] = StoneTest0.state
|
||||||
chunk[Vec3i(3, 11, 2)] = StoneTest0.state
|
chunk[Vec3i(3, 11, 2)] = StoneTest0.state
|
||||||
chunk[Vec3i(3, 12, 4)] = StoneTest0.state
|
chunk[Vec3i(3, 12, 4)] = StoneTest0.state
|
||||||
chunk[Vec3i(4, 13, 3)] = StoneTest0.state
|
chunk[Vec3i(4, 13, 3)] = StoneTest0.state
|
||||||
assertEquals(chunk.light.getNeighbourMinHeight(chunk.neighbours.get()!!, 3, 3), 11)
|
assertEquals(chunk.light.sky.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() {
|
fun testMinHeightNeighbourEast() {
|
||||||
@ -60,8 +43,26 @@ class GeneralHeightmapTest {
|
|||||||
chunk[Vec3i(15, 12, 2)] = StoneTest0.state
|
chunk[Vec3i(15, 12, 2)] = StoneTest0.state
|
||||||
chunk[Vec3i(15, 13, 4)] = StoneTest0.state
|
chunk[Vec3i(15, 13, 4)] = StoneTest0.state
|
||||||
neighbours[ChunkNeighbours.EAST][Vec3i(0, 10, 3)] = StoneTest0.state
|
neighbours[ChunkNeighbours.EAST][Vec3i(0, 10, 3)] = StoneTest0.state
|
||||||
assertEquals(chunk.light.getNeighbourMinHeight(neighbours, 15, 3), 11)
|
assertEquals(chunk.light.sky.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)] = StairsTest0.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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Test other directions
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `heightmap optimization west, upper block set 2`() {
|
||||||
|
val world = createConnection(3, light = true).world
|
||||||
|
world[Vec3i(8, 255, 8)] = StoneTest0.state
|
||||||
|
world[Vec3i(7, 1, 8)] = StoneTest0.state
|
||||||
|
for (y in 0..254) {
|
||||||
|
world.assertLight(8, y, 8, 0xE0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 0xE0)
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,7 @@ package de.bixilon.minosoft.data.world.chunk.light.place
|
|||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
import de.bixilon.kutil.collections.CollectionUtil.synchronizedSetOf
|
import de.bixilon.kutil.collections.CollectionUtil.synchronizedListOf
|
||||||
import de.bixilon.minosoft.data.registries.blocks.TorchTest0
|
import de.bixilon.minosoft.data.registries.blocks.TorchTest0
|
||||||
import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0
|
import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0
|
||||||
import de.bixilon.minosoft.data.world.WorldTestUtil.fill
|
import de.bixilon.minosoft.data.world.WorldTestUtil.fill
|
||||||
@ -166,24 +166,23 @@ class BlockLightPlaceIT {
|
|||||||
|
|
||||||
fun lightUpdate() {
|
fun lightUpdate() {
|
||||||
val world = ConnectionTestUtil.createConnection(3, light = true).world
|
val world = ConnectionTestUtil.createConnection(3, light = true).world
|
||||||
val events: MutableSet<Vec3i> = synchronizedSetOf()
|
val events: MutableList<Vec3i> = synchronizedListOf()
|
||||||
world.connection.events.listen<WorldUpdateEvent> {
|
world.connection.events.listen<WorldUpdateEvent> {
|
||||||
if (it.update !is ChunkLightUpdate) return@listen
|
if (it.update !is ChunkLightUpdate) return@listen
|
||||||
events += Vec3i(it.update.chunkPosition.x, (it.update as ChunkLightUpdate).sectionHeight, it.update.chunkPosition.y)
|
events += Vec3i(it.update.chunkPosition.x, (it.update as ChunkLightUpdate).sectionHeight, it.update.chunkPosition.y)
|
||||||
}
|
}
|
||||||
world[Vec3i(8, 24, 8)] = TorchTest0.state
|
world[Vec3i(8, 24, 8)] = TorchTest0.state
|
||||||
|
|
||||||
assertEquals(
|
assertEquals(events.toSet(), setOf(
|
||||||
events, setOf(
|
Vec3i(+0, 1, +0),
|
||||||
Vec3i(+0, 1, +0),
|
Vec3i(+0, 0, +0),
|
||||||
Vec3i(+0, 0, +0),
|
Vec3i(+0, 2, +0),
|
||||||
Vec3i(+0, 2, +0),
|
Vec3i(+0, 1, -1),
|
||||||
Vec3i(+0, 1, -1),
|
Vec3i(+0, 1, +1),
|
||||||
Vec3i(+0, 1, +1),
|
Vec3i(-1, 1, +0),
|
||||||
Vec3i(-1, 1, +0),
|
Vec3i(+1, 1, +0),
|
||||||
Vec3i(+1, 1, +0),
|
))
|
||||||
)
|
assertEquals(events.size, 7)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ import de.bixilon.minosoft.protocol.network.connection.play.ConnectionTestUtil.c
|
|||||||
import org.testng.annotations.Test
|
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 {
|
class SkyLightPlaceIT {
|
||||||
|
|
||||||
fun aboveBlock() {
|
fun aboveBlock() {
|
||||||
@ -44,9 +44,37 @@ class SkyLightPlaceIT {
|
|||||||
world.assertLight(8, 9, 8, 0xE0)
|
world.assertLight(8, 9, 8, 0xE0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun `below block 1`() {
|
||||||
|
val world = createConnection(3, light = true).world
|
||||||
|
world[Vec3i(8, 16, 8)] = StoneTest0.state
|
||||||
|
world.assertLight(8, 15, 8, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `below block 2`() {
|
||||||
|
val world = createConnection(3, light = true).world
|
||||||
|
world[Vec3i(8, 0, 8)] = StoneTest0.state
|
||||||
|
world.assertLight(8, -1, 8, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `below block 3`() {
|
||||||
|
val world = createConnection(3, light = true).world
|
||||||
|
world[Vec3i(8, 15, 8)] = StoneTest0.state
|
||||||
|
world.assertLight(8, 14, 8, 0xE0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `more blocks below block`() {
|
||||||
|
val world = createConnection(3, light = true).world
|
||||||
|
world[Vec3i(8, 37, 8)] = StoneTest0.state
|
||||||
|
for (y in 0..36) {
|
||||||
|
world.assertLight(8, y, 8, 0xE0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun belowBlock3() {
|
fun belowBlock3() {
|
||||||
val world = createConnection(3, light = true).world
|
val world = createConnection(3, light = true).world
|
||||||
world.fill(7, 10, 7, 9, 10, 9, StoneTest0.state, false)
|
world.fill(7, 10, 7, 9, 10, 9, StoneTest0.state, false)
|
||||||
|
// world.chunks[0,0]!!.light.reset()
|
||||||
|
// world.chunks[0,0]!!.light.sky.calculate()
|
||||||
world.assertLight(8, 9, 8, 0xD0)
|
world.assertLight(8, 9, 8, 0xD0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,79 @@
|
|||||||
|
package de.bixilon.minosoft.data.world.container.block
|
||||||
|
|
||||||
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
|
import de.bixilon.minosoft.data.registries.blocks.types.stone.StoneTest0
|
||||||
|
import org.testng.Assert.*
|
||||||
|
import org.testng.annotations.Test
|
||||||
|
|
||||||
|
@Test(groups = ["chunk"], dependsOnGroups = ["block"])
|
||||||
|
class BlockSectionDataProviderTest {
|
||||||
|
|
||||||
|
private fun create(): BlockSectionDataProvider {
|
||||||
|
return BlockSectionDataProvider(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `initial empty`() {
|
||||||
|
val blocks = create()
|
||||||
|
assertTrue(blocks.isEmpty)
|
||||||
|
assertEquals(blocks.fluidCount, 0)
|
||||||
|
assertEquals(blocks.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `single block set and removed`() {
|
||||||
|
val blocks = create()
|
||||||
|
blocks[0] = StoneTest0.state
|
||||||
|
blocks[0] = null
|
||||||
|
assertTrue(blocks.isEmpty)
|
||||||
|
assertEquals(blocks.fluidCount, 0)
|
||||||
|
assertEquals(blocks.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `single block set`() {
|
||||||
|
val blocks = create()
|
||||||
|
blocks[0] = StoneTest0.state
|
||||||
|
assertFalse(blocks.isEmpty)
|
||||||
|
assertEquals(blocks.fluidCount, 0)
|
||||||
|
assertEquals(blocks.count, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `initial min max position`() {
|
||||||
|
val blocks = create()
|
||||||
|
assertEquals(blocks.minPosition, Vec3i(16, 16, 16))
|
||||||
|
assertEquals(blocks.maxPosition, Vec3i(0, 0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `set min max position`() {
|
||||||
|
val blocks = create()
|
||||||
|
blocks[0] = StoneTest0.state
|
||||||
|
assertEquals(blocks.minPosition, Vec3i(0, 0, 0))
|
||||||
|
assertEquals(blocks.maxPosition, Vec3i(0, 0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `set min max position but block not on edge`() {
|
||||||
|
val blocks = create()
|
||||||
|
blocks[3, 5, 8] = StoneTest0.state
|
||||||
|
assertEquals(blocks.minPosition, Vec3i(3, 5, 8))
|
||||||
|
assertEquals(blocks.maxPosition, Vec3i(3, 5, 8))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `set min max position but multiple blocks set`() {
|
||||||
|
val blocks = create()
|
||||||
|
blocks[3, 5, 8] = StoneTest0.state
|
||||||
|
blocks[1, 2, 12] = StoneTest0.state
|
||||||
|
assertEquals(blocks.minPosition, Vec3i(1, 2, 8))
|
||||||
|
assertEquals(blocks.maxPosition, Vec3i(3, 5, 12))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun `remove one min max position but multiple blocks set`() {
|
||||||
|
val blocks = create()
|
||||||
|
blocks[3, 5, 8] = StoneTest0.state
|
||||||
|
blocks[1, 2, 12] = StoneTest0.state
|
||||||
|
blocks[15, 14, 13] = StoneTest0.state
|
||||||
|
assertEquals(blocks.minPosition, Vec3i(1, 2, 8))
|
||||||
|
assertEquals(blocks.maxPosition, Vec3i(15, 14, 13))
|
||||||
|
blocks[15, 14, 13] = null
|
||||||
|
assertEquals(blocks.maxPosition, Vec3i(3, 5, 12))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test initial block set
|
||||||
|
}
|
@ -124,7 +124,7 @@ class TargetHandler(
|
|||||||
if (chunk == null) {
|
if (chunk == null) {
|
||||||
chunk = camera.connection.world.chunks[chunkPosition] ?: break
|
chunk = camera.connection.world.chunks[chunkPosition] ?: break
|
||||||
} else if (chunk.chunkPosition != chunkPosition) {
|
} else if (chunk.chunkPosition != chunkPosition) {
|
||||||
chunk = chunk.traceChunk(chunkPosition - chunk.chunkPosition) ?: break
|
chunk = chunk.neighbours.trace(chunkPosition - chunk.chunkPosition) ?: break
|
||||||
}
|
}
|
||||||
val state = chunk[blockPosition.inChunkPosition] ?: continue
|
val state = chunk[blockPosition.inChunkPosition] ?: continue
|
||||||
if (state.block is FluidBlock) {
|
if (state.block is FluidBlock) {
|
||||||
|
@ -20,7 +20,7 @@ import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
|||||||
|
|
||||||
interface DimensionEffects : Identified {
|
interface DimensionEffects : Identified {
|
||||||
val daylightCycle: Boolean
|
val daylightCycle: Boolean
|
||||||
val skylight: Boolean
|
val skyLight: Boolean
|
||||||
val fixedTexture: ResourceLocation? get() = null
|
val fixedTexture: ResourceLocation? get() = null
|
||||||
|
|
||||||
val weather: Boolean
|
val weather: Boolean
|
||||||
|
@ -24,7 +24,7 @@ object EndEffects : DimensionEffects {
|
|||||||
override val identifier = minecraft("the_end")
|
override val identifier = minecraft("the_end")
|
||||||
|
|
||||||
override val daylightCycle: Boolean get() = false
|
override val daylightCycle: Boolean get() = false
|
||||||
override val skylight: Boolean get() = false
|
override val skyLight: Boolean get() = false
|
||||||
override val fixedTexture: ResourceLocation = minecraft("environment/end_sky").texture()
|
override val fixedTexture: ResourceLocation = minecraft("environment/end_sky").texture()
|
||||||
|
|
||||||
override val weather: Boolean get() = false
|
override val weather: Boolean get() = false
|
||||||
|
@ -21,7 +21,7 @@ object NetherEffects : DimensionEffects {
|
|||||||
override val identifier = minecraft("the_nether")
|
override val identifier = minecraft("the_nether")
|
||||||
|
|
||||||
override val daylightCycle: Boolean get() = false
|
override val daylightCycle: Boolean get() = false
|
||||||
override val skylight: Boolean get() = false
|
override val skyLight: Boolean get() = false
|
||||||
|
|
||||||
override val weather: Boolean get() = false
|
override val weather: Boolean get() = false
|
||||||
override val sun: Boolean get() = false
|
override val sun: Boolean get() = false
|
||||||
|
@ -21,7 +21,7 @@ object OverworldEffects : DimensionEffects {
|
|||||||
override val identifier = minecraft("overworld")
|
override val identifier = minecraft("overworld")
|
||||||
|
|
||||||
override val daylightCycle: Boolean get() = true
|
override val daylightCycle: Boolean get() = true
|
||||||
override val skylight: Boolean get() = true
|
override val skyLight: Boolean get() = true
|
||||||
|
|
||||||
override val weather: Boolean get() = true
|
override val weather: Boolean get() = true
|
||||||
override val sun: Boolean get() = true
|
override val sun: Boolean get() = true
|
||||||
|
@ -54,7 +54,7 @@ abstract class Fluid(override val identifier: ResourceLocation) : RegistryItem()
|
|||||||
|
|
||||||
val offset = blockPosition.inChunkPosition
|
val offset = blockPosition.inChunkPosition
|
||||||
for (direction in Directions.SIDES) {
|
for (direction in Directions.SIDES) {
|
||||||
val neighbour = chunk.traceBlock(offset + direction) ?: continue
|
val neighbour = chunk.neighbours.traceBlock(offset + direction) ?: continue
|
||||||
if (!this.matches(neighbour)) {
|
if (!this.matches(neighbour)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ import de.bixilon.minosoft.data.world.biome.accessor.BiomeAccessor
|
|||||||
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
||||||
import de.bixilon.minosoft.data.world.border.WorldBorder
|
import de.bixilon.minosoft.data.world.border.WorldBorder
|
||||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||||
import de.bixilon.minosoft.data.world.chunk.light.ChunkLight.Companion.canSkylight
|
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
|
||||||
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
|
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
|
||||||
import de.bixilon.minosoft.data.world.chunk.manager.ChunkManager
|
import de.bixilon.minosoft.data.world.chunk.manager.ChunkManager
|
||||||
import de.bixilon.minosoft.data.world.difficulty.WorldDifficulty
|
import de.bixilon.minosoft.data.world.difficulty.WorldDifficulty
|
||||||
@ -187,7 +187,7 @@ class World(
|
|||||||
chunkDelta.x = (origin.x - position.x) shr 4
|
chunkDelta.x = (origin.x - position.x) shr 4
|
||||||
chunkDelta.y = (origin.z - position.z) shr 4
|
chunkDelta.y = (origin.z - position.z) shr 4
|
||||||
|
|
||||||
val state = chunk.traceBlock(position.x and 0x0F, position.y, position.z and 0x0F, chunkDelta) ?: return
|
val state = chunk.neighbours.traceBlock(position.x and 0x0F, position.y, position.z and 0x0F, chunkDelta) ?: return
|
||||||
if (state.block !is RandomDisplayTickable) return
|
if (state.block !is RandomDisplayTickable) return
|
||||||
if (!state.block.hasRandomTicks(connection, state, position)) return
|
if (!state.block.hasRandomTicks(connection, state, position)) return
|
||||||
|
|
||||||
@ -214,7 +214,7 @@ class World(
|
|||||||
fun getBrightness(position: BlockPosition): Float {
|
fun getBrightness(position: BlockPosition): Float {
|
||||||
val light = getLight(position)
|
val light = getLight(position)
|
||||||
var level = light and SectionLight.BLOCK_LIGHT_MASK
|
var level = light and SectionLight.BLOCK_LIGHT_MASK
|
||||||
if (dimension.canSkylight()) {
|
if (dimension.hasSkyLight()) {
|
||||||
level = maxOf(level, light and SectionLight.SKY_LIGHT_MASK shr 4)
|
level = maxOf(level, light and SectionLight.SKY_LIGHT_MASK shr 4)
|
||||||
}
|
}
|
||||||
return dimension.ambientLight[level]
|
return dimension.ambientLight[level]
|
||||||
@ -228,7 +228,7 @@ class World(
|
|||||||
reset += { chunk.light.reset() }
|
reset += { chunk.light.reset() }
|
||||||
calculate += {
|
calculate += {
|
||||||
if (heightmap) {
|
if (heightmap) {
|
||||||
chunk.light.recalculateHeightmap()
|
chunk.light.heightmap.recalculate()
|
||||||
}
|
}
|
||||||
chunk.light.calculate()
|
chunk.light.calculate()
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ import de.bixilon.kutil.array.ArrayUtil.cast
|
|||||||
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
import de.bixilon.kutil.cast.CastUtil.unsafeNull
|
||||||
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
import de.bixilon.minosoft.data.entities.block.BlockEntity
|
||||||
import de.bixilon.minosoft.data.registries.biomes.Biome
|
import de.bixilon.minosoft.data.registries.biomes.Biome
|
||||||
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
|
||||||
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
||||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||||
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
|
import de.bixilon.minosoft.data.world.chunk.light.SectionLight
|
||||||
@ -97,25 +96,6 @@ class ChunkSection(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
operator fun set(x: Int, y: Int, z: Int, block: BlockState?): BlockState? {
|
|
||||||
val previous = blocks.set(x, y, z, block)
|
|
||||||
if (chunk.world.dimension.light) {
|
|
||||||
light.onBlockChange(x, y, z, previous, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
return previous
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unsafeSet(x: Int, y: Int, z: Int, block: BlockState?): BlockState? {
|
|
||||||
val previous = blocks.unsafeSet(x, y, z, block)
|
|
||||||
|
|
||||||
if (chunk.world.dimension.light) {
|
|
||||||
light.onBlockChange(x, y, z, previous, block)
|
|
||||||
}
|
|
||||||
|
|
||||||
return previous
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
fun clear() {
|
||||||
blocks.clear()
|
blocks.clear()
|
||||||
biomes.clear()
|
biomes.clear()
|
||||||
|
@ -15,7 +15,6 @@ package de.bixilon.minosoft.data.world.chunk.chunk
|
|||||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
|
import de.bixilon.kutil.concurrent.lock.thread.ThreadLock
|
||||||
import de.bixilon.kutil.exception.Broken
|
|
||||||
import de.bixilon.kutil.math.simple.IntMath.clamp
|
import de.bixilon.kutil.math.simple.IntMath.clamp
|
||||||
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
|
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
|
||||||
import de.bixilon.minosoft.data.direction.Directions
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
@ -31,15 +30,12 @@ import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
|
|||||||
import de.bixilon.minosoft.data.world.chunk.update.block.ChunkLocalBlockUpdate
|
import de.bixilon.minosoft.data.world.chunk.update.block.ChunkLocalBlockUpdate
|
||||||
import de.bixilon.minosoft.data.world.chunk.update.block.SingleBlockUpdate
|
import de.bixilon.minosoft.data.world.chunk.update.block.SingleBlockUpdate
|
||||||
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
import de.bixilon.minosoft.data.world.positions.ChunkPosition
|
||||||
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition
|
|
||||||
import de.bixilon.minosoft.data.world.positions.InChunkPosition
|
import de.bixilon.minosoft.data.world.positions.InChunkPosition
|
||||||
import de.bixilon.minosoft.data.world.positions.SectionHeight
|
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.inSectionHeight
|
||||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
||||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.inChunkPosition
|
|
||||||
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
||||||
import de.bixilon.minosoft.util.chunk.ChunkUtil
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,13 +58,9 @@ class Chunk(
|
|||||||
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
light.recalculateHeightmap()
|
light.heightmap.recalculate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("neighbours.complete", ReplaceWith("neighbours.complete"))
|
|
||||||
val isFullyLoaded: Boolean
|
|
||||||
get() = neighbours.complete
|
|
||||||
|
|
||||||
operator fun get(sectionHeight: SectionHeight): ChunkSection? = sections.getOrNull(sectionHeight - minSection)
|
operator fun get(sectionHeight: SectionHeight): ChunkSection? = sections.getOrNull(sectionHeight - minSection)
|
||||||
|
|
||||||
operator fun get(x: Int, y: Int, z: Int): BlockState? {
|
operator fun get(x: Int, y: Int, z: Int): BlockState? {
|
||||||
@ -79,19 +71,23 @@ class Chunk(
|
|||||||
|
|
||||||
operator fun set(x: Int, y: Int, z: Int, state: BlockState?) {
|
operator fun set(x: Int, y: Int, z: Int, state: BlockState?) {
|
||||||
val section = getOrPut(y.sectionHeight) ?: return
|
val section = getOrPut(y.sectionHeight) ?: return
|
||||||
val previous = section.set(x, y and 0x0F, z, state)
|
val previous = section.blocks.set(x, y and 0x0F, z, state)
|
||||||
if (previous == state) return
|
if (previous == state) return
|
||||||
val entity = getOrPutBlockEntity(x, y, z)
|
val entity = getOrPutBlockEntity(x, y, z)
|
||||||
light.onBlockChange(x, y, z, section, state) // TODO: heightmap gets updated after the block set? -> optimize/maybe even invalid
|
|
||||||
|
if (world.dimension.light) {
|
||||||
|
light.onBlockChange(x, y, z, section, previous, state)
|
||||||
|
}
|
||||||
|
|
||||||
SingleBlockUpdate(Vec3i(chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X + x, y, chunkPosition.y * ProtocolDefinition.SECTION_WIDTH_Z + z), this, state, entity).fire(connection)
|
SingleBlockUpdate(Vec3i(chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X + x, y, chunkPosition.y * ProtocolDefinition.SECTION_WIDTH_Z + z), this, state, entity).fire(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun set(x: Int, y: Int, z: Int, state: BlockState?, entity: BlockEntity?) {
|
// fun set(x: Int, y: Int, z: Int, state: BlockState?, entity: BlockEntity?) {
|
||||||
val section = getOrPut(y.sectionHeight) ?: return
|
// val section = getOrPut(y.sectionHeight) ?: return
|
||||||
section[x, y and 0x0F, z] = state
|
// section.blocks[x, y and 0x0F, z] = state
|
||||||
section.blockEntities[x, y and 0x0F, z] = entity
|
// section.blockEntities[x, y and 0x0F, z] = entity
|
||||||
}
|
// // TODO: light update
|
||||||
|
// }
|
||||||
|
|
||||||
operator fun set(position: Vec3i, blockState: BlockState?) = set(position.x, position.y, position.z, blockState)
|
operator fun set(position: Vec3i, blockState: BlockState?) = set(position.x, position.y, position.z, blockState)
|
||||||
|
|
||||||
@ -162,7 +158,7 @@ class Chunk(
|
|||||||
if (executed.isEmpty()) {
|
if (executed.isEmpty()) {
|
||||||
return lock.unlock()
|
return lock.unlock()
|
||||||
}
|
}
|
||||||
light.recalculateHeightmap()
|
light.heightmap.recalculate()
|
||||||
light.recalculate()
|
light.recalculate()
|
||||||
|
|
||||||
for (section in sections) {
|
for (section in sections) {
|
||||||
@ -203,7 +199,7 @@ class Chunk(
|
|||||||
if (neighbours != null) {
|
if (neighbours != null) {
|
||||||
for (neighbour in neighbours) {
|
for (neighbour in neighbours) {
|
||||||
val neighbourNeighbours = neighbour.neighbours.get() ?: continue
|
val neighbourNeighbours = neighbour.neighbours.get() ?: continue
|
||||||
neighbour.updateNeighbours(neighbourNeighbours, sectionHeight)
|
neighbour.neighbours.update(neighbourNeighbours, sectionHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,67 +231,6 @@ class Chunk(
|
|||||||
}
|
}
|
||||||
return biomeSource.getBiome(x and 0x0F, y, z and 0x0F)
|
return biomeSource.getBiome(x and 0x0F, y, z and 0x0F)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("")
|
|
||||||
private fun updateNeighbours(neighbours: Array<Chunk>, sectionHeight: Int) {
|
|
||||||
for (nextSectionHeight in sectionHeight - 1..sectionHeight + 1) {
|
|
||||||
if (nextSectionHeight < minSection || nextSectionHeight > maxSection) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val section = this[nextSectionHeight] ?: continue
|
|
||||||
val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours, this, nextSectionHeight)
|
|
||||||
section.neighbours = sectionNeighbours
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("neighbours")
|
|
||||||
fun traceBlock(offset: Vec3i, origin: Vec3i, blockPosition: Vec3i = origin + offset): BlockState? {
|
|
||||||
val chunkDelta = (origin - blockPosition).chunkPosition
|
|
||||||
|
|
||||||
return traceBlock(blockPosition.x and 0x0F, blockPosition.y, blockPosition.z and 0x0F, chunkDelta)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("neighbours")
|
|
||||||
fun traceBlock(offset: Vec3i): BlockState? {
|
|
||||||
return traceBlock(offset.inChunkPosition, offset.chunkPosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("neighbours")
|
|
||||||
fun traceChunk(offset: Vec2i): Chunk? {
|
|
||||||
if (offset.x == 0 && offset.y == 0) {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
if (offset.x > 0) {
|
|
||||||
offset.x--
|
|
||||||
return neighbours[6]?.traceChunk(offset)
|
|
||||||
}
|
|
||||||
if (offset.x < 0) {
|
|
||||||
offset.x++
|
|
||||||
return neighbours[1]?.traceChunk(offset)
|
|
||||||
}
|
|
||||||
if (offset.y > 0) {
|
|
||||||
offset.y--
|
|
||||||
return neighbours[4]?.traceChunk(offset)
|
|
||||||
}
|
|
||||||
if (offset.y < 0) {
|
|
||||||
offset.y++
|
|
||||||
return neighbours[3]?.traceChunk(offset)
|
|
||||||
}
|
|
||||||
|
|
||||||
Broken("Can not get chunk from offset: $offset")
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("neighbours")
|
|
||||||
private fun traceBlock(inChunkPosition: Vec3i, chunkOffset: Vec2i): BlockState? {
|
|
||||||
return traceChunk(chunkOffset)?.get(inChunkPosition)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated("neighbours")
|
|
||||||
fun traceBlock(x: Int, y: Int, z: Int, chunkOffset: Vec2i): BlockState? {
|
|
||||||
return traceChunk(chunkOffset)?.get(x, y, z)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* 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 ChunkHeightmap(protected val chunk: Chunk) : Heightmap {
|
||||||
|
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 + 1) * 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
|
||||||
|
|
||||||
|
val min = section.blocks.minPosition
|
||||||
|
val max = section.blocks.maxPosition
|
||||||
|
|
||||||
|
if (x < min.x || x > max.x || z < min.z || z > max.z) continue // out of section
|
||||||
|
|
||||||
|
section.acquire()
|
||||||
|
for (sectionY in max.y downTo min.y) {
|
||||||
|
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
|
||||||
|
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, state: BlockState?) {
|
||||||
|
chunk.lock.lock()
|
||||||
|
val index = (z shl 4) or x
|
||||||
|
|
||||||
|
val previous = heightmap[index]
|
||||||
|
|
||||||
|
if (previous > y + 1) {
|
||||||
|
// our block is/was not the highest, ignore everything
|
||||||
|
chunk.lock.unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (state == null) {
|
||||||
|
trace(x, y, z, true)
|
||||||
|
chunk.lock.unlock()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val next = when (passes(state)) {
|
||||||
|
HeightmapPass.ABOVE -> y + 1
|
||||||
|
HeightmapPass.IN -> y
|
||||||
|
HeightmapPass.PASSES -> previous
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.lock.unlock()
|
||||||
|
|
||||||
|
if (previous != next) {
|
||||||
|
heightmap[index] = next
|
||||||
|
onHeightmapUpdate(x, z, previous, next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected enum class HeightmapPass {
|
||||||
|
ABOVE,
|
||||||
|
IN,
|
||||||
|
PASSES,
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
@ -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) : Heightmap {
|
||||||
|
|
||||||
|
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, state: BlockState?) = Unit
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val MAX_VALUE = FixedHeightmap(Int.MAX_VALUE)
|
||||||
|
}
|
||||||
|
}
|
@ -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 Heightmap {
|
||||||
|
|
||||||
|
fun recalculate()
|
||||||
|
|
||||||
|
operator fun get(x: Int, z: Int): Int
|
||||||
|
operator fun get(index: Int): Int
|
||||||
|
|
||||||
|
fun onBlockChange(x: Int, y: Int, z: Int, state: BlockState?)
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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) : ChunkHeightmap(chunk) {
|
||||||
|
|
||||||
|
|
||||||
|
override fun recalculate() {
|
||||||
|
super.recalculate()
|
||||||
|
chunk.light.sky.calculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onHeightmapUpdate(x: Int, z: Int, previous: Int, now: Int) {
|
||||||
|
if (previous > now) {
|
||||||
|
// block is lower
|
||||||
|
return chunk.light.sky.floodFill(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.sky.calculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun passes(state: BlockState): HeightmapPass {
|
||||||
|
val light = state.block.getLightProperties(state)
|
||||||
|
if (!light.skylightEnters) return HeightmapPass.ABOVE
|
||||||
|
|
||||||
|
if (light.filtersSkylight) return HeightmapPass.IN
|
||||||
|
if (!light.propagatesLight(Directions.DOWN)) return HeightmapPass.IN
|
||||||
|
|
||||||
|
|
||||||
|
return HeightmapPass.PASSES
|
||||||
|
}
|
||||||
|
}
|
@ -128,9 +128,9 @@ class BorderSectionLight(
|
|||||||
val neighbourLevel = nextLevel - 1
|
val neighbourLevel = nextLevel - 1
|
||||||
|
|
||||||
if (top) {
|
if (top) {
|
||||||
chunk.sections.getLast()?.light?.traceSkylightIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, neighbourLevel, Directions.DOWN, chunk.maxSection * ProtocolDefinition.SECTION_HEIGHT_Y + ProtocolDefinition.SECTION_MAX_Y)
|
chunk.sections.getLast()?.light?.traceSkyLightIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, neighbourLevel, Directions.DOWN, chunk.maxSection * ProtocolDefinition.SECTION_HEIGHT_Y + ProtocolDefinition.SECTION_MAX_Y)
|
||||||
} else {
|
} else {
|
||||||
chunk.sections.getFirst()?.light?.traceSkylightIncrease(x, 0, z, neighbourLevel, Directions.UP, chunk.minSection * ProtocolDefinition.SECTION_HEIGHT_Y)
|
chunk.sections.getFirst()?.light?.traceSkyLightIncrease(x, 0, z, neighbourLevel, Directions.UP, chunk.minSection * ProtocolDefinition.SECTION_HEIGHT_Y)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (z > 0) {
|
if (z > 0) {
|
||||||
|
@ -17,32 +17,30 @@ import de.bixilon.kotlinglm.vec2.Vec2i
|
|||||||
import de.bixilon.kotlinglm.vec3.Vec3i
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
import de.bixilon.minosoft.data.direction.Directions
|
import de.bixilon.minosoft.data.direction.Directions
|
||||||
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
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.ChunkSection
|
||||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||||
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
|
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.light.ChunkLightUtil.hasSkyLight
|
||||||
import de.bixilon.minosoft.data.world.chunk.update.AbstractWorldUpdate
|
import de.bixilon.minosoft.data.world.chunk.update.AbstractWorldUpdate
|
||||||
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkLightUpdate
|
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkLightUpdate
|
||||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight
|
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight
|
||||||
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
|
||||||
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
|
|
||||||
|
|
||||||
class ChunkLight(private val chunk: Chunk) {
|
class ChunkLight(val chunk: Chunk) {
|
||||||
private val connection = chunk.connection
|
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.hasSkyLight()) LightHeightmap(chunk) else FixedHeightmap.MAX_VALUE
|
||||||
|
|
||||||
val bottom = BorderSectionLight(false, chunk)
|
val bottom = BorderSectionLight(false, chunk)
|
||||||
val top = BorderSectionLight(true, chunk)
|
val top = BorderSectionLight(true, chunk)
|
||||||
|
|
||||||
|
val sky = ChunkSkyLight(this)
|
||||||
|
|
||||||
fun onBlockChange(x: Int, y: Int, z: Int, section: ChunkSection, next: BlockState?) {
|
|
||||||
if (!chunk.world.dimension.light) {
|
fun onBlockChange(x: Int, y: Int, z: Int, section: ChunkSection, previous: BlockState?, next: BlockState?) {
|
||||||
return
|
heightmap.onBlockChange(x, y, z, next)
|
||||||
}
|
|
||||||
val heightmapIndex = (z shl 4) or x
|
section.light.onBlockChange(x, y and 0x0F, z, previous, next)
|
||||||
val previous = heightmap[heightmapIndex]
|
|
||||||
recalculateHeightmap(x, y, z, next)
|
|
||||||
onHeightmapUpdate(x, y, z, previous, heightmap[heightmapIndex])
|
|
||||||
|
|
||||||
val neighbours = chunk.neighbours.get() ?: return
|
val neighbours = chunk.neighbours.get() ?: return
|
||||||
|
|
||||||
@ -50,7 +48,7 @@ class ChunkLight(private val chunk: Chunk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun fireLightChange(section: ChunkSection, sectionHeight: Int, neighbours: Array<Chunk>, fireSameChunkEvent: Boolean = true) {
|
fun fireLightChange(section: ChunkSection, sectionHeight: Int, neighbours: Array<Chunk>, fireSameChunkEvent: Boolean = true) {
|
||||||
if (!section.light.update) {
|
if (!section.light.update) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -135,7 +133,7 @@ class ChunkLight(private val chunk: Chunk) {
|
|||||||
}
|
}
|
||||||
section.light.recalculate()
|
section.light.recalculate()
|
||||||
}
|
}
|
||||||
calculateSkylight()
|
sky.calculate()
|
||||||
if (fireEvent) {
|
if (fireEvent) {
|
||||||
fireLightChange(sections, fireSameChunkEvent)
|
fireLightChange(sections, fireSameChunkEvent)
|
||||||
}
|
}
|
||||||
@ -149,7 +147,7 @@ class ChunkLight(private val chunk: Chunk) {
|
|||||||
}
|
}
|
||||||
section.light.calculate()
|
section.light.calculate()
|
||||||
}
|
}
|
||||||
calculateSkylight()
|
sky.calculate()
|
||||||
if (fireEvent) {
|
if (fireEvent) {
|
||||||
fireLightChange(sections, fireSameChunkEvent)
|
fireLightChange(sections, fireSameChunkEvent)
|
||||||
}
|
}
|
||||||
@ -180,245 +178,8 @@ class ChunkLight(private val chunk: Chunk) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun recalculateHeightmap() {
|
@Deprecated("heightmap", ReplaceWith("heightmap[x, z]"))
|
||||||
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() {
|
|
||||||
if (!chunk.world.dimension.canSkylight() || !chunk.neighbours.complete) {
|
|
||||||
// no need to calculate it
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
|
|
||||||
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
|
||||||
startSkylightFloodFill(x, z)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getNeighbourMaxHeight(neighbours: Array<Chunk>, x: Int, z: Int, heightmapIndex: Int = (z shl 4) or x): Int {
|
|
||||||
return maxOf(
|
|
||||||
if (x > 0) {
|
|
||||||
heightmap[heightmapIndex - 1]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.WEST].light.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X]
|
|
||||||
},
|
|
||||||
|
|
||||||
if (x < ProtocolDefinition.SECTION_MAX_X) {
|
|
||||||
heightmap[heightmapIndex + 1]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.EAST].light.heightmap[(z shl 4) or 0]
|
|
||||||
},
|
|
||||||
|
|
||||||
if (z > 0) {
|
|
||||||
heightmap[((z - 1) shl 4) or x]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.NORTH].light.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x]
|
|
||||||
},
|
|
||||||
|
|
||||||
if (z < ProtocolDefinition.SECTION_MAX_Z) {
|
|
||||||
heightmap[((z + 1) shl 4) or x]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.SOUTH].light.heightmap[(0 shl 4) or x]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getNeighbourMinHeight(neighbours: Array<Chunk>, x: Int, z: Int, heightmapIndex: Int = (z shl 4) or x): Int {
|
|
||||||
return minOf(
|
|
||||||
if (x > 0) {
|
|
||||||
heightmap[heightmapIndex - 1]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.WEST].light.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X]
|
|
||||||
},
|
|
||||||
|
|
||||||
if (x < ProtocolDefinition.SECTION_MAX_X) {
|
|
||||||
heightmap[heightmapIndex + 1]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.EAST].light.heightmap[(z shl 4) or 0]
|
|
||||||
},
|
|
||||||
|
|
||||||
if (z > 0) {
|
|
||||||
heightmap[((z - 1) shl 4) or x]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.NORTH].light.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x]
|
|
||||||
},
|
|
||||||
|
|
||||||
if (z < ProtocolDefinition.SECTION_MAX_Z) {
|
|
||||||
heightmap[((z + 1) shl 4) or x]
|
|
||||||
} else {
|
|
||||||
neighbours[ChunkNeighbours.SOUTH].light.heightmap[(0 shl 4) or x]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startSkylightFloodFill(x: Int, z: Int) {
|
|
||||||
val neighbours = chunk.neighbours.get() ?: return
|
|
||||||
val heightmapIndex = (z shl 4) or x
|
|
||||||
val maxHeight = heightmap[heightmapIndex]
|
|
||||||
val maxHeightSection = maxHeight.sectionHeight
|
|
||||||
val skylightStart = getNeighbourMaxHeight(neighbours, x, z, heightmapIndex)
|
|
||||||
|
|
||||||
if (maxHeight == Int.MIN_VALUE && skylightStart == Int.MIN_VALUE) return
|
|
||||||
|
|
||||||
val skylightStartSectionHeight = skylightStart.sectionHeight
|
|
||||||
if (skylightStart.inSectionHeight == 1) {
|
|
||||||
// Create section below max section
|
|
||||||
chunk.getOrPut(skylightStartSectionHeight - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
for (sectionHeight in minOf(skylightStartSectionHeight, chunk.maxSection) downTo maxOf(maxHeightSection + 1, chunk.minSection)) {
|
|
||||||
val section = chunk.sections.get(sectionHeight - chunk.minSection) ?: continue
|
|
||||||
|
|
||||||
// ToDo: Only update if affected by heightmap change
|
|
||||||
section.light.update = true
|
|
||||||
// ToDo: bare tracing
|
|
||||||
val baseY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y
|
|
||||||
for (y in ProtocolDefinition.SECTION_MAX_Y downTo 0) {
|
|
||||||
section.light.traceSkylightIncrease(x, y, z, ProtocolDefinition.MAX_LIGHT_LEVEL_I, null, baseY + y, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (maxHeight.sectionHeight < chunk.minSection) {
|
|
||||||
// bottom section
|
|
||||||
bottom.traceSkyIncrease(x, z, ProtocolDefinition.MAX_LIGHT_LEVEL_I)
|
|
||||||
} else {
|
|
||||||
val maxSection = chunk.getOrPut(maxHeightSection)
|
|
||||||
val baseY = maxHeightSection * ProtocolDefinition.SECTION_HEIGHT_Y
|
|
||||||
if (maxSection != null) {
|
|
||||||
for (y in (if (skylightStartSectionHeight != maxHeightSection) ProtocolDefinition.SECTION_MAX_Y else skylightStart.inSectionHeight) downTo maxHeight.inSectionHeight) {
|
|
||||||
maxSection.light.traceSkylightIncrease(x, y, z, ProtocolDefinition.MAX_LIGHT_LEVEL_I, null, baseY + y, true)
|
|
||||||
}
|
|
||||||
maxSection.light.update = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fun getMaxHeight(x: Int, z: Int): Int {
|
inline fun getMaxHeight(x: Int, z: Int): Int {
|
||||||
return heightmap[(z shl 4) or x]
|
return heightmap[x, z]
|
||||||
}
|
|
||||||
|
|
||||||
fun recalculateSkylight(sectionHeight: Int) {
|
|
||||||
val minY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y
|
|
||||||
|
|
||||||
// TODO: clear neighbours and let them propagate?
|
|
||||||
// TODO: Optimize for specific section height (i.e. not trace everything above)
|
|
||||||
calculateSkylight()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
fun DimensionProperties?.canSkylight(): Boolean {
|
|
||||||
if (this == null) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return !(!this.skyLight || !this.effects.skylight)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* 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.minosoft.data.registries.dimension.DimensionProperties
|
||||||
|
|
||||||
|
object ChunkLightUtil {
|
||||||
|
|
||||||
|
fun DimensionProperties.hasSkyLight(): Boolean {
|
||||||
|
return this.skyLight || this.effects.skyLight
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* 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.minosoft.data.direction.Directions
|
||||||
|
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||||
|
import de.bixilon.minosoft.data.world.chunk.light.ChunkLightUtil.hasSkyLight
|
||||||
|
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
|
||||||
|
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
|
||||||
|
|
||||||
|
class ChunkSkyLight(val light: ChunkLight) {
|
||||||
|
private val chunk = light.chunk
|
||||||
|
|
||||||
|
|
||||||
|
fun calculate() {
|
||||||
|
if (!chunk.world.dimension.hasSkyLight() || !chunk.neighbours.complete) {
|
||||||
|
// no need to calculate it
|
||||||
|
return
|
||||||
|
}
|
||||||
|
floodFill()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun traceSection(sectionHeight: SectionHeight, x: Int, topY: Int, bottomY: Int, z: Int, target: Directions) {
|
||||||
|
val section = chunk.getOrPut(sectionHeight) ?: return
|
||||||
|
val baseY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y
|
||||||
|
|
||||||
|
for (y in topY downTo bottomY) {
|
||||||
|
section.light.traceSkyLightIncrease(x, y, z, NEIGHBOUR_TRACE_LEVEL, target, baseY + y, false)
|
||||||
|
}
|
||||||
|
section.light.update = true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun trace(x: Int, topY: Int, bottomY: Int, z: Int, target: Directions) {
|
||||||
|
if (topY == Int.MIN_VALUE) return // no blocks are set in that column, no need to trace. all levels are MAX
|
||||||
|
if (bottomY > topY) return // started position is higher than at this, no need to trace
|
||||||
|
|
||||||
|
// trace section after section
|
||||||
|
val topSection = topY.sectionHeight
|
||||||
|
val bottomSection = bottomY.sectionHeight
|
||||||
|
|
||||||
|
val sections = topSection - maxOf(chunk.minSection, bottomSection) + 1
|
||||||
|
|
||||||
|
|
||||||
|
// top section
|
||||||
|
if (sections > 1) {
|
||||||
|
traceSection(topSection, x, topY.inSectionHeight, 0, z, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// middle sections
|
||||||
|
for (sectionHeight in (topSection - 1) downTo maxOf(chunk.minSection, bottomSection) + 1) {
|
||||||
|
traceSection(sectionHeight, x, ProtocolDefinition.SECTION_MAX_Y, 0, z, target)
|
||||||
|
}
|
||||||
|
|
||||||
|
// lowest section
|
||||||
|
traceSection(if (bottomY == Int.MIN_VALUE) chunk.minSection else bottomSection, x, if (topSection == bottomSection) topY.inSectionHeight else ProtocolDefinition.SECTION_MAX_Y, if (bottomY == Int.MIN_VALUE) 0 else bottomY.inSectionHeight, z, target)
|
||||||
|
|
||||||
|
if (bottomY == Int.MIN_VALUE) {
|
||||||
|
chunk.light.bottom.traceSkyIncrease(x, z, NEIGHBOUR_TRACE_LEVEL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun traceDown(x: Int, y: Int, z: Int) {
|
||||||
|
val sectionHeight = y.sectionHeight
|
||||||
|
if (sectionHeight == chunk.minSection - 1) {
|
||||||
|
chunk.light.bottom.traceSkyIncrease(x, z, ProtocolDefinition.MAX_LIGHT_LEVEL_I)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val section = chunk[y.sectionHeight] ?: return
|
||||||
|
section.light.traceSkyLightIncrease(x, y.inSectionHeight, z, ProtocolDefinition.MAX_LIGHT_LEVEL_I, null, y, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun floodFill(neighbours: Array<Chunk>, x: Int, z: Int) {
|
||||||
|
val heightmapIndex = (z shl 4) or x
|
||||||
|
val maxHeight = light.heightmap[heightmapIndex]
|
||||||
|
|
||||||
|
|
||||||
|
traceDown(x, maxHeight, z)
|
||||||
|
|
||||||
|
if (x > 0) {
|
||||||
|
trace(x - 1, light.heightmap[heightmapIndex - 1], maxHeight, z, Directions.WEST)
|
||||||
|
} else {
|
||||||
|
val neighbour = neighbours[ChunkNeighbours.WEST].light
|
||||||
|
neighbour.sky.trace(ProtocolDefinition.SECTION_MAX_X, neighbour.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X], maxHeight, z, Directions.WEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x < ProtocolDefinition.SECTION_MAX_X) {
|
||||||
|
trace(x + 1, light.heightmap[heightmapIndex + 1], maxHeight, z, Directions.EAST)
|
||||||
|
} else {
|
||||||
|
val neighbour = neighbours[ChunkNeighbours.EAST].light
|
||||||
|
neighbour.sky.trace(0, neighbour.heightmap[(z shl 4) or 0], maxHeight, z, Directions.EAST)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z > 0) {
|
||||||
|
trace(x, light.heightmap[((z - 1) shl 4) or x], maxHeight, z - 1, Directions.NORTH)
|
||||||
|
} else {
|
||||||
|
val neighbour = neighbours[ChunkNeighbours.NORTH].light
|
||||||
|
neighbour.sky.trace(x, neighbour.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x], maxHeight, ProtocolDefinition.SECTION_MAX_Z, Directions.NORTH)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (z < ProtocolDefinition.SECTION_MAX_Z) {
|
||||||
|
trace(x, light.heightmap[((z + 1) shl 4) or x], maxHeight, z + 1, Directions.SOUTH)
|
||||||
|
} else {
|
||||||
|
val neighbour = neighbours[ChunkNeighbours.SOUTH].light
|
||||||
|
neighbour.sky.trace(x, neighbour.heightmap[(0 shl 4) or x], maxHeight, 0, Directions.SOUTH)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun floodFill(x: Int, z: Int) {
|
||||||
|
val neighbours = chunk.neighbours.get() ?: return
|
||||||
|
floodFill(neighbours, x, z)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun floodFill() {
|
||||||
|
val neighbours = this.chunk.neighbours.get() ?: return
|
||||||
|
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
|
||||||
|
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
||||||
|
floodFill(neighbours, x, z)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun recalculate(sectionHeight: Int) {
|
||||||
|
val minY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y
|
||||||
|
|
||||||
|
// TODO: clear neighbours and let them propagate?
|
||||||
|
// TODO: Optimize for specific section height (i.e. not trace everything above)
|
||||||
|
calculate()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getNeighbourMinHeight(neighbours: Array<Chunk>, x: Int, z: Int, heightmapIndex: Int = (z shl 4) or x): Int {
|
||||||
|
return minOf(
|
||||||
|
if (x > 0) {
|
||||||
|
light.heightmap[heightmapIndex - 1]
|
||||||
|
} else {
|
||||||
|
neighbours[ChunkNeighbours.WEST].light.heightmap[(z shl 4) or ProtocolDefinition.SECTION_MAX_X]
|
||||||
|
},
|
||||||
|
|
||||||
|
if (x < ProtocolDefinition.SECTION_MAX_X) {
|
||||||
|
light.heightmap[heightmapIndex + 1]
|
||||||
|
} else {
|
||||||
|
neighbours[ChunkNeighbours.EAST].light.heightmap[(z shl 4) or 0]
|
||||||
|
},
|
||||||
|
|
||||||
|
if (z > 0) {
|
||||||
|
light.heightmap[((z - 1) shl 4) or x]
|
||||||
|
} else {
|
||||||
|
neighbours[ChunkNeighbours.NORTH].light.heightmap[(ProtocolDefinition.SECTION_MAX_Z shl 4) or x]
|
||||||
|
},
|
||||||
|
|
||||||
|
if (z < ProtocolDefinition.SECTION_MAX_Z) {
|
||||||
|
light.heightmap[((z + 1) shl 4) or x]
|
||||||
|
} else {
|
||||||
|
neighbours[ChunkNeighbours.SOUTH].light.heightmap[(0 shl 4) or x]
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val NEIGHBOUR_TRACE_LEVEL = ProtocolDefinition.MAX_LIGHT_LEVEL_I - 1
|
||||||
|
}
|
||||||
|
}
|
@ -27,12 +27,12 @@ class SectionLight(
|
|||||||
var light: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION), // packed (skyLight: 0xF0, blockLight: 0x0F)
|
var light: ByteArray = ByteArray(ProtocolDefinition.BLOCKS_PER_SECTION), // packed (skyLight: 0xF0, blockLight: 0x0F)
|
||||||
) : AbstractSectionLight() {
|
) : AbstractSectionLight() {
|
||||||
|
|
||||||
fun onBlockChange(x: Int, y: Int, z: Int, previous: BlockState?, now: BlockState?) {
|
fun onBlockChange(x: Int, y: Int, z: Int, previous: BlockState?, state: BlockState?) {
|
||||||
val previousLuminance = previous?.luminance ?: 0
|
val previousLuminance = previous?.luminance ?: 0
|
||||||
val luminance = now?.luminance ?: 0
|
val luminance = state?.luminance ?: 0
|
||||||
|
|
||||||
if (previousLuminance == luminance) {
|
if (previousLuminance == luminance) {
|
||||||
val nowProperties = now?.block?.getLightProperties(now)
|
val nowProperties = state?.block?.getLightProperties(state)
|
||||||
if (previous?.block?.getLightProperties(previous)?.propagatesLight == nowProperties?.propagatesLight) {
|
if (previous?.block?.getLightProperties(previous)?.propagatesLight == nowProperties?.propagatesLight) {
|
||||||
// no change for light data
|
// no change for light data
|
||||||
return
|
return
|
||||||
@ -228,9 +228,12 @@ class SectionLight(
|
|||||||
val blocks = section.blocks
|
val blocks = section.blocks
|
||||||
|
|
||||||
blocks.acquire()
|
blocks.acquire()
|
||||||
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
|
val min = blocks.minPosition
|
||||||
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
val max = blocks.maxPosition
|
||||||
for (y in 0 until ProtocolDefinition.SECTION_HEIGHT_Y) {
|
|
||||||
|
for (x in min.x..max.x) {
|
||||||
|
for (z in min.z..max.z) {
|
||||||
|
for (y in min.y..max.y) {
|
||||||
val index = getIndex(x, y, z)
|
val index = getIndex(x, y, z)
|
||||||
val luminance = blocks[index]?.luminance ?: continue
|
val luminance = blocks[index]?.luminance ?: continue
|
||||||
if (luminance == 0) {
|
if (luminance == 0) {
|
||||||
@ -242,7 +245,7 @@ class SectionLight(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
blocks.release()
|
blocks.release()
|
||||||
section.chunk.light.recalculateSkylight(section.sectionHeight)
|
section.chunk.light.sky.recalculate(section.sectionHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -275,11 +278,11 @@ class SectionLight(
|
|||||||
val totalY = baseY + y
|
val totalY = baseY + y
|
||||||
neighbours[Directions.O_WEST]?.light?.get(ProtocolDefinition.SECTION_MAX_Z, y, z)?.toInt()?.let { light ->
|
neighbours[Directions.O_WEST]?.light?.get(ProtocolDefinition.SECTION_MAX_Z, y, z)?.toInt()?.let { light ->
|
||||||
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(0, y, z, it - 1, Directions.EAST) }
|
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(0, y, z, it - 1, Directions.EAST) }
|
||||||
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkylightIncrease(0, y, z, it - 1, Directions.EAST, totalY) }
|
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(0, y, z, it - 1, Directions.EAST, totalY) }
|
||||||
}
|
}
|
||||||
neighbours[Directions.O_EAST]?.light?.get(0, y, z)?.toInt()?.let { light ->
|
neighbours[Directions.O_EAST]?.light?.get(0, y, z)?.toInt()?.let { light ->
|
||||||
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, it - 1, Directions.WEST) }
|
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, it - 1, Directions.WEST) }
|
||||||
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkylightIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, it - 1, Directions.WEST, totalY) }
|
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, it - 1, Directions.WEST, totalY) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -290,11 +293,11 @@ class SectionLight(
|
|||||||
val totalY = baseY + y
|
val totalY = baseY + y
|
||||||
neighbours[Directions.O_NORTH]?.light?.get(x, y, ProtocolDefinition.SECTION_MAX_Z)?.toInt()?.let { light ->
|
neighbours[Directions.O_NORTH]?.light?.get(x, y, ProtocolDefinition.SECTION_MAX_Z)?.toInt()?.let { light ->
|
||||||
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, y, 0, it - 1, Directions.SOUTH) }
|
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, y, 0, it - 1, Directions.SOUTH) }
|
||||||
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkylightIncrease(x, y, 0, it - 1, Directions.SOUTH, totalY) }
|
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(x, y, 0, it - 1, Directions.SOUTH, totalY) }
|
||||||
}
|
}
|
||||||
neighbours[Directions.O_SOUTH]?.light?.get(x, y, 0)?.toInt()?.let { light ->
|
neighbours[Directions.O_SOUTH]?.light?.get(x, y, 0)?.toInt()?.let { light ->
|
||||||
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, it - 1, Directions.NORTH) }
|
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, it - 1, Directions.NORTH) }
|
||||||
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkylightIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, it - 1, Directions.NORTH, totalY) }
|
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, it - 1, Directions.NORTH, totalY) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -304,20 +307,20 @@ class SectionLight(
|
|||||||
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
|
||||||
neighbours[Directions.O_DOWN]?.light?.get(x, ProtocolDefinition.SECTION_MAX_Y, z)?.toInt()?.let { light ->
|
neighbours[Directions.O_DOWN]?.light?.get(x, ProtocolDefinition.SECTION_MAX_Y, z)?.toInt()?.let { light ->
|
||||||
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, 0, z, it - 1, Directions.UP) }
|
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, 0, z, it - 1, Directions.UP) }
|
||||||
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkylightIncrease(x, 0, z, it - 1, Directions.UP, baseY + 0) } // ToDo: Is that possible?
|
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(x, 0, z, it - 1, Directions.UP, baseY + 0) } // ToDo: Is that possible?
|
||||||
}
|
}
|
||||||
neighbours[Directions.O_UP]?.light?.get(x, 0, z)?.toInt()?.let { light ->
|
neighbours[Directions.O_UP]?.light?.get(x, 0, z)?.toInt()?.let { light ->
|
||||||
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, it - 1, Directions.DOWN) }
|
(light and BLOCK_LIGHT_MASK).let { if (it > 1) traceBlockIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, it - 1, Directions.DOWN) }
|
||||||
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkylightIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, it - 1, Directions.DOWN, baseY + ProtocolDefinition.SECTION_MAX_Y) }
|
(light and SKY_LIGHT_MASK shr 4).let { if (it > 1) traceSkyLightIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, it - 1, Directions.DOWN, baseY + ProtocolDefinition.SECTION_MAX_Y) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal inline fun traceSkylightIncrease(x: Int, y: Int, z: Int, nextLevel: Int, direction: Directions?, totalY: Int) {
|
internal inline fun traceSkyLightIncrease(x: Int, y: Int, z: Int, nextLevel: Int, direction: Directions?, totalY: Int) {
|
||||||
return traceSkylightIncrease(x, y, z, nextLevel, direction, totalY, false)
|
return traceSkyLightIncrease(x, y, z, nextLevel, direction, totalY, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun traceSkylightIncrease(x: Int, y: Int, z: Int, nextLevel: Int, target: Directions?, totalY: Int, force: Boolean) {
|
fun traceSkyLightIncrease(x: Int, y: Int, z: Int, nextLevel: Int, target: Directions?, totalY: Int, force: Boolean) {
|
||||||
val chunk = section.chunk
|
val chunk = section.chunk
|
||||||
val heightmapIndex = (z shl 4) or x
|
val heightmapIndex = (z shl 4) or x
|
||||||
if (!force && totalY >= chunk.light.heightmap[heightmapIndex]) {
|
if (!force && totalY >= chunk.light.heightmap[heightmapIndex]) {
|
||||||
@ -362,46 +365,46 @@ class SectionLight(
|
|||||||
|
|
||||||
if (target != Directions.UP && (target == null || lightProperties.propagatesLight(Directions.DOWN))) {
|
if (target != Directions.UP && (target == null || lightProperties.propagatesLight(Directions.DOWN))) {
|
||||||
if (y > 0) {
|
if (y > 0) {
|
||||||
traceSkylightIncrease(x, y - 1, z, nextNeighbourLevel, Directions.DOWN, totalY - 1)
|
traceSkyLightIncrease(x, y - 1, z, nextNeighbourLevel, Directions.DOWN, totalY - 1)
|
||||||
} else if (section.sectionHeight == chunk.minSection) {
|
} else if (section.sectionHeight == chunk.minSection) {
|
||||||
chunk.light.bottom.traceSkyIncrease(x, z, nextLevel)
|
chunk.light.bottom.traceSkyIncrease(x, z, nextLevel)
|
||||||
} else {
|
} else {
|
||||||
(neighbours[Directions.O_DOWN] ?: chunk.getOrPut(section.sectionHeight - 1, false))?.light?.traceSkylightIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, nextNeighbourLevel, Directions.DOWN, totalY - 1)
|
(neighbours[Directions.O_DOWN] ?: chunk.getOrPut(section.sectionHeight - 1, false))?.light?.traceSkyLightIncrease(x, ProtocolDefinition.SECTION_MAX_Y, z, nextNeighbourLevel, Directions.DOWN, totalY - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target != Directions.DOWN && target != null && (lightProperties.propagatesLight(Directions.UP))) {
|
if (target != Directions.DOWN && (target != null || lightProperties.propagatesLight(Directions.UP))) {
|
||||||
if (y < ProtocolDefinition.SECTION_MAX_Y) {
|
if (y < ProtocolDefinition.SECTION_MAX_Y) {
|
||||||
traceSkylightIncrease(x, y + 1, z, nextNeighbourLevel, Directions.UP, totalY + 1)
|
traceSkyLightIncrease(x, y + 1, z, nextNeighbourLevel, Directions.UP, totalY + 1)
|
||||||
} else if (section.sectionHeight < chunk.maxSection) {
|
} else if (section.sectionHeight < chunk.maxSection) {
|
||||||
(neighbours[Directions.O_UP] ?: chunk.getOrPut(section.sectionHeight + 1, false))?.light?.traceSkylightIncrease(x, 0, z, nextNeighbourLevel, Directions.UP, totalY + 1)
|
(neighbours[Directions.O_UP] ?: chunk.getOrPut(section.sectionHeight + 1, false))?.light?.traceSkyLightIncrease(x, 0, z, nextNeighbourLevel, Directions.UP, totalY + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target != Directions.SOUTH && (target == null || lightProperties.propagatesLight(Directions.NORTH))) {
|
if (target != Directions.SOUTH && (target == null || lightProperties.propagatesLight(Directions.NORTH))) {
|
||||||
if (z > 0) {
|
if (z > 0) {
|
||||||
traceSkylightIncrease(x, y, z - 1, nextNeighbourLevel, Directions.NORTH, totalY)
|
traceSkyLightIncrease(x, y, z - 1, nextNeighbourLevel, Directions.NORTH, totalY)
|
||||||
} else {
|
} else {
|
||||||
neighbours[Directions.O_NORTH, ChunkNeighbours.NORTH, chunkNeighbours]?.light?.traceSkylightIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, nextNeighbourLevel, Directions.NORTH, totalY)
|
neighbours[Directions.O_NORTH, ChunkNeighbours.NORTH, chunkNeighbours]?.light?.traceSkyLightIncrease(x, y, ProtocolDefinition.SECTION_MAX_Z, nextNeighbourLevel, Directions.NORTH, totalY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target != Directions.NORTH && (target == null || lightProperties.propagatesLight(Directions.SOUTH))) {
|
if (target != Directions.NORTH && (target == null || lightProperties.propagatesLight(Directions.SOUTH))) {
|
||||||
if (z < ProtocolDefinition.SECTION_MAX_Z) {
|
if (z < ProtocolDefinition.SECTION_MAX_Z) {
|
||||||
traceSkylightIncrease(x, y, z + 1, nextNeighbourLevel, Directions.SOUTH, totalY)
|
traceSkyLightIncrease(x, y, z + 1, nextNeighbourLevel, Directions.SOUTH, totalY)
|
||||||
} else {
|
} else {
|
||||||
neighbours[Directions.O_SOUTH, ChunkNeighbours.SOUTH, chunkNeighbours]?.light?.traceSkylightIncrease(x, y, 0, nextNeighbourLevel, Directions.SOUTH, totalY)
|
neighbours[Directions.O_SOUTH, ChunkNeighbours.SOUTH, chunkNeighbours]?.light?.traceSkyLightIncrease(x, y, 0, nextNeighbourLevel, Directions.SOUTH, totalY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target != Directions.EAST && (target == null || lightProperties.propagatesLight(Directions.WEST))) {
|
if (target != Directions.EAST && (target == null || lightProperties.propagatesLight(Directions.WEST))) {
|
||||||
if (x > 0) {
|
if (x > 0) {
|
||||||
traceSkylightIncrease(x - 1, y, z, nextNeighbourLevel, Directions.WEST, totalY)
|
traceSkyLightIncrease(x - 1, y, z, nextNeighbourLevel, Directions.WEST, totalY)
|
||||||
} else {
|
} else {
|
||||||
neighbours[Directions.O_WEST, ChunkNeighbours.WEST, chunkNeighbours]?.light?.traceSkylightIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, nextNeighbourLevel, Directions.WEST, totalY)
|
neighbours[Directions.O_WEST, ChunkNeighbours.WEST, chunkNeighbours]?.light?.traceSkyLightIncrease(ProtocolDefinition.SECTION_MAX_X, y, z, nextNeighbourLevel, Directions.WEST, totalY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (target != Directions.WEST && (target == null || lightProperties.propagatesLight(Directions.EAST))) {
|
if (target != Directions.WEST && (target == null || lightProperties.propagatesLight(Directions.EAST))) {
|
||||||
if (x < ProtocolDefinition.SECTION_MAX_X) {
|
if (x < ProtocolDefinition.SECTION_MAX_X) {
|
||||||
traceSkylightIncrease(x + 1, y, z, nextNeighbourLevel, Directions.EAST, totalY)
|
traceSkyLightIncrease(x + 1, y, z, nextNeighbourLevel, Directions.EAST, totalY)
|
||||||
} else {
|
} else {
|
||||||
neighbours[Directions.O_EAST, ChunkNeighbours.EAST, chunkNeighbours]?.light?.traceSkylightIncrease(0, y, z, nextNeighbourLevel, Directions.EAST, totalY)
|
neighbours[Directions.O_EAST, ChunkNeighbours.EAST, chunkNeighbours]?.light?.traceSkyLightIncrease(0, y, z, nextNeighbourLevel, Directions.EAST, totalY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -413,14 +416,15 @@ class SectionLight(
|
|||||||
fun propagateFromNeighbours(x: Int, y: Int, z: Int) {
|
fun propagateFromNeighbours(x: Int, y: Int, z: Int) {
|
||||||
val neighbours = section.neighbours ?: return
|
val neighbours = section.neighbours ?: return
|
||||||
|
|
||||||
var skylight = 0
|
// TODO: those 2 values are boxed in wrapper classes (slow!)
|
||||||
|
var skyLight = 0
|
||||||
var blockLight = 0
|
var blockLight = 0
|
||||||
|
|
||||||
|
|
||||||
fun pushLight(light: Byte) {
|
fun pushLight(light: Byte) {
|
||||||
val nextSkylight = light.toInt() and SKY_LIGHT_MASK shr 4
|
val nextSkyLight = light.toInt() and SKY_LIGHT_MASK shr 4
|
||||||
if (nextSkylight > skylight) {
|
if (nextSkyLight > skyLight) {
|
||||||
skylight = nextSkylight
|
skyLight = nextSkyLight
|
||||||
}
|
}
|
||||||
val nextBlockLight = light.toInt() and BLOCK_LIGHT_MASK
|
val nextBlockLight = light.toInt() and BLOCK_LIGHT_MASK
|
||||||
if (nextBlockLight > blockLight) {
|
if (nextBlockLight > blockLight) {
|
||||||
@ -470,12 +474,12 @@ class SectionLight(
|
|||||||
section.chunk.let {
|
section.chunk.let {
|
||||||
// check if neighbours are above heightmap, if so set light level to max
|
// check if neighbours are above heightmap, if so set light level to max
|
||||||
val chunkNeighbours = it.neighbours.get() ?: return@let
|
val chunkNeighbours = it.neighbours.get() ?: return@let
|
||||||
val minHeight = it.light.getNeighbourMinHeight(chunkNeighbours, x, z)
|
val minHeight = it.light.sky.getNeighbourMinHeight(chunkNeighbours, x, z)
|
||||||
if (totalY > minHeight) {
|
if (totalY > minHeight) {
|
||||||
skylight = ProtocolDefinition.MAX_LIGHT_LEVEL_I
|
skyLight = ProtocolDefinition.MAX_LIGHT_LEVEL_I
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
traceSkylightIncrease(x, y, z, skylight - 1, null, totalY)
|
traceSkyLightIncrease(x, y, z, skyLight - 1, null, totalY)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -14,12 +14,16 @@
|
|||||||
package de.bixilon.minosoft.data.world.chunk.neighbours
|
package de.bixilon.minosoft.data.world.chunk.neighbours
|
||||||
|
|
||||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||||
|
import de.bixilon.kotlinglm.vec3.Vec3i
|
||||||
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
import de.bixilon.kutil.cast.CastUtil.unsafeCast
|
||||||
import de.bixilon.kutil.exception.Broken
|
import de.bixilon.kutil.exception.Broken
|
||||||
|
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
|
||||||
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
import de.bixilon.minosoft.data.world.biome.accessor.NoiseBiomeAccessor
|
||||||
import de.bixilon.minosoft.data.world.chunk.ChunkSection
|
import de.bixilon.minosoft.data.world.chunk.ChunkSection
|
||||||
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
|
||||||
|
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition
|
||||||
import de.bixilon.minosoft.data.world.positions.SectionHeight
|
import de.bixilon.minosoft.data.world.positions.SectionHeight
|
||||||
|
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.inChunkPosition
|
||||||
import de.bixilon.minosoft.util.chunk.ChunkUtil
|
import de.bixilon.minosoft.util.chunk.ChunkUtil
|
||||||
|
|
||||||
class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
|
class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
|
||||||
@ -100,6 +104,68 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
|
|||||||
return neighbours.iterator()
|
return neighbours.iterator()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun update(neighbours: Array<Chunk>, sectionHeight: Int) {
|
||||||
|
for (nextSectionHeight in sectionHeight - 1..sectionHeight + 1) {
|
||||||
|
if (nextSectionHeight < chunk.minSection || nextSectionHeight > chunk.maxSection) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val section = chunk[nextSectionHeight] ?: continue
|
||||||
|
val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, nextSectionHeight)
|
||||||
|
section.neighbours = sectionNeighbours
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun trace(offset: Vec2i): Chunk? {
|
||||||
|
if (offset.x == 0 && offset.y == 0) {
|
||||||
|
return chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
val chunk = when {
|
||||||
|
offset.x > 0 -> {
|
||||||
|
offset.x--
|
||||||
|
this[6]
|
||||||
|
}
|
||||||
|
|
||||||
|
offset.x < 0 -> {
|
||||||
|
offset.x++
|
||||||
|
this[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
offset.y > 0 -> {
|
||||||
|
offset.y--
|
||||||
|
this[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
offset.y < 0 -> {
|
||||||
|
offset.y++
|
||||||
|
this[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> Broken("Can not get chunk from offset: $offset")
|
||||||
|
}
|
||||||
|
return chunk?.neighbours?.trace(offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun traceBlock(offset: Vec3i, origin: Vec3i, blockPosition: Vec3i = origin + offset): BlockState? {
|
||||||
|
val chunkDelta = (origin - blockPosition).chunkPosition
|
||||||
|
|
||||||
|
return traceBlock(blockPosition.x and 0x0F, blockPosition.y, blockPosition.z and 0x0F, chunkDelta)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun traceBlock(offset: Vec3i): BlockState? {
|
||||||
|
return traceBlock(offset.inChunkPosition, offset.chunkPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun traceBlock(inChunkPosition: Vec3i, chunkOffset: Vec2i): BlockState? {
|
||||||
|
return trace(chunkOffset)?.get(inChunkPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun traceBlock(x: Int, y: Int, z: Int, chunkOffset: Vec2i): BlockState? {
|
||||||
|
return trace(chunkOffset)?.get(x, y, z)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val COUNT = 8
|
const val COUNT = 8
|
||||||
const val NORTH = 3
|
const val NORTH = 3
|
||||||
|
@ -42,7 +42,7 @@ open class SectionDataProvider<T>(
|
|||||||
if (data != null && calculateInitial) {
|
if (data != null && calculateInitial) {
|
||||||
recalculate()
|
recalculate()
|
||||||
} else {
|
} else {
|
||||||
minPosition = Vec3i.EMPTY
|
minPosition = Vec3i(ProtocolDefinition.CHUNK_SECTION_SIZE)
|
||||||
maxPosition = Vec3i.EMPTY
|
maxPosition = Vec3i.EMPTY
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,7 @@ class WorldIterator(
|
|||||||
} else if (chunk.chunkPosition != chunkPosition) {
|
} else if (chunk.chunkPosition != chunkPosition) {
|
||||||
offset.x = chunkPosition.x - chunk.chunkPosition.x
|
offset.x = chunkPosition.x - chunk.chunkPosition.x
|
||||||
offset.y = chunkPosition.y - chunk.chunkPosition.y
|
offset.y = chunkPosition.y - chunk.chunkPosition.y
|
||||||
chunk = chunk.traceChunk(offset) ?: continue
|
chunk = chunk.neighbours.trace(offset) ?: continue
|
||||||
}
|
}
|
||||||
if (this.chunk !== chunk) {
|
if (this.chunk !== chunk) {
|
||||||
this.chunk = chunk
|
this.chunk = chunk
|
||||||
|
@ -76,7 +76,7 @@ class RenderLight(val context: RenderContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
val RECALCULATE = minosoft("recalculate")
|
val RECALCULATE = minosoft("recalculate_light")
|
||||||
val FULLBRIGHT = minosoft("fullbright")
|
val FULLBRIGHT = minosoft("fullbright")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ abstract class RenderParticle(connection: PlayConnection, position: Vec3d, veloc
|
|||||||
inChunk.y = position.y
|
inChunk.y = position.y
|
||||||
inChunk.z = position.z and 0x0F
|
inChunk.z = position.z and 0x0F
|
||||||
|
|
||||||
val light = chunk.traceChunk(offset)?.light?.get(inChunk) ?: SectionLight.SKY_LIGHT_MASK
|
val light = chunk.neighbours.trace(offset)?.light?.get(inChunk) ?: SectionLight.SKY_LIGHT_MASK
|
||||||
if (light and SectionLight.BLOCK_LIGHT_MASK > maxBlockLight) {
|
if (light and SectionLight.BLOCK_LIGHT_MASK > maxBlockLight) {
|
||||||
maxBlockLight = light and SectionLight.BLOCK_LIGHT_MASK
|
maxBlockLight = light and SectionLight.BLOCK_LIGHT_MASK
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,7 @@ class SkyboxRenderer(
|
|||||||
val x = offset.x + xOffset
|
val x = offset.x + xOffset
|
||||||
val y = offset.y + yOffset
|
val y = offset.y + yOffset
|
||||||
val z = offset.z + zOffset
|
val z = offset.z + zOffset
|
||||||
val neighbour = chunk.traceChunk(Vec2i(x shr 4, z shr 4)) ?: continue
|
val neighbour = chunk.neighbours.trace(Vec2i(x shr 4, z shr 4)) ?: continue
|
||||||
val biome = neighbour.getBiome(x and 0x0F, y, z and 0x0F) ?: continue
|
val biome = neighbour.getBiome(x and 0x0F, y, z and 0x0F) ?: continue
|
||||||
|
|
||||||
count++
|
count++
|
||||||
|
@ -63,7 +63,7 @@ class EntityPositionInfo(
|
|||||||
val chunks = physics.entity.connection.world.chunks
|
val chunks = physics.entity.connection.world.chunks
|
||||||
val revision = chunks.revision
|
val revision = chunks.revision
|
||||||
|
|
||||||
var chunk = if (previous.revision == revision) previous.chunk?.traceChunk(chunkPosition - previous.chunkPosition) else null
|
var chunk = if (previous.revision == revision) previous.chunk?.neighbours?.trace(chunkPosition - previous.chunkPosition) else null
|
||||||
|
|
||||||
if (chunk == null) {
|
if (chunk == null) {
|
||||||
chunk = chunks[chunkPosition]
|
chunk = chunks[chunkPosition]
|
||||||
|
@ -72,7 +72,7 @@ class ExplosionS2CP(buffer: PlayInByteBuffer) : PlayS2CPacket {
|
|||||||
} else if (chunk.chunkPosition != chunkPosition) {
|
} else if (chunk.chunkPosition != chunkPosition) {
|
||||||
chunkOffset.x = chunkPosition.x - chunk.chunkPosition.x
|
chunkOffset.x = chunkPosition.x - chunk.chunkPosition.x
|
||||||
chunkOffset.y = chunkPosition.y - chunk.chunkPosition.y
|
chunkOffset.y = chunkPosition.y - chunk.chunkPosition.y
|
||||||
chunk = chunk.traceChunk(chunkOffset) ?: continue
|
chunk = chunk.neighbours.trace(chunkOffset) ?: continue
|
||||||
}
|
}
|
||||||
|
|
||||||
val inChunkPosition = total.inChunkPosition
|
val inChunkPosition = total.inChunkPosition
|
||||||
|
Loading…
x
Reference in New Issue
Block a user