(Backport) Add support for rendering modded armor

# Conflicts:
#	src/main/java/com/minelittlepony/client/model/armour/PonifiedEquipmentRenderer.java
#	src/main/java/com/minelittlepony/client/render/entity/feature/ArmourFeature.java
This commit is contained in:
Sollace 2024-12-17 14:37:06 +01:00
parent 74090596ff
commit 3284669e24
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
4 changed files with 95 additions and 0 deletions

View file

@ -77,6 +77,15 @@ public class PonyConfig extends Config {
.addComment("Disables certain easter eggs and secrets (party pooper)") .addComment("Disables certain easter eggs and secrets (party pooper)")
.addComment("Turning this off may help with compatibility in some cases"); .addComment("Turning this off may help with compatibility in some cases");
public final Setting<Boolean> enableFabricModelsApiSupport = value("settings", "enableFabricModelsApiSupport", false)
.addComment("Enables rendering of modded armour registered via the fabric api")
.addComment("Note that since any armour registered in this way is designed to work for the human model, pieces may not fit exactly.")
.addComment("i.e. Anything that goes on the plyer's backs needs to be rotated by the mod developer when rendered on a pony")
.addComment("Developers: To know if you're being rendered on a pony model, check the renderstate or model class with")
.addComment(" state instanceof com.minelittlepony.api.model.PonyModel$AttributedHolder or model instanceof com.minelittlepony.api.model.PonyModel")
.addComment(" Note that the matrix stack your receieve is pre-transformed for the body part your model is attached to, so if you intend to call model.transform(state, part, matrices)")
.addComment(" with a different part (ie BACK) you must pop the stack to revert to the previous first.");
public final Setting<VisibilityMode> horseButton = value("horseButton", VisibilityMode.AUTO) public final Setting<VisibilityMode> horseButton = value("horseButton", VisibilityMode.AUTO)
.addComment("Whether to show the mine little pony settings button on the main menu") .addComment("Whether to show the mine little pony settings button on the main menu")
.addComment("AUTO (default) - only show when HDSkins is not installed") .addComment("AUTO (default) - only show when HDSkins is not installed")

View file

@ -8,9 +8,17 @@ import com.minelittlepony.common.util.Color;
import java.util.*; import java.util.*;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.api.model.*;
import com.minelittlepony.client.model.ClientPonyModel;
import com.minelittlepony.client.util.render.MatrixStackUtil;
import net.fabricmc.fabric.api.client.rendering.v1.ArmorRenderer;
import net.fabricmc.fabric.impl.client.rendering.ArmorRendererRegistryImpl;
import net.minecraft.client.render.*; import net.minecraft.client.render.*;
import net.minecraft.client.render.entity.model.*; import net.minecraft.client.render.entity.model.*;
import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.client.render.entity.model.BipedEntityModel;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.DataComponentTypes; import net.minecraft.component.DataComponentTypes;
import net.minecraft.entity.EquipmentSlot; import net.minecraft.entity.EquipmentSlot;
@ -18,8 +26,16 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.item.*; import net.minecraft.item.*;
import net.minecraft.item.trim.ArmorTrim; import net.minecraft.item.trim.ArmorTrim;
import net.minecraft.util.Colors; import net.minecraft.util.Colors;
import net.minecraft.util.Unit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & PonyModel<T>> extends AbstractPonyFeature<T, M> { public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & PonyModel<T>> extends AbstractPonyFeature<T, M> {
private static final Logger LOGGER = LogManager.getLogger("PonifiedEquipmentRenderer");
private static boolean FABRIC_API_FAILURE;
public ArmourFeature(PonyRenderContext<T, M> context, BakedModelManager bakery) { public ArmourFeature(PonyRenderContext<T, M> context, BakedModelManager bakery) {
super(context); super(context);
} }
@ -56,6 +72,17 @@ public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & Po
continue; continue;
} }
if (!FABRIC_API_FAILURE && PonyConfig.getInstance().enableFabricModelsApiSupport.get()) {
try {
if (FabricArmorRendererInvoker.renderArmor(stack, pony, matrices, provider, light, entity, armorSlot)) {
continue;
}
} catch (Throwable t) {
LOGGER.error("Failure calling fabric armor rendering api", t);
FABRIC_API_FAILURE = true;
}
}
float glintAlpha = plugin.getGlintAlpha(armorSlot, stack); float glintAlpha = plugin.getGlintAlpha(armorSlot, stack);
boolean glint = glintAlpha > 0; boolean glint = glintAlpha > 0;
int color = plugin.getDyeColor(armorSlot, stack); int color = plugin.getDyeColor(armorSlot, stack);
@ -118,4 +145,43 @@ public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & Po
plugin.onArmourRendered(entity, matrices, provider, armorSlot, layer, ArmourRendererPlugin.ArmourType.ARMOUR); plugin.onArmourRendered(entity, matrices, provider, armorSlot, layer, ArmourRendererPlugin.ArmourType.ARMOUR);
} }
private static final class FabricArmorRendererInvoker {
private static final Map<ArmorRenderer, Unit> FAILING_RENDERERS = new WeakHashMap<>();
@SuppressWarnings({"rawtypes", "unchecked"})
private static <T extends LivingEntity, V extends ClientPonyModel<T>> boolean renderArmor(
ItemStack stack,
Models<T, ? extends PonyModel<T>> models, MatrixStack matrices,
VertexConsumerProvider vertices, int light, T entity,
EquipmentSlot armorSlot) {
ArmorRenderer renderer = ArmorRendererRegistryImpl.get(stack.getItem());
if (renderer != null && !FAILING_RENDERERS.containsKey(renderer)) {
MatrixStack isolation = MatrixStackUtil.pushIsolation(matrices);
try {
isolation.push();
models.body().transform(getBodyPart(armorSlot), isolation);
renderer.render(isolation, vertices, stack, entity, armorSlot, light, (BipedEntityModel)models.body());
isolation.pop();
} catch (Throwable t) {
LOGGER.error("Exception occured whilst rendering custom armor via fabric api. Renderer {} has been disabled", renderer, t);
FAILING_RENDERERS.put(renderer, Unit.INSTANCE);
} finally {
MatrixStackUtil.popIsolation();
}
return true;
}
return false;
}
private static BodyPart getBodyPart(EquipmentSlot slot) {
return switch (slot) {
case HEAD -> BodyPart.HEAD;
case CHEST, BODY -> BodyPart.BODY;
case LEGS, FEET, MAINHAND, OFFHAND -> BodyPart.LEGS;
};
}
}
} }

View file

@ -0,0 +1,19 @@
package com.minelittlepony.client.util.render;
import net.minecraft.client.util.math.MatrixStack;
public class MatrixStackUtil {
static final MatrixStack ISOLATION_STACK = new MatrixStack();
public static MatrixStack pushIsolation(MatrixStack matrices) {
ISOLATION_STACK.peek().getNormalMatrix().set(matrices.peek().getNormalMatrix());
ISOLATION_STACK.peek().getPositionMatrix().set(matrices.peek().getPositionMatrix());
return ISOLATION_STACK;
}
public static void popIsolation() {
while (!ISOLATION_STACK.isEmpty()) {
ISOLATION_STACK.pop();
}
}
}

View file

@ -14,6 +14,7 @@
"minelp.options.skins.hdskins.open": "Open HD Skins", "minelp.options.skins.hdskins.open": "Open HD Skins",
"minelp.options.skins.hdskins.disabled": "HD Skins is not installed\n\nThe HD Skins mod is required to upload skins from in-game and to use custom skin servers.\n\nIf you cannot use that you will have to go to www.minecraft.net to upload your skin there.", "minelp.options.skins.hdskins.disabled": "HD Skins is not installed\n\nThe HD Skins mod is required to upload skins from in-game and to use custom skin servers.\n\nIf you cannot use that you will have to go to www.minecraft.net to upload your skin there.",
"minelp.options.snuzzles": "Show Snuzzles", "minelp.options.snuzzles": "Show Snuzzles",
"minelp.options.enablefabricmodelsapisupport": "(Experimental) Show Modded Armor",
"minelp.options.fillycam": "Filly Cam", "minelp.options.fillycam": "Filly Cam",
"minelp.options.showscale": "Show-accurate scaling", "minelp.options.showscale": "Show-accurate scaling",
"minelp.options.fpsmagic": "Magic in first-person", "minelp.options.fpsmagic": "Magic in first-person",