diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt index 75a8b9342..7b5b67288 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/android/AndroidResearchType.kt @@ -23,6 +23,7 @@ import net.minecraft.world.level.ItemLike import net.minecraftforge.registries.ForgeRegistries import ru.dbotthepony.mc.otm.client.render.AbstractSkinElement import ru.dbotthepony.mc.otm.client.render.SkinElement +import ru.dbotthepony.mc.otm.client.render.SkinElementType import ru.dbotthepony.mc.otm.core.ListSet import ru.dbotthepony.mc.otm.core.TranslatableComponent import ru.dbotthepony.mc.otm.core.isActuallyEmpty @@ -92,7 +93,7 @@ class AndroidResearchType( private val customName: Component? = null, // ok - val skinIcon: JsonElement? = null, + val skinIcon: JsonObject? = null, val itemIcon: Item? = null, iconText: Component? = null, ) { @@ -102,7 +103,7 @@ class AndroidResearchType( val resolvedSkinIcon by lazy { check(isClient) { "Invalid realm" } if (skinIcon != null) - AbstractSkinElement.fromJson(skinIcon) + SkinElementType.fromJson(skinIcon) else null } @@ -550,7 +551,7 @@ class AndroidResearchType( } val skinIcon = if (buff.readBoolean()) { - TypeAdapters.JSON_ELEMENT.fromJson(buff.readUtf()) + TypeAdapters.JSON_ELEMENT.fromJson(buff.readUtf()) as JsonObject } else { null } @@ -591,7 +592,7 @@ class AndroidResearchType( val experience = value["experience"]?.asInt ?: 0 val customName = value["custom_name"]?.let(Component.Serializer::fromJson) val iconText = value["icon_text"]?.let(Component.Serializer::fromJson) - val skinIcon = value["skin_icon"] + val skinIcon = value["skin_icon"] as JsonObject? val itemIcon = value["item_icon"]?.let { ForgeRegistries.ITEMS.getValue(ResourceLocation(it.asString)).let { if (it == Items.AIR) null else it } } return AndroidResearchType( diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt index 79eaa9e0a..11ede6357 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/MatteryGUI.kt @@ -30,11 +30,11 @@ import java.util.* import kotlin.math.ceil object MatteryGUI { - val CHARGE = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge")) - val CHARGE_BG = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge_bg")) + val CHARGE = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge"), 80f, 9f) + val CHARGE_BG = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge_bg"), 80f, 9f) - val CHARGE_HUNGER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge_hunger")) - val CHARGE_HUNGER_BG = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge_hunger_bg")) + val CHARGE_HUNGER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge_hunger"), 80f, 9f) + val CHARGE_HUNGER_BG = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_charge/android_charge_hunger_bg"), 80f, 9f) private var originalBedButtonX = -1 private var originalBedButtonY = -1 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 1c01c365f..e6c95855b 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 @@ -22,14 +22,25 @@ import ru.dbotthepony.mc.otm.core.linearInterpolation import java.lang.reflect.Type sealed class AbstractSkinElement { - abstract val x: Float - abstract val y: Float + /** + * Expected image width in pixels, used in calculations + * and as default width argument in render methods + */ abstract val width: Float + + /** + * Expected image height in pixels, used in calculations + * and as default height argument in render methods + */ abstract val height: Float + abstract val u0: Float abstract val v0: Float abstract val u1: Float abstract val v1: Float + + abstract val type: SkinElementType + open val winding: UVWindingOrder get() = UVWindingOrder.NORMAL abstract val texture: ResourceLocation @@ -290,55 +301,11 @@ sealed class AbstractSkinElement { uploadOnto(pose, builder, x, y, width, height, z, color, winding) } - abstract fun toJson(): JsonElement - abstract fun toNetwork(buff: FriendlyByteBuf) + fun toJson(): JsonObject { + return type.toActualJson(this) + } - companion object : TypeAdapter(), JsonSerializer, JsonDeserializer { - fun toNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) { - buff.writeBoolean(value is AtlasSkinElement) - value.toNetwork(buff) - } - - fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { - if (buff.readBoolean()) { - return AtlasSkinElement.fromNetwork(buff) - } - - return SkinElement.fromNetwork(buff) - } - - fun fromJson(json: JsonElement): AbstractSkinElement { - if (json is JsonPrimitive) { - return AtlasSkinElement.fromJson(json) - } else if (json is JsonObject) { - return SkinElement.fromJson(json) - } else { - throw JsonSyntaxException("Unknown element type ${json::class.qualifiedName}") - } - } - - override fun write(out: JsonWriter, value: AbstractSkinElement) { - TypeAdapters.JSON_ELEMENT.write(out, value.toJson()) - } - - override fun read(`in`: JsonReader): AbstractSkinElement { - return fromJson(TypeAdapters.JSON_ELEMENT.read(`in`)) - } - - override fun serialize( - src: AbstractSkinElement, - typeOfSrc: Type, - context: JsonSerializationContext - ): JsonElement { - return src.toJson() - } - - override fun deserialize( - json: JsonElement, - typeOfT: Type, - context: JsonDeserializationContext - ): AbstractSkinElement { - return fromJson(json) - } + fun toNetwork(buff: FriendlyByteBuf) { + type.toActualNetwork(this, buff) } } 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 df60eb4af..702cae142 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 @@ -27,7 +27,9 @@ import java.lang.reflect.Type import java.util.stream.Stream class AtlasSkinElement( - val location: ResourceLocation + val location: ResourceLocation, + override val width: Float, + override val height: Float, ) : AbstractSkinElement() { init { synchronized(keys) { @@ -46,7 +48,7 @@ class AtlasSkinElement( check(isClient) { "Invalid realm" } val _textureAtlasSprite = _textureAtlasSprite - if (_textureAtlasSprite == null || changeset != WidgetAtlasHolder.INSTANCE.changeset || (WidgetAtlasHolder.INSTANCE.demandsRebuild && isClientThread())) { + if (_textureAtlasSprite == null || _textureAtlasSprite.name.let { it.namespace == "minecraft" && it.path == "missingno" } || changeset != WidgetAtlasHolder.INSTANCE.changeset || WidgetAtlasHolder.INSTANCE.speculateIfMinecraftEngineIsDumDum()) { val get = WidgetAtlasHolder.INSTANCE.getSprite(location) this._textureAtlasSprite = get changeset = WidgetAtlasHolder.INSTANCE.changeset @@ -61,11 +63,6 @@ class AtlasSkinElement( return textureAtlasSpriteImpl } - override val x get() = textureAtlasSprite.x.toFloat() - override val y get() = textureAtlasSprite.y.toFloat() - override val width get() = textureAtlasSprite.width.toFloat() - override val height get() = textureAtlasSprite.height.toFloat() - override val u0 get() = textureAtlasSprite.u0 override val v0 get() = textureAtlasSprite.v0 override val u1 get() = textureAtlasSprite.u1 @@ -87,15 +84,10 @@ class AtlasSkinElement( return "AtlasSkinElement[$location]" } - override fun toJson(): JsonElement { - return JsonPrimitive(location.toString()) - } + override val type: SkinElementType + get() = SkinElementType.ATLAS - override fun toNetwork(buff: FriendlyByteBuf) { - buff.writeResourceLocation(location) - } - - companion object : TypeAdapter(), JsonSerializer, JsonDeserializer { + companion object { private val keys = HashSet() val keysStream: Stream get() = keys.stream() @@ -103,38 +95,6 @@ class AtlasSkinElement( WidgetAtlasHolder.INSTANCE.queueRebuild() } - fun fromNetwork(buff: FriendlyByteBuf): AtlasSkinElement { - return AtlasSkinElement(buff.readResourceLocation()) - } - - fun fromJson(element: JsonElement): AtlasSkinElement { - return AtlasSkinElement(ResourceLocation.tryParse(element.asString) ?: ResourceLocation("missing")) - } - - override fun write(out: JsonWriter, value: AtlasSkinElement) { - TypeAdapters.JSON_ELEMENT.write(out, value.toJson()) - } - - override fun read(`in`: JsonReader): AtlasSkinElement { - return fromJson(TypeAdapters.JSON_ELEMENT.read(`in`)) - } - - override fun serialize( - src: AtlasSkinElement, - typeOfSrc: Type, - context: JsonSerializationContext - ): JsonElement { - return src.toJson() - } - - override fun deserialize( - json: JsonElement, - typeOfT: Type, - context: JsonDeserializationContext - ): AtlasSkinElement { - return fromJson(json) - } - val renderTypeGUI by lazy { val builder = RenderType.CompositeState.builder() diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt index 0db207f22..6bb570ef3 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/ResearchIcons.kt @@ -36,28 +36,28 @@ object ResearchIcons { init { var i = 0 - ICON_TRANSFER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_ATTACK_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_PLASMA_SHIELD_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_CLOAK = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_GRAVITATIONAL_STABILIZER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_AIR_BAGS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_JUMP_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_FEATHER_FALLING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_ITEM_MAGNET = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_ARROW = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_ARMOR = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_NANOBOTS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_NIGHT_VISION = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_OXYGEN_SUPPLY = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_PLASMA_SHIELD = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_SHOCKWAVE = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_LIMB_OVERCLOCKING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_STEP_ASSIST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_ENDER_TELEPORT = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_WIRELESS_CHARGING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_UNKNOWN = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_EXTENDED_REACH = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) - ICON_PHANTOM_ATTRACTOR = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}")) + ICON_TRANSFER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_ATTACK_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_PLASMA_SHIELD_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_CLOAK = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_GRAVITATIONAL_STABILIZER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_AIR_BAGS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_JUMP_BOOST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_FEATHER_FALLING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_ITEM_MAGNET = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_ARROW = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_ARMOR = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_NANOBOTS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_NIGHT_VISION = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_OXYGEN_SUPPLY = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_PLASMA_SHIELD = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_SHOCKWAVE = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_LIMB_OVERCLOCKING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_STEP_ASSIST = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_ENDER_TELEPORT = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_WIRELESS_CHARGING = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_UNKNOWN = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_EXTENDED_REACH = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) + ICON_PHANTOM_ATTRACTOR = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "android_research/android_stuff-${i++}"), 18f, 18f) } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt index 8f9a39c81..b590d5b72 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElement.kt @@ -52,91 +52,14 @@ fun ResourceLocation.vLine( @Suppress("unused") data class SkinElement @JvmOverloads constructor( override val texture: ResourceLocation, - override val x: Float, - override val y: Float, + val x: Float, + val y: Float, override val width: Float, override val height: Float, val imageWidth: Float = 256f, val imageHeight: Float = 256f, override val winding: UVWindingOrder = UVWindingOrder.NORMAL, ) : AbstractSkinElement() { - override fun toJson(): JsonObject { - return JsonObject().also { - it["texture"] = JsonPrimitive(texture.toString()) - it["x"] = JsonPrimitive(x) - it["y"] = JsonPrimitive(y) - it["width"] = JsonPrimitive(width) - it["height"] = JsonPrimitive(height) - it["imageWidth"] = JsonPrimitive(imageWidth) - it["imageHeight"] = JsonPrimitive(imageHeight) - it["winding"] = JsonPrimitive(winding.name) - } - } - - override fun toNetwork(buff: FriendlyByteBuf) { - buff.writeUtf(texture.toString()) - buff.writeFloat(x) - buff.writeFloat(y) - buff.writeFloat(width) - buff.writeFloat(height) - buff.writeFloat(imageWidth) - buff.writeFloat(imageHeight) - buff.writeEnum(winding) - } - - companion object : TypeAdapter(), JsonSerializer, JsonDeserializer { - fun fromNetwork(buff: FriendlyByteBuf): SkinElement { - val texture = ResourceLocation(buff.readUtf()) - val x = buff.readFloat() - val y = buff.readFloat() - val width = buff.readFloat() - val height = buff.readFloat() - val imageWidth = buff.readFloat() - val imageHeight = buff.readFloat() - val winding = buff.readEnum(UVWindingOrder::class.java) - - return SkinElement(texture, x, y, width, height, imageWidth, imageHeight, winding) - } - - fun fromJson(value: JsonObject): SkinElement { - val texture = value["texture"]?.asString ?: throw JsonSyntaxException("Missing texture element") - val x = value["x"]?.asFloat ?: throw JsonSyntaxException("Missing x element") - val y = value["y"]?.asFloat ?: throw JsonSyntaxException("Missing y element") - val width = value["width"]?.asFloat ?: throw JsonSyntaxException("Missing width element") - val height = value["height"]?.asFloat ?: throw JsonSyntaxException("Missing height element") - val imageWidth = value["imageWidth"]?.asFloat ?: throw JsonSyntaxException("Missing imageWidth element") - val imageHeight = value["imageHeight"]?.asFloat ?: throw JsonSyntaxException("Missing imageHeight element") - val winding = value["winding"]?.asString ?: throw JsonSyntaxException("Missing winding element") - - return SkinElement(ResourceLocation(texture), x, y, width, height, imageWidth, imageHeight, UVWindingOrder.valueOf(winding)) - } - - fun fromJson(value: JsonElement): SkinElement { - return fromJson(value as? JsonObject ?: throw JsonSyntaxException("Expected JsonObject, got ${value::class.qualifiedName}")) - } - - override fun write(out: JsonWriter, value: SkinElement) { - TypeAdapters.JSON_ELEMENT.write(out, value.toJson()) - } - - override fun read(reader: JsonReader): SkinElement { - val read = TypeAdapters.JSON_ELEMENT.read(reader) - return fromJson(read as? JsonObject ?: throw JsonSyntaxException("Expected JsonObject, got ${read::class.qualifiedName}")) - } - - override fun serialize(src: SkinElement, typeOfSrc: Type, context: JsonSerializationContext): JsonElement { - return src.toJson() - } - - override fun deserialize( - json: JsonElement, - typeOfT: Type, - context: JsonDeserializationContext - ): SkinElement { - return fromJson(json as? JsonObject ?: throw JsonSyntaxException("Expected JsonObject, got ${json::class.qualifiedName}")) - } - } - init { require(x >= 0f) { "Invalid x $x" } require(y >= 0f) { "Invalid y $y" } @@ -150,4 +73,7 @@ data class SkinElement @JvmOverloads constructor( override val v0 = this.y / imageHeight override val u1 = (this.x + width) / imageWidth override val v1 = (this.y + height) / imageHeight + + override val type: SkinElementType + get() = SkinElementType.SINGLE } 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 new file mode 100644 index 000000000..2b871be60 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SkinElementType.kt @@ -0,0 +1,172 @@ +package ru.dbotthepony.mc.otm.client.render + +import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive +import com.google.gson.JsonSyntaxException +import net.minecraft.network.FriendlyByteBuf +import net.minecraft.resources.ResourceLocation +import ru.dbotthepony.mc.otm.core.set + +enum class SkinElementType { + SINGLE { + override fun toJson(value: AbstractSkinElement): JsonObject { + require(value is SkinElement) { "Invalid skin element provided, expected SkinElement, got ${value::class.qualifiedName}" } + + return JsonObject().also { + it["texture"] = JsonPrimitive(value.texture.toString()) + it["x"] = JsonPrimitive(value.x) + it["y"] = JsonPrimitive(value.y) + it["width"] = JsonPrimitive(value.width) + it["height"] = JsonPrimitive(value.height) + it["imageWidth"] = JsonPrimitive(value.imageWidth) + it["imageHeight"] = JsonPrimitive(value.imageHeight) + it["winding"] = JsonPrimitive(value.winding.name) + } + } + + override fun toNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) { + require(value is SkinElement) { "Invalid skin element provided, expected SkinElement, got ${value::class.qualifiedName}" } + + buff.writeUtf(value.texture.toString()) + buff.writeFloat(value.x) + buff.writeFloat(value.y) + buff.writeFloat(value.width) + buff.writeFloat(value.height) + buff.writeFloat(value.imageWidth) + buff.writeFloat(value.imageHeight) + buff.writeEnum(value.winding) + } + + override fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { + val texture = ResourceLocation(buff.readUtf()) + val x = buff.readFloat() + val y = buff.readFloat() + val width = buff.readFloat() + val height = buff.readFloat() + val imageWidth = buff.readFloat() + val imageHeight = buff.readFloat() + val winding = buff.readEnum(UVWindingOrder::class.java) + + return SkinElement(texture, x, y, width, height, imageWidth, imageHeight, winding) + } + + override fun fromJson(element: JsonObject): AbstractSkinElement { + val texture = element["texture"]?.asString ?: throw JsonSyntaxException("Missing texture element") + val x = element["x"]?.asFloat ?: throw JsonSyntaxException("Missing x element") + val y = element["y"]?.asFloat ?: throw JsonSyntaxException("Missing y element") + val width = element["width"]?.asFloat ?: throw JsonSyntaxException("Missing width element") + val height = element["height"]?.asFloat ?: throw JsonSyntaxException("Missing height element") + val imageWidth = element["imageWidth"]?.asFloat ?: throw JsonSyntaxException("Missing imageWidth element") + val imageHeight = element["imageHeight"]?.asFloat ?: throw JsonSyntaxException("Missing imageHeight element") + val winding = element["winding"]?.asString ?: throw JsonSyntaxException("Missing winding element") + + return SkinElement(ResourceLocation.tryParse(texture) ?: throw JsonSyntaxException("Invalid resource location: $texture"), x, y, width, height, imageWidth, imageHeight, UVWindingOrder.valueOf(winding)) + } + }, + ATLAS { + override fun toJson(value: AbstractSkinElement): JsonObject { + require(value is AtlasSkinElement) { "Invalid skin element provided, expected AtlasSkinElement, got ${value::class.qualifiedName}" } + + return JsonObject().also { + it["location"] = JsonPrimitive(value.location.toString()) + it["width"] = JsonPrimitive(value.width) + it["height"] = JsonPrimitive(value.height) + } + } + + override fun toNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) { + require(value is AtlasSkinElement) { "Invalid skin element provided, expected AtlasSkinElement, got ${value::class.qualifiedName}" } + + buff.writeResourceLocation(value.location) + buff.writeFloat(value.width) + buff.writeFloat(value.height) + } + + override fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { + val location = ResourceLocation(buff.readUtf()) + val width = buff.readFloat() + val height = buff.readFloat() + + return AtlasSkinElement(location, width, height) + } + + override fun fromJson(element: JsonObject): AbstractSkinElement { + val location = element["location"]?.asString ?: throw JsonSyntaxException("Missing location element") + val width = element["width"]?.asFloat ?: throw JsonSyntaxException("Missing width element") + val height = element["height"]?.asFloat ?: throw JsonSyntaxException("Missing height element") + + return AtlasSkinElement(ResourceLocation.tryParse(location) ?: throw JsonSyntaxException("Invalid resource location: $location"), width, height) + } + }, + SUBELEMENT { + override fun toJson(value: AbstractSkinElement): JsonObject { + require(value is SubSkinElement) { "Invalid skin element provided, expected SubSkinElement, got ${value::class.qualifiedName}" } + + return JsonObject().also { + 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) + } + } + + override fun toNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) { + require(value is SubSkinElement) { "Invalid skin element provided, expected SubSkinElement, got ${value::class.qualifiedName}" } + + value.parent.toNetwork(buff) + buff.writeFloat(value.xOffset) + buff.writeFloat(value.yOffset) + buff.writeFloat(value.subWidth) + buff.writeFloat(value.subHeight) + } + + override fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { + val parent = fromNetwork(buff) + val xOffset = buff.readFloat() + val yOffset = buff.readFloat() + val subWidth = buff.readFloat() + val subHeight = buff.readFloat() + + return SubSkinElement(parent, xOffset, yOffset, subWidth, subHeight) + } + + override fun fromJson(element: JsonObject): AbstractSkinElement { + val parent = element["parent"] as? JsonObject ?: throw JsonSyntaxException("Invalid parent") + val xOffset = element["xOffset"].asFloat + val yOffset = element["yOffset"].asFloat + val subWidth = element["subWidth"].asFloat + val subHeight = element["subHeight"].asFloat + + return SubSkinElement(fromJson(parent), xOffset, yOffset, subWidth, subHeight) + } + }; + + protected abstract fun toJson(value: AbstractSkinElement): JsonObject + protected abstract fun toNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) + protected abstract fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement + protected abstract fun fromJson(element: JsonObject): AbstractSkinElement + + fun toActualJson(value: AbstractSkinElement): JsonObject { + return toJson(value).also { + it["type"] = JsonPrimitive(name) + } + } + + fun toActualNetwork(value: AbstractSkinElement, buff: FriendlyByteBuf) { + buff.writeEnum(this) + toNetwork(value, buff) + } + + companion object { + fun fromNetwork(buff: FriendlyByteBuf): AbstractSkinElement { + val type = buff.readEnum(SkinElementType::class.java) + return type.fromNetwork(buff) + } + + fun fromJson(element: JsonObject): AbstractSkinElement { + val type = SkinElementType.valueOf(element["type"].asString) + return type.fromJson(element) + } + } +} 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 new file mode 100644 index 000000000..32d31ed00 --- /dev/null +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/SubSkinElement.kt @@ -0,0 +1,39 @@ +package ru.dbotthepony.mc.otm.client.render + +import net.minecraft.resources.ResourceLocation + +class SubSkinElement( + val parent: AbstractSkinElement, + val xOffset: Float = 0f, + val yOffset: Float = 0f, + val subWidth: Float = parent.width, + val subHeight: Float = parent.height, +) : AbstractSkinElement() { + override val width: Float + get() = subWidth + override val height: Float + get() = subHeight + + 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) + + override val u1: Float + get() = parent.u1 + ((xOffset + subWidth) / parent.width) * (parent.u1 - parent.u0) + override val v1: Float + get() = parent.v1 + ((yOffset + subHeight) / parent.height) * (parent.v1 - parent.v0) + + override val texture: ResourceLocation + get() = parent.texture + + override val type: SkinElementType + get() = SkinElementType.SUBELEMENT +} + +fun AbstractSkinElement.subElement( + x: Float = 0f, + y: Float = 0f, + width: Float = this.width, + height: Float = this.height, +) = SubSkinElement(this, x, y, width, height) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt index 2987d56cc..f4876d786 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/render/WidgetAtlasHolder.kt @@ -28,6 +28,10 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt private set var demandsRebuild = false private set + var demandsRebuildAt = 0L + private set + var dirtyUntil = 0L + private set private var resourceManager by Delegates.notNull() private var profileManager by Delegates.notNull() @@ -38,23 +42,46 @@ class WidgetAtlasHolder private constructor(manager: TextureManager) : TextureAt resourceManager = p_118891_ profileManager = p_118892_ changeset++ + dirtyUntil = System.nanoTime() + 1_000_000_000L return super.prepare(p_118891_, p_118892_) } + override fun apply(p_118894_: TextureAtlas.Preparations, p_118895_: ResourceManager, p_118896_: ProfilerFiller) { + once = true + demandsRebuild = false + resourceManager = p_118895_ + profileManager = p_118896_ + changeset++ + dirtyUntil = System.nanoTime() + 1_000_000_000L + super.apply(p_118894_, p_118895_, p_118896_) + } + + fun rebuildIfRequired(): Boolean { + if (once && demandsRebuild && demandsRebuildAt <= System.nanoTime() && isClientThread()) { + apply(prepare(resourceManager, profileManager), resourceManager, profileManager) + } + + return demandsRebuild + } + + fun speculateIfMinecraftEngineIsDumDum(): Boolean { + return demandsRebuild || dirtyUntil >= System.nanoTime() + } + public override fun getSprite(p_118902_: ResourceLocation): TextureAtlasSprite { if (!once) { throw IllegalStateException("Trying to get sprite too early") } - if (demandsRebuild && isClientThread()) { - apply(prepare(resourceManager, profileManager), resourceManager, profileManager) - } - + rebuildIfRequired() return super.getSprite(p_118902_) } fun queueRebuild() { - demandsRebuild = true + if (!demandsRebuild) { + demandsRebuild = true + demandsRebuildAt = System.nanoTime() + 1_000_000_000L + } } override fun getResourcesToLoad(): Stream { diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ScrollBarConstants.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ScrollBarConstants.kt index 764a624df..4221caaa0 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ScrollBarConstants.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/ScrollBarConstants.kt @@ -5,6 +5,7 @@ import ru.dbotthepony.mc.otm.OverdriveThatMatters import ru.dbotthepony.mc.otm.client.render.AtlasSkinElement import ru.dbotthepony.mc.otm.client.render.WidgetLocation import ru.dbotthepony.mc.otm.client.render.element +import ru.dbotthepony.mc.otm.client.render.subElement object ScrollBarConstants { const val WIDTH = 14f @@ -12,21 +13,24 @@ object ScrollBarConstants { const val TEXTURE_WIDTH = 22f const val TEXTURE_HEIGHT = 68f - val TOP = WidgetLocation.SCROLL.element(0f, 45f, 14f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT) - val BODY = WidgetLocation.SCROLL.element(0f, 46f, 14f, 6f, TEXTURE_WIDTH, TEXTURE_HEIGHT) - val BOTTOM = WidgetLocation.SCROLL.element(0f, 51f, 14f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT) + val BACKGROUND = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/background"), 14f, 8f) + val BACKGROUND_SLIM = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/background"), 8f, 8f) - val BUTTON = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/idle")) - val BUTTON_HOVER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/hovered")) - val BUTTON_PRESS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/pressed")) - val BUTTON_DISABLED = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/disabled")) + val TOP = BACKGROUND.subElement(height = 2f) + val BODY = BACKGROUND.subElement(x = 2f, height = 6f) + val BOTTOM = BACKGROUND.subElement(x = 7f, height = 2f) - val SLIM_TOP = WidgetLocation.SCROLL.element(14f, 45f, 8f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT) - val SLIM_BODY = WidgetLocation.SCROLL.element(14f, 46f, 8f, 6f, TEXTURE_WIDTH, TEXTURE_HEIGHT) - val SLIM_BOTTOM = WidgetLocation.SCROLL.element(14f, 51f, 8f, 2f, TEXTURE_WIDTH, TEXTURE_HEIGHT) + val BUTTON = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/idle"), 12f, 15f) + val BUTTON_HOVER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/hovered"), 12f, 15f) + val BUTTON_PRESS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/pressed"), 12f, 15f) + val BUTTON_DISABLED = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar/disabled"), 12f, 15f) - val SLIM_BUTTON = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/idle")) - val SLIM_BUTTON_HOVER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/hovered")) - val SLIM_BUTTON_PRESS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/pressed")) - val SLIM_BUTTON_DISABLED = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/disabled")) + val SLIM_TOP = BACKGROUND_SLIM.subElement(height = 2f) + val SLIM_BODY = BACKGROUND_SLIM.subElement(x = 2f, height = 6f) + val SLIM_BOTTOM = BACKGROUND_SLIM.subElement(x = 7f, height = 2f) + + val SLIM_BUTTON = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/idle"), 6f, 15f) + val SLIM_BUTTON_HOVER = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/hovered"), 6f, 15f) + val SLIM_BUTTON_PRESS = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/pressed"), 6f, 15f) + val SLIM_BUTTON_DISABLED = AtlasSkinElement(ResourceLocation(OverdriveThatMatters.MOD_ID, "widgets/scrollbar_slim/disabled"), 6f, 15f) }