make it build

This commit is contained in:
Moritz Zwerger 2025-03-03 19:04:52 +01:00
parent dd9aee9c21
commit 6abc316d0d
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
27 changed files with 360 additions and 446 deletions

View File

@ -35,7 +35,6 @@ import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
import de.bixilon.minosoft.modding.event.master.EventMaster
import de.bixilon.minosoft.protocol.network.session.Session
import de.bixilon.minosoft.protocol.network.session.play.PlaySession
@ -80,7 +79,7 @@ object LightTestingUtil {
}
fun createChunkWithNeighbours(): Chunk {
val chunk = createEmptyChunk(Vec2i.EMPTY)
val chunk = createEmptyChunk(ChunkPosition.EMPTY)
var index = 0
for (x in -1..1) {
for (z in -1..1) {

View File

@ -18,6 +18,7 @@ import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.biome.source.SpatialBiomeArray
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
class VoronoiBiomeAccessor(
@ -37,9 +38,9 @@ class VoronoiBiomeAccessor(
val biomeY = unpackY(offset)
val biomeZ = unpackZ(offset)
val biomeChunk = chunk.neighbours.trace(biomeX shr 4, biomeZ shr 4)
val biomeChunk = chunk.neighbours.traceChunk(ChunkPosition(biomeX shr 4, biomeZ shr 4))
return biomeChunk?.biomeSource?.get(biomeX and 0x0F, biomeY, biomeZ and 0x0F)
return biomeChunk?.biomeSource?.get(InChunkPosition(biomeX and 0x0F, biomeY, biomeZ and 0x0F))
}
fun getBiomeOffset(seed: Long, x: Int, y: Int, z: Int): Int {

View File

@ -21,9 +21,9 @@ import de.bixilon.minosoft.data.world.container.SectionDataProvider
import de.bixilon.minosoft.data.world.container.biome.BiomeSectionDataProvider
import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.network.session.play.PlaySession
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import java.util.*
/**
@ -70,59 +70,16 @@ class ChunkSection(
blockEntities.clear()
}
fun traceBlock(offset: BlockPosition): BlockState?
fun traceBlock(origin: InSectionPosition, offset: BlockPosition) = traceBlock(offset - origin)
fun traceBlock(origin: InSectionPosition, direction: Directions) = traceBlock((BlockPosition(origin) + direction))
@Deprecated("")
fun traceBlock(x: Int, y: Int, z: Int, direction: Directions) = when (direction) {
Directions.DOWN -> {
if (y == 0) {
neighbours?.get(Directions.O_DOWN)?.blocks?.let { it[x, ProtocolDefinition.SECTION_MAX_Y, z] }
} else {
blocks[x, y - 1, z]
}
}
Directions.UP -> {
if (y == ProtocolDefinition.SECTION_MAX_Y) {
neighbours?.get(Directions.O_UP)?.blocks?.let { it[x, 0, z] }
} else {
blocks[x, y + 1, z]
}
}
Directions.NORTH -> {
if (z == 0) {
neighbours?.get(Directions.O_NORTH)?.blocks?.let { it[x, y, ProtocolDefinition.SECTION_MAX_Z] }
} else {
blocks[x, y, z - 1]
}
}
Directions.SOUTH -> {
if (z == ProtocolDefinition.SECTION_MAX_Z) {
neighbours?.get(Directions.O_SOUTH)?.blocks?.let { it[x, y, 0] }
} else {
blocks[x, y, z + 1]
}
}
Directions.WEST -> {
if (x == 0) {
neighbours?.get(Directions.O_WEST)?.blocks?.let { it[ProtocolDefinition.SECTION_MAX_X, y, z] }
} else {
blocks[x - 1, y, z]
}
}
Directions.EAST -> {
if (x == ProtocolDefinition.SECTION_MAX_X) {
neighbours?.get(Directions.O_EAST)?.blocks?.let { it[0, y, z] }
} else {
blocks[x + 1, y, z]
}
fun traceBlock(offset: BlockPosition): BlockState? {
val chunkOffset = offset.chunkPosition
val height = offset.sectionHeight
if (chunkOffset == ChunkPosition.EMPTY && height == 0) {
return blocks[offset.inSectionPosition]
}
val chunk = this.chunk.neighbours.traceChunk(chunkOffset) ?: return null
return chunk[offset.inChunkPosition]
}
fun traceBlock(origin: InSectionPosition, offset: BlockPosition) = traceBlock(offset - origin)
fun traceBlock(origin: InSectionPosition, direction: Directions) = traceBlock((BlockPosition(origin.x, origin.y, origin.z) + direction))
}

View File

@ -167,9 +167,9 @@ class Chunk(
var section = sections[index] // get another time, it might have changed already
if (section == null) {
section = ChunkSection(sectionHeight, chunk = this)
val neighbours = this.neighbours.get()
if (neighbours != null) {
this.neighbours.completeSection(neighbours, section, sectionHeight, world.biomes.noise)
val neighbours = this.neighbours
if (neighbours.complete) {
this.neighbours.completeSection(section, sectionHeight, world.biomes.noise)
}
sections[index] = section
@ -181,10 +181,10 @@ class Chunk(
sections[index + 1]?.neighbours?.set(Directions.O_DOWN, section)
}
if (neighbours != null) {
for (neighbour in neighbours) {
val neighbourNeighbours = neighbour.neighbours.get() ?: continue
neighbour.neighbours.update(neighbourNeighbours, sectionHeight)
if (neighbours.complete) {
for (neighbour in neighbours.neighbours.array) {
if (neighbour == null || !neighbour.neighbours.complete) continue
neighbour.neighbours.update(sectionHeight)
}
}

View File

@ -25,6 +25,7 @@ abstract class ChunkHeightmap(protected val chunk: Chunk) : Heightmap {
override fun get(index: Int) = heightmap[index]
override fun get(x: Int, z: Int) = this[(z shl 4) or x]
override fun get(position: InChunkPosition) = get(position.x, position.z)
protected abstract fun passes(state: BlockState): HeightmapPass
@ -47,7 +48,7 @@ abstract class ChunkHeightmap(protected val chunk: Chunk) : Heightmap {
val sections = chunk.sections
var y = Int.MIN_VALUE
val index = (z shl 4) or x
val index = position.xz
sectionLoop@ for (sectionIndex in (position.y.sectionHeight - chunk.minSection) downTo 0) {
if (sectionIndex >= sections.size) {
@ -88,7 +89,7 @@ abstract class ChunkHeightmap(protected val chunk: Chunk) : Heightmap {
override fun onBlockChange(position: InChunkPosition, state: BlockState?) {
chunk.lock.lock()
val index = (z shl 4) or x
val index = position.xz
val previous = heightmap[index]

View File

@ -17,7 +17,6 @@ import de.bixilon.kutil.array.ArrayUtil.getFirst
import de.bixilon.kutil.array.ArrayUtil.getLast
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
@ -71,23 +70,23 @@ class BorderSectionLight(
if (z > 0) {
traceBlockIncrease(x, z - 1, neighbourLuminance)
} else {
chunk.neighbours[ChunkNeighbours.NORTH]?.getBorderLight()?.traceBlockIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance)
chunk.neighbours[Directions.NORTH]?.getBorderLight()?.traceBlockIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLuminance)
}
if (z < ProtocolDefinition.SECTION_MAX_Z) {
traceBlockIncrease(x, z + 1, neighbourLuminance)
} else {
chunk.neighbours[ChunkNeighbours.SOUTH]?.getBorderLight()?.traceBlockIncrease(x, 0, neighbourLuminance)
chunk.neighbours[Directions.SOUTH]?.getBorderLight()?.traceBlockIncrease(x, 0, neighbourLuminance)
}
if (x > 0) {
traceBlockIncrease(x - 1, z, neighbourLuminance)
} else {
chunk.neighbours[ChunkNeighbours.WEST]?.getBorderLight()?.traceBlockIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLuminance)
chunk.neighbours[Directions.WEST]?.getBorderLight()?.traceBlockIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLuminance)
}
if (x < ProtocolDefinition.SECTION_MAX_X) {
traceBlockIncrease(x + 1, z, neighbourLuminance)
} else {
chunk.neighbours[ChunkNeighbours.EAST]?.getBorderLight()?.traceBlockIncrease(0, z, neighbourLuminance)
chunk.neighbours[Directions.EAST]?.getBorderLight()?.traceBlockIncrease(0, z, neighbourLuminance)
}
}
@ -124,49 +123,49 @@ class BorderSectionLight(
if (z > 0) {
traceSkyIncrease(x, z - 1, neighbourLevel)
} else {
chunk.neighbours.get(ChunkNeighbours.NORTH)?.getBorderLight()?.traceSkyIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLevel)
chunk.neighbours[Directions.NORTH]?.getBorderLight()?.traceSkyIncrease(x, ProtocolDefinition.SECTION_MAX_Z, neighbourLevel)
}
if (z < ProtocolDefinition.SECTION_MAX_Y) {
traceSkyIncrease(x, z + 1, neighbourLevel)
} else {
chunk.neighbours.get(ChunkNeighbours.SOUTH)?.getBorderLight()?.traceSkyIncrease(x, 0, neighbourLevel)
chunk.neighbours[Directions.SOUTH]?.getBorderLight()?.traceSkyIncrease(x, 0, neighbourLevel)
}
if (x > 0) {
traceSkyIncrease(x - 1, z, neighbourLevel)
} else {
chunk.neighbours.get(ChunkNeighbours.WEST)?.getBorderLight()?.traceSkyIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLevel)
chunk.neighbours[Directions.WEST]?.getBorderLight()?.traceSkyIncrease(ProtocolDefinition.SECTION_MAX_X, z, neighbourLevel)
}
if (x < ProtocolDefinition.SECTION_MAX_X) {
traceSkyIncrease(x + 1, z, neighbourLevel)
} else {
chunk.neighbours.get(ChunkNeighbours.EAST)?.getBorderLight()?.traceSkyIncrease(0, z, neighbourLevel)
chunk.neighbours[Directions.EAST]?.getBorderLight()?.traceSkyIncrease(0, z, neighbourLevel)
}
}
internal fun decreaseCheckLevel(x: Int, z: Int, light: Int, reset: Boolean) {
decreaseCheckX(z, light, reset)
val neighbours = chunk.neighbours.get() ?: return
val neighbours = chunk.neighbours
if (x - light < 0) {
neighbours[ChunkNeighbours.WEST].getBorderLight().decreaseCheckX(z, light - x, reset)
neighbours[Directions.WEST]?.getBorderLight()?.decreaseCheckX(z, light - x, reset)
}
if (x + light > ProtocolDefinition.SECTION_MAX_X) {
neighbours[ChunkNeighbours.EAST].getBorderLight().decreaseCheckX(z, light - (ProtocolDefinition.SECTION_MAX_X - x), reset)
neighbours[Directions.EAST]?.getBorderLight()?.decreaseCheckX(z, light - (ProtocolDefinition.SECTION_MAX_X - x), reset)
}
}
private fun decreaseCheckX(z: Int, light: Int, reset: Boolean) {
val neighbours = chunk.neighbours.get() ?: return
val neighbours = chunk.neighbours
if (reset) reset()
if (z - light < 0) {
val neighbour = neighbours[ChunkNeighbours.NORTH].getBorderLight()
if (reset) neighbour.reset()
val neighbour = neighbours[Directions.NORTH]?.getBorderLight()
if (reset) neighbour?.reset()
}
if (z + light > ProtocolDefinition.SECTION_MAX_Z) {
val neighbour = neighbours[ChunkNeighbours.SOUTH].getBorderLight()
if (reset) neighbour.reset()
val neighbour = neighbours[Directions.SOUTH]?.getBorderLight()
if (reset) neighbour?.reset()
}
}

View File

@ -75,7 +75,7 @@ class ChunkLight(val chunk: Chunk) {
for (chunkX in -1..1) {
for (chunkZ in -1..1) {
val offset = ChunkPosition(chunkX, chunkZ)
if (offset.xz == 0) continue
if (offset == ChunkPosition.EMPTY) continue
val nextPosition = chunkPosition + offset
val chunk = neighbours[neighbourIndex++]

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -14,9 +14,9 @@
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.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.InSectionPosition
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
@ -39,7 +39,7 @@ class ChunkSkyLight(val light: ChunkLight) {
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)
section.light.traceSkyLightIncrease(InSectionPosition(x, y, z), NEIGHBOUR_TRACE_LEVEL, target, baseY + y)
}
section.light.update = true
}
@ -81,10 +81,10 @@ class ChunkSkyLight(val light: ChunkLight) {
}
val section = chunk[y.sectionHeight] ?: return
section.light.traceSkyLightDown(x, y.inSectionHeight, z, Directions.DOWN, y)
section.light.traceSkyLightDown(InSectionPosition(x, y.inSectionHeight, z), Directions.DOWN, y)
}
private fun floodFill(neighbours: Array<Chunk>, x: Int, z: Int) {
private fun floodFill(neighbours: ChunkNeighbourArray, x: Int, z: Int) {
val heightmapIndex = (z shl 4) or x
val maxHeight = light.heightmap[heightmapIndex]
@ -94,39 +94,40 @@ class ChunkSkyLight(val light: ChunkLight) {
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)
val neighbour = neighbours[Directions.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)
val neighbour = neighbours[Directions.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)
val neighbour = neighbours[Directions.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)
val neighbour = neighbours[Directions.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)
if (!this.chunk.neighbours.complete) return
floodFill(chunk.neighbours.neighbours, x, z)
}
private fun floodFill() {
val neighbours = this.chunk.neighbours.get() ?: return
val neighbours = this.chunk.neighbours.neighbours
if (!this.chunk.neighbours.complete) return
for (z in 0 until ProtocolDefinition.SECTION_WIDTH_Z) {
for (x in 0 until ProtocolDefinition.SECTION_WIDTH_X) {
floodFill(neighbours, x, z)
@ -142,30 +143,30 @@ class ChunkSkyLight(val light: ChunkLight) {
calculate()
}
fun getNeighbourMinHeight(neighbours: Array<Chunk>, x: Int, z: Int, heightmapIndex: Int = (z shl 4) or x): Int {
fun getNeighbourMinHeight(neighbours: ChunkNeighbourArray, 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]
neighbours[Directions.WEST]?.light?.heightmap?.get((z shl 4) or ProtocolDefinition.SECTION_MAX_X) ?: 0
},
if (x < ProtocolDefinition.SECTION_MAX_X) {
light.heightmap[heightmapIndex + 1]
} else {
neighbours[ChunkNeighbours.EAST].light.heightmap[(z shl 4) or 0]
neighbours[Directions.EAST]?.light?.heightmap?.get((z shl 4) or 0) ?: 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]
neighbours[Directions.NORTH]?.light?.heightmap?.get((ProtocolDefinition.SECTION_MAX_Z shl 4) or x) ?: 0
},
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]
neighbours[Directions.SOUTH]?.light?.heightmap?.get((0 shl 4) or x) ?: 0
}
)
}

View File

@ -17,9 +17,8 @@ import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.blocks.light.TransparentProperty
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.ChunkSkyLight.Companion.NEIGHBOUR_TRACE_LEVEL
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
@ -144,7 +143,7 @@ class SectionLight(
update = true
}
val chunk = section.chunk
val chunkNeighbours = chunk.neighbours.get() ?: return
val chunkNeighbours = chunk.neighbours.neighbours
val neighbours = section.neighbours ?: return
if (nextLuminance == 1) {
@ -183,28 +182,28 @@ class SectionLight(
if (position.z > 0) {
traceBlockIncrease(position.minusZ(), neighbourLuminance, Directions.NORTH)
} else {
neighbours[Directions.O_NORTH, ChunkNeighbours.NORTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), neighbourLuminance, Directions.NORTH)
neighbours[Directions.O_NORTH, Directions.NORTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), neighbourLuminance, Directions.NORTH)
}
}
if (target == null || (target != Directions.NORTH && lightProperties.propagatesLight(Directions.SOUTH))) {
if (position.z < ProtocolDefinition.SECTION_MAX_Y) {
traceBlockIncrease(position.plusZ(), neighbourLuminance, Directions.SOUTH)
} else {
neighbours[Directions.O_SOUTH, ChunkNeighbours.SOUTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = 0), neighbourLuminance, Directions.SOUTH)
neighbours[Directions.O_SOUTH, Directions.SOUTH, chunkNeighbours]?.light?.traceBlockIncrease(position.with(z = 0), neighbourLuminance, Directions.SOUTH)
}
}
if (target == null || (target != Directions.EAST && lightProperties.propagatesLight(Directions.WEST))) {
if (position.x > 0) {
traceBlockIncrease(position.minusX(), neighbourLuminance, Directions.WEST)
} else {
neighbours[Directions.O_WEST, ChunkNeighbours.WEST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), neighbourLuminance, Directions.WEST)
neighbours[Directions.O_WEST, Directions.WEST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), neighbourLuminance, Directions.WEST)
}
}
if (target == null || (target != Directions.WEST && lightProperties.propagatesLight(Directions.EAST))) {
if (position.x < ProtocolDefinition.SECTION_MAX_X) {
traceBlockIncrease(position.plusX(), neighbourLuminance, Directions.EAST)
} else {
neighbours[Directions.O_EAST, ChunkNeighbours.EAST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = 0), neighbourLuminance, Directions.EAST)
neighbours[Directions.O_EAST, Directions.EAST, chunkNeighbours]?.light?.traceBlockIncrease(position.with(x = 0), neighbourLuminance, Directions.EAST)
}
}
}
@ -317,7 +316,7 @@ class SectionLight(
// this light level will be 15, don't care
return
}
val chunkNeighbours = chunk.neighbours.get() ?: return
val chunkNeighbours = chunk.neighbours.neighbours
val currentLight = this[position].toInt()
if (((currentLight and SKY_LIGHT_MASK) shr 4) >= nextLevel) {
return
@ -372,28 +371,28 @@ class SectionLight(
if (position.z > 0) {
traceSkyLightIncrease(position.minusZ(), nextNeighbourLevel, Directions.NORTH, totalY)
} else {
neighbours[Directions.O_NORTH, ChunkNeighbours.NORTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), nextNeighbourLevel, Directions.NORTH, totalY)
neighbours[Directions.O_NORTH, Directions.NORTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = ProtocolDefinition.SECTION_MAX_Z), nextNeighbourLevel, Directions.NORTH, totalY)
}
}
if (target != Directions.NORTH && (target == null || lightProperties.propagatesLight(Directions.SOUTH))) {
if (position.z < ProtocolDefinition.SECTION_MAX_Z) {
traceSkyLightIncrease(position.plusZ(), nextNeighbourLevel, Directions.SOUTH, totalY)
} else {
neighbours[Directions.O_SOUTH, ChunkNeighbours.SOUTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = 0), nextNeighbourLevel, Directions.SOUTH, totalY)
neighbours[Directions.O_SOUTH, Directions.SOUTH, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(z = 0), nextNeighbourLevel, Directions.SOUTH, totalY)
}
}
if (target != Directions.EAST && (target == null || lightProperties.propagatesLight(Directions.WEST))) {
if (position.x > 0) {
traceSkyLightIncrease(position.minusX(), nextNeighbourLevel, Directions.WEST, totalY)
} else {
neighbours[Directions.O_WEST, ChunkNeighbours.WEST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), nextNeighbourLevel, Directions.WEST, totalY)
neighbours[Directions.O_WEST, Directions.WEST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = ProtocolDefinition.SECTION_MAX_X), nextNeighbourLevel, Directions.WEST, totalY)
}
}
if (target != Directions.WEST && (target == null || lightProperties.propagatesLight(Directions.EAST))) {
if (position.x < ProtocolDefinition.SECTION_MAX_X) {
traceSkyLightIncrease(position.plusX(), nextNeighbourLevel, Directions.EAST, totalY)
} else {
neighbours[Directions.O_EAST, ChunkNeighbours.EAST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = 0), nextNeighbourLevel, Directions.EAST, totalY)
neighbours[Directions.O_EAST, Directions.EAST, chunkNeighbours]?.light?.traceSkyLightIncrease(position.with(x = 0), nextNeighbourLevel, Directions.EAST, totalY)
}
}
}
@ -428,8 +427,8 @@ class SectionLight(
}
}
private inline operator fun Array<ChunkSection?>.get(direction: Int, neighbour: Int, neighbours: Array<Chunk>): ChunkSection? {
return this[direction] ?: neighbours[neighbour].getOrPut(section.sectionHeight, false)
private inline operator fun Array<ChunkSection?>.get(direction: Int, neighbour: Directions, neighbours: ChunkNeighbourArray): ChunkSection? {
return this[direction] ?: neighbours[neighbour]?.getOrPut(section.sectionHeight, false)
}
fun propagateFromNeighbours(position: InSectionPosition) {
@ -492,7 +491,8 @@ class SectionLight(
val totalY = section.sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y + position.y
section.chunk.let {
// check if neighbours are above heightmap, if so set light level to max
val chunkNeighbours = it.neighbours.get() ?: return@let
val chunkNeighbours = it.neighbours.neighbours
if (!it.neighbours.complete) return@let
val minHeight = it.light.sky.getNeighbourMinHeight(chunkNeighbours, position.x, position.z)
if (totalY > minHeight) {
skyLight = ProtocolDefinition.MAX_LIGHT_LEVEL_I

View File

@ -21,7 +21,7 @@ import de.bixilon.minosoft.data.world.biome.source.DummyBiomeSource
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.chunk.ChunkPrototype
import de.bixilon.minosoft.data.world.chunk.manager.size.WorldSizeManager
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.chunk.update.AbstractWorldUpdate
import de.bixilon.minosoft.data.world.chunk.update.WorldUpdateEvent
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkCreateUpdate
@ -57,9 +57,9 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity:
}
val updates = hashSetOf<AbstractWorldUpdate>(ChunkUnloadUpdate(position, chunk))
for ((index, neighbour) in chunk.neighbours.neighbours.withIndex()) {
for ((index, neighbour) in chunk.neighbours.neighbours.array.withIndex()) {
if (neighbour == null) continue
val offset = ChunkNeighbours.OFFSETS[index]
val offset = ChunkPosition(ChunkNeighbourArray.OFFSETS[index])
val neighbourPosition = position + offset
neighbour.neighbours.remove(-offset)
updates += NeighbourChangeUpdate(neighbourPosition, neighbour)
@ -137,10 +137,10 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity:
val updates = HashSet<AbstractWorldUpdate>(9, 1.0f)
updates += ChunkCreateUpdate(chunk.position, chunk)
for (index in 0 until ChunkNeighbours.COUNT) {
val offset = ChunkNeighbours.OFFSETS[index]
for (index in 0 until ChunkNeighbourArray.COUNT) {
val offset = ChunkPosition(ChunkNeighbourArray.OFFSETS[index])
val neighbour = this.chunks.unsafe[chunk.position + offset] ?: continue
chunk.neighbours[index] = neighbour
chunk.neighbours[offset] = neighbour
neighbour.neighbours[-offset] = chunk
}
@ -148,7 +148,7 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity:
// TODO: fire event
for (neighbour in chunk.neighbours) {
for (neighbour in chunk.neighbours.neighbours.array) {
if (neighbour == null) continue
updates += NeighbourChangeUpdate(neighbour.position, neighbour)
}

View File

@ -0,0 +1,78 @@
/*
* Minosoft
* Copyright (C) 2020-2025 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.neighbours
import de.bixilon.minosoft.data.Axes
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.ChunkPosition
@JvmInline
value class ChunkNeighbourArray(val array: Array<Chunk?>) {
constructor() : this(arrayOfNulls(OFFSETS.size))
private operator fun get(index: Int) = array[index]
private operator fun set(index: Int, chunk: Chunk?) {
array[index] = chunk
}
operator fun get(direction: Directions): Chunk? {
if (direction.axis == Axes.Y) throw IllegalArgumentException("Chunk neighbours are 2D!")
return this[BY_DIRECTION[direction.ordinal - Directions.SIDE_OFFSET]]
}
operator fun get(offset: ChunkPosition) = this[offset.neighbourIndex]
operator fun set(offset: ChunkPosition, chunk: Chunk?) {
this[offset.neighbourIndex] = chunk
}
companion object {
const val COUNT = 8
private val BY_DIRECTION = intArrayOf(3, 4, 1, 6)
/**
* 0 | 3 | 5
* 1 | - | 6
* 2 | 4 | 7
*/
private val _0 = ChunkPosition(-1, -1)
private val _1 = ChunkPosition(-1, +0)
private val _2 = ChunkPosition(-1, +1)
private val _3 = ChunkPosition(+0, -1)
private val _4 = ChunkPosition(+0, +1)
private val _5 = ChunkPosition(+1, -1)
private val _6 = ChunkPosition(+1, +0)
private val _7 = ChunkPosition(+1, +1)
val OFFSETS = longArrayOf(_0.raw, _1.raw, _2.raw, _3.raw, _4.raw, _5.raw, _6.raw, _7.raw)
private val ChunkPosition.neighbourIndex: Int
get() = when (this) { // TODO: use some hash table
_0 -> 0
_1 -> 1
_2 -> 2
_3 -> 3
_4 -> 4
_5 -> 5
_6 -> 6
_7 -> 7
else -> throw IllegalArgumentException("Invalid chunk offset: $this")
}
}
}

View File

@ -25,84 +25,69 @@ import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight
import de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk.ChunkUtil
import kotlin.math.abs
class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
val neighbours: Array<Chunk?> = arrayOfNulls(COUNT)
class ChunkNeighbours(val chunk: Chunk) {
val neighbours = ChunkNeighbourArray()
private var count = 0
val complete: Boolean get() = count == COUNT
val complete: Boolean get() = count == ChunkNeighbourArray.COUNT
fun get(): Array<Chunk>? {
if (count == COUNT) { // TODO: Race condition!
if (complete) { // TODO: Race condition!
return neighbours.unsafeCast()
}
return null
}
@Deprecated("index")
operator fun set(index: Int, chunk: Chunk) {
operator fun set(offset: ChunkPosition, chunk: Chunk) {
this.chunk.lock.lock()
val current = neighbours[index]
neighbours[index] = chunk
val current = neighbours[offset]
neighbours[offset] = chunk
if (current == null) {
count++
if (count == COUNT) {
complete(get()!!)
if (complete) {
complete()
}
}
this.chunk.lock.unlock()
}
operator fun set(offset: ChunkPosition, chunk: Chunk) {
set(getIndex(offset), chunk)
}
@Deprecated("index")
fun remove(index: Int) {
fun remove(offset: ChunkPosition) {
chunk.lock.lock()
val current = neighbours[index]
neighbours[index] = null
val current = neighbours[offset]
if (current != null) {
neighbours[offset] = null
count--
}
chunk.lock.unlock()
}
fun remove(offset: ChunkPosition) {
remove(getIndex(offset))
}
fun completeSection(neighbours: Array<Chunk>, section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) {
fun completeSection(section: ChunkSection, sectionHeight: SectionHeight, noise: NoiseBiomeAccessor?) {
section.neighbours = ChunkUtil.getDirectNeighbours(neighbours, chunk, sectionHeight)
}
private fun complete(neighbours: Array<Chunk>) {
private fun complete() {
val noise = chunk.world.biomes.noise
for ((index, section) in chunk.sections.withIndex()) {
if (section == null) continue
val sectionHeight = index + chunk.minSection
completeSection(neighbours, section, sectionHeight, noise)
completeSection(section, sectionHeight, noise)
}
chunk.light.recalculate(false)
chunk.light.propagateFromNeighbours(fireEvent = false, fireSameChunkEvent = false)
}
@Deprecated("index")
operator fun get(index: Int): Chunk? {
return neighbours[index]
operator fun get(direction: Directions): Chunk? {
return neighbours[direction]
}
operator fun get(offset: ChunkPosition): Chunk {
if (offset.xz == 0) return chunk
return this[getIndex(offset)] // TODO: trace
operator fun get(offset: ChunkPosition): Chunk? {
if (offset == ChunkPosition.EMPTY) return chunk
return traceChunk(offset)
}
override fun iterator(): Iterator<Chunk?> {
return neighbours.iterator()
}
fun update(neighbours: Array<Chunk>, sectionHeight: Int) {
fun update(sectionHeight: Int) {
for (nextSectionHeight in sectionHeight - 1..sectionHeight + 1) {
if (nextSectionHeight < chunk.minSection || nextSectionHeight > chunk.maxSection) {
continue
@ -114,29 +99,18 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
}
}
fun traceChunk(offset: ChunkPosition): Chunk = when {
offsetX == 0 -> when {
offsetZ == 0 -> chunk
offsetZ < 0 -> neighbours[3]?.neighbours?.trace(offsetX, offsetZ + 1)
offsetZ > 0 -> neighbours[4]?.neighbours?.trace(offsetX, offsetZ - 1)
fun traceChunk(offset: ChunkPosition): Chunk? {
if (offset == ChunkPosition.EMPTY) return chunk
if (abs(offset.x) <= 1 && abs(offset.z) <= 1) return this.neighbours[offset]
// TODO: optimize diagonal trace
return when {
offset.z < 0 -> neighbours[Directions.NORTH]?.neighbours?.traceChunk(offset.plusZ())
offset.z > 0 -> neighbours[Directions.SOUTH]?.neighbours?.traceChunk(offset.minusZ())
offset.x < 0 -> neighbours[Directions.WEST]?.neighbours?.traceChunk(offset.plusX())
offset.x > 0 -> neighbours[Directions.WEST]?.neighbours?.traceChunk(offset.minusX())
else -> Broken()
}
offsetX < 0 -> when {
offsetZ == 0 -> neighbours[1]?.neighbours?.trace(offsetX + 1, offsetZ)
offsetZ < 0 -> neighbours[0]?.neighbours?.trace(offsetX + 1, offsetZ + 1)
offsetZ > 0 -> neighbours[2]?.neighbours?.trace(offsetX + 1, offsetZ - 1)
else -> Broken()
}
offsetX > 0 -> when {
offsetZ == 0 -> neighbours[6]?.neighbours?.trace(offsetX - 1, offsetZ)
offsetZ < 0 -> neighbours[5]?.neighbours?.trace(offsetX - 1, offsetZ + 1)
offsetZ > 0 -> neighbours[7]?.neighbours?.trace(offsetX - 1, offsetZ - 1)
else -> Broken()
}
else -> Broken()
}
fun traceBlock(position: BlockPosition): BlockState? {
@ -148,30 +122,4 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
fun traceBlock(origin: InChunkPosition, offset: BlockPosition) = traceBlock(offset - origin)
fun traceBlock(origin: InChunkPosition, direction: Directions) = traceBlock((BlockPosition(origin) + direction))
companion object {
const val COUNT = 8
const val NORTH = 3
const val SOUTH = 4
const val WEST = 1
const val EAST = 6
/**
* 0 | 3 | 5
* 1 | - | 6
* 2 | 4 | 7
*/
val OFFSETS = arrayOf(
ChunkPosition(-1, -1), // 0
ChunkPosition(-1, +0), // 1
ChunkPosition(-1, +1), // 2
ChunkPosition(+0, -1), // 3
ChunkPosition(+0, +1), // 4
ChunkPosition(+1, -1), // 5
ChunkPosition(+1, +0), // 6
ChunkPosition(+1, +1), // 7
)
}
}

View File

@ -29,7 +29,7 @@ open class SectionDataProvider<T>(
private set
val isEmpty: Boolean
get() = count == 0
var minPosition = InSectionPosition(ProtocolDefinition.SECTION_WIDTH_X, ProtocolDefinition.SECTION_HEIGHT_Y, ProtocolDefinition.SECTION_WIDTH_Z)
var minPosition = InSectionPosition(ProtocolDefinition.SECTION_MAX_X, ProtocolDefinition.SECTION_MAX_Y, ProtocolDefinition.SECTION_MAX_Z)
private set
var maxPosition = InSectionPosition(0, 0, 0)
private set

View File

@ -27,67 +27,61 @@ value class BlockPosition(
inline val index: Long,
) : TextFormattable {
init {
TODO()
}
constructor() : this(0, 0, 0)
constructor(x: Int, y: Int, z: Int) : this((y and 0xFFF shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) {
assertPosition(x, 0, ProtocolDefinition.SECTION_MAX_X)
assertPosition(y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y)
assertPosition(z, 0, ProtocolDefinition.SECTION_MAX_Z)
constructor(x: Int, y: Int, z: Int) : this(((y and MASK_Y).toLong() shl SHIFT_Y) or ((z and MASK_Z).toLong() shl SHIFT_Z) or ((x and MASK_X).toLong() shl SHIFT_X)) {
assertPosition(x, -MAX_X, MAX_X)
assertPosition(y, -MAX_Y, MAX_Y)
assertPosition(z, -MAX_Z, MAX_Z)
}
constructor(position: InChunkPosition) : this(position.x, position.y, position.z)
constructor(position: InSectionPosition) : this(position.x, position.y, position.z)
inline val x: Int get() = (index and MASK_X) shr SHIFT_X
inline val y: Int get() = (index and MASK_Y) shr SHIFT_Y
inline val z: Int get() = (index and MASK_Z) shr SHIFT_Z
inline val xz: Int get() = (index and MASK_Z or MASK_X)
inline val x: Int get() = (index shr SHIFT_X).toInt() and MASK_X
inline val y: Int get() = (index shr SHIFT_Y).toInt() and MASK_Y
inline val z: Int get() = (index shr SHIFT_Z).toInt() and MASK_Z
inline fun plusX(): BlockPosition {
assertPosition(this.x < ProtocolDefinition.SECTION_MAX_X)
assertPosition(this.x < MAX_X)
return BlockPosition(index + X * 1)
}
inline fun plusX(x: Int): BlockPosition {
assertPosition(this.x + x, 0, ProtocolDefinition.SECTION_MAX_X)
assertPosition(this.x + x, -MAX_X, MAX_X)
return BlockPosition(index + X * x)
}
inline fun minusX(): BlockPosition {
assertPosition(this.x > 0)
assertPosition(this.x > -MAX_X)
return BlockPosition(index - X * 1)
}
inline fun plusY(): BlockPosition {
assertPosition(this.y < ProtocolDefinition.CHUNK_MAX_Y)
assertPosition(this.y < MAX_Y)
return BlockPosition(index + Y * 1)
}
inline fun plusY(y: Int): BlockPosition {
assertPosition(this.y + y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y)
assertPosition(this.y + y, -MAX_Y, MAX_Y)
return BlockPosition(index + Y * y)
}
inline fun minusY(): BlockPosition {
assertPosition(this.y > ProtocolDefinition.CHUNK_MIN_Y)
assertPosition(this.y > -MAX_Y)
return BlockPosition(index - Y * 1)
}
inline fun plusZ(): BlockPosition {
assertPosition(this.z < ProtocolDefinition.SECTION_MAX_Z)
assertPosition(this.z < MAX_Y)
return BlockPosition(index + Z * 1)
}
inline fun plusZ(z: Int): BlockPosition {
assertPosition(this.z + z, 0, ProtocolDefinition.SECTION_MAX_Z)
assertPosition(this.z + z, -MAX_Z, MAX_Z)
return BlockPosition(index + Z * z)
}
inline fun minusZ(): BlockPosition {
assertPosition(this.z > 0)
assertPosition(this.z > -MAX_Z)
return BlockPosition(index - Z * 1)
}
@ -123,19 +117,26 @@ value class BlockPosition(
override fun toString() = "b($x $y $z)"
companion object {
const val MASK_X = 0x00F
const val BITS_X = 26
const val MASK_X = (1 shl BITS_X) - 1
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val BITS_Z = 26
const val MASK_Z = (1 shl BITS_Z) - 1
const val SHIFT_Z = BITS_X
const val MASK_Y = 0xFFF00
const val SHIFT_Y = 8
const val BITS_Y = 12
const val MASK_Y = (1 shl BITS_Y) - 1
const val SHIFT_Y = BITS_X + BITS_Z
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z
const val Y = 1 shl SHIFT_Y
const val MAX_X = 0
const val MAX_Y = 0
const val MAX_Z = 0
val EMPTY = BlockPosition(0, 0, 0)

View File

@ -32,7 +32,7 @@ object BlockPositionUtil {
inline fun assertPosition(value: Int, min: Int, max: Int) {
if (!DebugOptions.VERIFY_COORDINATES) return
if (value < min) throw AssertionError("coordinate out of range: $value < $min")
if (value > min) throw AssertionError("coordinate out of range: $value > $max")
if (value > max) throw AssertionError("coordinate out of range: $value > $max")
}

View File

@ -13,9 +13,9 @@
package de.bixilon.minosoft.data.world.positions
import de.bixilon.kutil.exception.Broken
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.text.formatting.TextFormattable
import de.bixilon.minosoft.data.world.positions.BlockPositionUtil.assertPosition
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.format
@ -23,53 +23,45 @@ import de.bixilon.minosoft.util.KUtil.format
value class ChunkPosition(
inline val raw: Long,
) : TextFormattable {
init {
TODO()
}
constructor() : this(0, 0)
constructor(x: Int, z: Int) : this((z shl SHIFT_Z) or (x shl SHIFT_X)) {
assert(x >= 0)
assert(x <= ProtocolDefinition.SECTION_MAX_X)
assert(z >= 0)
assert(z <= ProtocolDefinition.SECTION_MAX_Z)
constructor(x: Int, z: Int) : this((z.toLong() shl SHIFT_Z) or (x.toLong() shl SHIFT_X)) {
assertPosition(x, -MAX_X, MAX_X)
assertPosition(z, -MAX_Z, MAX_Z)
}
inline val x: Int get() = (raw and MASK_X) shr SHIFT_X
inline val z: Int get() = (raw and MASK_Z) shr SHIFT_Z
inline val xz: Int get() = (raw and MASK_Z or MASK_X)
inline val x: Int get() = (raw ushr SHIFT_X).toInt() and MASK_X
inline val z: Int get() = (raw ushr SHIFT_Z).toInt() and MASK_Z
inline fun plusX(): ChunkPosition {
assert(this.x < ProtocolDefinition.SECTION_MAX_X)
assertPosition(this.x < MAX_X)
return ChunkPosition(raw + X * 1)
}
inline fun plusX(x: Int): ChunkPosition {
assert(this.x + x < ProtocolDefinition.SECTION_MAX_X)
assert(this.x + x > 0)
assertPosition(this.x + x, -MAX_X, MAX_X)
return ChunkPosition(raw + X * x)
}
inline fun minusX(): ChunkPosition {
assert(this.x > 0)
assert(this.x > -MAX_X)
return ChunkPosition(raw - X * 1)
}
inline fun plusZ(): ChunkPosition {
assert(this.z < ProtocolDefinition.SECTION_MAX_Z)
assert(this.z < MAX_Z)
return ChunkPosition(raw + Z * 1)
}
inline fun plusZ(z: Int): ChunkPosition {
assert(this.z + z < ProtocolDefinition.SECTION_MAX_Z)
assert(this.z + z > 0)
assertPosition(this.z + z, -MAX_Z, MAX_Z)
return ChunkPosition(raw + Z * z)
}
inline fun minusZ(): ChunkPosition {
assert(this.z > 0)
assert(this.z > -MAX_Z)
return ChunkPosition(raw - Z * 1)
}
@ -98,28 +90,21 @@ value class ChunkPosition(
override fun toString() = "c($x $z)"
val index: Int
get() = when {
x == -1 && z == -1 -> 0
x == -1 && z == 0 -> 1
x == -1 && z == 1 -> 2
x == 0 && z == -1 -> 3
x == 0 && z == 1 -> 4
x == 1 && z == -1 -> 5
x == 1 && z == 0 -> 6
x == 1 && z == 1 -> 7
else -> Broken("Can not get neighbour chunk from offset $this")
}
companion object {
const val MASK_X = 0x00F
const val BITS_X = 32
const val MASK_X = (1 shl BITS_X) - 1
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val BITS_Z = 32
const val MASK_Z = (1 shl BITS_Z) - 1
const val SHIFT_Z = BITS_X
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z
const val X = 1L shl SHIFT_X
const val Z = 1L shl SHIFT_Z
const val MAX_X = Int.MAX_VALUE
const val MAX_Z = Int.MAX_VALUE
val EMPTY = ChunkPosition(0, 0)

View File

@ -28,16 +28,16 @@ value class InChunkPosition(
constructor() : this(0, 0, 0)
constructor(x: Int, y: Int, z: Int) : this((y and 0xFFF shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) {
constructor(x: Int, y: Int, z: Int) : this(((y and 0xFFF) shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) {
assertPosition(x, 0, ProtocolDefinition.SECTION_MAX_X)
assertPosition(y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y)
assertPosition(z, 0, ProtocolDefinition.SECTION_MAX_Z)
}
inline val x: Int get() = (index and MASK_X) shr SHIFT_X
inline val y: Int get() = (index and MASK_Y) shr SHIFT_Y
inline val z: Int get() = (index and MASK_Z) shr SHIFT_Z
inline val xz: Int get() = (index and MASK_Z or MASK_X)
inline val x: Int get() = (index shr SHIFT_X) and MASK_X
inline val y: Int get() = (index and (MASK_Y shl SHIFT_Y)) shl 20 shr 20
inline val z: Int get() = (index shr SHIFT_Z) and MASK_Z
inline val xz: Int get() = index and ((MASK_X shl SHIFT_X) or (MASK_Z shl SHIFT_Z))
inline fun plusX(): InChunkPosition {
@ -99,14 +99,17 @@ value class InChunkPosition(
inline val sectionHeight get() = y.sectionHeight
companion object {
const val MASK_X = 0x00F
const val BITS_X = 4
const val MASK_X = (1 shl BITS_X) - 1
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val BITS_Z = 4
const val MASK_Z = (1 shl BITS_Z) - 1
const val SHIFT_Z = BITS_X
const val MASK_Y = 0xFFF00
const val SHIFT_Y = 8
const val BITS_Y = 12
const val MASK_Y = (1 shl BITS_Y) - 1
const val SHIFT_Y = BITS_X + BITS_Z
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z

View File

@ -32,11 +32,11 @@ value class InSectionPosition(
assertPosition(z, 0, ProtocolDefinition.SECTION_MAX_Z)
}
inline val x: Int get() = (index and MASK_X) shr SHIFT_X
inline val y: Int get() = (index and MASK_Y) shr SHIFT_Y
inline val z: Int get() = (index and MASK_Z) shr SHIFT_Z
inline val x: Int get() = (index shr SHIFT_X) and MASK_X
inline val y: Int get() = (index shr SHIFT_Y) and MASK_Y
inline val z: Int get() = (index shr SHIFT_Z) and MASK_Z
inline val xz: Int get() = index and (MASK_X or MASK_Z)
inline val xz: Int get() = index and ((MASK_X shl SHIFT_X) or (MASK_Z shl SHIFT_Z))
inline fun plusX(): InSectionPosition {
@ -95,14 +95,17 @@ value class InSectionPosition(
override fun toString() = "s($x $y $z)"
companion object {
const val MASK_X = 0x00F
const val BITS_X = 4
const val MASK_X = (1 shl BITS_X) - 1
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val BITS_Z = 4
const val MASK_Z = (1 shl BITS_Z) - 1
const val SHIFT_Z = BITS_X
const val MASK_Y = 0xF00
const val SHIFT_Y = 8
const val BITS_Y = 4
const val MASK_Y = (1 shl BITS_Y) - 1
const val SHIFT_Y = BITS_X + BITS_Z
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z

View File

@ -156,7 +156,7 @@ class Frustum(
return containsRegion(min.x, min.y, min.z, max.x, max.y, max.z)
}
fun containsChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = CHUNK_NIN_POSITION, maxPosition: InSectionPosition = ProtocolDefinition.CHUNK_SECTION_SIZE): Boolean {
fun containsChunkSection(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = SECTION_MIN_POSITION, maxPosition: InSectionPosition = SECTION_MAX_POSITION): Boolean {
val offset = camera.offset.offset
val baseX = ((chunkPosition.x shl 4) - offset.x).toFloat()
val baseY = ((sectionHeight shl 4) - offset.y).toFloat()
@ -215,7 +215,8 @@ class Frustum(
}
}
private companion object {
val CHUNK_NIN_POSITION = InSectionPosition(0, 0, 0)
companion object {
val SECTION_MIN_POSITION = InSectionPosition(0, 0, 0)
val SECTION_MAX_POSITION = InSectionPosition(ProtocolDefinition.SECTION_MAX_X, ProtocolDefinition.SECTION_MAX_Y, ProtocolDefinition.SECTION_MAX_Z)
}
}

View File

@ -21,7 +21,6 @@ import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.shapes.aabb.AABB
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.update.WorldUpdateEvent
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkCreateUpdate
import de.bixilon.minosoft.data.world.chunk.update.chunk.ChunkUnloadUpdate
@ -31,10 +30,10 @@ import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.camera.Camera
import de.bixilon.minosoft.gui.rendering.camera.frustum.Frustum
import de.bixilon.minosoft.gui.rendering.events.VisibilityGraphChangeEvent
import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen
import de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk.ChunkUtil.isInViewDistance
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import it.unimi.dsi.fastutil.ints.IntOpenHashSet
/**
@ -149,7 +148,7 @@ class WorldVisibilityGraph(
return frustum.containsAABB(aabb)
}
fun isSectionVisible(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = DEFAULT_MIN_POSITION, maxPosition: InSectionPosition = ProtocolDefinition.CHUNK_SECTION_SIZE, checkChunk: Boolean = true): Boolean {
fun isSectionVisible(chunkPosition: ChunkPosition, sectionHeight: Int, minPosition: InSectionPosition = Frustum.SECTION_MIN_POSITION, maxPosition: InSectionPosition = Frustum.SECTION_MIN_POSITION, checkChunk: Boolean = true): Boolean {
if (checkChunk && !isChunkVisible(chunkPosition)) {
return false
}
@ -249,7 +248,7 @@ class WorldVisibilityGraph(
if (directionX <= 0 && (section?.occlusion?.isOccluded(inverted, Directions.WEST) != true) && chunkPosition.x > chunkMin.x) {
val next = chunkPosition.minusX()
val nextChunk = chunk.neighbours[ChunkNeighbours.WEST]
val nextChunk = chunk.neighbours[Directions.WEST]
if (nextChunk != null) {
val nextVisibilities = getVisibility(next) ?: return
if (!nextVisibilities[visibilitySectionIndex]) {
@ -261,7 +260,7 @@ class WorldVisibilityGraph(
if (directionX >= 0 && (section?.occlusion?.isOccluded(inverted, Directions.EAST) != true) && chunkPosition.x < chunkMax.x) {
val next = chunkPosition.plusX()
val nextChunk = chunk.neighbours[ChunkNeighbours.EAST]
val nextChunk = chunk.neighbours[Directions.EAST]
if (nextChunk != null) {
val nextVisibilities = getVisibility(next) ?: return
if (!nextVisibilities[visibilitySectionIndex]) {
@ -286,7 +285,7 @@ class WorldVisibilityGraph(
if (directionZ <= 0 && (section?.occlusion?.isOccluded(inverted, Directions.NORTH) != true) && chunkPosition.z > chunkMin.z) {
val next = chunkPosition.minusZ()
val nextChunk = chunk.neighbours[ChunkNeighbours.NORTH]
val nextChunk = chunk.neighbours[Directions.NORTH]
if (nextChunk != null) {
val nextVisibilities = getVisibility(next) ?: return
if (!nextVisibilities[visibilitySectionIndex]) {
@ -298,7 +297,7 @@ class WorldVisibilityGraph(
if (directionZ >= 0 && (section?.occlusion?.isOccluded(inverted, Directions.SOUTH) != true) && chunkPosition.z < chunkMax.z) {
val next = chunkPosition.plusZ()
val nextChunk = chunk.neighbours[ChunkNeighbours.SOUTH]
val nextChunk = chunk.neighbours[Directions.SOUTH]
if (nextChunk != null) {
val nextVisibilities = getVisibility(next) ?: return
if (!nextVisibilities[visibilitySectionIndex]) {
@ -371,8 +370,4 @@ class WorldVisibilityGraph(
calculateGraph()
}
}
companion object {
private val DEFAULT_MIN_POSITION = InSectionPosition.EMPTY
}
}

View File

@ -32,15 +32,15 @@ class ChunkMesher(
renderer.unload(item)
return null
}
val neighbours = item.chunk.neighbours.get()
if (neighbours == null) {
val neighbours = item.chunk.neighbours
if (!neighbours.complete) {
renderer.unload(item)
return null
}
val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours, item.chunk, item.section.sectionHeight)
val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours.neighbours, item.chunk, item.section.sectionHeight)
val mesh = ChunkMeshes(renderer.context, item.chunkPosition, item.sectionHeight, item.section.smallMesh)
try {
solid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, neighbours, sectionNeighbours, mesh)
solid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, neighbours.neighbours, sectionNeighbours, mesh)
if (item.section.blocks.hasFluid) {
fluid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, mesh)

View File

@ -254,7 +254,7 @@ class FluidSectionMesher(
for (side in 0 until 4) {
val now = BlockPosition(x = position.x - (side and 0x01), y = position.y, z = position.z - (side shr 1 and 0x01))
val offset = position.chunkPosition - providedChunk.position
val chunk = neighbours[offset]
val chunk = neighbours[offset] ?: continue
val inChunk = now.inChunkPosition

View File

@ -30,7 +30,7 @@ import de.bixilon.minosoft.data.registries.blocks.types.properties.offset.Offset
import de.bixilon.minosoft.data.world.chunk.ChunkSection
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.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
@ -59,7 +59,7 @@ class SolidSectionMesher(
profile.light::ambientOcclusion.observe(this, true) { this.ambientOcclusion = it }
}
fun mesh(chunkPosition: ChunkPosition, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: Array<Chunk>, neighbours: Array<ChunkSection?>, mesh: ChunkMeshes) {
fun mesh(chunkPosition: ChunkPosition, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: ChunkNeighbourArray, neighbours: Array<ChunkSection?>, mesh: ChunkMeshes) {
val random = if (profile.antiMoirePattern) Random(0L) else null
@ -188,13 +188,13 @@ class SolidSectionMesher(
}
}
private inline fun setZ(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
private inline fun setZ(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, neighbourChunks: ChunkNeighbourArray, section: ChunkSection, chunk: Chunk) {
if (position.z == 0) {
setNeighbour(neighbourBlocks, position.with(z = ProtocolDefinition.SECTION_MAX_Z), light, neighbours[O_NORTH], neighbourChunks[ChunkNeighbours.NORTH], O_NORTH)
setNeighbour(neighbourBlocks, position.with(z = ProtocolDefinition.SECTION_MAX_Z), light, neighbours[O_NORTH], neighbourChunks[Directions.NORTH]!!, O_NORTH) // TODO: bad assert
setNeighbour(neighbourBlocks, position.plusZ(), light, section, chunk, O_SOUTH)
} else if (position.z == ProtocolDefinition.SECTION_MAX_Z) {
setNeighbour(neighbourBlocks, position.minusZ(), light, section, chunk, O_NORTH)
setNeighbour(neighbourBlocks, position.with(z = 0), light, neighbours[O_SOUTH], neighbourChunks[ChunkNeighbours.SOUTH], O_SOUTH)
setNeighbour(neighbourBlocks, position.with(z = 0), light, neighbours[O_SOUTH], neighbourChunks[Directions.SOUTH]!!, O_SOUTH)// TODO: bad assert
} else {
setNeighbour(neighbourBlocks, position.minusZ(), light, section, chunk, O_NORTH)
setNeighbour(neighbourBlocks, position.plusZ(), light, section, chunk, O_SOUTH)
@ -202,12 +202,12 @@ class SolidSectionMesher(
}
private inline fun setX(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
private inline fun setX(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, neighbourChunks: ChunkNeighbourArray, section: ChunkSection, chunk: Chunk) {
if (position.x == 0) {
setNeighbour(neighbourBlocks, position.with(x = ProtocolDefinition.SECTION_MAX_X), light, neighbours[O_WEST], neighbourChunks[ChunkNeighbours.WEST], O_WEST)
setNeighbour(neighbourBlocks, position.with(x = ProtocolDefinition.SECTION_MAX_X), light, neighbours[O_WEST], neighbourChunks[Directions.WEST]!!, O_WEST) // TODO: bad assert
setNeighbour(neighbourBlocks, position.plusX(), light, section, chunk, O_EAST)
} else if (position.x == ProtocolDefinition.SECTION_MAX_X) {
setNeighbour(neighbourBlocks, position.with(x = 0), light, neighbours[O_EAST], neighbourChunks[ChunkNeighbours.EAST], O_EAST)
setNeighbour(neighbourBlocks, position.with(x = 0), light, neighbours[O_EAST], neighbourChunks[Directions.EAST]!!, O_EAST) // TODO: bad assert
setNeighbour(neighbourBlocks, position.minusX(), light, section, chunk, O_WEST)
} else {
setNeighbour(neighbourBlocks, position.minusX(), light, section, chunk, O_WEST)

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.protocol.packets.s2c.play.block.chunk
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.minosoft.config.StaticConfiguration
import de.bixilon.minosoft.data.direction.Directions
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
@ -24,7 +25,7 @@ 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.ChunkPrototype
import de.bixilon.minosoft.data.world.chunk.light.LightArray
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbours
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.container.palette.PalettedContainerReader
import de.bixilon.minosoft.data.world.container.palette.palettes.BiomePaletteFactory
import de.bixilon.minosoft.data.world.container.palette.palettes.BlockStatePaletteFactory
@ -226,80 +227,18 @@ object ChunkUtil {
return XZBiomeArray(biomes)
}
val Array<Chunk?>.fullyLoaded: Boolean
get() {
for (neighbour in this) {
if (neighbour == null) return false
if (neighbour.neighbours.complete) {
continue
}
return false
}
return true
}
fun getChunkNeighbourPositions(chunkPosition: ChunkPosition): Array<ChunkPosition> {
return arrayOf(
chunkPosition + ChunkNeighbours.OFFSETS[0],
chunkPosition + ChunkNeighbours.OFFSETS[1],
chunkPosition + ChunkNeighbours.OFFSETS[2],
chunkPosition + ChunkNeighbours.OFFSETS[3],
chunkPosition + ChunkNeighbours.OFFSETS[4],
chunkPosition + ChunkNeighbours.OFFSETS[5],
chunkPosition + ChunkNeighbours.OFFSETS[6],
chunkPosition + ChunkNeighbours.OFFSETS[7],
)
}
/**
* @param neighbourChunks: **Fully loaded** direct neighbour chunks
*/
fun getDirectNeighbours(neighbourChunks: Array<Chunk>, chunk: Chunk, sectionHeight: SectionHeight): Array<ChunkSection?> {
fun getDirectNeighbours(neighbourChunks: ChunkNeighbourArray, chunk: Chunk, sectionHeight: SectionHeight): Array<ChunkSection?> {
return arrayOf(
chunk[sectionHeight - 1],
chunk[sectionHeight + 1],
neighbourChunks[3][sectionHeight],
neighbourChunks[4][sectionHeight],
neighbourChunks[1][sectionHeight],
neighbourChunks[6][sectionHeight],
)
}
/**
* @param neighbourChunks: **Fully loaded** direct neighbour chunks
*/
fun getAllNeighbours(neighbourChunks: Array<Chunk>, chunk: Chunk, sectionHeight: SectionHeight): Array<ChunkSection?> {
return arrayOf(
neighbourChunks[0][sectionHeight - 1], // 0, (-1 | -1)
neighbourChunks[1][sectionHeight - 1], // 1, (-1 | +0)
neighbourChunks[2][sectionHeight - 1], // 2, (-1 | +1)
neighbourChunks[3][sectionHeight - 1], // 3, (+0 | -1)
chunk[sectionHeight - 1], // 4, (+0 | +0)
neighbourChunks[4][sectionHeight - 1], // 5, (+0 | +1)
neighbourChunks[5][sectionHeight - 1], // 6, (-1 | -1)
neighbourChunks[6][sectionHeight - 1], // 7, (-1 | -1)
neighbourChunks[7][sectionHeight - 1], // 8, (-1 | -1)
neighbourChunks[0][sectionHeight + 0], // 9, (-1 | -1)
neighbourChunks[1][sectionHeight + 0], // 10, (-1 | +0)
neighbourChunks[2][sectionHeight + 0], // 11, (-1 | +1)
neighbourChunks[3][sectionHeight + 0], // 12, (+0 | -1)
chunk[sectionHeight + 0], // 13, (+0 | +0)
neighbourChunks[4][sectionHeight + 0], // 14, (+0 | +1)
neighbourChunks[5][sectionHeight + 0], // 15, (+1 | -1)
neighbourChunks[6][sectionHeight + 0], // 16, (+1 | +0)
neighbourChunks[7][sectionHeight + 0], // 17, (+1 | +1)
neighbourChunks[0][sectionHeight + 1], // 18, (-1 | -1)
neighbourChunks[1][sectionHeight + 1], // 19, (-1 | +0)
neighbourChunks[2][sectionHeight + 1], // 20, (-1 | +1)
neighbourChunks[3][sectionHeight + 1], // 21, (+0 | -1)
chunk[sectionHeight + 1], // 22, (+0 | +0)
neighbourChunks[4][sectionHeight + 1], // 23, (+0 | +1)
neighbourChunks[5][sectionHeight + 1], // 24, (+1 | -1)
neighbourChunks[6][sectionHeight + 1], // 25, (+1 | +0)
neighbourChunks[7][sectionHeight + 1], // 26, (+1 | +1)
neighbourChunks[Directions.NORTH]!![sectionHeight],
neighbourChunks[Directions.SOUTH]!![sectionHeight],
neighbourChunks[Directions.WEST]!![sectionHeight],
neighbourChunks[Directions.EAST]!![sectionHeight],
)
}

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.protocol.protocol;
import de.bixilon.minosoft.data.text.formatting.color.ChatColors;
import de.bixilon.minosoft.data.text.formatting.color.RGBColor;
import de.bixilon.minosoft.data.world.positions.InSectionPosition;
import java.util.regex.Pattern;
@ -48,8 +47,6 @@ public final class ProtocolDefinition {
public static final int SECTION_HEIGHT_Y = SECTION_LENGTH;
public static final int SECTION_MAX_Y = SECTION_HEIGHT_Y - 1;
public static final int BLOCKS_PER_SECTION = SECTION_WIDTH_X * SECTION_HEIGHT_Y * SECTION_WIDTH_X;
@Deprecated
public static final InSectionPosition CHUNK_SECTION_SIZE = new InSectionPosition(SECTION_WIDTH_X, SECTION_HEIGHT_Y, SECTION_WIDTH_Z);
public static final int CHUNK_MIN_Y = -2048;
public static final int CHUNK_MIN_SECTION = CHUNK_MIN_Y / SECTION_HEIGHT_Y;

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
* Copyright (C) 2020-2025 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.
*
@ -13,7 +13,7 @@
package de.bixilon.minosoft.data.registries.shapes.aabb
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.world.positions.BlockPosition
import org.junit.jupiter.api.Assertions.*
import org.junit.jupiter.api.Test
@ -33,7 +33,7 @@ class AABBIteratorTest {
val positions = aabb.positions()
assertEquals(1, positions.size)
assertTrue(positions.hasNext())
assertEquals(Vec3i(0, 0, 0), positions.next())
assertEquals(BlockPosition(0, 0, 0), positions.next())
assertFalse(positions.hasNext())
}
@ -43,7 +43,7 @@ class AABBIteratorTest {
val positions = aabb.positions()
assertEquals(24, positions.size)
val set: MutableSet<Vec3i> = mutableSetOf()
val set: MutableSet<BlockPosition> = mutableSetOf()
for (position in positions) {
set += position
@ -51,30 +51,30 @@ class AABBIteratorTest {
assertEquals(24, set.size)
assertEquals(setOf(
Vec3i(0, 0, 0),
Vec3i(0, 0, 1),
Vec3i(0, 0, 2),
Vec3i(0, 0, 3),
Vec3i(0, 1, 0),
Vec3i(0, 1, 1),
Vec3i(0, 1, 2),
Vec3i(0, 1, 3),
Vec3i(0, 2, 0),
Vec3i(0, 2, 1),
Vec3i(0, 2, 2),
Vec3i(0, 2, 3),
Vec3i(1, 0, 0),
Vec3i(1, 0, 1),
Vec3i(1, 0, 2),
Vec3i(1, 0, 3),
Vec3i(1, 1, 0),
Vec3i(1, 1, 1),
Vec3i(1, 1, 2),
Vec3i(1, 1, 3),
Vec3i(1, 2, 0),
Vec3i(1, 2, 1),
Vec3i(1, 2, 2),
Vec3i(1, 2, 3),
BlockPosition(0, 0, 0),
BlockPosition(0, 0, 1),
BlockPosition(0, 0, 2),
BlockPosition(0, 0, 3),
BlockPosition(0, 1, 0),
BlockPosition(0, 1, 1),
BlockPosition(0, 1, 2),
BlockPosition(0, 1, 3),
BlockPosition(0, 2, 0),
BlockPosition(0, 2, 1),
BlockPosition(0, 2, 2),
BlockPosition(0, 2, 3),
BlockPosition(1, 0, 0),
BlockPosition(1, 0, 1),
BlockPosition(1, 0, 2),
BlockPosition(1, 0, 3),
BlockPosition(1, 1, 0),
BlockPosition(1, 1, 1),
BlockPosition(1, 1, 2),
BlockPosition(1, 1, 3),
BlockPosition(1, 2, 0),
BlockPosition(1, 2, 1),
BlockPosition(1, 2, 2),
BlockPosition(1, 2, 3),
), set)
}
@ -85,7 +85,7 @@ class AABBIteratorTest {
val positions = aabb.positions()
assertEquals(1, positions.size)
assertTrue(positions.hasNext())
assertEquals(Vec3i(0, 0, 0), positions.next())
assertEquals(BlockPosition(0, 0, 0), positions.next())
assertFalse(positions.hasNext())
}
@ -96,9 +96,9 @@ class AABBIteratorTest {
val positions = aabb.positions()
assertEquals(2, positions.size)
assertTrue(positions.hasNext())
assertEquals(Vec3i(0, 0, 0), positions.next())
assertEquals(BlockPosition(0, 0, 0), positions.next())
assertTrue(positions.hasNext())
assertEquals(Vec3i(1, 0, 0), positions.next())
assertEquals(BlockPosition(1, 0, 0), positions.next())
assertFalse(positions.hasNext())
}
@ -111,7 +111,7 @@ class AABBIteratorTest {
for (x in -2 until 2) {
for (y in -2 until 2) {
for (z in -2 until 2) {
assertEquals(Vec3i(x, y, z), positions.next())
assertEquals(BlockPosition(x, y, z), positions.next())
}
}
}

View File

@ -22,100 +22,106 @@ class InChunkPositionTest {
@Test
fun `init correct min`() {
val position = InSectionPosition(0, -2048, 0)
val position = InChunkPosition(0, -2048, 0)
}
@Test
fun `init correct max`() {
val position = InSectionPosition(15, 2047, 15)
val position = InChunkPosition(15, 2047, 15)
}
@Test
fun `init badly`() {
assertThrows<AssertionError> { InSectionPosition(-1, -5000, -1) }
assertThrows<AssertionError> { InChunkPosition(-1, -5000, -1) }
}
@Test
fun `correct x`() {
val position = InSectionPosition(2, 0xF, 0xF)
val position = InChunkPosition(2, 0xF, 0xF)
assertEquals(position.x, 2)
}
@Test
fun `correct plus x`() {
val position = InSectionPosition(2, 0xF, 0xF)
val position = InChunkPosition(2, 0xF, 0xF)
assertEquals(position.plusX().x, 3)
}
@Test
fun `correct plus 2 x`() {
val position = InSectionPosition(2, 0xF, 0xF)
val position = InChunkPosition(2, 0xF, 0xF)
assertEquals(position.plusX(2).x, 4)
}
@Test
fun `correct minus x`() {
val position = InSectionPosition(2, 0xF, 0xF)
val position = InChunkPosition(2, 0xF, 0xF)
assertEquals(position.minusX().x, 1)
}
@Test
fun `correct y`() {
val position = InSectionPosition(0xF, 3, 0xF)
val position = InChunkPosition(0xF, 3, 0xF)
assertEquals(position.y, 3)
}
@Test
fun `correct negative y`() {
val position = InSectionPosition(0xF, -1000, 0xF)
val position = InChunkPosition(0xF, -1000, 0xF)
assertEquals(position.y, -1000)
}
@Test
fun `correct positive y`() {
val position = InSectionPosition(0xF, 1000, 0xF)
val position = InChunkPosition(0xF, 1000, 0xF)
assertEquals(position.y, 1000)
}
@Test
fun `correct plus y`() {
val position = InSectionPosition(0xF, 2, 0xF)
val position = InChunkPosition(0xF, 2, 0xF)
assertEquals(position.plusY().y, 3)
}
@Test
fun `correct plus 2 y`() {
val position = InSectionPosition(0xF, 2, 0xF)
val position = InChunkPosition(0xF, 2, 0xF)
assertEquals(position.plusY(2).y, 4)
}
@Test
fun `correct minus y`() {
val position = InSectionPosition(0xF, 2, 0xF)
val position = InChunkPosition(0xF, 2, 0xF)
assertEquals(position.minusY().y, 1)
}
@Test
fun `correct minus y 2`() {
val position = InChunkPosition(0xF, -3, 0xF)
assertEquals(position.minusY().y, -4)
}
@Test
fun `correct z`() {
val position = InSectionPosition(0xF, 0xF, 4)
val position = InChunkPosition(0xF, 0xF, 4)
assertEquals(position.z, 4)
}
@Test
fun `correct plus z`() {
val position = InSectionPosition(0xF, 0xF, 2)
val position = InChunkPosition(0xF, 0xF, 2)
assertEquals(position.plusZ().z, 3)
}
@Test
fun `correct plus 2 z`() {
val position = InSectionPosition(0xF, 0xF, 2)
val position = InChunkPosition(0xF, 0xF, 2)
assertEquals(position.plusZ(2).z, 4)
}
@Test
fun `correct minus z`() {
val position = InSectionPosition(0xF, 0xF, 2)
val position = InChunkPosition(0xF, 0xF, 2)
assertEquals(position.minusZ().z, 1)
}
}