From a21b1ea4c9eed06cb5cc576f0b15eacaa955c51c Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Mon, 31 Mar 2025 15:10:25 +0200 Subject: [PATCH] aabb iterator: don't use IntRange, reduce more allocations --- .../registries/shapes/aabb/AABBIterator.kt | 40 ++++++++--------- .../minosoft/gui/rendering/util/VecUtil.kt | 8 +++- .../gui/rendering/util/vec/vec3/Vec3dUtil.kt | 7 ++- .../protocol/buffers/play/PlayInByteBuffer.kt | 3 +- .../shapes/aabb/AABBIteratorTest.kt | 45 ++++++++++++++++--- 5 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIterator.kt b/src/main/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIterator.kt index 0c2980427..3a45f184e 100644 --- a/src/main/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIterator.kt +++ b/src/main/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIterator.kt @@ -17,44 +17,44 @@ import de.bixilon.minosoft.data.world.World import de.bixilon.minosoft.data.world.chunk.chunk.Chunk import de.bixilon.minosoft.data.world.iterator.WorldIterator import de.bixilon.minosoft.data.world.positions.BlockPosition +import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.ceil +import de.bixilon.minosoft.gui.rendering.util.vec.vec3.Vec3dUtil.floor class AABBIterator( - private val rangeX: IntRange, - private val rangeY: IntRange, - private val rangeZ: IntRange, + val min: BlockPosition, + val max: BlockPosition, ) : Iterator { private var count = 0 - private var x = rangeX.first - private var y = rangeY.first - private var z = rangeZ.first + private var current = min + val size = maxOf(0, max.x - min.x + 1) * maxOf(0, max.y - min.y + 1) * maxOf(0, max.z - min.z + 1) - val size: Int = maxOf(0, rangeX.last - rangeX.first + 1) * maxOf(0, rangeY.last - rangeY.first + 1) * maxOf(0, rangeZ.last - rangeZ.first + 1) - - constructor(aabb: AABB) : this(AABB.getRange(aabb.min.x, aabb.max.x), AABB.getRange(aabb.min.y, aabb.max.y), AABB.getRange(aabb.min.z, aabb.max.z)) - constructor(min: BlockPosition, max: BlockPosition) : this(min.x..max.x, min.y..max.y, min.z..max.z) - constructor(minX: Int, minY: Int, minZ: Int, maxX: Int, maxY: Int, maxZ: Int) : this(minX..maxX, minY..maxY, minZ..maxZ) - + constructor(aabb: AABB) : this(aabb.min.floor, aabb.max.ceil - 1) + constructor(minX: Int, minY: Int, minZ: Int, maxX: Int, maxY: Int, maxZ: Int) : this(BlockPosition(minX, minY, minZ), BlockPosition(maxX, maxY, maxZ)) override fun hasNext(): Boolean { return count < size } override fun next(): BlockPosition { - if (count >= size) throw IllegalStateException("No positions available anymore!") + if (!hasNext()) throw IllegalStateException("No positions available anymore!") + val current = current + var next = current - val position = BlockPosition(x, y, z) - if (z < rangeZ.last) z++ else { - z = rangeZ.first - if (y < rangeY.last) y++ else { - y = rangeY.first - if (x < rangeX.last) x++ + if (next.x < max.x) next = next.plusX() else { + next = next.with(x = min.x) + if (next.z < max.z) next = next.plusZ() else { + next = next.with(z = min.z) + + if (next.y < max.y) next = next.plusY() } } + this.current = next + count++ - return position + return current } fun blocks(world: World, chunk: Chunk? = null): WorldIterator { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt index 2c9aaed13..a3671b259 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/VecUtil.kt @@ -159,14 +159,17 @@ object VecUtil { return (((axisHash and 0xF) / 15.0f) - 0.5f) / 2.0f } - return Vec3( + val offset = Vec3( x = horizontal(positionHash), y = if (offsetType === RandomOffsetTypes.XYZ) { (((positionHash shr 4 and 0xF) / 15.0f) - 1.0f) / 5.0f } else { 0.0f }, - z = horizontal(positionHash shr 8)).clamp(-maxModelOffset, maxModelOffset) + z = horizontal(positionHash shr 8)) + offset.clampAssign(-maxModelOffset, maxModelOffset) + + return offset } fun Vec3.clampAssign(min: Float, max: Float) { @@ -174,6 +177,7 @@ object VecUtil { this.y = y.clamp(min, max) this.z = z.clamp(min, max) } + fun Vec3.clamp(min: Float, max: Float): Vec3 { return Vec3( x = x.clamp(min, max), diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt index 68f5899e3..3b13dd4c4 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/vec/vec3/Vec3dUtil.kt @@ -45,8 +45,11 @@ object Vec3dUtil { val Vec3d.Companion.MAX: Vec3d get() = Vec3d(Double.MAX_VALUE, Double.MAX_VALUE, Double.MAX_VALUE) - val Vec3d.floor: Vec3i - get() = Vec3i(this.x.floor, this.y.floor, this.z.floor) + val Vec3d.floor: BlockPosition + get() = BlockPosition(this.x.floor, this.y.floor, this.z.floor) + + val Vec3d.ceil: BlockPosition + get() = BlockPosition(this.x.ceil, this.y.ceil, this.z.ceil) val Vec3d.blockPosition: BlockPosition get() = BlockPosition(this.x.floor, this.y.floor, this.z.floor) diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/buffers/play/PlayInByteBuffer.kt b/src/main/java/de/bixilon/minosoft/protocol/protocol/buffers/play/PlayInByteBuffer.kt index 85cdee34c..202c2c455 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/buffers/play/PlayInByteBuffer.kt +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/buffers/play/PlayInByteBuffer.kt @@ -41,6 +41,7 @@ import de.bixilon.minosoft.protocol.PlayerPublicKey import de.bixilon.minosoft.protocol.network.session.play.PlaySession import de.bixilon.minosoft.protocol.packets.s2c.play.sound.PlayedSound import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition.VELOCITY_NETWORK_DIVIDER import de.bixilon.minosoft.protocol.protocol.ProtocolVersions import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W04A import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_14W21A @@ -378,7 +379,7 @@ class PlayInByteBuffer : InByteBuffer { } fun readVelocity(): Vec3d { - return Vec3d(readShort(), readShort(), readShort()) * (1.0f / ProtocolDefinition.VELOCITY_NETWORK_DIVIDER) + return Vec3d(readShort() * (1.0 / VELOCITY_NETWORK_DIVIDER), readShort() * (1.0 / VELOCITY_NETWORK_DIVIDER), readShort() * (1.0 / VELOCITY_NETWORK_DIVIDER)) } fun readVibrationSource(): VibrationSource { diff --git a/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt b/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt index 05d84fa66..c175a1017 100644 --- a/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/registries/shapes/aabb/AABBIteratorTest.kt @@ -27,7 +27,39 @@ class AABBIteratorTest { } @Test - fun singleBlock() { + fun `correct min and max`() { + val iterator = AABBIterator(1, 2, 3, 4, 5, 6) + assertEquals(iterator.min, BlockPosition(1, 2, 3)) + assertEquals(iterator.max, BlockPosition(4, 5, 6)) + } + + @Test + fun `correct floor and ceil not`() { + val iterator = AABB(1.0, 2.0, 3.0, 4.0, 5.0, 6.0).positions() + assertEquals(iterator.min, BlockPosition(1, 2, 3)) + assertEquals(iterator.max, BlockPosition(3, 4, 5)) + } + + @Test + fun `correct floor and ceil`() { + val iterator = AABB(1.2, 2.2, 3.3, 4.5, 5.7, 6.4).positions() + assertEquals(iterator.min, BlockPosition(1, 2, 3)) + assertEquals(iterator.max, BlockPosition(4, 5, 6)) + } + + @Test + fun `less than single block`() { + val aabb = AABB(0.0, 0.0, 0.0, 0.1, 0.1, 0.1) + + val positions = aabb.positions() + assertEquals(1, positions.size) + assertTrue(positions.hasNext()) + assertEquals(BlockPosition(0, 0, 0), positions.next()) + assertFalse(positions.hasNext()) + } + + @Test + fun `exactly one block`() { val aabb = AABB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0) val positions = aabb.positions() @@ -43,14 +75,14 @@ class AABBIteratorTest { val positions = aabb.positions() assertEquals(24, positions.size) - val set: MutableSet = mutableSetOf() + val set: MutableSet = hashSetOf() for (position in positions) { set += position } assertEquals(24, set.size) - assertEquals(setOf( + assertEquals(hashSetOf( BlockPosition(0, 0, 0), BlockPosition(0, 0, 1), BlockPosition(0, 0, 2), @@ -108,9 +140,10 @@ class AABBIteratorTest { val positions = aabb.positions() assertEquals(64, positions.size) - for (x in -2 until 2) { - for (y in -2 until 2) { - for (z in -2 until 2) { + + for (y in -2 until 2) { + for (z in -2 until 2) { + for (x in -2 until 2) { assertEquals(BlockPosition(x, y, z), positions.next()) } }