На самом деле, мы уже избавились от старого framegrid
This commit is contained in:
parent
69a5061e9e
commit
e263e29989
@ -1,355 +0,0 @@
|
||||
package ru.dbotthepony.kstarbound.defs
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.gson.*
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectArrayMap
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kvector.vector.nint.Vector2i
|
||||
import java.io.FileNotFoundException
|
||||
import kotlin.collections.HashMap
|
||||
|
||||
class MalformedFrameGridException(message: String? = null, cause: Throwable? = null) : Throwable(message, cause)
|
||||
|
||||
data class Frame(
|
||||
val texture: String,
|
||||
val texturePosition: Vector2i,
|
||||
val textureSize: Vector2i,
|
||||
) {
|
||||
val textureEndPosition = texturePosition + textureSize
|
||||
val width get() = textureSize.x
|
||||
val height get() = textureSize.y
|
||||
|
||||
val aspectRatioWH: Float get() {
|
||||
if (height == 0) {
|
||||
return 1f
|
||||
}
|
||||
|
||||
return width.toFloat() / height.toFloat()
|
||||
}
|
||||
|
||||
val aspectRatioHW: Float get() {
|
||||
if (width == 0) {
|
||||
return 1f
|
||||
}
|
||||
|
||||
return height.toFloat() / width.toFloat()
|
||||
}
|
||||
}
|
||||
|
||||
data class FrameSet(
|
||||
val texture: String,
|
||||
val name: String,
|
||||
val frames: List<Frame>,
|
||||
) {
|
||||
val frameCount get() = frames.size
|
||||
fun frame(num: Int) = frames[num]
|
||||
}
|
||||
|
||||
private class FrameSetBuilder(val name: String) {
|
||||
val frames = Object2ObjectArrayMap<String, Frame>()
|
||||
|
||||
fun build(texture: String): FrameSet {
|
||||
val list = ImmutableList.builder<Frame>()
|
||||
val rebuild = Int2ObjectArrayMap<Frame>()
|
||||
|
||||
for ((k, v) in frames) {
|
||||
val int = k.toIntOrNull()
|
||||
|
||||
if (int != null) {
|
||||
rebuild[int] = v
|
||||
}
|
||||
}
|
||||
|
||||
if (rebuild.size == 0) {
|
||||
throw IllegalStateException("Frame Set $name is empty")
|
||||
}
|
||||
|
||||
for (i in 0 until rebuild.size) {
|
||||
list.add(rebuild[i] ?: throw IllegalStateException("Frame Set $name has gap at $i"))
|
||||
}
|
||||
|
||||
return FrameSet(
|
||||
texture = texture,
|
||||
name = name,
|
||||
frames = list.build()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
interface IFrameGrid {
|
||||
val texture: String
|
||||
val size: Vector2i
|
||||
val dimensions: Vector2i
|
||||
val frames: List<FrameSet>
|
||||
val frameCount get() = frames.size
|
||||
val isResolved: Boolean
|
||||
|
||||
val textureSize get() = size * dimensions
|
||||
|
||||
fun resolve(determinedSize: Vector2i)
|
||||
|
||||
operator fun get(index: Int) = frames[index]
|
||||
|
||||
operator fun get(index: String): FrameSet {
|
||||
for (frame in frames) {
|
||||
if (index == frame.name) {
|
||||
return frame
|
||||
}
|
||||
}
|
||||
|
||||
throw IndexOutOfBoundsException("No such frame strip with name $index")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun splitName(textureName: String, input: String, warn: Boolean, lazy: () -> String): Pair<String, String> {
|
||||
val split = input.split(':')
|
||||
val frameName: String
|
||||
val setName: String
|
||||
|
||||
when (split.size) {
|
||||
1 -> {
|
||||
setName = "root"
|
||||
frameName = split[0]
|
||||
}
|
||||
2 -> {
|
||||
setName = split[0]
|
||||
frameName = split[1]
|
||||
}
|
||||
else -> throw IllegalArgumentException("${lazy.invoke()}: Malformed frame name $input")
|
||||
}
|
||||
|
||||
val frameNumber = frameName.toIntOrNull()
|
||||
|
||||
if (frameNumber == null) {
|
||||
if (warn)
|
||||
LOGGER.warn("{}: Frame {} will be discarded after frame grid is built, because it is not an integer", textureName, frameName)
|
||||
} else {
|
||||
require(frameNumber >= 0) { "${lazy.invoke()}: Frame number of $frameNumber does not make any sense" }
|
||||
}
|
||||
|
||||
return setName to frameName
|
||||
}
|
||||
|
||||
private fun generateFakeNames(dimensions: Vector2i): JsonArray {
|
||||
return JsonArray(dimensions.y).also {
|
||||
var stripElem = 0
|
||||
|
||||
for (stripNum in 0 until dimensions.y) {
|
||||
val strip = JsonArray(dimensions.x)
|
||||
|
||||
for (i in 0 until dimensions.x) {
|
||||
strip.add(stripElem.toString())
|
||||
stripElem++
|
||||
}
|
||||
|
||||
it.add(strip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Так как объект довольно сложный для автоматической десериализации через gson
|
||||
* поэтому вот так
|
||||
*/
|
||||
fun fromJson(input: JsonObject, texture: String): IFrameGrid {
|
||||
val frameGrid: JsonObject
|
||||
val aliases: JsonObject
|
||||
val texturePath = "$texture.png".intern()
|
||||
|
||||
if (input["frameGrid"] is JsonObject) {
|
||||
frameGrid = input["frameGrid"] as JsonObject
|
||||
} else {
|
||||
frameGrid = input
|
||||
}
|
||||
|
||||
if (input["aliases"] is JsonObject) {
|
||||
aliases = input["aliases"] as JsonObject
|
||||
} else {
|
||||
aliases = JsonObject()
|
||||
}
|
||||
|
||||
val size = Starbound.gson.fromJson(frameGrid["size"], Vector2i::class.java) ?: throw IllegalArgumentException("Size is missing")
|
||||
val dimensions = Starbound.gson.fromJson(frameGrid["dimensions"], Vector2i::class.java) ?: throw IllegalArgumentException("Dimensions are missing")
|
||||
|
||||
require(size.x > 0) { "Invalid texture width of ${size.x}" }
|
||||
require(size.y > 0) { "Invalid texture height of ${size.y}" }
|
||||
|
||||
require(dimensions.x > 0) { "Invalid texture frame count of ${dimensions.x}" }
|
||||
require(dimensions.y > 0) { "Invalid texture stripe count of ${dimensions.y}" }
|
||||
|
||||
val names = frameGrid["names"] as? JsonArray ?: generateFakeNames(dimensions)
|
||||
|
||||
if (names.size() != dimensions.y) {
|
||||
LOGGER.warn("{} inconsistency: it has Y frame span of {}, but {} name strips are defined", texture, dimensions.y, names.size())
|
||||
}
|
||||
|
||||
val frameSets = Object2ObjectArrayMap<String, FrameSetBuilder>()
|
||||
|
||||
for (yPosition in 0 until names.size()) {
|
||||
val list = names[yPosition] as? JsonArray ?: throw IllegalArgumentException("names->$yPosition is not an array")
|
||||
|
||||
if (list.size() != dimensions.x) {
|
||||
LOGGER.warn("{} inconsistency: it has X frame span of {}, but strip at {} has {} names defined", texture, dimensions.x, yPosition, list.size())
|
||||
}
|
||||
|
||||
for (xPosition in 0 until list.size()) {
|
||||
val fullName = list[xPosition]
|
||||
|
||||
if (fullName is JsonNull) {
|
||||
continue
|
||||
}
|
||||
|
||||
fullName as? JsonPrimitive ?: throw IllegalArgumentException("names->$yPosition->$xPosition: Illegal value $fullName")
|
||||
|
||||
val (setName, frameNumber) = splitName(texture, fullName.asString, true) { "names->$yPosition->$xPosition" }
|
||||
val frameSet = frameSets.computeIfAbsent(setName, ::FrameSetBuilder)
|
||||
|
||||
frameSet.frames[frameNumber] = Frame(
|
||||
texture = texturePath,
|
||||
textureSize = size,
|
||||
texturePosition = Vector2i(x = size.x * xPosition, y = size.y * yPosition))
|
||||
}
|
||||
}
|
||||
|
||||
for ((newName, originalName) in aliases.entrySet()) {
|
||||
originalName as? JsonPrimitive ?: throw IllegalArgumentException("aliases->$newName: Illegal value $originalName")
|
||||
|
||||
val (oSetName, oFrameNumber) = splitName(texture, originalName.asString, false) { "alias->$newName" }
|
||||
val (nSetName, nFrameNumber) = splitName(texture, newName, true) { "alias->$newName" }
|
||||
|
||||
val oFrameSet = frameSets.computeIfAbsent(oSetName, ::FrameSetBuilder)
|
||||
val nFrameSet = frameSets.computeIfAbsent(nSetName, ::FrameSetBuilder)
|
||||
|
||||
nFrameSet.frames[nFrameNumber] = requireNotNull(oFrameSet.frames[oFrameNumber]) { "alias->$newName points to nothing" }
|
||||
}
|
||||
|
||||
val frameSetList = ImmutableList.builder<FrameSet>()
|
||||
|
||||
for (frameSet in frameSets.values) {
|
||||
frameSetList.add(frameSet.build(texturePath))
|
||||
}
|
||||
|
||||
return ResolvedFrameGrid(texturePath, size, dimensions, frameSetList.build())
|
||||
}
|
||||
|
||||
fun singleFrame(texturePath: String, size: Vector2i = Vector2i.ZERO): IFrameGrid {
|
||||
val frame = Frame(
|
||||
texture = texturePath,
|
||||
textureSize = size,
|
||||
texturePosition = Vector2i.ZERO)
|
||||
|
||||
return ResolvedFrameGrid(
|
||||
texture = texturePath,
|
||||
size = size,
|
||||
dimensions = Vector2i.POSITIVE_XY,
|
||||
frames = listOf(FrameSet(
|
||||
name = "root",
|
||||
frames = listOf(frame),
|
||||
texture = texturePath))
|
||||
)
|
||||
}
|
||||
|
||||
private val cache = HashMap<String, IFrameGrid>()
|
||||
|
||||
fun loadCached(path: String, weak: Boolean = false, weakSize: Vector2i = Vector2i.ZERO): IFrameGrid {
|
||||
if (path[0] != '/')
|
||||
throw IllegalArgumentException("Path must be absolute")
|
||||
|
||||
val splitPath = path.split('/').toMutableList()
|
||||
val last = splitPath.last()
|
||||
splitPath.removeLast()
|
||||
val splitLast = last.split('.')
|
||||
|
||||
try {
|
||||
if (splitLast.size == 1) {
|
||||
// имя уже абсолютное
|
||||
return cache.computeIfAbsent(path) {
|
||||
val frames = Starbound.locate("$path.frames", "${splitPath.joinToString("/")}/default.frames").orNull()
|
||||
|
||||
if (weak && frames == null) {
|
||||
LOGGER.warn("Expected animated texture at {}, but .frames metafile is missing.", path)
|
||||
|
||||
return@computeIfAbsent singleFrame("$path.png", weakSize)
|
||||
}
|
||||
|
||||
return@computeIfAbsent fromJson((frames?.readJson() ?: throw FileNotFoundException("Unable to find .frames meta for $path")) as JsonObject, path)
|
||||
}
|
||||
}
|
||||
|
||||
val newPath = "${splitPath.joinToString("/")}/${splitLast[0]}"
|
||||
|
||||
return cache.computeIfAbsent(newPath) {
|
||||
val frames = Starbound.locate("$newPath.frames", "${splitPath.joinToString("/")}/default.frames").orNull()
|
||||
|
||||
if (weak && frames == null) {
|
||||
LOGGER.warn("Expected animated texture at {}, but .frames metafile is missing.", newPath)
|
||||
|
||||
return@computeIfAbsent singleFrame("$newPath.png", weakSize)
|
||||
}
|
||||
|
||||
return@computeIfAbsent fromJson((frames?.readJson() ?: throw FileNotFoundException("Unable to find .frames meta for $path")) as JsonObject, newPath)
|
||||
}
|
||||
} catch (err: Throwable) {
|
||||
throw MalformedFrameGridException("Reading animated texture definition $path", err)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadFrameStrip(path: String, weak: Boolean = false, weakSize: Vector2i = Vector2i.ZERO): FrameSet {
|
||||
if (path[0] != '/')
|
||||
throw IllegalArgumentException("Path must be absolute")
|
||||
|
||||
val split = path.split(':')
|
||||
|
||||
if (split.size == 1) {
|
||||
// мы хотим получить главный кадр, который является анонимным
|
||||
val load = loadCached(path, weak, weakSize)
|
||||
check(load.frameCount == 1) { "$path has ${load.frameCount} frame strips, but we want exactly one!" }
|
||||
return load[0]
|
||||
}
|
||||
|
||||
val load = loadCached(split[0], weak, weakSize)
|
||||
return load[split[1]]
|
||||
}
|
||||
|
||||
private val LOGGER = LogManager.getLogger()
|
||||
}
|
||||
}
|
||||
|
||||
data class ResolvedFrameGrid(
|
||||
override val texture: String,
|
||||
override val size: Vector2i,
|
||||
override val dimensions: Vector2i,
|
||||
|
||||
override val frames: List<FrameSet>
|
||||
) : IFrameGrid {
|
||||
override val isResolved = true
|
||||
|
||||
override fun resolve(determinedSize: Vector2i) {
|
||||
require(determinedSize.x > 0) { "Invalid image width ${determinedSize.x}" }
|
||||
require(determinedSize.y > 0) { "Invalid image height ${determinedSize.y}" }
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
data class LazyFrameGrid(
|
||||
override val texture: String,
|
||||
override val size: Vector2i,
|
||||
override val dimensions: Vector2i,
|
||||
) : IFrameGrid {
|
||||
private var _frames: List<FrameSet>? = null
|
||||
|
||||
override val frames: List<FrameSet>
|
||||
get() = _frames ?: throw IllegalStateException("Call resolve() first")
|
||||
|
||||
override var isResolved = false
|
||||
private set
|
||||
|
||||
override fun resolve(determinedSize: Vector2i) {
|
||||
if (_frames != null)
|
||||
return
|
||||
|
||||
check(dimensions == determinedSize) { "$texture was expected to have dimensions of $dimensions, $determinedSize given" }
|
||||
}
|
||||
}
|
@ -5,7 +5,6 @@ import org.apache.logging.log4j.LogManager
|
||||
import ru.dbotthepony.kstarbound.Starbound
|
||||
import ru.dbotthepony.kstarbound.defs.AssembledPrototype
|
||||
import ru.dbotthepony.kstarbound.defs.DamageType
|
||||
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.Projectile
|
||||
|
Loading…
Reference in New Issue
Block a user