diff --git a/README.md b/README.md index 1c4501e..9f91c52 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,41 @@ -# ArmorModelPredicate - Adds the armor model predicates to Minecraft using Fabric Mixins. +Armor Render Lib +
+ +[![GitHub license](https://img.shields.io/github/license/CleverNucleus/ArmorRenderLib?style=flat-square)](https://github.com/CleverNucleus/ArmorRenderLib/blob/main/LICENSE) +[![GitHub stars](https://img.shields.io/github/stars/CleverNucleus/ArmorRenderLib?style=flat-square)](https://github.com/CleverNucleus/ArmorRenderLib/stargazers) +[![GitHub forks](https://img.shields.io/github/forks/CleverNucleus/ArmorRenderLib?style=flat-square)](https://github.com/CleverNucleus/ArmorRenderLib/network) +[![GitHub issues](https://img.shields.io/github/issues/CleverNucleus/ArmorRenderLib?style=flat-square)](https://github.com/CleverNucleus/ArmorRenderLib/issues) + +Armor Render Lib is a lightweight extension library to Fabric API's fabric-rendering-v1 [ArmorRenderer](https://github.com/FabricMC/fabric/tree/1.18.2/fabric-rendering-v1). Since the Fabric API implementation is quite abstract, a more targeted implementation is needed for some specific use cases. These use cases are present across more than one of my mods, so in the spirit of code reuse this library was created. It is robust, using only a few very targeted mixins and is fully compatible with Cosmetic Armor and GeckoLib. + +### Use + +Armor Render Lib adds armor render layers. These are objects containing a dynamic texture location, color and glint boolean that render armor for an item (or items). They are roughly equivalent to Fabric API's [ArmorRenderer#renderPart](https://github.com/FabricMC/fabric/blob/f14603e8624d4cb192846321c429cc00c9ef6f55/fabric-rendering-v1/src/main/java/net/fabricmc/fabric/api/client/rendering/v1/ArmorRenderer.java#L69), but the texture, color and glint can be dynamically specified based on the `ItemStack`, `LivingEntity` and `EquipmentSlot`. + +They should be registered like so: + +```java +public class ExampleMod implements ClientModInitializer { + private static ArmorRenderProvider render(ItemStack stack, LivingEntity entity, EquipmentSlot slot) { + // Dynamic texture path + String texture = "examplemod:textures/model/armor/example_chestplate.png"; + + // Dynamic color + int color = 0xFF00FF; + + return data -> data.accept(texture, color, stack.hasGlint()); + } + + @Override + public void onInitializeClient() { + ArmorRenderLib.register(ExampleMod::render, Items.IRON_CHESTPLATE); + } +} +``` + +### Notes + +- An item can have multiple different armor render layers registered to it. +- Multiple items can have the same armor render layer registered to them. +- Registering a render layer to a vanilla item (or other armor item) overrides the default armor rendering. + - This means that the texture, color and/or enchantment glint of vanilla armor can be modified. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8920490..ce02388 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'fabric-loom' version '0.11-SNAPSHOT' + id 'fabric-loom' version '0.12-SNAPSHOT' id 'maven-publish' } @@ -18,6 +18,7 @@ dependencies { minecraft "com.mojang:minecraft:${project.minecraft_version}" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" } processResources { @@ -48,8 +49,8 @@ publishing { from components.java } } - + repositories { - + } } diff --git a/gradle.properties b/gradle.properties index 614b228..5218fee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,16 +1,11 @@ -# Done to increase the memory available to gradle. org.gradle.jvmargs=-Xmx1G -# Fabric Properties - # check these on https://fabricmc.net/develop - minecraft_version=1.18.1 - yarn_mappings=1.18.1+build.22 - loader_version=0.12.12 +minecraft_version=1.18.2 +yarn_mappings=1.18.2+build.3 +loader_version=0.14.6 -# Mod Properties - mod_version = 0.1.0 - maven_group = com.github.clevernucleus - archives_base_name = armor-model-predicate +mod_version = 0.1.1 +maven_group = com.github.clevernucleus +archives_base_name = armorrenderlib -# Dependencies - +fabric_version=0.55.1+1.18.2 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e6e589..41dfb87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/img/logo.png b/img/logo.png new file mode 100644 index 0000000..d2fd1f8 Binary files /dev/null and b/img/logo.png differ diff --git a/specification.zip b/specification.zip deleted file mode 100644 index 0fc5029..0000000 Binary files a/specification.zip and /dev/null differ diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/api/ArmorModelPredicateProviderRegistry.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/api/ArmorModelPredicateProviderRegistry.java deleted file mode 100644 index 14b1be5..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/api/ArmorModelPredicateProviderRegistry.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.api; - -import java.util.ArrayList; -import java.util.List; - -import com.github.clevernucleus.armormodelpredicate.impl.ArmorModelPredicate; -import com.github.clevernucleus.armormodelpredicate.impl.InvertedFunction; -import com.github.clevernucleus.armormodelpredicate.impl.InvertedProvider; - -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; -import net.minecraft.item.DyeableArmorItem; -import net.minecraft.util.Identifier; - -/** - * Register custom armor texture/colors with this class. - * - * @author CleverNucleus - * - */ -@Environment(EnvType.CLIENT) -public final class ArmorModelPredicateProviderRegistry { - private static final List PROVIDERS = new ArrayList(); - - /** - * This method can be used to register armor colors and textures. An example is provided below: - *

- * - * ArmorModelPredicateProviderRegistry.register((itemStack, entity) -> { - * return itemStack.getItem() == Items.IRON_CHESTPLATE; - * }, (itemStack, entity, legs) -> { - * Identifier texture = new Identifier("example_mod:textures/models/armor/custom_iron_layer_" + (legs ? 2 : 1) + ".png"); - * return provider -> provider.provide(texture, 0xFF0000); - * }); - * - * - * @param predicate - * @param provider - */ - public static void register(final ArmorModelPredicate predicate, final InvertedProvider provider) { - PROVIDERS.add(wrapper -> wrapper.apply(predicate, provider)); - } - - public static List providers() { - return PROVIDERS; - } - - static { - register((itemStack, entity) -> itemStack.getItem() instanceof DyeableArmorItem, (itemStack, entity, legs) -> { - Identifier texture = new Identifier("minecraft:textures/models/armor/leather_layer_" + (legs ? 2 : 1) + ".png"); - int color = ((DyeableArmorItem)itemStack.getItem()).getColor(itemStack); - return provider -> provider.provide(texture, color); - }); - register((itemStack, entity) -> itemStack.getItem() instanceof DyeableArmorItem, (itemStack, entity, legs) -> { - Identifier texture = new Identifier("minecraft:textures/models/armor/leather_layer_" + (legs ? 2 : 1) + "_overlay.png"); - return provider -> provider.provide(texture, 16777215); - }); - } -} diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/ArmorModelPredicate.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/ArmorModelPredicate.java deleted file mode 100644 index 6e82167..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/ArmorModelPredicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.impl; - -import net.minecraft.entity.LivingEntity; -import net.minecraft.item.ItemStack; - -@FunctionalInterface -public interface ArmorModelPredicate { - boolean test(final ItemStack itemStack, final LivingEntity entity); -} diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/ArmorModelProvider.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/ArmorModelProvider.java deleted file mode 100644 index 332074e..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/ArmorModelProvider.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.impl; - -import net.minecraft.util.Identifier; - -@FunctionalInterface -public interface ArmorModelProvider { - void provide(final Identifier texture, final int color); -} diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/InvertedFunction.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/InvertedFunction.java deleted file mode 100644 index b1ee37c..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/InvertedFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.impl; - -@FunctionalInterface -public interface InvertedFunction { - boolean invert(final WrapperFunction function); -} diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/InvertedProvider.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/InvertedProvider.java deleted file mode 100644 index f0c76a6..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/InvertedProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.impl; - -import java.util.function.Consumer; - -import net.minecraft.entity.LivingEntity; -import net.minecraft.item.ItemStack; - -@FunctionalInterface -public interface InvertedProvider { - Consumer invert(final ItemStack itemStack, final LivingEntity entity, final boolean legs); -} diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/WrapperFunction.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/WrapperFunction.java deleted file mode 100644 index b751c05..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/impl/WrapperFunction.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.impl; - -@FunctionalInterface -public interface WrapperFunction { - boolean apply(final ArmorModelPredicate predicate, final InvertedProvider prodiver); -} diff --git a/src/main/java/com/github/clevernucleus/armormodelpredicate/mixin/ArmorFeatureRendererMixin.java b/src/main/java/com/github/clevernucleus/armormodelpredicate/mixin/ArmorFeatureRendererMixin.java deleted file mode 100644 index 87c0df3..0000000 --- a/src/main/java/com/github/clevernucleus/armormodelpredicate/mixin/ArmorFeatureRendererMixin.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.github.clevernucleus.armormodelpredicate.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -import org.spongepowered.asm.mixin.injection.callback.LocalCapture; - -import com.github.clevernucleus.armormodelpredicate.api.ArmorModelPredicateProviderRegistry; - -import net.minecraft.client.render.OverlayTexture; -import net.minecraft.client.render.RenderLayer; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; -import net.minecraft.client.render.entity.feature.FeatureRenderer; -import net.minecraft.client.render.entity.feature.FeatureRendererContext; -import net.minecraft.client.render.entity.model.AnimalModel; -import net.minecraft.client.render.entity.model.BipedEntityModel; -import net.minecraft.client.render.item.ItemRenderer; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.entity.EquipmentSlot; -import net.minecraft.entity.LivingEntity; -import net.minecraft.item.ArmorItem; -import net.minecraft.item.ItemStack; -import net.minecraft.util.Identifier; - -@Mixin(ArmorFeatureRenderer.class) -abstract class ArmorFeatureRendererMixin, A extends BipedEntityModel> extends FeatureRenderer { - private ArmorFeatureRendererMixin(FeatureRendererContext context, A leggingsModel, A bodyModel) { super(context); } - - private void amp_renderArmorParts(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, Identifier texture, boolean usesSecondLayer, A model, float red, float green, float blue) { - VertexConsumer vertexConsumer = ItemRenderer.getArmorGlintConsumer(vertexConsumers, RenderLayer.getArmorCutoutNoCull(texture), false, usesSecondLayer); - ((AnimalModel)model).render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, red, green, blue, 1.0F); - } - - @Inject(method = "renderArmor", at = @At(value = "INVOKE", target = "Lnet/minecraft/item/ItemStack;hasGlint()Z", shift = At.Shift.AFTER), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true) - private void onRenderArmor(MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, EquipmentSlot armorSlot, int light, A model, CallbackInfo info, ItemStack itemStack, ArmorItem armorItem, boolean bl) { - boolean bl2 = itemStack.hasGlint(); - boolean check = true; - - for(var function : ArmorModelPredicateProviderRegistry.providers()) { - if(function.invert((predicate, provider) -> { - if(predicate.test(itemStack, entity)) { - provider.invert(itemStack, entity, bl).accept((texture, color) -> { - float r = (float)(color >> 16 & 0xFF) / 255.0F; - float g = (float)(color >> 8 & 0xFF) / 255.0F; - float b = (float)(color & 0xFF) / 255.0F; - this.amp_renderArmorParts(matrices, vertexConsumers, light, texture, bl2, model, r, g, b); - }); - - return true; - } else { - return false; - } - })) { - check = false; - } - } - - if(check) { - this.amp_renderArmorParts(matrices, vertexConsumers, light, new Identifier("minecraft:textures/models/armor/" + armorItem.getMaterial().getName() + "_layer_" + (bl ? 2 : 1) + ".png"), bl2, model, 1.0F, 1.0F, 1.0F); - } - - info.cancel(); - } -} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderData.java b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderData.java new file mode 100644 index 0000000..61942a7 --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderData.java @@ -0,0 +1,13 @@ +package com.github.clevernucleus.armorrenderlib.api; + +@FunctionalInterface +public interface ArmorRenderData { + + /** + * + * @param texturePath + * @param color + * @param hasGlint + */ + void accept(final String texturePath, final int color, final boolean hasGlint); +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderLayer.java b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderLayer.java new file mode 100644 index 0000000..9e0e9a5 --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderLayer.java @@ -0,0 +1,18 @@ +package com.github.clevernucleus.armorrenderlib.api; + +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ItemStack; + +@FunctionalInterface +public interface ArmorRenderLayer { + + /** + * + * @param itemStack + * @param livingEntity + * @param slot + * @return + */ + ArmorRenderProvider render(final ItemStack itemStack, final LivingEntity livingEntity, final EquipmentSlot slot); +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderLib.java b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderLib.java new file mode 100644 index 0000000..a19092b --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderLib.java @@ -0,0 +1,26 @@ +package com.github.clevernucleus.armorrenderlib.api; + +import net.minecraft.item.ItemConvertible; + +/** + * Use this to register armor render layers! + * + * @author CleverNucleus + * + */ +public interface ArmorRenderLib { + /** + *

+ * Registers an armor render layer for the input item(s). The same armor render layer can be registered for multiple items. The same item(s) can have multiple armor render layers. The armor render layer specifies what armor texture should be used, what color should be applied (if any) and whether it has a glint. + *

+ *

+ * This is an extension of {@link net.fabricmc.fabric.api.client.rendering.v1.ArmorRenderer#render(net.minecraft.client.util.math.MatrixStack, net.minecraft.client.render.VertexConsumerProvider, net.minecraft.item.ItemStack, net.minecraft.entity.LivingEntity, net.minecraft.entity.EquipmentSlot, int, net.minecraft.client.render.entity.model.BipedEntityModel)}. + *

+ * + * @param layer + * @param items + */ + static void register(ArmorRenderLayer layer, ItemConvertible ... items) { + com.github.clevernucleus.armorrenderlib.impl.ArmorRenderLibImpl.register(layer, items); + } +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderProvider.java b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderProvider.java new file mode 100644 index 0000000..9e07beb --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/api/ArmorRenderProvider.java @@ -0,0 +1,11 @@ +package com.github.clevernucleus.armorrenderlib.api; + +@FunctionalInterface +public interface ArmorRenderProvider { + + /** + * + * @param data + */ + void from(final ArmorRenderData data); +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/impl/ArmorRenderLibImpl.java b/src/main/java/com/github/clevernucleus/armorrenderlib/impl/ArmorRenderLibImpl.java new file mode 100644 index 0000000..6b5d860 --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/impl/ArmorRenderLibImpl.java @@ -0,0 +1,64 @@ +package com.github.clevernucleus.armorrenderlib.impl; + +import com.github.clevernucleus.armorrenderlib.api.ArmorRenderLayer; +import com.github.clevernucleus.armorrenderlib.mixin.ArmorFeatureRendererInvoker; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Multimap; + +import net.fabricmc.fabric.api.client.rendering.v1.ArmorRenderer; +import net.fabricmc.fabric.impl.client.rendering.ArmorRendererRegistryImpl; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.OverlayTexture; +import net.minecraft.client.render.RenderLayer; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.ArmorItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemConvertible; +import net.minecraft.item.ItemStack; + +public final class ArmorRenderLibImpl { + private static final Multimap RENDERERS = ArrayListMultimap.create(); + + @SuppressWarnings("unchecked") + private static , A extends BipedEntityModel> void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, ItemStack stack, LivingEntity entity, EquipmentSlot slot, int light, BipedEntityModel contextModel) { + if(!(stack.getItem() instanceof ArmorItem)) return; + ArmorItem item = (ArmorItem)stack.getItem(); + + if(item.getSlotType() != slot) return; + var entityRenderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity); + ArmorFeatureRenderer armorFeatureRenderer = (ArmorFeatureRenderer)((FeatureRendererAccessor)entityRenderer).getFeatureRenderer(ArmorFeatureRenderer.class); + A model = ((ArmorFeatureRendererInvoker)armorFeatureRenderer).invokeGetArmor(slot); + contextModel.setAttributes((BipedEntityModel)model); + ((ArmorFeatureRendererInvoker)armorFeatureRenderer).invokeSetVisible(model, slot); + + for(ArmorRenderLayer layer : RENDERERS.get(item)) { + layer.render(stack, entity, slot).from((path, color, glint) -> { + float red = (float)(color >> 16 & 0xFF) / 255.0F; + float green = (float)(color >> 8 & 0xFF) / 255.0F; + float blue = (float)(color & 0xFF) / 255.0F; + + VertexConsumer vertexConsumer = ItemRenderer.getArmorGlintConsumer(vertexConsumers, RenderLayer.getArmorCutoutNoCull(((ArmorTextureCache)armorFeatureRenderer).getOrCache(path)), false, glint); + model.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, red, green, blue, 1.0F); + }); + } + } + + public static void register(ArmorRenderLayer layer, ItemConvertible ... items) { + if(items == null || items.length == 0) return; + + for(ItemConvertible item : items) { + if(ArmorRendererRegistryImpl.get(item.asItem()) == null) { + ArmorRenderer.register(ArmorRenderLibImpl::render, item); + } + + RENDERERS.put(item.asItem(), layer); + } + } +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/impl/ArmorTextureCache.java b/src/main/java/com/github/clevernucleus/armorrenderlib/impl/ArmorTextureCache.java new file mode 100644 index 0000000..492415a --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/impl/ArmorTextureCache.java @@ -0,0 +1,7 @@ +package com.github.clevernucleus.armorrenderlib.impl; + +import net.minecraft.util.Identifier; + +public interface ArmorTextureCache { + Identifier getOrCache(final String path); +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/impl/FeatureRendererAccessor.java b/src/main/java/com/github/clevernucleus/armorrenderlib/impl/FeatureRendererAccessor.java new file mode 100644 index 0000000..a6330f7 --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/impl/FeatureRendererAccessor.java @@ -0,0 +1,9 @@ +package com.github.clevernucleus.armorrenderlib.impl; + +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.entity.LivingEntity; + +public interface FeatureRendererAccessor> { + ArmorFeatureRenderer getFeatureRenderer(Object key); +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/ArmorFeatureRendererInvoker.java b/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/ArmorFeatureRendererInvoker.java new file mode 100644 index 0000000..541d6e5 --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/ArmorFeatureRendererInvoker.java @@ -0,0 +1,18 @@ +package com.github.clevernucleus.armorrenderlib.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; + +@Mixin(ArmorFeatureRenderer.class) +public interface ArmorFeatureRendererInvoker, A extends BipedEntityModel> { + @Invoker("getArmor") + public A invokeGetArmor(EquipmentSlot slot); + + @Invoker("setVisible") + public void invokeSetVisible(A bipedModel, EquipmentSlot slot); +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/ArmorFeatureRendererMixin.java b/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/ArmorFeatureRendererMixin.java new file mode 100644 index 0000000..bf0e49e --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/ArmorFeatureRendererMixin.java @@ -0,0 +1,25 @@ +package com.github.clevernucleus.armorrenderlib.mixin; + +import java.util.Map; + +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import com.github.clevernucleus.armorrenderlib.impl.ArmorTextureCache; + +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.util.Identifier; + +@Mixin(value = ArmorFeatureRenderer.class, priority = 900) +abstract class ArmorFeatureRendererMixin implements ArmorTextureCache { + + @Shadow + @Final + private static Map ARMOR_TEXTURE_CACHE; + + @Override + public Identifier getOrCache(final String path) { + return ARMOR_TEXTURE_CACHE.computeIfAbsent(path, Identifier::new); + } +} diff --git a/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/LivingEntityRendererMixin.java b/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/LivingEntityRendererMixin.java new file mode 100644 index 0000000..4ebb4d8 --- /dev/null +++ b/src/main/java/com/github/clevernucleus/armorrenderlib/mixin/LivingEntityRendererMixin.java @@ -0,0 +1,45 @@ +package com.github.clevernucleus.armorrenderlib.mixin; + +import java.util.List; +import java.util.Map; + +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.Redirect; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.github.clevernucleus.armorrenderlib.impl.FeatureRendererAccessor; + +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; +import net.minecraft.client.render.entity.LivingEntityRenderer; +import net.minecraft.client.render.entity.feature.ArmorFeatureRenderer; +import net.minecraft.client.render.entity.feature.FeatureRenderer; +import net.minecraft.client.render.entity.model.EntityModel; +import net.minecraft.entity.LivingEntity; + +@Mixin(LivingEntityRenderer.class) +abstract class LivingEntityRendererMixin> implements FeatureRendererAccessor { + + @Unique + private final Map>, FeatureRenderer> arl_features = new Object2ObjectArrayMap<>(); + + @SuppressWarnings("unchecked") + @Inject(method = "addFeature", at = @At("HEAD"), cancellable = true) + private void arl_addFeature(FeatureRenderer feature, CallbackInfoReturnable info) { + this.arl_features.put((Class>)feature.getClass(), feature); + info.setReturnValue(true); + } + + @Redirect(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/render/entity/LivingEntityRenderer;features:Ljava/util/List;", opcode = Opcodes.GETFIELD)) + private List> arl_get(LivingEntityRenderer renderer) { + return this.arl_features.values().stream().toList(); + } + + @Override + public ArmorFeatureRenderer getFeatureRenderer(Object key) { + return (ArmorFeatureRenderer)this.arl_features.get(key); + } +} diff --git a/src/main/resources/armor-model-predicate.mixins.json b/src/main/resources/armorrenderlib.mixins.json similarity index 52% rename from src/main/resources/armor-model-predicate.mixins.json rename to src/main/resources/armorrenderlib.mixins.json index 0c77989..0c96be4 100644 --- a/src/main/resources/armor-model-predicate.mixins.json +++ b/src/main/resources/armorrenderlib.mixins.json @@ -1,13 +1,15 @@ { "required": true, "minVersion": "0.8", - "package": "com.github.clevernucleus.armormodelpredicate.mixin", + "package": "com.github.clevernucleus.armorrenderlib.mixin", "compatibilityLevel": "JAVA_17", "mixins": [ ], "client": [ - "ArmorFeatureRendererMixin" + "LivingEntityRendererMixin", + "ArmorFeatureRendererMixin", + "ArmorFeatureRendererInvoker" ], "injectors": { "defaultRequire": 1 diff --git a/src/main/resources/assets/armor-model-predicate/icon.png b/src/main/resources/assets/armorrenderlib/icon.png similarity index 100% rename from src/main/resources/assets/armor-model-predicate/icon.png rename to src/main/resources/assets/armorrenderlib/icon.png diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 287f516..ae5c2a6 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -1,19 +1,19 @@ { "schemaVersion": 1, - "id": "armor-model-predicate", + "id": "armorrenderlib", "version": "${version}", - "name": "Armor Model Predicate", - "description": "Adds armor model predicates to Minecraft using Fabric Mixins.", + "name": "Armor Render Lib", + "description": "Extends Fabric API's armor renderer registry to enable a more specific implementation.", "authors": [ "CleverNucleus" ], "contact": { - "homepage": "https://github.com/CleverNucleus/ArmorModelPredicate", - "sources": "https://github.com/CleverNucleus/ArmorModelPredicate" + "homepage": "https://github.com/CleverNucleus/ArmorRenderLib", + "sources": "https://github.com/CleverNucleus/ArmorRenderLib" }, "license": "MIT", - "icon": "assets/armor-model-predicate/icon.png", + "icon": "assets/armorrenderlib/icon.png", "environment": "*", "entrypoints": { @@ -22,11 +22,12 @@ ] }, "mixins": [ - "armor-model-predicate.mixins.json" + "armorrenderlib.mixins.json" ], "depends": { - "fabricloader": ">=0.12.12", - "minecraft": "1.18.x", + "fabricloader": ">=0.14.6", + "fabric": ">=0.55.1", + "minecraft": "1.18.2", "java": ">=17" } } \ No newline at end of file