section position

This commit is contained in:
Moritz Zwerger 2025-03-06 16:14:42 +01:00
parent 8f1247d4b1
commit 375bccca47
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
24 changed files with 428 additions and 104 deletions

View File

@ -27,6 +27,7 @@ import de.bixilon.minosoft.data.registries.dimension.DimensionProperties
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.camera.Camera import de.bixilon.minosoft.gui.rendering.camera.Camera
import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer
@ -80,9 +81,9 @@ class SolidSectionMesherTest {
val mesher = SolidSectionMesher(context) val mesher = SolidSectionMesher(context)
val chunk = world.chunks[0, 0]!! val chunk = world.chunks[0, 0]!!
val meshes = ChunkMeshes(context, chunk.position, 0, true) val meshes = ChunkMeshes(context, SectionPosition.of(chunk.position, 0), true)
mesher.mesh(chunk.position, 0, chunk, chunk.sections[0]!!, chunk.neighbours.neighbours, chunk.sections[0]!!.neighbours!!, meshes) mesher.mesh(SectionPosition.of(chunk.position, 0), chunk, chunk.sections[0]!!, chunk.neighbours.neighbours, chunk.sections[0]!!.neighbours!!, meshes)
return meshes return meshes
} }

View File

@ -23,6 +23,7 @@ import de.bixilon.minosoft.data.world.container.block.BlockSectionDataProvider
import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight
import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.network.session.play.PlaySession
import java.util.* import java.util.*
@ -30,7 +31,7 @@ import java.util.*
* Collection of 16x16x16 blocks * Collection of 16x16x16 blocks
*/ */
class ChunkSection( class ChunkSection(
val height: Int, val height: SectionHeight,
val chunk: Chunk, val chunk: Chunk,
) { ) {
val blocks = BlockSectionDataProvider(chunk.lock, this) val blocks = BlockSectionDataProvider(chunk.lock, this)

View File

@ -111,6 +111,7 @@ value class BlockPosition(
inline val hash get() = generatePositionHash(x, y, z) inline val hash get() = generatePositionHash(x, y, z)
inline val sectionHeight get() = y.sectionHeight inline val sectionHeight get() = y.sectionHeight
inline val chunkPosition get() = ChunkPosition(x shr 4, z shr 4) inline val chunkPosition get() = ChunkPosition(x shr 4, z shr 4)
inline val sectionPosition get() = SectionPosition(x shr 4, y shr 4, z shr 4)
inline val inChunkPosition get() = InChunkPosition(x and 0x0F, y, this.z and 0x0F) inline val inChunkPosition get() = InChunkPosition(x and 0x0F, y, this.z and 0x0F)
inline val inSectionPosition get() = InSectionPosition(x and 0x0F, y.inSectionHeight, z and 0x0F) inline val inSectionPosition get() = InSectionPosition(x and 0x0F, y.inSectionHeight, z and 0x0F)
@ -169,5 +170,13 @@ value class BlockPosition(
chunk.z * ProtocolDefinition.SECTION_WIDTH_Z + inSection.z chunk.z * ProtocolDefinition.SECTION_WIDTH_Z + inSection.z
) // ToDo: Confirm ) // ToDo: Confirm
} }
fun of(section: SectionPosition, inSection: InSectionPosition): BlockPosition {
return BlockPosition(
section.x * ProtocolDefinition.SECTION_WIDTH_X + inSection.x,
section.y * ProtocolDefinition.SECTION_HEIGHT_Y + inSection.y,
section.z * ProtocolDefinition.SECTION_WIDTH_Z + inSection.z
) // ToDo: Confirm
}
} }
} }

View File

@ -88,6 +88,8 @@ value class ChunkPosition(
inline operator fun component1() = x inline operator fun component1() = x
inline operator fun component2() = z inline operator fun component2() = z
fun sectionPosition(y: SectionHeight) = SectionPosition(x, y, z)
override fun toText() = "(${this.x.format()} ${this.z.format()})" override fun toText() = "(${this.x.format()} ${this.z.format()})"
override fun toString() = "c($x $z)" override fun toString() = "c($x $z)"
@ -105,8 +107,8 @@ value class ChunkPosition(
const val Z = 1L shl SHIFT_Z const val Z = 1L shl SHIFT_Z
const val MAX_X = (BlockPosition.MAX_X shr 4) + 1 const val MAX_X = BlockPosition.MAX_X shr 4
const val MAX_Z = (BlockPosition.MAX_Z shr 4) + 1 const val MAX_Z = BlockPosition.MAX_Z shr 4
val EMPTY = ChunkPosition(0, 0) val EMPTY = ChunkPosition(0, 0)

View File

@ -0,0 +1,146 @@
/*
* 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 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.util.KUtil.format
@JvmInline
value class SectionPosition(
inline val raw: Long,
) : TextFormattable {
constructor() : this(0, 0, 0)
constructor(x: Int, y: SectionHeight, 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, MIN_Y, MAX_Y)
assertPosition(z, -MAX_Z, MAX_Z)
}
inline val x: Int get() = (((raw ushr SHIFT_X).toInt() and MASK_X) shl (Int.SIZE_BITS - BITS_X)) shr (Int.SIZE_BITS - BITS_X)
inline val y: SectionHeight get() = (((raw ushr SHIFT_Y).toInt() and MASK_Y) shl (Int.SIZE_BITS - BITS_Y)) shr (Int.SIZE_BITS - BITS_Y)
inline val z: Int get() = (((raw ushr SHIFT_Z).toInt() and MASK_Z) shl (Int.SIZE_BITS - BITS_Z)) shr (Int.SIZE_BITS - BITS_Z)
inline fun plusX(): SectionPosition {
assertPosition(this.x < MAX_X)
return SectionPosition(raw + X * 1)
}
inline fun plusX(x: Int): SectionPosition {
assertPosition(this.x + x, -MAX_X, MAX_X)
return SectionPosition(raw + X * x)
}
inline fun minusX(): SectionPosition {
assert(this.x > -MAX_X)
return SectionPosition(raw - X * 1)
}
inline fun plusY(): SectionPosition {
assertPosition(this.y < MAX_Y)
return SectionPosition(raw + Y * 1)
}
inline fun plusY(y: Int): SectionPosition {
assertPosition(this.y + y, MIN_Y, MAX_Y)
return SectionPosition(raw + Y * y)
}
inline fun minusY(): SectionPosition {
assert(this.y > MIN_Y)
return SectionPosition(raw - Y * 1)
}
inline fun plusZ(): SectionPosition {
assert(this.z < MAX_Z)
return SectionPosition(raw + Z * 1)
}
inline fun plusZ(z: Int): SectionPosition {
assertPosition(this.z + z, -MAX_Z, MAX_Z)
return SectionPosition(raw + Z * z)
}
inline fun minusZ(): SectionPosition {
assert(this.z > -MAX_Z)
return SectionPosition(raw - Z * 1)
}
inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = SectionPosition(x, y, z)
inline operator fun plus(value: Int) = SectionPosition(this.x + value, this.y + value, this.z + value)
inline operator fun minus(value: Int) = SectionPosition(this.x - value, this.y - value, this.z - value)
inline operator fun times(value: Int) = SectionPosition(this.x * value, this.y * value, this.z * value)
inline operator fun div(value: Int) = SectionPosition(this.x / value, this.y * value, this.z / value)
inline operator fun plus(position: SectionPosition) = SectionPosition(this.x + position.x, this.y + position.y, this.z + position.z)
inline operator fun minus(position: SectionPosition) = SectionPosition(this.x - position.x, this.y - position.y, this.z - position.z)
inline operator fun plus(position: ChunkPosition) = SectionPosition(this.x + position.x, this.y, this.z + position.z)
inline operator fun minus(position: ChunkPosition) = SectionPosition(this.x - position.x, this.y, this.z - position.z)
inline operator fun plus(direction: Directions) = SectionPosition(this.x + direction.vector.x, this.y + direction.vector.y, this.z + direction.vector.z)
inline operator fun minus(direction: Directions) = SectionPosition(this.x - direction.vector.x, this.y - direction.vector.y, this.z - direction.vector.z)
inline operator fun unaryMinus() = SectionPosition(-this.x, -this.y, -this.z)
inline operator fun unaryPlus() = this
inline operator fun component1() = x
inline operator fun component2() = y
inline operator fun component3() = z
inline fun length2() = (x * x + y * y + z * z)
inline fun sectionIndex(minSection: SectionHeight): SectionIndex = this.y - minSection
inline val chunkPosition get() = ChunkPosition(x, z)
override fun toText() = "(${this.x.format()} ${this.y.format()} ${this.z.format()})"
override fun toString() = "c($x $y $z)"
companion object {
const val BITS_X = 22
const val MASK_X = (1 shl BITS_X) - 1
const val SHIFT_X = 0
const val BITS_Z = 22
const val MASK_Z = (1 shl BITS_Z) - 1
const val SHIFT_Z = BITS_X
const val BITS_Y = 8
const val MASK_Y = (1 shl BITS_Y) - 1
const val SHIFT_Y = BITS_X + BITS_Z
const val X = 1L shl SHIFT_X
const val Z = 1L shl SHIFT_Z
const val Y = 1L shl SHIFT_Y
const val MAX_X = BlockPosition.MAX_X shr 4
const val MIN_Y = BlockPosition.MIN_Y shr 4
const val MAX_Y = BlockPosition.MAX_Y shr 4
const val MAX_Z = BlockPosition.MAX_Z shr 4
fun of(chunkPosition: ChunkPosition, sectionHeight: Int) = SectionPosition(chunkPosition.x, sectionHeight, chunkPosition.z)
fun of(chunkPosition: ChunkPosition, sectionIndex: SectionIndex, minSection: SectionHeight) = SectionPosition(chunkPosition.x, sectionIndex + minSection, chunkPosition.z)
val EMPTY = SectionPosition(0, 0, 0)
}
}

View File

@ -138,7 +138,7 @@ class MatrixHandler(
if (view.updateFrustum) { if (view.updateFrustum) {
frustum.recalculate() frustum.recalculate()
camera.visibilityGraph.updateCamera(cameraBlockPosition.chunkPosition, cameraBlockPosition.sectionHeight) camera.visibilityGraph.updateCamera(cameraBlockPosition.sectionPosition)
} }
session.events.fire(CameraPositionChangeEvent(context, useEyePosition)) session.events.fire(CameraPositionChangeEvent(context, useEyePosition))

View File

@ -23,6 +23,7 @@ import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.World
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.RenderingStates import de.bixilon.minosoft.gui.rendering.RenderingStates
import de.bixilon.minosoft.gui.rendering.chunk.mesh.VisibleMeshes import de.bixilon.minosoft.gui.rendering.chunk.mesh.VisibleMeshes
@ -86,8 +87,7 @@ class ChunkRenderer(
private var previousViewDistance = session.world.view.viewDistance private var previousViewDistance = session.world.view.viewDistance
private var cameraPosition = Vec3.EMPTY private var cameraPosition = Vec3.EMPTY
var cameraChunkPosition = ChunkPosition.EMPTY var cameraSectionPosition = SectionPosition.EMPTY
var cameraSectionHeight = 0
var limitChunkTransferTime = true var limitChunkTransferTime = true
@ -198,15 +198,15 @@ class ChunkRenderer(
} }
fun unload(item: WorldQueueItem) = unload(QueuePosition(item.chunkPosition, item.sectionHeight)) fun unload(item: WorldQueueItem) = unload(QueuePosition(item.position))
fun unload(position: QueuePosition) { fun unload(position: QueuePosition) {
lock.lock() lock.lock()
loaded.unload(position.position, position.sectionHeight, false) loaded.unload(position.position, false)
culledQueue.remove(position.position, position.sectionHeight, false) culledQueue.remove(position.position.chunkPosition, false)
meshingQueue.remove(position, false) meshingQueue.remove(position, false)
loadingQueue.abort(position, false) loadingQueue.abort(position, false)
meshingQueue.tasks.interrupt(position.position, position.sectionHeight) meshingQueue.tasks.interrupt(position.position.chunkPosition)
lock.unlock() lock.unlock()
} }
@ -252,15 +252,10 @@ class ChunkRenderer(
private fun onFrustumChange() { private fun onFrustumChange() {
var sortQueue = false var sortQueue = false
val cameraPosition = Vec3(session.player.renderInfo.eyePosition - context.camera.offset.offset) val cameraPosition = Vec3(session.player.renderInfo.eyePosition - context.camera.offset.offset)
val cameraChunkPosition = cameraPosition.blockPosition.chunkPosition val sectionPosition = cameraPosition.blockPosition.sectionPosition
val cameraSectionHeight = this.cameraSectionHeight
if (this.cameraPosition != cameraPosition) { if (this.cameraPosition != cameraPosition) {
if (this.cameraChunkPosition != cameraChunkPosition) { if (this.cameraSectionPosition != sectionPosition) {
this.cameraChunkPosition = cameraChunkPosition this.cameraSectionPosition = sectionPosition
sortQueue = true
}
if (this.cameraSectionHeight != cameraSectionHeight) {
this.cameraSectionHeight = cameraSectionHeight
sortQueue = true sortQueue = true
} }
this.cameraPosition = cameraPosition this.cameraPosition = cameraPosition

View File

@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.chunk
import de.bixilon.kutil.concurrent.lock.RWLock import de.bixilon.kutil.concurrent.lock.RWLock
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
import de.bixilon.minosoft.gui.rendering.chunk.mesh.VisibleMeshes import de.bixilon.minosoft.gui.rendering.chunk.mesh.VisibleMeshes
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
@ -67,17 +68,17 @@ class LoadedMeshes(
if (lock) unlock() if (lock) unlock()
} }
fun unload(position: ChunkPosition, sectionHeight: Int, lock: Boolean) { fun unload(position: SectionPosition, lock: Boolean) {
if (lock) lock() if (lock) lock()
val meshes = this.meshes[position] val meshes = this.meshes[position.chunkPosition]
if (meshes != null) { if (meshes != null) {
meshes.remove(sectionHeight)?.let { meshes.remove(position.y)?.let {
renderer.unloadingQueue.forceQueue(it, lock) renderer.unloadingQueue.forceQueue(it, lock)
if (meshes.isEmpty()) { if (meshes.isEmpty()) {
this.meshes.remove(position) this.meshes.remove(position.chunkPosition)
} }
} }
} }
@ -106,7 +107,7 @@ class LoadedMeshes(
for (entry in meshes.int2ObjectEntrySet()) { for (entry in meshes.int2ObjectEntrySet()) {
val mesh = entry.value val mesh = entry.value
if (!renderer.visibilityGraph.isSectionVisible(chunkPosition, entry.intKey, mesh.minPosition, mesh.maxPosition, false)) { if (!renderer.visibilityGraph.isSectionVisible(SectionPosition.of(chunkPosition, entry.intKey), mesh.minPosition, mesh.maxPosition, false)) {
continue continue
} }
visible.addMesh(mesh) visible.addMesh(mesh)

View File

@ -14,22 +14,18 @@
package de.bixilon.minosoft.gui.rendering.chunk package de.bixilon.minosoft.gui.rendering.chunk
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kotlinglm.vec3.Vec3i
import de.bixilon.minosoft.data.world.chunk.ChunkSection import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
import de.bixilon.minosoft.gui.rendering.chunk.queue.QueuePosition import de.bixilon.minosoft.gui.rendering.chunk.queue.QueuePosition
import java.util.*
class WorldQueueItem( class WorldQueueItem(
val chunkPosition: ChunkPosition, val position: SectionPosition,
val sectionHeight: Int,
val chunk: Chunk, val chunk: Chunk,
val section: ChunkSection, val section: ChunkSection,
val center: Vec3, val center: Vec3,
) { ) {
val sectionPosition = Vec3i(chunkPosition.x, sectionHeight, chunkPosition.z)
var mesh: ChunkMeshes? = null var mesh: ChunkMeshes? = null
var distance = 0 var distance = 0
@ -37,15 +33,15 @@ class WorldQueueItem(
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is WorldQueueItem) { if (other is WorldQueueItem) {
return chunkPosition == other.chunkPosition && sectionHeight == other.sectionHeight return position == other.position
} }
if (other is QueuePosition) { if (other is QueuePosition) {
return chunkPosition == other.position && sectionHeight == other.sectionHeight return position == other.position
} }
return false return false
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return Objects.hash(chunkPosition, sectionHeight) return position.hashCode()
} }
} }

View File

@ -17,8 +17,8 @@ import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec3.Vec3 import de.bixilon.kotlinglm.vec3.Vec3
import de.bixilon.kutil.exception.Broken import de.bixilon.kutil.exception.Broken
import de.bixilon.minosoft.data.world.positions.BlockPosition 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.data.world.positions.InSectionPosition
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
@ -29,11 +29,10 @@ import de.bixilon.minosoft.util.collections.floats.DirectArrayFloatList
class ChunkMeshes( class ChunkMeshes(
context: RenderContext, context: RenderContext,
val chunkPosition: ChunkPosition, val position: SectionPosition,
val sectionHeight: Int,
smallMesh: Boolean = false, smallMesh: Boolean = false,
) : BlockVertexConsumer { ) : BlockVertexConsumer {
val center: Vec3 = Vec3(BlockPosition.of(chunkPosition, sectionHeight, InSectionPosition(8, 8, 8))) val center: Vec3 = Vec3(BlockPosition.of(position, InSectionPosition(8, 8, 8)))
var opaqueMesh: ChunkMesh? = ChunkMesh(context, if (smallMesh) 8192 else 65536) var opaqueMesh: ChunkMesh? = ChunkMesh(context, if (smallMesh) 8192 else 65536)
var translucentMesh: ChunkMesh? = ChunkMesh(context, if (smallMesh) 4096 else 16384) var translucentMesh: ChunkMesh? = ChunkMesh(context, if (smallMesh) 4096 else 16384)
var textMesh: ChunkMesh? = ChunkMesh(context, if (smallMesh) 1024 else 4096) var textMesh: ChunkMesh? = ChunkMesh(context, if (smallMesh) 1024 else 4096)

View File

@ -38,12 +38,12 @@ class ChunkMesher(
return null return null
} }
val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours.neighbours, item.chunk, item.section.height) val sectionNeighbours = ChunkUtil.getDirectNeighbours(neighbours.neighbours, item.chunk, item.section.height)
val mesh = ChunkMeshes(renderer.context, item.chunkPosition, item.sectionHeight, item.section.smallMesh) val mesh = ChunkMeshes(renderer.context, item.position, item.section.smallMesh)
try { try {
solid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, neighbours.neighbours, sectionNeighbours, mesh) solid.mesh(item.position, item.chunk, item.section, neighbours.neighbours, sectionNeighbours, mesh)
if (item.section.blocks.hasFluid) { if (item.section.blocks.hasFluid) {
fluid.mesh(item.chunkPosition, item.sectionHeight, item.chunk, item.section, mesh) fluid.mesh(item.position, item.chunk, item.section, mesh)
} }
} catch (exception: Exception) { } catch (exception: Exception) {
mesh.unload() mesh.unload()

View File

@ -30,9 +30,9 @@ import de.bixilon.minosoft.data.text.formatting.color.Colors
import de.bixilon.minosoft.data.world.chunk.ChunkSection import de.bixilon.minosoft.data.world.chunk.ChunkSection
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition import de.bixilon.minosoft.data.world.positions.InChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMesh import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMesh
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
@ -62,7 +62,7 @@ class FluidSectionMesher(
// ToDo: Should this be combined with the solid renderer (but we'd need to render faces twice, because of cullface) // ToDo: Should this be combined with the solid renderer (but we'd need to render faces twice, because of cullface)
fun mesh(chunkPosition: ChunkPosition, sectionHeight: Int, chunk: Chunk, section: ChunkSection, mesh: ChunkMeshes) { fun mesh(sectionPosition: SectionPosition, chunk: Chunk, section: ChunkSection, mesh: ChunkMeshes) {
val blocks = section.blocks val blocks = section.blocks
var position = BlockPosition() var position = BlockPosition()
@ -70,9 +70,9 @@ class FluidSectionMesher(
val cameraOffset = context.camera.offset.offset val cameraOffset = context.camera.offset.offset
val offsetX = chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X val offsetX = sectionPosition.x * ProtocolDefinition.SECTION_WIDTH_X
val offsetY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y val offsetY = sectionPosition.y * ProtocolDefinition.SECTION_HEIGHT_Y
val offsetZ = chunkPosition.z * ProtocolDefinition.SECTION_WIDTH_Z val offsetZ = sectionPosition.z * ProtocolDefinition.SECTION_WIDTH_Z
for (y in blocks.minPosition.y..blocks.maxPosition.y) { for (y in blocks.minPosition.y..blocks.maxPosition.y) {
for (z in blocks.minPosition.z..blocks.maxPosition.z) { for (z in blocks.minPosition.z..blocks.maxPosition.z) {

View File

@ -32,9 +32,9 @@ import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.chunk.light.SectionLight import de.bixilon.minosoft.data.world.chunk.light.SectionLight
import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray import de.bixilon.minosoft.data.world.chunk.neighbours.ChunkNeighbourArray
import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.InChunkPosition import de.bixilon.minosoft.data.world.positions.InChunkPosition
import de.bixilon.minosoft.data.world.positions.InSectionPosition import de.bixilon.minosoft.data.world.positions.InSectionPosition
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.RenderContext import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer import de.bixilon.minosoft.gui.rendering.chunk.entities.BlockEntityRenderer
import de.bixilon.minosoft.gui.rendering.chunk.entities.renderer.RenderedBlockEntity import de.bixilon.minosoft.gui.rendering.chunk.entities.renderer.RenderedBlockEntity
@ -59,12 +59,12 @@ class SolidSectionMesher(
profile.light::ambientOcclusion.observe(this, true) { this.ambientOcclusion = it } profile.light::ambientOcclusion.observe(this, true) { this.ambientOcclusion = it }
} }
fun mesh(chunkPosition: ChunkPosition, sectionHeight: Int, chunk: Chunk, section: ChunkSection, neighbourChunks: ChunkNeighbourArray, neighbours: Array<ChunkSection?>, mesh: ChunkMeshes) { fun mesh(sectionPosition: SectionPosition, chunk: Chunk, section: ChunkSection, neighbourChunks: ChunkNeighbourArray, neighbours: Array<ChunkSection?>, mesh: ChunkMeshes) {
val random = if (profile.antiMoirePattern) Random(0L) else null val random = if (profile.antiMoirePattern) Random(0L) else null
val isLowestSection = sectionHeight == chunk.minSection val isLowestSection = sectionPosition.y == chunk.minSection
val isHighestSection = sectionHeight == chunk.maxSection val isHighestSection = sectionPosition.y == chunk.maxSection
val blocks = section.blocks val blocks = section.blocks
val entities: ArrayList<BlockEntityRenderer<*>> = ArrayList(section.blockEntities.count) val entities: ArrayList<BlockEntityRenderer<*>> = ArrayList(section.blockEntities.count)
@ -76,9 +76,9 @@ class SolidSectionMesher(
val cameraOffset = context.camera.offset.offset val cameraOffset = context.camera.offset.offset
val offsetX = chunkPosition.x * ProtocolDefinition.SECTION_WIDTH_X val offsetX = sectionPosition.x * ProtocolDefinition.SECTION_WIDTH_X
val offsetY = sectionHeight * ProtocolDefinition.SECTION_HEIGHT_Y val offsetY = sectionPosition.y * ProtocolDefinition.SECTION_HEIGHT_Y
val offsetZ = chunkPosition.z * ProtocolDefinition.SECTION_WIDTH_Z val offsetZ = sectionPosition.z * ProtocolDefinition.SECTION_WIDTH_Z
val floatOffset = FloatArray(3) val floatOffset = FloatArray(3)

View File

@ -17,6 +17,7 @@ import de.bixilon.kutil.concurrent.lock.RWLock
import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.chunk.chunk.Chunk
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight import de.bixilon.minosoft.data.world.positions.SectionHeight
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
import it.unimi.dsi.fastutil.ints.IntOpenHashSet import it.unimi.dsi.fastutil.ints.IntOpenHashSet
@ -99,7 +100,7 @@ class CulledQueue(
val heightIterator = sectionHeights.intIterator() val heightIterator = sectionHeights.intIterator()
for (sectionHeight in heightIterator) { for (sectionHeight in heightIterator) {
val section = chunk[sectionHeight] ?: continue val section = chunk[sectionHeight] ?: continue
if (!renderer.visibilityGraph.isSectionVisible(chunkPosition, sectionHeight, section.blocks.minPosition, section.blocks.maxPosition, false)) { if (!renderer.visibilityGraph.isSectionVisible(SectionPosition.Companion.of(chunkPosition, sectionHeight), section.blocks.minPosition, section.blocks.maxPosition, false)) {
continue continue
} }
list += Pair(chunk, sectionHeight) list += Pair(chunk, sectionHeight)

View File

@ -1,6 +1,6 @@
/* /*
* Minosoft * 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. * 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,30 +13,28 @@
package de.bixilon.minosoft.gui.rendering.chunk.queue package de.bixilon.minosoft.gui.rendering.chunk.queue
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.chunk.WorldQueueItem import de.bixilon.minosoft.gui.rendering.chunk.WorldQueueItem
import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes import de.bixilon.minosoft.gui.rendering.chunk.mesh.ChunkMeshes
import java.util.*
class QueuePosition( class QueuePosition(
val position: ChunkPosition, val position: SectionPosition,
val sectionHeight: Int,
) { ) {
constructor(mesh: ChunkMeshes) : this(mesh.chunkPosition, mesh.sectionHeight) constructor(mesh: ChunkMeshes) : this(mesh.position)
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is WorldQueueItem) { if (other is WorldQueueItem) {
return position == other.chunkPosition && sectionHeight == other.sectionHeight return position == other.position
} }
if (other is QueuePosition) { if (other is QueuePosition) {
return position == other.position && sectionHeight == other.sectionHeight return position == other.position
} }
return false return false
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return Objects.hash(position, sectionHeight) return position.hashCode()
} }
} }

View File

@ -58,18 +58,18 @@ class MeshLoadingQueue(
mesh.load() mesh.load()
if (position != mesh.chunkPosition) { if (position != mesh.position.chunkPosition) {
meshes = renderer.loaded.meshes.getOrPut(mesh.chunkPosition) { Int2ObjectOpenHashMap() } meshes = renderer.loaded.meshes.getOrPut(mesh.position.chunkPosition) { Int2ObjectOpenHashMap() }
position = mesh.chunkPosition position = mesh.position.chunkPosition
} }
meshes.put(mesh.sectionHeight, mesh)?.let { meshes.put(mesh.position.y, mesh)?.let {
renderer.visible.removeMesh(it) renderer.visible.removeMesh(it)
it.unload() it.unload()
} }
val visible = renderer.visibilityGraph.isSectionVisible(mesh.chunkPosition, mesh.sectionHeight, mesh.minPosition, mesh.maxPosition, true) val visible = renderer.visibilityGraph.isSectionVisible(mesh.position, mesh.minPosition, mesh.maxPosition, true)
if (visible) { if (visible) {
count++ count++
renderer.visible.addMesh(mesh) renderer.visible.addMesh(mesh)
@ -91,7 +91,7 @@ class MeshLoadingQueue(
// already inside, remove // already inside, remove
meshes.remove(mesh) meshes.remove(mesh)
} }
if (mesh.chunkPosition == renderer.cameraChunkPosition) { if (mesh.position.chunkPosition == renderer.cameraSectionPosition.chunkPosition) {
// still higher priority // still higher priority
meshes.add(0, mesh) meshes.add(0, mesh)
} else { } else {
@ -104,20 +104,20 @@ class MeshLoadingQueue(
if (lock) lock() if (lock) lock()
val positions: MutableSet<QueuePosition> = mutableSetOf() val positions: MutableSet<QueuePosition> = mutableSetOf()
this.positions.removeAll { this.positions.removeAll {
if (it.position != position) { if (it.position.chunkPosition != position) {
return@removeAll false return@removeAll false
} }
positions += it positions += it
return@removeAll true return@removeAll true
} }
this.meshes.removeAll { QueuePosition(it.chunkPosition, it.sectionHeight) in positions } this.meshes.removeAll { QueuePosition(it.position) in positions }
if (lock) unlock() if (lock) unlock()
} }
fun abort(position: QueuePosition, lock: Boolean = true) { fun abort(position: QueuePosition, lock: Boolean = true) {
if (lock) lock() if (lock) lock()
if (this.positions.remove(position)) { if (this.positions.remove(position)) {
this.meshes.removeAll { it.chunkPosition == position.position && it.sectionHeight == position.sectionHeight } this.meshes.removeAll { it.position == position.position }
} }
if (lock) unlock() if (lock) unlock()
} }
@ -128,7 +128,7 @@ class MeshLoadingQueue(
if (lock) lock() if (lock) lock()
this.positions.removeAll { this.positions.removeAll {
if (renderer.visibilityGraph.isChunkVisible(it.position)) { if (renderer.visibilityGraph.isChunkVisible(it.position.chunkPosition)) {
return@removeAll false return@removeAll false
} }
remove += it remove += it

View File

@ -60,7 +60,7 @@ class MeshUnloadingQueue(
fun forceQueue(mesh: ChunkMeshes, lock: Boolean = true) { fun forceQueue(mesh: ChunkMeshes, lock: Boolean = true) {
if (lock) lock() if (lock) lock()
if (mesh.chunkPosition == renderer.session.camera.entity.physics.positionInfo.chunkPosition) { if (mesh.position.chunkPosition == renderer.session.camera.entity.physics.positionInfo.chunkPosition) {
this.meshes.add(0, mesh) this.meshes.add(0, mesh)
} else { } else {
this.meshes += mesh this.meshes += mesh

View File

@ -72,11 +72,11 @@ class ChunkMeshingQueue(
items += item items += item
} }
unlock() unlock()
val camera = renderer.cameraChunkPosition val camera = renderer.cameraSectionPosition
for (item in items) { for (item in items) {
val distance = abs(item.chunkPosition.x - camera.x) + abs(item.chunkPosition.z - camera.z) val distance = abs(item.position.x - camera.x) + abs(item.position.z - camera.z) // TODO: Check y?
val runnable = HeavyPoolRunnable(if (distance < 1) ThreadPool.HIGH else ThreadPool.LOW, interruptable = true) val runnable = HeavyPoolRunnable(if (distance < 1) ThreadPool.HIGH else ThreadPool.LOW, interruptable = true)
val task = MeshPrepareTask(item.chunkPosition, item.sectionHeight, runnable) val task = MeshPrepareTask(item.position, runnable)
task.runnable.runnable = Runnable { renderer.mesher.tryMesh(item, task, task.runnable) } task.runnable.runnable = Runnable { renderer.mesher.tryMesh(item, task, task.runnable) }
tasks += task tasks += task
} }
@ -87,7 +87,7 @@ class ChunkMeshingQueue(
fun remove(chunkPosition: ChunkPosition) { fun remove(chunkPosition: ChunkPosition) {
val remove: MutableSet<WorldQueueItem> = mutableSetOf() val remove: MutableSet<WorldQueueItem> = mutableSetOf()
queue.removeAll { queue.removeAll {
if (it.chunkPosition != chunkPosition) { if (it.position.chunkPosition != chunkPosition) {
return@removeAll false return@removeAll false
} }
remove += it remove += it
@ -100,7 +100,7 @@ class ChunkMeshingQueue(
if (lock) lock() if (lock) lock()
val remove: MutableSet<WorldQueueItem> = mutableSetOf() val remove: MutableSet<WorldQueueItem> = mutableSetOf()
queue.removeAll { queue.removeAll {
if (renderer.visibilityGraph.isChunkVisible(it.chunkPosition)) { if (renderer.visibilityGraph.isChunkVisible(it.position.chunkPosition)) {
return@removeAll false return@removeAll false
} }
remove += it remove += it
@ -153,7 +153,7 @@ class ChunkMeshingQueue(
if (set.remove(item)) { if (set.remove(item)) {
queue -= item queue -= item
} }
if (item.chunkPosition == renderer.cameraChunkPosition) { if (item.position.chunkPosition == renderer.cameraSectionPosition.chunkPosition) {
queue.add(0, item) queue.add(0, item)
} else { } else {
queue += item queue += item

View File

@ -13,31 +13,26 @@
package de.bixilon.minosoft.gui.rendering.chunk.queue.meshing package de.bixilon.minosoft.gui.rendering.chunk.queue.meshing
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
import de.bixilon.minosoft.gui.rendering.chunk.WorldQueueItem import de.bixilon.minosoft.gui.rendering.chunk.WorldQueueItem
class ChunkQueueComparator : Comparator<WorldQueueItem> { class ChunkQueueComparator : Comparator<WorldQueueItem> {
private var sort = 1 private var sort = 1
private var position: ChunkPosition = ChunkPosition.EMPTY private var position = SectionPosition()
private var height = 0
fun update(renderer: ChunkRenderer) { fun update(renderer: ChunkRenderer) {
if (this.position == renderer.cameraChunkPosition && this.height == renderer.cameraSectionHeight) return if (this.position == renderer.cameraSectionPosition) return
this.position = renderer.cameraChunkPosition this.position = renderer.cameraSectionPosition
this.height = renderer.cameraSectionHeight
sort++ sort++
} }
private fun getDistance(item: WorldQueueItem): Int { private fun getDistance(item: WorldQueueItem): Int {
if (item.sort == this.sort) return item.distance if (item.sort == this.sort) return item.distance
val array = item.sectionPosition.array val position = item.position
val x = array[0] - position.x val distance = (position - this.position).length2()
val y = array[1] - height
val z = array[2] - position.z
val distance = (x * x + y * y + z * z)
item.distance = distance item.distance = distance
item.sort = sort item.sort = sort

View File

@ -14,10 +14,9 @@
package de.bixilon.minosoft.gui.rendering.chunk.queue.meshing.tasks package de.bixilon.minosoft.gui.rendering.chunk.queue.meshing.tasks
import de.bixilon.kutil.concurrent.pool.runnable.HeavyPoolRunnable import de.bixilon.kutil.concurrent.pool.runnable.HeavyPoolRunnable
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.SectionPosition
class MeshPrepareTask( class MeshPrepareTask(
val chunkPosition: ChunkPosition, val position: SectionPosition,
val sectionHeight: Int,
val runnable: HeavyPoolRunnable, val runnable: HeavyPoolRunnable,
) )

View File

@ -16,7 +16,7 @@ package de.bixilon.minosoft.gui.rendering.chunk.queue.meshing.tasks
import de.bixilon.kutil.concurrent.lock.RWLock import de.bixilon.kutil.concurrent.lock.RWLock
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.minosoft.data.world.positions.ChunkPosition import de.bixilon.minosoft.data.world.positions.ChunkPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
class MeshPrepareTaskManager( class MeshPrepareTaskManager(
@ -58,17 +58,17 @@ class MeshPrepareTaskManager(
fun interrupt(position: ChunkPosition) { fun interrupt(position: ChunkPosition) {
lock.acquire() lock.acquire()
for (task in tasks) { for (task in tasks) {
if (task.chunkPosition == position) { if (task.position.chunkPosition == position) {
task.runnable.interrupt() task.runnable.interrupt()
} }
} }
lock.release() lock.release()
} }
fun interrupt(position: ChunkPosition, height: SectionHeight) { fun interrupt(position: SectionPosition) {
lock.acquire() lock.acquire()
for (task in tasks) { for (task in tasks) {
if (task.chunkPosition == position && task.sectionHeight == height) { if (task.position == position) {
task.runnable.interrupt() task.runnable.interrupt()
} }
} }
@ -79,7 +79,7 @@ class MeshPrepareTaskManager(
fun cleanup() { fun cleanup() {
lock.acquire() lock.acquire()
for (task in tasks) { for (task in tasks) {
if (!renderer.visibilityGraph.isChunkVisible(task.chunkPosition)) { if (!renderer.visibilityGraph.isChunkVisible(task.position.chunkPosition)) {
task.runnable.interrupt() task.runnable.interrupt()
} }
} }

View File

@ -20,6 +20,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.Chunk
import de.bixilon.minosoft.data.world.positions.BlockPosition import de.bixilon.minosoft.data.world.positions.BlockPosition
import de.bixilon.minosoft.data.world.positions.SectionHeight import de.bixilon.minosoft.data.world.positions.SectionHeight
import de.bixilon.minosoft.data.world.positions.SectionPosition
import de.bixilon.minosoft.gui.rendering.RenderingStates import de.bixilon.minosoft.gui.rendering.RenderingStates
import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer import de.bixilon.minosoft.gui.rendering.chunk.ChunkRenderer
import de.bixilon.minosoft.gui.rendering.chunk.WorldQueueItem import de.bixilon.minosoft.gui.rendering.chunk.WorldQueueItem
@ -32,15 +33,16 @@ class ChunkQueueMaster(
) { ) {
private fun queue(section: ChunkSection, chunk: Chunk, force: Boolean): Boolean { private fun queue(section: ChunkSection, chunk: Chunk, force: Boolean): Boolean {
val position = SectionPosition.of(chunk.position, section.height)
if (section.blocks.isEmpty) { if (section.blocks.isEmpty) {
renderer.unload(QueuePosition(chunk.position, section.height)) renderer.unload(QueuePosition(position))
return false return false
} }
val visible = force || renderer.visibilityGraph.isSectionVisible(chunk.position, section.height, section.blocks.minPosition, section.blocks.maxPosition, true) val visible = force || renderer.visibilityGraph.isSectionVisible(position, section.blocks.minPosition, section.blocks.maxPosition, true)
if (visible) { if (visible) {
val center = CHUNK_CENTER + BlockPosition.of(chunk.position, section.height) val center = CHUNK_CENTER + BlockPosition.of(chunk.position, section.height)
val item = WorldQueueItem(chunk.position, section.height, chunk, section, center) val item = WorldQueueItem(position, chunk, section, center)
renderer.meshingQueue.queue(item) renderer.meshingQueue.queue(item)
return true return true
} }

View File

@ -84,8 +84,8 @@ class BlockPositionTest {
@Test @Test
fun `correct negative y large`() { fun `correct negative y large`() {
val position = BlockPosition(-2048, 0xF, 0xF) val position = BlockPosition(1234, -2048, 0xF)
assertEquals(position.x, -2048) assertEquals(position.y, -2048)
} }
@Test @Test

View File

@ -0,0 +1,179 @@
/*
* 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 SectionPositionTest {
@Test
fun `init correct min`() {
val position = SectionPosition(-1875000, -128, -1875000)
}
@Test
fun `init correct max`() {
val position = SectionPosition(1875000, 127, 1875000)
}
@Test
fun `init badly`() {
assertThrows<AssertionError> { SectionPosition(-1875001, 128, -1875001) }
}
@Test
fun `correct positive x`() {
val position = SectionPosition(2, 0xF, 0xF)
assertEquals(position.x, 2)
}
@Test
fun `correct positive x large`() {
val position = SectionPosition(1875000, 0xF, 0xF)
assertEquals(position.x, 1875000)
}
@Test
fun `correct negative x`() {
val position = SectionPosition(-2, 0xF, 0xF)
assertEquals(position.x, -2)
}
@Test
fun `correct negative x large`() {
val position = SectionPosition(-1875000, 0xF, 0xF)
assertEquals(position.x, -1875000)
}
@Test
fun `correct plus x`() {
val position = SectionPosition(2, 0xF, 0xF)
assertEquals(position.plusX().x, 3)
}
@Test
fun `correct plus 2 x`() {
val position = SectionPosition(2, 0xF, 0xF)
assertEquals(position.plusX(2).x, 4)
}
@Test
fun `correct minus x`() {
val position = SectionPosition(2, 0xF, 0xF)
assertEquals(position.minusX().x, 1)
}
@Test
fun `correct negative y`() {
val position = SectionPosition(0xF, -4, 0xF)
assertEquals(position.y, -4)
}
@Test
fun `correct negative y large`() {
val position = SectionPosition(123, -128, 0xF)
assertEquals(position.y, -128)
}
@Test
fun `correct positive y`() {
val position = SectionPosition(0xF, 100, 0xF)
assertEquals(position.y, 100)
}
@Test
fun `correct positive y large`() {
val position = SectionPosition(0xF, 127, 0xF)
assertEquals(position.y, 127)
}
@Test
fun `correct plus y`() {
val position = SectionPosition(0xF, 2, 0xF)
assertEquals(position.plusY().y, 3)
}
@Test
fun `correct plus 2 y`() {
val position = SectionPosition(0xF, 2, 0xF)
assertEquals(position.plusY(2).y, 4)
}
@Test
fun `correct minus y`() {
val position = SectionPosition(0xF, 2, 0xF)
assertEquals(position.minusY().y, 1)
}
@Test
fun `correct positive z`() {
val position = SectionPosition(0xF, 0xF, 4)
assertEquals(position.z, 4)
}
@Test
fun `correct positive z large`() {
val position = SectionPosition(0, 0, 1875000)
assertEquals(position.z, 1875000)
}
@Test
fun `correct negative z`() {
val position = SectionPosition(0xF, 0xF, -4)
assertEquals(position.z, -4)
}
@Test
fun `correct negative z large`() {
val position = SectionPosition(0, 0, -1875000)
assertEquals(position.z, -1875000)
}
@Test
fun `correct plus z`() {
val position = SectionPosition(0xF, 0xF, 2)
assertEquals(position.plusZ().z, 3)
}
@Test
fun `correct plus 2 z`() {
val position = SectionPosition(0xF, 0xF, 2)
assertEquals(position.plusZ(2).z, 4)
}
@Test
fun `correct minus z`() {
val position = SectionPosition(0xF, 0xF, 2)
assertEquals(position.minusZ().z, 1)
}
@Test
fun `unary minus`() {
val position = -SectionPosition(2, 2, 2)
assertEquals(position.x, -2)
assertEquals(position.y, -2)
assertEquals(position.z, -2)
}
@Test
fun `unary plus`() {
val position = +SectionPosition(2, 2, 2)
assertEquals(position.x, 2)
assertEquals(position.y, 2)
assertEquals(position.z, 2)
}
}