From 44802abf1b849c9649de0d94434f7cdda790d609 Mon Sep 17 00:00:00 2001 From: Sollace Date: Mon, 9 Jan 2023 01:25:52 +0100 Subject: [PATCH] Properly render both arms in first person when holding an entity --- .../client/FirstPersonRendererOverrides.java | 32 ++++++++++ .../render/AccessoryFeatureRenderer.java | 31 +++++++++- .../render/HeldEntityFeatureRenderer.java | 36 +++++++++--- .../mixin/client/MixinHeldItemRenderer.java | 58 +++++++++++++++++++ .../client/MixinPlayerEntityRenderer.java | 7 ++- src/main/resources/unicopia.aw | 1 + src/main/resources/unicopia.mixin.json | 1 + 7 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/client/FirstPersonRendererOverrides.java create mode 100644 src/main/java/com/minelittlepony/unicopia/mixin/client/MixinHeldItemRenderer.java diff --git a/src/main/java/com/minelittlepony/unicopia/client/FirstPersonRendererOverrides.java b/src/main/java/com/minelittlepony/unicopia/client/FirstPersonRendererOverrides.java new file mode 100644 index 00000000..11a2cc18 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/FirstPersonRendererOverrides.java @@ -0,0 +1,32 @@ +package com.minelittlepony.unicopia.client; + +import java.util.Optional; + +import com.minelittlepony.unicopia.client.render.AccessoryFeatureRenderer; +import com.minelittlepony.unicopia.entity.player.Pony; + +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.item.HeldItemRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.util.Arm; + +public class FirstPersonRendererOverrides { + public static final FirstPersonRendererOverrides INSTANCE = new FirstPersonRendererOverrides(); + + public Optional getHandRenderType(PlayerEntity player) { + return Pony.of(player).getEntityInArms().map(e -> HeldItemRenderer.HandRenderType.RENDER_BOTH_HANDS); + } + + public boolean beforeRenderHands(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, ClientPlayerEntity player, int light) { + var root = AccessoryFeatureRenderer.FeatureRoot.of(player); + return root != null && root.getAccessories().beforeRenderArms(sender, tickDelta, matrices, vertexConsumers, player, light); + } + + public interface ArmRenderer { + void invokeRenderArmHoldingItem(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, float equipProgress, float swingProgress, Arm arm); + + float getEquipProgress(float tickDelta); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java index 97835764..d85bc640 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/AccessoryFeatureRenderer.java @@ -1,9 +1,14 @@ package com.minelittlepony.unicopia.client.render; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer; + +import net.minecraft.client.MinecraftClient; import net.minecraft.client.model.ModelPart; +import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.feature.FeatureRenderer; import net.minecraft.client.render.entity.feature.FeatureRendererContext; @@ -39,6 +44,14 @@ public class AccessoryFeatureRenderer< features.forEach(feature -> feature.renderArm(matrices, vertexConsumers, light, entity, arm, side)); } + public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, int light) { + boolean cancelled = false; + for (var feature : features) { + cancelled |= feature.beforeRenderArms(sender, tickDelta, matrices, vertexConsumers, entity, light); + } + return cancelled; + } + public interface FeatureFactory { Feature create(FeatureRendererContext> context); } @@ -46,7 +59,10 @@ public class AccessoryFeatureRenderer< public interface Feature { void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch); - default void renderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, ModelPart arm, Arm side) { + default void renderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, ModelPart arm, Arm side) {} + + default boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, T entity, int light) { + return false; } } @@ -54,5 +70,14 @@ public class AccessoryFeatureRenderer< T extends LivingEntity, M extends BipedEntityModel> { AccessoryFeatureRenderer getAccessories(); + @SuppressWarnings("unchecked") + @Nullable + static > FeatureRoot of(T entity) { + var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity); + if (renderer instanceof FeatureRoot) { + return (FeatureRoot)renderer; + } + return null; + } } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/HeldEntityFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/HeldEntityFeatureRenderer.java index 8bf06c37..88d8ab7b 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/HeldEntityFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/HeldEntityFeatureRenderer.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.client.render; +import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer; import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.duck.EntityDuck; @@ -20,7 +21,10 @@ import net.minecraft.util.math.*; public class HeldEntityFeatureRenderer implements AccessoryFeatureRenderer.Feature { + private final FeatureRendererContext> context; + public HeldEntityFeatureRenderer(FeatureRendererContext> context) { + this.context = context; } @Override @@ -47,23 +51,37 @@ public class HeldEntityFeatureRenderer implements Access @Override public void renderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, E entity, ModelPart arm, Arm side) { - Pony.of(entity).flatMap(Pony::getEntityInArms).ifPresent(passenger -> { - float tickDelta = MinecraftClient.getInstance().getTickDelta(); + } + + @Override + public boolean beforeRenderArms(ArmRenderer sender, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, E entity, int light) { + return Pony.of(entity).flatMap(Pony::getEntityInArms).filter(passenger -> { + float swingProgress = entity.getHandSwingProgress(MinecraftClient.getInstance().getTickDelta()); + float f = -0.4f * MathHelper.sin(MathHelper.sqrt(swingProgress) * (float)Math.PI); + float g = 0.2f * MathHelper.sin(MathHelper.sqrt(swingProgress) * ((float)Math.PI * 2)); + float h = -0.2f * MathHelper.sin(swingProgress * (float)Math.PI); matrices.push(); - matrices.translate(0.8F, 0.4F, -0.1F); - matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90)); - matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-60 - 13)); - matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-30)); - matrices.translate(0, 0, -0.2F); + matrices.translate(f, g, h); + matrices.translate(0, -1.3F, -1.3F); + matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(13)); if (!(passenger instanceof Pony)) { matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90)); } - matrices.translate(-passenger.asEntity().getWidth() / 16F, 0.3F, 0); renderCarriedEntity(passenger.asEntity(), matrices, vertexConsumers, light, tickDelta); matrices.pop(); - }); + + float equipProgress = 1 - sender.getEquipProgress(tickDelta); + + matrices.push(); + sender.invokeRenderArmHoldingItem(matrices, vertexConsumers, light, equipProgress, swingProgress, Arm.LEFT); + matrices.pop(); + matrices.push(); + sender.invokeRenderArmHoldingItem(matrices, vertexConsumers, light, equipProgress, swingProgress, Arm.RIGHT); + matrices.pop(); + return true; + }).isPresent(); } private void renderCarriedEntity(LivingEntity p, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, float tickDelta) { diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinHeldItemRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinHeldItemRenderer.java new file mode 100644 index 00000000..c99f0546 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinHeldItemRenderer.java @@ -0,0 +1,58 @@ +package com.minelittlepony.unicopia.mixin.client; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Invoker; +import org.spongepowered.asm.mixin.injection.*; +import org.spongepowered.asm.mixin.injection.At.Shift; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.item.HeldItemRenderer; +import net.minecraft.client.render.item.HeldItemRenderer.HandRenderType; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Arm; +import net.minecraft.util.math.MathHelper; + +@Mixin(HeldItemRenderer.class) +abstract class MixinHeldItemRenderer implements FirstPersonRendererOverrides.ArmRenderer { + @Shadow + private float equipProgressMainHand; + @Shadow + private float prevEquipProgressMainHand; + + @Override + @Invoker("renderArmHoldingItem") + public abstract void invokeRenderArmHoldingItem(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, float equipProgress, float swingProgress, Arm arm); + + @Override + public float getEquipProgress(float tickDelta) { + return MathHelper.lerp(tickDelta, prevEquipProgressMainHand, equipProgressMainHand); + } + + @Inject( + method = "getHandRenderType", + at = @At("HEAD"), + cancellable = true + ) + private static void onGetHandRenderType(ClientPlayerEntity player, CallbackInfoReturnable info) { + FirstPersonRendererOverrides.INSTANCE.getHandRenderType(player).ifPresent(info::setReturnValue); + } + + @Inject( + method = "renderItem(FLnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumerProvider$Immediate;Lnet/minecraft/client/network/ClientPlayerEntity;I)V", + at = @At( + value = "FIELD", + target = "net/minecraft/client/render/item/HeldItemRenderer$HandRenderType.renderMainHand", + shift = Shift.BEFORE + ), + cancellable = true) + private void onRenderItem(float tickDelta, MatrixStack matrices, VertexConsumerProvider.Immediate vertexConsumers, ClientPlayerEntity player, int light, CallbackInfo info) { + if (FirstPersonRendererOverrides.INSTANCE.beforeRenderHands(this, tickDelta, matrices, vertexConsumers, player, light)) { + info.cancel(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java index 4b0f02b7..35503ffd 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/client/MixinPlayerEntityRenderer.java @@ -21,14 +21,17 @@ import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Arm; @Mixin(PlayerEntityRenderer.class) -abstract class MixinPlayerEntityRenderer extends LivingEntityRenderer> { +abstract class MixinPlayerEntityRenderer + extends LivingEntityRenderer> + implements FeatureRoot> { @Nullable private AccessoryFeatureRenderer> accessories; MixinPlayerEntityRenderer() { super(null, null, 0); } + @Override @SuppressWarnings("unchecked") - private AccessoryFeatureRenderer> getAccessories() { + public AccessoryFeatureRenderer> getAccessories() { if (accessories == null) { accessories = features.stream() .filter(a -> a instanceof FeatureRoot) diff --git a/src/main/resources/unicopia.aw b/src/main/resources/unicopia.aw index 30c717ac..668c9c3f 100644 --- a/src/main/resources/unicopia.aw +++ b/src/main/resources/unicopia.aw @@ -1,3 +1,4 @@ accessWidener v1 named accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;IZZLnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; +accessible class net/minecraft/client/render/item/HeldItemRenderer$HandRenderType \ No newline at end of file diff --git a/src/main/resources/unicopia.mixin.json b/src/main/resources/unicopia.mixin.json index 94cd7398..06cef538 100644 --- a/src/main/resources/unicopia.mixin.json +++ b/src/main/resources/unicopia.mixin.json @@ -47,6 +47,7 @@ "client.MixinCamera", "client.MixinEntityRenderDispatcher", "client.MixinGameRenderer", + "client.MixinHeldItemRenderer", "client.MixinInGameHud", "client.MixinInGameHud$HeartType", "client.MixinItem",