mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-12 08:58:02 -04:00
fix some animation bugs
This commit is contained in:
parent
be2e266279
commit
44004d5919
@ -79,6 +79,14 @@ class TextureAnimationTest {
|
||||
fun `draw 3,0 frames`() {
|
||||
val animation = create()
|
||||
animation.update(3.0f)
|
||||
assertSame(animation.frame1, c)
|
||||
assertSame(animation.frame2, a)
|
||||
assertEquals(animation.progress, 0.75f)
|
||||
}
|
||||
|
||||
fun `draw 3,5 frames`() {
|
||||
val animation = create()
|
||||
animation.update(3.5f)
|
||||
assertSame(animation.frame1, a)
|
||||
assertSame(animation.frame2, b)
|
||||
assertEquals(animation.progress, 0.0f)
|
||||
@ -104,13 +112,10 @@ class TextureAnimationTest {
|
||||
val animation = create()
|
||||
animation.update(1.0f)
|
||||
animation.update(1.0f)
|
||||
animation.update(0.2f)
|
||||
animation.update(0.3f)
|
||||
animation.update(0.5f)
|
||||
assertSame(animation.frame1, c)
|
||||
assertSame(animation.frame2, a)
|
||||
assertEquals(animation.progress, 0.5f)
|
||||
}
|
||||
|
||||
// TODO: update twice
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ class AnimationPropertiesTest {
|
||||
val json = """{
|
||||
"animation": {
|
||||
"interpolate": true,
|
||||
"frametime": 1,
|
||||
"frametime": 4,
|
||||
"frames": [
|
||||
{
|
||||
"index": 2,
|
||||
@ -41,9 +41,9 @@ class AnimationPropertiesTest {
|
||||
val data = properties.animation!!.create(Vec2i(16, 64))
|
||||
assertEquals(data, AnimationProperties.FrameData(listOf(
|
||||
AnimationProperties.Frame(0.1f, 2),
|
||||
AnimationProperties.Frame(0.05f, 1),
|
||||
AnimationProperties.Frame(0.05f, 2),
|
||||
AnimationProperties.Frame(0.05f, 3),
|
||||
AnimationProperties.Frame(0.2f, 1),
|
||||
AnimationProperties.Frame(0.2f, 2),
|
||||
AnimationProperties.Frame(0.2f, 3),
|
||||
), 4, Vec2i(16, 16)))
|
||||
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ abstract class SimpleTextureParticle(connection: PlayConnection, position: Vec3d
|
||||
}
|
||||
// calculate next texture
|
||||
val nextTextureResourceLocation = data.type.textures[age / (maxAge / totalTextures + 1)]
|
||||
if (texture?.nullCast<FileTexture>()?.resourceLocation == nextTextureResourceLocation) {
|
||||
if (texture?.nullCast<FileTexture>()?.file == nextTextureResourceLocation) {
|
||||
return
|
||||
}
|
||||
texture = connection.rendering?.context?.textures?.static?.get(nextTextureResourceLocation)
|
||||
|
@ -20,12 +20,13 @@ import de.bixilon.kutil.time.TimeUtil.nanos
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.buffer.uniform.IntUniformBuffer
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.shader.NativeShader
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.memory.MemoryTexture
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureAnimation
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.AnimationFrame
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.AnimationProperties
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
|
||||
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY_INSTANCE
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
@ -88,31 +89,29 @@ class SpriteAnimator(val context: RenderContext) {
|
||||
buffer!!.use(shader, bufferName)
|
||||
}
|
||||
|
||||
fun create(texture: Texture, properties: AnimationProperties): Pair<AnimationProperties.FrameData, TextureAnimation> {
|
||||
val data = properties.create(texture.size)
|
||||
fun create(texture: Texture, source: TextureBuffer, properties: AnimationProperties): Pair<AnimationProperties.FrameData, TextureAnimation> {
|
||||
val data = properties.create(source.size)
|
||||
|
||||
val source = texture.data.buffer
|
||||
|
||||
val textures: Array<Texture> = arrayOfNulls<Texture?>(data.textures).cast()
|
||||
val sprites: Array<Texture> = arrayOfNulls<Texture?>(data.textures).cast()
|
||||
for (i in 0 until data.textures) {
|
||||
val buffer = source.create(data.size)
|
||||
buffer.put(source, Vec2i(0, i * data.size.y), Vec2i.EMPTY, data.size)
|
||||
buffer.put(source, Vec2i(0, i * buffer.size.y), Vec2i.EMPTY_INSTANCE, data.size)
|
||||
|
||||
textures[i] = MemoryTexture(size = data.size, texture.properties, texture.mipmaps, buffer)
|
||||
sprites[i] = MemoryTexture(size = data.size, texture.properties, texture.mipmaps, buffer)
|
||||
}
|
||||
|
||||
val frames: Array<AnimationFrame> = arrayOfNulls<AnimationFrame?>(properties.frames.size).cast()
|
||||
val frames: Array<AnimationFrame> = arrayOfNulls<AnimationFrame?>(data.frames.size).cast()
|
||||
|
||||
for ((index, frame) in data.frames.withIndex()) {
|
||||
var sprite = textures.getOrNull(frame.texture)
|
||||
var sprite = sprites.getOrNull(frame.texture)
|
||||
if (sprite == null) {
|
||||
Log.log(LogMessageType.LOADING, LogLevels.WARN) { "Animation is referencing invalid frame: $texture (frame=${frame.texture})" }
|
||||
sprite = textures.first()
|
||||
sprite = sprites.first()
|
||||
}
|
||||
frames[index] = AnimationFrame(index, frame.time, sprite)
|
||||
}
|
||||
|
||||
val animation = TextureAnimation(animations.size, frames, properties.interpolate, textures)
|
||||
val animation = TextureAnimation(animations.size, frames, properties.interpolate, sprites)
|
||||
this.animations += animation
|
||||
|
||||
return Pair(data, animation)
|
||||
|
@ -58,7 +58,4 @@ interface Texture : ShaderTexture {
|
||||
if (mipmaps <= 0) return TextureData(buffer)
|
||||
return MipmapTextureData(buffer, mipmaps)
|
||||
}
|
||||
|
||||
fun updateAnimation(size: Vec2i, animation: TextureAnimation) = Unit
|
||||
fun updateProperties(properties: ImageProperties) = Unit
|
||||
}
|
||||
|
@ -13,23 +13,112 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.minosoft.assets.AssetsManager
|
||||
import de.bixilon.minosoft.assets.util.InputStreamUtil.readJson
|
||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.rendering.RenderConstants
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayProperties
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.TextureRenderData
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureAnimation
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.AnimationProperties
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
|
||||
import de.bixilon.minosoft.util.KUtil.toResourceLocation
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
interface FileTexture : Texture {
|
||||
val resourceLocation: ResourceLocation
|
||||
abstract class FileTexture(
|
||||
val file: ResourceLocation,
|
||||
override var mipmaps: Int,
|
||||
) : Texture {
|
||||
override lateinit var renderData: TextureRenderData
|
||||
override lateinit var array: TextureArrayProperties
|
||||
override lateinit var size: Vec2i
|
||||
override lateinit var transparency: TextureTransparencies
|
||||
override lateinit var data: TextureData
|
||||
|
||||
override var properties: ImageProperties = ImageProperties.DEFAULT
|
||||
override var animation: TextureAnimation? = null
|
||||
override var state: TextureStates = TextureStates.DECLARED
|
||||
|
||||
|
||||
@Synchronized
|
||||
override fun load(context: RenderContext) {
|
||||
load(context.connection.assetsManager)
|
||||
if (state == TextureStates.LOADED) return
|
||||
|
||||
updateImageProperties(context.connection.assetsManager)
|
||||
val properties = this.properties
|
||||
val buffer = tryRead(context.connection.assetsManager)
|
||||
|
||||
|
||||
if (properties.animation != null) {
|
||||
loadSprites(context, properties.animation, buffer)
|
||||
} else {
|
||||
load(buffer)
|
||||
}
|
||||
|
||||
state = TextureStates.LOADED
|
||||
}
|
||||
|
||||
fun load(assets: AssetsManager)
|
||||
private fun loadSprites(context: RenderContext, properties: AnimationProperties, buffer: TextureBuffer) {
|
||||
val (frames, animation) = context.textures.static.animator.create(this, buffer, properties)
|
||||
this.animation = animation
|
||||
this.size = frames.size
|
||||
|
||||
var transparency = TextureTransparencies.OPAQUE
|
||||
|
||||
for (sprite in animation.sprites) {
|
||||
when (sprite.transparency) {
|
||||
TextureTransparencies.OPAQUE -> continue
|
||||
TextureTransparencies.TRANSPARENT -> transparency = TextureTransparencies.TRANSPARENT
|
||||
TextureTransparencies.TRANSLUCENT -> {
|
||||
transparency = TextureTransparencies.TRANSLUCENT; break
|
||||
}
|
||||
}
|
||||
}
|
||||
this.transparency = transparency
|
||||
}
|
||||
|
||||
private fun load(buffer: TextureBuffer) {
|
||||
val data = createData(mipmaps, buffer)
|
||||
|
||||
this.size = data.size
|
||||
this.transparency = buffer.getTransparency()
|
||||
this.data = data
|
||||
}
|
||||
|
||||
private fun tryRead(assets: AssetsManager): TextureBuffer {
|
||||
try {
|
||||
return read(assets)
|
||||
} catch (error: Throwable) {
|
||||
state = TextureStates.ERRORED
|
||||
Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Can not load texture $file: $error" }
|
||||
if (error !is FileNotFoundException) {
|
||||
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { error }
|
||||
}
|
||||
return assets[RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION].readTexture()
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun read(assets: AssetsManager): TextureBuffer
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return file.toString()
|
||||
}
|
||||
|
||||
private fun updateImageProperties(assets: AssetsManager) {
|
||||
properties = assets.readImageProperties(file) ?: return
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -13,89 +13,18 @@
|
||||
|
||||
package de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2i
|
||||
import de.bixilon.minosoft.assets.AssetsManager
|
||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.rendering.RenderConstants
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureStates
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.TextureTransparencies
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.array.TextureArrayProperties
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.TextureData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.data.buffer.TextureBuffer
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.TextureRenderData
|
||||
import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.file.FileTexture.Companion.readImageProperties
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureAnimation
|
||||
import de.bixilon.minosoft.gui.rendering.textures.TextureUtil.readTexture
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
|
||||
class PNGTexture(
|
||||
override val resourceLocation: ResourceLocation,
|
||||
override var mipmaps: Int,
|
||||
) : FileTexture {
|
||||
override lateinit var renderData: TextureRenderData
|
||||
override var animation: TextureAnimation? = null
|
||||
file: ResourceLocation,
|
||||
mipmaps: Int,
|
||||
) : FileTexture(file, mipmaps) {
|
||||
|
||||
override lateinit var array: TextureArrayProperties
|
||||
override var state: TextureStates = TextureStates.DECLARED
|
||||
private set
|
||||
override lateinit var size: Vec2i
|
||||
private set
|
||||
override lateinit var transparency: TextureTransparencies
|
||||
private set
|
||||
override var properties: ImageProperties = ImageProperties.DEFAULT
|
||||
|
||||
|
||||
override lateinit var data: TextureData
|
||||
|
||||
|
||||
private fun updateImageProperties(assets: AssetsManager) {
|
||||
properties = assets.readImageProperties(resourceLocation) ?: return
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun load(assets: AssetsManager) {
|
||||
if (state == TextureStates.LOADED) return
|
||||
|
||||
updateImageProperties(assets)
|
||||
val buffer = readTexture(assets)
|
||||
|
||||
val data = createData(mipmaps, buffer)
|
||||
|
||||
this.size = data.size
|
||||
this.transparency = buffer.getTransparency()
|
||||
this.data = data
|
||||
|
||||
state = TextureStates.LOADED
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return resourceLocation.toString()
|
||||
}
|
||||
|
||||
private fun readTexture(assets: AssetsManager): TextureBuffer {
|
||||
try {
|
||||
return assets[resourceLocation].readTexture()
|
||||
} catch (error: Throwable) {
|
||||
state = TextureStates.ERRORED
|
||||
Log.log(LogMessageType.RENDERING, LogLevels.WARN) { "Can not load texture $resourceLocation: $error" }
|
||||
if (error !is FileNotFoundException) {
|
||||
Log.log(LogMessageType.RENDERING, LogLevels.VERBOSE) { error }
|
||||
}
|
||||
return assets[RenderConstants.DEBUG_TEXTURE_RESOURCE_LOCATION].readTexture()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateAnimation(size: Vec2i, animation: TextureAnimation) {
|
||||
this.animation = animation
|
||||
this.size = size
|
||||
}
|
||||
|
||||
override fun updateProperties(properties: ImageProperties) {
|
||||
this.properties = properties
|
||||
override fun read(assets: AssetsManager): TextureBuffer {
|
||||
return assets[file].readTexture()
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import de.bixilon.minosoft.gui.rendering.system.base.texture.texture.Texture
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.OpenGLRenderSystem
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.glFormat
|
||||
import de.bixilon.minosoft.gui.rendering.system.opengl.texture.OpenGLTextureUtil.glType
|
||||
import de.bixilon.minosoft.gui.rendering.textures.properties.ImageProperties
|
||||
import de.bixilon.minosoft.util.logging.Log
|
||||
import de.bixilon.minosoft.util.logging.LogLevels
|
||||
import de.bixilon.minosoft.util.logging.LogMessageType
|
||||
@ -85,7 +84,6 @@ class OpenGLFontTextureArray(
|
||||
|
||||
private fun load(texture: Texture) {
|
||||
if (texture.state != TextureStates.LOADED) texture.load(context)
|
||||
texture.updateProperties(ImageProperties.DEFAULT)
|
||||
val pixel = 1.0f / resolution
|
||||
val size = texture.size
|
||||
|
||||
|
@ -134,25 +134,21 @@ class OpenGLTextureArray(
|
||||
val uvEnd = if (size.x == resolution && size.y == resolution) null else Vec2(size) / resolution
|
||||
val array = TextureArrayProperties(uvEnd, resolution, pixel)
|
||||
|
||||
val animationProperties = texture.properties.animation
|
||||
if (animationProperties == null) {
|
||||
val animation = texture.animation
|
||||
if (animation == null) {
|
||||
this.resolution[arrayId] += texture
|
||||
texture.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, -1)
|
||||
texture.array = array
|
||||
return
|
||||
}
|
||||
|
||||
val (frames, animation) = animator.create(texture, animationProperties)
|
||||
|
||||
texture.renderData = OpenGLTextureData(-1, -1, uvEnd, animation.animationData)
|
||||
|
||||
for (split in animation.textures) {
|
||||
split.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, animation.animationData)
|
||||
split.array = array
|
||||
this.resolution[arrayId] += split
|
||||
for (sprite in animation.sprites) {
|
||||
sprite.renderData = OpenGLTextureData(arrayId, lastTextureId[arrayId]++, uvEnd, animation.animationData)
|
||||
sprite.array = array
|
||||
this.resolution[arrayId] += sprite
|
||||
}
|
||||
|
||||
texture.updateAnimation(frames.size, animation)
|
||||
}
|
||||
|
||||
override fun load(textures: Collection<Texture>) {
|
||||
|
@ -21,7 +21,7 @@ class TextureAnimation(
|
||||
val animationData: Int,
|
||||
val frames: Array<AnimationFrame>,
|
||||
val interpolate: Boolean,
|
||||
val textures: Array<Texture>,
|
||||
val sprites: Array<Texture>,
|
||||
) {
|
||||
private val totalTime = frames.getTotalTime()
|
||||
private var frame = frames.first()
|
||||
@ -56,6 +56,7 @@ class TextureAnimation(
|
||||
left -= frame.time
|
||||
frame = frame.next()
|
||||
}
|
||||
this.frame = frame
|
||||
this.time = left
|
||||
this.frame1 = frame.texture
|
||||
this.frame2 = frame.next().texture
|
||||
|
@ -33,16 +33,17 @@ data class AnimationProperties(
|
||||
val count = size.y / height
|
||||
|
||||
val frames: MutableList<Frame> = mutableListOf()
|
||||
val frameTime = ticksToSeconds(this.frameTime)
|
||||
|
||||
if (this.frames.isEmpty()) {
|
||||
// automatic
|
||||
for (i in 0 until count) {
|
||||
frames += Frame(DEFAULT_FRAME_TIME, i)
|
||||
frames += Frame(frameTime, i)
|
||||
}
|
||||
} else {
|
||||
for (frame in this.frames) {
|
||||
when (frame) {
|
||||
is Number -> frames += Frame(DEFAULT_FRAME_TIME, frame.toInt())
|
||||
is Number -> frames += Frame(frameTime, frame.toInt())
|
||||
is Map<*, *> -> {
|
||||
frames += Frame(ticksToSeconds(frame["time"].toInt()), frame["index"].toInt())
|
||||
}
|
||||
@ -65,8 +66,6 @@ data class AnimationProperties(
|
||||
)
|
||||
|
||||
companion object {
|
||||
val DEFAULT_FRAME_TIME = ticksToSeconds(1)
|
||||
|
||||
|
||||
private fun ticksToSeconds(ticks: Int): Float {
|
||||
val millis = ticks * ProtocolDefinition.TICK_TIME
|
||||
|
Loading…
x
Reference in New Issue
Block a user