actually using (and rediscovering) the kdtree

This commit is contained in:
minjaesong
2019-04-18 23:42:54 +09:00
parent 62b687c86b
commit 5a95f1c21a
5 changed files with 111 additions and 44 deletions

View File

@@ -17,7 +17,7 @@ import net.torvald.terrarum.sqr
* Remarks: * Remarks:
* - NOT using the fullCodePage with 2x2 mode makes it slower... skewed tree generation? * - NOT using the fullCodePage with 2x2 mode makes it slower... skewed tree generation?
*/ */
class KDHeapifiedTree() { class KDHeapifiedTree(actors: List<ActorWBMovable>) {
private val dimension = 2 private val dimension = 2
private val initialSize = 128 private val initialSize = 128
@@ -25,8 +25,8 @@ class KDHeapifiedTree() {
private val root: Int = 0 private val root: Int = 0
fun findNearest(query: Point2d) = fun findNearestActor(query: Point2d): ActorWBMovable =
getNearest(root, query, 0).get()!! getNearest(root, query, 0).getActor()!!
private fun Int.get() = nodes[this]?.feetPosPoint private fun Int.get() = nodes[this]?.feetPosPoint
private fun Int.getActor() = nodes[this] private fun Int.getActor() = nodes[this]
@@ -38,6 +38,10 @@ class KDHeapifiedTree() {
private val zeroPoint = Point2d(0.0, 0.0) private val zeroPoint = Point2d(0.0, 0.0)
init {
create(actors, 0, 0)
}
private fun create(points: List<ActorWBMovable?>, depth: Int, index: Int): ActorWBMovable? { private fun create(points: List<ActorWBMovable?>, depth: Int, index: Int): ActorWBMovable? {
if (points.isEmpty()) { if (points.isEmpty()) {
index.set(null) index.set(null)
@@ -101,38 +105,3 @@ class KDHeapifiedTree() {
private operator fun Point2d.get(index: Int) = if (index == 0) this.x else this.y 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
}

View File

@@ -0,0 +1,77 @@
package net.torvald.aa
/**
* Created by minjaesong on 2019-04-18.
*/
/*class KDTree(points: List<ActorWithBody>) {
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<ActorWithBody>, 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*/

View File

@@ -481,6 +481,8 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
if (!paused) { if (!paused) {
WorldSimulator.resetForThisFrame()
/////////////////////////// ///////////////////////////
// world-related updates // // world-related updates //
/////////////////////////// ///////////////////////////

View File

@@ -1,10 +1,12 @@
package net.torvald.terrarum.modulebasegame.gameworld package net.torvald.terrarum.modulebasegame.gameworld
import com.badlogic.gdx.graphics.Color import com.badlogic.gdx.graphics.Color
import net.torvald.aa.KDHeapifiedTree
import net.torvald.terrarum.AppLoader import net.torvald.terrarum.AppLoader
import net.torvald.terrarum.Terrarum import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.blockproperties.BlockCodex import net.torvald.terrarum.blockproperties.BlockCodex
import net.torvald.terrarum.blockproperties.Fluid import net.torvald.terrarum.blockproperties.Fluid
import net.torvald.terrarum.gameactors.ActorWBMovable
import net.torvald.terrarum.gameworld.FluidType import net.torvald.terrarum.gameworld.FluidType
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.roundInt import net.torvald.terrarum.roundInt
@@ -47,16 +49,25 @@ object WorldSimulator {
val colourNone = Color(0x808080FF.toInt()) val colourNone = Color(0x808080FF.toInt())
val colourWater = Color(0x66BBFFFF.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, "============================") //printdbg(this, "============================")
if (p != null) { if (player != null) {
updateXFrom = p.hitbox.centeredX.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt() updateXFrom = player.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() updateYFrom = player.hitbox.centeredY.div(CreateTileAtlas.TILE_SIZE).minus(FLUID_UPDATING_SQUARE_RADIUS).roundInt()
updateXTo = updateXFrom + DOUBLE_RADIUS updateXTo = updateXFrom + DOUBLE_RADIUS
updateYTo = updateYFrom + DOUBLE_RADIUS updateYTo = updateYFrom + DOUBLE_RADIUS
} }

View File

@@ -46,7 +46,15 @@ class SortedArrayList<T: Comparable<T>>(initialSize: Int = 10) {
fun iterator() = arrayList.iterator() fun iterator() = arrayList.iterator()
fun forEach(action: (T) -> Unit) = arrayList.forEach(action) fun forEach(action: (T) -> Unit) = arrayList.forEach(action)
fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action) fun forEachIndexed(action: (Int, T) -> Unit) = arrayList.forEachIndexed(action)
//fun <R> map(transformation: (T) -> R) = arrayList.map(transformation)
fun <R> map(transformation: (T) -> R) = arrayList.map(transformation)
fun <R> filter(function: (T) -> Boolean): List<R> {
val retList = ArrayList<R>() // 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. * Select one unsorted element from the array and put it onto the sorted spot.