Hi!
I'm trying to add my custom models/textures renderer like this:
public class PonyPlayerWrapperRenderer extends EntityRenderer<Player> { // wrapper class under my LivingEntityRenderer class implementation
private final PonyPlayerRenderer innerRenderer;
private final PonyPlayerRenderer innerSlimRenderer;
public PonyPlayerWrapperRenderer(final EntityRendererProvider.Context context) {
super(context);
System.out.println("creating new PonyPlayerWrapperRenderer");
this.innerRenderer = new PonyPlayerRenderer(context, false);
this.innerSlimRenderer = new PonyPlayerRenderer(context, true);
}
@Override
public void render(final Player entity, final float yaw, final float partialTicks, final PoseStack poseStack,
final MultiBufferSource bufferSource, final int packedLight) {
System.out.println("PonyPlayerWrapperRenderer render: " + entity.toString());
if (entity instanceof AbstractClientPlayer clientPlayer) {
if (clientPlayer.getModelName().contains("slim")) {
innerSlimRenderer.render(clientPlayer, yaw, partialTicks, poseStack, bufferSource, packedLight);
} else {
innerRenderer.render(clientPlayer, yaw, partialTicks, poseStack, bufferSource, packedLight);
}
}
}
@Override
public ResourceLocation getTextureLocation(final Player player) {
System.out.println("PonyPlayerWrapperRenderer getTextureLocation");
if (player instanceof AbstractClientPlayer clientPlayer) {
return clientPlayer.getSkinTextureLocation();
}
System.out.println("player instanceof AbstractClientPlayer is false");
return getDefaultSkin(player.getUUID());
}
}
public class PonyPlayerRenderer extends LivingEntityRenderer<AbstractClientPlayer, PlayerModel<AbstractClientPlayer>> {
private final PlayerModel<AbstractClientPlayer> earthModel;
private final PlayerModel<AbstractClientPlayer> pegasusModel;
private final PlayerModel<AbstractClientPlayer> unicornModel;
public PonyPlayerRenderer(final EntityRendererProvider.Context context, final boolean slim) {
super(
context, slim
? new PonyModelSlim(context.bakeLayer(PonyModelSlim.LAYER_LOCATION))
: new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)),
0.5f
);
System.out.println("creating new PonyPlayerRenderer");
this.earthModel = slim
? new PonyModelSlim(context.bakeLayer(PonyModelSlim.LAYER_LOCATION))
: new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION));
this.pegasusModel = new PegasusModel(context.bakeLayer(PegasusModel.LAYER_LOCATION));
this.unicornModel = new UnicornModel(context.bakeLayer(UnicornModel.LAYER_LOCATION));
}
@Override
public void render(final AbstractClientPlayer player, final float entityYaw, final float partialTicks,
final PoseStack poseStack, final MultiBufferSource buffer, final int packedLight) {
final PonyRace race = player.getCapability(PONY_DATA)
.map(data -> ofNullable(data.getRace()).orElse(PonyRace.EARTH))
.orElse(PonyRace.EARTH);
this.model = switch (race) {
case PEGASUS -> pegasusModel;
case UNICORN -> unicornModel;
case EARTH -> earthModel;
};
super.render(player, entityYaw, partialTicks, poseStack, buffer, packedLight);
}
@Override
public ResourceLocation getTextureLocation(final AbstractClientPlayer player) {
final PonyRace race = player.getCapability(PONY_DATA)
.map(data -> ofNullable(data.getRace()).orElse(PonyRace.EARTH))
.orElse(PonyRace.EARTH);
return switch (race) {
case EARTH -> fromNamespaceAndPath(MODID, "textures/entity/earth_pony.png");
case PEGASUS -> fromNamespaceAndPath(MODID, "textures/entity/pegasus.png");
case UNICORN -> fromNamespaceAndPath(MODID, "textures/entity/unicorn.png");
};
}
}
@Mod.EventBusSubscriber(modid = MODID, bus = MOD, value = CLIENT)
public class ClientRenderers { // mod bus render registration config
@SubscribeEvent
public static void onRegisterLayerDefinitions(final EntityRenderersEvent.RegisterLayerDefinitions event) {
event.registerLayerDefinition(PonyModel.LAYER_LOCATION, PonyModel::createBodyLayer);
event.registerLayerDefinition(PonyModelSlim.LAYER_LOCATION, PonyModelSlim::createBodyLayer);
event.registerLayerDefinition(PegasusModel.LAYER_LOCATION, PegasusModel::createBodyLayer);
event.registerLayerDefinition(UnicornModel.LAYER_LOCATION, UnicornModel::createBodyLayer);
event.registerLayerDefinition(InnerPonyArmorModel.LAYER_LOCATION, InnerPonyArmorModel::createBodyLayer);
event.registerLayerDefinition(OuterPonyArmorModel.LAYER_LOCATION, OuterPonyArmorModel::createBodyLayer);
}
@SubscribeEvent
public static void onRegisterRenderers(final EntityRenderersEvent.RegisterRenderers event) {
event.registerEntityRenderer(EntityType.PLAYER, PonyPlayerWrapperRenderer::new);
System.out.println("onRegisterRenderers end");
}
}
Method onRegisterRenderers() is called and I can see it being logged. But when I enter the world, my PonyWrapperRenderer render() method doesn't ever seem to be called.
I also tried to put my renderer to EntityRenderDispatcher's playerRenderers via reflection:
@Mod.EventBusSubscriber(modid = MODID, bus = MOD, value = CLIENT)
public class ClientRenderers {
@SubscribeEvent
public static void onRegisterLayerDefinitions(final EntityRenderersEvent.RegisterLayerDefinitions event) {
event.registerLayerDefinition(PonyModel.LAYER_LOCATION, PonyModel::createBodyLayer);
event.registerLayerDefinition(PonyModelSlim.LAYER_LOCATION, PonyModelSlim::createBodyLayer);
event.registerLayerDefinition(PegasusModel.LAYER_LOCATION, PegasusModel::createBodyLayer);
event.registerLayerDefinition(UnicornModel.LAYER_LOCATION, UnicornModel::createBodyLayer);
event.registerLayerDefinition(InnerPonyArmorModel.LAYER_LOCATION, InnerPonyArmorModel::createBodyLayer);
event.registerLayerDefinition(OuterPonyArmorModel.LAYER_LOCATION, OuterPonyArmorModel::createBodyLayer);
}
@SubscribeEvent
public static void onClientSetup(final FMLClientSetupEvent event) {
event.enqueueWork(() -> {
try {
final EntityRenderDispatcher dispatcher = Minecraft.getInstance().getEntityRenderDispatcher();
final Field renderersField = getEntityRenderDispatcherField("playerRenderers");
final Field itemInHandRenderer = getEntityRenderDispatcherField("itemInHandRenderer");
@SuppressWarnings("unchecked") final Map<String, EntityRenderer<? extends Player>> playerRenderers =
(Map<String, EntityRenderer<? extends Player>>)renderersField.get(dispatcher);
final PonyPlayerWrapperRenderer renderer = new PonyPlayerWrapperRenderer(
new EntityRendererProvider.Context(
dispatcher,
Minecraft.getInstance().getItemRenderer(),
Minecraft.getInstance().getBlockRenderer(),
(ItemInHandRenderer)itemInHandRenderer.get(dispatcher),
Minecraft.getInstance().getResourceManager(),
Minecraft.getInstance().getEntityModels(),
Minecraft.getInstance().font
)
);
playerRenderers.put("default", renderer);
playerRenderers.put("slim", renderer);
System.out.println("Player renderers replaced");
} catch (final Exception e) {
throw new RuntimeException("Failed to replace player renderers", e);
}
});
}
private static Field getEntityRenderDispatcherField(final String fieldName) throws NoSuchFieldException {
final Field field = EntityRenderDispatcher.class.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
}
}
But I receive the error before Minecraft Client appears (RuntimeException: Failed to replace player renderers - from ClientRenderers onClientSetup() method - and its cause below):
java.lang.IllegalArgumentException: No model for layer anotherlittlepony:earth_pony#main
at net.minecraft.client.model.geom.EntityModelSet.bakeLayer(EntityModelSet.java:18) ~[forge-1.20.1-47.4.0_mapped_official_1.20.1-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}
at net.minecraft.client.renderer.entity.EntityRendererProvider$Context.bakeLayer(EntityRendererProvider.java:69) ~[forge-1.20.1-47.4.0_mapped_official_1.20.1-recomp.jar:?] {re:classloading,pl:runtimedistcleaner:A}
at com.thuggeelya.anotherlittlepony.client.renderer.pony.PonyPlayerRenderer.<init>(PonyPlayerRenderer.java:32) ~[main/:?] {re:classloading}
at com.thuggeelya.anotherlittlepony.client.renderer.pony.PonyPlayerWrapperRenderer.<init>(PonyPlayerWrapperRenderer.java:24) ~[main/:?] {re:classloading}
at com.thuggeelya.anotherlittlepony.client.renderer.ClientRenderers.lambda$onClientSetup$0(ClientRenderers.java:79) ~[main/:?] {re:classloading}
... 33 more
Problem appears when EntityRendererProvider context tries to bakeLayer with my model layer location:
new PonyModel(context.bakeLayer(PonyModel.LAYER_LOCATION)); // PonyPlayerRenderer.java:32
public class PonyModel extends PlayerModel<AbstractClientPlayer> { // the model class itself
public static final ModelLayerLocation LAYER_LOCATION = new ModelLayerLocation(
ResourceLocation.fromNamespaceAndPath(MODID, "earth_pony"), "main"
);
public PonyModel(final ModelPart root) {
super(root, false);
}
public static LayerDefinition createBodyLayer() {
// some CubeListBuilder stuff for model appearance
}
}
Textures PNGs are placed at: resources/assets/[my mod id]/textures/entity.
My forge version is 1.20.1. Would appreciate any help.