diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/AmuletGear.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/AmuletGear.java new file mode 100644 index 00000000..9ddeceb1 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/AmuletGear.java @@ -0,0 +1,66 @@ +package com.minelittlepony.unicopia.client.minelittlepony; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +import com.minelittlepony.api.model.BodyPart; +import com.minelittlepony.api.model.IModel; +import com.minelittlepony.api.model.gear.IGear; +import com.minelittlepony.unicopia.client.render.AmuletFeatureRenderer.AmuletModel; +import com.minelittlepony.unicopia.item.AmuletItem; + +import net.minecraft.client.model.Dilation; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.util.Identifier; +import net.minecraft.util.registry.Registry; + +class AmuletGear extends AmuletModel implements IGear { + + private final Map textures = new HashMap<>(); + + private IModel model; + + public AmuletGear() { + super(AmuletModel.getData(new Dilation(0.3F)).createModel()); + } + + @Override + public boolean canRender(IModel model, Entity entity) { + return entity instanceof LivingEntity living + && living.getEquippedStack(EquipmentSlot.CHEST).getItem() instanceof AmuletItem; + } + + @Override + public BodyPart getGearLocation() { + return BodyPart.BODY; + } + + @Override + public Identifier getTexture(T entity, Context context) { + return textures.computeIfAbsent(Registry.ITEM.getId(((LivingEntity)entity).getEquippedStack(EquipmentSlot.CHEST).getItem()), id -> new Identifier(id.getNamespace(), "textures/models/armor/" + id.getPath() + ".png")); + } + + @Override + public void setModelAttributes(IModel model, Entity entity) { + this.model = model; + + if (model instanceof BipedEntityModel biped) { + setAngles((LivingEntity)entity, biped); + } + } + + @Override + public void render(MatrixStack stack, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha, UUID interpolatorId) { + BangleGear.popAndApply(model, BodyPart.BODY, stack); + + stack.translate(0, 0.25, 0); + + render(stack, consumer, light, overlay, red, green, blue, 1); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BangleGear.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BangleGear.java new file mode 100644 index 00000000..51e92cc7 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/BangleGear.java @@ -0,0 +1,102 @@ +package com.minelittlepony.unicopia.client.minelittlepony; + +import java.util.UUID; + +import com.minelittlepony.api.model.BodyPart; +import com.minelittlepony.api.model.IModel; +import com.minelittlepony.api.model.gear.IGear; +import com.minelittlepony.common.util.Color; +import com.minelittlepony.unicopia.client.render.BraceletFeatureRenderer; +import com.minelittlepony.unicopia.client.render.BraceletFeatureRenderer.BraceletModel; +import com.minelittlepony.unicopia.item.FriendshipBraceletItem; +import com.minelittlepony.unicopia.item.GlowableItem; + +import net.minecraft.client.model.Dilation; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.entity.model.BipedEntityModel; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.item.DyeableItem; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; + +class BangleGear implements IGear { + private boolean glowing; + private int color; + private boolean alex; + + private BraceletModel steveModel; + private BraceletModel alexModel; + + private IModel model; + + public BangleGear() { + Dilation dilation = new Dilation(0.3F); + steveModel = new BraceletModel(BraceletModel.getData(dilation, false, -1, 4, 0).createModel(), false); + alexModel = new BraceletModel(BraceletModel.getData(dilation, true, -1, 4, 0).createModel(), true); + } + + @Override + public boolean canRender(IModel model, Entity entity) { + return entity instanceof LivingEntity living + && living.getEquippedStack(EquipmentSlot.CHEST).getItem() instanceof FriendshipBraceletItem; + } + + @Override + public BodyPart getGearLocation() { + return BodyPart.LEGS; + } + + @Override + public Identifier getTexture(T entity, Context context) { + return BraceletFeatureRenderer.TEXTURE; + } + + @Override + public void setModelAttributes(IModel model, Entity entity) { + + Dilation dilation = new Dilation(0.3F); + steveModel = new BraceletModel(BraceletModel.getData(dilation, false, -1, 4, 0).createModel(), false); + alexModel = new BraceletModel(BraceletModel.getData(dilation, true, -1, 4, 0).createModel(), true); + + ItemStack item = ((LivingEntity)entity).getEquippedStack(EquipmentSlot.CHEST); + + color = ((DyeableItem)item.getItem()).getColor(item); + glowing = ((GlowableItem)item.getItem()).isGlowing(item); + alex = entity instanceof ClientPlayerEntity && ((ClientPlayerEntity)entity).getModel().startsWith("slim"); + this.model = model; + + BraceletModel m = alex ? alexModel : steveModel; + + if (model instanceof BipedEntityModel biped) { + m.setAngles(biped); + } + m.setVisible(((LivingEntity)entity).getMainArm()); + } + + @Override + public void render(MatrixStack stack, VertexConsumer consumer, int light, int overlay, float red, float green, float blue, float alpha, UUID interpolatorId) { + popAndApply(model, BodyPart.LEGS, stack); + + BraceletModel m = alex ? alexModel : steveModel; + + m.render(stack, consumer, glowing ? 0x0F00F0 : light, overlay, Color.r(color), Color.g(color), Color.b(color), 1); + } + + /** + * Discards and applies default transformations without body part rotations. + *

+ * TODO: this is a workaround to undo the {@code model.getBodyPart(part).rotate(stack)} in GearFeature. + * That's useful for things that render on the head or body, but not so much if you're on the legs or tail, + * since the default implementation falls to body rotation, which we don't want + */ + static void popAndApply(IModel model, BodyPart part, MatrixStack matrices) { + matrices.pop(); + matrices.push(); + // re-apply leg transformation + model.transform(part, matrices); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java index 6a099775..c3a6ce78 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java +++ b/src/main/java/com/minelittlepony/unicopia/client/minelittlepony/Main.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client.minelittlepony; import com.minelittlepony.api.model.IModel; import com.minelittlepony.api.model.ModelAttributes; import com.minelittlepony.api.model.fabric.PonyModelPrepareCallback; +import com.minelittlepony.api.model.gear.IGear; import com.minelittlepony.unicopia.Owned; import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.entity.player.Pony; @@ -19,6 +20,8 @@ public class Main implements ClientModInitializer { @Override public void onInitializeClient() { PonyModelPrepareCallback.EVENT.register(this::onPonyModelPrepared); + IGear.register(BangleGear::new); + IGear.register(AmuletGear::new); } @SuppressWarnings("unchecked") diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/AmuletFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/AmuletFeatureRenderer.java index 51c273f6..5c9537e5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/AmuletFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/AmuletFeatureRenderer.java @@ -55,7 +55,7 @@ public class AmuletFeatureRenderer implements AccessoryF } } - static class AmuletModel extends Model { + public static class AmuletModel extends Model { private final ModelPart amulet; diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/BraceletFeatureRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/render/BraceletFeatureRenderer.java index 2f551b85..6552dff5 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/BraceletFeatureRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/BraceletFeatureRenderer.java @@ -32,7 +32,7 @@ import net.minecraft.util.Identifier; public class BraceletFeatureRenderer implements AccessoryFeatureRenderer.Feature { - private static final Identifier TEXTURE = new Identifier("unicopia", "textures/models/armor/bracelet.png"); + public static final Identifier TEXTURE = new Identifier("unicopia", "textures/models/armor/bracelet.png"); private final BraceletModel steveModel; private final BraceletModel alexModel; @@ -42,8 +42,8 @@ public class BraceletFeatureRenderer implements Accessor public BraceletFeatureRenderer(FeatureRendererContext> context) { this.context = context; Dilation dilation = new Dilation(0.3F); - steveModel = new BraceletModel(BraceletModel.getData(dilation, false).createModel(), false); - alexModel = new BraceletModel(BraceletModel.getData(dilation, true).createModel(), true); + steveModel = new BraceletModel(BraceletModel.getData(dilation, false, 0, 0, 0).createModel(), false); + alexModel = new BraceletModel(BraceletModel.getData(dilation, true, 0, 0, 0).createModel(), true); } @Override @@ -77,7 +77,7 @@ public class BraceletFeatureRenderer implements Accessor } } - static class BraceletModel extends Model { + public static class BraceletModel extends Model { private final ModelPart leftArm; private final ModelPart rightArm; @@ -91,12 +91,12 @@ public class BraceletFeatureRenderer implements Accessor this.rightArm = tree.getChild(EntityModelPartNames.RIGHT_ARM); } - public static TexturedModelData getData(Dilation dilation, boolean alex) { + public static TexturedModelData getData(Dilation dilation, boolean alex, int x, int y, int z) { ModelData data = new ModelData(); ModelPartData root = data.getRoot(); - root.addChild(EntityModelPartNames.RIGHT_ARM, ModelPartBuilder.create().uv(0, alex ? 6 : 0).cuboid(-3, 7, -2, alex ? 3 : 4, 2, 4, dilation), ModelTransform.NONE); - root.addChild(EntityModelPartNames.LEFT_ARM, ModelPartBuilder.create().mirrored().uv(0, alex ? 6 : 0).cuboid(-1, 7, -2, alex ? 3 : 4, 2, 4, dilation), ModelTransform.NONE); + root.addChild(EntityModelPartNames.RIGHT_ARM, ModelPartBuilder.create().uv(0, alex ? 6 : 0).cuboid(-3 + x, 7 + y, -2 + z, alex ? 3 : 4, 2, 4, dilation), ModelTransform.NONE); + root.addChild(EntityModelPartNames.LEFT_ARM, ModelPartBuilder.create().mirrored().uv(0, alex ? 6 : 0).cuboid(-1 - x, 7 + y, -2 + z, alex ? 3 : 4, 2, 4, dilation), ModelTransform.NONE); return TexturedModelData.of(data, 64, 32); }