diff --git a/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java b/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java index cf678772..8d2aa0fe 100644 --- a/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java +++ b/src/main/java/com/minelittlepony/client/model/AbstractPonyModel.java @@ -107,6 +107,12 @@ public abstract class AbstractPonyModel extends ClientPo upperTorsoOverlay.copyTransform(upperTorso); } + public void setHeadRotation(float animationProgress, float yaw, float pitch) { + head.yaw = yaw * ((float)Math.PI / 180); + head.pitch = pitch * ((float)Math.PI / 180); + hat.copyTransform(head); + } + /** * Aligns legs to a sneaky position. */ @@ -548,11 +554,11 @@ public abstract class AbstractPonyModel extends ClientPo stack.pop(); } - protected void renderHead(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { + public void renderHead(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { head.render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); } - protected void renderHelmet(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { + public void renderHelmet(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { hat.render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); } diff --git a/src/main/java/com/minelittlepony/client/model/ModelType.java b/src/main/java/com/minelittlepony/client/model/ModelType.java index da25dfe5..1a337db2 100644 --- a/src/main/java/com/minelittlepony/client/model/ModelType.java +++ b/src/main/java/com/minelittlepony/client/model/ModelType.java @@ -72,7 +72,6 @@ public final class ModelType { public static final ModelKey PARASPRITE = register("parasprite", ParaspriteModel::new); public static final ModelKey> ELYTRA = register("elytra", PonyElytra::new); - public static final ModelKey SKULL = register("skull", PonySkullModel::new); public static final ModelKey ARMOUR_STAND = register("armour_stand", PonyArmourStandModel::new); public static final ModelKey> ARMOUR_INNER = register("armour_inner", PonyArmourModel::new); diff --git a/src/main/java/com/minelittlepony/client/model/ModelWrapper.java b/src/main/java/com/minelittlepony/client/model/ModelWrapper.java index 4389b227..7f5778bf 100644 --- a/src/main/java/com/minelittlepony/client/model/ModelWrapper.java +++ b/src/main/java/com/minelittlepony/client/model/ModelWrapper.java @@ -12,24 +12,23 @@ import com.minelittlepony.mson.api.ModelKey; /** * Container class for the various models and their associated piece of armour. */ -public class ModelWrapper implements IModelWrapper { - - private final M body; - - private final IArmour armor; +public record ModelWrapper ( + M body, + IArmour armor +) implements IModelWrapper { /** * Creates a new model wrapper to contain the given pony. */ - @SuppressWarnings("unchecked") - public ModelWrapper(ModelKey key) { - body = (M)key.createModel(); - armor = body.createArmour(); + public static ModelWrapper of(ModelKey key) { + M body = key.createModel(); + IArmour armor = body.createArmour(); armor.applyMetadata(body.getMetadata()); + return new ModelWrapper<>(body, armor); } public M getBody() { - return body; + return body(); } /** @@ -37,7 +36,7 @@ public class ModelWrapper implements I */ @SuppressWarnings("unchecked") public IArmour getArmor() { - return (IArmour)armor; + return (IArmour)armor(); } @Override diff --git a/src/main/java/com/minelittlepony/client/model/PonySkullModel.java b/src/main/java/com/minelittlepony/client/model/PonySkullModel.java index 883bf679..5657383d 100644 --- a/src/main/java/com/minelittlepony/client/model/PonySkullModel.java +++ b/src/main/java/com/minelittlepony/client/model/PonySkullModel.java @@ -44,8 +44,8 @@ public class PonySkullModel extends SkullEntityModel implements MsonModel, ICapi } @Override - public void setHeadRotation(float poweredTicks, float yaw, float pitch) { - super.setHeadRotation(poweredTicks, yaw, pitch); + public void setHeadRotation(float animationProgress, float yaw, float pitch) { + super.setHeadRotation(animationProgress, yaw, pitch); hair.yaw = head.yaw; hair.pitch = head.pitch; } diff --git a/src/main/java/com/minelittlepony/client/model/entity/race/EarthPonyModel.java b/src/main/java/com/minelittlepony/client/model/entity/race/EarthPonyModel.java index 0a79f632..4e37fe63 100644 --- a/src/main/java/com/minelittlepony/client/model/entity/race/EarthPonyModel.java +++ b/src/main/java/com/minelittlepony/client/model/entity/race/EarthPonyModel.java @@ -39,6 +39,12 @@ public class EarthPonyModel extends AbstractPonyModel cape.pivotY = sneaking ? 2 : riding ? -4 : 0; } + @Override + public void setHeadRotation(float animationProgress, float yaw, float pitch) { + super.setHeadRotation(animationProgress, yaw, pitch); + snout.setGender(getMetadata().getGender()); + } + @Override protected void shakeBody(float move, float swing, float bodySwing, float ticks) { super.shakeBody(move, swing, bodySwing, ticks); diff --git a/src/main/java/com/minelittlepony/client/model/entity/race/UnicornModel.java b/src/main/java/com/minelittlepony/client/model/entity/race/UnicornModel.java index c15d20b3..1077ad8c 100644 --- a/src/main/java/com/minelittlepony/client/model/entity/race/UnicornModel.java +++ b/src/main/java/com/minelittlepony/client/model/entity/race/UnicornModel.java @@ -64,7 +64,7 @@ public class UnicornModel extends EarthPonyModel impl } @Override - protected void renderHead(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { + public void renderHead(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { super.renderHead(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); if (hasHorn()) { diff --git a/src/main/java/com/minelittlepony/client/pony/PonyManager.java b/src/main/java/com/minelittlepony/client/pony/PonyManager.java index a451f66d..a44f636b 100644 --- a/src/main/java/com/minelittlepony/client/pony/PonyManager.java +++ b/src/main/java/com/minelittlepony/client/pony/PonyManager.java @@ -6,6 +6,7 @@ import com.google.common.cache.LoadingCache; import com.minelittlepony.api.pony.IPony; import com.minelittlepony.api.pony.IPonyManager; import com.minelittlepony.client.MineLittlePony; +import com.minelittlepony.client.render.blockentity.skull.PonySkullRenderer; import com.minelittlepony.settings.PonyConfig; import com.minelittlepony.settings.PonyLevel; import org.jetbrains.annotations.Nullable; @@ -128,6 +129,7 @@ public class PonyManager implements IPonyManager, IdentifiableResourceReloadList clientProfiler.push("Reloading all background ponies"); poniesCache.invalidateAll(); backgroundPonyList.reloadAll(sender); + PonySkullRenderer.reload(); clientProfiler.pop(); clientProfiler.endTick(); }, clientExecutor); diff --git a/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java b/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java index bb3c9054..8c1ed3eb 100644 --- a/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java +++ b/src/main/java/com/minelittlepony/client/render/EquineRenderManager.java @@ -137,8 +137,7 @@ public class EquineRenderManager setModel(ModelKey key) { - playerModel = new ModelWrapper<>(key); - + playerModel = ModelWrapper.of(key); return playerModel; } diff --git a/src/main/java/com/minelittlepony/client/render/blockentity/skull/AbstractPonySkull.java b/src/main/java/com/minelittlepony/client/render/blockentity/skull/AbstractPonySkull.java deleted file mode 100644 index 714ae5f6..00000000 --- a/src/main/java/com/minelittlepony/client/render/blockentity/skull/AbstractPonySkull.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.minelittlepony.client.render.blockentity.skull; - -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.util.math.MatrixStack; - -import com.minelittlepony.client.model.PonySkullModel; -import com.minelittlepony.client.render.blockentity.skull.PonySkullRenderer.ISkull; -import com.minelittlepony.api.pony.IPony; -import com.minelittlepony.client.model.ModelType; - -public abstract class AbstractPonySkull implements ISkull { - - private PonySkullModel ponyHead = ModelType.SKULL.createModel(); - - @Override - public void setAngles(float angle, float poweredTicks) { - ponyHead.setHeadRotation(poweredTicks, angle, 0); - } - - @Override - public void bindPony(IPony pony) { - ponyHead.metadata = pony.getMetadata(); - } - - @Override - public void render(MatrixStack stack, VertexConsumer vertices, int lightUv, int overlayUv, float red, float green, float blue, float alpha) { - ponyHead.render(stack, vertices, lightUv, overlayUv, red, green, blue, alpha); - } -} diff --git a/src/main/java/com/minelittlepony/client/render/blockentity/skull/MobSkull.java b/src/main/java/com/minelittlepony/client/render/blockentity/skull/MobSkull.java index 1cc767c5..05593abc 100644 --- a/src/main/java/com/minelittlepony/client/render/blockentity/skull/MobSkull.java +++ b/src/main/java/com/minelittlepony/client/render/blockentity/skull/MobSkull.java @@ -1,20 +1,34 @@ package com.minelittlepony.client.render.blockentity.skull; +import com.google.common.base.Suppliers; +import com.minelittlepony.api.pony.IPony; +import com.minelittlepony.client.model.AbstractPonyModel; import com.minelittlepony.client.render.MobRenderers; +import com.minelittlepony.client.render.blockentity.skull.PonySkullRenderer.ISkull; +import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.settings.PonyConfig; import com.mojang.authlib.GameProfile; + +import java.util.function.Supplier; + +import net.minecraft.client.model.ModelPart; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3f; import org.jetbrains.annotations.Nullable; -public class MobSkull extends AbstractPonySkull { - +public class MobSkull implements ISkull { private final Identifier texture; private final MobRenderers type; - MobSkull(Identifier texture, MobRenderers type) { + private final Supplier> ponyHead; + + MobSkull(Identifier texture, MobRenderers type, ModelKey> modelKey) { this.texture = texture; this.type = type; + this.ponyHead = Suppliers.memoize(modelKey::createModel); } @Override @@ -26,4 +40,28 @@ public class MobSkull extends AbstractPonySkull { public Identifier getSkinResource(@Nullable GameProfile profile) { return texture; } + + @Override + public boolean bindPony(IPony pony) { + ponyHead.get().setMetadata(pony.getMetadata()); + return true; + } + + @Override + public void setAngles(float yaw, float animationProgress) { + Vec3f v = new Vec3f(0, -2, 1.99F); + v.rotate(Vec3f.POSITIVE_Y.getDegreesQuaternion(yaw)); + ModelPart head = ponyHead.get().getHead(); + head.pivotX = v.getX(); + head.pivotY = v.getY(); + head.pivotZ = v.getZ(); + ponyHead.get().setVisible(true); + ponyHead.get().setHeadRotation(animationProgress, yaw, 0); + } + + @Override + public void render(MatrixStack stack, VertexConsumer vertices, int lightUv, int overlayUv, float red, float green, float blue, float alpha) { + ponyHead.get().renderHead(stack, vertices, lightUv, overlayUv, red, green, blue, alpha); + ponyHead.get().renderHelmet(stack, vertices, lightUv, overlayUv, red, green, blue, alpha); + } } diff --git a/src/main/java/com/minelittlepony/client/render/blockentity/skull/PlayerPonySkull.java b/src/main/java/com/minelittlepony/client/render/blockentity/skull/PlayerPonySkull.java new file mode 100644 index 00000000..caf02671 --- /dev/null +++ b/src/main/java/com/minelittlepony/client/render/blockentity/skull/PlayerPonySkull.java @@ -0,0 +1,82 @@ +package com.minelittlepony.client.render.blockentity.skull; + +import com.google.common.base.Suppliers; +import com.minelittlepony.api.pony.IPony; +import com.minelittlepony.api.pony.meta.Race; +import com.minelittlepony.client.SkinsProxy; +import com.minelittlepony.client.model.*; +import com.minelittlepony.client.render.blockentity.skull.PonySkullRenderer.ISkull; +import com.minelittlepony.settings.PonyConfig; +import com.minelittlepony.settings.PonyLevel; +import com.mojang.authlib.GameProfile; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.util.DefaultSkinHelper; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.Identifier; +import net.minecraft.util.dynamic.DynamicSerializableUuid; +import net.minecraft.util.math.Vec3f; + +import org.jetbrains.annotations.Nullable; + +public class PlayerPonySkull implements ISkull { + private AbstractPonyModel ponyHead; + private final Map>, AbstractPonyModel> modelCache = new HashMap<>(); + + private final Supplier deadMau5 = Suppliers.memoize(ModelType.DJ_PON_3::createModel); + + @Override + public boolean canRender(PonyConfig config) { + return config.ponyskulls.get() && config.ponyLevel.get() != PonyLevel.HUMANS; + } + + @Override + public Identifier getSkinResource(@Nullable GameProfile profile) { + deadMau5.get().setVisible(profile != null && "deadmau5".equals(profile.getName())); + + if (profile != null) { + Identifier skin = SkinsProxy.instance.getSkinTexture(profile); + + if (skin != null) { + return skin; + } + + return DefaultSkinHelper.getTexture(DynamicSerializableUuid.getUuidFromProfile(profile)); + } + + return DefaultSkinHelper.getTexture(); + } + + @Override + public boolean bindPony(IPony pony) { + Race race = pony.getRace(false); + if (race.isHuman()) { + return false; + } + ponyHead = modelCache.computeIfAbsent(ModelType.getPlayerModel(race), key -> key.getKey(false).createModel()); + ponyHead.setMetadata(pony.getMetadata()); + return true; + } + + @Override + public void setAngles(float yaw, float animationProgress) { + Vec3f v = new Vec3f(0, -2, 2); + v.rotate(Vec3f.POSITIVE_Y.getDegreesQuaternion(yaw)); + ponyHead.getHead().pivotX = v.getX(); + ponyHead.getHead().pivotY = v.getY(); + ponyHead.getHead().pivotZ = v.getZ(); + ponyHead.setVisible(true); + ponyHead.setHeadRotation(animationProgress, yaw, 0); + } + + @Override + public void render(MatrixStack stack, VertexConsumer vertices, int lightUv, int overlayUv, float red, float green, float blue, float alpha) { + ponyHead.renderHead(stack, vertices, lightUv, overlayUv, red, green, blue, alpha); + ponyHead.renderHelmet(stack, vertices, lightUv, overlayUv, red, green, blue, alpha); + deadMau5.get().render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); + } +} diff --git a/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkull.java b/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkull.java deleted file mode 100644 index 12649526..00000000 --- a/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkull.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.minelittlepony.client.render.blockentity.skull; - -import com.minelittlepony.client.SkinsProxy; -import com.minelittlepony.client.model.DJPon3EarsModel; -import com.minelittlepony.client.model.ModelType; -import com.minelittlepony.settings.PonyConfig; -import com.minelittlepony.settings.PonyLevel; -import com.mojang.authlib.GameProfile; - -import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.util.DefaultSkinHelper; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.Identifier; -import net.minecraft.util.dynamic.DynamicSerializableUuid; - -import org.jetbrains.annotations.Nullable; - -public class PonySkull extends AbstractPonySkull { - - private final DJPon3EarsModel deadMau5 = ModelType.DJ_PON_3.createModel(); - - @Override - public boolean canRender(PonyConfig config) { - return config.ponyLevel.get() != PonyLevel.HUMANS; - } - - @Override - public Identifier getSkinResource(@Nullable GameProfile profile) { - deadMau5.setVisible(profile != null && "deadmau5".equals(profile.getName())); - - if (profile != null) { - Identifier skin = SkinsProxy.instance.getSkinTexture(profile); - - if (skin != null) { - return skin; - } - - return DefaultSkinHelper.getTexture(DynamicSerializableUuid.getUuidFromProfile(profile)); - } - - return DefaultSkinHelper.getTexture(); - } - - @Override - public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha) { - super.render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); - deadMau5.render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); - } -} diff --git a/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkullRenderer.java b/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkullRenderer.java index 72319db2..eb9064e2 100644 --- a/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkullRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/blockentity/skull/PonySkullRenderer.java @@ -3,6 +3,7 @@ package com.minelittlepony.client.render.blockentity.skull; import com.google.common.collect.Maps; import com.minelittlepony.api.pony.IPony; import com.minelittlepony.client.MineLittlePony; +import com.minelittlepony.client.model.ModelType; import com.minelittlepony.client.render.LevitatingItemRenderer; import com.minelittlepony.client.render.MobRenderers; import com.minelittlepony.client.render.entity.SkeleponyRenderer; @@ -17,7 +18,6 @@ import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; -import net.minecraft.util.Util; import net.minecraft.util.math.Direction; import java.util.Map; @@ -28,17 +28,23 @@ import org.jetbrains.annotations.Nullable; */ public class PonySkullRenderer { - - private static final Map SKULLS = Util.make(Maps.newHashMap(), (skullMap) -> { - skullMap.put(SkullBlock.Type.SKELETON, new MobSkull(SkeleponyRenderer.SKELETON, MobRenderers.SKELETON)); - skullMap.put(SkullBlock.Type.WITHER_SKELETON, new MobSkull(SkeleponyRenderer.WITHER, MobRenderers.SKELETON)); - skullMap.put(SkullBlock.Type.ZOMBIE, new MobSkull(ZomponyRenderer.ZOMBIE, MobRenderers.ZOMBIE)); - skullMap.put(SkullBlock.Type.PLAYER, new PonySkull()); - }); + private static final Map SKULLS = Maps.newHashMap(); private static ISkull selectedSkull; private static Identifier selectedSkin; + public static void reload() { + SKULLS.clear(); + loadSkulls(SKULLS); + } + + private static void loadSkulls(Map skullMap) { + skullMap.put(SkullBlock.Type.SKELETON, new MobSkull(SkeleponyRenderer.SKELETON, MobRenderers.SKELETON, ModelType.SKELETON)); + skullMap.put(SkullBlock.Type.WITHER_SKELETON, new MobSkull(SkeleponyRenderer.WITHER, MobRenderers.SKELETON, ModelType.ENDERMAN)); + skullMap.put(SkullBlock.Type.ZOMBIE, new MobSkull(ZomponyRenderer.ZOMBIE, MobRenderers.ZOMBIE, ModelType.ZOMBIE)); + skullMap.put(SkullBlock.Type.PLAYER, new PlayerPonySkull()); + } + public static RenderLayer getSkullRenderLayer(SkullBlock.SkullType skullType, @Nullable GameProfile profile) { selectedSkull = null; selectedSkin = null; @@ -55,7 +61,7 @@ public class PonySkullRenderer { } public static boolean renderSkull(@Nullable Direction direction, - float angle, float poweredTicks, + float yaw, float animationProgress, MatrixStack stack, VertexConsumerProvider renderContext, RenderLayer layer, int lightUv) { @@ -63,7 +69,9 @@ public class PonySkullRenderer { return false; } - selectedSkull.bindPony(MineLittlePony.getInstance().getManager().getPony(selectedSkin)); + if (!selectedSkull.bindPony(MineLittlePony.getInstance().getManager().getPony(selectedSkin))) { + return false; + } stack.push(); @@ -82,7 +90,7 @@ public class PonySkullRenderer { VertexConsumer vertices = renderContext.getBuffer(layer); - selectedSkull.setAngles(angle, poweredTicks); + selectedSkull.setAngles(yaw, animationProgress); selectedSkull.render(stack, vertices, lightUv, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1); stack.pop(); @@ -105,6 +113,6 @@ public class PonySkullRenderer { Identifier getSkinResource(@Nullable GameProfile profile); - void bindPony(IPony pony); + boolean bindPony(IPony pony); } } diff --git a/src/main/java/com/minelittlepony/client/render/entity/PlayerSeaponyRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/PlayerSeaponyRenderer.java index 9752ebf9..2909fbcc 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/PlayerSeaponyRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/PlayerSeaponyRenderer.java @@ -22,7 +22,7 @@ public class PlayerSeaponyRenderer extends PlayerPonyRenderer { public PlayerSeaponyRenderer(EntityRendererFactory.Context context, boolean slim, ModelKey> key) { super(context, slim, key); - normalPony = new ModelWrapper<>(ModelType.>getPlayerModel(Race.UNICORN).getKey(slim)); + normalPony = ModelWrapper.of(ModelType.>getPlayerModel(Race.UNICORN).getKey(slim)); seapony = this.manager.getModelWrapper(); } diff --git a/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java b/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java index d4b4c369..169dd778 100644 --- a/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java +++ b/src/main/java/com/minelittlepony/client/render/entity/PonyStandRenderer.java @@ -54,7 +54,7 @@ public class PonyStandRenderer extends ArmorStandEntityRenderer { } class Armour extends ArmorFeatureRenderer { - private final ModelWrapper> pony = new ModelWrapper<>(ModelType.EARTH_PONY.getKey(false)); + private final ModelWrapper> pony = ModelWrapper.of(ModelType.EARTH_PONY.getKey(false)); public Armour(FeatureRendererContext renderer, EntityRendererFactory.Context context) { super(renderer, diff --git a/src/main/resources/assets/minelittlepony/models/skull.json b/src/main/resources/assets/minelittlepony/models/skull.json deleted file mode 100644 index 92571848..00000000 --- a/src/main/resources/assets/minelittlepony/models/skull.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "texture": {"w": 64, "h": 64}, - "data": { - "head": { - "cubes": [ - {"from": [-4, -8, -4], "size": [8, 8, 8]} - ], - "children": { - "snout": { - "type": "mson:slot", - "name": "snout", - "locals": { - "x": 0, - "y": -2, - "z": 2 - }, - "implementation": "com.minelittlepony.client.model.part.PonySnout", - "data": "minelittlepony:components/snout" - }, - "ears": { - "type": "mson:slot", - "name": "ears", - "locals": { - "x": 0, - "y": -2, - "z": 2 - }, - "implementation": "com.minelittlepony.client.model.part.PonyEars", - "data": "minelittlepony:components/ears" - }, - "horn": { - "type": "mson:slot", - "name": "horn", - "locals": { - "x": 0, - "y": -2, - "z": 2 - }, - "implementation": "com.minelittlepony.client.model.part.UnicornHorn", - "data": "minelittlepony:components/horn" - } - } - }, - "hair": { - "texture": {"u": 32}, - "cubes": [ - {"from": [-4, -8, -4], "size": [8, 8, 8], "dilate": 0.25} - ] - } - } -}