diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/WorldBiomes.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/WorldBiomes.kt index 268830c80..0e7881e28 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/biome/WorldBiomes.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/WorldBiomes.kt @@ -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) diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/BiomeAccessor.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/BiomeAccessor.kt index e7795627c..e4d5ab6de 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/BiomeAccessor.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/BiomeAccessor.kt @@ -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? } diff --git a/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/NullBiomeAccessor.kt b/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/NullBiomeAccessor.kt index a9fd93c6c..aab6a892f 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/NullBiomeAccessor.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/biome/accessor/NullBiomeAccessor.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt index 42686a5c5..6a66ee8d0 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/manager/ChunkManager.kt @@ -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 = LockMap(HashMap(chunkCapacity), world.lock) - val prototypes: LockMap = LockMap(HashMap(prototypeCapacity), world.lock) + val chunks: LockMap = LockMap(HashMap(chunkCapacity), world.lock) + val prototypes: LockMap = 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) { diff --git a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt index b982b74e8..96dbc34e6 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/chunk/neighbours/ChunkNeighbours.kt @@ -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 { 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.lock.unlock() } - fun remove(offset: Vec2i) { + fun remove(offset: ChunkPosition) { remove(getIndex(offset)) } @@ -114,8 +115,8 @@ class ChunkNeighbours(val chunk: Chunk) : Iterable { } } - 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 { */ 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") } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt b/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt index 1399bade0..f8e30d02c 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/container/biome/BiomeSectionDataProvider.kt @@ -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 } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt index 07318458d..7ed1708c4 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt @@ -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()})" +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt index f9876d028..07ca163a7 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt @@ -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()})" +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPositionUtil.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPositionUtil.kt index 447c37efe..44b530c0c 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPositionUtil.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPositionUtil.kt @@ -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 - } } diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt index 1384434cf..00d44d329 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt @@ -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()})" +} diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkSectionPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkSectionPosition.kt deleted file mode 100644 index 6147afb73..000000000 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkSectionPosition.kt +++ /dev/null @@ -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 . - * - * 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 diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt index aff9e7da6..ea336399f 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/InSectionPosition.kt @@ -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) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt index a124bad0c..d138c6320 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/chunk/mesher/SolidSectionMesher.kt @@ -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, neighbours: Array, 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, neighbours: Array, 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, neighbours: Array, x: Int, y: Int, z: Int, light: ByteArray, section: ChunkSection, chunk: Chunk) { + fun checkUp(highest: Boolean, position: InSectionPosition, neighbourBlocks: Array, neighbours: Array, 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, neighbours: Array, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array, section: ChunkSection, chunk: Chunk) { + private inline fun checkNorth(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, position: Vec3i, neighbourChunks: Array, 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, neighbours: Array, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array, section: ChunkSection, chunk: Chunk) { + private inline fun checkSouth(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, position: Vec3i, neighbourChunks: Array, 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, neighbours: Array, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array, section: ChunkSection, chunk: Chunk) { + private inline fun checkWest(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, position: Vec3i, neighbourChunks: Array, 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, neighbours: Array, x: Int, y: Int, z: Int, light: ByteArray, position: Vec3i, neighbourChunks: Array, section: ChunkSection, chunk: Chunk) { + private inline fun checkEast(neighbourBlocks: Array, position: InSectionPosition, neighbours: Array, light: ByteArray, position: Vec3i, neighbourChunks: Array, 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, 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, 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 } } diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt new file mode 100644 index 000000000..7ab15e9c9 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt @@ -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 . + * + * 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 { 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) + } +} diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt new file mode 100644 index 000000000..52fe2cff3 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt @@ -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 . + * + * 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 { 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) + } +} diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt new file mode 100644 index 000000000..1c2557687 --- /dev/null +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt @@ -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 . + * + * 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 { 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) + } +}