mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-25 05:48:00 +01:00
Update armour rendering to use the armor material layers defined by the item.
Also fixes #269, #270
This commit is contained in:
parent
8d385a3202
commit
3c44b00c1d
2 changed files with 107 additions and 126 deletions
|
@ -1,25 +1,22 @@
|
|||
package com.minelittlepony.client.model.armour;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.texture.TextureManager;
|
||||
import net.minecraft.component.DataComponentTypes;
|
||||
import net.minecraft.component.type.CustomModelDataComponent;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.item.ArmorItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.util.Colors;
|
||||
import net.minecraft.util.Identifier;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.*;
|
||||
import com.minelittlepony.api.config.PonyConfig;
|
||||
import com.minelittlepony.client.MineLittlePony;
|
||||
import com.minelittlepony.util.ResourceUtil;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The default texture resolver used by Mine Little Pony.
|
||||
|
@ -39,98 +36,85 @@ import java.util.concurrent.TimeUnit;
|
|||
public class ArmourTextureResolver {
|
||||
public static final ArmourTextureResolver INSTANCE = new ArmourTextureResolver();
|
||||
|
||||
private final Cache<String, Identifier> cache = CacheBuilder.newBuilder()
|
||||
private static final String CUSTOM_NONE = "none";
|
||||
|
||||
private final Cache<String, ArmourTexture> cache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.<String, Identifier>build();
|
||||
.<String, ArmourTexture>build();
|
||||
private final LoadingCache<String, ArmourTexture> layerCache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.build(CacheLoader.from(texture -> {
|
||||
String[] parts = texture.split("#");
|
||||
if (!parts[1].equals(CUSTOM_NONE)) {
|
||||
parts[0] = parts[0].replace(".png", parts[1] + ".png");
|
||||
}
|
||||
List<ArmourTexture> options = new ArrayList<>();
|
||||
ArmourTexture.resolveHumanOrPony(new Identifier(parts[0].replace("1", "inner").replace("2", "outer")), options);
|
||||
ArmourTexture.resolveHumanOrPony(new Identifier(parts[0]), options);
|
||||
ArmourTexture result = ArmourTexture.pick(options);
|
||||
if (result == ArmourTexture.UNKNOWN) {
|
||||
MineLittlePony.logger.warn("Could not identify correct texture to use for {}. Was none of: [" + System.lineSeparator() + "{}" + System.lineSeparator() + "]", texture, options.stream()
|
||||
.map(ArmourTexture::texture)
|
||||
.map(Identifier::toString)
|
||||
.collect(Collectors.joining("," + System.lineSeparator())));
|
||||
return new ArmourTexture(new Identifier(parts[0]), ArmourVariant.LEGACY);
|
||||
}
|
||||
return result;
|
||||
}));
|
||||
private final LoadingCache<Identifier, List<ArmorMaterial.Layer>> nonDyedLayers = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.build(CacheLoader.from(material -> List.of(new ArmorMaterial.Layer(material, "", false))));
|
||||
private final LoadingCache<Identifier, List<ArmorMaterial.Layer>> dyedLayers = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.build(CacheLoader.from(material -> List.of(
|
||||
new ArmorMaterial.Layer(material, "", false),
|
||||
new ArmorMaterial.Layer(material, "overlay", true)
|
||||
)));
|
||||
|
||||
public void invalidate() {
|
||||
cache.invalidateAll();
|
||||
}
|
||||
|
||||
public Identifier getTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) {
|
||||
Identifier material = stack.getItem() instanceof ArmorItem armor
|
||||
? armor.getMaterial().getKey().get().getValue()
|
||||
: Registries.ITEM.getId(stack.getItem());
|
||||
String custom = getCustom(stack);
|
||||
public ArmourTexture getTexture(ItemStack stack, ArmourLayer layer, ArmorMaterial.Layer armorLayer) {
|
||||
return layerCache.getUnchecked(armorLayer.getTexture(layer == ArmourLayer.OUTER) + "#" + getCustom(stack));
|
||||
}
|
||||
|
||||
try {
|
||||
return cache.get(String.format("%s#%s#%s#%s", material, layer, type, custom), () -> {
|
||||
String typed = Strings.nullToEmpty(type);
|
||||
String extra = typed.isEmpty() ? "" : "_" + typed;
|
||||
|
||||
Identifier texture;
|
||||
|
||||
if (!"none".equals(custom)) {
|
||||
texture = resolveNewOrOld(material, layer, custom + extra, typed);
|
||||
if (texture != null) {
|
||||
return texture;
|
||||
}
|
||||
}
|
||||
|
||||
texture = resolveNewOrOld(material, layer, extra, typed);
|
||||
if (texture != null) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
return TextureManager.MISSING_IDENTIFIER;
|
||||
});
|
||||
} catch (ExecutionException ignored) {
|
||||
return TextureManager.MISSING_IDENTIFIER;
|
||||
public List<ArmorMaterial.Layer> getArmorLayers(ItemStack stack, int dyeColor) {
|
||||
if (stack.getItem() instanceof ArmorItem armor) {
|
||||
return armor.getMaterial().value().layers();
|
||||
}
|
||||
|
||||
return (dyeColor == Colors.WHITE ? nonDyedLayers : dyedLayers).getUnchecked(Registries.ITEM.getId(stack.getItem()));
|
||||
}
|
||||
|
||||
private String getCustom(ItemStack stack) {
|
||||
CustomModelDataComponent customModelData = stack.get(DataComponentTypes.CUSTOM_MODEL_DATA);
|
||||
if (customModelData != null) {
|
||||
return "custom_" + customModelData.value();
|
||||
}
|
||||
return "none";
|
||||
int custom = stack.getOrDefault(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelDataComponent.DEFAULT).value();
|
||||
return custom == 0 ? "none" : String.valueOf(custom);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Identifier resolveNewOrOld(Identifier material, ArmourLayer layer, String extra, String type) {
|
||||
Identifier texture = resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%s%s.png", material.getNamespace(), material.getPath(), layer, extra), type);
|
||||
public record ArmourTexture(Identifier texture, ArmourVariant variant) {
|
||||
public static final ArmourTexture UNKNOWN = new ArmourTexture(TextureManager.MISSING_IDENTIFIER, ArmourVariant.LEGACY);
|
||||
|
||||
if (texture != null) {
|
||||
return texture;
|
||||
public boolean validate() {
|
||||
return texture != TextureManager.MISSING_IDENTIFIER && ResourceUtil.textureExists(texture);
|
||||
}
|
||||
|
||||
return resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%d%s.png", material.getNamespace(), material.getPath(), layer.getLegacyId(), extra), type);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Identifier resolveHumanOrPony(String res, String type) {
|
||||
Identifier human = new Identifier(res);
|
||||
|
||||
String domain = human.getNamespace();
|
||||
if ("minecraft".equals(domain)) {
|
||||
domain = "minelittlepony"; // it's a vanilla armor. I provide these.
|
||||
public static ArmourTexture pick(List<ArmourTexture> options) {
|
||||
return options.stream().filter(ArmourTexture::validate).findFirst().orElse(ArmourTexture.UNKNOWN);
|
||||
}
|
||||
|
||||
if (!PonyConfig.getInstance().disablePonifiedArmour.get()) {
|
||||
|
||||
Identifier pony = new Identifier(domain, human.getPath().replace(".png", "_pony.png"));
|
||||
|
||||
if (isValid(pony)) {
|
||||
return pony;
|
||||
@Nullable
|
||||
private static void resolveHumanOrPony(Identifier human, List<ArmourTexture> output) {
|
||||
String domain = human.getNamespace();
|
||||
if (Identifier.DEFAULT_NAMESPACE.contentEquals(domain)) {
|
||||
domain = "minelittlepony"; // it's a vanilla armor. I provide these.
|
||||
}
|
||||
|
||||
if (!PonyConfig.getInstance().disablePonifiedArmour.get()) {
|
||||
output.add(new ArmourTexture(new Identifier(domain, human.getPath().replace(".png", "_pony.png")), ArmourVariant.NORMAL));
|
||||
}
|
||||
|
||||
output.add(new ArmourTexture(human, ArmourVariant.LEGACY));
|
||||
}
|
||||
|
||||
if (isValid(human)) {
|
||||
return human;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private final boolean isValid(Identifier texture) {
|
||||
return MinecraftClient.getInstance().getResourceManager().getResource(texture).isPresent();
|
||||
}
|
||||
|
||||
public ArmourVariant getVariant(ArmourLayer layer, Identifier resolvedTexture) {
|
||||
if (resolvedTexture.getPath().endsWith("_pony.png")) {
|
||||
return ArmourVariant.NORMAL;
|
||||
}
|
||||
return ArmourVariant.LEGACY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,15 @@ 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.render.ArmorRenderLayers;
|
||||
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.item.ItemRenderer;
|
||||
import net.minecraft.client.render.model.BakedModelManager;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.texture.SpriteAtlasTexture;
|
||||
|
@ -22,7 +23,8 @@ import net.minecraft.entity.LivingEntity;
|
|||
import net.minecraft.item.*;
|
||||
import net.minecraft.item.trim.ArmorTrim;
|
||||
import net.minecraft.registry.entry.RegistryEntry;
|
||||
import net.minecraft.util.Identifier;
|
||||
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> {
|
||||
|
||||
|
@ -44,7 +46,7 @@ public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & Po
|
|||
|
||||
public static <T extends LivingEntity, V extends PonyArmourModel<T>> void renderArmor(
|
||||
Models<T, ? extends PonyModel<T>> pony, MatrixStack matrices,
|
||||
VertexConsumerProvider renderContext, int light, T entity,
|
||||
VertexConsumerProvider provider, int light, T entity,
|
||||
float limbDistance, float limbAngle,
|
||||
float age, float headYaw, float headPitch,
|
||||
EquipmentSlot armorSlot, ArmourLayer layer) {
|
||||
|
@ -55,61 +57,56 @@ public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & Po
|
|||
return;
|
||||
}
|
||||
|
||||
Identifier texture = ArmourTextureResolver.INSTANCE.getTexture(entity, stack, armorSlot, layer, null);
|
||||
ArmourVariant variant = ArmourTextureResolver.INSTANCE.getVariant(layer, texture);
|
||||
|
||||
boolean glint = stack.hasGlint();
|
||||
|
||||
pony.getArmourModel(stack, layer, variant)
|
||||
.filter(m -> m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body()))
|
||||
.ifPresent(model -> {
|
||||
float red = 1;
|
||||
float green = 1;
|
||||
float blue = 1;
|
||||
int color = stack.isIn(ItemTags.DYEABLE) ? DyedColorComponent.getColor(stack, -6265536) : Colors.WHITE;
|
||||
|
||||
DyedColorComponent colorComponent = stack.get(DataComponentTypes.DYED_COLOR);
|
||||
Set<PonyArmourModel<?>> models = glint ? new HashSet<>() : null;
|
||||
|
||||
if (colorComponent != null) {
|
||||
int color = colorComponent.rgb();
|
||||
red = Color.r(color);
|
||||
green = Color.g(color);
|
||||
blue = Color.b(color);
|
||||
for (ArmorMaterial.Layer armorLayer : ArmourTextureResolver.INSTANCE.getArmorLayers(stack, color)) {
|
||||
ArmourTexture layerTexture = ArmourTextureResolver.INSTANCE.getTexture(stack, layer, armorLayer);
|
||||
|
||||
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);
|
||||
}
|
||||
m.render(matrices, provider.getBuffer(RenderLayer.getArmorCutoutNoCull(layerTexture.texture())), light, OverlayTexture.DEFAULT_UV, red, green, blue, 1);
|
||||
if (glint) {
|
||||
models.add(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
model.render(matrices, getArmorConsumer(renderContext, texture, glint), light, OverlayTexture.DEFAULT_UV, red, green, blue, 1);
|
||||
ArmorTrim trim = stack.get(DataComponentTypes.TRIM);
|
||||
|
||||
if (colorComponent != null) {
|
||||
Identifier tex = ArmourTextureResolver.INSTANCE.getTexture(entity, stack, armorSlot, layer, "overlay");
|
||||
pony.getArmourModel(stack, layer, ArmourTextureResolver.INSTANCE.getVariant(layer, tex))
|
||||
.filter(m -> m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body()))
|
||||
.ifPresent(m -> {
|
||||
m.render(matrices, getArmorConsumer(renderContext, tex, false), light, OverlayTexture.DEFAULT_UV, 1, 1, 1, 1);
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
ArmorTrim trim = stack.get(DataComponentTypes.TRIM);
|
||||
|
||||
if (trim != null && stack.getItem() instanceof ArmorItem armor) {
|
||||
pony.getArmourModel(stack, layer, ArmourVariant.TRIM)
|
||||
.filter(m -> m.poseModel(entity, limbAngle, limbDistance, age, headYaw, headPitch, armorSlot, layer, pony.body()))
|
||||
.ifPresent(m -> {
|
||||
m.render(matrices, getTrimConsumer(renderContext, armor.getMaterial(), trim, layer, glint), 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 getArmorConsumer(VertexConsumerProvider provider, Identifier texture, boolean glint) {
|
||||
return ItemRenderer.getArmorGlintConsumer(provider, ArmorRenderLayers.getArmorTranslucentNoCull(texture, false), false, glint);
|
||||
}
|
||||
|
||||
private static VertexConsumer getTrimConsumer(VertexConsumerProvider provider, RegistryEntry<ArmorMaterial> material, ArmorTrim trim, ArmourLayer layer, boolean glint) {
|
||||
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)
|
||||
);
|
||||
RenderLayer renderLayer = ArmorRenderLayers.getArmorTranslucentNoCull(TexturedRenderLayers.ARMOR_TRIMS_ATLAS_TEXTURE, trim.getPattern().value().decal());
|
||||
|
||||
return sprite.getTextureSpecificVertexConsumer(ItemRenderer.getDirectItemGlintConsumer(provider, renderLayer, true, glint));
|
||||
return sprite.getTextureSpecificVertexConsumer(
|
||||
provider.getBuffer(TexturedRenderLayers.getArmorTrims(trim.getPattern().value().decal()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue