mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Properly render both arms in first person when holding an entity
This commit is contained in:
parent
015d3b29fa
commit
44802abf1b
7 changed files with 152 additions and 14 deletions
|
@ -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<HeldItemRenderer.HandRenderType> 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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,14 @@
|
||||||
package com.minelittlepony.unicopia.client.render;
|
package com.minelittlepony.unicopia.client.render;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
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.model.ModelPart;
|
||||||
|
import net.minecraft.client.render.VertexConsumer;
|
||||||
import net.minecraft.client.render.VertexConsumerProvider;
|
import net.minecraft.client.render.VertexConsumerProvider;
|
||||||
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
import net.minecraft.client.render.entity.feature.FeatureRenderer;
|
||||||
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
|
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));
|
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<T extends LivingEntity> {
|
public interface FeatureFactory<T extends LivingEntity> {
|
||||||
Feature<T> create(FeatureRendererContext<T, ? extends BipedEntityModel<T>> context);
|
Feature<T> create(FeatureRendererContext<T, ? extends BipedEntityModel<T>> context);
|
||||||
}
|
}
|
||||||
|
@ -46,7 +59,10 @@ public class AccessoryFeatureRenderer<
|
||||||
public interface Feature<T extends LivingEntity> {
|
public interface Feature<T extends LivingEntity> {
|
||||||
void render(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, T entity, float limbAngle, float limbDistance, float tickDelta, float animationProgress, float headYaw, float headPitch);
|
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,
|
T extends LivingEntity,
|
||||||
M extends BipedEntityModel<T>> {
|
M extends BipedEntityModel<T>> {
|
||||||
AccessoryFeatureRenderer<T, M> getAccessories();
|
AccessoryFeatureRenderer<T, M> getAccessories();
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Nullable
|
||||||
|
static <T extends LivingEntity, M extends BipedEntityModel<T>> FeatureRoot<T, M> of(T entity) {
|
||||||
|
var renderer = MinecraftClient.getInstance().getEntityRenderDispatcher().getRenderer(entity);
|
||||||
|
if (renderer instanceof FeatureRoot) {
|
||||||
|
return (FeatureRoot<T, M>)renderer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package com.minelittlepony.unicopia.client.render;
|
package com.minelittlepony.unicopia.client.render;
|
||||||
|
|
||||||
|
import com.minelittlepony.unicopia.client.FirstPersonRendererOverrides.ArmRenderer;
|
||||||
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate;
|
||||||
import com.minelittlepony.unicopia.entity.Living;
|
import com.minelittlepony.unicopia.entity.Living;
|
||||||
import com.minelittlepony.unicopia.entity.duck.EntityDuck;
|
import com.minelittlepony.unicopia.entity.duck.EntityDuck;
|
||||||
|
@ -20,7 +21,10 @@ import net.minecraft.util.math.*;
|
||||||
|
|
||||||
public class HeldEntityFeatureRenderer<E extends LivingEntity> implements AccessoryFeatureRenderer.Feature<E> {
|
public class HeldEntityFeatureRenderer<E extends LivingEntity> implements AccessoryFeatureRenderer.Feature<E> {
|
||||||
|
|
||||||
|
private final FeatureRendererContext<E, ? extends BipedEntityModel<E>> context;
|
||||||
|
|
||||||
public HeldEntityFeatureRenderer(FeatureRendererContext<E, ? extends BipedEntityModel<E>> context) {
|
public HeldEntityFeatureRenderer(FeatureRendererContext<E, ? extends BipedEntityModel<E>> context) {
|
||||||
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,23 +51,37 @@ public class HeldEntityFeatureRenderer<E extends LivingEntity> implements Access
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void renderArm(MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, E entity, ModelPart arm, Arm side) {
|
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.push();
|
||||||
matrices.translate(0.8F, 0.4F, -0.1F);
|
matrices.translate(f, g, h);
|
||||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90));
|
matrices.translate(0, -1.3F, -1.3F);
|
||||||
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(-60 - 13));
|
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(13));
|
||||||
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-30));
|
|
||||||
matrices.translate(0, 0, -0.2F);
|
|
||||||
if (!(passenger instanceof Pony)) {
|
if (!(passenger instanceof Pony)) {
|
||||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90));
|
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(90));
|
||||||
}
|
}
|
||||||
matrices.translate(-passenger.asEntity().getWidth() / 16F, 0.3F, 0);
|
|
||||||
|
|
||||||
renderCarriedEntity(passenger.asEntity(), matrices, vertexConsumers, light, tickDelta);
|
renderCarriedEntity(passenger.asEntity(), matrices, vertexConsumers, light, tickDelta);
|
||||||
matrices.pop();
|
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) {
|
private void renderCarriedEntity(LivingEntity p, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, float tickDelta) {
|
||||||
|
|
|
@ -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<HandRenderType> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,14 +21,17 @@ import net.minecraft.client.util.math.MatrixStack;
|
||||||
import net.minecraft.util.Arm;
|
import net.minecraft.util.Arm;
|
||||||
|
|
||||||
@Mixin(PlayerEntityRenderer.class)
|
@Mixin(PlayerEntityRenderer.class)
|
||||||
abstract class MixinPlayerEntityRenderer extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
|
abstract class MixinPlayerEntityRenderer
|
||||||
|
extends LivingEntityRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>>
|
||||||
|
implements FeatureRoot<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> {
|
||||||
@Nullable
|
@Nullable
|
||||||
private AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> accessories;
|
private AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> accessories;
|
||||||
|
|
||||||
MixinPlayerEntityRenderer() { super(null, null, 0); }
|
MixinPlayerEntityRenderer() { super(null, null, 0); }
|
||||||
|
|
||||||
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> getAccessories() {
|
public AccessoryFeatureRenderer<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>> getAccessories() {
|
||||||
if (accessories == null) {
|
if (accessories == null) {
|
||||||
accessories = features.stream()
|
accessories = features.stream()
|
||||||
.filter(a -> a instanceof FeatureRoot)
|
.filter(a -> a instanceof FeatureRoot)
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
accessWidener v1 named
|
accessWidener v1 named
|
||||||
accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters
|
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 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
|
|
@ -47,6 +47,7 @@
|
||||||
"client.MixinCamera",
|
"client.MixinCamera",
|
||||||
"client.MixinEntityRenderDispatcher",
|
"client.MixinEntityRenderDispatcher",
|
||||||
"client.MixinGameRenderer",
|
"client.MixinGameRenderer",
|
||||||
|
"client.MixinHeldItemRenderer",
|
||||||
"client.MixinInGameHud",
|
"client.MixinInGameHud",
|
||||||
"client.MixinInGameHud$HeartType",
|
"client.MixinInGameHud$HeartType",
|
||||||
"client.MixinItem",
|
"client.MixinItem",
|
||||||
|
|
Loading…
Reference in a new issue