Тест аниматора, RebindableSprite и ещё приборка в GL
This commit is contained in:
parent
0a00595520
commit
9da968695e
@ -6,6 +6,8 @@ import org.apache.logging.log4j.LogManager
|
||||
import org.lwjgl.Version
|
||||
import org.lwjgl.glfw.GLFW.glfwSetWindowShouldClose
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.render.Animator
|
||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||
import ru.dbotthepony.kstarbound.defs.item.DynamicItemDefinition
|
||||
import ru.dbotthepony.kstarbound.io.BTreeDB
|
||||
import ru.dbotthepony.kstarbound.world.ChunkPos
|
||||
@ -189,6 +191,16 @@ fun main() {
|
||||
}
|
||||
|
||||
// println(Starbound.statusEffects["firecharge"])
|
||||
|
||||
starbound.pathStack.push("/animations/dust4")
|
||||
val def = starbound.gson.fromJson(starbound.locate("/animations/dust4/dust4.animation").reader(), AnimationDefinition::class.java)
|
||||
starbound.pathStack.pop()
|
||||
|
||||
val animator = Animator(client.world!!, def)
|
||||
|
||||
client.onPostDrawWorld {
|
||||
animator.render(client.gl.matrixStack)
|
||||
}
|
||||
}
|
||||
|
||||
//ent.position += Vector2d(y = 14.0, x = -10.0)
|
||||
|
@ -424,7 +424,7 @@ class ClientChunk(world: ClientWorld, pos: ChunkPos) : Chunk<ClientWorld, Client
|
||||
private val entityRenderers = HashMap<Entity, EntityRenderer>()
|
||||
|
||||
override fun onEntityAdded(entity: Entity) {
|
||||
entityRenderers[entity] = EntityRenderer.getRender(state, entity, this)
|
||||
entityRenderers[entity] = EntityRenderer.getRender(world.client, entity, this)
|
||||
}
|
||||
|
||||
override fun onEntityTransferedToThis(entity: Entity, otherChunk: ClientChunk) {
|
||||
|
@ -18,15 +18,12 @@ import ru.dbotthepony.kstarbound.client.render.Box2DRenderer
|
||||
import ru.dbotthepony.kstarbound.client.render.Font
|
||||
import ru.dbotthepony.kvector.api.IStruct4f
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.matrix.nfloat.Matrix4f
|
||||
import ru.dbotthepony.kvector.util2d.AABB
|
||||
import ru.dbotthepony.kvector.vector.Color
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.lang.ref.Cleaner
|
||||
import java.util.*
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashMap
|
||||
import kotlin.math.roundToInt
|
||||
@ -437,61 +434,30 @@ class GLStateTracker(val locator: ISBFileLocator) {
|
||||
|
||||
private val named2DTextures = HashMap<String, GLTexture2D>()
|
||||
|
||||
fun loadNamedTexture(path: String, memoryFormat: Int, fileFormat: Int): GLTexture2D {
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!locator.exists(path)) {
|
||||
throw FileNotFoundException("Unable to locate $path")
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path), memoryFormat, fileFormat).generateMips()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadNamedTexture(path: String): GLTexture2D {
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!locator.exists(path)) {
|
||||
throw FileNotFoundException("Unable to locate $path")
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path)).generateMips()
|
||||
}
|
||||
}
|
||||
|
||||
private var loadedEmptyTexture = false
|
||||
private var missingTexture: GLTexture2D? = null
|
||||
private val missingTexturePath = "/assetmissing.png"
|
||||
|
||||
fun loadNamedTextureSafe(path: String, memoryFormat: Int, fileFormat: Int): GLTexture2D {
|
||||
if (!loadedEmptyTexture) {
|
||||
loadedEmptyTexture = true
|
||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(locator.readDirect(missingTexturePath), memoryFormat, fileFormat).generateMips()
|
||||
}
|
||||
fun loadTexture(path: String): GLTexture2D {
|
||||
ensureSameThread()
|
||||
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!locator.exists(path)) {
|
||||
LOGGER.error("Texture {} is missing! Falling back to {}", path, missingTexturePath)
|
||||
return@computeIfAbsent named2DTextures[missingTexturePath]!!
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path), memoryFormat, fileFormat).generateMips()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadNamedTextureSafe(path: String): GLTexture2D {
|
||||
if (!loadedEmptyTexture) {
|
||||
loadedEmptyTexture = true
|
||||
named2DTextures[missingTexturePath] = newTexture(missingTexturePath).upload(locator.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips()
|
||||
}
|
||||
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!locator.exists(path)) {
|
||||
LOGGER.error("Texture {} is missing! Falling back to {}", path, missingTexturePath)
|
||||
return@computeIfAbsent named2DTextures[missingTexturePath]!!
|
||||
}
|
||||
|
||||
return@computeIfAbsent newTexture(path).upload(locator.readDirect(path)).generateMips().also {
|
||||
if (missingTexture == null) {
|
||||
missingTexture = newTexture(missingTexturePath).upload(locator.readDirect(missingTexturePath), GL_RGBA, GL_RGBA).generateMips().also {
|
||||
it.textureMinFilter = GL_NEAREST
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
}
|
||||
}
|
||||
|
||||
return named2DTextures.computeIfAbsent(path) {
|
||||
if (!locator.exists(path)) {
|
||||
LOGGER.error("Texture {} is missing! Falling back to {}", path, missingTexturePath)
|
||||
missingTexture!!
|
||||
} else {
|
||||
newTexture(path).upload(locator.readDirect(path)).generateMips().also {
|
||||
it.textureMinFilter = GL_NEAREST
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(obj: VertexBufferObject): VertexBufferObject {
|
||||
|
@ -13,7 +13,7 @@ class TextureLoadingException(message: String) : Throwable(message)
|
||||
|
||||
data class UVCoord(val u: Float, val v: Float)
|
||||
|
||||
class GLTexturePropertyTracker(private val flag: Int, var value: Int) {
|
||||
private class GLTexturePropertyTracker(private val flag: Int, private var value: Int) {
|
||||
operator fun getValue(thisRef: GLTexture2D, property: KProperty<*>): Int {
|
||||
return value
|
||||
}
|
||||
@ -62,8 +62,6 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
||||
return height.toFloat() / width.toFloat()
|
||||
}
|
||||
|
||||
private var mipsWarning = 2
|
||||
|
||||
var textureMinFilter by GLTexturePropertyTracker(GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR)
|
||||
var textureMagFilter by GLTexturePropertyTracker(GL_TEXTURE_MAG_FILTER, GL_LINEAR)
|
||||
|
||||
@ -71,15 +69,6 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
||||
var textureWrapT by GLTexturePropertyTracker(GL_TEXTURE_WRAP_T, GL_REPEAT)
|
||||
|
||||
fun bind(): GLTexture2D {
|
||||
if (textureMinFilter != GL_LINEAR && textureMinFilter != GL_NEAREST) {
|
||||
if (mipsWarning == 1) {
|
||||
LOGGER.warn("(Likely) Trying to use texture {} before generated it's mips, this probably won't work!", this)
|
||||
mipsWarning = 0
|
||||
} else if (mipsWarning == 2) {
|
||||
mipsWarning = 1
|
||||
}
|
||||
}
|
||||
|
||||
state.texture2D = this
|
||||
return this
|
||||
}
|
||||
@ -87,8 +76,7 @@ class GLTexture2D(val state: GLStateTracker, val name: String = "<unknown>") : A
|
||||
fun generateMips(): GLTexture2D {
|
||||
state.ensureSameThread()
|
||||
glGenerateTextureMipmap(pointer)
|
||||
checkForGLError()
|
||||
mipsWarning = 0
|
||||
checkForGLError("Generating texture mipmaps")
|
||||
return this
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,51 @@
|
||||
package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.client.ClientWorld
|
||||
import ru.dbotthepony.kstarbound.defs.animation.AnimationDefinition
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
import ru.dbotthepony.kvector.vector.nfloat.Vector3f
|
||||
|
||||
class Animator(
|
||||
val world: ClientWorld,
|
||||
val def: AnimationDefinition
|
||||
val def: AnimationDefinition,
|
||||
val renderParams: ((String) -> String?)? = null
|
||||
) {
|
||||
inline val state get() = world.client.gl
|
||||
val frameAnimator: FrameAnimator?
|
||||
val mainSprite: RebindableSprite?
|
||||
|
||||
init {
|
||||
if (def.frames != null && def.animationCycle != null && def.frameNumber != null) {
|
||||
frameAnimator = FrameAnimator(lastFrame = def.frameNumber - 1, time = world.client.time, animationCycle = def.animationCycle)
|
||||
mainSprite = RebindableSprite(world.client, def.frames, ::getRenderParam)
|
||||
} else {
|
||||
frameAnimator = null
|
||||
mainSprite = null
|
||||
}
|
||||
}
|
||||
|
||||
fun render(stack: Matrix4fStack) {
|
||||
private fun getRenderParam(name: String): String? {
|
||||
if (name == "frame") {
|
||||
return frameAnimator?.frameString
|
||||
}
|
||||
|
||||
return renderParams?.invoke(name)
|
||||
}
|
||||
|
||||
fun render(stack: Matrix4fStack) {
|
||||
frameAnimator?.advance()
|
||||
val sprite = mainSprite?.update() ?: return
|
||||
sprite.texture.bind()
|
||||
|
||||
stack.push().translateWithMultiplication(Vector3f(world.client.camera.pos))
|
||||
|
||||
state.programs.textured.use()
|
||||
state.programs.textured.transform = stack.last
|
||||
state.activeTexture = 0
|
||||
state.programs.textured.texture = 0
|
||||
|
||||
state.flat2DTexturedQuads.singleSprite(sprite.width / PIXELS_IN_STARBOUND_UNITf, sprite.height / PIXELS_IN_STARBOUND_UNITf, 0.0, sprite.transformer)
|
||||
stack.pop()
|
||||
}
|
||||
}
|
||||
|
@ -17,12 +17,12 @@ class BoundSprite(
|
||||
/**
|
||||
* Настоящая ширина спрайта, в пикселях
|
||||
*/
|
||||
inline val width get() = sprite.width(texture.width)
|
||||
val width = sprite.width(texture.width)
|
||||
|
||||
/**
|
||||
* Настоящая высота спрайта, в пикселях
|
||||
*/
|
||||
inline val height get() = sprite.height(texture.height)
|
||||
val height = sprite.height(texture.height)
|
||||
|
||||
override val u0: Float
|
||||
override val v0: Float
|
||||
@ -41,24 +41,3 @@ class BoundSprite(
|
||||
fun bind() = texture.bind()
|
||||
val transformer = QuadTransformers.uv(u0, v0, u1, v1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт связку текстуры-атласа + координгат спрайта на ней
|
||||
*/
|
||||
fun ImageReference.bind(texture: GLTexture2D): BoundSprite {
|
||||
return BoundSprite(sprite!!, texture)
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт связку текстуры-атласа, которая загружается через [GLStateTracker.loadNamedTextureSafe] + координгат спрайта на ней
|
||||
*/
|
||||
fun ImageReference.bind(state: GLStateTracker): BoundSprite {
|
||||
return BoundSprite(sprite!!, state.loadNamedTextureSafe(imagePath.value!!))
|
||||
}
|
||||
|
||||
/**
|
||||
* Создаёт связку текстуры-атласа + координгат спрайта на ней
|
||||
*/
|
||||
fun AtlasConfiguration.Sprite.bind(texture: GLTexture2D): BoundSprite {
|
||||
return BoundSprite(this, texture)
|
||||
}
|
||||
|
@ -32,6 +32,13 @@ class FrameAnimator(
|
||||
var frame = 0
|
||||
private set
|
||||
|
||||
/**
|
||||
* Эффективное преобразование [frame] в строку
|
||||
*/
|
||||
val frameString: String get() {
|
||||
return framenames.getOrNull(frame) ?: frame.toString()
|
||||
}
|
||||
|
||||
private var counter = 0.0
|
||||
private var lastRender = time.seconds
|
||||
|
||||
@ -73,4 +80,14 @@ class FrameAnimator(
|
||||
lastRender = time.seconds
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val framenames = ArrayList<String>()
|
||||
|
||||
init {
|
||||
for (i in 0 .. 500) {
|
||||
framenames.add(i.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
package ru.dbotthepony.kstarbound.client.render
|
||||
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.defs.image.ImageReference
|
||||
|
||||
class RebindableSprite(
|
||||
val client: StarboundClient,
|
||||
ref: ImageReference,
|
||||
val renderParams: ((String) -> String?)? = null
|
||||
) {
|
||||
var sprite: BoundSprite? = null
|
||||
private set
|
||||
|
||||
var ref: ImageReference = ref
|
||||
private set
|
||||
|
||||
init {
|
||||
val unbound = ref.sprite
|
||||
|
||||
if (unbound != null) {
|
||||
sprite = BoundSprite(unbound, client.gl.loadTexture(ref.imagePath.value!!))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Обновляет [ref] и [sprite] по значениям, которые выдаст [renderParams]
|
||||
*
|
||||
* Возвращает новое значение [sprite]
|
||||
*/
|
||||
fun update(): BoundSprite? {
|
||||
client.gl.ensureSameThread()
|
||||
|
||||
if (renderParams == null)
|
||||
return sprite
|
||||
|
||||
val newRef = ref.with(renderParams)
|
||||
|
||||
if (newRef !== ref) {
|
||||
ref = newRef
|
||||
sprite = null
|
||||
val unbound = newRef.sprite
|
||||
|
||||
if (unbound != null) {
|
||||
sprite = BoundSprite(unbound, client.gl.loadTexture(newRef.imagePath.value!!))
|
||||
}
|
||||
}
|
||||
|
||||
return sprite
|
||||
}
|
||||
}
|
@ -186,7 +186,7 @@ private class ModifierEqualityTester(val definition: MaterialModifier) : Equalit
|
||||
|
||||
class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
val state get() = renderers.state
|
||||
val texture = state.loadNamedTexture(def.renderParameters.texture.imagePath.value!!).also {
|
||||
val texture = state.loadTexture(def.renderParameters.texture.imagePath.value!!).also {
|
||||
it.textureMagFilter = GL_NEAREST
|
||||
}
|
||||
|
||||
@ -255,9 +255,9 @@ class TileRenderer(val renderers: TileRenderers, val def: IRenderableTile) {
|
||||
for (renderPiece in matchPiece.pieces) {
|
||||
if (renderPiece.piece.texture != null) {
|
||||
val program = if (background) {
|
||||
renderers.background(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
renderers.background(state.loadTexture(renderPiece.piece.texture!!))
|
||||
} else {
|
||||
renderers.foreground(state.loadNamedTexture(renderPiece.piece.texture!!))
|
||||
renderers.foreground(state.loadTexture(renderPiece.piece.texture!!))
|
||||
}
|
||||
|
||||
tesselateAt(self, renderPiece.piece, getter, layers.computeIfAbsent(program, def.renderParameters.zLevel, ::vertexTextureBuilder), pos, renderPiece.offset, isModifier)
|
||||
|
@ -2,6 +2,7 @@ package ru.dbotthepony.kstarbound.client.render.entity
|
||||
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap
|
||||
import ru.dbotthepony.kstarbound.client.ClientChunk
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.world.entities.Entity
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
@ -13,7 +14,8 @@ import java.io.Closeable
|
||||
*
|
||||
* Считается, что процесс отрисовки ограничен лишь одним слоем (т.е. отрисовка происходит в один проход)
|
||||
*/
|
||||
open class EntityRenderer(val state: GLStateTracker, val entity: Entity, open var chunk: ClientChunk?) : Closeable {
|
||||
open class EntityRenderer(val client: StarboundClient, val entity: Entity, open var chunk: ClientChunk?) : Closeable {
|
||||
inline val state: GLStateTracker get() = client.gl
|
||||
open val renderPos: Vector2d get() = entity.position
|
||||
|
||||
open fun render(stack: Matrix4fStack) {
|
||||
@ -39,20 +41,20 @@ open class EntityRenderer(val state: GLStateTracker, val entity: Entity, open va
|
||||
*/
|
||||
const val Z_LEVEL_ENTITIES = 30000
|
||||
|
||||
private val renderers = Reference2ObjectOpenHashMap<Class<*>, (state: GLStateTracker, entity: Entity, chunk: ClientChunk?) -> EntityRenderer>()
|
||||
private val renderers = Reference2ObjectOpenHashMap<Class<*>, (client: StarboundClient, entity: Entity, chunk: ClientChunk?) -> EntityRenderer>()
|
||||
|
||||
@Suppress("unchecked_cast")
|
||||
fun <T : Entity> registerRenderer(clazz: Class<T>, renderer: (state: GLStateTracker, entity: T, chunk: ClientChunk?) -> EntityRenderer) {
|
||||
check(renderers.put(clazz, renderer as (state: GLStateTracker, entity: Entity, chunk: ClientChunk?) -> EntityRenderer) == null) { "Already has renderer for ${clazz.canonicalName}!" }
|
||||
fun <T : Entity> registerRenderer(clazz: Class<T>, renderer: (client: StarboundClient, entity: T, chunk: ClientChunk?) -> EntityRenderer) {
|
||||
check(renderers.put(clazz, renderer as (client: StarboundClient, entity: Entity, chunk: ClientChunk?) -> EntityRenderer) == null) { "Already has renderer for ${clazz.canonicalName}!" }
|
||||
}
|
||||
|
||||
inline fun <reified T : Entity> registerRenderer(noinline renderer: (state: GLStateTracker, entity: T, chunk: ClientChunk?) -> EntityRenderer) {
|
||||
inline fun <reified T : Entity> registerRenderer(noinline renderer: (client: StarboundClient, entity: T, chunk: ClientChunk?) -> EntityRenderer) {
|
||||
registerRenderer(T::class.java, renderer)
|
||||
}
|
||||
|
||||
fun getRender(state: GLStateTracker, entity: Entity, chunk: ClientChunk? = null): EntityRenderer {
|
||||
val factory = renderers[entity::class.java] ?: return EntityRenderer(state, entity, chunk)
|
||||
return factory.invoke(state, entity, chunk)
|
||||
fun getRender(client: StarboundClient, entity: Entity, chunk: ClientChunk? = null): EntityRenderer {
|
||||
val factory = renderers[entity::class.java] ?: return EntityRenderer(client, entity, chunk)
|
||||
return factory.invoke(client, entity, chunk)
|
||||
}
|
||||
|
||||
init {
|
||||
|
@ -2,14 +2,14 @@ package ru.dbotthepony.kstarbound.client.render.entity
|
||||
|
||||
import ru.dbotthepony.kstarbound.PIXELS_IN_STARBOUND_UNITf
|
||||
import ru.dbotthepony.kstarbound.client.ClientChunk
|
||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||
import ru.dbotthepony.kstarbound.client.render.bind
|
||||
import ru.dbotthepony.kstarbound.client.StarboundClient
|
||||
import ru.dbotthepony.kstarbound.client.render.RebindableSprite
|
||||
import ru.dbotthepony.kstarbound.world.entities.ItemEntity
|
||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||
|
||||
class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
|
||||
class ItemRenderer(client: StarboundClient, entity: ItemEntity, chunk: ClientChunk?) : EntityRenderer(client, entity, chunk) {
|
||||
private val def = entity.def
|
||||
private val textures = def.inventoryIcon?.stream()?.map { it.image.bind(state) }?.toList() ?: listOf()
|
||||
private val textures = def.inventoryIcon?.stream()?.map { RebindableSprite(client, it.image) }?.toList() ?: listOf()
|
||||
|
||||
override fun render(stack: Matrix4fStack) {
|
||||
if (textures.isEmpty())
|
||||
@ -20,10 +20,10 @@ class ItemRenderer(state: GLStateTracker, entity: ItemEntity, chunk: ClientChunk
|
||||
state.activeTexture = 0
|
||||
state.programs.textured.texture = 0
|
||||
|
||||
for (texture in textures) {
|
||||
texture.bind()
|
||||
|
||||
state.flat2DTexturedQuads.singleSprite(texture.width / PIXELS_IN_STARBOUND_UNITf, texture.height / PIXELS_IN_STARBOUND_UNITf, entity.movement.angle, texture.transformer)
|
||||
for (unbound in textures) {
|
||||
val sprite = unbound.update() ?: continue
|
||||
sprite.texture.bind()
|
||||
state.flat2DTexturedQuads.singleSprite(sprite.width / PIXELS_IN_STARBOUND_UNITf, sprite.height / PIXELS_IN_STARBOUND_UNITf, entity.movement.angle, sprite.transformer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,16 +58,25 @@ class AtlasConfiguration private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [Sprite] если он существует с данным [name], или `null`
|
||||
*/
|
||||
operator fun get(name: String): Sprite? {
|
||||
return sprites[name]
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [Sprite] если он существует с данным [name], или `null`
|
||||
*/
|
||||
operator fun get(name: Int): Sprite? {
|
||||
return intIndexed[name]
|
||||
}
|
||||
|
||||
/**
|
||||
* @return [Sprite] если он существует с данным [name], или первый спрайт в атласе
|
||||
*/
|
||||
fun any(name: String): Sprite {
|
||||
return get(name) ?: first
|
||||
return get(name) ?: any()
|
||||
}
|
||||
|
||||
private val any by lazy { get("root") ?: get("0") ?: get("default") ?: first }
|
||||
|
@ -13,7 +13,7 @@ import ru.dbotthepony.kstarbound.util.PathStack
|
||||
import ru.dbotthepony.kstarbound.util.SBPattern
|
||||
|
||||
/**
|
||||
* @see [AtlasConfiguration.Companion.get]
|
||||
* @see [AtlasConfiguration.Registry.get]
|
||||
*/
|
||||
class ImageReference private constructor(
|
||||
val raw: DirectAssetReference,
|
||||
@ -26,8 +26,9 @@ class ImageReference private constructor(
|
||||
* Спрайт, на которое ссылается данный референс, или `null` если:
|
||||
* * [atlas] равен `null`
|
||||
* * [spritePath] является шаблоном и определены не все значения
|
||||
* * [spritePath] не является правильным именем спрайта внутри [atlas] (смотрим [AtlasConfiguration.get])
|
||||
*/
|
||||
val sprite by lazy {
|
||||
val sprite by lazy(LazyThreadSafetyMode.NONE) {
|
||||
if (atlas == null)
|
||||
null
|
||||
else if (spritePath == null)
|
||||
@ -38,7 +39,15 @@ class ImageReference private constructor(
|
||||
|
||||
fun with(values: (String) -> String?): ImageReference {
|
||||
val imagePath = this.imagePath.with(values)
|
||||
val spritePath = this.spritePath?.with(values)
|
||||
var spritePath = this.spritePath?.with(values)
|
||||
|
||||
if (spritePath == null) {
|
||||
val frame = values.invoke("frame")
|
||||
|
||||
if (frame != null) {
|
||||
spritePath = SBPattern.FRAME.with(values)
|
||||
}
|
||||
}
|
||||
|
||||
if (imagePath != this.imagePath || spritePath != this.spritePath) {
|
||||
if (imagePath != this.imagePath) {
|
||||
|
@ -52,8 +52,11 @@ class SBPattern private constructor(
|
||||
}
|
||||
|
||||
fun resolve(values: (String) -> String?): String? {
|
||||
if (names.isEmpty())
|
||||
if (names.isEmpty()) {
|
||||
return raw
|
||||
} else if (pieces.size == 1) {
|
||||
return pieces[0].resolve(values, params::get)
|
||||
}
|
||||
|
||||
val buffer = ArrayList<String>(pieces.size)
|
||||
|
||||
@ -116,6 +119,9 @@ class SBPattern private constructor(
|
||||
@JvmField
|
||||
val EMPTY = raw("")
|
||||
|
||||
@JvmField
|
||||
val FRAME = of("<frame>")
|
||||
|
||||
override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
|
||||
if (type.rawType == SBPattern::class.java) {
|
||||
return object : TypeAdapter<SBPattern>() {
|
||||
@ -126,7 +132,7 @@ class SBPattern private constructor(
|
||||
}
|
||||
|
||||
override fun read(`in`: JsonReader): SBPattern? {
|
||||
return interner.intern(of(strings.read(`in`) ?: return null))
|
||||
return of(strings.read(`in`) ?: return null)
|
||||
}
|
||||
} as TypeAdapter<T>
|
||||
}
|
||||
@ -159,7 +165,7 @@ class SBPattern private constructor(
|
||||
throw IllegalArgumentException("Malformed pattern string: $raw")
|
||||
}
|
||||
|
||||
pieces.add(Piece(name = raw.substring(open + 1, closing - 1)))
|
||||
pieces.add(Piece(name = raw.substring(open + 1, closing)))
|
||||
i = closing + 1
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user