From ced4a2d980e4b993f9edc9e425f75e5dde1f78c4 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 4 May 2024 21:21:16 +0100 Subject: [PATCH] Implement a armour renderplugin api --- .../model/armour/ArmourRendererPlugin.java | 65 ++++++++ .../client/model/armour/ArmourTexture.java | 39 +++++ .../model/armour/ArmourTextureLookup.java | 12 ++ .../model/armour/ArmourTextureResolver.java | 35 +---- .../render/entity/PonyStandRenderer.java | 10 +- .../render/entity/feature/ArmourFeature.java | 139 +++++++++--------- 6 files changed, 189 insertions(+), 111 deletions(-) create mode 100644 src/main/java/com/minelittlepony/client/model/armour/ArmourRendererPlugin.java create mode 100644 src/main/java/com/minelittlepony/client/model/armour/ArmourTexture.java create mode 100644 src/main/java/com/minelittlepony/client/model/armour/ArmourTextureLookup.java diff --git a/src/main/java/com/minelittlepony/client/model/armour/ArmourRendererPlugin.java b/src/main/java/com/minelittlepony/client/model/armour/ArmourRendererPlugin.java new file mode 100644 index 00000000..8dc721c0 --- /dev/null +++ b/src/main/java/com/minelittlepony/client/model/armour/ArmourRendererPlugin.java @@ -0,0 +1,65 @@ +package com.minelittlepony.client.model.armour; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.*; +import net.minecraft.client.texture.Sprite; +import net.minecraft.client.texture.SpriteAtlasTexture; +import net.minecraft.component.type.DyedColorComponent; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ArmorMaterial; +import net.minecraft.item.ItemStack; +import net.minecraft.item.trim.ArmorTrim; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.registry.tag.ItemTags; +import net.minecraft.util.Colors; +import net.minecraft.util.Identifier; + +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.atomic.AtomicReference; + +public interface ArmourRendererPlugin { + AtomicReference INSTANCE = new AtomicReference<>(new ArmourRendererPlugin() {}); + + static void register(ArmourRendererPlugin plugin) { + INSTANCE.set(plugin); + } + + default ArmourTextureLookup getTextureLookup() { + return ArmourTextureResolver.INSTANCE; + } + + default ItemStack[] getArmorStacks(LivingEntity entity, EquipmentSlot armorSlot, ArmourLayer layer) { + return new ItemStack[] { entity.getEquippedStack(armorSlot) }; + } + + default boolean shouldRenderGlint(EquipmentSlot slot, ItemStack stack) { + return stack.hasGlint(); + } + + default int getDyeColor(EquipmentSlot slot, ItemStack stack) { + return stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, -6265536) : Colors.WHITE; + } + + @Nullable + default VertexConsumer getTrimConsumer(EquipmentSlot slot, VertexConsumerProvider provider, RegistryEntry material, ArmorTrim trim, ArmourLayer layer) { + SpriteAtlasTexture armorTrimsAtlas = MinecraftClient.getInstance().getBakedModelManager().getAtlas(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE); + Sprite sprite = armorTrimsAtlas.getSprite( + layer == ArmourLayer.INNER ? trim.getLeggingsModelId(material) : trim.getGenericModelId(material) + ); + return sprite.getTextureSpecificVertexConsumer( + provider.getBuffer(TexturedRenderLayers.getArmorTrims(trim.getPattern().value().decal())) + ); + } + + @Nullable + default VertexConsumer getArmourConsumer(EquipmentSlot slot, VertexConsumerProvider provider, Identifier texture, ArmourLayer layer) { + return provider.getBuffer(RenderLayer.getArmorCutoutNoCull(texture)); + } + + @Nullable + default VertexConsumer getGlintConsumer(EquipmentSlot slot, VertexConsumerProvider provider, ArmourLayer layer) { + return provider.getBuffer(RenderLayer.getArmorEntityGlint()); + } +} diff --git a/src/main/java/com/minelittlepony/client/model/armour/ArmourTexture.java b/src/main/java/com/minelittlepony/client/model/armour/ArmourTexture.java new file mode 100644 index 00000000..32b91abe --- /dev/null +++ b/src/main/java/com/minelittlepony/client/model/armour/ArmourTexture.java @@ -0,0 +1,39 @@ +package com.minelittlepony.client.model.armour; + +import net.minecraft.client.texture.TextureManager; +import net.minecraft.util.Identifier; + +import com.google.common.collect.Interner; +import com.google.common.collect.Interners; +import com.minelittlepony.api.config.PonyConfig; +import com.minelittlepony.util.ResourceUtil; + +import java.util.stream.Stream; + +public record ArmourTexture(Identifier texture, ArmourVariant variant) { + private static final Interner INTERNER = Interners.newWeakInterner(); + public static final ArmourTexture UNKNOWN = legacy(TextureManager.MISSING_IDENTIFIER); + + public boolean validate() { + return texture != TextureManager.MISSING_IDENTIFIER && ResourceUtil.textureExists(texture); + } + + public static ArmourTexture legacy(Identifier texture) { + return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.LEGACY)); + } + + public static ArmourTexture modern(Identifier texture) { + return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.NORMAL)); + } + + public Stream named() { + return Stream.of(legacy(texture().withPath(p -> p.replace("1", "inner").replace("2", "outer"))), this); + } + + public Stream ponify() { + if (!PonyConfig.getInstance().disablePonifiedArmour.get()) { + return Stream.of(this, modern(ResourceUtil.ponify(texture()))); + } + return Stream.of(this); + } +} diff --git a/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureLookup.java b/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureLookup.java new file mode 100644 index 00000000..9364b39e --- /dev/null +++ b/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureLookup.java @@ -0,0 +1,12 @@ +package com.minelittlepony.client.model.armour; + +import net.minecraft.item.ArmorMaterial; +import net.minecraft.item.ItemStack; + +import java.util.List; + +public interface ArmourTextureLookup { + ArmourTexture getTexture(ItemStack stack, ArmourLayer layer, ArmorMaterial.Layer armorLayer); + + List getArmorLayers(ItemStack stack, int dyeColor); +} diff --git a/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureResolver.java b/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureResolver.java index 1fb87572..491fe934 100644 --- a/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureResolver.java +++ b/src/main/java/com/minelittlepony/client/model/armour/ArmourTextureResolver.java @@ -1,7 +1,6 @@ package com.minelittlepony.client.model.armour; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; -import net.minecraft.client.texture.TextureManager; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.CustomModelDataComponent; import net.minecraft.item.*; @@ -14,9 +13,7 @@ import net.minecraft.util.profiler.Profiler; import com.google.common.cache.*; import com.google.common.collect.Interner; import com.google.common.collect.Interners; -import com.minelittlepony.api.config.PonyConfig; import com.minelittlepony.client.MineLittlePony; -import com.minelittlepony.util.ResourceUtil; import java.util.*; import java.util.concurrent.*; @@ -38,7 +35,7 @@ import java.util.stream.Stream; * - the "minecraft" namespace is always replaced with "minelittlepony" *

*/ -public class ArmourTextureResolver implements IdentifiableResourceReloadListener { +public class ArmourTextureResolver implements ArmourTextureLookup, IdentifiableResourceReloadListener { public static final Identifier ID = MineLittlePony.id("armor_textures"); public static final ArmourTextureResolver INSTANCE = new ArmourTextureResolver(); @@ -102,10 +99,12 @@ public class ArmourTextureResolver implements IdentifiableResourceReloadListener return ID; } + @Override public ArmourTexture getTexture(ItemStack stack, ArmourLayer layer, ArmorMaterial.Layer armorLayer) { return layerCache.getUnchecked(new ArmourParameters(layer, armorLayer, getCustom(stack))); } + @Override public List getArmorLayers(ItemStack stack, int dyeColor) { if (stack.getItem() instanceof ArmorItem armor) { return armor.getMaterial().value().layers(); @@ -121,32 +120,4 @@ public class ArmourTextureResolver implements IdentifiableResourceReloadListener private record ArmourParameters(ArmourLayer layer, ArmorMaterial.Layer material, int customModelId) { } - - public record ArmourTexture(Identifier texture, ArmourVariant variant) { - private static final Interner INTERNER = Interners.newWeakInterner(); - public static final ArmourTexture UNKNOWN = legacy(TextureManager.MISSING_IDENTIFIER); - - public boolean validate() { - return texture != TextureManager.MISSING_IDENTIFIER && ResourceUtil.textureExists(texture); - } - - public static ArmourTexture legacy(Identifier texture) { - return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.LEGACY)); - } - - public static ArmourTexture modern(Identifier texture) { - return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.NORMAL)); - } - - public Stream named() { - return Stream.of(legacy(texture().withPath(p -> p.replace("1", "inner").replace("2", "outer"))), this); - } - - public Stream ponify() { - if (!PonyConfig.getInstance().disablePonifiedArmour.get()) { - return Stream.of(this, modern(ResourceUtil.ponify(texture()))); - } - return Stream.of(this); - } - } } diff --git a/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java index da4615f0..d1e071fe 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java @@ -7,7 +7,6 @@ import net.minecraft.client.render.entity.feature.*; import net.minecraft.client.render.entity.model.ArmorStandArmorEntityModel; import net.minecraft.client.render.entity.model.EntityModelLayers; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.util.math.Vec3d; @@ -15,7 +14,6 @@ import com.minelittlepony.api.model.Models; import com.minelittlepony.api.pony.PonyData; import com.minelittlepony.api.pony.meta.Race; import com.minelittlepony.client.model.ModelType; -import com.minelittlepony.client.model.armour.ArmourLayer; import com.minelittlepony.client.model.entity.PonyArmourStandModel; import com.minelittlepony.client.model.entity.race.EarthPonyModel; import com.minelittlepony.client.render.entity.feature.ArmourFeature; @@ -82,13 +80,7 @@ public class PonyStandRenderer extends ArmorStandEntityRenderer { pony.body().animateModel(entity, limbDistance, limbAngle, tickDelta); pony.body().setAngles(entity, limbDistance, limbAngle, age, headYaw, headPitch); PonyStandRenderer.this.pony.applyAnglesTo(pony.body()); - - for (EquipmentSlot i : EquipmentSlot.values()) { - if (i.getType() == EquipmentSlot.Type.ARMOR) { - ArmourFeature.renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.INNER); - ArmourFeature.renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.OUTER); - } - } + ArmourFeature.renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch); } else { super.render(stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch); } diff --git a/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java b/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java index c69cb902..ab072639 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java +++ b/src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java @@ -3,114 +3,113 @@ package com.minelittlepony.client.render.entity.feature; import com.minelittlepony.api.model.Models; import com.minelittlepony.api.model.PonyModel; import com.minelittlepony.client.model.armour.*; -import com.minelittlepony.client.model.armour.ArmourTextureResolver.ArmourTexture; import com.minelittlepony.client.render.PonyRenderContext; import com.minelittlepony.common.util.Color; import java.util.*; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.*; import net.minecraft.client.render.entity.model.*; import net.minecraft.client.render.model.BakedModelManager; -import net.minecraft.client.texture.Sprite; -import net.minecraft.client.texture.SpriteAtlasTexture; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.DyedColorComponent; import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.LivingEntity; import net.minecraft.item.*; import net.minecraft.item.trim.ArmorTrim; -import net.minecraft.registry.entry.RegistryEntry; -import net.minecraft.registry.tag.ItemTags; import net.minecraft.util.Colors; public class ArmourFeature & PonyModel> extends AbstractPonyFeature { - public ArmourFeature(PonyRenderContext context, BakedModelManager bakery) { super(context); } @Override - public void render(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) { - Models pony = getModelWrapper(); - - for (EquipmentSlot i : EquipmentSlot.values()) { - if (i.getType() == EquipmentSlot.Type.ARMOR) { - renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.INNER); - renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.OUTER); - } - } + public void render(MatrixStack matrices, VertexConsumerProvider provider, int light, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) { + renderArmor(getModelWrapper(), matrices, provider, light, entity, limbDistance, limbAngle, age, headYaw, headPitch); } public static > void renderArmor( Models> pony, MatrixStack matrices, VertexConsumerProvider provider, int light, T entity, float limbDistance, float limbAngle, - float age, float headYaw, float headPitch, - EquipmentSlot armorSlot, ArmourLayer layer) { + float age, float headYaw, float headPitch) { + ArmourRendererPlugin plugin = ArmourRendererPlugin.INSTANCE.get(); - ItemStack stack = entity.getEquippedStack(armorSlot); - - if (stack.isEmpty()) { - return; + for (EquipmentSlot i : EquipmentSlot.values()) { + if (i.getType() == EquipmentSlot.Type.ARMOR) { + renderArmor(pony, matrices, provider, light, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.INNER, plugin); + renderArmor(pony, matrices, provider, light, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.OUTER, plugin); + } } + } - boolean glint = stack.hasGlint(); + private static > void renderArmor( + Models> pony, MatrixStack matrices, + VertexConsumerProvider provider, int light, T entity, + float limbDistance, float limbAngle, + float age, float headYaw, float headPitch, + EquipmentSlot armorSlot, ArmourLayer layer, ArmourRendererPlugin plugin) { - int color = stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, -6265536) : Colors.WHITE; - - Set> models = glint ? new HashSet<>() : null; - - for (ArmorMaterial.Layer armorLayer : ArmourTextureResolver.INSTANCE.getArmorLayers(stack, color)) { - ArmourTexture layerTexture = ArmourTextureResolver.INSTANCE.getTexture(stack, layer, armorLayer); - - if (layerTexture == ArmourTexture.UNKNOWN) { + for (ItemStack stack : plugin.getArmorStacks(entity, armorSlot, layer)) { + if (stack.isEmpty()) { continue; } - var m = pony.getArmourModel(stack, layer, layerTexture.variant()).orElse(null); - if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) { - float red = 1; - float green = 1; - float blue = 1; - if (armorLayer.isDyeable() && color != Colors.WHITE) { - red = Color.r(color); - green = Color.g(color); - blue = Color.b(color); + boolean glint = plugin.shouldRenderGlint(armorSlot, stack); + int color = plugin.getDyeColor(armorSlot, stack); + + Set> models = glint ? new HashSet<>() : null; + + ArmourTextureLookup textureLookup = plugin.getTextureLookup(); + + for (ArmorMaterial.Layer armorLayer : textureLookup.getArmorLayers(stack, color)) { + ArmourTexture layerTexture = textureLookup.getTexture(stack, layer, armorLayer); + + if (layerTexture == ArmourTexture.UNKNOWN) { + continue; } - m.render(matrices, provider.getBuffer(RenderLayer.getArmorCutoutNoCull(layerTexture.texture())), light, OverlayTexture.DEFAULT_UV, red, green, blue, 1); - if (glint) { - models.add(m); + + var m = pony.getArmourModel(stack, layer, layerTexture.variant()).orElse(null); + if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) { + VertexConsumer armorConsumer = plugin.getArmourConsumer(armorSlot, provider, layerTexture.texture(), layer); + if (armorConsumer != null) { + float red = 1; + float green = 1; + float blue = 1; + if (armorLayer.isDyeable() && color != Colors.WHITE) { + red = Color.r(color); + green = Color.g(color); + blue = Color.b(color); + } + m.render(matrices, armorConsumer, light, OverlayTexture.DEFAULT_UV, red, green, blue, 1); + } + if (glint) { + models.add(m); + } + } + } + + ArmorTrim trim = stack.get(DataComponentTypes.TRIM); + + if (trim != null && stack.getItem() instanceof ArmorItem armor) { + var m = pony.getArmourModel(stack, layer, ArmourVariant.TRIM).orElse(null); + if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) { + VertexConsumer trimConsumer = plugin.getTrimConsumer(armorSlot, provider, armor.getMaterial(), trim, layer); + if (trimConsumer != null) { + m.render(matrices, trimConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + } + } + } + + if (glint) { + VertexConsumer glintConsumer = plugin.getGlintConsumer(armorSlot, provider, layer); + if (glintConsumer != null) { + for (var m : models) { + m.render(matrices, glintConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); + } } } } - - ArmorTrim trim = stack.get(DataComponentTypes.TRIM); - - if (trim != null && stack.getItem() instanceof ArmorItem armor) { - var m = pony.getArmourModel(stack, layer, ArmourVariant.TRIM).orElse(null); - if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) { - m.render(matrices, getTrimConsumer(provider, armor.getMaterial(), trim, layer), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); - } - } - - if (glint) { - VertexConsumer glintConsumer = provider.getBuffer(RenderLayer.getArmorEntityGlint()); - for (var m : models) { - m.render(matrices, glintConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); - } - } - } - - private static VertexConsumer getTrimConsumer(VertexConsumerProvider provider, RegistryEntry material, ArmorTrim trim, ArmourLayer layer) { - SpriteAtlasTexture armorTrimsAtlas = MinecraftClient.getInstance().getBakedModelManager().getAtlas(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE); - Sprite sprite = armorTrimsAtlas.getSprite( - layer == ArmourLayer.INNER ? trim.getLeggingsModelId(material) : trim.getGenericModelId(material) - ); - return sprite.getTextureSpecificVertexConsumer( - provider.getBuffer(TexturedRenderLayers.getArmorTrims(trim.getPattern().value().decal())) - ); } }