diff --git a/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java b/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java index 2e52aa4b..f2040265 100644 --- a/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java +++ b/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java @@ -13,6 +13,7 @@ import net.minecraft.client.model.ModelPart; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.math.Vec3f; + import net.minecraft.entity.Entity; import net.minecraft.entity.LivingEntity; import net.minecraft.util.Arm; @@ -80,6 +81,10 @@ public abstract class AbstractPonyModel extends ClientPo shakeBody(move, swing, getWobbleAmount(), ticks); rotateLegs(move, swing, ticks, entity); + if (onSetModelAngles != null) { + onSetModelAngles.poseModel(this, move, swing, ticks, entity); + } + if (!attributes.isSwimming && !attributes.isGoingFast) { holdItem(swing); } diff --git a/src/main/java/com/minelittlepony/client/model/ClientPonyModel.java b/src/main/java/com/minelittlepony/client/model/ClientPonyModel.java index 86b2c70a..c53ff94e 100644 --- a/src/main/java/com/minelittlepony/client/model/ClientPonyModel.java +++ b/src/main/java/com/minelittlepony/client/model/ClientPonyModel.java @@ -2,10 +2,13 @@ package com.minelittlepony.client.model; import net.minecraft.client.model.ModelPart; import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.render.entity.model.ModelWithHat; import net.minecraft.entity.LivingEntity; import net.minecraft.util.Arm; import net.minecraft.util.Hand; +import org.jetbrains.annotations.Nullable; + import com.minelittlepony.api.model.ModelAttributes; import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback; import com.minelittlepony.api.pony.IPony; @@ -22,7 +25,7 @@ import com.minelittlepony.mson.api.model.biped.MsonPlayer; * * Modders can extend this class to make their own pony models if they wish. */ -public abstract class ClientPonyModel extends MsonPlayer implements IPonyModel { +public abstract class ClientPonyModel extends MsonPlayer implements IPonyModel, ModelWithHat { /** * The model attributes. @@ -34,10 +37,17 @@ public abstract class ClientPonyModel extends MsonPlayer */ protected IPonyData metadata = PonyData.NULL; + @Nullable + protected PosingCallback onSetModelAngles; + public ClientPonyModel(ModelPart tree) { super(tree); } + public void onSetModelAngles(PosingCallback callback) { + onSetModelAngles = callback; + } + protected Arm getPreferredArm(T livingEntity) { Arm arm = livingEntity.getMainArm(); return livingEntity.preferredHand == Hand.MAIN_HAND ? arm : arm.getOpposite(); @@ -114,4 +124,13 @@ public abstract class ClientPonyModel extends MsonPlayer ((ClientPonyModel)model).metadata = metadata; } } + + @Override + public void setHatVisible(boolean visible) { + + } + + public interface PosingCallback { + void poseModel(ClientPonyModel model, float move, float swing, float ticks, T entity); + } } diff --git a/src/main/java/com/minelittlepony/client/model/IMobModel.java b/src/main/java/com/minelittlepony/client/model/IMobModel.java index 3f43ee14..e24864f5 100644 --- a/src/main/java/com/minelittlepony/client/model/IMobModel.java +++ b/src/main/java/com/minelittlepony/client/model/IMobModel.java @@ -1,15 +1,16 @@ package com.minelittlepony.client.model; import net.minecraft.client.model.ModelPart; +import net.minecraft.util.Arm; import net.minecraft.util.math.MathHelper; import com.minelittlepony.api.model.PonyModelConstants; +import com.minelittlepony.mson.util.PartUtil; /** * Common interface for all undead enemies. */ public interface IMobModel { - /** * Rotates the provided arm to the correct orientation for holding an item. * @@ -18,7 +19,7 @@ public interface IMobModel { * @param swingProgress How far we are through the current swing * @param ticks Render partial ticks */ - default void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) { + static void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) { float swing = MathHelper.sin(swingProgress * PonyModelConstants.PI); float roll = MathHelper.sin((1 - (1 - swingProgress) * (1 - swingProgress)) * PonyModelConstants.PI); @@ -32,4 +33,21 @@ public interface IMobModel { arm.yaw = direction * (0.1F - swing * 0.6F); arm.roll = cos; } + + static void rotateUndeadArms(ClientPonyModel model, float move, float ticks) { + ModelPart leftArm = model.getArm(Arm.LEFT); + ModelPart rightArm = model.getArm(Arm.RIGHT); + + if (islookAngleRight(move)) { + IMobModel.rotateArmHolding(rightArm, 1, model.getSwingAmount(), ticks); + PartUtil.shift(rightArm, 0.5F, 1.5F, 3); + } else { + IMobModel.rotateArmHolding(leftArm, -1, model.getSwingAmount(), ticks); + PartUtil.shift(leftArm, -0.5F, 1.5F, 3); + } + } + + static boolean islookAngleRight(float move) { + return MathHelper.sin(move / 20) < 0; + } } diff --git a/src/main/java/com/minelittlepony/client/model/ModelType.java b/src/main/java/com/minelittlepony/client/model/ModelType.java index 755fd0ab..e2828f97 100644 --- a/src/main/java/com/minelittlepony/client/model/ModelType.java +++ b/src/main/java/com/minelittlepony/client/model/ModelType.java @@ -5,8 +5,7 @@ import net.minecraft.client.model.ModelPart; import net.minecraft.client.render.entity.model.ArmorStandEntityModel; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.mob.VexEntity; -import net.minecraft.entity.passive.AllayEntity; -import net.minecraft.entity.passive.StriderEntity; +import net.minecraft.entity.passive.*; import net.minecraft.util.Identifier; import com.minelittlepony.api.model.gear.IGear; @@ -36,11 +35,9 @@ public final class ModelType { public static final ModelKey DJ_PON_3 = register("dj_pon_three", DJPon3EarsModel::new); - public static final ModelKey> VILLAGER = register("villager", VillagerPonyModel::new); public static final ModelKey WITCH = register("witch", WitchPonyModel::new); public static final ModelKey> ZOMBIE = register("zombie", ZomponyModel::new); public static final ModelKey PIGLIN = register("piglin", PiglinPonyModel::new); - public static final ModelKey ZOMBIE_VILLAGER = register("zombie_villager", ZomponyVillagerModel::new); public static final ModelKey> SKELETON = register("skeleton", SkeleponyModel::new); public static final ModelKey> SKELETON_CLOTHES = register("skeleton_clothes", SkeleponyModel::new); public static final ModelKey> PILLAGER = register("pillager", PillagerPonyModel::new); diff --git a/src/main/java/com/minelittlepony/client/model/ModelWrapper.java b/src/main/java/com/minelittlepony/client/model/ModelWrapper.java index 87aad703..eaedfed5 100644 --- a/src/main/java/com/minelittlepony/client/model/ModelWrapper.java +++ b/src/main/java/com/minelittlepony/client/model/ModelWrapper.java @@ -2,12 +2,16 @@ package com.minelittlepony.client.model; import net.minecraft.entity.LivingEntity; +import org.jetbrains.annotations.Nullable; + import com.minelittlepony.api.model.IModel; import com.minelittlepony.api.model.IModelWrapper; import com.minelittlepony.api.model.armour.IArmour; import com.minelittlepony.api.pony.IPonyData; import com.minelittlepony.mson.api.ModelKey; +import java.util.function.Consumer; + /** * Container class for the various models and their associated piece of armour. */ @@ -19,8 +23,13 @@ public record ModelWrapper ( * Creates a new model wrapper to contain the given pony. */ public static ModelWrapper of(ModelKey key) { + return of(key, null); + } + + public static ModelWrapper of(ModelKey key, @Nullable Consumer initializer) { @SuppressWarnings("unchecked") M body = (M)key.createModel(); + if (initializer != null) initializer.accept(body); IArmour armor = body.createArmour(); armor.applyMetadata(body.getMetadata()); return new ModelWrapper<>(body, armor); diff --git a/src/main/java/com/minelittlepony/client/model/entity/SkeleponyModel.java b/src/main/java/com/minelittlepony/client/model/entity/SkeleponyModel.java index 92e62daa..9c62dfe1 100644 --- a/src/main/java/com/minelittlepony/client/model/entity/SkeleponyModel.java +++ b/src/main/java/com/minelittlepony/client/model/entity/SkeleponyModel.java @@ -73,6 +73,10 @@ public class SkeleponyModel extends AlicornModel imp } } + protected void rotateArmHolding(ModelPart arm, float direction, float swingProgress, float ticks) { + IMobModel.rotateArmHolding(arm, direction, swingProgress, ticks); + } + @Override public boolean canCast() { return isUnicorn; diff --git a/src/main/java/com/minelittlepony/client/model/entity/VillagerPonyModel.java b/src/main/java/com/minelittlepony/client/model/entity/VillagerPonyModel.java deleted file mode 100644 index 102241aa..00000000 --- a/src/main/java/com/minelittlepony/client/model/entity/VillagerPonyModel.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.minelittlepony.client.model.entity; - -import net.minecraft.client.model.ModelPart; -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.entity.model.ModelWithHat; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.passive.MerchantEntity; -import net.minecraft.util.math.MathHelper; -import net.minecraft.village.VillagerDataContainer; -import net.minecraft.village.VillagerProfession; - -import com.minelittlepony.api.model.IPart; -import com.minelittlepony.api.model.ModelAttributes; -import com.minelittlepony.api.pony.IPony; -import com.minelittlepony.api.pony.meta.Race; -import com.minelittlepony.client.model.entity.race.AlicornModel; -import com.minelittlepony.client.render.entity.npc.PonyTextures; -import com.minelittlepony.mson.api.ModelContext; - -public class VillagerPonyModel extends AlicornModel implements ModelWithHat { - - private final ModelPart apron; - - private IPart batWings; - private IPart batEars; - - public VillagerPonyModel(ModelPart tree) { - super(tree, false); - apron = tree.getChild("apron"); - } - - @Override - public void init(ModelContext context) { - super.init(context); - batWings = context.findByName("bat_wings"); - batEars = context.findByName("bat_ears"); - } - - @Override - public void updateLivingState(T entity, IPony pony, ModelAttributes.Mode mode) { - super.updateLivingState(entity, pony, mode); - - ears.setVisible(pony.getMetadata().getRace() != Race.BATPONY); - batEars.setVisible(pony.getMetadata().getRace() == Race.BATPONY); - } - - @Override - public IPart getWings() { - if (getMetadata().getRace() == Race.BATPONY) { - return batWings; - } - return super.getWings(); - } - - @Override - protected void shakeBody(float move, float swing, float bodySwing, float ticks) { - super.shakeBody(move, swing, bodySwing, ticks); - apron.yaw = bodySwing; - } - - @Override - public void animateModel(T entity, float limbSwing, float limbSwingAmount, float partialTickTime) { - boolean special = PonyTextures.isBestPony(entity); - - VillagerProfession profession = entity.getVillagerData().getProfession(); - - attributes.visualHeight += PonyTextures.isCrownPony(entity) ? 0.3F : -0.1F; - apron.visible = !special && profession == VillagerProfession.BUTCHER; - } - - @Override - protected void renderBody(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { - super.renderBody(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); - apron.render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); - } - - @Override - public void setHatVisible(boolean visible) { - } - - @Override - public void setModelAngles(T entity, float move, float swing, float ticks, float headYaw, float headPitch) { - super.setModelAngles(entity, move, swing, ticks, headYaw, headPitch); - - boolean isHeadRolling = entity instanceof MerchantEntity && ((MerchantEntity)entity).getHeadRollingTimeLeft() > 0; - - if (isHeadRolling) { - head.roll = 0.3F * MathHelper.sin(0.45F * ticks); - head.pitch = 0.4F; - } else { - head.roll = 0; - } - } -} diff --git a/src/main/java/com/minelittlepony/client/model/entity/ZomponyModel.java b/src/main/java/com/minelittlepony/client/model/entity/ZomponyModel.java index c68d4e7c..6d28ae6e 100644 --- a/src/main/java/com/minelittlepony/client/model/entity/ZomponyModel.java +++ b/src/main/java/com/minelittlepony/client/model/entity/ZomponyModel.java @@ -2,11 +2,8 @@ package com.minelittlepony.client.model.entity; import com.minelittlepony.client.model.IMobModel; import com.minelittlepony.client.model.entity.race.AlicornModel; -import com.minelittlepony.mson.util.PartUtil; - import net.minecraft.client.model.ModelPart; import net.minecraft.entity.mob.HostileEntity; -import net.minecraft.util.math.MathHelper; public class ZomponyModel extends AlicornModel implements IMobModel { @@ -25,28 +22,17 @@ public class ZomponyModel extends AlicornModel implements IMobModel { - - public ZomponyVillagerModel(ModelPart tree) { - super(tree); - } - - @Override - protected void rotateLegs(float move, float swing, float ticks, ZombieVillagerEntity entity) { - super.rotateLegs(move, swing, ticks, entity); - - if (rightArmPose == ArmPose.EMPTY) { - if (islookAngleRight(move)) { - rotateArmHolding(rightArm, 1, getSwingAmount(), ticks); - PartUtil.shift(rightArm, 0.5F, 1.5F, 3); - } else { - rotateArmHolding(leftArm, -1, getSwingAmount(), ticks); - PartUtil.shift(leftArm, -0.5F, 1.5F, 3); - } - } - } - - public boolean islookAngleRight(float move) { - return MathHelper.sin(move / 20) < 0; - } -} diff --git a/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java b/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java index 5c852226..7135ea49 100644 --- a/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java +++ b/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java @@ -80,7 +80,7 @@ public class EquineRenderManager setModel(ModelKey key) { - playerModel = ModelWrapper.of(key); - return playerModel; + return setModel(ModelWrapper.of(key)); } public ModelWrapper setModel(ModelWrapper wrapper) { diff --git a/src/main/java/com/minelittlepony/client/render/entity/PonyRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/PonyRenderer.java index 7f7d5fd0..94f5136a 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/PonyRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/PonyRenderer.java @@ -1,6 +1,5 @@ package com.minelittlepony.client.render.entity; -import com.minelittlepony.api.model.IUnicorn; import com.minelittlepony.api.pony.IPony; import com.minelittlepony.client.MineLittlePony; import com.minelittlepony.client.model.ClientPonyModel; @@ -127,7 +126,7 @@ public abstract class PonyRenderer return MineLittlePony.getInstance().getManager().getPony(findTexture(entity)); } - public abstract static class Caster & IUnicorn> extends PonyRenderer { + public abstract static class Caster> extends PonyRenderer { public Caster(EntityRendererFactory.Context context, ModelKey key) { super(context, key); diff --git a/src/main/java/com/minelittlepony/client/render/entity/npc/AbstractNpcRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/npc/AbstractNpcRenderer.java index 37bf4ce1..8be37857 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/npc/AbstractNpcRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/npc/AbstractNpcRenderer.java @@ -1,37 +1,38 @@ package com.minelittlepony.client.render.entity.npc; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.EntityRendererFactory; -import net.minecraft.client.render.entity.model.ModelWithHat; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.mob.MobEntity; import net.minecraft.util.Identifier; import net.minecraft.village.VillagerDataContainer; import net.minecraft.village.VillagerProfession; -import com.minelittlepony.api.model.IUnicorn; import com.minelittlepony.api.model.gear.IGear; +import com.minelittlepony.api.pony.meta.Race; import com.minelittlepony.api.pony.meta.Wearable; -import com.minelittlepony.client.model.ClientPonyModel; +import com.minelittlepony.client.model.*; import com.minelittlepony.client.render.entity.PonyRenderer; -import com.minelittlepony.mson.api.ModelKey; +import java.util.HashMap; +import java.util.Map; -abstract class AbstractNpcRenderer< - T extends MobEntity & VillagerDataContainer, - M extends ClientPonyModel & IUnicorn & ModelWithHat> extends PonyRenderer.Caster { +abstract class AbstractNpcRenderer extends PonyRenderer.Caster> { private final TextureSupplier baseTextures; private final String entityType; - public AbstractNpcRenderer(EntityRendererFactory.Context context, ModelKey key, String type, TextureSupplier formatter) { - super(context, key); + private final Map>> models = new HashMap<>(); + public AbstractNpcRenderer(EntityRendererFactory.Context context, String type, TextureSupplier formatter) { + super(context, ModelType.getPlayerModel(Race.EARTH).getKey(false)); entityType = type; baseTextures = new PonyTextures<>(formatter); addFeature(new NpcClothingFeature<>(this, entityType)); } @Override - public boolean shouldRender(M model, T entity, Wearable wearable, IGear gear) { + public boolean shouldRender(ClientPonyModel model, T entity, Wearable wearable, IGear gear) { boolean special = PonyTextures.isBestPony(entity); @@ -52,6 +53,20 @@ abstract class AbstractNpcRenderer< return super.shouldRender(model, entity, wearable, gear); } + public void render(T entity, float entityYaw, float tickDelta, MatrixStack stack, VertexConsumerProvider renderContext, int lightUv) { + model = manager.setModel(models.computeIfAbsent(getEntityPony(entity).getRace(false), this::createModel)).body(); + + super.render(entity, entityYaw, tickDelta, stack, renderContext, lightUv); + } + + private ModelWrapper> createModel(Race race) { + return ModelWrapper.>of(ModelType.getPlayerModel(race).getKey(false), this::initializeModel); + } + + protected void initializeModel(ClientPonyModel model) { + + } + @Override public Identifier getDefaultTexture(T villager, Wearable wearable) { if (wearable == Wearable.SADDLE_BAGS) { diff --git a/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java index 0008fe3a..5c963b8f 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/npc/VillagerPonyRenderer.java @@ -1,19 +1,36 @@ package com.minelittlepony.client.render.entity.npc; -import com.minelittlepony.client.model.ModelType; -import com.minelittlepony.client.model.entity.VillagerPonyModel; - import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.passive.MerchantEntity; import net.minecraft.entity.passive.VillagerEntity; +import net.minecraft.util.math.MathHelper; -public class VillagerPonyRenderer extends AbstractNpcRenderer> { +import com.minelittlepony.client.model.ClientPonyModel; + +public class VillagerPonyRenderer extends AbstractNpcRenderer { private static final String TYPE = "villager"; private static final TextureSupplier FORMATTER = TextureSupplier.formatted("minelittlepony", "textures/entity/villager/%s.png"); public VillagerPonyRenderer(EntityRendererFactory.Context context) { - super(context, ModelType.VILLAGER, TYPE, FORMATTER); + super(context, TYPE, FORMATTER); + } + + @Override + protected void initializeModel(ClientPonyModel model) { + model.onSetModelAngles((m, move, swing, ticks, entity) -> { + m.getAttributes().visualHeight += PonyTextures.isCrownPony(entity) ? 0.3F : -0.1F; + + boolean isHeadRolling = entity instanceof MerchantEntity && ((MerchantEntity)entity).getHeadRollingTimeLeft() > 0; + + if (isHeadRolling) { + m.head.roll = 0.3F * MathHelper.sin(0.45F * ticks); + m.head.pitch = 0.4F; + } else { + m.head.roll = 0; + } + }); } @Override diff --git a/src/main/java/com/minelittlepony/client/render/entity/npc/ZomponyVillagerRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/npc/ZomponyVillagerRenderer.java index eea1bf8a..0b014d27 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/npc/ZomponyVillagerRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/npc/ZomponyVillagerRenderer.java @@ -1,19 +1,31 @@ package com.minelittlepony.client.render.entity.npc; -import com.minelittlepony.client.model.ModelType; -import com.minelittlepony.client.model.entity.ZomponyVillagerModel; - import net.minecraft.client.render.entity.EntityRendererFactory; +import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.mob.ZombieVillagerEntity; -public class ZomponyVillagerRenderer extends AbstractNpcRenderer { +import com.minelittlepony.client.model.ClientPonyModel; +import com.minelittlepony.client.model.IMobModel; + +public class ZomponyVillagerRenderer extends AbstractNpcRenderer { private static final String TYPE = "zombie_villager"; private static final TextureSupplier FORMATTER = TextureSupplier.formatted("minelittlepony", "textures/entity/zombie_villager/zombie_%s.png"); public ZomponyVillagerRenderer(EntityRendererFactory.Context context) { - super(context, ModelType.ZOMBIE_VILLAGER, TYPE, FORMATTER); + super(context, TYPE, FORMATTER); + } + + @Override + protected void initializeModel(ClientPonyModel model) { + model.onSetModelAngles((m, move, swing, ticks, entity) -> { + m.getAttributes().visualHeight += PonyTextures.isCrownPony(entity) ? 0.3F : -0.1F; + + if (m.rightArmPose == ArmPose.EMPTY) { + IMobModel.rotateUndeadArms(m, move, ticks); + } + }); } @Override