From ea610c56c3295350a959167a0df47ef6394f5cd7 Mon Sep 17 00:00:00 2001 From: ChampionAsh5357 Date: Wed, 15 Jan 2025 23:15:44 -0500 Subject: [PATCH] fix(neoforge): Add EntityRenderState to IClientItemExtensions#get*ArmorModel --- .../entity/EntityRenderDispatcher.java.patch | 8 ++++ .../layers/EquipmentLayerRenderer.java.patch | 2 +- .../neoforge/client/ClientHooks.java | 6 +++ .../common/IClientItemExtensions.java | 44 +++++++------------ .../renderstate/RenderStateExtensions.java | 12 +++++ .../neoforge/debug/client/ClientTests.java | 26 +++++++++++ .../oldtest/client/CustomArmorModelTest.java | 3 +- 7 files changed, 70 insertions(+), 31 deletions(-) diff --git a/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch b/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch index aaca1f7592..1ae892b9df 100644 --- a/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch +++ b/patches/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java.patch @@ -1,5 +1,13 @@ --- a/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java +++ b/net/minecraft/client/renderer/entity/EntityRenderDispatcher.java +@@ -203,6 +_,7 @@ + } + + p_363537_.popPose(); ++ net.neoforged.neoforge.client.renderstate.RenderStateExtensions.unsetEntityRenderState(); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.forThrowable(throwable, "Rendering entity in world"); + CrashReportCategory crashreportcategory = crashreport.addCategory("Entity being rendered"); @@ -246,12 +_,12 @@ ) { AABB aabb = p_114444_.getBoundingBox().move(-p_114444_.getX(), -p_114444_.getY(), -p_114444_.getZ()); diff --git a/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch b/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch index ab8e222398..94b070ff2e 100644 --- a/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch +++ b/patches/net/minecraft/client/renderer/entity/layers/EquipmentLayerRenderer.java.patch @@ -5,7 +5,7 @@ @Nullable ResourceLocation p_371639_ ) { + net.neoforged.neoforge.client.extensions.common.IClientItemExtensions extensions = net.neoforged.neoforge.client.extensions.common.IClientItemExtensions.of(p_371670_); -+ p_371731_ = extensions.getGenericArmorModel(p_371670_, p_387484_, p_371731_); ++ p_371731_ = net.neoforged.neoforge.client.ClientHooks.getGenericArmorModel(extensions, p_371670_, p_387484_, p_371731_); List list = this.equipmentAssets.get(p_387603_).getLayers(p_387484_); if (!list.isEmpty()) { - int i = p_371670_.is(ItemTags.DYEABLE) ? DyedItemColor.getOrDefault(p_371670_, 0) : 0; diff --git a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java index 7e497ecaf2..d0264cd96e 100644 --- a/src/main/java/net/neoforged/neoforge/client/ClientHooks.java +++ b/src/main/java/net/neoforged/neoforge/client/ClientHooks.java @@ -51,6 +51,7 @@ import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipComponent; import net.minecraft.client.gui.screens.inventory.tooltip.ClientTooltipPositioner; import net.minecraft.client.model.HumanoidModel; +import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelLayerLocation; import net.minecraft.client.model.geom.builders.LayerDefinition; import net.minecraft.client.multiplayer.ClientLevel; @@ -181,6 +182,7 @@ import net.neoforged.neoforge.client.gui.map.MapDecorationRendererManager; import net.neoforged.neoforge.client.model.data.ModelData; import net.neoforged.neoforge.client.renderstate.RegisterRenderStateModifiersEvent; +import net.neoforged.neoforge.client.renderstate.RenderStateExtensions; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.common.NeoForgeMod; import net.neoforged.neoforge.forge.snapshots.ForgeSnapshotsModClient; @@ -255,6 +257,10 @@ public static float getGuiFarPlane() { return 11_000F + depth; } + public static Model getGenericArmorModel(IClientItemExtensions extensions, ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model original) { + return extensions.getGenericArmorModel(itemStack, RenderStateExtensions.getCurrentRenderState(), layerType, original); + } + public static ResourceLocation getArmorTexture(ItemStack armor, EquipmentClientInfo.LayerType type, EquipmentClientInfo.Layer layer, ResourceLocation _default) { ResourceLocation result = IClientItemExtensions.of(armor).getArmorTexture(armor, type, layer, _default); return result != null ? result : _default; diff --git a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java index 5533e4223c..72269c30cd 100644 --- a/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java +++ b/src/main/java/net/neoforged/neoforge/client/extensions/common/IClientItemExtensions.java @@ -13,6 +13,7 @@ import net.minecraft.client.model.Model; import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.renderer.entity.layers.EquipmentLayerRenderer; +import net.minecraft.client.renderer.entity.state.EntityRenderState; import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.core.component.DataComponents; import net.minecraft.resources.ResourceLocation; @@ -93,31 +94,33 @@ default boolean applyForgeHandTransform(PoseStack poseStack, LocalPlayer player, /** * Queries the humanoid armor model for this item when it's equipped. * - * @param itemStack The item stack - * @param layerType The slot the item is in - * @param original The original armor model. Will have attributes set. + * @param itemStack The item stack + * @param renderState The state of the entity being rendered + * @param layerType The slot the item is in + * @param original The original armor model. Will have attributes set. * @return A HumanoidModel to be rendered. Relevant properties are to be copied over by the caller. - * @see #getGenericArmorModel(ItemStack, EquipmentClientInfo.LayerType, Model) + * @see #getGenericArmorModel(ItemStack, EntityRenderState, EquipmentClientInfo.LayerType, Model) */ - default Model getHumanoidArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model original) { + default Model getHumanoidArmorModel(ItemStack itemStack, EntityRenderState renderState, EquipmentClientInfo.LayerType layerType, Model original) { return original; } /** * Queries the armor model for this item when it's equipped. Useful in place of - * {@link #getHumanoidArmorModel(ItemStack, EquipmentClientInfo.LayerType, Model)} for wrapping the original + * {@link #getHumanoidArmorModel(ItemStack, EntityRenderState, EquipmentClientInfo.LayerType, Model)} for wrapping the original * model or returning anything non-standard. *

* If you override this method you are responsible for copying any properties you care about from the original model. * - * @param itemStack The item stack - * @param layerType The slot the item is in - * @param original The original armor model. Will have attributes set. + * @param itemStack The item stack + * @param renderState The state of the entity being rendered + * @param layerType The slot the item is in + * @param original The original armor model. Will have attributes set. * @return A Model to be rendered. Relevant properties must be copied over manually. - * @see #getHumanoidArmorModel(ItemStack, EquipmentClientInfo.LayerType, Model) + * @see #getHumanoidArmorModel(ItemStack, EntityRenderState, EquipmentClientInfo.LayerType, Model) */ - default Model getGenericArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType layerType, Model original) { - Model replacement = getHumanoidArmorModel(itemStack, layerType, original); + default Model getGenericArmorModel(ItemStack itemStack, EntityRenderState renderState, EquipmentClientInfo.LayerType layerType, Model original) { + Model replacement = getHumanoidArmorModel(itemStack, renderState, layerType, original); if (replacement != original) { if (original instanceof HumanoidModel originalHumanoid && replacement instanceof HumanoidModel replacementHumanoid) { ClientHooks.copyModelProperties(originalHumanoid, replacementHumanoid); @@ -127,23 +130,6 @@ default Model getGenericArmorModel(ItemStack itemStack, EquipmentClientInfo.Laye return original; } - /** - * Called when an armor piece is about to be rendered, allowing parts of the model to be animated or changed. - * - * @param itemStack The item stack being worn - * @param livingEntity The entity wearing the armor - * @param equipmentSlot The slot the armor stack is being worn in - * @param model The armor model being rendered - * @param limbSwing The swing position of the entity's walk animation - * @param limbSwingAmount The swing speed of the entity's walk animation - * @param partialTick The partial tick time - * @param ageInTicks The total age of the entity, with partialTick already applied - * @param netHeadYaw The yaw (Y rotation) of the entity's head - * @param headPitch The pitch (X rotation) of the entity's head - */ - // TODO 1.21.2: add back patch that calls this method from HumanoidArmorLayer - default void setupModelAnimations(LivingEntity livingEntity, ItemStack itemStack, EquipmentSlot equipmentSlot, Model model, float limbSwing, float limbSwingAmount, float partialTick, float ageInTicks, float netHeadYaw, float headPitch) {} - /** * Called to render an overlay on the first-person camera. *

diff --git a/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java b/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java index 76c0525f13..e89433dbce 100644 --- a/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java +++ b/src/main/java/net/neoforged/neoforge/client/renderstate/RenderStateExtensions.java @@ -33,6 +33,8 @@ private RenderStateExtensions() {} private static final Map, Collection> MAP_DECORATION = new Reference2ObjectArrayMap<>(); + private static final ThreadLocal RENDERING_STATE = ThreadLocal.withInitial(() -> null); + @SuppressWarnings("unchecked") @ApiStatus.Internal public static void onUpdateEntityRenderState(EntityRenderer renderer, E entity, S renderState) { @@ -49,6 +51,16 @@ public static void onUpdateEntit for (BiConsumer modifier : modifiers) { modifier.accept(entity, renderState); } + + RENDERING_STATE.set(renderState); + } + + public static EntityRenderState getCurrentRenderState() { + return RENDERING_STATE.get(); + } + + public static void unsetEntityRenderState() { + RENDERING_STATE.remove(); } @ApiStatus.Internal diff --git a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java index 63cc542c93..b880ce332c 100644 --- a/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java +++ b/tests/src/main/java/net/neoforged/neoforge/debug/client/ClientTests.java @@ -13,9 +13,12 @@ import net.minecraft.client.KeyMapping; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.model.Model; import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.entity.state.EntityRenderState; import net.minecraft.client.renderer.texture.MissingTextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlas; +import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.client.resources.sounds.AbstractSoundInstance; import net.minecraft.client.resources.sounds.Sound; import net.minecraft.client.resources.sounds.SoundInstance; @@ -24,11 +27,14 @@ import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundSource; +import net.minecraft.util.Mth; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; +import net.minecraft.world.item.equipment.ArmorMaterials; +import net.minecraft.world.item.equipment.ArmorType; import net.minecraft.world.phys.Vec3; import net.neoforged.api.distmarker.Dist; import net.neoforged.neoforge.client.event.ClientChatEvent; @@ -143,6 +149,26 @@ public void renderFirstPersonOverlay(ItemStack stack, EquipmentSlot equipmentSlo }); } + @TestHolder(description = "Tests that the render state can be properly accessed when choosing an armor model for an item.", enabledByDefault = true) + static void renderStateOnEquipment(final DynamicTest test) { + var item = test.registrationHelper().items().registerItem("wiggling_chestplate", properties -> new Item(ArmorMaterials.IRON.humanoidProperties(properties, ArmorType.CHESTPLATE))); + test.framework().modEventBus().addListener((final RegisterClientExtensionsEvent event) -> event.registerItem(new IClientItemExtensions() { + @Override + public Model getGenericArmorModel(ItemStack itemStack, EntityRenderState renderState, EquipmentClientInfo.LayerType layerType, Model original) { + var result = IClientItemExtensions.super.getGenericArmorModel(itemStack, renderState, layerType, original); + result.root().yRot = 0.35F * Mth.sin(0.6F * renderState.ageInTicks); + return result; + } + }, item)); + test.eventListeners().forge().addListener((final PlayerEvent.PlayerLoggedInEvent event) -> { + // Give item to player for testing + event.getEntity().addItem(item.toStack()); + + // Request confirmation from the player + test.requestConfirmation(event.getEntity(), Component.literal("Does the 'wiggling_chestplate' wiggle while being worn?")); + }); + } + private static final class SineSound extends AbstractSoundInstance { SineSound(Vec3 position) { super(ResourceLocation.fromNamespaceAndPath("neotests_audio_stream_test", "sine_wave"), SoundSource.BLOCKS, SoundInstance.createUnseededRandom()); diff --git a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java index cc3a1486d2..f68c4556d3 100644 --- a/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java +++ b/tests/src/main/java/net/neoforged/neoforge/oldtest/client/CustomArmorModelTest.java @@ -9,6 +9,7 @@ import net.minecraft.client.model.HumanoidModel; import net.minecraft.client.model.Model; import net.minecraft.client.model.geom.ModelLayers; +import net.minecraft.client.renderer.entity.state.EntityRenderState; import net.minecraft.client.renderer.entity.state.HumanoidRenderState; import net.minecraft.client.resources.model.EquipmentClientInfo; import net.minecraft.core.component.DataComponents; @@ -70,7 +71,7 @@ private static final class ClientEvents { private static void onRegisterClientExtensions(RegisterClientExtensionsEvent event) { event.registerItem(new IClientItemExtensions() { @Override - public HumanoidModel getHumanoidArmorModel(ItemStack itemStack, EquipmentClientInfo.LayerType armorSlot, Model _default) { + public HumanoidModel getHumanoidArmorModel(ItemStack itemStack, EntityRenderState renderState, EquipmentClientInfo.LayerType armorSlot, Model _default) { return ENDERMAN.get(); }