ImageReference, SpriteAnimator и почти избавились от старого FrameGrid
This commit is contained in:
parent
9357835f4e
commit
69a5061e9e
@ -15,6 +15,7 @@ import ru.dbotthepony.kstarbound.api.NonExistingFile
|
|||||||
import ru.dbotthepony.kstarbound.api.PhysicalFile
|
import ru.dbotthepony.kstarbound.api.PhysicalFile
|
||||||
import ru.dbotthepony.kstarbound.api.explore
|
import ru.dbotthepony.kstarbound.api.explore
|
||||||
import ru.dbotthepony.kstarbound.defs.*
|
import ru.dbotthepony.kstarbound.defs.*
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.AtlasConfiguration
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
|
import ru.dbotthepony.kstarbound.defs.item.ItemDefinition
|
||||||
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
import ru.dbotthepony.kstarbound.defs.item.ItemRarity
|
||||||
@ -65,6 +66,9 @@ object Starbound {
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
fun readingFolderTransformer(input: String): String {
|
fun readingFolderTransformer(input: String): String {
|
||||||
|
val readingFolder = readingFolder
|
||||||
|
require(readingFolder != null) { "Not reading an asset on current thread" }
|
||||||
|
|
||||||
if (input[0] == '/')
|
if (input[0] == '/')
|
||||||
return input
|
return input
|
||||||
|
|
||||||
@ -72,6 +76,8 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun readingFolderTransformerNullable(input: String?): String? {
|
fun readingFolderTransformerNullable(input: String?): String? {
|
||||||
|
require(readingFolder != null) { "Not reading an asset on current thread" }
|
||||||
|
|
||||||
if (input != null)
|
if (input != null)
|
||||||
return readingFolderTransformer(input)
|
return readingFolderTransformer(input)
|
||||||
|
|
||||||
@ -158,6 +164,7 @@ object Starbound {
|
|||||||
.also(ItemDefinition::registerGson)
|
.also(ItemDefinition::registerGson)
|
||||||
.also(ItemRarity::registerGson)
|
.also(ItemRarity::registerGson)
|
||||||
.also(SpriteReference::registerGson)
|
.also(SpriteReference::registerGson)
|
||||||
|
.also(AtlasConfiguration::registerGson)
|
||||||
|
|
||||||
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
.registerTypeAdapter(DamageType::class.java, CustomEnumTypeAdapter(DamageType.values()).nullSafe())
|
||||||
|
|
||||||
@ -371,6 +378,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
|
readingFolder = listedFile.computeDirectory()
|
||||||
val def = gson.fromJson(listedFile.reader(), ConfigurableProjectile::class.java).assemble(listedFile.computeDirectory())
|
val def = gson.fromJson(listedFile.reader(), ConfigurableProjectile::class.java).assemble(listedFile.computeDirectory())
|
||||||
check(projectiles[def.projectileName] == null) { "Already has projectile with ID ${def.projectileName} loaded!" }
|
check(projectiles[def.projectileName] == null) { "Already has projectile with ID ${def.projectileName} loaded!" }
|
||||||
projectiles[def.projectileName] = def
|
projectiles[def.projectileName] = def
|
||||||
@ -384,6 +392,8 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readingFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadFunctions(callback: (String) -> Unit) {
|
private fun loadFunctions(callback: (String) -> Unit) {
|
||||||
@ -392,6 +402,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
|
readingFolder = listedFile.computeDirectory()
|
||||||
val readObject = JsonParser.parseReader(listedFile.reader()) as JsonObject
|
val readObject = JsonParser.parseReader(listedFile.reader()) as JsonObject
|
||||||
|
|
||||||
for (key in readObject.keySet()) {
|
for (key in readObject.keySet()) {
|
||||||
@ -407,6 +418,8 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readingFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadParallax(callback: (String) -> Unit) {
|
private fun loadParallax(callback: (String) -> Unit) {
|
||||||
@ -415,6 +428,7 @@ object Starbound {
|
|||||||
try {
|
try {
|
||||||
callback("Loading $listedFile")
|
callback("Loading $listedFile")
|
||||||
|
|
||||||
|
readingFolder = listedFile.computeDirectory()
|
||||||
val def = gson.fromJson(listedFile.reader(), ParallaxPrototype::class.java)
|
val def = gson.fromJson(listedFile.reader(), ParallaxPrototype::class.java)
|
||||||
parallax[listedFile.name.substringBefore('.')] = def
|
parallax[listedFile.name.substringBefore('.')] = def
|
||||||
} catch(err: Throwable) {
|
} catch(err: Throwable) {
|
||||||
@ -426,6 +440,8 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readingFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadMaterialModifiers(callback: (String) -> Unit) {
|
private fun loadMaterialModifiers(callback: (String) -> Unit) {
|
||||||
@ -478,6 +494,8 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readingFolder = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadItemDefinitions(callback: (String) -> Unit) {
|
private fun loadItemDefinitions(callback: (String) -> Unit) {
|
||||||
@ -502,5 +520,7 @@ object Starbound {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readingFolder = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package ru.dbotthepony.kstarbound.client.render
|
|||||||
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.GLTexture2D
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.AtlasConfiguration
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.IUVCoordinates
|
import ru.dbotthepony.kstarbound.defs.animation.IUVCoordinates
|
||||||
import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
||||||
|
|
||||||
@ -10,20 +11,18 @@ import ru.dbotthepony.kstarbound.defs.animation.SpriteReference
|
|||||||
* Связка текстуры-атласа + координат спрайта на ней
|
* Связка текстуры-атласа + координат спрайта на ней
|
||||||
*/
|
*/
|
||||||
class BoundSprite(
|
class BoundSprite(
|
||||||
val reference: SpriteReference,
|
val sprite: AtlasConfiguration.Sprite,
|
||||||
val texture: GLTexture2D
|
val texture: GLTexture2D
|
||||||
) : IUVCoordinates {
|
) : IUVCoordinates {
|
||||||
inline val sprite get() = reference.sprite
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Настоящая ширина спрайта, в пикселях
|
* Настоящая ширина спрайта, в пикселях
|
||||||
*/
|
*/
|
||||||
inline val width get() = reference.sprite.width(texture.width)
|
inline val width get() = sprite.width(texture.width)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Настоящая высота спрайта, в пикселях
|
* Настоящая высота спрайта, в пикселях
|
||||||
*/
|
*/
|
||||||
inline val height get() = reference.sprite.height(texture.height)
|
inline val height get() = sprite.height(texture.height)
|
||||||
|
|
||||||
override val u0: Float
|
override val u0: Float
|
||||||
override val v0: Float
|
override val v0: Float
|
||||||
@ -31,7 +30,7 @@ class BoundSprite(
|
|||||||
override val v1: Float
|
override val v1: Float
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val coords = reference.sprite.compute(texture)
|
val coords = sprite.compute(texture)
|
||||||
|
|
||||||
this.u0 = coords.u0
|
this.u0 = coords.u0
|
||||||
this.v0 = coords.v0
|
this.v0 = coords.v0
|
||||||
@ -47,12 +46,19 @@ class BoundSprite(
|
|||||||
* Создаёт связку текстуры-атласа + координгат спрайта на ней
|
* Создаёт связку текстуры-атласа + координгат спрайта на ней
|
||||||
*/
|
*/
|
||||||
fun SpriteReference.bind(texture: GLTexture2D): BoundSprite {
|
fun SpriteReference.bind(texture: GLTexture2D): BoundSprite {
|
||||||
return BoundSprite(this, texture)
|
return BoundSprite(sprite, texture)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Создаёт связку текстуры-атласа, которая загружается через [GLStateTracker.loadNamedTextureSafe] + координгат спрайта на ней
|
* Создаёт связку текстуры-атласа, которая загружается через [GLStateTracker.loadNamedTextureSafe] + координгат спрайта на ней
|
||||||
*/
|
*/
|
||||||
fun SpriteReference.bind(state: GLStateTracker): BoundSprite {
|
fun SpriteReference.bind(state: GLStateTracker): BoundSprite {
|
||||||
return BoundSprite(this, state.loadNamedTextureSafe(path))
|
return BoundSprite(sprite, state.loadNamedTextureSafe(image))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Создаёт связку текстуры-атласа + координгат спрайта на ней
|
||||||
|
*/
|
||||||
|
fun AtlasConfiguration.Sprite.bind(texture: GLTexture2D): BoundSprite {
|
||||||
|
return BoundSprite(this, texture)
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
package ru.dbotthepony.kstarbound.client.render
|
package ru.dbotthepony.kstarbound.client.render
|
||||||
|
|
||||||
import org.lwjgl.glfw.GLFW.glfwGetTime
|
import org.lwjgl.glfw.GLFW.glfwGetTime
|
||||||
import ru.dbotthepony.kstarbound.defs.FrameSet
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Анимирует заданный FrameSet
|
* Таймер для анимирования набора спрайтов
|
||||||
*/
|
*/
|
||||||
class FrameSetAnimator(
|
open class FrameAnimator(
|
||||||
val set: FrameSet,
|
/**
|
||||||
|
* Первый кадр в анимации
|
||||||
|
*/
|
||||||
|
var firstFrame: Int = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Последний кадр в анимации
|
||||||
|
*/
|
||||||
|
var lastFrame: Int,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сколько времени занимает один кадр
|
* Сколько времени занимает один кадр
|
||||||
@ -17,23 +24,11 @@ class FrameSetAnimator(
|
|||||||
/**
|
/**
|
||||||
* Зациклить ли анимацию
|
* Зациклить ли анимацию
|
||||||
*/
|
*/
|
||||||
var animationLoops: Boolean,
|
var animationLoops: Boolean = true,
|
||||||
) {
|
) {
|
||||||
/**
|
|
||||||
* Последний кадр анимации
|
|
||||||
*/
|
|
||||||
var lastFrame = set.frameCount - 1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Первый кадр анимации
|
|
||||||
*/
|
|
||||||
var firstFrame = 0
|
|
||||||
|
|
||||||
var frame = 0
|
var frame = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
val frameObj get() = set.frames[frame + firstFrame]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Возвращает разницу между последним и первым кадром анимации
|
* Возвращает разницу между последним и первым кадром анимации
|
||||||
*/
|
*/
|
@ -0,0 +1,56 @@
|
|||||||
|
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.animation.AtlasConfiguration
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.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), 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)
|
||||||
|
}
|
@ -5,39 +5,36 @@ import ru.dbotthepony.kstarbound.client.ClientChunk
|
|||||||
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
import ru.dbotthepony.kstarbound.client.gl.GLStateTracker
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
import ru.dbotthepony.kstarbound.client.gl.vertex.QuadTransformers
|
||||||
import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ
|
import ru.dbotthepony.kstarbound.client.gl.vertex.quadRotatedZ
|
||||||
import ru.dbotthepony.kstarbound.client.render.FrameSetAnimator
|
import ru.dbotthepony.kstarbound.client.render.FrameAnimator
|
||||||
|
import ru.dbotthepony.kstarbound.client.render.SpriteAnimator
|
||||||
|
import ru.dbotthepony.kstarbound.client.render.makeSpriteAnimator
|
||||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
||||||
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
import ru.dbotthepony.kvector.matrix.Matrix4fStack
|
||||||
|
|
||||||
open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
|
open class ProjectileRenderer(state: GLStateTracker, entity: Projectile, chunk: ClientChunk?) : EntityRenderer(state, entity, chunk) {
|
||||||
private val def = entity.def
|
private val def = entity.def
|
||||||
private val texture = state.loadNamedTextureSafe(def.image.texture)
|
private val animator = def.image.makeSpriteAnimator(state, def.animationCycle, def.animationLoops)
|
||||||
private val animator = FrameSetAnimator(def.image, def.animationCycle, entity.def.animationLoops)
|
|
||||||
|
|
||||||
override fun render(stack: Matrix4fStack) {
|
override fun render(stack: Matrix4fStack) {
|
||||||
state.shaderVertexTexture.use()
|
state.shaderVertexTexture.use()
|
||||||
state.shaderVertexTexture.transform.set(stack.last)
|
state.shaderVertexTexture.transform.set(stack.last)
|
||||||
state.activeTexture = 0
|
state.activeTexture = 0
|
||||||
state.shaderVertexTexture["_texture"] = 0
|
state.shaderVertexTexture["_texture"] = 0
|
||||||
texture.bind()
|
|
||||||
|
|
||||||
animator.advance()
|
animator.advance()
|
||||||
|
val sprite = animator.sprite
|
||||||
|
sprite.texture.bind()
|
||||||
|
|
||||||
val builder = state.flat2DTexturedQuads.small
|
val builder = state.flat2DTexturedQuads.small
|
||||||
|
|
||||||
builder.begin()
|
builder.begin()
|
||||||
|
|
||||||
val (u0, v0) = texture.pixelToUV(animator.frameObj.texturePosition)
|
val width = (sprite.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||||
val (u1, v1) = texture.pixelToUV(animator.frameObj.textureEndPosition)
|
val height = (sprite.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
||||||
|
|
||||||
val width = (animator.frameObj.width / PIXELS_IN_STARBOUND_UNITf) / 2f
|
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle, sprite.transformer)
|
||||||
val height = (animator.frameObj.height / PIXELS_IN_STARBOUND_UNITf) / 2f
|
|
||||||
|
|
||||||
builder.quadRotatedZ(-width, -height, width, height, 5f, 0f, 0f, entity.movement.angle,
|
|
||||||
QuadTransformers.uv(u0, v0, u1, v1)
|
|
||||||
)
|
|
||||||
|
|
||||||
builder.upload()
|
builder.upload()
|
||||||
builder.draw()
|
builder.draw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,15 +2,19 @@ package ru.dbotthepony.kstarbound.defs.animation
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableList
|
import com.google.common.collect.ImmutableList
|
||||||
import com.google.common.collect.ImmutableMap
|
import com.google.common.collect.ImmutableMap
|
||||||
|
import com.google.gson.GsonBuilder
|
||||||
import com.google.gson.JsonArray
|
import com.google.gson.JsonArray
|
||||||
import com.google.gson.JsonNull
|
import com.google.gson.JsonNull
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import com.google.gson.JsonSyntaxException
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
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 ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
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
|
||||||
|
import ru.dbotthepony.kstarbound.io.json.transform
|
||||||
|
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||||
import ru.dbotthepony.kvector.vector.nint.Vector4i
|
import ru.dbotthepony.kvector.vector.nint.Vector4i
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
@ -22,7 +26,7 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
*
|
*
|
||||||
* Несмотря на название, НЕ ЯВЛЯЕТСЯ изображением, а лишь метаданными для *множества* возможных изображений
|
* Несмотря на название, НЕ ЯВЛЯЕТСЯ изображением, а лишь метаданными для *множества* возможных изображений
|
||||||
*/
|
*/
|
||||||
class AtlasDefinition private constructor(
|
class AtlasConfiguration private constructor(
|
||||||
/**
|
/**
|
||||||
* Имя данного атласа (путь к файлу)
|
* Имя данного атласа (путь к файлу)
|
||||||
*/
|
*/
|
||||||
@ -149,14 +153,14 @@ class AtlasDefinition private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val EMPTY: AtlasDefinition
|
val EMPTY: AtlasConfiguration
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val sprite = Sprite("root", Vector4i(0, 0, 0, 0))
|
val sprite = Sprite("root", Vector4i(0, 0, 0, 0))
|
||||||
EMPTY = AtlasDefinition("null", ImmutableMap.of("root", sprite, "default", sprite, "0", sprite), ImmutableList.of(sprite))
|
EMPTY = AtlasConfiguration("null", ImmutableMap.of("root", sprite, "default", sprite, "0", sprite), ImmutableList.of(sprite))
|
||||||
}
|
}
|
||||||
|
|
||||||
private val cache = ConcurrentHashMap<String, AtlasDefinition>()
|
private val cache = ConcurrentHashMap<String, AtlasConfiguration>()
|
||||||
|
|
||||||
private fun generateFakeNames(dimensions: Vector2i): JsonArray {
|
private fun generateFakeNames(dimensions: Vector2i): JsonArray {
|
||||||
return JsonArray(dimensions.y).also {
|
return JsonArray(dimensions.y).also {
|
||||||
@ -175,7 +179,7 @@ class AtlasDefinition private constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseFrames(input: JsonReader, name: String): AtlasDefinition {
|
private fun parseFrames(input: JsonReader, name: String): AtlasConfiguration {
|
||||||
val read = TypeAdapters.JSON_ELEMENT.read(input)
|
val read = TypeAdapters.JSON_ELEMENT.read(input)
|
||||||
|
|
||||||
if (read !is JsonObject) {
|
if (read !is JsonObject) {
|
||||||
@ -241,10 +245,10 @@ class AtlasDefinition private constructor(
|
|||||||
sprites[k] = sprites[v.asString] ?: throw JsonSyntaxException("$k want to refer to sprite $v, but it does not exist")
|
sprites[k] = sprites[v.asString] ?: throw JsonSyntaxException("$k want to refer to sprite $v, but it does not exist")
|
||||||
}
|
}
|
||||||
|
|
||||||
return AtlasDefinition(name, ImmutableMap.copyOf(sprites), spriteList)
|
return AtlasConfiguration(name, ImmutableMap.copyOf(sprites), spriteList)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun recursiveGet(name: String, folder: String): AtlasDefinition? {
|
private fun recursiveGet(name: String, folder: String): AtlasConfiguration? {
|
||||||
var current = folder
|
var current = folder
|
||||||
|
|
||||||
while (current != "/" && current != "") {
|
while (current != "/" && current != "") {
|
||||||
@ -272,10 +276,24 @@ class AtlasDefinition private constructor(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(path: String): AtlasDefinition {
|
/**
|
||||||
|
* Пытается найти конфигурацию (файл "frames") атласа для заданного пути (заданного изображения/атласа)
|
||||||
|
*
|
||||||
|
* Алгоритм поиска конфигурации таков:
|
||||||
|
* * Проверяется папка с целевым файлом на наличие файла с таким же именем, но с расширением frames;
|
||||||
|
* * Если файл не был найден, процесс повторяется переходом в папку выше, пока не будет достигнут корень файловой системы;
|
||||||
|
* * Если файл не был найден, процесс начинается заново, внутри целевой папки, но уже происходит поиск файла с именем default.frames;
|
||||||
|
* * Процесс повторяется как в пункте 2 пока не будет найден файл в одной из вышестоящих папок.
|
||||||
|
*
|
||||||
|
* Если ни один файл не был найден, то возвращается [EMPTY]
|
||||||
|
*
|
||||||
|
* Данная функция кеширует результаты поиска
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun get(path: String): AtlasConfiguration {
|
||||||
require(path[0] == '/') { "$path is not an absolute path" }
|
require(path[0] == '/') { "$path is not an absolute path" }
|
||||||
val folder = path.substringBeforeLast('/').lowercase()
|
val folder = path.substringBeforeLast('/').lowercase()
|
||||||
val filename = path.substringAfterLast('/').substringBefore('.').lowercase()
|
val filename = path.substringAfterLast('/').substringBefore(':').substringBefore('.').lowercase()
|
||||||
|
|
||||||
val direct = recursiveGet(filename, folder)
|
val direct = recursiveGet(filename, folder)
|
||||||
if (direct != null) return direct
|
if (direct != null) return direct
|
||||||
@ -285,5 +303,11 @@ class AtlasDefinition private constructor(
|
|||||||
|
|
||||||
return EMPTY
|
return EMPTY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val ADAPTER: TypeAdapter<AtlasConfiguration?> = Starbound.stringTypeAdapter.transform(read = read@{ get(it ?: return@read it as AtlasConfiguration?) }, write = write@{ it?.name })
|
||||||
|
|
||||||
|
fun registerGson(gsonBuilder: GsonBuilder) {
|
||||||
|
gsonBuilder.registerTypeAdapter(ADAPTER)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package ru.dbotthepony.kstarbound.defs.animation
|
||||||
|
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import com.google.gson.TypeAdapter
|
||||||
|
import com.google.gson.stream.JsonReader
|
||||||
|
import com.google.gson.stream.JsonToken
|
||||||
|
import com.google.gson.stream.JsonWriter
|
||||||
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Хранит данные (пару) вида "/example/animated.png" у которого, вероятнее всего, есть "/example/animated.frames"
|
||||||
|
*
|
||||||
|
* @see [AtlasConfiguration.Companion.get]
|
||||||
|
*/
|
||||||
|
data class ImageReference(
|
||||||
|
val image: String,
|
||||||
|
val config: AtlasConfiguration,
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Вызывает [AtlasConfiguration.Companion.get] автоматически
|
||||||
|
*
|
||||||
|
* @see ImageReference
|
||||||
|
*/
|
||||||
|
constructor(image: String) : this(image, AtlasConfiguration.get(image))
|
||||||
|
|
||||||
|
companion object : TypeAdapter<ImageReference>() {
|
||||||
|
override fun write(out: JsonWriter, value: ImageReference) {
|
||||||
|
out.value(value.image)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun read(`in`: JsonReader): ImageReference {
|
||||||
|
if (`in`.peek() == JsonToken.STRING) {
|
||||||
|
val image = Starbound.readingFolderTransformer(`in`.nextString())
|
||||||
|
|
||||||
|
if (image.contains(':')) {
|
||||||
|
throw JsonSyntaxException("Expected atlas/image reference, but got sprite reference: $image")
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImageReference(image, AtlasConfiguration.get(image))
|
||||||
|
}
|
||||||
|
|
||||||
|
throw JsonSyntaxException("Expected atlas/image reference, but got: ${`in`.peek()}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,13 +7,16 @@ import com.google.gson.stream.JsonWriter
|
|||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Хранит данные (пару) вида "/example/image.png:sprite.name"
|
||||||
|
*/
|
||||||
data class SpriteReference(
|
data class SpriteReference(
|
||||||
val path: String,
|
val image: String,
|
||||||
val sprite: AtlasDefinition.Sprite
|
val sprite: AtlasConfiguration.Sprite
|
||||||
) {
|
) {
|
||||||
companion object : TypeAdapter<SpriteReference>() {
|
companion object : TypeAdapter<SpriteReference>() {
|
||||||
fun parse(input: String): SpriteReference {
|
fun parse(input: String): SpriteReference {
|
||||||
val grid = AtlasDefinition.get(input.substringBefore(':'))
|
val grid = AtlasConfiguration.get(input.substringBefore(':'))
|
||||||
|
|
||||||
return when (input.count { it == ':' }) {
|
return when (input.count { it == ':' }) {
|
||||||
0 -> SpriteReference(input, grid.any())
|
0 -> SpriteReference(input, grid.any())
|
||||||
@ -23,7 +26,7 @@ data class SpriteReference(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun write(out: JsonWriter, value: SpriteReference) {
|
override fun write(out: JsonWriter, value: SpriteReference) {
|
||||||
out.value(value.path + ":" + value.sprite.name)
|
out.value(value.image + ":" + value.sprite.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): SpriteReference {
|
override fun read(`in`: JsonReader): SpriteReference {
|
||||||
|
@ -7,6 +7,7 @@ import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
|||||||
import org.apache.logging.log4j.LogManager
|
import org.apache.logging.log4j.LogManager
|
||||||
import ru.dbotthepony.kstarbound.Starbound
|
import ru.dbotthepony.kstarbound.Starbound
|
||||||
import ru.dbotthepony.kstarbound.defs.*
|
import ru.dbotthepony.kstarbound.defs.*
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.ImageReference
|
||||||
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
import ru.dbotthepony.kstarbound.io.json.BuilderAdapter
|
||||||
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
import ru.dbotthepony.kstarbound.io.json.CustomEnumTypeAdapter
|
||||||
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
import ru.dbotthepony.kstarbound.registerTypeAdapter
|
||||||
@ -79,7 +80,7 @@ class ConfigurableProjectile : RawPrototype<ConfigurableProjectile, ConfiguredPr
|
|||||||
lightColor = lightColor,
|
lightColor = lightColor,
|
||||||
onlyHitTerrain = onlyHitTerrain,
|
onlyHitTerrain = onlyHitTerrain,
|
||||||
orientationLocked = orientationLocked,
|
orientationLocked = orientationLocked,
|
||||||
image = IFrameGrid.loadFrameStrip(ensureAbsolutePath(requireNotNull(image) { "image is null" }, directory), weak = true),
|
image = ImageReference(Starbound.readingFolderTransformer(requireNotNull(image) { "image is null" })),
|
||||||
timeToLive = timeToLive,
|
timeToLive = timeToLive,
|
||||||
animationCycle = animationCycle,
|
animationCycle = animationCycle,
|
||||||
bounces = bounces,
|
bounces = bounces,
|
||||||
|
@ -6,6 +6,7 @@ import ru.dbotthepony.kstarbound.Starbound
|
|||||||
import ru.dbotthepony.kstarbound.defs.AssembledPrototype
|
import ru.dbotthepony.kstarbound.defs.AssembledPrototype
|
||||||
import ru.dbotthepony.kstarbound.defs.DamageType
|
import ru.dbotthepony.kstarbound.defs.DamageType
|
||||||
import ru.dbotthepony.kstarbound.defs.FrameSet
|
import ru.dbotthepony.kstarbound.defs.FrameSet
|
||||||
|
import ru.dbotthepony.kstarbound.defs.animation.ImageReference
|
||||||
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
|
import ru.dbotthepony.kstarbound.world.entities.projectile.AbstractProjectileMovementController
|
||||||
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
import ru.dbotthepony.kstarbound.world.entities.projectile.Projectile
|
||||||
import ru.dbotthepony.kvector.vector.Color
|
import ru.dbotthepony.kvector.vector.Color
|
||||||
@ -21,7 +22,7 @@ class ConfiguredProjectile(
|
|||||||
val lightColor: Color?,
|
val lightColor: Color?,
|
||||||
val onlyHitTerrain: Boolean,
|
val onlyHitTerrain: Boolean,
|
||||||
val orientationLocked: Boolean,
|
val orientationLocked: Boolean,
|
||||||
val image: FrameSet,
|
val image: ImageReference,
|
||||||
val timeToLive: Double,
|
val timeToLive: Double,
|
||||||
val animationCycle: Double,
|
val animationCycle: Double,
|
||||||
val bounces: Int,
|
val bounces: Int,
|
||||||
|
@ -29,14 +29,14 @@ fun <T> TypeAdapter<T>.transformWrite(transformer: (T) -> T): TypeAdapter<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun <T> TypeAdapter<T>.transform(transformRead: (T) -> T, transformWrite: (T) -> T): TypeAdapter<T> {
|
fun <In, Out> TypeAdapter<In>.transform(read: (In) -> Out, write: (Out) -> In): TypeAdapter<Out> {
|
||||||
return object : TypeAdapter<T>() {
|
return object : TypeAdapter<Out>() {
|
||||||
override fun write(out: JsonWriter, value: T) {
|
override fun write(out: JsonWriter, value: Out) {
|
||||||
return this@transform.write(out, transformWrite(value))
|
return this@transform.write(out, write(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun read(`in`: JsonReader): T {
|
override fun read(`in`: JsonReader): Out {
|
||||||
return transformRead(this@transform.read(`in`))
|
return read(this@transform.read(`in`))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user