inline block, chunk, in chunk position

This commit is contained in:
Moritz Zwerger 2025-02-14 20:41:03 +01:00
parent 387877fe27
commit b655998304
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
16 changed files with 678 additions and 120 deletions

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2024 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.
*
@ -37,7 +37,6 @@ class WorldBiomes(val world: World) : BiomeAccessor {
operator fun get(position: BlockPosition) = getBiome(position)
override fun getBiome(position: BlockPosition) = getBiome(position.x, position.y, position.z)
operator fun get(x: Int, y: Int, z: Int) = getBiome(x, y, z)
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
val chunk = world.chunks[x shr 4, z shr 4] ?: return null
return getBiome(x and 0x0F, y.clamp(world.dimension.minY, world.dimension.maxY), z and 0x0F, chunk)

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,15 +13,11 @@
package de.bixilon.minosoft.data.world.biome.accessor
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.positions.InChunkPosition
interface BiomeAccessor {
fun getBiome(position: Vec3i): Biome? {
return getBiome(position.x, position.y, position.z)
}
fun getBiome(x: Int, y: Int, z: Int): Biome?
fun getBiome(position: InChunkPosition): Biome?
}

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,17 +13,10 @@
package de.bixilon.minosoft.data.world.biome.accessor
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.registries.biomes.Biome
import de.bixilon.minosoft.data.world.positions.InChunkPosition
object NullBiomeAccessor : BiomeAccessor {
override fun getBiome(position: Vec3i): Biome? {
return null
}
override fun getBiome(x: Int, y: Int, z: Int): Biome? {
return null
}
override fun getBiome(position: InChunkPosition) = null
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2024 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.
*
@ -33,8 +33,8 @@ import de.bixilon.minosoft.data.world.chunk.update.chunk.prototype.PrototypeChan
import de.bixilon.minosoft.data.world.positions.ChunkPosition
class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity: Int = 0) {
val chunks: LockMap<Vec2i, Chunk> = LockMap(HashMap(chunkCapacity), world.lock)
val prototypes: LockMap<Vec2i, ChunkPrototype> = LockMap(HashMap(prototypeCapacity), world.lock)
val chunks: LockMap<ChunkPosition, Chunk> = LockMap(HashMap(chunkCapacity), world.lock)
val prototypes: LockMap<ChunkPosition, ChunkPrototype> = LockMap(HashMap(prototypeCapacity), world.lock)
val size = WorldSizeManager(world)
val ticker = ChunkTicker(this)
var revision by observed(0)
@ -45,7 +45,7 @@ class ChunkManager(val world: World, chunkCapacity: Int = 0, prototypeCapacity:
}
operator fun get(x: Int, z: Int): Chunk? {
return this[Vec2i(x, z)]
return this[ChunkPosition(x, z)]
}
fun unload(position: ChunkPosition) {

View File

@ -21,6 +21,7 @@ import de.bixilon.minosoft.data.registries.blocks.state.BlockState
import de.bixilon.minosoft.data.world.biome.accessor.noise.NoiseBiomeAccessor
import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.ChunkPositionUtil.chunkPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight
import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3iUtil.inChunkPosition
@ -52,7 +53,7 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
this.chunk.lock.unlock()
}
operator fun set(offset: Vec2i, chunk: Chunk) {
operator fun set(offset: ChunkPosition, chunk: Chunk) {
set(getIndex(offset), chunk)
}
@ -66,7 +67,7 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
chunk.lock.unlock()
}
fun remove(offset: Vec2i) {
fun remove(offset: ChunkPosition) {
remove(getIndex(offset))
}
@ -114,8 +115,8 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
}
}
fun trace(offset: Vec2i): Chunk? {
return trace(offset.x, offset.y)
fun trace(offset: ChunkPosition): Chunk? {
return trace(offset.x, offset.z)
}
fun trace(offsetX: Int, offsetZ: Int): Chunk? = when {
@ -177,26 +178,26 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable<Chunk?> {
*/
val OFFSETS = arrayOf(
Vec2i(-1, -1), // 0
Vec2i(-1, +0), // 1
Vec2i(-1, +1), // 2
Vec2i(+0, -1), // 3
Vec2i(+0, +1), // 4
Vec2i(+1, -1), // 5
Vec2i(+1, +0), // 6
Vec2i(+1, +1), // 7
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
)
fun getIndex(offset: Vec2i): Int {
fun getIndex(offset: ChunkPosition): Int {
return when {
offset.x == -1 && offset.y == -1 -> 0
offset.x == -1 && offset.y == 0 -> 1
offset.x == -1 && offset.y == 1 -> 2
offset.x == 0 && offset.y == -1 -> 3
offset.x == 0 && offset.y == 1 -> 4
offset.x == 1 && offset.y == -1 -> 5
offset.x == 1 && offset.y == 0 -> 6
offset.x == 1 && offset.y == 1 -> 7
offset.x == -1 && offset.z == -1 -> 0
offset.x == -1 && offset.z == 0 -> 1
offset.x == -1 && offset.z == 1 -> 2
offset.x == 0 && offset.z == -1 -> 3
offset.x == 0 && offset.z == 1 -> 4
offset.x == 1 && offset.z == -1 -> 5
offset.x == 1 && offset.z == 0 -> 6
offset.x == 1 && offset.z == 1 -> 7
else -> Broken("Can not get neighbour chunk from offset $offset")
}
}

View File

@ -39,7 +39,7 @@ class BiomeSectionDataProvider(
var biome = super.get(x, inSectionY, z)
if (biome != null) return biome
biome = section.chunk.world.biomes.noise?.get(x, y, z, section.chunk)
unsafeSet(x, inSectionY, z, biome)
unsafeSet(InSectionPosition(x, inSectionY, z), biome)
return biome
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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,102 @@
package de.bixilon.minosoft.data.world.positions
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.text.formatting.TextFormattable
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.format
typealias BlockPosition = Vec3i
typealias MutableBlockPosition = Vec3i
@JvmInline
value class BlockPosition(
inline val index: Long,
) : TextFormattable {
constructor(x: Int, y: Int, z: Int) : this((y and 0xFFF shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) {
assert(x >= 0)
assert(x <= ProtocolDefinition.SECTION_MAX_X)
assert(y >= ProtocolDefinition.CHUNK_MIN_Y)
assert(y <= ProtocolDefinition.CHUNK_MAX_Y)
assert(z >= 0)
assert(z <= 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 fun plusX(): BlockPosition {
assert(this.x < ProtocolDefinition.SECTION_MAX_X)
return BlockPosition(index + X * 1)
}
inline fun plusX(x: Int): BlockPosition {
assert(this.x + x < ProtocolDefinition.SECTION_MAX_X)
assert(this.x + x > 0)
return BlockPosition(index + X * x)
}
inline fun minusX(): BlockPosition {
assert(this.x > 0)
return BlockPosition(index - X * 1)
}
inline fun plusY(): BlockPosition {
assert(this.y < ProtocolDefinition.CHUNK_MAX_Y)
return BlockPosition(index + Y * 1)
}
inline fun plusY(y: Int): BlockPosition {
assert(this.y + y < ProtocolDefinition.CHUNK_MAX_Y)
assert(this.y + y > ProtocolDefinition.CHUNK_MIN_Y)
return BlockPosition(index + Y * y)
}
inline fun minusY(): BlockPosition {
assert(this.y > ProtocolDefinition.CHUNK_MIN_Y)
return BlockPosition(index - Y * 1)
}
inline fun plusZ(): BlockPosition {
assert(this.z < ProtocolDefinition.SECTION_MAX_Z)
return BlockPosition(index + Z * 1)
}
inline fun plusZ(z: Int): BlockPosition {
assert(this.z + z < ProtocolDefinition.SECTION_MAX_Z)
assert(this.z + z > 0)
return BlockPosition(index + Z * z)
}
inline fun minusZ(): BlockPosition {
assert(this.z > 0)
return BlockPosition(index - Z * 1)
}
inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = BlockPosition(x, y, z)
inline operator fun plus(position: BlockPosition) = BlockPosition(this.x + position.x, this.y + position.y, this.z + position.z)
inline operator fun unaryMinus() = BlockPosition(-this.x, -this.y, -this.z)
inline operator fun unaryPlus() = this
companion object {
const val MASK_X = 0x00F
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val MASK_Y = 0xFFF00
const val SHIFT_Y = 8
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z
const val Y = 1 shl SHIFT_Y
val EMPTY = BlockPosition(0, 0, 0)
}
override fun toText() = "(${this.x.format()} ${this.y.format()} ${this.z.format()})"
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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,79 @@
package de.bixilon.minosoft.data.world.positions
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.data.text.formatting.TextFormattable
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.format
typealias ChunkPosition = Vec2i
typealias MutableChunkPosition = Vec2i
@JvmInline
value class ChunkPosition(
inline val index: Long,
) : TextFormattable {
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)
}
inline val x: Int get() = (index and MASK_X) shr SHIFT_X
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 fun plusX(): ChunkPosition {
assert(this.x < ProtocolDefinition.SECTION_MAX_X)
return ChunkPosition(index + X * 1)
}
inline fun plusX(x: Int): ChunkPosition {
assert(this.x + x < ProtocolDefinition.SECTION_MAX_X)
assert(this.x + x > 0)
return ChunkPosition(index + X * x)
}
inline fun minusX(): ChunkPosition {
assert(this.x > 0)
return ChunkPosition(index - X * 1)
}
inline fun plusZ(): ChunkPosition {
assert(this.z < ProtocolDefinition.SECTION_MAX_Z)
return ChunkPosition(index + Z * 1)
}
inline fun plusZ(z: Int): ChunkPosition {
assert(this.z + z < ProtocolDefinition.SECTION_MAX_Z)
assert(this.z + z > 0)
return ChunkPosition(index + Z * z)
}
inline fun minusZ(): ChunkPosition {
assert(this.z > 0)
return ChunkPosition(index - Z * 1)
}
inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = ChunkPosition(x, y, z)
inline operator fun plus(position: ChunkPosition) = ChunkPosition(this.x + position.x, this.z + position.z)
inline operator fun unaryMinus() = ChunkPosition(-this.x, -this.z)
inline operator fun unaryPlus() = this
companion object {
const val MASK_X = 0x00F
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z
val EMPTY = ChunkPosition(0, 0)
}
override fun toText() = "(${this.x.format()} ${this.z.format()})"
}

View File

@ -13,9 +13,7 @@
package de.bixilon.minosoft.data.world.positions
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.kotlinglm.vec3.Vec3d
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.gui.rendering.util.VecUtil.inSectionHeight
import de.bixilon.minosoft.gui.rendering.util.VecUtil.sectionHeight
@ -32,20 +30,8 @@ object ChunkPositionUtil {
get() = ChunkPosition(x.toInt() shr 4, z.toInt() shr 4)
val BlockPosition.inChunkPosition: InChunkPosition
get() = Vec3i(x and 0x0F, y, this.z and 0x0F)
get() = InChunkPosition(x and 0x0F, y, this.z and 0x0F)
val BlockPosition.inSectionPosition: InSectionPosition
get() = InSectionPosition(x and 0x0F, y.inSectionHeight, z and 0x0F)
fun Vec2i.assignChunkPosition(position: Vec3i) {
this.x = position.x shr 4
this.y = position.z shr 4
}
fun Vec3i.assignInChunkPosition(position: Vec3i) {
this.x = position.x and 0x0F
this.y = position.y and 0x0F
this.z = position.z and 0x0F
}
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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,99 @@
package de.bixilon.minosoft.data.world.positions
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.text.formatting.TextFormattable
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.format
typealias InChunkPosition = Vec3i
typealias MutableInChunkPosition = Vec3i
@JvmInline
value class InChunkPosition(
inline val index: Int,
) : TextFormattable {
constructor(x: Int, y: Int, z: Int) : this((y and 0xFFF shl SHIFT_Y) or (z shl SHIFT_Z) or (x shl SHIFT_X)) {
assert(x >= 0)
assert(x <= ProtocolDefinition.SECTION_MAX_X)
assert(y >= ProtocolDefinition.CHUNK_MIN_Y)
assert(y <= ProtocolDefinition.CHUNK_MAX_Y)
assert(z >= 0)
assert(z <= 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 fun plusX(): InChunkPosition {
assert(this.x < ProtocolDefinition.SECTION_MAX_X)
return InChunkPosition(index + X * 1)
}
inline fun plusX(x: Int): InChunkPosition {
assert(this.x + x < ProtocolDefinition.SECTION_MAX_X)
assert(this.x + x > 0)
return InChunkPosition(index + X * x)
}
inline fun minusX(): InChunkPosition {
assert(this.x > 0)
return InChunkPosition(index - X * 1)
}
inline fun plusY(): InChunkPosition {
assert(this.y < ProtocolDefinition.CHUNK_MAX_Y)
return InChunkPosition(index + Y * 1)
}
inline fun plusY(y: Int): InChunkPosition {
assert(this.y + y < ProtocolDefinition.CHUNK_MAX_Y)
assert(this.y + y > ProtocolDefinition.CHUNK_MIN_Y)
return InChunkPosition(index + Y * y)
}
inline fun minusY(): InChunkPosition {
assert(this.y > ProtocolDefinition.CHUNK_MIN_Y)
return InChunkPosition(index - Y * 1)
}
inline fun plusZ(): InChunkPosition {
assert(this.z < ProtocolDefinition.SECTION_MAX_Z)
return InChunkPosition(index + Z * 1)
}
inline fun plusZ(z: Int): InChunkPosition {
assert(this.z + z < ProtocolDefinition.SECTION_MAX_Z)
assert(this.z + z > 0)
return InChunkPosition(index + Z * z)
}
inline fun minusZ(): InChunkPosition {
assert(this.z > 0)
return InChunkPosition(index - Z * 1)
}
inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = InChunkPosition(x, y, z)
inline operator fun plus(position: InChunkPosition) = InChunkPosition(this.x + position.x, this.y + position.y, this.z + position.z)
companion object {
const val MASK_X = 0x00F
const val SHIFT_X = 0
const val MASK_Z = 0x0F0
const val SHIFT_Z = 4
const val MASK_Y = 0xFFF00
const val SHIFT_Y = 8
const val X = 1 shl SHIFT_X
const val Z = 1 shl SHIFT_Z
const val Y = 1 shl SHIFT_Y
val EMPTY = InChunkPosition(0, 0, 0)
}
override fun toText() = "(${this.x.format()} ${this.y.format()} ${this.z.format()})"
}

View File

@ -1,19 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020-2022 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.positions
import de.bixilon.kotlinglm.vec3.Vec3i
typealias InChunkSectionPosition = Vec3i
typealias MutableInChunkSectionPosition = Vec3i

View File

@ -35,6 +35,8 @@ value class InSectionPosition(
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_X or MASK_Z)
inline fun plusX(): InSectionPosition {
assert(this.x < ProtocolDefinition.SECTION_MAX_X)

View File

@ -34,6 +34,7 @@ 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.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer
@ -97,7 +98,6 @@ class SolidSectionMesher(
position.x = offsetX + x
floatOffset[0] = (position.x - cameraOffset.x).toFloat()
for (z in blocks.minPosition.z..blocks.maxPosition.z) {
val baseIndex = (z shl 4) or x
inSectionPosition = inSectionPosition.with(z = z)
val state = blocks[inSectionPosition] ?: continue
if (state.block is FluidBlock) continue // fluids are rendered in a different renderer
@ -112,18 +112,18 @@ class SolidSectionMesher(
position.z = offsetZ + z
floatOffset[2] = (position.z - cameraOffset.z).toFloat()
val maxHeight = chunk.light.heightmap[baseIndex]
val maxHeight = chunk.light.heightmap[inSectionPosition.xz]
if (position.y >= maxHeight) {
light[SELF_LIGHT_INDEX] = (light[SELF_LIGHT_INDEX].toInt() or 0xF0).toByte()
}
checkDown(state, fastBedrock, baseIndex, isLowestSection, neighbourBlocks, neighbours, x, y, z, light, section, chunk)
checkUp(isHighestSection, baseIndex, neighbourBlocks, neighbours, x, y, z, light, section, chunk)
checkDown(state, fastBedrock, inSectionPosition, isLowestSection, neighbourBlocks, neighbours, light, section, chunk)
checkUp(isHighestSection, inSectionPosition, neighbourBlocks, neighbours, light, section, chunk)
checkNorth(neighbourBlocks, neighbours, x, y, z, light, position, neighbourChunks, section, chunk)
checkSouth(neighbourBlocks, neighbours, x, y, z, light, position, neighbourChunks, section, chunk)
checkWest(neighbourBlocks, neighbours, x, y, z, light, position, neighbourChunks, section, chunk)
checkEast(neighbourBlocks, neighbours, x, y, z, light, position, neighbourChunks, section, chunk)
checkNorth(neighbourBlocks, inSectionPosition, neighbours, light, position, neighbourChunks, section, chunk)
checkSouth(neighbourBlocks, inSectionPosition, neighbours, light, position, neighbourChunks, section, chunk)
checkWest(neighbourBlocks, inSectionPosition, neighbours, light, position, neighbourChunks, section, chunk)
checkEast(neighbourBlocks, inSectionPosition, neighbours, light, position, neighbourChunks, section, chunk)
// TODO: cull neighbours
@ -167,31 +167,31 @@ class SolidSectionMesher(
mesh.blockEntities = entities
}
private inline fun checkDown(state: BlockState, fastBedrock: Boolean, baseIndex: Int, lowest: Boolean, neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, x: Int, y: Int, z: Int, light: ByteArray, section: ChunkSection, chunk: Chunk) {
if (y == 0) {
private inline fun checkDown(state: BlockState, fastBedrock: Boolean, position: InSectionPosition, lowest: Boolean, neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, light: ByteArray, section: ChunkSection, chunk: Chunk) {
if (position.y == 0) {
if (fastBedrock && state === bedrock) {
neighbourBlocks[O_DOWN] = bedrock
} else {
neighbourBlocks[O_DOWN] = neighbours[O_DOWN]?.blocks?.let { it[x, ProtocolDefinition.SECTION_MAX_Y, z] }
light[O_DOWN] = (if (lowest) chunk.light.bottom else neighbours[O_DOWN]?.light)?.get(ProtocolDefinition.SECTION_MAX_Y shl 8 or baseIndex) ?: 0x00
neighbourBlocks[O_DOWN] = neighbours[O_DOWN]?.blocks?.let { it[position.with(y = ProtocolDefinition.SECTION_MAX_Y)] }
light[O_DOWN] = (if (lowest) chunk.light.bottom else neighbours[O_DOWN]?.light)?.get(position.with(y = ProtocolDefinition.SECTION_MAX_Y).index) ?: 0x00
}
} else {
neighbourBlocks[O_DOWN] = section.blocks[(y - 1) shl 8 or baseIndex]
light[O_DOWN] = section.light[(y - 1) shl 8 or baseIndex]
neighbourBlocks[O_DOWN] = section.blocks[position.minusY()]
light[O_DOWN] = section.light[position.minusY().index]
}
}
fun checkUp(highest: Boolean, baseIndex: Int, neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, x: Int, y: Int, z: Int, light: ByteArray, section: ChunkSection, chunk: Chunk) {
fun checkUp(highest: Boolean, position: InSectionPosition, neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, light: ByteArray, section: ChunkSection, chunk: Chunk) {
if (y == ProtocolDefinition.SECTION_MAX_Y) {
neighbourBlocks[O_UP] = neighbours[O_UP]?.blocks?.let { it[x, 0, z] }
light[O_UP] = (if (highest) chunk.light.top else neighbours[O_UP]?.light)?.get((z shl 4) or x) ?: 0x00
neighbourBlocks[O_UP] = neighbours[O_UP]?.blocks?.let { it[position.with(y = 0)] }
light[O_UP] = (if (highest) chunk.light.top else neighbours[O_UP]?.light)?.get(position.with(y = 0).index) ?: 0x00
} else {
neighbourBlocks[O_UP] = section.blocks[(y + 1) shl 8 or baseIndex]
light[O_UP] = section.light[(y + 1) shl 8 or baseIndex]
neighbourBlocks[O_UP] = section.blocks[position.plusY()]
light[O_UP] = section.light[position.plusY().index]
}
}
private inline fun checkNorth(neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
private inline fun checkNorth(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
if (z == 0) {
setNeighbour(neighbourBlocks, x, y, ProtocolDefinition.SECTION_MAX_Z, light, position, neighbours[O_NORTH], neighbourChunks[ChunkNeighbours.NORTH], O_NORTH)
} else {
@ -199,7 +199,7 @@ class SolidSectionMesher(
}
}
private inline fun checkSouth(neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
private inline fun checkSouth(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
if (z == ProtocolDefinition.SECTION_MAX_Z) {
setNeighbour(neighbourBlocks, x, y, 0, light, position, neighbours[O_SOUTH], neighbourChunks[ChunkNeighbours.SOUTH], O_SOUTH)
} else {
@ -207,7 +207,7 @@ class SolidSectionMesher(
}
}
private inline fun checkWest(neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
private inline fun checkWest(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
if (x == 0) {
setNeighbour(neighbourBlocks, ProtocolDefinition.SECTION_MAX_X, y, z, light, position, neighbours[O_WEST], neighbourChunks[ChunkNeighbours.WEST], O_WEST)
} else {
@ -215,7 +215,7 @@ class SolidSectionMesher(
}
}
private inline fun checkEast(neighbourBlocks: Array<BlockState?>, neighbours: Array<ChunkSection?>, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
private inline fun checkEast(neighbourBlocks: Array<BlockState?>, position: InSectionPosition, neighbours: Array<ChunkSection?>, light: ByteArray, position: Vec3i, neighbourChunks: Array<Chunk>, section: ChunkSection, chunk: Chunk) {
if (x == ProtocolDefinition.SECTION_MAX_X) {
setNeighbour(neighbourBlocks, 0, y, z, light, position, neighbours[O_EAST], neighbourChunks[ChunkNeighbours.EAST], O_EAST)
} else {
@ -223,12 +223,10 @@ class SolidSectionMesher(
}
}
private inline fun setNeighbour(neighbourBlocks: Array<BlockState?>, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, section: ChunkSection?, chunk: Chunk, ordinal: Int) {
val heightmapIndex = (z shl 4) or x
val neighbourIndex = y shl 8 or heightmapIndex
neighbourBlocks[ordinal] = section?.blocks?.let { it[neighbourIndex] }
light[ordinal] = section?.light?.get(neighbourIndex) ?: 0x00
if (position.y >= chunk.light.heightmap[heightmapIndex]) {
private inline fun setNeighbour(neighbourBlocks: Array<BlockState?>, position: InChunkPosition, light: ByteArray, section: ChunkSection?, chunk: Chunk, ordinal: Int) {
neighbourBlocks[ordinal] = section?.blocks?.let { it[position] }
light[ordinal] = section?.light?.get(position.index) ?: 0x00
if (position.y >= chunk.light.heightmap[position.x]) {
light[ordinal] = (light[ordinal].toInt() or SectionLight.SKY_LIGHT_MASK).toByte() // set sky light to 0x0F
}
}

View File

@ -0,0 +1,126 @@
/*
* 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.positions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
class BlockPositionTest {
@Test
fun `init correct min`() {
val position = BlockPosition(-30_000_000, -2048, -30_000_000)
}
@Test
fun `init correct max`() {
val position = BlockPosition(30_000_000, 2047, 30_000_000)
}
@Test
fun `init badly`() {
assertThrows<AssertionError> { BlockPosition(-40_000_000, -5000, -40_000_000) }
}
@Test
fun `correct positive x`() {
val position = BlockPosition(2, 0xF, 0xF)
assertEquals(position.x, 2)
}
@Test
fun `correct negative x`() {
val position = BlockPosition(-2, 0xF, 0xF)
assertEquals(position.x, -2)
}
@Test
fun `correct plus x`() {
val position = BlockPosition(2, 0xF, 0xF)
assertEquals(position.plusX().x, 3)
}
@Test
fun `correct plus 2 x`() {
val position = BlockPosition(2, 0xF, 0xF)
assertEquals(position.plusX(2).x, 4)
}
@Test
fun `correct minus x`() {
val position = BlockPosition(2, 0xF, 0xF)
assertEquals(position.minusX().x, 1)
}
@Test
fun `correct negative y`() {
val position = BlockPosition(0xF, -1000, 0xF)
assertEquals(position.y, -1000)
}
@Test
fun `correct positive y`() {
val position = BlockPosition(0xF, 1000, 0xF)
assertEquals(position.y, 1000)
}
@Test
fun `correct plus y`() {
val position = BlockPosition(0xF, 2, 0xF)
assertEquals(position.plusY().y, 3)
}
@Test
fun `correct plus 2 y`() {
val position = BlockPosition(0xF, 2, 0xF)
assertEquals(position.plusY(2).y, 4)
}
@Test
fun `correct minus y`() {
val position = BlockPosition(0xF, 2, 0xF)
assertEquals(position.minusY().y, 1)
}
@Test
fun `correct positive z`() {
val position = BlockPosition(0xF, 0xF, 4)
assertEquals(position.z, 4)
}
@Test
fun `correct negative z`() {
val position = BlockPosition(0xF, 0xF, -4)
assertEquals(position.z, -4)
}
@Test
fun `correct plus z`() {
val position = BlockPosition(0xF, 0xF, 2)
assertEquals(position.plusZ().z, 3)
}
@Test
fun `correct plus 2 z`() {
val position = BlockPosition(0xF, 0xF, 2)
assertEquals(position.plusZ(2).z, 4)
}
@Test
fun `correct minus z`() {
val position = BlockPosition(0xF, 0xF, 2)
assertEquals(position.minusZ().z, 1)
}
}

View File

@ -0,0 +1,96 @@
/*
* 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.positions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
class ChunkPositionTest {
@Test
fun `init correct min`() {
val position = ChunkPosition(-1875000, -1875000)
}
@Test
fun `init correct max`() {
val position = ChunkPosition(1875000, 1875000)
}
@Test
fun `init badly`() {
assertThrows<AssertionError> { ChunkPosition(-40_000_000, -40_000_000) }
}
@Test
fun `correct positive x`() {
val position = ChunkPosition(2, 0xF)
assertEquals(position.x, 2)
}
@Test
fun `correct negative x`() {
val position = ChunkPosition(-2, 0xF)
assertEquals(position.x, -2)
}
@Test
fun `correct plus x`() {
val position = ChunkPosition(2, 0xF)
assertEquals(position.plusX().x, 3)
}
@Test
fun `correct plus 2 x`() {
val position = ChunkPosition(2, 0xF)
assertEquals(position.plusX(2).x, 4)
}
@Test
fun `correct minus x`() {
val position = ChunkPosition(2, 0xF)
assertEquals(position.minusX().x, 1)
}
@Test
fun `correct positive z`() {
val position = ChunkPosition(0xF, 4)
assertEquals(position.z, 4)
}
@Test
fun `correct negative z`() {
val position = ChunkPosition(0xF, -4)
assertEquals(position.z, -4)
}
@Test
fun `correct plus z`() {
val position = ChunkPosition(0xF, 2)
assertEquals(position.plusZ().z, 3)
}
@Test
fun `correct plus 2 z`() {
val position = ChunkPosition(0xF, 2)
assertEquals(position.plusZ(2).z, 4)
}
@Test
fun `correct minus z`() {
val position = ChunkPosition(0xF, 2)
assertEquals(position.minusZ().z, 1)
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.positions
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
class InChunkPositionTest {
@Test
fun `init correct min`() {
val position = InSectionPosition(0, -2048, 0)
}
@Test
fun `init correct max`() {
val position = InSectionPosition(15, 2047, 15)
}
@Test
fun `init badly`() {
assertThrows<AssertionError> { InSectionPosition(-1, -5000, -1) }
}
@Test
fun `correct x`() {
val position = InSectionPosition(2, 0xF, 0xF)
assertEquals(position.x, 2)
}
@Test
fun `correct plus x`() {
val position = InSectionPosition(2, 0xF, 0xF)
assertEquals(position.plusX().x, 3)
}
@Test
fun `correct plus 2 x`() {
val position = InSectionPosition(2, 0xF, 0xF)
assertEquals(position.plusX(2).x, 4)
}
@Test
fun `correct minus x`() {
val position = InSectionPosition(2, 0xF, 0xF)
assertEquals(position.minusX().x, 1)
}
@Test
fun `correct y`() {
val position = InSectionPosition(0xF, 3, 0xF)
assertEquals(position.y, 3)
}
@Test
fun `correct negative y`() {
val position = InSectionPosition(0xF, -1000, 0xF)
assertEquals(position.y, -1000)
}
@Test
fun `correct positive y`() {
val position = InSectionPosition(0xF, 1000, 0xF)
assertEquals(position.y, 1000)
}
@Test
fun `correct plus y`() {
val position = InSectionPosition(0xF, 2, 0xF)
assertEquals(position.plusY().y, 3)
}
@Test
fun `correct plus 2 y`() {
val position = InSectionPosition(0xF, 2, 0xF)
assertEquals(position.plusY(2).y, 4)
}
@Test
fun `correct minus y`() {
val position = InSectionPosition(0xF, 2, 0xF)
assertEquals(position.minusY().y, 1)
}
@Test
fun `correct z`() {
val position = InSectionPosition(0xF, 0xF, 4)
assertEquals(position.z, 4)
}
@Test
fun `correct plus z`() {
val position = InSectionPosition(0xF, 0xF, 2)
assertEquals(position.plusZ().z, 3)
}
@Test
fun `correct plus 2 z`() {
val position = InSectionPosition(0xF, 0xF, 2)
assertEquals(position.plusZ(2).z, 4)
}
@Test
fun `correct minus z`() {
val position = InSectionPosition(0xF, 0xF, 2)
assertEquals(position.minusZ().z, 1)
}
}