From 44004d59195d94f8cbb928dd86249fc0fa0f9cc9 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Tue, 12 Dec 2023 23:35:24 +0100 Subject: [PATCH] fix some animation bugs --- .../textures/TextureAnimationTest.kt | 13 ++- .../properties/AnimationPropertiesTest.kt | 8 +- .../texture/simple/SimpleTextureParticle.kt | 2 +- .../base/texture/sprite/SpriteAnimator.kt | 23 +++-- .../system/base/texture/texture/Texture.kt | 3 - .../base/texture/texture/file/FileTexture.kt | 97 ++++++++++++++++++- .../base/texture/texture/file/PNGTexture.kt | 81 +--------------- .../opengl/texture/OpenGLFontTextureArray.kt | 2 - .../opengl/texture/OpenGLTextureArray.kt | 16 ++- .../rendering/textures/TextureAnimation.kt | 3 +- .../properties/AnimationProperties.kt | 7 +- 11 files changed, 134 insertions(+), 121 deletions(-) diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/TextureAnimationTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/TextureAnimationTest.kt index 063ffe5d0..ac564df82 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/TextureAnimationTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/TextureAnimationTest.kt @@ -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 } diff --git a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationPropertiesTest.kt b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationPropertiesTest.kt index c69de1917..476f60db1 100644 --- a/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationPropertiesTest.kt +++ b/src/integration-test/kotlin/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationPropertiesTest.kt @@ -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))) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/simple/SimpleTextureParticle.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/simple/SimpleTextureParticle.kt index 25a953d15..b47099963 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/simple/SimpleTextureParticle.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/particle/types/render/texture/simple/SimpleTextureParticle.kt @@ -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()?.resourceLocation == nextTextureResourceLocation) { + if (texture?.nullCast()?.file == nextTextureResourceLocation) { return } texture = connection.rendering?.context?.textures?.static?.get(nextTextureResourceLocation) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/sprite/SpriteAnimator.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/sprite/SpriteAnimator.kt index ede658252..e7dad6879 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/sprite/SpriteAnimator.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/sprite/SpriteAnimator.kt @@ -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 { - val data = properties.create(texture.size) + fun create(texture: Texture, source: TextureBuffer, properties: AnimationProperties): Pair { + val data = properties.create(source.size) - val source = texture.data.buffer - - val textures: Array = arrayOfNulls(data.textures).cast() + val sprites: Array = arrayOfNulls(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 = arrayOfNulls(properties.frames.size).cast() + val frames: Array = arrayOfNulls(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) diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/Texture.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/Texture.kt index 7b4026808..be7d0786e 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/Texture.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/Texture.kt @@ -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 } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/FileTexture.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/FileTexture.kt index 8332053e0..49c02ea7d 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/FileTexture.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/FileTexture.kt @@ -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 { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/PNGTexture.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/PNGTexture.kt index 189687ef2..2b175f12a 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/PNGTexture.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/base/texture/texture/file/PNGTexture.kt @@ -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() } } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLFontTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLFontTextureArray.kt index f6f735c5b..9618d76be 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLFontTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLFontTextureArray.kt @@ -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 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt index 142176a4f..690a0bc44 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/system/opengl/texture/OpenGLTextureArray.kt @@ -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) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureAnimation.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureAnimation.kt index ab0201598..57b5a766b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureAnimation.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/TextureAnimation.kt @@ -21,7 +21,7 @@ class TextureAnimation( val animationData: Int, val frames: Array, val interpolate: Boolean, - val textures: Array, + val sprites: Array, ) { 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 diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationProperties.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationProperties.kt index f5c5b7619..078d50176 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationProperties.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/textures/properties/AnimationProperties.kt @@ -33,16 +33,17 @@ data class AnimationProperties( val count = size.y / height val frames: MutableList = 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