diff --git a/src/net/torvald/aa/KDHeapifiedTree.kt b/src/net/torvald/aa/KDHeapifiedTree.kt index 799c83fb6..d9190e069 100644 --- a/src/net/torvald/aa/KDHeapifiedTree.kt +++ b/src/net/torvald/aa/KDHeapifiedTree.kt @@ -17,7 +17,7 @@ import net.torvald.terrarum.sqr * Remarks: * - NOT using the fullCodePage with 2x2 mode makes it slower... skewed tree generation? */ -class KDHeapifiedTree() { +class KDHeapifiedTree(actors: List) { private val dimension = 2 private val initialSize = 128 @@ -25,8 +25,8 @@ class KDHeapifiedTree() { private val root: Int = 0 - fun findNearest(query: Point2d) = - getNearest(root, query, 0).get()!! + fun findNearestActor(query: Point2d): ActorWBMovable = + getNearest(root, query, 0).getActor()!! private fun Int.get() = nodes[this]?.feetPosPoint private fun Int.getActor() = nodes[this] @@ -38,6 +38,10 @@ class KDHeapifiedTree() { private val zeroPoint = Point2d(0.0, 0.0) + init { + create(actors, 0, 0) + } + private fun create(points: List, depth: Int, index: Int): ActorWBMovable? { if (points.isEmpty()) { index.set(null) @@ -101,38 +105,3 @@ class KDHeapifiedTree() { private operator fun Point2d.get(index: Int) = if (index == 0) this.x else this.y } - -fun intLog2(number: Int): Int { - var number = number - if (number == 0) return 0 - var log = 0 - if (number and 0xffff0000.toInt() != 0) { - number = number ushr 16 - log = 16 - } - if (number >= 256) { - number = number ushr 8 - log += 8 - } - if (number >= 16) { - number = number ushr 4 - log += 4 - } - if (number >= 4) { - number = number ushr 2 - log += 2 - } - return log + number.ushr(1) -} - -fun nextPowerOfTwo(number: Int): Int { - var number = number - 1 - number = number or (number shr 1) - number = number or (number shr 2) - number = number or (number shr 4) - number = number or (number shr 8) - number = number or (number shr 16) - number++ - number += if (number == 0) 1 else 0 - return number -} \ No newline at end of file diff --git a/src/net/torvald/aa/KDTree.kt b/src/net/torvald/aa/KDTree.kt new file mode 100644 index 000000000..bd9372376 --- /dev/null +++ b/src/net/torvald/aa/KDTree.kt @@ -0,0 +1,77 @@ +package net.torvald.aa + +/** + * Created by minjaesong on 2019-04-18. + */ +/*class KDTree(points: List) { + + companion object { + const val DIMENSION = 2 + } + + private val root: KDNode? + + init { + root = create(points, 0) + } + + fun findNearest(query: ActorWithBody) = getNearest(root!!, query, 0) + + private fun create(points: List, depth: Int): KDNode? { + if (points.isEmpty()) { + return null + } + else { + val items = points.sortedBy { it.getDimensionalPoint(depth) } + val currentIndex = items.size shr 1 + + return KDNode( + create(items.subList(0, currentIndex), depth + 1), + create(items.subList(currentIndex + 1, items.size), depth + 1), + items[currentIndex], + items[currentIndex].hitbox.clone() + ) + } + } + + private fun getNearest(currentNode: KDNode, query: ActorWithBody, depth: Int = 0): KDNode { + val direction = currentNode.compare(query, depth % DIMENSION) + + val next = if (direction < 0) currentNode.left else currentNode.right + val other = if (direction < 0) currentNode.right else currentNode.left + var best = if (next == null) currentNode else getNearest(next, query, depth + 1) // traverse to leaf + + if (currentNode.position.distSqr(query) < best.position.distSqr(query)) { + best = currentNode + } + + if (other != null) { + if (currentNode.position.dimDistSqr(query, depth % DIMENSION) < best.position.distSqr(query)) { + val bestCandidate = getNearest(other, query, depth + 1) + if (bestCandidate.position.distSqr(query) < best.position.distSqr(query)) { + best = bestCandidate + } + } + } + + return best // work back up + } + + data class KDNode(val left: KDNode?, val right: KDNode?, val actor: ActorWithBody, val position: Hitbox) { + fun compare(other: ActorWithBody, dimension: Int) = other.getDimensionalPoint(dimension) - this.position.getDimensionalPoint(dimension) + + } + + private fun Hitbox.distSqr(other: Hitbox) { + var dist = 0.0 + for (i in 0 until DIMENSION) + dist += (this.getDimensionalPoint(i) - other.getDimensionalPoint(i)).sqr() + } + + private fun Hitbox.dimDistSqr(other: Hitbox, dimension: Int) = other.getDimensionalPoint(dimension).minus(this.getDimensionalPoint(dimension)).sqr() +} + +internal fun ActorWithBody.getDimensionalPoint(depth: Int) = this.hitbox.getDimensionalPoint(depth) +// TODO take ROUNDWORLD into account +internal fun Hitbox.getDimensionalPoint(depth: Int) = + if (depth % KDTree.DIMENSION == 0) this.canonicalX else this.canonicalY*/ \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/Ingame.kt b/src/net/torvald/terrarum/modulebasegame/Ingame.kt index c64bf0867..4cbdf7da3 100644 --- a/src/net/torvald/terrarum/modulebasegame/Ingame.kt +++ b/src/net/torvald/terrarum/modulebasegame/Ingame.kt @@ -481,6 +481,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) { if (!paused) { + WorldSimulator.resetForThisFrame() + /////////////////////////// // world-related updates // /////////////////////////// diff --git a/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt b/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt index 85e008509..8f2e35a96 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameworld/WorldSimulator.kt @@ -1,10 +1,12 @@ package net.torvald.terrarum.modulebasegame.gameworld import com.badlogic.gdx.graphics.Color +import net.torvald.aa.KDHeapifiedTree import net.torvald.terrarum.AppLoader import net.torvald.terrarum.Terrarum import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.Fluid +import net.torvald.terrarum.gameactors.ActorWBMovable import net.torvald.terrarum.gameworld.FluidType import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.roundInt @@ -47,16 +49,25 @@ object WorldSimulator { val colourNone = Color(0x808080FF.toInt()) val colourWater = Color(0x66BBFFFF.toInt()) - private val world = (Terrarum.ingame!!.world) + private val ingame = Terrarum.ingame!! + private val world = ingame.world + private var actorsKDTree: KDHeapifiedTree? = null + fun resetForThisFrame() { + actorsKDTree = null + } + + operator fun invoke(player: ActorHumanoid?, delta: Float) { + // build the kdtree that will be used during a single frame of updating + if (actorsKDTree == null) + actorsKDTree = KDHeapifiedTree(ingame.actorContainerActive.filter { it is ActorWBMovable }) - operator fun invoke(p: ActorHumanoid?, delta: Float) { //printdbg(this, "============================") - if (p != null) { - updateXFrom = p.hitbox.centeredX.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() - updateYFrom = p.hitbox.centeredY.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() + if (player != null) { + updateXFrom = player.hitbox.centeredX.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() + updateYFrom = player.hitbox.centeredY.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() updateXTo = updateXFrom + DOUBLE_RADIUS updateYTo = updateYFrom + DOUBLE_RADIUS } diff --git a/src/net/torvald/util/SortedArrayList.kt b/src/net/torvald/util/SortedArrayList.kt index 28a8df196..91e0537cc 100644 --- a/src/net/torvald/util/SortedArrayList.kt +++ b/src/net/torvald/util/SortedArrayList.kt @@ -46,7 +46,15 @@ class SortedArrayList>(initialSize: Int = 10) { fun iterator() = arrayList.iterator() fun forEach(action: (T) -> Unit) = arrayList.forEach(action) fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action) - //fun map(transformation: (T) -> R) = arrayList.map(transformation) + + + fun map(transformation: (T) -> R) = arrayList.map(transformation) + + fun filter(function: (T) -> Boolean): List { + val retList = ArrayList() // sorted-ness is preserved + this.arrayList.forEach { if (function(it)) retList.add(it as R) } + return retList + } /** * Select one unsorted element from the array and put it onto the sorted spot.