На самом деле, мы уже избавились от старого framegrid

This commit is contained in:
DBotThePony 2023-01-01 18:08:32 +07:00
parent 69a5061e9e
commit e263e29989
Signed by: DBot
GPG Key ID: DCC23B5715498507
2 changed files with 0 additions and 356 deletions

View File

@ -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" }
}
}

View File

@ -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