diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt index 5c76c7373..2805c30d2 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AbstractSkinElement.kt @@ -1,17 +1,6 @@ package ru.dbotthepony.mc.otm.client.render -import com.google.gson.JsonDeserializationContext -import com.google.gson.JsonDeserializer -import com.google.gson.JsonElement import com.google.gson.JsonObject -import com.google.gson.JsonPrimitive -import com.google.gson.JsonSerializationContext -import com.google.gson.JsonSerializer -import com.google.gson.JsonSyntaxException -import com.google.gson.TypeAdapter -import com.google.gson.internal.bind.TypeAdapters -import com.google.gson.stream.JsonReader -import com.google.gson.stream.JsonWriter import com.mojang.blaze3d.systems.RenderSystem import com.mojang.blaze3d.vertex.PoseStack import com.mojang.blaze3d.vertex.VertexConsumer @@ -19,7 +8,7 @@ import net.minecraft.network.FriendlyByteBuf import net.minecraft.resources.ResourceLocation import ru.dbotthepony.mc.otm.core.RGBAColor import ru.dbotthepony.mc.otm.core.linearInterpolation -import java.lang.reflect.Type +import java.lang.ref.WeakReference sealed class AbstractSkinElement { /** @@ -39,6 +28,14 @@ sealed class AbstractSkinElement { abstract val u1: Float abstract val v1: Float + internal open fun addListener(listener: AbstractSkinElement) { + // no-op by default + } + + internal open fun parentChanges(parent: AbstractSkinElement) { + + } + fun partialU(offset: Float): Float { return u0 + (offset / width) * (u1 - u0) } @@ -316,4 +313,40 @@ sealed class AbstractSkinElement { fun toNetwork(buff: FriendlyByteBuf) { type.toActualNetwork(this, buff) } + + abstract class Mutable : AbstractSkinElement() { + private val listeners = ArrayList>(0) + + override fun addListener(listener: AbstractSkinElement) { + synchronized(listeners) { + val iterator = listeners.listIterator() + + for (ref in iterator) { + if (ref.get() == null) { + iterator.remove() + } else if (ref.get() === listener) { + return + } + } + + listeners.add(WeakReference(listener)) + } + } + + protected fun notifyListeners() { + synchronized(listeners) { + val iterator = listeners.listIterator() + + for (ref in iterator) { + val value = ref.get() + + if (value == null) { + iterator.remove() + } else { + value.parentChanges(this) + } + } + } + } + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt index b129d29e0..be2bbc42b 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/AtlasSkinElement.kt @@ -22,7 +22,7 @@ class AtlasSkinElement( val spriteWidth: Float = Mth.smallestEncompassingPowerOfTwo(width.toInt()).toFloat(), val spriteHeight: Float = Mth.smallestEncompassingPowerOfTwo(height.toInt()).toFloat(), override val winding: UVWindingOrder = UVWindingOrder.NORMAL, -) : AbstractSkinElement() { +) : AbstractSkinElement.Mutable() { init { synchronized(keys) { if (keys.add(location)) { @@ -33,9 +33,9 @@ class AtlasSkinElement( } // optimistic: we should not create a lot of instances of this class - synchronized(listeners) { - listeners.add(WeakReference(this)) - val iterator = listeners.listIterator() + synchronized(atlasListeners) { + atlasListeners.add(WeakReference(this)) + val iterator = atlasListeners.listIterator() for (ref in iterator) { val value = ref.get() @@ -78,6 +78,8 @@ class AtlasSkinElement( u1 = get.u1 - (get.u1 - get.u0) * (1f - width / spriteWidth) v1 = get.v1 - (get.v1 - get.v0) * (1f - height / spriteHeight) + notifyListeners() + return get } @@ -120,12 +122,12 @@ class AtlasSkinElement( companion object { private val keys = HashSet() - private val listeners = ArrayList>() + private val atlasListeners = ArrayList>() val keysStream: Stream get() = keys.stream() fun notifyListeners() { - synchronized(listeners) { - val iterator = listeners.listIterator() + synchronized(atlasListeners) { + val iterator = atlasListeners.listIterator() for (ref in iterator) { val value = ref.get() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElementType.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElementType.kt index fc4c02131..e8af0c1a5 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElementType.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElementType.kt @@ -106,8 +106,8 @@ enum class SkinElementType { it["parent"] = value.parent.toJson() it["xOffset"] = JsonPrimitive(value.xOffset) it["yOffset"] = JsonPrimitive(value.yOffset) - it["subWidth"] = JsonPrimitive(value.subWidth) - it["subHeight"] = JsonPrimitive(value.subHeight) + it["subWidth"] = JsonPrimitive(value.width) + it["subHeight"] = JsonPrimitive(value.height) } } @@ -117,8 +117,8 @@ enum class SkinElementType { value.parent.toNetwork(buff) buff.writeFloat(value.xOffset) buff.writeFloat(value.yOffset) - buff.writeFloat(value.subWidth) - buff.writeFloat(value.subHeight) + buff.writeFloat(value.width) + buff.writeFloat(value.height) } override fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SubSkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SubSkinElement.kt index 02304f554..61dc54c57 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SubSkinElement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SubSkinElement.kt @@ -6,24 +6,37 @@ class SubSkinElement( val parent: AbstractSkinElement, val xOffset: Float = 0f, val yOffset: Float = 0f, - val subWidth: Float = parent.width, - val subHeight: Float = parent.height, + override val width: Float = parent.width, + override val height: Float = parent.height, private val overrideWinding: UVWindingOrder? = null -) : AbstractSkinElement() { - override val width: Float - get() = subWidth - override val height: Float - get() = subHeight +) : AbstractSkinElement.Mutable() { + override var u0: Float = 0f + private set + override var v0: Float = 0f + private set + override var u1: Float = 0f + private set + override var v1: Float = 0f + private set - override val u0: Float - get() = parent.u0 + (xOffset / parent.width) * (parent.u1 - parent.u0) - override val v0: Float - get() = parent.v0 + (yOffset / parent.height) * (parent.v1 - parent.v0) + private fun calculateUVs() { + u0 = parent.u0 + (xOffset / parent.width) * (parent.u1 - parent.u0) + v0 = parent.v0 + (yOffset / parent.height) * (parent.v1 - parent.v0) + u1 = parent.u0 + ((xOffset + width) / parent.width) * (parent.u1 - parent.u0) + v1 = parent.v0 + ((yOffset + height) / parent.height) * (parent.v1 - parent.v0) - override val u1: Float - get() = parent.u0 + ((xOffset + subWidth) / parent.width) * (parent.u1 - parent.u0) - override val v1: Float - get() = parent.v0 + ((yOffset + subHeight) / parent.height) * (parent.v1 - parent.v0) + notifyListeners() + } + + override fun parentChanges(parent: AbstractSkinElement) = calculateUVs() + + init { + parent.addListener(this) + } + + init { + calculateUVs() + } override val texture: ResourceLocation get() = parent.texture @@ -37,10 +50,10 @@ class SubSkinElement( fun copy( xOffset: Float = this.xOffset, yOffset: Float = this.yOffset, - subWidth: Float = this.subWidth, - subHeight: Float = this.subHeight, + width: Float = this.width, + height: Float = this.height, overrideWinding: UVWindingOrder? = this.overrideWinding - ) = SubSkinElement(parent, xOffset, yOffset, subWidth, subHeight, overrideWinding) + ) = SubSkinElement(parent, xOffset, yOffset, width, height, overrideWinding) } fun AbstractSkinElement.subElement(