Implement a armour renderplugin api

This commit is contained in:
Sollace 2024-05-04 21:21:16 +01:00
parent 85726e8c8d
commit ced4a2d980
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
6 changed files with 189 additions and 111 deletions

View file

@ -0,0 +1,65 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.component.type.DyedColorComponent;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.ArmorMaterial;
import net.minecraft.item.ItemStack;
import net.minecraft.item.trim.ArmorTrim;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.ItemTags;
import net.minecraft.util.Colors;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicReference;
public interface ArmourRendererPlugin {
AtomicReference<ArmourRendererPlugin> INSTANCE = new AtomicReference<>(new ArmourRendererPlugin() {});
static void register(ArmourRendererPlugin plugin) {
INSTANCE.set(plugin);
}
default ArmourTextureLookup getTextureLookup() {
return ArmourTextureResolver.INSTANCE;
}
default ItemStack[] getArmorStacks(LivingEntity entity, EquipmentSlot armorSlot, ArmourLayer layer) {
return new ItemStack[] { entity.getEquippedStack(armorSlot) };
}
default boolean shouldRenderGlint(EquipmentSlot slot, ItemStack stack) {
return stack.hasGlint();
}
default int getDyeColor(EquipmentSlot slot, ItemStack stack) {
return stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, -6265536) : Colors.WHITE;
}
@Nullable
default VertexConsumer getTrimConsumer(EquipmentSlot slot, VertexConsumerProvider provider, RegistryEntry<ArmorMaterial> material, ArmorTrim trim, ArmourLayer layer) {
SpriteAtlasTexture armorTrimsAtlas = MinecraftClient.getInstance().getBakedModelManager().getAtlas(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE);
Sprite sprite = armorTrimsAtlas.getSprite(
layer == ArmourLayer.INNER ? trim.getLeggingsModelId(material) : trim.getGenericModelId(material)
);
return sprite.getTextureSpecificVertexConsumer(
provider.getBuffer(TexturedRenderLayers.getArmorTrims(trim.getPattern().value().decal()))
);
}
@Nullable
default VertexConsumer getArmourConsumer(EquipmentSlot slot, VertexConsumerProvider provider, Identifier texture, ArmourLayer layer) {
return provider.getBuffer(RenderLayer.getArmorCutoutNoCull(texture));
}
@Nullable
default VertexConsumer getGlintConsumer(EquipmentSlot slot, VertexConsumerProvider provider, ArmourLayer layer) {
return provider.getBuffer(RenderLayer.getArmorEntityGlint());
}
}

View file

@ -0,0 +1,39 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.client.texture.TextureManager;
import net.minecraft.util.Identifier;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.util.ResourceUtil;
import java.util.stream.Stream;
public record ArmourTexture(Identifier texture, ArmourVariant variant) {
private static final Interner<ArmourTexture> INTERNER = Interners.newWeakInterner();
public static final ArmourTexture UNKNOWN = legacy(TextureManager.MISSING_IDENTIFIER);
public boolean validate() {
return texture != TextureManager.MISSING_IDENTIFIER && ResourceUtil.textureExists(texture);
}
public static ArmourTexture legacy(Identifier texture) {
return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.LEGACY));
}
public static ArmourTexture modern(Identifier texture) {
return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.NORMAL));
}
public Stream<ArmourTexture> named() {
return Stream.of(legacy(texture().withPath(p -> p.replace("1", "inner").replace("2", "outer"))), this);
}
public Stream<ArmourTexture> ponify() {
if (!PonyConfig.getInstance().disablePonifiedArmour.get()) {
return Stream.of(this, modern(ResourceUtil.ponify(texture())));
}
return Stream.of(this);
}
}

View file

@ -0,0 +1,12 @@
package com.minelittlepony.client.model.armour;
import net.minecraft.item.ArmorMaterial;
import net.minecraft.item.ItemStack;
import java.util.List;
public interface ArmourTextureLookup {
ArmourTexture getTexture(ItemStack stack, ArmourLayer layer, ArmorMaterial.Layer armorLayer);
List<ArmorMaterial.Layer> getArmorLayers(ItemStack stack, int dyeColor);
}

View file

@ -1,7 +1,6 @@
package com.minelittlepony.client.model.armour;
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
import net.minecraft.client.texture.TextureManager;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.CustomModelDataComponent;
import net.minecraft.item.*;
@ -14,9 +13,7 @@ import net.minecraft.util.profiler.Profiler;
import com.google.common.cache.*;
import com.google.common.collect.Interner;
import com.google.common.collect.Interners;
import com.minelittlepony.api.config.PonyConfig;
import com.minelittlepony.client.MineLittlePony;
import com.minelittlepony.util.ResourceUtil;
import java.util.*;
import java.util.concurrent.*;
@ -38,7 +35,7 @@ import java.util.stream.Stream;
* - the "minecraft" namespace is always replaced with "minelittlepony"
* <p>
*/
public class ArmourTextureResolver implements IdentifiableResourceReloadListener {
public class ArmourTextureResolver implements ArmourTextureLookup, IdentifiableResourceReloadListener {
public static final Identifier ID = MineLittlePony.id("armor_textures");
public static final ArmourTextureResolver INSTANCE = new ArmourTextureResolver();
@ -102,10 +99,12 @@ public class ArmourTextureResolver implements IdentifiableResourceReloadListener
return ID;
}
@Override
public ArmourTexture getTexture(ItemStack stack, ArmourLayer layer, ArmorMaterial.Layer armorLayer) {
return layerCache.getUnchecked(new ArmourParameters(layer, armorLayer, getCustom(stack)));
}
@Override
public List<ArmorMaterial.Layer> getArmorLayers(ItemStack stack, int dyeColor) {
if (stack.getItem() instanceof ArmorItem armor) {
return armor.getMaterial().value().layers();
@ -121,32 +120,4 @@ public class ArmourTextureResolver implements IdentifiableResourceReloadListener
private record ArmourParameters(ArmourLayer layer, ArmorMaterial.Layer material, int customModelId) {
}
public record ArmourTexture(Identifier texture, ArmourVariant variant) {
private static final Interner<ArmourTexture> INTERNER = Interners.newWeakInterner();
public static final ArmourTexture UNKNOWN = legacy(TextureManager.MISSING_IDENTIFIER);
public boolean validate() {
return texture != TextureManager.MISSING_IDENTIFIER && ResourceUtil.textureExists(texture);
}
public static ArmourTexture legacy(Identifier texture) {
return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.LEGACY));
}
public static ArmourTexture modern(Identifier texture) {
return INTERNER.intern(new ArmourTexture(texture, ArmourVariant.NORMAL));
}
public Stream<ArmourTexture> named() {
return Stream.of(legacy(texture().withPath(p -> p.replace("1", "inner").replace("2", "outer"))), this);
}
public Stream<ArmourTexture> ponify() {
if (!PonyConfig.getInstance().disablePonifiedArmour.get()) {
return Stream.of(this, modern(ResourceUtil.ponify(texture())));
}
return Stream.of(this);
}
}
}

View file

@ -7,7 +7,6 @@ import net.minecraft.client.render.entity.feature.*;
import net.minecraft.client.render.entity.model.ArmorStandArmorEntityModel;
import net.minecraft.client.render.entity.model.EntityModelLayers;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.util.math.Vec3d;
@ -15,7 +14,6 @@ import com.minelittlepony.api.model.Models;
import com.minelittlepony.api.pony.PonyData;
import com.minelittlepony.api.pony.meta.Race;
import com.minelittlepony.client.model.ModelType;
import com.minelittlepony.client.model.armour.ArmourLayer;
import com.minelittlepony.client.model.entity.PonyArmourStandModel;
import com.minelittlepony.client.model.entity.race.EarthPonyModel;
import com.minelittlepony.client.render.entity.feature.ArmourFeature;
@ -82,13 +80,7 @@ public class PonyStandRenderer extends ArmorStandEntityRenderer {
pony.body().animateModel(entity, limbDistance, limbAngle, tickDelta);
pony.body().setAngles(entity, limbDistance, limbAngle, age, headYaw, headPitch);
PonyStandRenderer.this.pony.applyAnglesTo(pony.body());
for (EquipmentSlot i : EquipmentSlot.values()) {
if (i.getType() == EquipmentSlot.Type.ARMOR) {
ArmourFeature.renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.INNER);
ArmourFeature.renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.OUTER);
}
}
ArmourFeature.renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch);
} else {
super.render(stack, renderContext, lightUv, entity, limbDistance, limbAngle, tickDelta, age, headYaw, headPitch);
}

View file

@ -3,114 +3,113 @@ package com.minelittlepony.client.render.entity.feature;
import com.minelittlepony.api.model.Models;
import com.minelittlepony.api.model.PonyModel;
import com.minelittlepony.client.model.armour.*;
import com.minelittlepony.client.model.armour.ArmourTextureResolver.ArmourTexture;
import com.minelittlepony.client.render.PonyRenderContext;
import com.minelittlepony.common.util.Color;
import java.util.*;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.*;
import net.minecraft.client.render.entity.model.*;
import net.minecraft.client.render.model.BakedModelManager;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.texture.SpriteAtlasTexture;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.component.DataComponentTypes;
import net.minecraft.component.type.DyedColorComponent;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.*;
import net.minecraft.item.trim.ArmorTrim;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.registry.tag.ItemTags;
import net.minecraft.util.Colors;
public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & PonyModel<T>> extends AbstractPonyFeature<T, M> {
public ArmourFeature(PonyRenderContext<T, M> context, BakedModelManager bakery) {
super(context);
}
@Override
public void render(MatrixStack stack, VertexConsumerProvider renderContext, int lightUv, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) {
Models<T, M> pony = getModelWrapper();
for (EquipmentSlot i : EquipmentSlot.values()) {
if (i.getType() == EquipmentSlot.Type.ARMOR) {
renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.INNER);
renderArmor(pony, stack, renderContext, lightUv, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.OUTER);
}
}
public void render(MatrixStack matrices, VertexConsumerProvider provider, int light, T entity, float limbDistance, float limbAngle, float tickDelta, float age, float headYaw, float headPitch) {
renderArmor(getModelWrapper(), matrices, provider, light, entity, limbDistance, limbAngle, age, headYaw, headPitch);
}
public static <T extends LivingEntity, V extends PonyArmourModel<T>> void renderArmor(
Models<T, ? extends PonyModel<T>> pony, MatrixStack matrices,
VertexConsumerProvider provider, int light, T entity,
float limbDistance, float limbAngle,
float age, float headYaw, float headPitch,
EquipmentSlot armorSlot, ArmourLayer layer) {
float age, float headYaw, float headPitch) {
ArmourRendererPlugin plugin = ArmourRendererPlugin.INSTANCE.get();
ItemStack stack = entity.getEquippedStack(armorSlot);
if (stack.isEmpty()) {
return;
for (EquipmentSlot i : EquipmentSlot.values()) {
if (i.getType() == EquipmentSlot.Type.ARMOR) {
renderArmor(pony, matrices, provider, light, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.INNER, plugin);
renderArmor(pony, matrices, provider, light, entity, limbDistance, limbAngle, age, headYaw, headPitch, i, ArmourLayer.OUTER, plugin);
}
}
}
boolean glint = stack.hasGlint();
private static <T extends LivingEntity, V extends PonyArmourModel<T>> void renderArmor(
Models<T, ? extends PonyModel<T>> pony, MatrixStack matrices,
VertexConsumerProvider provider, int light, T entity,
float limbDistance, float limbAngle,
float age, float headYaw, float headPitch,
EquipmentSlot armorSlot, ArmourLayer layer, ArmourRendererPlugin plugin) {
int color = stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, -6265536) : Colors.WHITE;
Set<PonyArmourModel<?>> models = glint ? new HashSet<>() : null;
for (ArmorMaterial.Layer armorLayer : ArmourTextureResolver.INSTANCE.getArmorLayers(stack, color)) {
ArmourTexture layerTexture = ArmourTextureResolver.INSTANCE.getTexture(stack, layer, armorLayer);
if (layerTexture == ArmourTexture.UNKNOWN) {
for (ItemStack stack : plugin.getArmorStacks(entity, armorSlot, layer)) {
if (stack.isEmpty()) {
continue;
}
var m = pony.getArmourModel(stack, layer, layerTexture.variant()).orElse(null);
if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) {
float red = 1;
float green = 1;
float blue = 1;
if (armorLayer.isDyeable() && color != Colors.WHITE) {
red = Color.r(color);
green = Color.g(color);
blue = Color.b(color);
boolean glint = plugin.shouldRenderGlint(armorSlot, stack);
int color = plugin.getDyeColor(armorSlot, stack);
Set<PonyArmourModel<?>> models = glint ? new HashSet<>() : null;
ArmourTextureLookup textureLookup = plugin.getTextureLookup();
for (ArmorMaterial.Layer armorLayer : textureLookup.getArmorLayers(stack, color)) {
ArmourTexture layerTexture = textureLookup.getTexture(stack, layer, armorLayer);
if (layerTexture == ArmourTexture.UNKNOWN) {
continue;
}
m.render(matrices, provider.getBuffer(RenderLayer.getArmorCutoutNoCull(layerTexture.texture())), light, OverlayTexture.DEFAULT_UV, red, green, blue, 1);
if (glint) {
models.add(m);
var m = pony.getArmourModel(stack, layer, layerTexture.variant()).orElse(null);
if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) {
VertexConsumer armorConsumer = plugin.getArmourConsumer(armorSlot, provider, layerTexture.texture(), layer);
if (armorConsumer != null) {
float red = 1;
float green = 1;
float blue = 1;
if (armorLayer.isDyeable() && color != Colors.WHITE) {
red = Color.r(color);
green = Color.g(color);
blue = Color.b(color);
}
m.render(matrices, armorConsumer, light, OverlayTexture.DEFAULT_UV, red, green, blue, 1);
}
if (glint) {
models.add(m);
}
}
}
ArmorTrim trim = stack.get(DataComponentTypes.TRIM);
if (trim != null && stack.getItem() instanceof ArmorItem armor) {
var m = pony.getArmourModel(stack, layer, ArmourVariant.TRIM).orElse(null);
if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) {
VertexConsumer trimConsumer = plugin.getTrimConsumer(armorSlot, provider, armor.getMaterial(), trim, layer);
if (trimConsumer != null) {
m.render(matrices, trimConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
}
}
}
if (glint) {
VertexConsumer glintConsumer = plugin.getGlintConsumer(armorSlot, provider, layer);
if (glintConsumer != null) {
for (var m : models) {
m.render(matrices, glintConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
}
}
}
}
ArmorTrim trim = stack.get(DataComponentTypes.TRIM);
if (trim != null && stack.getItem() instanceof ArmorItem armor) {
var m = pony.getArmourModel(stack, layer, ArmourVariant.TRIM).orElse(null);
if (m != null && m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body())) {
m.render(matrices, getTrimConsumer(provider, armor.getMaterial(), trim, layer), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
}
}
if (glint) {
VertexConsumer glintConsumer = provider.getBuffer(RenderLayer.getArmorEntityGlint());
for (var m : models) {
m.render(matrices, glintConsumer, light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
}
}
}
private static VertexConsumer getTrimConsumer(VertexConsumerProvider provider, RegistryEntry<ArmorMaterial> material, ArmorTrim trim, ArmourLayer layer) {
SpriteAtlasTexture armorTrimsAtlas = MinecraftClient.getInstance().getBakedModelManager().getAtlas(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE);
Sprite sprite = armorTrimsAtlas.getSprite(
layer == ArmourLayer.INNER ? trim.getLeggingsModelId(material) : trim.getGenericModelId(material)
);
return sprite.getTextureSpecificVertexConsumer(
provider.getBuffer(TexturedRenderLayers.getArmorTrims(trim.getPattern().value().decal()))
);
}
}