Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add EntityRenderState to IClientItemExtensions#get*ArmorModel #1858

Open
wants to merge 1 commit into
base: 1.21.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<EquipmentClientInfo.Layer> 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;
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/net/neoforged/neoforge/client/ClientHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
* <p>
* 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);
Expand All @@ -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.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ private RenderStateExtensions() {}

private static final Map<ResourceKey<MapDecorationType>, Collection<MapDecorationRenderStateModifier>> MAP_DECORATION = new Reference2ObjectArrayMap<>();

private static final ThreadLocal<EntityRenderState> RENDERING_STATE = ThreadLocal.withInitial(() -> null);

@SuppressWarnings("unchecked")
@ApiStatus.Internal
public static <E extends Entity, S extends EntityRenderState> void onUpdateEntityRenderState(EntityRenderer<E, S> renderer, E entity, S renderState) {
Expand All @@ -49,6 +51,16 @@ public static <E extends Entity, S extends EntityRenderState> void onUpdateEntit
for (BiConsumer<E, S> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}

Expand Down
Loading