multithreaded chunk geometry batching
This commit is contained in:
parent
71a6f388fc
commit
6ae8066ebc
@ -82,7 +82,7 @@ dependencies {
|
|||||||
implementation("net.java.dev.jna:jna:5.13.0")
|
implementation("net.java.dev.jna:jna:5.13.0")
|
||||||
implementation("com.github.jnr:jnr-ffi:2.2.13")
|
implementation("com.github.jnr:jnr-ffi:2.2.13")
|
||||||
|
|
||||||
implementation("ru.dbotthepony:kbox2d:2.4.1.7")
|
implementation("ru.dbotthepony:kbox2d:2.4.1-1.0.2")
|
||||||
implementation("ru.dbotthepony:kvector:2.10.2")
|
implementation("ru.dbotthepony:kvector:2.10.2")
|
||||||
|
|
||||||
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
implementation("com.github.ben-manes.caffeine:caffeine:3.1.5")
|
||||||
|
@ -7,6 +7,7 @@ import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
|||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||||
import ru.dbotthepony.kstarbound.io.BTreeDB
|
import ru.dbotthepony.kstarbound.io.BTreeDB
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.BinaryJsonReader
|
||||||
import ru.dbotthepony.kstarbound.player.Avatar
|
import ru.dbotthepony.kstarbound.player.Avatar
|
||||||
import ru.dbotthepony.kstarbound.player.QuestDescriptor
|
import ru.dbotthepony.kstarbound.player.QuestDescriptor
|
||||||
import ru.dbotthepony.kstarbound.player.QuestInstance
|
import ru.dbotthepony.kstarbound.player.QuestInstance
|
||||||
@ -17,6 +18,7 @@ import ru.dbotthepony.kstarbound.world.entities.PlayerEntity
|
|||||||
import ru.dbotthepony.kstarbound.io.json.VersionedJson
|
import ru.dbotthepony.kstarbound.io.json.VersionedJson
|
||||||
import ru.dbotthepony.kstarbound.io.readVarInt
|
import ru.dbotthepony.kstarbound.io.readVarInt
|
||||||
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
import ru.dbotthepony.kstarbound.util.AssetPathStack
|
||||||
|
import ru.dbotthepony.kstarbound.world.entities.Move
|
||||||
import ru.dbotthepony.kstarbound.world.entities.WorldObject
|
import ru.dbotthepony.kstarbound.world.entities.WorldObject
|
||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
import java.io.BufferedInputStream
|
import java.io.BufferedInputStream
|
||||||
@ -24,8 +26,10 @@ import java.io.ByteArrayInputStream
|
|||||||
import java.io.DataInputStream
|
import java.io.DataInputStream
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.ForkJoinPool
|
||||||
import java.util.zip.Inflater
|
import java.util.zip.Inflater
|
||||||
import java.util.zip.InflaterInputStream
|
import java.util.zip.InflaterInputStream
|
||||||
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
private val LOGGER = LogManager.getLogger()
|
private val LOGGER = LogManager.getLogger()
|
||||||
|
|
||||||
@ -37,6 +41,11 @@ fun main() {
|
|||||||
val db = BTreeDB(File("F:\\SteamLibrary\\steamapps\\common\\Starbound - Unstable\\storage\\universe\\389760395_938904237_-238610574_5.world"))
|
val db = BTreeDB(File("F:\\SteamLibrary\\steamapps\\common\\Starbound - Unstable\\storage\\universe\\389760395_938904237_-238610574_5.world"))
|
||||||
//val db = BTreeDB(File("world.world"))
|
//val db = BTreeDB(File("world.world"))
|
||||||
|
|
||||||
|
//val meta = DataInputStream(BufferedInputStream(InflaterInputStream(ByteArrayInputStream(db.read(byteArrayOf(0, 0, 0, 0, 0))), Inflater())))
|
||||||
|
|
||||||
|
//println(meta.readInt())
|
||||||
|
//println(meta.readInt())
|
||||||
|
|
||||||
val client = StarboundClient()
|
val client = StarboundClient()
|
||||||
|
|
||||||
//Starbound.addFilePath(File("./unpacked_assets/"))
|
//Starbound.addFilePath(File("./unpacked_assets/"))
|
||||||
@ -64,6 +73,7 @@ fun main() {
|
|||||||
//for (chunkX in 17 .. 18) {
|
//for (chunkX in 17 .. 18) {
|
||||||
//for (chunkX in 14 .. 24) {
|
//for (chunkX in 14 .. 24) {
|
||||||
for (chunkX in 0 .. 100) {
|
for (chunkX in 0 .. 100) {
|
||||||
|
//for (chunkX in 0 .. 17) {
|
||||||
// for (chunkY in 21 .. 21) {
|
// for (chunkY in 21 .. 21) {
|
||||||
for (chunkY in 18 .. 24) {
|
for (chunkY in 18 .. 24) {
|
||||||
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
|
val data = db.read(byteArrayOf(1, 0, chunkX.toByte(), 0, chunkY.toByte()))
|
||||||
@ -114,8 +124,6 @@ fun main() {
|
|||||||
val item = Registries.items.values.random()
|
val item = Registries.items.values.random()
|
||||||
val rand = Random()
|
val rand = Random()
|
||||||
|
|
||||||
client.world!!.physics.gravity = Vector2d.ZERO
|
|
||||||
|
|
||||||
for (i in 0 .. 0) {
|
for (i in 0 .. 0) {
|
||||||
val item = ItemEntity(client.world!!, item.value)
|
val item = ItemEntity(client.world!!, item.value)
|
||||||
|
|
||||||
@ -149,7 +157,7 @@ fun main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//ent.position += Vector2d(y = 14.0, x = -10.0)
|
//ent.position += Vector2d(y = 14.0, x = -10.0)
|
||||||
ent.position = Vector2d(600.0 + 16.0, 721.0 + 48.0)
|
ent.position = Vector2d(238.0, 685.0)
|
||||||
client.camera.pos = Vector2d(238.0, 685.0)
|
client.camera.pos = Vector2d(238.0, 685.0)
|
||||||
//client.camera.pos = Vector2f(0f, 0f)
|
//client.camera.pos = Vector2f(0f, 0f)
|
||||||
|
|
||||||
@ -166,57 +174,6 @@ fun main() {
|
|||||||
//client.camera.pos.y = ent.pos.y.toFloat()
|
//client.camera.pos.y = ent.pos.y.toFloat()
|
||||||
}
|
}
|
||||||
|
|
||||||
/*val lightRenderer = LightRenderer(client.gl)
|
|
||||||
|
|
||||||
lightRenderer.resizeFramebuffer(client.viewportWidth, client.viewportHeight)
|
|
||||||
client.onViewportChanged(lightRenderer::resizeFramebuffer)
|
|
||||||
|
|
||||||
lightRenderer.addShadowGeometry(object : LightRenderer.ShadowGeometryRenderer {
|
|
||||||
override fun renderHardGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLHardLightGeometryProgram) {
|
|
||||||
val builder = lightRenderer.builder
|
|
||||||
|
|
||||||
builder.begin()
|
|
||||||
builder.quad(-6f, 0f, -2f, 2f)
|
|
||||||
builder.quad(0f, 0f, 2f, 2f)
|
|
||||||
|
|
||||||
builder.upload()
|
|
||||||
builder.draw(GL_LINES)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun renderSoftGeometry(lightPosition: Vector2f, stack: Matrix4fStack, program: GLSoftLightGeometryProgram) {
|
|
||||||
val builder = lightRenderer.builderSoft
|
|
||||||
|
|
||||||
builder.begin()
|
|
||||||
builder.shadowQuad(-6f, 0f, -2f, 2f)
|
|
||||||
builder.shadowQuad(0f, 0f, 2f, 2f)
|
|
||||||
|
|
||||||
builder.upload()
|
|
||||||
builder.draw(GL_TRIANGLES)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
client.onPostDrawWorld {
|
|
||||||
lightRenderer.begin()
|
|
||||||
|
|
||||||
for ((lightPosition, color) in listOf(
|
|
||||||
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
|
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(0.1f)) to Color.GREEN,
|
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(-0.1f)) to Color.BLUE,
|
|
||||||
)) {
|
|
||||||
lightRenderer.renderSoftLight(lightPosition, color, radius = 40f)
|
|
||||||
}
|
|
||||||
|
|
||||||
for ((lightPosition, color) in listOf(
|
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(10.1f)) to Color.RED,
|
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(10.1f)) to Color.GREEN,
|
|
||||||
(client.screenToWorld(client.mouseCoordinatesF) + Vector2f(9.9f)) to Color.BLUE,
|
|
||||||
)) {
|
|
||||||
//lightRenderer.renderSoftLight(lightPosition, color, radius = 10f)
|
|
||||||
}
|
|
||||||
|
|
||||||
lightRenderer.renderOutputAdditive()
|
|
||||||
}*/
|
|
||||||
|
|
||||||
client.box2dRenderer.drawShapes = false
|
client.box2dRenderer.drawShapes = false
|
||||||
client.box2dRenderer.drawPairs = false
|
client.box2dRenderer.drawPairs = false
|
||||||
client.box2dRenderer.drawAABB = false
|
client.box2dRenderer.drawAABB = false
|
||||||
@ -240,8 +197,8 @@ fun main() {
|
|||||||
//client.camera.pos.y = ent.position.y.toFloat()
|
//client.camera.pos.y = ent.position.y.toFloat()
|
||||||
|
|
||||||
client.camera.pos += Vector2d(
|
client.camera.pos += Vector2d(
|
||||||
(if (client.input.KEY_LEFT_DOWN || client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_RIGHT_DOWN || client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0),
|
(if (client.input.KEY_A_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_D_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0),
|
||||||
(if (client.input.KEY_UP_DOWN || client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_DOWN_DOWN || client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0)
|
(if (client.input.KEY_W_DOWN) Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0) + (if (client.input.KEY_S_DOWN) -Starbound.TICK_TIME_ADVANCE * 32f / client.settings.zoom else 0.0)
|
||||||
)
|
)
|
||||||
|
|
||||||
//println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1)
|
//println(client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1)
|
||||||
@ -249,7 +206,7 @@ fun main() {
|
|||||||
//if (ent.onGround)
|
//if (ent.onGround)
|
||||||
//ent.velocity += client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1
|
//ent.velocity += client.camera.velocity.toDoubleVector() * client.frameRenderTime * 0.1
|
||||||
|
|
||||||
/*if (client.input.KEY_LEFT_DOWN) {
|
if (client.input.KEY_LEFT_DOWN) {
|
||||||
ent.movement.moveDirection = Move.MOVE_LEFT
|
ent.movement.moveDirection = Move.MOVE_LEFT
|
||||||
} else if (client.input.KEY_RIGHT_DOWN) {
|
} else if (client.input.KEY_RIGHT_DOWN) {
|
||||||
ent.movement.moveDirection = Move.MOVE_RIGHT
|
ent.movement.moveDirection = Move.MOVE_RIGHT
|
||||||
@ -261,7 +218,7 @@ fun main() {
|
|||||||
ent.movement.requestJump()
|
ent.movement.requestJump()
|
||||||
} else if (client.input.KEY_SPACE_RELEASED) {
|
} else if (client.input.KEY_SPACE_RELEASED) {
|
||||||
ent.movement.recallJump()
|
ent.movement.recallJump()
|
||||||
}*/
|
}
|
||||||
|
|
||||||
if (client.input.KEY_ESCAPE_PRESSED) {
|
if (client.input.KEY_ESCAPE_PRESSED) {
|
||||||
glfwSetWindowShouldClose(client.window, true)
|
glfwSetWindowShouldClose(client.window, true)
|
||||||
|
@ -49,6 +49,7 @@ import ru.dbotthepony.kstarbound.client.world.ClientWorld
|
|||||||
import ru.dbotthepony.kstarbound.defs.image.Image
|
import ru.dbotthepony.kstarbound.defs.image.Image
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsNegativeInfinity
|
||||||
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
import ru.dbotthepony.kstarbound.math.roundTowardsPositiveInfinity
|
||||||
|
import ru.dbotthepony.kstarbound.util.Either
|
||||||
import ru.dbotthepony.kstarbound.util.forEachValid
|
import ru.dbotthepony.kstarbound.util.forEachValid
|
||||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||||
import ru.dbotthepony.kstarbound.world.LightCalculator
|
import ru.dbotthepony.kstarbound.world.LightCalculator
|
||||||
@ -65,20 +66,32 @@ import ru.dbotthepony.kvector.vector.Vector2i
|
|||||||
import ru.dbotthepony.kvector.vector.Vector4f
|
import ru.dbotthepony.kvector.vector.Vector4f
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ref.Cleaner
|
import java.lang.ref.PhantomReference
|
||||||
|
import java.lang.ref.ReferenceQueue
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
import java.util.concurrent.CancellationException
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
import java.util.concurrent.ExecutionException
|
||||||
|
import java.util.concurrent.ExecutorService
|
||||||
|
import java.util.concurrent.Future
|
||||||
|
import java.util.concurrent.FutureTask
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.concurrent.TimeoutException
|
||||||
import java.util.concurrent.locks.LockSupport
|
import java.util.concurrent.locks.LockSupport
|
||||||
import java.util.concurrent.locks.ReentrantLock
|
import java.util.concurrent.locks.ReentrantLock
|
||||||
import java.util.stream.StreamSupport
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
||||||
|
import java.util.function.IntConsumer
|
||||||
|
import kotlin.NoSuchElementException
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class StarboundClient : Closeable {
|
class StarboundClient : Closeable, ExecutorService {
|
||||||
val window: Long
|
val window: Long
|
||||||
val camera = Camera(this)
|
val camera = Camera(this)
|
||||||
val input = UserInput()
|
val input = UserInput()
|
||||||
@ -111,7 +124,7 @@ class StarboundClient : Closeable {
|
|||||||
var viewportTopRight = Vector2d()
|
var viewportTopRight = Vector2d()
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var fullbright = false
|
var fullbright = true
|
||||||
|
|
||||||
var clientTerminated = false
|
var clientTerminated = false
|
||||||
private set
|
private set
|
||||||
@ -128,25 +141,141 @@ class StarboundClient : Closeable {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
private val scissorStack = LinkedList<ScissorRect>()
|
private val scissorStack = LinkedList<ScissorRect>()
|
||||||
private val cleanerBacklog = ArrayList<() -> Unit>()
|
|
||||||
private val onDrawGUI = ArrayList<() -> Unit>()
|
private val onDrawGUI = ArrayList<() -> Unit>()
|
||||||
private val onPreDrawWorld = ArrayList<(LayeredRenderer) -> Unit>()
|
private val onPreDrawWorld = ArrayList<(LayeredRenderer) -> Unit>()
|
||||||
private val onPostDrawWorld = ArrayList<() -> Unit>()
|
private val onPostDrawWorld = ArrayList<() -> Unit>()
|
||||||
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
|
private val onPostDrawWorldOnce = ArrayList<(LayeredRenderer) -> Unit>()
|
||||||
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
|
private val onViewportChanged = ArrayList<(width: Int, height: Int) -> Unit>()
|
||||||
private val terminateCallbacks = ArrayList<() -> Unit>()
|
private val terminateCallbacks = ArrayList<() -> Unit>()
|
||||||
|
|
||||||
val loadingLog = LoadingLog()
|
val loadingLog = LoadingLog()
|
||||||
|
|
||||||
private val cleaner = Cleaner.create { r ->
|
private val executeQueue = ConcurrentLinkedQueue<Runnable>()
|
||||||
val thread = Thread(r, "OpenGL Cleaner for '${thread.name}'")
|
private val futureQueue = ConcurrentLinkedQueue<FutureTask<*>>()
|
||||||
thread.priority = 2
|
private val openglCleanQueue = ReferenceQueue<Any>()
|
||||||
thread
|
private var openglCleanQueueHead: CleanRef? = null
|
||||||
|
|
||||||
|
private class CleanRef(ref: Any, queue: ReferenceQueue<Any>, val fn: IntConsumer, val value: Int) : PhantomReference<Any>(ref, queue) {
|
||||||
|
var next: CleanRef? = null
|
||||||
|
var prev: CleanRef? = null
|
||||||
}
|
}
|
||||||
|
|
||||||
var objectsCreated = 0L
|
private data class InPlaceFuture<V>(private val value: V) : Future<V> {
|
||||||
|
override fun cancel(mayInterruptIfRunning: Boolean): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isCancelled(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isDone(): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(): V {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun get(timeout: Long, unit: TimeUnit): V {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val EMPTY = InPlaceFuture(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun execute(command: Runnable) {
|
||||||
|
if (isSameThread()) {
|
||||||
|
command.run()
|
||||||
|
} else {
|
||||||
|
executeQueue.add(command)
|
||||||
|
LockSupport.unpark(thread)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shutdown() {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shutdownNow(): MutableList<Runnable> {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isShutdown(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isTerminated(): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun awaitTermination(timeout: Long, unit: TimeUnit): Boolean {
|
||||||
|
throw UnsupportedOperationException()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> submit(task: Callable<T>): Future<T> {
|
||||||
|
if (isSameThread()) return InPlaceFuture(task.call())
|
||||||
|
return FutureTask(task).also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> submit(task: Runnable, result: T): Future<T> {
|
||||||
|
if (isSameThread()) { task.run(); return InPlaceFuture(result) }
|
||||||
|
return FutureTask { task.run(); result }.also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun submit(task: Runnable): Future<*> {
|
||||||
|
if (isSameThread()) { task.run(); return InPlaceFuture.EMPTY }
|
||||||
|
return FutureTask { task.run() }.also { futureQueue.add(it); LockSupport.unpark(thread) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> invokeAll(tasks: Collection<Callable<T>>): List<Future<T>> {
|
||||||
|
if (isSameThread()) {
|
||||||
|
return tasks.map { InPlaceFuture(it.call()) }
|
||||||
|
} else {
|
||||||
|
return tasks.map { submit(it) }.onEach { it.get() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> invokeAll(
|
||||||
|
tasks: Collection<Callable<T>>,
|
||||||
|
timeout: Long,
|
||||||
|
unit: TimeUnit
|
||||||
|
): List<Future<T>> {
|
||||||
|
if (isSameThread()) {
|
||||||
|
return tasks.map { InPlaceFuture(it.call()) }
|
||||||
|
} else {
|
||||||
|
return tasks.map { submit(it) }.onEach { it.get(timeout, unit) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> invokeAny(tasks: Collection<Callable<T>>): T {
|
||||||
|
if (tasks.isEmpty())
|
||||||
|
throw NoSuchElementException("Provided task list is empty")
|
||||||
|
|
||||||
|
if (isSameThread()) {
|
||||||
|
return tasks.first().call()
|
||||||
|
} else {
|
||||||
|
return submit(tasks.first()).get()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <T : Any?> invokeAny(tasks: Collection<Callable<T>>, timeout: Long, unit: TimeUnit): T {
|
||||||
|
if (tasks.isEmpty())
|
||||||
|
throw NoSuchElementException("Provided task list is empty")
|
||||||
|
|
||||||
|
if (isSameThread()) {
|
||||||
|
return tasks.first().call()
|
||||||
|
} else {
|
||||||
|
return submit(tasks.first()).get(timeout, unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var openglObjectsCreated = 0L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
var objectsCleaned = 0L
|
var openglObjectsCleaned = 0L
|
||||||
private set
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -278,35 +407,52 @@ class StarboundClient : Closeable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun registerCleanable(ref: Any, fn: (Int) -> Unit, nativeRef: Int): Cleaner.Cleanable {
|
fun registerCleanable(ref: Any, fn: IntConsumer, nativeRef: Int) {
|
||||||
objectsCreated++
|
openglObjectsCreated++
|
||||||
|
val ref0 = CleanRef(ref, openglCleanQueue, fn, nativeRef)
|
||||||
|
|
||||||
val cleanable = cleaner.register(ref) {
|
if (openglCleanQueueHead == null) {
|
||||||
if (isSameThread()) {
|
openglCleanQueueHead = ref0
|
||||||
objectsCleaned++
|
} else {
|
||||||
fn(nativeRef)
|
ref0.next = openglCleanQueueHead
|
||||||
checkForGLError()
|
openglCleanQueueHead!!.prev = ref0
|
||||||
} else {
|
openglCleanQueueHead = ref0
|
||||||
synchronized(cleanerBacklog) {
|
|
||||||
cleanerBacklog.add {
|
|
||||||
objectsCleaned++
|
|
||||||
fn(nativeRef)
|
|
||||||
checkForGLError()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cleanable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cleanup() {
|
private fun executeQueuedTasks() {
|
||||||
synchronized(cleanerBacklog) {
|
var next = executeQueue.poll()
|
||||||
for (lambda in cleanerBacklog) {
|
|
||||||
lambda.invoke()
|
while (next != null) {
|
||||||
|
next.run()
|
||||||
|
next = executeQueue.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
var next2 = futureQueue.poll()
|
||||||
|
|
||||||
|
while (next2 != null) {
|
||||||
|
next2.run()
|
||||||
|
Thread.interrupted()
|
||||||
|
next2 = futureQueue.poll()
|
||||||
|
}
|
||||||
|
|
||||||
|
var next3 = openglCleanQueue.poll() as CleanRef?
|
||||||
|
|
||||||
|
while (next3 != null) {
|
||||||
|
openglObjectsCleaned++
|
||||||
|
next3.fn.accept(next3.value)
|
||||||
|
checkForGLError("Removing unreachable OpenGL object")
|
||||||
|
|
||||||
|
val head = openglCleanQueueHead
|
||||||
|
|
||||||
|
if (next3 === head) {
|
||||||
|
openglCleanQueueHead = head.next
|
||||||
|
} else {
|
||||||
|
next3.prev?.next = next3.next
|
||||||
|
next3.next?.prev = next3.prev
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanerBacklog.clear()
|
next3 = openglCleanQueue.poll() as CleanRef?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,7 +877,7 @@ class StarboundClient : Closeable {
|
|||||||
onPostDrawWorldOnce.add(lambda)
|
onPostDrawWorldOnce.add(lambda)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val layers = LayeredRenderer()
|
private val layers = LayeredRenderer(this)
|
||||||
|
|
||||||
fun renderFrame(): Boolean {
|
fun renderFrame(): Boolean {
|
||||||
ensureSameThread()
|
ensureSameThread()
|
||||||
@ -740,6 +886,8 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
// try to sleep until next frame as precise as possible
|
// try to sleep until next frame as precise as possible
|
||||||
while (diff > 0L) {
|
while (diff > 0L) {
|
||||||
|
executeQueuedTasks()
|
||||||
|
|
||||||
if (diff >= 1_500_000L) {
|
if (diff >= 1_500_000L) {
|
||||||
LockSupport.parkNanos(1_000_000L)
|
LockSupport.parkNanos(1_000_000L)
|
||||||
} else {
|
} else {
|
||||||
@ -760,7 +908,7 @@ class StarboundClient : Closeable {
|
|||||||
val world = world
|
val world = world
|
||||||
|
|
||||||
if (!isRenderingGame) {
|
if (!isRenderingGame) {
|
||||||
cleanup()
|
executeQueuedTasks()
|
||||||
GLFW.glfwPollEvents()
|
GLFW.glfwPollEvents()
|
||||||
|
|
||||||
if (world != null && Starbound.initialized)
|
if (world != null && Starbound.initialized)
|
||||||
@ -852,7 +1000,9 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
layers.render()
|
layers.render()
|
||||||
|
|
||||||
|
box2dRenderer.begin()
|
||||||
world.physics.debugDraw()
|
world.physics.debugDraw()
|
||||||
|
box2dRenderer.render()
|
||||||
|
|
||||||
for (lambda in onPostDrawWorld) {
|
for (lambda in onPostDrawWorld) {
|
||||||
lambda.invoke()
|
lambda.invoke()
|
||||||
@ -906,7 +1056,7 @@ class StarboundClient : Closeable {
|
|||||||
font.render("Latency: ${(averageRenderWait * 1_00000.0).toInt() / 100f}ms", scale = 0.4f)
|
font.render("Latency: ${(averageRenderWait * 1_00000.0).toInt() / 100f}ms", scale = 0.4f)
|
||||||
font.render("Frame: ${(averageRenderTime * 1_00000.0).toInt() / 100f}ms", y = font.lineHeight * 0.6f, scale = 0.4f)
|
font.render("Frame: ${(averageRenderTime * 1_00000.0).toInt() / 100f}ms", y = font.lineHeight * 0.6f, scale = 0.4f)
|
||||||
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 1.2f, scale = 0.4f)
|
font.render("JVM Heap: ${formatBytesShort(runtime.totalMemory() - runtime.freeMemory())}", y = font.lineHeight * 1.2f, scale = 0.4f)
|
||||||
font.render("OGL C: $objectsCreated D: $objectsCleaned A: ${objectsCreated - objectsCleaned}", y = font.lineHeight * 1.8f, scale = 0.4f)
|
font.render("OGL C: $openglObjectsCreated D: $openglObjectsCleaned A: ${openglObjectsCreated - openglObjectsCleaned}", y = font.lineHeight * 1.8f, scale = 0.4f)
|
||||||
|
|
||||||
GLFW.glfwSwapBuffers(window)
|
GLFW.glfwSwapBuffers(window)
|
||||||
GLFW.glfwPollEvents()
|
GLFW.glfwPollEvents()
|
||||||
@ -914,7 +1064,7 @@ class StarboundClient : Closeable {
|
|||||||
|
|
||||||
camera.think(Starbound.TICK_TIME_ADVANCE)
|
camera.think(Starbound.TICK_TIME_ADVANCE)
|
||||||
|
|
||||||
cleanup()
|
executeQueuedTasks()
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -5,6 +5,7 @@ import ru.dbotthepony.kstarbound.client.StarboundClient
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
import ru.dbotthepony.kstarbound.client.gl.VertexArrayObject
|
||||||
import ru.dbotthepony.kstarbound.client.gl.BufferObject
|
import ru.dbotthepony.kstarbound.client.gl.BufferObject
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
* Быстрое наполнение буфера вершинами, загрузка в память видеокарты, и отрисовка
|
||||||
@ -13,29 +14,38 @@ class StreamVertexBuilder(
|
|||||||
attributes: VertexAttributes,
|
attributes: VertexAttributes,
|
||||||
type: GeometryType? = null,
|
type: GeometryType? = null,
|
||||||
initialCapacity: Int = 32,
|
initialCapacity: Int = 32,
|
||||||
|
val client: StarboundClient = StarboundClient.current()
|
||||||
) {
|
) {
|
||||||
val state = StarboundClient.current()
|
|
||||||
val builder = VertexBuilder(attributes, type, initialCapacity)
|
val builder = VertexBuilder(attributes, type, initialCapacity)
|
||||||
private val vao = VertexArrayObject()
|
|
||||||
private val vbo = BufferObject.VBO()
|
|
||||||
private val ebo = BufferObject.EBO()
|
|
||||||
|
|
||||||
init {
|
private val vao = client.submit(Callable { VertexArrayObject() })
|
||||||
vao.elementBuffer = ebo
|
private val vbo = client.submit(Callable { BufferObject.VBO() })
|
||||||
vao.bindAttributes(vbo, attributes)
|
private val ebo = client.submit(Callable { BufferObject.EBO() })
|
||||||
}
|
|
||||||
|
private var initialized = false
|
||||||
|
|
||||||
fun upload(drawType: Int = GL45.GL_DYNAMIC_DRAW) {
|
fun upload(drawType: Int = GL45.GL_DYNAMIC_DRAW) {
|
||||||
builder.upload(vbo, ebo, drawType)
|
if (vbo.isDone && ebo.isDone)
|
||||||
|
builder.upload(vbo.get(), ebo.get(), drawType)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun bind() = vao.bind()
|
fun bind() = vao.get().bind()
|
||||||
fun unbind() = vao.unbind()
|
fun unbind() = vao.get().unbind()
|
||||||
|
|
||||||
fun draw(primitives: Int = GL45.GL_TRIANGLES) {
|
fun draw(primitives: Int = GL45.GL_TRIANGLES) {
|
||||||
|
if (!initialized) {
|
||||||
|
vao.get().elementBuffer = ebo.get()
|
||||||
|
vao.get().bindAttributes(vbo.get(), builder.attributes)
|
||||||
|
initialized = true
|
||||||
|
}
|
||||||
|
|
||||||
bind()
|
bind()
|
||||||
GL45.glDrawElements(primitives, builder.indexCount, builder.indexType, 0L)
|
|
||||||
checkForGLError()
|
try {
|
||||||
unbind()
|
GL45.glDrawElements(primitives, builder.indexCount, builder.indexType, 0L)
|
||||||
|
checkForGLError()
|
||||||
|
} finally {
|
||||||
|
unbind()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ import kotlin.math.sin
|
|||||||
|
|
||||||
class Box2DRenderer : IDebugDraw {
|
class Box2DRenderer : IDebugDraw {
|
||||||
val state = StarboundClient.current()
|
val state = StarboundClient.current()
|
||||||
private val identity = Matrix3f.identity()
|
|
||||||
|
|
||||||
override var drawShapes: Boolean = false
|
override var drawShapes: Boolean = false
|
||||||
override var drawJoints: Boolean = false
|
override var drawJoints: Boolean = false
|
||||||
@ -25,51 +24,57 @@ class Box2DRenderer : IDebugDraw {
|
|||||||
override var drawPairs: Boolean = false
|
override var drawPairs: Boolean = false
|
||||||
override var drawCenterOfMess: Boolean = false
|
override var drawCenterOfMess: Boolean = false
|
||||||
|
|
||||||
private val builder = StreamVertexBuilder(VertexAttributes.POSITION)
|
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) {
|
override fun drawPolygon(vertices: List<Vector2d>, color: RGBAColor) {
|
||||||
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
require(vertices.size > 1) { "Vertex list had only ${vertices.size} namings in it" }
|
||||||
|
|
||||||
builder.builder.begin()
|
if (!vertices.any { state.viewportRectangle.isInside(it) }) return
|
||||||
|
|
||||||
for (i in vertices.indices) {
|
for (i in vertices.indices) {
|
||||||
val current = vertices[i]
|
val current = vertices[i]
|
||||||
val next = vertices[(i + 1) % vertices.size]
|
val next = vertices[(i + 1) % vertices.size]
|
||||||
builder.builder.vertex(current.x.toFloat(), current.y.toFloat())
|
lines.builder.vertex(state.stack.last(), current.x.toFloat(), current.y.toFloat()).color(color)
|
||||||
builder.builder.vertex(next.x.toFloat(), next.y.toFloat())
|
lines.builder.vertex(state.stack.last(), next.x.toFloat(), next.y.toFloat()).color(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
state.programs.position.use()
|
|
||||||
state.programs.position.colorMultiplier = color
|
|
||||||
state.programs.position.modelMatrix = state.stack.last()
|
|
||||||
|
|
||||||
builder.draw(GL_LINES)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun drawSolid(vertices: List<Vector2d>, color: RGBAColor) {
|
private fun drawSolid(vertices: List<Vector2d>, color: RGBAColor) {
|
||||||
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
require(vertices.size >= 3) { "Vertex list had only ${vertices.size} namings in it" }
|
||||||
|
|
||||||
builder.builder.begin(GeometryType.TRIANGLES)
|
if (!vertices.any { state.viewportRectangle.isInside(it) }) return
|
||||||
|
|
||||||
val zero = vertices[0]
|
val zero = vertices[0]
|
||||||
|
|
||||||
for (i in 1 until vertices.size) {
|
for (i in 1 until vertices.size) {
|
||||||
val current = vertices[i]
|
val current = vertices[i]
|
||||||
val next = vertices[(i + 1) % vertices.size]
|
val next = vertices[(i + 1) % vertices.size]
|
||||||
builder.builder.vertex(zero.x.toFloat(), zero.y.toFloat())
|
triangles.builder.vertex(state.stack.last(), zero.x.toFloat(), zero.y.toFloat()).color(color)
|
||||||
builder.builder.vertex(current.x.toFloat(), current.y.toFloat())
|
triangles.builder.vertex(state.stack.last(), current.x.toFloat(), current.y.toFloat()).color(color)
|
||||||
builder.builder.vertex(next.x.toFloat(), next.y.toFloat())
|
triangles.builder.vertex(state.stack.last(), next.x.toFloat(), next.y.toFloat()).color(color)
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.upload()
|
|
||||||
|
|
||||||
state.programs.position.use()
|
|
||||||
state.programs.position.colorMultiplier = color
|
|
||||||
state.programs.position.modelMatrix = state.stack.last()
|
|
||||||
|
|
||||||
builder.draw(GL_TRIANGLES)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun drawSolidPolygon(vertices: List<Vector2d>, color: RGBAColor) {
|
override fun drawSolidPolygon(vertices: List<Vector2d>, color: RGBAColor) {
|
||||||
|
@ -3,8 +3,10 @@ package ru.dbotthepony.kstarbound.client.render
|
|||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap
|
||||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectLinkedOpenHashMap
|
||||||
import org.lwjgl.opengl.GL15.GL_STREAM_DRAW
|
import org.lwjgl.opengl.GL15.GL_STREAM_DRAW
|
||||||
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.StreamVertexBuilder
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
import ru.dbotthepony.kstarbound.client.gl.vertex.VertexBuilder
|
||||||
|
import java.util.concurrent.Callable
|
||||||
import java.util.function.Function
|
import java.util.function.Function
|
||||||
import java.util.stream.Stream
|
import java.util.stream.Stream
|
||||||
|
|
||||||
@ -38,9 +40,10 @@ object OneShotGeometryLayer : IGeometryLayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LayeredRenderer {
|
class LayeredRenderer(val client: StarboundClient) {
|
||||||
class Layer(val layer: RenderLayer.Point) : IGeometryLayer {
|
private data class Tracker(val builder: StreamVertexBuilder, var emptyFrames: Int = 0)
|
||||||
private data class Tracker(val builder: StreamVertexBuilder, var emptyFrames: Int = 0)
|
|
||||||
|
inner class Layer(val layer: RenderLayer.Point) : IGeometryLayer {
|
||||||
private val meshes = ArrayList<ConfiguredMesh<*>>()
|
private val meshes = ArrayList<ConfiguredMesh<*>>()
|
||||||
private val callbacks = ArrayList<() -> Unit>()
|
private val callbacks = ArrayList<() -> Unit>()
|
||||||
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, Tracker>()
|
private val builders = Reference2ObjectLinkedOpenHashMap<RenderConfig<*>, Tracker>()
|
||||||
@ -51,7 +54,7 @@ class LayeredRenderer {
|
|||||||
|
|
||||||
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
|
override fun getBuilder(config: RenderConfig<*>): VertexBuilder {
|
||||||
return builders.computeIfAbsent(config, Function {
|
return builders.computeIfAbsent(config, Function {
|
||||||
Tracker(StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity))
|
Tracker(StreamVertexBuilder(config.program.attributes, initialCapacity = config.initialBuilderCapacity, client = client))
|
||||||
}).builder.builder
|
}).builder.builder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ import ru.dbotthepony.kvector.arrays.Matrix3f
|
|||||||
import ru.dbotthepony.kvector.vector.RGBAColor
|
import ru.dbotthepony.kvector.vector.RGBAColor
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
import ru.dbotthepony.kvector.vector.Vector2i
|
||||||
import java.time.Duration
|
import java.time.Duration
|
||||||
|
import java.util.concurrent.Callable
|
||||||
import kotlin.collections.HashMap
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,14 +52,14 @@ class TileRenderers(val client: StarboundClient) {
|
|||||||
fun getMaterialRenderer(defName: String): TileRenderer {
|
fun getMaterialRenderer(defName: String): TileRenderer {
|
||||||
return matCache.get(defName) {
|
return matCache.get(defName) {
|
||||||
val def = Registries.tiles[defName] // TODO: Пустой рендерер
|
val def = Registries.tiles[defName] // TODO: Пустой рендерер
|
||||||
TileRenderer(this, def!!.value)
|
client.submit(Callable { TileRenderer(this, def!!.value) }).get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getModifierRenderer(defName: String): TileRenderer {
|
fun getModifierRenderer(defName: String): TileRenderer {
|
||||||
return modCache.get(defName) {
|
return modCache.get(defName) {
|
||||||
val def = Registries.tileModifiers[defName] // TODO: Пустой рендерер
|
val def = Registries.tileModifiers[defName] // TODO: Пустой рендерер
|
||||||
TileRenderer(this, def!!.value)
|
client.submit(Callable { TileRenderer(this, def!!.value) }).get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,12 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap
|
|||||||
import it.unimi.dsi.fastutil.longs.LongArraySet
|
import it.unimi.dsi.fastutil.longs.LongArraySet
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceArraySet
|
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.PIXELS_IN_STARBOUND_UNITf
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||||
import ru.dbotthepony.kstarbound.client.render.ConfiguredMesh
|
import ru.dbotthepony.kstarbound.client.render.ConfiguredMesh
|
||||||
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
@ -31,6 +36,9 @@ import ru.dbotthepony.kvector.vector.RGBAColor
|
|||||||
import ru.dbotthepony.kvector.vector.Vector2d
|
import ru.dbotthepony.kvector.vector.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.Vector2f
|
import ru.dbotthepony.kvector.vector.Vector2f
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
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.PI
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
@ -80,42 +88,56 @@ class ClientWorld(
|
|||||||
inner class RenderRegion(val x: Int, val y: Int) {
|
inner class RenderRegion(val x: Int, val y: Int) {
|
||||||
inner class Layer(private val view: ITileAccess, private val isBackground: Boolean) {
|
inner class Layer(private val view: ITileAccess, private val isBackground: Boolean) {
|
||||||
val bakedMeshes = ArrayList<Pair<ConfiguredMesh<*>, RenderLayer.Point>>()
|
val bakedMeshes = ArrayList<Pair<ConfiguredMesh<*>, RenderLayer.Point>>()
|
||||||
|
private var currentBakeTask: Future<LayeredRenderer>? = null
|
||||||
var isDirty = true
|
var isDirty = true
|
||||||
|
|
||||||
fun bake() {
|
fun bake() {
|
||||||
if (!isDirty) return
|
if (!isDirty) {
|
||||||
|
val currentBakeTask = currentBakeTask ?: return
|
||||||
|
|
||||||
|
if (currentBakeTask.isDone) {
|
||||||
|
bakedMeshes.clear()
|
||||||
|
|
||||||
|
for ((baked, zLevel) in currentBakeTask.get().bakeIntoMeshes()) {
|
||||||
|
bakedMeshes.add(baked to zLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.currentBakeTask = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
isDirty = false
|
isDirty = false
|
||||||
|
|
||||||
bakedMeshes.clear()
|
currentBakeTask = ForkJoinPool.commonPool().submit(Callable {
|
||||||
|
val meshes = LayeredRenderer(client)
|
||||||
|
|
||||||
val meshes = LayeredRenderer()
|
for (x in 0 until renderRegionWidth) {
|
||||||
|
for (y in 0 until renderRegionHeight) {
|
||||||
|
if (!inBounds(x, y)) continue
|
||||||
|
|
||||||
for (x in 0 until renderRegionWidth) {
|
val tile = view.getTile(x, y) ?: continue
|
||||||
for (y in 0 until renderRegionHeight) {
|
val material = tile.material
|
||||||
if (!inBounds(x, y)) continue
|
|
||||||
|
|
||||||
val tile = view.getTile(x, y) ?: continue
|
if (!material.isMeta) {
|
||||||
val material = tile.material
|
client.tileRenderers
|
||||||
|
.getMaterialRenderer(material.materialName)
|
||||||
|
.tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground)
|
||||||
|
}
|
||||||
|
|
||||||
if (!material.isMeta) {
|
val modifier = tile.modifier
|
||||||
client.tileRenderers
|
|
||||||
.getMaterialRenderer(material.materialName)
|
|
||||||
.tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground)
|
|
||||||
}
|
|
||||||
|
|
||||||
val modifier = tile.modifier
|
if (modifier != null) {
|
||||||
|
client.tileRenderers
|
||||||
if (modifier != null) {
|
.getModifierRenderer(modifier.modName)
|
||||||
client.tileRenderers
|
.tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground, isModifier = true)
|
||||||
.getModifierRenderer(modifier.modName)
|
}
|
||||||
.tesselate(tile, view, meshes, Vector2i(x, y), isBackground = isBackground, isModifier = true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for ((baked, zLevel) in meshes.bakeIntoMeshes()) {
|
meshes
|
||||||
bakedMeshes.add(baked to zLevel)
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@ package ru.dbotthepony.kstarbound.world
|
|||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet
|
||||||
import ru.dbotthepony.kbox2d.api.BodyDef
|
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.kbox2d.dynamics.B2Fixture
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
import ru.dbotthepony.kstarbound.defs.tile.BuiltinMetaMaterials
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
@ -290,38 +292,19 @@ abstract class Chunk<WorldType : World<WorldType, This>, This : Chunk<WorldType,
|
|||||||
|
|
||||||
collisionChains.clear()
|
collisionChains.clear()
|
||||||
|
|
||||||
val xyAdd = Vector2d(pos.x * CHUNK_SIZEd, pos.y * CHUNK_SIZEd)
|
for (y in 0 .. CHUNK_SIZE_FF) {
|
||||||
val seen = BooleanArray(CHUNK_SIZE * CHUNK_SIZE)
|
|
||||||
|
|
||||||
/*for (y in 0 .. CHUNK_SIZE_FF) {
|
|
||||||
for (x in 0..CHUNK_SIZE_FF) {
|
for (x in 0..CHUNK_SIZE_FF) {
|
||||||
if (!seen[x or (y shl CHUNK_SIZE_BITS)] && cells[x, y].foreground.material != null) {
|
val cell = getCell(x, y)
|
||||||
val depthFirst = RectTileFlooderDepthFirst(this, seen, x, y)
|
|
||||||
val sizeFirst = RectTileFlooderSizeFirst(this, seen, x, y)
|
|
||||||
val xSpanDepth = depthFirst.maxs.x - depthFirst.mins.x
|
|
||||||
val ySpanDepth = depthFirst.maxs.y - depthFirst.mins.y
|
|
||||||
val xSpanSize = sizeFirst.maxs.x - sizeFirst.mins.x
|
|
||||||
val ySpanSize = sizeFirst.maxs.y - sizeFirst.mins.y
|
|
||||||
|
|
||||||
val aabb: AABB
|
|
||||||
|
|
||||||
if (xSpanDepth * ySpanDepth > xSpanSize * ySpanSize) {
|
|
||||||
depthFirst.markSeen()
|
|
||||||
aabb = AABB(depthFirst.mins.toDoubleVector(), depthFirst.maxs.toDoubleVector() + Vector2d.POSITIVE_XY)
|
|
||||||
} else {
|
|
||||||
sizeFirst.markSeen()
|
|
||||||
aabb = AABB(sizeFirst.mins.toDoubleVector(), sizeFirst.maxs.toDoubleVector() + Vector2d.POSITIVE_XY)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/*if (!cell.foreground.material.collisionKind.isEmpty) {
|
||||||
collisionChains.add(body.createFixture(FixtureDef(
|
collisionChains.add(body.createFixture(FixtureDef(
|
||||||
shape = PolygonShape().also { it.setAsBox(aabb.width / 2.0, aabb.height / 2.0, aabb.centre, 0.0) },
|
shape = PolygonShape().also { it.setAsBox(0.5, 0.5, Vector2d(x + 0.5, y + 0.5), 0.0) },
|
||||||
friction = 0.4,
|
friction = 0.4,
|
||||||
|
userData = cell,
|
||||||
)))
|
)))
|
||||||
|
}*/
|
||||||
collisionCache.add(aabb + xyAdd)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,177 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.phys
|
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_BITS
|
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
|
|
||||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
|
||||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
|
||||||
import ru.dbotthepony.kvector.arrays.Object2DArray
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
|
||||||
|
|
||||||
class RectTileFlooderDepthFirst(
|
|
||||||
private val tiles: ICellAccess,
|
|
||||||
private val seen: BooleanArray,
|
|
||||||
rootx: Int,
|
|
||||||
rooty: Int
|
|
||||||
) {
|
|
||||||
val mins: Vector2i
|
|
||||||
val maxs: Vector2i
|
|
||||||
|
|
||||||
private fun filled(x: Int, y: Int): Boolean {
|
|
||||||
if (x < 0 || x > CHUNK_SIZE_FF) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y < 0 || y > CHUNK_SIZE_FF) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return !seen[x or (y shl CHUNK_SIZE_BITS)] && tiles.getCell(x, y)?.foreground?.material != null
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
// expand wide
|
|
||||||
var widthPositive = 1
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (!filled(rootx + widthPositive, rooty)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
widthPositive++
|
|
||||||
}
|
|
||||||
|
|
||||||
var widthNegative = 1
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (!filled(rootx - widthNegative, rooty)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
widthNegative++
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand tall
|
|
||||||
var heightPositive = 1
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (!filled(rootx, rooty + heightPositive)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
heightPositive++
|
|
||||||
}
|
|
||||||
|
|
||||||
var heightNegative = 1
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (!filled(rootx, rooty - heightNegative)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
heightNegative++
|
|
||||||
}
|
|
||||||
|
|
||||||
widthPositive -= 1
|
|
||||||
widthNegative -= 1
|
|
||||||
|
|
||||||
heightNegative -= 1
|
|
||||||
heightPositive -= 1
|
|
||||||
|
|
||||||
if (heightPositive + heightNegative > widthPositive + widthNegative) {
|
|
||||||
// height is bigger
|
|
||||||
// try to expand wide
|
|
||||||
widthPositive = 0
|
|
||||||
widthNegative = 0
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var escape = false
|
|
||||||
|
|
||||||
for (i in -heightNegative .. heightPositive) {
|
|
||||||
if (!filled(rootx + widthPositive, rooty + i)) {
|
|
||||||
escape = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (escape) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
widthPositive++
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var escape = false
|
|
||||||
|
|
||||||
for (i in -heightNegative .. heightPositive) {
|
|
||||||
if (!filled(rootx - widthNegative, rooty + i)) {
|
|
||||||
escape = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (escape) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
widthNegative++
|
|
||||||
}
|
|
||||||
|
|
||||||
widthNegative -= 1
|
|
||||||
widthPositive -= 1
|
|
||||||
} else {
|
|
||||||
// height is equal or lesser than width
|
|
||||||
// expand high
|
|
||||||
heightNegative = 0
|
|
||||||
heightPositive = 0
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var escape = false
|
|
||||||
|
|
||||||
for (i in -widthNegative .. widthPositive) {
|
|
||||||
if (!filled(rootx + i, rooty + heightPositive)) {
|
|
||||||
escape = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (escape) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
heightPositive++
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
var escape = false
|
|
||||||
|
|
||||||
for (i in -widthNegative .. widthPositive) {
|
|
||||||
if (!filled(rootx + i, rooty - heightNegative)) {
|
|
||||||
escape = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (escape) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
heightNegative++
|
|
||||||
}
|
|
||||||
|
|
||||||
heightNegative -= 1
|
|
||||||
heightPositive -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
mins = Vector2i(rootx - widthNegative, rooty - heightNegative)
|
|
||||||
maxs = Vector2i(rootx + widthPositive, rooty + heightPositive)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markSeen() {
|
|
||||||
for (x in mins.x .. maxs.x) {
|
|
||||||
for (y in mins.y .. maxs.y) {
|
|
||||||
seen[x or (y shl CHUNK_SIZE_BITS)] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.world.phys
|
|
||||||
|
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_BITS
|
|
||||||
import ru.dbotthepony.kstarbound.world.CHUNK_SIZE_FF
|
|
||||||
import ru.dbotthepony.kstarbound.world.api.ICellAccess
|
|
||||||
import ru.dbotthepony.kstarbound.world.api.IChunkCell
|
|
||||||
import ru.dbotthepony.kvector.arrays.Object2DArray
|
|
||||||
import ru.dbotthepony.kvector.vector.Vector2i
|
|
||||||
|
|
||||||
class RectTileFlooderSizeFirst(
|
|
||||||
private val tiles: ICellAccess,
|
|
||||||
private val seen: BooleanArray,
|
|
||||||
private val rootx: Int,
|
|
||||||
private val rooty: Int
|
|
||||||
) {
|
|
||||||
val mins: Vector2i
|
|
||||||
val maxs: Vector2i
|
|
||||||
|
|
||||||
private fun filled(x: Int, y: Int): Boolean {
|
|
||||||
if (x < 0 || x > CHUNK_SIZE_FF) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y < 0 || y > CHUNK_SIZE_FF) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return !seen[x or (y shl CHUNK_SIZE_BITS)] && tiles.getCell(x, y)?.foreground?.material != null
|
|
||||||
}
|
|
||||||
|
|
||||||
private var widthPositive = 0
|
|
||||||
private var widthNegative = 0
|
|
||||||
private var heightPositive = 0
|
|
||||||
private var heightNegative = 0
|
|
||||||
|
|
||||||
private fun checkLeft(): Boolean {
|
|
||||||
for (i in -heightNegative .. heightPositive) {
|
|
||||||
if (!filled(rootx - widthNegative, rooty + i)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkRight(): Boolean {
|
|
||||||
for (i in -heightNegative .. heightPositive) {
|
|
||||||
if (!filled(rootx + widthPositive, rooty + i)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkUp(): Boolean {
|
|
||||||
for (i in -widthNegative .. widthPositive) {
|
|
||||||
if (!filled(rootx + i, rooty + heightPositive)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun checkDown(): Boolean {
|
|
||||||
for (i in -widthNegative .. widthPositive) {
|
|
||||||
if (!filled(rootx + i, rooty - heightNegative)) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
var expanded = true
|
|
||||||
var hitLeft = false
|
|
||||||
var hitRight = false
|
|
||||||
var hitUp = false
|
|
||||||
var hitDown = false
|
|
||||||
|
|
||||||
while (expanded) {
|
|
||||||
expanded = false
|
|
||||||
|
|
||||||
// expand left
|
|
||||||
if (!hitLeft) {
|
|
||||||
widthNegative++
|
|
||||||
|
|
||||||
if (!checkLeft()) {
|
|
||||||
widthNegative--
|
|
||||||
hitLeft = true
|
|
||||||
} else {
|
|
||||||
expanded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand up
|
|
||||||
if (!hitUp) {
|
|
||||||
heightPositive++
|
|
||||||
|
|
||||||
if (!checkUp()) {
|
|
||||||
heightPositive--
|
|
||||||
hitUp = true
|
|
||||||
} else {
|
|
||||||
expanded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand right
|
|
||||||
if (!hitRight) {
|
|
||||||
widthPositive++
|
|
||||||
|
|
||||||
if (!checkRight()) {
|
|
||||||
widthPositive--
|
|
||||||
hitRight = true
|
|
||||||
} else {
|
|
||||||
expanded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// expand down
|
|
||||||
if (!hitDown) {
|
|
||||||
heightNegative++
|
|
||||||
|
|
||||||
if (!checkDown()) {
|
|
||||||
heightNegative--
|
|
||||||
hitDown = true
|
|
||||||
} else {
|
|
||||||
expanded = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mins = Vector2i(rootx - widthNegative, rooty - heightNegative)
|
|
||||||
maxs = Vector2i(rootx + widthPositive, rooty + heightPositive)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markSeen() {
|
|
||||||
for (x in mins.x .. maxs.x) {
|
|
||||||
for (y in mins.y .. maxs.y) {
|
|
||||||
seen[x or (y shl CHUNK_SIZE_BITS)] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user