Improve modding support for adding custom gear

This commit is contained in:
Sollace 2021-02-05 22:11:19 +02:00
parent eefb58e6b7
commit abfab92d78
19 changed files with 144 additions and 69 deletions

View file

@ -2,7 +2,7 @@ package com.minelittlepony.client.model;
import com.minelittlepony.client.model.armour.PonyArmourModel; import com.minelittlepony.client.model.armour.PonyArmourModel;
import com.minelittlepony.client.render.EquineRenderManager; import com.minelittlepony.client.render.EquineRenderManager;
import com.minelittlepony.model.capabilities.fabric.PonyModelPrepareCallback; import com.minelittlepony.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.meta.Race; import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.api.pony.meta.Sizes; import com.minelittlepony.api.pony.meta.Sizes;
import com.minelittlepony.client.model.armour.ArmourWrapper; import com.minelittlepony.client.model.armour.ArmourWrapper;

View file

@ -6,7 +6,7 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.util.Arm; import net.minecraft.util.Arm;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import com.minelittlepony.model.capabilities.fabric.PonyModelPrepareCallback; import com.minelittlepony.model.fabric.PonyModelPrepareCallback;
import com.minelittlepony.api.pony.IPony; import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.IPonyData; import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.api.pony.meta.Size; import com.minelittlepony.api.pony.meta.Size;

View file

@ -4,9 +4,9 @@ import net.minecraft.entity.LivingEntity;
import com.minelittlepony.api.pony.IPonyData; import com.minelittlepony.api.pony.IPonyData;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.IModelWrapper;
import com.minelittlepony.model.armour.IArmour; import com.minelittlepony.model.armour.IArmour;
import com.minelittlepony.model.armour.IEquestrianArmour; import com.minelittlepony.model.armour.IEquestrianArmour;
import com.minelittlepony.model.capabilities.IModelWrapper;
import com.minelittlepony.mson.api.ModelKey; import com.minelittlepony.mson.api.ModelKey;
/** /**

View file

@ -25,7 +25,7 @@ public abstract class AbstractGear extends Model implements IGear {
} }
@Override @Override
public void renderPart(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) { public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) {
render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha); render(stack, vertices, overlayUv, lightUv, red, green, blue, alpha);
} }

View file

@ -11,11 +11,13 @@ import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.common.util.Color; import com.minelittlepony.common.util.Color;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.PonyModelConstants;
import com.minelittlepony.model.gear.IRenderContext;
import java.util.Calendar; import java.util.Calendar;
import java.util.UUID; import java.util.UUID;
public class ChristmasHat extends AbstractGear { public class ChristmasHat extends AbstractGear implements PonyModelConstants {
private static boolean dayChecked = false; private static boolean dayChecked = false;
private static boolean dayResult = false; private static boolean dayResult = false;
@ -49,12 +51,12 @@ public class ChristmasHat extends AbstractGear {
} }
@Override @Override
public void setLivingAnimations(IModel model, Entity entity) { public void setModelAttributes(IModel model, Entity entity) {
tint = model.getMetadata().getGlowColor(); tint = model.getMetadata().getGlowColor();
} }
@Override @Override
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) { public void pose(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
float pi = PI * (float) Math.pow(swing, 16); float pi = PI * (float) Math.pow(swing, 16);
float mve = move * 0.6662f; float mve = move * 0.6662f;
@ -79,7 +81,7 @@ public class ChristmasHat extends AbstractGear {
} }
@Override @Override
public void renderPart(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) { public void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) {
if (tint != 0) { if (tint != 0) {
red = Color.r(tint); red = Color.r(tint);
green = Color.g(tint); green = Color.g(tint);

View file

@ -1,26 +0,0 @@
package com.minelittlepony.client.model.gear;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.model.IModel;
import com.minelittlepony.model.gear.IGear;
import javax.annotation.Nullable;
public interface IRenderContext<T extends Entity, M extends IModel> {
IRenderContext<?, ?> NULL = (e, g) -> null;
default boolean shouldRender(M model, T entity, Wearable wearable, IGear gear) {
return gear.canRender(model, entity);
}
@Nullable
default IModel getEntityModel() {
return null;
}
Identifier getDefaultTexture(T entity, Wearable wearable);
}

View file

@ -7,6 +7,7 @@ import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.gear.IRenderContext;
import com.minelittlepony.model.gear.IStackable; import com.minelittlepony.model.gear.IStackable;
public class Muffin extends AbstractGear implements IStackable { public class Muffin extends AbstractGear implements IStackable {

View file

@ -5,6 +5,8 @@ import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.IPegasus; import com.minelittlepony.model.IPegasus;
import com.minelittlepony.model.PonyModelConstants;
import com.minelittlepony.model.gear.IRenderContext;
import java.util.UUID; import java.util.UUID;
@ -15,7 +17,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
public class SaddleBags extends AbstractGear { public class SaddleBags extends AbstractGear implements PonyModelConstants {
public static final Identifier TEXTURE = new Identifier("minelittlepony", "textures/models/saddlebags.png"); public static final Identifier TEXTURE = new Identifier("minelittlepony", "textures/models/saddlebags.png");
@ -37,7 +39,7 @@ public class SaddleBags extends AbstractGear {
} }
@Override @Override
public void setLivingAnimations(IModel model, Entity entity) { public void setModelAttributes(IModel model, Entity entity) {
this.model = model; this.model = model;
hangLow = false; hangLow = false;
@ -48,7 +50,7 @@ public class SaddleBags extends AbstractGear {
} }
@Override @Override
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) { public void pose(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
float pi = PI * (float) Math.pow(swing, 16); float pi = PI * (float) Math.pow(swing, 16);
float mve = move * 0.6662f; float mve = move * 0.6662f;
@ -75,7 +77,7 @@ public class SaddleBags extends AbstractGear {
} }
@Override @Override
public void renderPart(MatrixStack stack, VertexConsumer renderContext, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) { public void render(MatrixStack stack, VertexConsumer renderContext, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId) {
dropAmount = model.getMetadata().getInterpolator(interpolatorId).interpolate("dropAmount", dropAmount, 3); dropAmount = model.getMetadata().getInterpolator(interpolatorId).interpolate("dropAmount", dropAmount, 3);
stack.push(); stack.push();

View file

@ -7,6 +7,7 @@ import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.gear.IRenderContext;
import com.minelittlepony.model.gear.IStackable; import com.minelittlepony.model.gear.IStackable;
public class Stetson extends AbstractGear implements IStackable { public class Stetson extends AbstractGear implements IStackable {

View file

@ -7,6 +7,7 @@ import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.gear.IRenderContext;
import com.minelittlepony.model.gear.IStackable; import com.minelittlepony.model.gear.IStackable;
public class WitchHat extends AbstractGear implements IStackable { public class WitchHat extends AbstractGear implements IStackable {

View file

@ -4,9 +4,9 @@ import com.minelittlepony.api.pony.IPony;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.model.IPonyModel; import com.minelittlepony.client.model.IPonyModel;
import com.minelittlepony.client.model.ModelWrapper; import com.minelittlepony.client.model.ModelWrapper;
import com.minelittlepony.client.model.gear.IRenderContext;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.PonyModelConstants; import com.minelittlepony.model.PonyModelConstants;
import com.minelittlepony.model.gear.IRenderContext;
import com.minelittlepony.util.MathUtil; import com.minelittlepony.util.MathUtil;
import net.minecraft.client.render.entity.model.EntityModel; import net.minecraft.client.render.entity.model.EntityModel;

View file

@ -8,6 +8,7 @@ import net.minecraft.client.render.entity.model.EntityModel;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import com.google.common.collect.Streams;
import com.minelittlepony.api.pony.meta.Wearable; import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.client.model.IPonyModel; import com.minelittlepony.client.model.IPonyModel;
import com.minelittlepony.client.model.ModelType; import com.minelittlepony.client.model.ModelType;
@ -16,21 +17,27 @@ import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.gear.IGear; import com.minelittlepony.model.gear.IGear;
import com.minelittlepony.model.gear.IStackable; import com.minelittlepony.model.gear.IStackable;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class GearFeature<T extends LivingEntity, M extends EntityModel<T> & IPonyModel<T>> extends AbstractPonyFeature<T, M> { public class GearFeature<T extends LivingEntity, M extends EntityModel<T> & IPonyModel<T>> extends AbstractPonyFeature<T, M> {
private final Map<Wearable, IGear> gears; private static final List<Entry> MOD_GEARS = new ArrayList<>();
public static IGear addModGear(IGear gear) {
MOD_GEARS.add(new Entry(gear, Wearable.NONE));
return gear;
}
private final List<Entry> gears;
public GearFeature(IPonyRenderContext<T, M> renderer) { public GearFeature(IPonyRenderContext<T, M> renderer) {
super(renderer); super(renderer);
gears = ModelType.getWearables().collect(Collectors.toMap( gears = Streams.concat(ModelType.getWearables().map(e -> new Entry(e.getValue().createModel(), e.getKey())), MOD_GEARS.stream()).collect(Collectors.toList());
Map.Entry::getKey,
e -> e.getValue().createModel()
));
} }
@Override @Override
@ -40,21 +47,20 @@ public class GearFeature<T extends LivingEntity, M extends EntityModel<T> & IPon
return; return;
} }
M model = getModelWrapper().getBody(); final M model = getModelWrapper().getBody();
Map<BodyPart, Float> renderStackingOffsets = new HashMap<>(); final Map<BodyPart, Float> renderStackingOffsets = new HashMap<>();
for (Map.Entry<Wearable, IGear> entry : gears.entrySet()) { for (Entry entry : gears) {
Wearable wearable = entry.getKey(); final IGear gear = entry.gear;
IGear gear = entry.getValue();
if (getContext().shouldRender(model, entity, wearable, gear)) { if (getContext().shouldRender(model, entity, entry.wearable, gear)) {
stack.push(); stack.push();
model.transform(gear.getGearLocation(), stack); BodyPart part = gear.getGearLocation();
model.getBodyPart(gear.getGearLocation()).rotate(stack); model.transform(part, stack);
model.getBodyPart(part).rotate(stack);
if (gear instanceof IStackable) { if (gear instanceof IStackable) {
BodyPart part = gear.getGearLocation();
renderStackingOffsets.compute(part, (k, v) -> { renderStackingOffsets.compute(part, (k, v) -> {
float offset = ((IStackable)gear).getStackingOffset(); float offset = ((IStackable)gear).getStackingOffset();
if (v != null) { if (v != null) {
@ -73,12 +79,22 @@ public class GearFeature<T extends LivingEntity, M extends EntityModel<T> & IPon
private void renderGear(M model, T entity, IGear gear, MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, float limbDistance, float limbAngle, float tickDelta) { private void renderGear(M model, T entity, IGear gear, MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, float limbDistance, float limbAngle, float tickDelta) {
gear.setLivingAnimations(model, entity); gear.setModelAttributes(model, entity);
gear.setRotationAndAngles(model.getAttributes().isGoingFast, entity.getUuid(), limbDistance, limbAngle, model.getWobbleAmount(), tickDelta); gear.pose(model.getAttributes().isGoingFast, entity.getUuid(), limbDistance, limbAngle, model.getWobbleAmount(), tickDelta);
RenderLayer layer = RenderLayer.getEntityTranslucent(gear.getTexture(entity, getContext())); RenderLayer layer = RenderLayer.getEntityTranslucent(gear.getTexture(entity, getContext()));
VertexConsumer vertexConsumer = renderContext.getBuffer(layer); VertexConsumer vertexConsumer = renderContext.getBuffer(layer);
gear.renderPart(stack, vertexConsumer, lightUv, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1, entity.getUuid()); gear.render(stack, vertexConsumer, lightUv, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1, entity.getUuid());
}
static class Entry {
IGear gear;
Wearable wearable;
Entry(IGear gear, Wearable wearable) {
this.gear = gear;
this.wearable = wearable;
}
} }
} }

View file

@ -1,4 +1,4 @@
package com.minelittlepony.model.capabilities; package com.minelittlepony.model;
import com.minelittlepony.api.pony.IPonyData; import com.minelittlepony.api.pony.IPonyData;

View file

@ -1,6 +1,6 @@
package com.minelittlepony.model.armour; package com.minelittlepony.model.armour;
import com.minelittlepony.model.capabilities.IModelWrapper; import com.minelittlepony.model.IModelWrapper;
public interface IEquestrianArmour<V extends IArmour> extends IModelWrapper { public interface IEquestrianArmour<V extends IArmour> extends IModelWrapper {
/** /**

View file

@ -1,4 +0,0 @@
@ParametersAreNonnullByDefault
package com.minelittlepony.model.capabilities.fabric;
import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -1,4 +1,4 @@
package com.minelittlepony.model.capabilities.fabric; package com.minelittlepony.model.fabric;
import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.Event;
import net.fabricmc.fabric.api.event.EventFactory; import net.fabricmc.fabric.api.event.EventFactory;

View file

@ -1,4 +1,4 @@
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
package com.minelittlepony.model.capabilities; package com.minelittlepony.model.fabric;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;

View file

@ -1,14 +1,30 @@
package com.minelittlepony.model.gear; package com.minelittlepony.model.gear;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import com.minelittlepony.client.model.gear.IRenderContext; import com.minelittlepony.client.render.entity.feature.GearFeature;
import com.minelittlepony.model.BodyPart; import com.minelittlepony.model.BodyPart;
import com.minelittlepony.model.IModel; import com.minelittlepony.model.IModel;
import com.minelittlepony.model.IPart;
public interface IGear extends IPart { import java.util.UUID;
/**
* Interface for an accessory on a pony's body.
*/
public interface IGear {
/**
* Registers a custom gear to be used with the mod.
* <p>
* This would be awesome for creating socks.
*/
static IGear addModGear(IGear gear) {
GearFeature.addModGear(gear);
return gear;
}
/** /**
* Determines if this wearable can and is worn by the selected entity. * Determines if this wearable can and is worn by the selected entity.
@ -27,14 +43,39 @@ public interface IGear extends IPart {
/** /**
* Gets the texture to use for this wearable. * Gets the texture to use for this wearable.
* Return null to use the same as the primary model. *
* If you need to use the player's own skin, use {@link IRenderContext#getDefaultTexture(entity, wearable)}
*/ */
<T extends Entity> Identifier getTexture(T entity, IRenderContext<T, ?> context); <T extends Entity> Identifier getTexture(T entity, IRenderContext<T, ?> context);
default <T extends Entity> RenderLayer getLayer(T entity, IRenderContext<T, ?> context) {
return RenderLayer.getEntityTranslucent(getTexture(entity, context));
}
/** /**
* Orients this wearable. * Orients this wearable.
*/ */
default void setLivingAnimations(IModel model, Entity entity) { default void setModelAttributes(IModel model, Entity entity) {
}
/**
* Sets the model's various rotation angles.
*
* See {@link AbstractPonyMode.setRotationAndAngle} for an explanation of the various parameters.
*/
default void pose(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
}
/**
* Renders this model component.
*/
void render(MatrixStack stack, VertexConsumer vertices, int overlayUv, int lightUv, float red, float green, float blue, float alpha, UUID interpolatorId);
/**
* Sets whether this part should be rendered.
*/
default void setVisible(boolean visible) {
} }
} }

View file

@ -0,0 +1,41 @@
package com.minelittlepony.model.gear;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import com.minelittlepony.api.pony.meta.Wearable;
import com.minelittlepony.model.IModel;
import javax.annotation.Nullable;
/**
* A render context for instance of IGear.
*
* @param <T> The type of entity being rendered.
* @param <M> The type of the entity's primary model.
*/
public interface IRenderContext<T extends Entity, M extends IModel> {
/**
* The empty context.
*/
IRenderContext<?, ?> NULL = (e, g) -> null;
/**
* Checks whether the given wearable and gear are able to render for this specific entity and its renderer.
*/
default boolean shouldRender(M model, T entity, Wearable wearable, IGear gear) {
return gear.canRender(model, entity);
}
@Nullable
default M getEntityModel() {
return null;
}
/**
* Gets the default texture to use for this entity and wearable.
*
* May be the entity's own texture or a specific texture allocated for that wearable.
*/
Identifier getDefaultTexture(T entity, Wearable wearable);
}