непонятно как делать animator
This commit is contained in:
parent
ed12e99d43
commit
0052adf89a
@ -44,6 +44,7 @@ import ru.dbotthepony.kstarbound.io.json.AABBiTypeAdapter
|
|||||||
import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.EitherTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
import ru.dbotthepony.kstarbound.io.json.InternedJsonElementAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.InternedStringAdapter
|
import ru.dbotthepony.kstarbound.io.json.InternedStringAdapter
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.NothingAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2dTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2fTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.Vector2iTypeAdapter
|
||||||
@ -110,6 +111,8 @@ class Starbound : ISBFileLocator {
|
|||||||
registerTypeAdapter(InternedStringAdapter(stringInterner))
|
registerTypeAdapter(InternedStringAdapter(stringInterner))
|
||||||
registerTypeAdapter(InternedJsonElementAdapter(stringInterner))
|
registerTypeAdapter(InternedJsonElementAdapter(stringInterner))
|
||||||
|
|
||||||
|
registerTypeAdapter(Nothing::class.java, NothingAdapter)
|
||||||
|
|
||||||
// Обработчик @JsonImplementation
|
// Обработчик @JsonImplementation
|
||||||
registerTypeAdapterFactory(JsonImplementationTypeFactory)
|
registerTypeAdapterFactory(JsonImplementationTypeFactory)
|
||||||
|
|
||||||
@ -387,7 +390,7 @@ class Starbound : ISBFileLocator {
|
|||||||
loadStage(callback, _particles, ext2files["particle"] ?: listOf())
|
loadStage(callback, _particles, ext2files["particle"] ?: listOf())
|
||||||
|
|
||||||
pathStack.block("/") {
|
pathStack.block("/") {
|
||||||
playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
|
//playerDefinition = gson.fromJson(locate("/player.config").reader(), PlayerDefinition::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
initializing = false
|
initializing = false
|
||||||
|
@ -11,8 +11,8 @@ import ru.dbotthepony.kstarbound.client.gl.vertex.quad
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowLine
|
import ru.dbotthepony.kstarbound.client.gl.vertex.shadowLine
|
||||||
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
import ru.dbotthepony.kstarbound.client.render.ConfiguredStaticMesh
|
||||||
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
import ru.dbotthepony.kstarbound.client.render.entity.EntityRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
||||||
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
import ru.dbotthepony.kstarbound.client.render.TileLayerList
|
||||||
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
import ru.dbotthepony.kstarbound.defs.tile.LiquidDefinition
|
||||||
import ru.dbotthepony.kstarbound.world.*
|
import ru.dbotthepony.kstarbound.world.*
|
||||||
@ -166,22 +166,6 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
backgroundRenderer.loadRenderers(getBackgroundView())
|
backgroundRenderer.loadRenderers(getBackgroundView())
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрисовывает всю геометрию напрямую
|
|
||||||
*/
|
|
||||||
fun render(stack: Matrix4fStack) {
|
|
||||||
backgroundRenderer.render(stack)
|
|
||||||
foregroundRenderer.render(stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Отрисовывает всю геометрию напрямую, с проверкой, изменился ли чанк
|
|
||||||
*/
|
|
||||||
fun bakeAndRender(stack: Matrix4fStack) {
|
|
||||||
backgroundRenderer.bakeAndRender(stack, this::getBackgroundView)
|
|
||||||
foregroundRenderer.bakeAndRender(stack, this::getForegroundView)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Тесселирует "статичную" геометрию в builders (к примеру тайлы), с проверкой, изменилось ли что либо,
|
* Тесселирует "статичную" геометрию в builders (к примеру тайлы), с проверкой, изменилось ли что либо,
|
||||||
* и загружает её в видеопамять.
|
* и загружает её в видеопамять.
|
||||||
@ -293,15 +277,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
inner class Renderer(val renderOrigin: ChunkPos = pos) : GPULightRenderer.ShadowGeometryRenderer {
|
||||||
* Хранит состояние отрисовки этого чанка
|
|
||||||
*
|
|
||||||
* Должен быть использован только один раз, после выкинут, иначе поведение
|
|
||||||
* кода невозможно будет предсказать
|
|
||||||
*/
|
|
||||||
inner class OneShotRenderer constructor(val origin: ChunkPos = pos) : ILayeredRenderer, GPULightRenderer.ShadowGeometryRenderer {
|
|
||||||
private val layerQueue = ArrayDeque<Pair<(Matrix4fStack) -> Unit, Int>>()
|
|
||||||
|
|
||||||
override fun renderHardGeometry(
|
override fun renderHardGeometry(
|
||||||
renderer: GPULightRenderer,
|
renderer: GPULightRenderer,
|
||||||
lightPosition: Vector2f,
|
lightPosition: Vector2f,
|
||||||
@ -309,7 +285,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
stack: Matrix4fStack,
|
stack: Matrix4fStack,
|
||||||
program: GLHardLightGeometryProgram
|
program: GLHardLightGeometryProgram
|
||||||
) {
|
) {
|
||||||
if (!intersectCircleRectangle(lightPosition, lightRadius, origin.x * CHUNK_SIZEf, origin.y * CHUNK_SIZEf, (origin.x + 1) * CHUNK_SIZEf, (origin.y + 1) * CHUNK_SIZEf)) {
|
if (!intersectCircleRectangle(lightPosition, lightRadius, renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf, (renderOrigin.x + 1) * CHUNK_SIZEf, (renderOrigin.y + 1) * CHUNK_SIZEf)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,16 +295,16 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
if (intersectCircleRectangle(
|
if (intersectCircleRectangle(
|
||||||
lightPosition,
|
lightPosition,
|
||||||
lightRadius,
|
lightRadius,
|
||||||
origin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
|
renderOrigin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
|
||||||
origin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
|
renderOrigin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
|
||||||
origin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
|
renderOrigin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
|
||||||
origin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
|
renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
|
||||||
) {
|
) {
|
||||||
if (!setOnce) {
|
if (!setOnce) {
|
||||||
program.localToWorldTransform.set(
|
program.localToWorldTransform.set(
|
||||||
Matrix4f.IDENTITY.translateWithMultiplication(
|
Matrix4f.IDENTITY.translateWithMultiplication(
|
||||||
Vector3f(x = origin.x * CHUNK_SIZEf,
|
Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
|
||||||
y = origin.y * CHUNK_SIZEf)))
|
y = renderOrigin.y * CHUNK_SIZEf)))
|
||||||
|
|
||||||
setOnce = true
|
setOnce = true
|
||||||
}
|
}
|
||||||
@ -345,7 +321,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
stack: Matrix4fStack,
|
stack: Matrix4fStack,
|
||||||
program: GLSoftLightGeometryProgram
|
program: GLSoftLightGeometryProgram
|
||||||
) {
|
) {
|
||||||
if (!intersectCircleRectangle(lightPosition, lightRadius, origin.x * CHUNK_SIZEf, origin.y * CHUNK_SIZEf, (origin.x + 1) * CHUNK_SIZEf, (origin.y + 1) * CHUNK_SIZEf)) {
|
if (!intersectCircleRectangle(lightPosition, lightRadius, renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf, (renderOrigin.x + 1) * CHUNK_SIZEf, (renderOrigin.y + 1) * CHUNK_SIZEf)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,18 +329,18 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
|
|
||||||
for (geometry in shadowGeometry) {
|
for (geometry in shadowGeometry) {
|
||||||
if (intersectCircleRectangle(
|
if (intersectCircleRectangle(
|
||||||
lightPosition,
|
lightPosition,
|
||||||
lightRadius,
|
lightRadius,
|
||||||
origin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
|
renderOrigin.x * CHUNK_SIZEf + geometry.x * SHADOW_GEOMETRY_SQUARE_SIZE,
|
||||||
origin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
|
renderOrigin.y * CHUNK_SIZEf + geometry.y * SHADOW_GEOMETRY_SQUARE_SIZE,
|
||||||
origin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
|
renderOrigin.x * CHUNK_SIZEf + (geometry.x + 1) * SHADOW_GEOMETRY_SQUARE_SIZE,
|
||||||
origin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
|
renderOrigin.y * CHUNK_SIZEf + (geometry.y + 1) * SHADOW_GEOMETRY_SQUARE_SIZE)
|
||||||
) {
|
) {
|
||||||
if (!setOnce) {
|
if (!setOnce) {
|
||||||
program.localToWorldTransform.set(
|
program.localToWorldTransform.set(
|
||||||
Matrix4f.IDENTITY.translateWithMultiplication(
|
Matrix4f.IDENTITY.translateWithMultiplication(
|
||||||
Vector3f(x = origin.x * CHUNK_SIZEf,
|
Vector3f(x = renderOrigin.x * CHUNK_SIZEf,
|
||||||
y = origin.y * CHUNK_SIZEf)))
|
y = renderOrigin.y * CHUNK_SIZEf)))
|
||||||
|
|
||||||
setOnce = true
|
setOnce = true
|
||||||
}
|
}
|
||||||
@ -374,26 +350,34 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
fun addLayers(layers: LayeredRenderer) {
|
||||||
for ((baked, zLevel) in backgroundRenderer.bakedMeshes) {
|
for ((baked, zLevel) in backgroundRenderer.bakedMeshes) {
|
||||||
layerQueue.add(baked::renderStacked to (zLevel + Z_LEVEL_BACKGROUND))
|
layers.add(zLevel + Z_LEVEL_BACKGROUND) {
|
||||||
|
it.push().translateWithMultiplication(renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf)
|
||||||
|
baked.renderStacked(it)
|
||||||
|
it.pop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for ((baked, zLevel) in foregroundRenderer.bakedMeshes) {
|
for ((baked, zLevel) in foregroundRenderer.bakedMeshes) {
|
||||||
layerQueue.add(baked::renderStacked to zLevel)
|
layers.add(zLevel) {
|
||||||
|
it.push().translateWithMultiplication(renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf)
|
||||||
|
baked.renderStacked(it)
|
||||||
|
it.pop()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (renderer in entityRenderers.values) {
|
for (renderer in entityRenderers.values) {
|
||||||
layerQueue.add(lambda@{ it: Matrix4fStack ->
|
layers.add(renderer.layer) {
|
||||||
val relative = renderer.renderPos - posVector2d
|
val relative = renderer.renderPos - posVector2d
|
||||||
it.push().translateWithMultiplication(relative.x.toFloat(), relative.y.toFloat())
|
it.push().translateWithMultiplication(renderOrigin.x * CHUNK_SIZEf + relative.x.toFloat(), renderOrigin.y * CHUNK_SIZEf + relative.y.toFloat())
|
||||||
renderer.render(it)
|
renderer.render(it)
|
||||||
it.pop()
|
it.pop()
|
||||||
Unit
|
}
|
||||||
} to renderer.layer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
layerQueue.add({ it: Matrix4fStack ->
|
layers.add(Z_LEVEL_LIQUID) {
|
||||||
|
it.push().translateWithMultiplication(renderOrigin.x * CHUNK_SIZEf, renderOrigin.y * CHUNK_SIZEf)
|
||||||
val types = ArrayList<LiquidDefinition>()
|
val types = ArrayList<LiquidDefinition>()
|
||||||
|
|
||||||
for (x in 0 until CHUNK_SIZE) {
|
for (x in 0 until CHUNK_SIZE) {
|
||||||
@ -431,44 +415,10 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
|||||||
builder.upload()
|
builder.upload()
|
||||||
builder.draw()
|
builder.draw()
|
||||||
}
|
}
|
||||||
} to Z_LEVEL_LIQUID)
|
|
||||||
|
|
||||||
layerQueue.sortBy {
|
it.pop()
|
||||||
return@sortBy it.second
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun renderLayerFromStack(zPos: Int, stack: Matrix4fStack): Int {
|
|
||||||
if (layerQueue.isEmpty())
|
|
||||||
return Int.MIN_VALUE
|
|
||||||
|
|
||||||
stack.push().translateWithMultiplication(x = origin.x * CHUNK_SIZEf, y = origin.y * CHUNK_SIZEf)
|
|
||||||
var pair = layerQueue.last()
|
|
||||||
|
|
||||||
while (pair.second >= zPos) {
|
|
||||||
pair.first.invoke(stack)
|
|
||||||
|
|
||||||
layerQueue.removeLast()
|
|
||||||
|
|
||||||
if (layerQueue.isEmpty()) {
|
|
||||||
stack.pop()
|
|
||||||
return Int.MIN_VALUE
|
|
||||||
}
|
|
||||||
|
|
||||||
pair = layerQueue.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
stack.pop()
|
|
||||||
return layerQueue.last().second
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun bottomMostZLevel(): Int {
|
|
||||||
if (layerQueue.isEmpty()) {
|
|
||||||
return Int.MIN_VALUE
|
|
||||||
}
|
|
||||||
|
|
||||||
return layerQueue.last().second
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val entityRenderers = HashMap<Entity, EntityRenderer>()
|
private val entityRenderers = HashMap<Entity, EntityRenderer>()
|
||||||
|
@ -1,13 +1,7 @@
|
|||||||
package ru.dbotthepony.kstarbound.client
|
package ru.dbotthepony.kstarbound.client
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46.*
|
import ru.dbotthepony.kstarbound.client.render.LayeredRenderer
|
||||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.quadZ
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.ILayeredRenderer
|
|
||||||
import ru.dbotthepony.kstarbound.client.render.renderLayeredList
|
|
||||||
import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB
|
import ru.dbotthepony.kstarbound.math.encasingChunkPosAABB
|
||||||
import ru.dbotthepony.kstarbound.util.DoubleEdgeProgression
|
|
||||||
import ru.dbotthepony.kstarbound.world.*
|
import ru.dbotthepony.kstarbound.world.*
|
||||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
@ -38,18 +32,17 @@ class ClientWorld(
|
|||||||
size: AABB,
|
size: AABB,
|
||||||
isScreenspaceRender: Boolean = true
|
isScreenspaceRender: Boolean = true
|
||||||
) {
|
) {
|
||||||
val determineRenderers = ArrayList<ILayeredRenderer>()
|
val layers = LayeredRenderer()
|
||||||
|
|
||||||
client.lightRenderer.begin()
|
client.lightRenderer.begin()
|
||||||
|
|
||||||
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
|
for (chunk in collectPositionAware(size.encasingChunkPosAABB())) {
|
||||||
val renderer = chunk.second.OneShotRenderer(chunk.first)
|
val renderer = chunk.second.Renderer(chunk.first)
|
||||||
determineRenderers.add(renderer)
|
renderer.addLayers(layers)
|
||||||
chunk.second.bake()
|
chunk.second.bake()
|
||||||
//client.lightRenderer.addShadowGeometry(renderer)
|
//client.lightRenderer.addShadowGeometry(renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
renderLayeredList(client.gl.matrixStack, determineRenderers)
|
layers.render(client.gl.matrixStack)
|
||||||
/*
|
/*
|
||||||
for ((lightPosition, color) in listOf(
|
for ((lightPosition, color) in listOf(
|
||||||
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
|
(client.screenToWorld(client.mouseCoordinatesF)) to Color.RED,
|
||||||
|
@ -18,6 +18,8 @@ import ru.dbotthepony.kstarbound.client.render.Camera
|
|||||||
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
import ru.dbotthepony.kstarbound.client.render.GPULightRenderer
|
||||||
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
import ru.dbotthepony.kstarbound.client.render.TextAlignY
|
||||||
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
import ru.dbotthepony.kstarbound.client.render.TileRenderers
|
||||||
|
import ru.dbotthepony.kstarbound.util.JVMTimeSource
|
||||||
|
import ru.dbotthepony.kstarbound.util.PausableTimeSource
|
||||||
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
import ru.dbotthepony.kstarbound.util.formatBytesShort
|
||||||
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
|
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
|
||||||
import ru.dbotthepony.kvector.util2d.AABB
|
import ru.dbotthepony.kvector.util2d.AABB
|
||||||
@ -25,6 +27,7 @@ import ru.dbotthepony.kvector.vector.Color
|
|||||||
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
||||||
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
import ru.dbotthepony.kvector.vector.nfloat.Vector2f
|
||||||
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
|
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
|
||||||
|
import java.io.Closeable
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@ -32,7 +35,8 @@ import java.util.concurrent.locks.LockSupport
|
|||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class StarboundClient(val starbound: Starbound) : AutoCloseable {
|
class StarboundClient(val starbound: Starbound) : Closeable {
|
||||||
|
val time = PausableTimeSource(JVMTimeSource.INSTANCE)
|
||||||
val window: Long
|
val window: Long
|
||||||
val camera = Camera(this)
|
val camera = Camera(this)
|
||||||
val input = UserInput()
|
val input = UserInput()
|
||||||
|
@ -4,6 +4,10 @@ import org.lwjgl.opengl.GL46
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject
|
import ru.dbotthepony.kstarbound.client.gl.VertexBufferObject
|
||||||
import ru.dbotthepony.kstarbound.util.ByteBufferOutputStream
|
import ru.dbotthepony.kstarbound.util.ByteBufferOutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создаёт буфер для данных вне кучи, записывает данные напрямую в него,
|
||||||
|
* при [upload] загружает данные из буфера напрямую в память видеокарты.
|
||||||
|
*/
|
||||||
open class DirectVertexBuilder<T : DirectVertexBuilder<T>>(
|
open class DirectVertexBuilder<T : DirectVertexBuilder<T>>(
|
||||||
attributes: GLAttributeList,
|
attributes: GLAttributeList,
|
||||||
type: GeometryType,
|
type: GeometryType,
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.gl.vertex
|
package ru.dbotthepony.kstarbound.client.gl.vertex
|
||||||
|
|
||||||
import org.lwjgl.opengl.GL46
|
import org.lwjgl.opengl.GL46
|
||||||
|
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
|
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||||
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
import ru.dbotthepony.kstarbound.client.gl.checkForGLError
|
||||||
import java.io.Closeable
|
import java.io.Closeable
|
||||||
|
|
||||||
@ -48,4 +50,21 @@ class StreamVertexBuilder(
|
|||||||
vbo.close()
|
vbo.close()
|
||||||
ebo.close()
|
ebo.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun singleSprite(x: Float, y: Float, width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
||||||
|
begin()
|
||||||
|
|
||||||
|
quadRotatedZ(x, y, width, height, z, 0f, 0f, angle, transformer)
|
||||||
|
|
||||||
|
upload()
|
||||||
|
draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun singleSprite(width: Float, height: Float, z: Float = 5f, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
||||||
|
singleSprite(-width / 2f, -height / 2f, width / 2f, height / 2f, z, angle, transformer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun singleSprite(width: Float, height: Float, angle: Double = 0.0, transformer: QuadVertexTransformer) {
|
||||||
|
singleSprite(-width / 2f, -height / 2f, width / 2f, height / 2f, 0f, angle, transformer)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
|
import ru.dbotthepony.kstarbound.client.ClientWorld
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||||
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
|
|
||||||
|
class Animator(
|
||||||
|
val world: ClientWorld,
|
||||||
|
val def: AnimationDefinition
|
||||||
|
) {
|
||||||
|
val frameAnimator: FrameAnimator?
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (def.frames != null && def.animationCycle != null && def.frameNumber != null) {
|
||||||
|
frameAnimator = FrameAnimator(lastFrame = def.frameNumber - 1, time = world.client.time, animationCycle = def.animationCycle)
|
||||||
|
} else {
|
||||||
|
frameAnimator = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(stack: Matrix4fStack) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,20 +1,21 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW.glfwGetTime
|
import ru.dbotthepony.kstarbound.util.ITimeSource
|
||||||
|
import ru.dbotthepony.kstarbound.util.JVMTimeSource
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Таймер для анимирования набора спрайтов
|
* Таймер для анимирования набора спрайтов
|
||||||
*/
|
*/
|
||||||
open class FrameAnimator(
|
class FrameAnimator(
|
||||||
/**
|
/**
|
||||||
* Первый кадр в анимации
|
* Первый кадр в анимации
|
||||||
*/
|
*/
|
||||||
var firstFrame: Int = 0,
|
val firstFrame: Int = 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Последний кадр в анимации
|
* Последний кадр в анимации
|
||||||
*/
|
*/
|
||||||
var lastFrame: Int,
|
val lastFrame: Int,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сколько времени занимает один кадр
|
* Сколько времени занимает один кадр
|
||||||
@ -25,38 +26,31 @@ open class FrameAnimator(
|
|||||||
* Зациклить ли анимацию
|
* Зациклить ли анимацию
|
||||||
*/
|
*/
|
||||||
var animationLoops: Boolean = true,
|
var animationLoops: Boolean = true,
|
||||||
|
|
||||||
|
val time: ITimeSource = JVMTimeSource.INSTANCE
|
||||||
) {
|
) {
|
||||||
var frame = 0
|
var frame = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращает разницу между последним и первым кадром анимации
|
|
||||||
*/
|
|
||||||
val frameDiff get() = lastFrame - firstFrame
|
|
||||||
|
|
||||||
private val initial = glfwGetTime()
|
|
||||||
private var lastRender = initial
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Сколько времени прошло с момента последнего кадра
|
|
||||||
*/
|
|
||||||
val delta get() = glfwGetTime() - lastRender
|
|
||||||
|
|
||||||
private var counter = 0.0
|
private var counter = 0.0
|
||||||
|
private var lastRender = time.seconds
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Проверяет glfw таймер и продвигает фрейм анимации
|
* Разница между последним и первым кадром анимации
|
||||||
*/
|
*/
|
||||||
fun advance() {
|
val frameDiff = lastFrame - firstFrame
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяет таймер на [seconds] и продвигает фрейм анимации
|
||||||
|
*/
|
||||||
|
fun advance(seconds: Double) {
|
||||||
if (frameDiff == 0)
|
if (frameDiff == 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
if (frame + firstFrame >= lastFrame && !animationLoops) {
|
if (frame + firstFrame >= lastFrame && !animationLoops)
|
||||||
return
|
return
|
||||||
}
|
|
||||||
|
|
||||||
counter += delta / animationCycle
|
counter += seconds / animationCycle
|
||||||
lastRender = glfwGetTime()
|
|
||||||
|
|
||||||
if (counter >= 1.0) {
|
if (counter >= 1.0) {
|
||||||
val desired = frame + counter.toInt()
|
val desired = frame + counter.toInt()
|
||||||
@ -69,4 +63,14 @@ open class FrameAnimator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяет таймер используя [time] и продвигает фрейм анимации
|
||||||
|
*/
|
||||||
|
fun advance() {
|
||||||
|
if (frameDiff != 0) {
|
||||||
|
advance(time.seconds - lastRender)
|
||||||
|
lastRender = time.seconds
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
|
||||||
|
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Интерфейс для отрисовки комплексных объектов, в которых множество слоёв, который
|
|
||||||
* определяет лишь один метод: [renderLayer]
|
|
||||||
*
|
|
||||||
* Используется вместе с другими [ILayeredRenderer], где необходимо отрисовывать комплексную сцену,
|
|
||||||
* реализуя ручную сортировку геометрии.
|
|
||||||
*/
|
|
||||||
interface ILayeredRenderer {
|
|
||||||
/**
|
|
||||||
* Главный метод отрисовки данной стопки слоёв. Вызов данного метода может что-либо
|
|
||||||
* отрисовать, а может вообще ничего не отрисовать.
|
|
||||||
*
|
|
||||||
* zLevel всегда положителен, и указывает на то, какой слой (или все слои за) надо отрисовать,
|
|
||||||
* т.е. при вызове этого метода отрисовываются все слои, у которых z позиция >= [zPos]
|
|
||||||
*
|
|
||||||
* Возвращается zNew следующего слоя (такой, что [zPos] > zNew).
|
|
||||||
*
|
|
||||||
* Если следующего слоя нет, вернуть [Int.MIN_VALUE], и данный объект
|
|
||||||
* будет считаться отрисованным.
|
|
||||||
*/
|
|
||||||
fun renderLayerFromStack(zPos: Int, stack: Matrix4fStack): Int
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Возвращает наибольшее zPos в данной стопке.
|
|
||||||
*
|
|
||||||
* Если стопка пуста, то необходимо вернуть [Int.MIN_VALUE].
|
|
||||||
*
|
|
||||||
* В зависимости от сцены, которую необходимо отрисовать,
|
|
||||||
* [renderLayerFromStack] может быть вызван сразу с этим же значением,
|
|
||||||
* если этот объект имеет самый дальний слой
|
|
||||||
*/
|
|
||||||
fun bottomMostZLevel(): Int
|
|
||||||
}
|
|
||||||
|
|
||||||
fun renderLayeredList(transform: Matrix4fStack, potentialRenderers: List<ILayeredRenderer>): Int {
|
|
||||||
val renderers = ArrayList<ILayeredRenderer>(potentialRenderers.size)
|
|
||||||
var bottomMost = Int.MIN_VALUE
|
|
||||||
|
|
||||||
for (render in potentialRenderers) {
|
|
||||||
val zLevel = render.bottomMostZLevel()
|
|
||||||
|
|
||||||
if (zLevel > Int.MIN_VALUE) {
|
|
||||||
bottomMost = bottomMost.coerceAtLeast(zLevel)
|
|
||||||
renderers.add(render)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastBottom = bottomMost
|
|
||||||
var renderCalls = 0
|
|
||||||
|
|
||||||
while (lastBottom != Int.MIN_VALUE && renderers.isNotEmpty()) {
|
|
||||||
var newBottom = Int.MIN_VALUE
|
|
||||||
|
|
||||||
for (i in renderers.size - 1 downTo 0) {
|
|
||||||
val renderer = renderers[i]
|
|
||||||
|
|
||||||
val newLevel = renderer.renderLayerFromStack(lastBottom, transform)
|
|
||||||
|
|
||||||
renderCalls++
|
|
||||||
|
|
||||||
if (newLevel == Int.MIN_VALUE) {
|
|
||||||
renderers.removeAt(i)
|
|
||||||
} else {
|
|
||||||
newBottom = newBottom.coerceAtLeast(newLevel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lastBottom = newBottom
|
|
||||||
}
|
|
||||||
|
|
||||||
return renderCalls
|
|
||||||
}
|
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectAVLTreeMap
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectFunction
|
||||||
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Позволяет вызывать отрисовщики в определённой (послойной) последовательности
|
||||||
|
*/
|
||||||
|
class LayeredRenderer {
|
||||||
|
private val layers = Int2ObjectAVLTreeMap<ArrayList<(Matrix4fStack) -> Unit>>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Сортировка [layer] происходит от дальнего (БОЛЬШЕ!) к ближнему (МЕНЬШЕ!)
|
||||||
|
*
|
||||||
|
* Пример:
|
||||||
|
* `8 -> 6 -> 4 -> 1 -> -4 -> -7`
|
||||||
|
*/
|
||||||
|
fun add(layer: Int, renderer: (Matrix4fStack) -> Unit) {
|
||||||
|
layers.computeIfAbsent(-layer, Int2ObjectFunction { ArrayList() }).add(renderer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(stack: Matrix4fStack) {
|
||||||
|
for (list in layers.values) {
|
||||||
|
for (renderer in list) {
|
||||||
|
renderer.invoke(stack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,56 +0,0 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
|
||||||
import ru.dbotthepony.kstarbound.defs.image.AtlasConfiguration
|
|
||||||
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
|
||||||
|
|
||||||
class SpriteAnimator(
|
|
||||||
val sprites: List<BoundSprite>,
|
|
||||||
animationCycle: Double,
|
|
||||||
animationLoops: Boolean = true,
|
|
||||||
firstFrame: Int = 0,
|
|
||||||
lastFrame: Int = sprites.size - 1
|
|
||||||
) : FrameAnimator(
|
|
||||||
firstFrame = firstFrame,
|
|
||||||
lastFrame = lastFrame,
|
|
||||||
animationCycle = animationCycle,
|
|
||||||
animationLoops = animationLoops,
|
|
||||||
) {
|
|
||||||
constructor(
|
|
||||||
image: GLTexture2D,
|
|
||||||
atlas: AtlasConfiguration,
|
|
||||||
animationCycle: Double,
|
|
||||||
animationLoops: Boolean = true,
|
|
||||||
firstFrame: Int = 0,
|
|
||||||
lastFrame: Int = atlas.spriteList.size - 1
|
|
||||||
) : this(
|
|
||||||
atlas.spriteList.stream().map { it.bind(image) }.collect(ImmutableList.toImmutableList()),
|
|
||||||
animationCycle = animationCycle,
|
|
||||||
animationLoops = animationLoops,
|
|
||||||
firstFrame = firstFrame,
|
|
||||||
lastFrame = lastFrame,
|
|
||||||
)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
image: ImageReference,
|
|
||||||
state: GLStateTracker,
|
|
||||||
animationCycle: Double,
|
|
||||||
animationLoops: Boolean = true,
|
|
||||||
firstFrame: Int = 0,
|
|
||||||
lastFrame: Int = image.config.spriteList.size - 1
|
|
||||||
) : this(state.loadNamedTextureSafe(image.image.fullPath), image.config, animationCycle, animationLoops, firstFrame, lastFrame)
|
|
||||||
|
|
||||||
val sprite get() = sprites[frame]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ImageReference.makeSpriteAnimator(
|
|
||||||
state: GLStateTracker,
|
|
||||||
animationCycle: Double,
|
|
||||||
animationLoops: Boolean = true,
|
|
||||||
firstFrame: Int = 0,
|
|
||||||
lastFrame: Int = config.spriteList.size - 1
|
|
||||||
): SpriteAnimator {
|
|
||||||
return SpriteAnimator(this, state, animationCycle, animationLoops, firstFrame, lastFrame)
|
|
||||||
}
|
|
@ -25,17 +25,7 @@ class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk
|
|||||||
for (texture in textures) {
|
for (texture in textures) {
|
||||||
texture.bind()
|
texture.bind()
|
||||||
|
|
||||||
val builder = state.flat2DTexturedQuads.small
|
state.flat2DTexturedQuads.small.singleSprite(texture.width / PIXELS_IN_STARBOUND_UNITf, texture.height / PIXELS_IN_STARBOUND_UNITf, entity.movement.angle, texture.transformer)
|
||||||
|
|
||||||
builder.begin()
|
|
||||||
|
|
||||||
val width = (texture.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
|
||||||
val height = (texture.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
|
||||||
|
|
||||||
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, texture.transformer)
|
|
||||||
|
|
||||||
builder.upload()
|
|
||||||
builder.draw()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,14 @@ import ru.dbotthepony.kvector.vector.ndouble.Vector2d
|
|||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class AnimationDefinition(
|
data class AnimationDefinition(
|
||||||
val frames: ImageReference? = null,
|
val frames: ImageReference? = null,
|
||||||
val animatedParts: AnimatedParts? = null,
|
|
||||||
val variants: Int? = null,
|
val variants: Int? = null,
|
||||||
val frameNumber: Int? = null,
|
val frameNumber: Int? = null,
|
||||||
val animationCycle: Double? = null,
|
val animationCycle: Double? = null,
|
||||||
val offset: Vector2d? = null,
|
val offset: Vector2d? = null,
|
||||||
|
|
||||||
val sounds: ImmutableMap<String, Either<ImmutableList<String>, CustomSound>> = ImmutableMap.of(),
|
val animatedParts: AnimatedParts? = null,
|
||||||
|
|
||||||
|
val sounds: ImmutableMap<String, ImmutableList<String>> = ImmutableMap.of(),
|
||||||
val transformationGroups: ImmutableMap<String, TransformConfig> = ImmutableMap.of(),
|
val transformationGroups: ImmutableMap<String, TransformConfig> = ImmutableMap.of(),
|
||||||
val particleEmitters: ImmutableMap<String, ParticleEmitter> = ImmutableMap.of(),
|
val particleEmitters: ImmutableMap<String, ParticleEmitter> = ImmutableMap.of(),
|
||||||
) {
|
) {
|
||||||
@ -27,12 +28,6 @@ data class AnimationDefinition(
|
|||||||
val interpolated: Boolean? = null
|
val interpolated: Boolean? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO
|
|
||||||
@JsonFactory
|
|
||||||
data class CustomSound(
|
|
||||||
val sound: String? = null
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class AnimatedParts(
|
data class AnimatedParts(
|
||||||
val stateTypes: ImmutableMap<String, StateType> = ImmutableMap.of(),
|
val stateTypes: ImmutableMap<String, StateType> = ImmutableMap.of(),
|
||||||
@ -41,6 +36,7 @@ data class AnimationDefinition(
|
|||||||
@JsonFactory
|
@JsonFactory
|
||||||
data class StateType(
|
data class StateType(
|
||||||
val default: String,
|
val default: String,
|
||||||
|
val priority: Int = 0,
|
||||||
val states: ImmutableMap<String, State> = ImmutableMap.of(),
|
val states: ImmutableMap<String, State> = ImmutableMap.of(),
|
||||||
) {
|
) {
|
||||||
@JsonFactory
|
@JsonFactory
|
||||||
|
@ -9,6 +9,7 @@ import com.google.gson.JsonObject
|
|||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
import com.google.gson.internal.bind.TypeAdapters
|
import com.google.gson.internal.bind.TypeAdapters
|
||||||
import com.google.gson.stream.JsonReader
|
import com.google.gson.stream.JsonReader
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
|
||||||
import ru.dbotthepony.kstarbound.api.ISBFileLocator
|
import ru.dbotthepony.kstarbound.api.ISBFileLocator
|
||||||
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
import ru.dbotthepony.kstarbound.client.gl.GLTexture2D
|
||||||
import ru.dbotthepony.kstarbound.io.json.stream
|
import ru.dbotthepony.kstarbound.io.json.stream
|
||||||
@ -18,7 +19,7 @@ import ru.dbotthepony.kvector.vector.nint.Vector4i
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Атлас спрайтов, собранный вручную артистом
|
* Конфигурация атласа спрайтов, собранный вручную артистом
|
||||||
*
|
*
|
||||||
* В файлах игры именуется frames
|
* В файлах игры именуется frames
|
||||||
*
|
*
|
||||||
@ -45,10 +46,26 @@ class AtlasConfiguration private constructor(
|
|||||||
*/
|
*/
|
||||||
val first: Sprite = sprites[sprites.keys.stream().sorted().findFirst().orElseThrow { NoSuchElementException("No a single key present in $name") }] ?: throw NoSuchElementException("IMPOSSIBRU in $name")
|
val first: Sprite = sprites[sprites.keys.stream().sorted().findFirst().orElseThrow { NoSuchElementException("No a single key present in $name") }] ?: throw NoSuchElementException("IMPOSSIBRU in $name")
|
||||||
|
|
||||||
|
private val intIndexed = Int2ObjectOpenHashMap<Sprite>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
for ((k, v) in sprites.entries) {
|
||||||
|
val int = k.toIntOrNull()
|
||||||
|
|
||||||
|
if (int != null) {
|
||||||
|
intIndexed[int] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
operator fun get(name: String): Sprite? {
|
operator fun get(name: String): Sprite? {
|
||||||
return sprites[name]
|
return sprites[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator fun get(name: Int): Sprite? {
|
||||||
|
return intIndexed[name]
|
||||||
|
}
|
||||||
|
|
||||||
fun any(name: String): Sprite {
|
fun any(name: String): Sprite {
|
||||||
return get(name) ?: first
|
return get(name) ?: first
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.io.json
|
||||||
|
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
|
|
||||||
|
object NothingAdapter : TypeAdapter<Nothing>() {
|
||||||
|
override fun write(out: JsonWriter, value: Nothing?) {
|
||||||
|
out.nullValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(`in`: JsonReader): Nothing? {
|
||||||
|
`in`.skipValue()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
@ -52,6 +52,9 @@ class SBPattern private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun resolve(values: (String) -> String?): String? {
|
fun resolve(values: (String) -> String?): String? {
|
||||||
|
if (names.isEmpty())
|
||||||
|
return raw
|
||||||
|
|
||||||
val buffer = ArrayList<String>(pieces.size)
|
val buffer = ArrayList<String>(pieces.size)
|
||||||
|
|
||||||
for (piece in pieces) {
|
for (piece in pieces) {
|
||||||
@ -70,6 +73,9 @@ class SBPattern private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun with(params: Map<String, String>): SBPattern {
|
fun with(params: Map<String, String>): SBPattern {
|
||||||
|
if (names.isEmpty())
|
||||||
|
return this
|
||||||
|
|
||||||
val map = Object2ObjectArrayMap<String, String>()
|
val map = Object2ObjectArrayMap<String, String>()
|
||||||
map.putAll(this.params)
|
map.putAll(this.params)
|
||||||
|
|
||||||
@ -89,14 +95,6 @@ class SBPattern private constructor(
|
|||||||
fun resolve(map0: (String) -> String?, map1: (String) -> String?): String? {
|
fun resolve(map0: (String) -> String?, map1: (String) -> String?): String? {
|
||||||
return contents ?: map0.invoke(name!!) ?: map1.invoke(name!!)
|
return contents ?: map0.invoke(name!!) ?: map1.invoke(name!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resolve(map0: (String) -> String?): String? {
|
|
||||||
return contents ?: map0.invoke(name!!)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resolve(): String? {
|
|
||||||
return contents
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object : TypeAdapterFactory {
|
companion object : TypeAdapterFactory {
|
||||||
|
68
src/main/kotlin/ru/dbotthepony/kstarbound/util/TimeSource.kt
Normal file
68
src/main/kotlin/ru/dbotthepony/kstarbound/util/TimeSource.kt
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.util
|
||||||
|
|
||||||
|
interface ITimeSource {
|
||||||
|
/**
|
||||||
|
* Время в наносекундах
|
||||||
|
*/
|
||||||
|
val nanos: Long
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Время в микросекундах
|
||||||
|
*/
|
||||||
|
val micros: Long get() = nanos / 1_000L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Время в миллисекундах
|
||||||
|
*/
|
||||||
|
val millis: Long get() = nanos / 1_000_000L
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Время в секундах, с точностью до микросекунд
|
||||||
|
*/
|
||||||
|
val seconds: Double get() = (nanos / 1_000L) / 1_000_000.0
|
||||||
|
}
|
||||||
|
|
||||||
|
class JVMTimeSource : ITimeSource {
|
||||||
|
private val origin = System.nanoTime()
|
||||||
|
|
||||||
|
override val nanos: Long
|
||||||
|
get() = System.nanoTime() - origin
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
val INSTANCE = JVMTimeSource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArtificialTimeSource(nanos: Long = 0L) : ITimeSource {
|
||||||
|
override var nanos: Long = nanos
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun advance(nanos: Long) {
|
||||||
|
this.nanos += nanos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class PausableTimeSource(private val parent: ITimeSource) : ITimeSource {
|
||||||
|
override val nanos: Long get() {
|
||||||
|
if (isPaused) {
|
||||||
|
return pausedSince - skipped
|
||||||
|
} else {
|
||||||
|
return parent.nanos - skipped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isPaused = false
|
||||||
|
private var pausedSince = 0L
|
||||||
|
private var skipped = 0L
|
||||||
|
|
||||||
|
fun pause() {
|
||||||
|
if (!isPaused) {
|
||||||
|
isPaused = true
|
||||||
|
pausedSince = parent.nanos
|
||||||
|
} else {
|
||||||
|
isPaused = false
|
||||||
|
skipped += parent.nanos - pausedSince
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user