Remove kbox2d from deps and remove leftover code from previous testing stages

This commit is contained in:
DBotThePony 2023-10-14 19:26:28 +07:00
parent b9975657d4
commit 30b168766d
Signed by: DBot
GPG Key ID: DCC23B5715498507
27 changed files with 184 additions and 561 deletions

View File

@ -82,8 +82,7 @@ dependencies {
implementation("net.java.dev.jna:jna:5.13.0")
implementation("com.github.jnr:jnr-ffi:2.2.13")
implementation("ru.dbotthepony:kbox2d:2.4.1-1.0.2")
implementation("ru.dbotthepony:kvector:2.10.2")
implementation("ru.dbotthepony:kvector:2.11.0")
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
}

View File

@ -1,8 +0,0 @@
package ru.dbotthepony.kstarbound
const val METRES_IN_STARBOUND_UNIT = 0.5
const val METRES_IN_STARBOUND_UNITf = 0.5f
const val PIXELS_IN_STARBOUND_UNITi = 8
const val PIXELS_IN_STARBOUND_UNIT = PIXELS_IN_STARBOUND_UNITi.toDouble()
const val PIXELS_IN_STARBOUND_UNITf = PIXELS_IN_STARBOUND_UNITi.toFloat()

View File

@ -20,6 +20,7 @@ import ru.dbotthepony.kstarbound.io.readVarInt
import ru.dbotthepony.kstarbound.util.AssetPathStack
import ru.dbotthepony.kstarbound.world.entities.WorldObject
import ru.dbotthepony.kstarbound.world.physics.Poly
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Vector2d
import java.io.BufferedInputStream
import java.io.ByteArrayInputStream
@ -122,12 +123,12 @@ fun main() {
val item = Registries.items.values.random()
val rand = Random()
for (i in 0 .. 0) {
for (i in 0 .. 128) {
val item = ItemEntity(client.world!!, item.value)
item.position = Vector2d(225.0 + i, 685.0)
item.position = Vector2d(225.0 - i, 685.0)
item.spawn()
item.velocity = Vector2d(0.01, -0.08)
item.velocity = Vector2d(rand.nextDouble() * 32.0 - 16.0, rand.nextDouble() * 32.0 - 16.0)
//item.movement.applyVelocity(Vector2d(rand.nextDouble() * 1000.0 - 500.0, rand.nextDouble() * 1000.0 - 500.0))
}
@ -164,53 +165,6 @@ fun main() {
client.font.render("World chunk: ${client.world!!.chunkFromCell(client.camera.pos)}", y = 180f, scale = 0.25f)
}
var p = Poly(Starbound.gson.fromJson<List<Vector2d>>("""[ [1.75, 2.55], [2.25, 2.05], [2.75, -3.55], [2.25, -3.95], [-2.25, -3.95], [-2.75, -3.55], [-2.25, 2.05], [-1.75, 2.55] ]""", TypeToken.getParameterized(ImmutableList::class.java, Vector2d::class.java).type))
val polies = ArrayList<Poly>()
val box = Poly(listOf(Vector2d(-0.5, -0.5), Vector2d(-0.5, 0.5), Vector2d(0.5, 0.5), Vector2d(0.5, -0.5)))
p = box
val triangle = Poly(listOf(Vector2d(-0.5, -0.5), Vector2d(0.5, 0.5), Vector2d(0.5, -0.5)))
//polies.add(triangle + Vector2d(0.0, -1.5))
polies.add(box)
polies.add(box + Vector2d(1.0))
//polies.add(box + Vector2d(1.0, -1.0))
//polies.add(box + Vector2d(1.0, -2.0))
//polies.add(box + Vector2d(1.0, -3.0))
//client.foregroundExecutor.scheduleAtFixedRate({ p.intersect(p2)?.let { p += it; println(it) } }, 1, 1, TimeUnit.SECONDS)
p += client.camera.pos
for (i in polies.indices)
polies[i] = polies[i] + client.camera.pos
client.onPostDrawWorld {
//client.camera.pos.x = ent.pos.x.toFloat()
//client.camera.pos.y = ent.pos.y.toFloat()
p += client.screenToWorld(client.mouseCoordinates) - p.aabb.centre
for (i in 0 until 10) {
val intersects = ArrayList<Poly.Penetration>()
polies.forEach { p.intersect(it)?.let { intersects.add(it) } }
if (intersects.isEmpty())
break
else
p += intersects.max()
}
p.render()
polies.forEach { it.render() }
}
client.box2dRenderer.drawShapes = false
client.box2dRenderer.drawPairs = false
client.box2dRenderer.drawAABB = false
client.box2dRenderer.drawJoints = false
//ent.spawn()
client.input.addScrollCallback { _, x, y ->

View File

@ -60,6 +60,7 @@ import ru.dbotthepony.kstarbound.util.WriteOnce
import ru.dbotthepony.kstarbound.util.filterNotNull
import ru.dbotthepony.kstarbound.util.set
import ru.dbotthepony.kstarbound.util.traverseJsonPath
import ru.dbotthepony.kstarbound.world.physics.Poly
import java.io.*
import java.lang.ref.Cleaner
import java.text.DateFormat
@ -163,7 +164,6 @@ object Starbound : ISBFileLocator {
registerTypeAdapter(Vector2iTypeAdapter)
registerTypeAdapter(Vector4iTypeAdapter)
registerTypeAdapter(Vector4dTypeAdapter)
registerTypeAdapter(PolyTypeAdapter)
registerTypeAdapter(LineF::Adapter)
// Функции
@ -192,6 +192,8 @@ object Starbound : ISBFileLocator {
registerTypeAdapter(Image.Companion)
registerTypeAdapterFactory(Poly.Companion)
registerTypeAdapterFactory(with(RegistryReferenceFactory()) {
add(Registries.tiles)
add(Registries.tileModifiers)

View File

@ -15,8 +15,8 @@ import org.lwjgl.system.MemoryStack
import org.lwjgl.system.MemoryUtil
import ru.dbotthepony.kstarbound.LoadingLog
import ru.dbotthepony.kstarbound.util.ManualExecutorService
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.freetype.FreeType
import ru.dbotthepony.kstarbound.client.freetype.InvalidArgumentException
@ -41,7 +41,6 @@ import ru.dbotthepony.kstarbound.client.gl.shader.UberShader
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
import ru.dbotthepony.kstarbound.client.input.UserInput
import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
import ru.dbotthepony.kstarbound.client.render.Camera
import ru.dbotthepony.kstarbound.client.render.Font
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
@ -382,7 +381,6 @@ class StarboundClient : Closeable {
val freeType = FreeType()
val font = Font()
val box2dRenderer = Box2DRenderer()
val programs = GLPrograms()
init {
@ -683,7 +681,8 @@ class StarboundClient : Closeable {
viewportRectangle = AABB.rectangle(
camera.pos,
viewportWidth / settings.zoom / PIXELS_IN_STARBOUND_UNIT,
viewportHeight / settings.zoom / PIXELS_IN_STARBOUND_UNIT)
viewportHeight / settings.zoom / PIXELS_IN_STARBOUND_UNIT
)
viewportCellX = roundTowardsNegativeInfinity(viewportRectangle.mins.x) - 16
viewportCellY = roundTowardsNegativeInfinity(viewportRectangle.mins.y) - 16
@ -855,10 +854,6 @@ class StarboundClient : Closeable {
layers.render()
box2dRenderer.begin()
world.physics.debugDraw()
box2dRenderer.render()
for (lambda in onPostDrawWorld) {
lambda.invoke()
}

View File

@ -1,137 +0,0 @@
package ru.dbotthepony.kstarbound.client.render
import org.lwjgl.opengl.GL45.*
import ru.dbotthepony.kbox2d.api.IDebugDraw
import ru.dbotthepony.kbox2d.api.Transform
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexAttributes
import ru.dbotthepony.kvector.arrays.Matrix3f
import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2d
import kotlin.math.cos
import kotlin.math.sin
class Box2DRenderer : IDebugDraw {
val state = StarboundClient.current()
override var drawShapes: Boolean = false
override var drawJoints: Boolean = false
override var drawAABB: Boolean = false
override var drawTreeAABB: Boolean = false
override var drawPairs: Boolean = false
override var drawCenterOfMess: Boolean = false
private val identity = Matrix3f.identity()
private val lines = StreamVertexBuilder(VertexAttributes.POSITION_COLOR, GeometryType.LINES)
private val triangles = StreamVertexBuilder(VertexAttributes.POSITION_COLOR, GeometryType.TRIANGLES)
fun begin() {
lines.builder.begin()
triangles.builder.begin()
}
fun render() {
if (lines.builder.isEmpty() && triangles.builder.isEmpty()) return
lines.upload(GL_STREAM_DRAW)
triangles.upload(GL_STREAM_DRAW)
state.programs.positionColor.use()
state.programs.positionColor.colorMultiplier = RGBAColor.WHITE
state.programs.positionColor.modelMatrix = identity
lines.draw(GL_LINES)
triangles.draw(GL_TRIANGLES)
}
override fun drawPolygon(vertices: List<Vector2d>, color: RGBAColor) {
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
if (!vertices.any { state.viewportRectangle.isInside(it) }) return
for (i in vertices.indices) {
val current = vertices[i]
val next = vertices[(i + 1) % vertices.size]
lines.builder.vertex(state.stack.last(), current).color(color)
lines.builder.vertex(state.stack.last(), next).color(color)
}
}
private fun drawSolid(vertices: List<Vector2d>, color: RGBAColor) {
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
if (!vertices.any { state.viewportRectangle.isInside(it) }) return
val zero = vertices[0]
for (i in 1 until vertices.size) {
val current = vertices[i]
val next = vertices[(i + 1) % vertices.size]
triangles.builder.vertex(state.stack.last(), zero.x.toFloat(), zero.y.toFloat()).color(color)
triangles.builder.vertex(state.stack.last(), current.x.toFloat(), current.y.toFloat()).color(color)
triangles.builder.vertex(state.stack.last(), next.x.toFloat(), next.y.toFloat()).color(color)
}
}
override fun drawSolidPolygon(vertices: List<Vector2d>, color: RGBAColor) {
drawSolid(vertices, color.copy(alpha = 0.5f))
drawPolygon(vertices, color)
}
override fun drawCircle(center: Vector2d, radius: Double, color: RGBAColor) {
val vertexList = ArrayList<Vector2d>()
for (i in 0 until 360 step 15) {
val rad = Math.toRadians(i.toDouble())
val c = cos(rad)
val s = sin(rad)
vertexList.add(Vector2d(
center.x + c * radius,
center.y + s * radius
))
}
drawPolygon(vertexList, color)
}
override fun drawSolidCircle(center: Vector2d, radius: Double, axis: Vector2d, color: RGBAColor) {
val vertexList = ArrayList<Vector2d>()
for (i in 0 until 360 step 15) {
val rad = Math.toRadians(i.toDouble())
val c = cos(rad)
val s = sin(rad)
vertexList.add(Vector2d(
center.x + c * radius,
center.y + s * radius
))
}
drawSolidPolygon(vertexList, color.copy(alpha = 0.5f))
drawPolygon(vertexList, color)
drawPolygon(listOf(center, center + axis * radius), color)
}
override fun drawSegment(p1: Vector2d, p2: Vector2d, color: RGBAColor) {
drawPolygon(listOf(p1, p2), color)
}
override fun drawTransform(xf: Transform) {
TODO("Not yet implemented")
}
override fun drawPoint(p: Vector2d, size: Double, color: RGBAColor) {
drawSolid(listOf(
Vector2d(x = p.x - size / (PIXELS_IN_STARBOUND_UNIT * 2.0), y = p.y - size / (PIXELS_IN_STARBOUND_UNIT * 2.0)),
Vector2d(x = p.x + size / (PIXELS_IN_STARBOUND_UNIT * 2.0), y = p.y - size / (PIXELS_IN_STARBOUND_UNIT * 2.0)),
Vector2d(x = p.x + size / (PIXELS_IN_STARBOUND_UNIT * 2.0), y = p.y + size / (PIXELS_IN_STARBOUND_UNIT * 2.0)),
Vector2d(x = p.x - size / (PIXELS_IN_STARBOUND_UNIT * 2.0), y = p.y + size / (PIXELS_IN_STARBOUND_UNIT * 2.0)),
), color)
}
}

View File

@ -5,7 +5,7 @@ import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.Scheduler
import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL45.*
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Registries
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.*

View File

@ -5,12 +5,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import it.unimi.dsi.fastutil.longs.LongArraySet
import it.unimi.dsi.fastutil.objects.ObjectArrayList
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
import ru.dbotthepony.kbox2d.api.BodyDef
import ru.dbotthepony.kbox2d.api.BodyType
import ru.dbotthepony.kbox2d.api.FixtureDef
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.render.ConfiguredMesh
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
@ -21,27 +16,18 @@ import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE
import ru.dbotthepony.kstarbound.world.ChunkPos
import ru.dbotthepony.kstarbound.world.NeverFilter
import ru.dbotthepony.kstarbound.world.NonEmptyFilter
import ru.dbotthepony.kstarbound.world.RayCastResult
import ru.dbotthepony.kstarbound.world.World
import ru.dbotthepony.kstarbound.world.api.ITileAccess
import ru.dbotthepony.kstarbound.world.api.OffsetCellAccess
import ru.dbotthepony.kstarbound.world.api.TileView
import ru.dbotthepony.kstarbound.world.castRay
import ru.dbotthepony.kstarbound.world.positiveModulo
import ru.dbotthepony.kvector.api.IStruct2i
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.RGBAColor
import ru.dbotthepony.kvector.vector.Vector2d
import ru.dbotthepony.kvector.vector.Vector2f
import ru.dbotthepony.kvector.vector.Vector2i
import java.util.concurrent.Callable
import java.util.concurrent.ForkJoinPool
import java.util.concurrent.Future
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
class ClientWorld(
val client: StarboundClient,
@ -50,10 +36,6 @@ class ClientWorld(
loopX: Boolean = false,
loopY: Boolean = false
) : World<ClientWorld, ClientChunk>(seed, size, loopX, loopY) {
init {
physics.debugDraw = client.box2dRenderer
}
private fun determineChunkSize(cells: Int): Int {
for (i in 32 downTo 1) {
if (cells % i == 0) {

View File

@ -7,11 +7,11 @@ import ru.dbotthepony.kstarbound.util.KOptional
@JsonImplementation(BaseMovementParameters.Impl::class)
interface BaseMovementParameters {
val mass: KOptional<Float>
val gravityMultiplier: KOptional<Float>
val liquidBuoyancy: KOptional<Float>
val airBuoyancy: KOptional<Float>
val bounceFactor: KOptional<Float>
val mass: KOptional<Double>
val gravityMultiplier: KOptional<Double>
val liquidBuoyancy: KOptional<Double>
val airBuoyancy: KOptional<Double>
val bounceFactor: KOptional<Double>
// If set to true, during an update that has more than one internal movement
// step, the movement will stop on the first bounce.
@ -21,50 +21,50 @@ interface BaseMovementParameters {
// other directions (within a set limit). Allows smooth sliding along
// horizontal ground without losing horizontal speed.
val enableSurfaceSlopeCorrection: KOptional<Boolean>
val slopeSlidingFactor: KOptional<Float>
val maxMovementPerStep: KOptional<Float>
val maximumCorrection: KOptional<Float>
val speedLimit: KOptional<Float>
val slopeSlidingFactor: KOptional<Double>
val maxMovementPerStep: KOptional<Double>
val maximumCorrection: KOptional<Double>
val speedLimit: KOptional<Double>
val stickyCollision: KOptional<Boolean>
val stickyForce: KOptional<Float>
val stickyForce: KOptional<Double>
val airFriction: KOptional<Float>
val liquidFriction: KOptional<Float>
val groundFriction: KOptional<Float>
val airFriction: KOptional<Double>
val liquidFriction: KOptional<Double>
val groundFriction: KOptional<Double>
val collisionEnabled: KOptional<Boolean>
val frictionEnabled: KOptional<Boolean>
val gravityEnabled: KOptional<Boolean>
val maximumPlatformCorrection: KOptional<Float>
val maximumPlatformCorrectionVelocityFactor: KOptional<Float>
val maximumPlatformCorrection: KOptional<Double>
val maximumPlatformCorrectionVelocityFactor: KOptional<Double>
val physicsEffectCategories: KOptional<ImmutableSet<String>>
@JsonFactory
data class Impl(
override val mass: KOptional<Float> = KOptional.empty(),
override val gravityMultiplier: KOptional<Float> = KOptional.empty(),
override val liquidBuoyancy: KOptional<Float> = KOptional.empty(),
override val airBuoyancy: KOptional<Float> = KOptional.empty(),
override val bounceFactor: KOptional<Float> = KOptional.empty(),
override val mass: KOptional<Double> = KOptional.empty(),
override val gravityMultiplier: KOptional<Double> = KOptional.empty(),
override val liquidBuoyancy: KOptional<Double> = KOptional.empty(),
override val airBuoyancy: KOptional<Double> = KOptional.empty(),
override val bounceFactor: KOptional<Double> = KOptional.empty(),
override val stopOnFirstBounce: KOptional<Boolean> = KOptional.empty(),
override val enableSurfaceSlopeCorrection: KOptional<Boolean> = KOptional.empty(),
override val slopeSlidingFactor: KOptional<Float> = KOptional.empty(),
override val maxMovementPerStep: KOptional<Float> = KOptional.empty(),
override val maximumCorrection: KOptional<Float> = KOptional.empty(),
override val speedLimit: KOptional<Float> = KOptional.empty(),
override val slopeSlidingFactor: KOptional<Double> = KOptional.empty(),
override val maxMovementPerStep: KOptional<Double> = KOptional.empty(),
override val maximumCorrection: KOptional<Double> = KOptional.empty(),
override val speedLimit: KOptional<Double> = KOptional.empty(),
override val stickyCollision: KOptional<Boolean> = KOptional.empty(),
override val stickyForce: KOptional<Float> = KOptional.empty(),
override val airFriction: KOptional<Float> = KOptional.empty(),
override val liquidFriction: KOptional<Float> = KOptional.empty(),
override val groundFriction: KOptional<Float> = KOptional.empty(),
override val stickyForce: KOptional<Double> = KOptional.empty(),
override val airFriction: KOptional<Double> = KOptional.empty(),
override val liquidFriction: KOptional<Double> = KOptional.empty(),
override val groundFriction: KOptional<Double> = KOptional.empty(),
override val collisionEnabled: KOptional<Boolean> = KOptional.empty(),
override val frictionEnabled: KOptional<Boolean> = KOptional.empty(),
override val gravityEnabled: KOptional<Boolean> = KOptional.empty(),
override val maximumPlatformCorrection: KOptional<Float> = KOptional.empty(),
override val maximumPlatformCorrectionVelocityFactor: KOptional<Float> = KOptional.empty(),
override val maximumPlatformCorrection: KOptional<Double> = KOptional.empty(),
override val maximumPlatformCorrectionVelocityFactor: KOptional<Double> = KOptional.empty(),
override val physicsEffectCategories: KOptional<ImmutableSet<String>> = KOptional.empty(),
) : BaseMovementParameters {
fun merge(other: Impl): Impl {

View File

@ -8,7 +8,7 @@ import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import org.apache.logging.log4j.LogManager
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.client.render.IGeometryLayer

View File

@ -1,10 +1,9 @@
package ru.dbotthepony.kstarbound.defs
import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.io.json.builder.JsonFlat
import ru.dbotthepony.kstarbound.util.KOptional
import ru.dbotthepony.kvector.vector.Vector2d
import ru.dbotthepony.kstarbound.world.physics.Poly
@JsonFactory
data class MovementParameters(
@ -12,7 +11,7 @@ data class MovementParameters(
val base: BaseMovementParameters.Impl = BaseMovementParameters.Impl(),
val discontinuityThreshold: KOptional<Float> = KOptional.empty(),
val collisionPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
val collisionPoly: KOptional<Poly> = KOptional.empty(),
val ignorePlatformCollision: KOptional<Boolean> = KOptional.empty(),
val restDuration: KOptional<Int> = KOptional.empty(),
) : BaseMovementParameters by base {

View File

@ -20,8 +20,8 @@ import org.apache.logging.log4j.LogManager
import org.lwjgl.opengl.GL45
import org.lwjgl.stb.STBImage
import org.lwjgl.system.MemoryUtil
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITi
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITi
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.api.IStarboundFile
import ru.dbotthepony.kstarbound.client.StarboundClient

View File

@ -10,7 +10,7 @@ import com.google.gson.TypeAdapter
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.world.PIXELS_IN_STARBOUND_UNITf
import ru.dbotthepony.kstarbound.client.render.RenderLayer
import ru.dbotthepony.kstarbound.defs.Drawable
import ru.dbotthepony.kstarbound.defs.JsonReference

View File

@ -1,14 +1,12 @@
package ru.dbotthepony.kstarbound.defs.player
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
import ru.dbotthepony.kstarbound.defs.BaseMovementParameters
import ru.dbotthepony.kstarbound.defs.JumpProfile
import ru.dbotthepony.kstarbound.io.json.builder.JsonAlias
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory
import ru.dbotthepony.kstarbound.io.json.builder.JsonFlat
import ru.dbotthepony.kstarbound.util.KOptional
import ru.dbotthepony.kvector.vector.Vector2d
import ru.dbotthepony.kstarbound.world.physics.Poly
@JsonFactory
data class PlayerMovementParameters(
@ -16,9 +14,9 @@ data class PlayerMovementParameters(
val base: BaseMovementParameters.Impl = BaseMovementParameters.Impl(),
@JsonAlias("collisionPoly")
val standingPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
val standingPoly: KOptional<Poly> = KOptional.empty(),
@JsonAlias("collisionPoly")
val crouchingPoly: KOptional<ImmutableList<Vector2d>> = KOptional.empty(),
val crouchingPoly: KOptional<Poly> = KOptional.empty(),
val walkSpeed: KOptional<Double> = KOptional.empty(),
val runSpeed: KOptional<Double> = KOptional.empty(),

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.defs.tile
import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.CollisionType
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.defs.ThingDescription
object BuiltinMetaMaterials {

View File

@ -2,7 +2,7 @@ package ru.dbotthepony.kstarbound.defs.tile
import com.google.common.collect.ImmutableList
import ru.dbotthepony.kstarbound.defs.AssetReference
import ru.dbotthepony.kstarbound.defs.CollisionType
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.defs.IThingWithDescription
import ru.dbotthepony.kstarbound.defs.ThingDescription
import ru.dbotthepony.kstarbound.io.json.builder.JsonFactory

View File

@ -1,43 +0,0 @@
package ru.dbotthepony.kstarbound.math
import com.google.common.collect.ImmutableList
import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
import ru.dbotthepony.kvector.vector.Vector2d
class Poly(vararg points: Vector2d) {
val points: List<Vector2d> = ImmutableList.copyOf(points)
override fun toString(): String {
return "Poly($points)"
}
}
object PolyTypeAdapter : TypeAdapter<Poly>() {
override fun write(out: JsonWriter, value: Poly) {
`out`.beginArray()
for (point in value.points) {
Vector2dTypeAdapter.write(out, point)
}
`out`.endArray()
}
override fun read(`in`: JsonReader): Poly {
`in`.beginArray()
val points = mutableListOf<Vector2d>()
while (`in`.peek() == JsonToken.BEGIN_ARRAY) {
points.add(Vector2dTypeAdapter.read(`in`))
}
`in`.endArray()
return Poly(*points.toTypedArray())
}
}

View File

@ -1,10 +1,6 @@
package ru.dbotthepony.kstarbound.world
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import ru.dbotthepony.kbox2d.api.BodyDef
import ru.dbotthepony.kbox2d.api.FixtureDef
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
import ru.dbotthepony.kbox2d.dynamics.B2Fixture
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
import ru.dbotthepony.kstarbound.defs.tile.MaterialModifier
@ -49,8 +45,6 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
private set
var cellChangeset = 0
private set
var collisionChangeset = 0
private set
var foregroundChangeset = 0
private set
@ -101,28 +95,15 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
private val collisionCache = ArrayList<AABB>()
private val collisionCacheView = Collections.unmodifiableCollection(collisionCache)
private val body by lazy {
world.physics.createBody(BodyDef(
position = pos.tile.toDoubleVector(),
userData = this
))
}
private val collisionChains = ArrayList<B2Fixture>()
protected open fun foregroundChanges(cell: Cell) {
cellChanges(cell)
tileChangeset++
collisionChangeset++
foregroundChangeset++
markPhysicsDirty()
}
protected open fun backgroundChanges(cell: Cell) {
cellChanges(cell)
tileChangeset++
backgroundChangeset++
}
@ -147,14 +128,6 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
world.chunkMap[pos.bottomRight]?.let(block)
}
fun markPhysicsDirty() {
if (isPhysicsDirty)
return
isPhysicsDirty = true
world.dirtyPhysicsChunks.add(this as This)
}
inner class Cell(x: Int, y: Int) : IChunkCell {
override val x: Int = x + pos.x * CHUNK_SIZE
override val y: Int = y + pos.y * CHUNK_SIZE
@ -279,45 +252,6 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
}
}
fun bakeCollisions() {
if (collisionChangeset == changeset)
return
collisionChangeset = changeset
collisionCache.clear()
for (box in collisionChains) {
body.destroyFixture(box)
}
collisionChains.clear()
for (y in 0 .. CHUNK_SIZE_FF) {
for (x in 0..CHUNK_SIZE_FF) {
val cell = getCell(x, y)
/*if (!cell.foreground.material.collisionKind.isEmpty) {
collisionChains.add(body.createFixture(FixtureDef(
shape = PolygonShape().also { it.setAsBox(0.5, 0.5, Vector2d(x + 0.5, y + 0.5), 0.0) },
friction = 0.4,
userData = cell,
)))
}*/
}
}
}
/**
* Возвращает список AABB тайлов этого слоя
*
* Данный список напрямую указывает на внутреннее состояние и будет изменён при перестройке
* коллизии чанка, поэтому если необходим стабильный список, его необходимо скопировать
*/
fun collisionLayers(): Collection<AABB> {
bakeCollisions()
return collisionCacheView
}
override fun randomLongFor(x: Int, y: Int): Long {
return world.randomLongFor(x or pos.x shl CHUNK_SIZE_BITS, y or pos.y shl CHUNK_SIZE_BITS)
}

View File

@ -0,0 +1,15 @@
package ru.dbotthepony.kstarbound.world
const val METRES_IN_STARBOUND_UNIT = 0.5
const val METRES_IN_STARBOUND_UNITf = 0.5f
const val PIXELS_IN_STARBOUND_UNITi = 8
const val PIXELS_IN_STARBOUND_UNIT = PIXELS_IN_STARBOUND_UNITi.toDouble()
const val PIXELS_IN_STARBOUND_UNITf = PIXELS_IN_STARBOUND_UNITi.toFloat()
const val CHUNK_SIZE_BITS = 5
const val CHUNK_SIZE_MASK = 1 or 2 or 4 or 8 or 16
const val CHUNK_SIZE = 1 shl CHUNK_SIZE_BITS // 32
const val CHUNK_SIZE_FF = CHUNK_SIZE - 1
const val CHUNK_SIZEd = CHUNK_SIZE.toDouble()
const val EARTH_FREEFALL_ACCELERATION = 9.8312 / METRES_IN_STARBOUND_UNIT

View File

@ -1,6 +1,6 @@
package ru.dbotthepony.kstarbound.world
import ru.dbotthepony.kstarbound.METRES_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.world.METRES_IN_STARBOUND_UNIT
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
import ru.dbotthepony.kstarbound.world.api.ICellAccess
import ru.dbotthepony.kstarbound.world.api.IChunkCell
@ -10,8 +10,6 @@ import kotlin.collections.ArrayList
import kotlin.math.pow
import kotlin.math.sqrt
const val EARTH_FREEFALL_ACCELERATION = 9.8312 / METRES_IN_STARBOUND_UNIT
data class RayCastResult(
val traversedTiles: List<HitCell>,
val hitTile: HitCell?,

View File

@ -5,14 +5,7 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet
import it.unimi.dsi.fastutil.objects.ReferenceLinkedOpenHashSet
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
import ru.dbotthepony.kbox2d.api.ContactImpulse
import ru.dbotthepony.kbox2d.api.IContactFilter
import ru.dbotthepony.kbox2d.api.IContactListener
import ru.dbotthepony.kbox2d.api.Manifold
import ru.dbotthepony.kbox2d.dynamics.B2Fixture
import ru.dbotthepony.kbox2d.dynamics.B2World
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.world.ClientWorld
import ru.dbotthepony.kstarbound.math.*
import ru.dbotthepony.kstarbound.util.ManualExecutorService
import ru.dbotthepony.kstarbound.world.api.ICellAccess
@ -20,6 +13,9 @@ import ru.dbotthepony.kstarbound.world.api.IChunkCell
import ru.dbotthepony.kstarbound.world.api.TileView
import ru.dbotthepony.kstarbound.world.entities.Entity
import ru.dbotthepony.kstarbound.world.entities.WorldObject
import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
import ru.dbotthepony.kstarbound.world.physics.CollisionType
import ru.dbotthepony.kstarbound.world.physics.Poly
import ru.dbotthepony.kvector.api.IStruct2d
import ru.dbotthepony.kvector.api.IStruct2i
import ru.dbotthepony.kvector.arrays.Object2DArray
@ -30,12 +26,6 @@ import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference
import java.util.random.RandomGenerator
const val CHUNK_SIZE_BITS = 5
const val CHUNK_SIZE_MASK = 1 or 2 or 4 or 8 or 16
const val CHUNK_SIZE = 1 shl CHUNK_SIZE_BITS // 32
const val CHUNK_SIZE_FF = CHUNK_SIZE - 1
const val CHUNK_SIZEd = CHUNK_SIZE.toDouble()
@Suppress("UNCHECKED_CAST")
abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, ChunkType>>(
val seed: Long,
@ -244,81 +234,7 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val random: RandomGenerator = RandomGenerator.of("Xoroshiro128PlusPlus")
/**
* Chunks, which have their collision mesh changed
*/
val dirtyPhysicsChunks = ObjectOpenHashSet<ChunkType>()
val physics = B2World(Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION))
init {
physics.contactFilter = object : IContactFilter {
override fun shouldCollide(fixtureA: B2Fixture, fixtureB: B2Fixture): Boolean {
val dataA = fixtureA.body!!.userData
val dataB = fixtureB.body!!.userData
//if (dataA is AbstractProjectileMovementController && dataB is AbstractProjectileMovementController) {
// return false
//}
return true
}
}
physics.contactListener = object : IContactListener {
override fun beginContact(contact: AbstractContact) {
val dataA = contact.fixtureA.body!!.userData
val dataB = contact.fixtureB.body!!.userData
if (dataA is IContactListener) {
dataA.beginContact(contact)
}
if (dataB is IContactListener) {
dataB.beginContact(contact)
}
}
override fun endContact(contact: AbstractContact) {
val dataA = contact.fixtureA.body!!.userData
val dataB = contact.fixtureB.body!!.userData
if (dataA is IContactListener) {
dataA.endContact(contact)
}
if (dataB is IContactListener) {
dataB.endContact(contact)
}
}
override fun preSolve(contact: AbstractContact, oldManifold: Manifold) {
val dataA = contact.fixtureA.body!!.userData
val dataB = contact.fixtureB.body!!.userData
if (dataA is IContactListener) {
dataA.preSolve(contact, oldManifold)
}
if (dataB is IContactListener) {
dataB.preSolve(contact, oldManifold)
}
}
override fun postSolve(contact: AbstractContact, impulse: ContactImpulse) {
val dataA = contact.fixtureA.body!!.userData
val dataB = contact.fixtureB.body!!.userData
if (dataA is IContactListener) {
dataA.postSolve(contact, impulse)
}
if (dataB is IContactListener) {
dataB.postSolve(contact, impulse)
}
}
}
}
var gravity = Vector2d(0.0, -EARTH_FREEFALL_ACCELERATION)
var ticks = 0
private set
@ -326,12 +242,6 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
fun think() {
try {
executor.executeQueuedTasks()
for (chunk in dirtyPhysicsChunks)
chunk.bakeCollisions()
dirtyPhysicsChunks.clear()
physics.step(Starbound.TICK_TIME_ADVANCE, 6, 4)
thinkInner()
ticks++
} catch(err: Throwable) {
@ -353,16 +263,9 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
val objects = ReferenceLinkedOpenHashSet<WorldObject>()
/**
* Стандартное ускорение свободного падения в Starbound Units/секунда^2
*
* При Vector2d.ZERO = невесомость
*/
var gravity by physics::gravity
protected abstract fun chunkFactory(pos: ChunkPos): ChunkType
fun testSpace(aabb: AABB, predicate: Predicate<IChunkCell> = Predicate { it.foreground.material != null }): Boolean {
fun testSpace(aabb: AABB, predicate: Predicate<IChunkCell> = Predicate { !it.foreground.material.collisionKind.isEmpty }): Boolean {
val tiles = aabb.encasingIntAABB()
for (x in tiles.mins.x .. tiles.maxs.x) {
@ -375,4 +278,21 @@ abstract class World<This : World<This, ChunkType>, ChunkType : Chunk<This, Chun
return false
}
fun queryCollisions(aabb: AABB): MutableList<CollisionPoly> {
val result = ArrayList<CollisionPoly>()
val tiles = aabb.encasingIntAABB()
for (x in tiles.mins.x .. tiles.maxs.x) {
for (y in tiles.mins.y .. tiles.maxs.y) {
val cell = getCell(x, y) ?: continue
for (poly in cell.polies) {
result.add(CollisionPoly(poly, cell.foreground.material.collisionKind, Vector2d(EARTH_FREEFALL_ACCELERATION, 0.0)))
}
}
}
return result
}
}

View File

@ -32,9 +32,6 @@ interface IChunkCell : IStruct2i {
}
val polies: Collection<Poly> get() {
if (foreground.material.isMeta)
return emptyList()
return listOf(rect + Vector2d(this.x.toDouble(), this.y.toDouble()))
}

View File

@ -1,15 +1,17 @@
package ru.dbotthepony.kstarbound.world.entities
import ru.dbotthepony.kstarbound.GlobalDefaults
import ru.dbotthepony.kstarbound.Starbound
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.defs.BaseMovementParameters
import ru.dbotthepony.kstarbound.world.Chunk
import ru.dbotthepony.kstarbound.world.World
import ru.dbotthepony.kstarbound.world.physics.CollisionPoly
import ru.dbotthepony.kstarbound.world.physics.Poly
import ru.dbotthepony.kvector.util2d.AABB
import ru.dbotthepony.kvector.vector.Vector2d
abstract class Entity(val world: World<*, *>) {
/**
* The chunk this entity currently in, it is automatically updated on each change of [position]
*/
var chunk: Chunk<*, *>? = null
set(value) {
if (!isSpawned) {
@ -40,16 +42,6 @@ abstract class Entity(val world: World<*, *>) {
}
}
/**
* Current entity position. If entity is logical, this can only be changed
* by external means, otherwise it always stands where it is.
*
* If entity is physical, then movement controller will update this on each physics step
* to match position of physical body.
*
* Setting this value will update [chunk] immediately, if [isSpawned] is true and [isRemoved] is false.
*/
var position = Vector2d()
set(value) {
if (field == value)
@ -68,27 +60,9 @@ abstract class Entity(val world: World<*, *>) {
}
}
/**
* This entity's angle in radians.
*
* Logical entities never rotate.
*
* Alive entities usually don't rotate.
*
* If entity is physical, this value is updated by movement controller on
* each physics step to match angle of physical body.
*/
var angle: Double = 0.0
set(value) {
if (field == value)
return
field = value
}
var velocity = Vector2d.ZERO
val hitboxes = ArrayList<Poly>()
open var movementParameters: BaseMovementParameters = GlobalDefaults.movementParameters
/**
* Whenever is this entity spawned in world ([spawn] called).
@ -128,9 +102,6 @@ abstract class Entity(val world: World<*, *>) {
}
}
/**
* Заставляет сущность "думать".
*/
fun think() {
if (!isSpawned) {
throw IllegalStateException("Tried to think before spawning in world")
@ -143,24 +114,20 @@ abstract class Entity(val world: World<*, *>) {
protected abstract fun thinkInner()
protected open fun move() {
position += velocity
velocity += world.gravity * Starbound.TICK_TIME_ADVANCE
position += velocity * Starbound.TICK_TIME_ADVANCE
val polies = ArrayList<Poly>()
for (x in -1 .. 1) {
for (y in -1 .. 1) {
world.chunkMap.getCell(position.x.toInt() + x, position.y.toInt() + y)?.let {
polies.addAll(it.polies)
}
}
}
if (hitboxes.isEmpty()) return
for (i in 0 until 10) {
val intersects = ArrayList<Poly.Penetration>()
val localHitboxes = hitboxes.map { it + position }
val polies = world.queryCollisions(localHitboxes.stream().map { it.aabb }.reduce(AABB::combine).get().enlarge(1.0, 1.0)).filter { !it.type.isEmpty }
if (polies.isEmpty()) break
this.hitboxes.forEach { hitbox0 ->
val hitbox = hitbox0 + position
polies.forEach { poly -> hitbox.intersect(poly)?.let { intersects.add(it) } }
val intersects = ArrayList<Poly.Penetration<CollisionPoly>>()
localHitboxes.forEach { hitbox ->
polies.forEach { poly -> hitbox.intersect(poly.poly, poly)?.let { intersects.add(it) } }
}
if (intersects.isEmpty())
@ -170,7 +137,13 @@ abstract class Entity(val world: World<*, *>) {
// resolve collision
position += max.vector
// collision response
velocity -= max.axis * velocity.dot(max.axis * 1.1)
velocity -= max.axis * velocity.dot(max.axis)
val gravityDot = world.gravity.unitVector.dot(max.axis)
// impulse?
velocity += max.data.velocity * gravityDot * Starbound.TICK_TIME_ADVANCE
// friction
velocity *= 1.0 - gravityDot * 0.08
}
}
}

View File

@ -1,11 +1,5 @@
package ru.dbotthepony.kstarbound.world.entities
import ru.dbotthepony.kbox2d.api.ContactImpulse
import ru.dbotthepony.kbox2d.api.FixtureDef
import ru.dbotthepony.kbox2d.api.Manifold
import ru.dbotthepony.kbox2d.collision.shapes.PolygonShape
import ru.dbotthepony.kbox2d.dynamics.contact.AbstractContact
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.defs.item.api.IItemDefinition
import ru.dbotthepony.kstarbound.world.World
import ru.dbotthepony.kstarbound.world.physics.Poly

View File

@ -0,0 +1,9 @@
package ru.dbotthepony.kstarbound.world.physics
import ru.dbotthepony.kvector.vector.Vector2d
data class CollisionPoly(
val poly: Poly,
val type: CollisionType,
val velocity: Vector2d = Vector2d.ZERO
)

View File

@ -1,4 +1,4 @@
package ru.dbotthepony.kstarbound.defs
package ru.dbotthepony.kstarbound.world.physics
enum class CollisionType(val isEmpty: Boolean) {
NULL(true),

View File

@ -1,10 +1,18 @@
package ru.dbotthepony.kstarbound.world.physics
import com.google.common.collect.ImmutableList
import com.google.gson.Gson
import com.google.gson.TypeAdapter
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.ObjectOpenHashSet
import org.lwjgl.opengl.GL11.GL_LINES
import ru.dbotthepony.kstarbound.client.StarboundClient
import ru.dbotthepony.kstarbound.client.gl.vertex.GeometryType
import ru.dbotthepony.kstarbound.io.json.consumeNull
import ru.dbotthepony.kstarbound.io.json.listAdapter
import ru.dbotthepony.kvector.api.IStruct2d
import ru.dbotthepony.kvector.arrays.Matrix3f
import ru.dbotthepony.kvector.util2d.AABB
@ -63,15 +71,15 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
}
}
data class Penetration(val axis: Vector2d, val penetration: Double) : Comparable<Penetration> {
data class Penetration<T>(val axis: Vector2d, val penetration: Double, val data: T) : Comparable<Penetration<*>> {
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<Vector2d>()
val edges = ImmutableList.Builder<Edge>()
@ -136,7 +144,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
return Vector2d(min, max)
}
fun intersect(other: Poly): Penetration? {
fun <T> intersect(other: Poly, data: T): Penetration<T>? {
if (!aabb.intersectWeak(other.aabb))
return null
@ -144,7 +152,7 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
edges.forEach { normals.add(it.normal) }
other.edges.forEach { normals.add(it.normal) }
val intersections = ArrayList<Penetration>()
val intersections = ArrayList<Penetration<T>>()
for (normal in normals) {
val projectThis = project(normal)
@ -165,10 +173,10 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
if (minMin <= maxMax) {
// push to left
intersections.add(Penetration(normal, -minMin - width))
intersections.add(Penetration(normal, -minMin - width, data))
} else {
// push to right
intersections.add(Penetration(normal, maxMax + width))
intersections.add(Penetration(normal, maxMax + width, data))
}
} else if (
projectThis.component1() in projectOther.component1() .. projectOther.component2() &&
@ -180,20 +188,20 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
if (minMin <= maxMax) {
// push to left
intersections.add(Penetration(normal, -minMin - width))
intersections.add(Penetration(normal, -minMin - width, data))
} else {
// push to right
intersections.add(Penetration(normal, maxMax + width))
intersections.add(Penetration(normal, maxMax + width, data))
}
} else if (projectOther.component1() in projectThis.component1() .. projectThis.component2()) {
// other's min point is within this
intersections.add(Penetration(normal, projectOther.component1() - projectThis.component2()))
intersections.add(Penetration(normal, projectOther.component1() - projectThis.component2(), data))
} else {
// other's max point in within this
intersections.add(Penetration(normal, projectOther.component2() - projectThis.component1()))
intersections.add(Penetration(normal, projectOther.component2() - projectThis.component1(), data))
}
if (intersections.last().penetration == 0.0) {
if (intersections.last().penetration.absoluteValue <= EPSILON) {
return null
}
}
@ -205,19 +213,23 @@ class Poly private constructor(val edges: ImmutableList<Edge>, 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
program.use()
lines.begin(GeometryType.LINES)
val normalSize = (aabb.width.coerceAtMost(aabb.height) / 3.0).coerceAtLeast(0.1)
for (edge in edges) {
val current = edge.p0
val next = edge.p1
lines.vertex(client.stack.last(), current)
lines.vertex(client.stack.last(), next)
lines.vertex(client.stack.last(), (next + current) / 2.0)
lines.vertex(client.stack.last(), (next + current) / 2.0 + edge.normal * 3.0)
lines.vertex(client.stack.last(), (next + current) / 2.0 + edge.normal * normalSize)
}
program.modelMatrix = identity
@ -227,10 +239,40 @@ class Poly private constructor(val edges: ImmutableList<Edge>, val vertices: Imm
}
override fun toString(): String {
return "Poly[edges = $edges]"
return "Poly[aabb = $aabb; edges = $edges]"
}
companion object {
override fun equals(other: Any?): Boolean {
return other === this || other is Poly && vertices == other.vertices
}
override fun hashCode(): Int {
return vertices.hashCode()
}
companion object : TypeAdapterFactory {
const val EPSILON = 0.01
private val identity = Matrix3f.identity()
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
if (type.rawType === Poly::class.java) {
return object : TypeAdapter<Poly>() {
private val list = gson.listAdapter<Vector2d>()
override fun write(out: JsonWriter, value: Poly?) {
list.write(out, value?.vertices)
}
override fun read(`in`: JsonReader): Poly? {
if (`in`.consumeNull())
return null
else
return Poly(list.read(`in`))
}
} as TypeAdapter<T>
}
return null
}
}
}