From 2b94bfd41f7efe912fe21886a63206a55780a02e Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Mon, 20 Nov 2023 14:18:57 +0700 Subject: [PATCH] Collision detection updates --- build.gradle.kts | 2 +- .../kotlin/ru/dbotthepony/kstarbound/Main.kt | 2 +- .../kstarbound/world/entities/Entity.kt | 14 +++--- .../kstarbound/world/physics/CollisionType.kt | 4 +- .../kstarbound/world/physics/Functions.kt | 2 +- .../kstarbound/world/physics/Poly.kt | 49 ++++++++++++------- 6 files changed, 41 insertions(+), 32 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 59fe27c6..4f4bd742 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -82,7 +82,7 @@ dependencies { implementation("net.java.dev.jna:jna:5.13.0") implementation("com.github.jnr:jnr-ffi:2.2.13") - implementation("ru.dbotthepony:kvector:2.11.0") + implementation("ru.dbotthepony:kvector:2.11.1") implementation("com.github.ben-manes.caffeine:caffeine:3.1.5") implementation("org.classdump.luna:luna-all-shaded:0.4.1") diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt index cc88a75f..2351e9b5 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/Main.kt @@ -82,7 +82,7 @@ fun main() { for (chunkX in 0 .. 100) { //for (chunkX in 0 .. 17) { // for (chunkY in 21 .. 21) { - for (chunkY in 0 .. 24) { + for (chunkY in 18 .. 24) { val data = db.read(byteArrayOf(1, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())) val data2 = db.read(byteArrayOf(2, (chunkX shr 8).toByte(), chunkX.toByte(), (chunkY shr 8).toByte(), chunkY.toByte())) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt index be4e15e1..7afc1b92 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/entities/Entity.kt @@ -188,29 +188,29 @@ abstract class Entity(val world: World<*, *>) { if (polies.isEmpty()) break - val intersects = ArrayList>() + val intersects = ArrayList>() localHitboxes.forEach { hitbox -> - polies.forEach { poly -> hitbox.intersect(poly.poly, poly)?.let { intersects.add(it) } } + polies.forEach { poly -> hitbox.intersect(poly.poly)?.let { intersects.add(it to poly) } } } if (intersects.isEmpty()) { break } else { - val max = intersects.max() + val (max, data) = intersects.maxByOrNull { it.first }!! // resolve collision - position += max.vector + position += max.axis * max.penetration // collision response - val response = max.axis * velocity.dot(max.axis * (1.0 + max.data.bounceFactor) * (1.0 + movementParameters.bounceFactor.orElse(0.0))) + val response = max.axis * velocity.dot(max.axis * (1.0 + data.bounceFactor) * (1.0 + movementParameters.bounceFactor.orElse(0.0))) velocity -= response val gravityDot = world.gravity.unitVector.dot(max.axis) // impulse? - velocity += max.data.velocity * gravityDot * dt + velocity += data.velocity * gravityDot * dt // friction velocity *= 1.0 - gravityDot.absoluteValue * 0.08 - onTouch(response, max.axis, max.data) + onTouch(response, max.axis, data) } } } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/CollisionType.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/CollisionType.kt index 52813f77..3e7206b7 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/CollisionType.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/CollisionType.kt @@ -9,7 +9,5 @@ enum class CollisionType(val isEmpty: Boolean) { PLATFORM(false), DYNAMIC(false), SLIPPERY(false), - BLOCK(false), - // stairs made out of blocks - BLOCK_SLOPE(false); + BLOCK(false); } diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt index 85ccdbde..d47b8353 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Functions.kt @@ -91,7 +91,7 @@ fun getBlockPlatforms(x: Int, y: Int, getter: CollisionTypeGetter, kind: Collisi target.addBlock(listOf(Vector2d(dx, dy + 1), Vector2d(dx + 1, dy + 1)), kind) } else if (upLeft && downLeft && !upRight && !downRight) { target.addBlock(listOf(Vector2d(dx + 1, dy), Vector2d(dx, dy + 1)), kind) - } else if (upRight && downRight && !upLeft && !upRight) { // TODO: figure out what original starbound devs meant by this condition + } else if (upRight && downRight && !upLeft) { // Fix: copy-paste typo in original sources target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) } else if (upRight && downLeft) { target.addBlock(listOf(Vector2d(dx, dy), Vector2d(dx + 1, dy + 1)), kind) diff --git a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt index c8615363..ee02bedf 100644 --- a/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt +++ b/src/main/kotlin/ru/dbotthepony/kstarbound/world/physics/Poly.kt @@ -7,6 +7,7 @@ import com.google.gson.TypeAdapterFactory import com.google.gson.reflect.TypeToken import com.google.gson.stream.JsonReader import com.google.gson.stream.JsonWriter +import it.unimi.dsi.fastutil.objects.ObjectArrayList import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet import org.lwjgl.opengl.GL11.GL_LINES import ru.dbotthepony.kstarbound.client.StarboundClient @@ -21,22 +22,33 @@ import ru.dbotthepony.kvector.vector.RGBAColor import ru.dbotthepony.kvector.vector.Vector2d import kotlin.math.absoluteValue -private fun calculateEdges(points: List): ImmutableList { +private fun calculateEdges(points: List): Pair, ImmutableList> { require(points.size >= 2) { "Provided poly is invalid (only ${points.size} points are defined)" } val edges = ImmutableList.Builder() - for (i in points.indices) { - val p0 = points[i] - val p1 = points[(i + 1) % points.size] + if (points.size == 2) { + // line, to make it one faced, we need to make only one edge + + val (p0, p1) = points val diff = (p1 - p0).unitVector val normal = Vector2d(-diff.y, diff.x) edges.add(Poly.Edge(p0, p1, normal)) + } else { + for (i in points.indices) { + val p0 = points[i] + val p1 = points[(i + 1) % points.size] + + val diff = (p1 - p0).unitVector + val normal = Vector2d(-diff.y, diff.x) + + edges.add(Poly.Edge(p0, p1, normal)) + } } - return edges.build() + return edges.build() to ImmutableList.copyOf(points) } /** @@ -45,7 +57,8 @@ private fun calculateEdges(points: List): ImmutableList { * If poly shape is not convex behavior of SAT is undefined */ class Poly private constructor(val edges: ImmutableList, val vertices: ImmutableList) { - constructor(points: List) : this(calculateEdges(points), ImmutableList.copyOf(points)) + private constructor(pair: Pair, ImmutableList>) : this(pair.first, pair.second) + constructor(points: List) : this(calculateEdges(points)) constructor(aabb: AABB) : this(listOf(aabb.bottomLeft, aabb.topLeft, aabb.topRight, aabb.bottomRight)) val aabb = AABB( @@ -71,15 +84,15 @@ class Poly private constructor(val edges: ImmutableList, val vertices: Imm } } - data class Penetration(val axis: Vector2d, val penetration: Double, val data: T) : Comparable> { + data class Penetration(val axis: Vector2d, val penetration: Double) : Comparable { val vector = axis * penetration - override fun compareTo(other: Penetration<*>): Int { + override fun compareTo(other: Penetration): Int { return penetration.absoluteValue.compareTo(other.penetration.absoluteValue) } } - operator fun plus(value: Penetration<*>): Poly { + operator fun plus(value: Penetration): Poly { val vertices = ImmutableList.Builder() val edges = ImmutableList.Builder() @@ -144,7 +157,7 @@ class Poly private constructor(val edges: ImmutableList, val vertices: Imm return Vector2d(min, max) } - fun intersect(other: Poly, data: T): Penetration? { + fun intersect(other: Poly): Penetration? { if (!aabb.intersectWeak(other.aabb)) return null @@ -152,7 +165,7 @@ class Poly private constructor(val edges: ImmutableList, val vertices: Imm edges.forEach { normals.add(it.normal) } other.edges.forEach { normals.add(it.normal) } - val intersections = ArrayList>() + val intersections = ArrayList() for (normal in normals) { val projectThis = project(normal) @@ -173,10 +186,10 @@ class Poly private constructor(val edges: ImmutableList, val vertices: Imm if (minMin <= maxMax) { // push to left - intersections.add(Penetration(normal, -minMin - width, data)) + intersections.add(Penetration(normal, -minMin - width)) } else { // push to right - intersections.add(Penetration(normal, maxMax + width, data)) + intersections.add(Penetration(normal, maxMax + width)) } } else if ( projectThis.component1() in projectOther.component1() .. projectOther.component2() && @@ -188,17 +201,17 @@ class Poly private constructor(val edges: ImmutableList, val vertices: Imm if (minMin <= maxMax) { // push to left - intersections.add(Penetration(normal, -minMin - width, data)) + intersections.add(Penetration(normal, -minMin - width)) } else { // push to right - intersections.add(Penetration(normal, maxMax + width, data)) + intersections.add(Penetration(normal, maxMax + width)) } } else if (projectOther.component1() in projectThis.component1() .. projectThis.component2()) { // other's min point is within this - intersections.add(Penetration(normal, projectOther.component1() - projectThis.component2(), data)) + intersections.add(Penetration(normal, projectOther.component1() - projectThis.component2())) } else { // other's max point in within this - intersections.add(Penetration(normal, projectOther.component2() - projectThis.component1(), data)) + intersections.add(Penetration(normal, projectOther.component2() - projectThis.component1())) } if (intersections.last().penetration == 0.0) { @@ -213,8 +226,6 @@ class Poly private constructor(val edges: ImmutableList, val vertices: Imm return intersections.min() } - fun intersect(other: Poly) = intersect(other, Unit) - fun render(client: StarboundClient = StarboundClient.current(), color: RGBAColor = RGBAColor.LIGHT_GREEN) { val program = client.programs.position val lines = program.builder.builder