mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-25 05:48:00 +01:00
Rewrite armour texture resolution and add support for custom armour using the CustomModelData nbt tag.
This commit is contained in:
parent
aa97a81249
commit
46f171523a
5 changed files with 115 additions and 77 deletions
|
@ -3,8 +3,16 @@ package com.minelittlepony.api.model.armour;
|
|||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import com.minelittlepony.api.model.IModelWrapper;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.api.model.IModelWrapper;
|
||||
import com.minelittlepony.api.pony.IPonyData;
|
||||
|
||||
/**
|
||||
* Wrapper for an armour model and texture.
|
||||
*
|
||||
* @param <V> The type of the contained armour model.
|
||||
*/
|
||||
public interface IArmour<V extends IArmourModel> extends IModelWrapper {
|
||||
/**
|
||||
* Registers a custom armour for the supplied item.
|
||||
|
@ -19,7 +27,10 @@ public interface IArmour<V extends IArmourModel> extends IModelWrapper {
|
|||
|
||||
/**
|
||||
* Gets the armour model to render for the given layer.
|
||||
* <p>
|
||||
* Return null to preserve the default behaviour or override and return your custom model.
|
||||
*/
|
||||
@Nullable
|
||||
V getModel(ArmourLayer layer);
|
||||
|
||||
/**
|
||||
|
@ -30,4 +41,9 @@ public interface IArmour<V extends IArmourModel> extends IModelWrapper {
|
|||
default IArmourTextureResolver getTextureResolver(IArmourTextureResolver defaultResolver) {
|
||||
return defaultResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
default IArmour<V> applyMetadata(IPonyData meta) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,11 @@ import net.minecraft.util.Identifier;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A resolver for looking up the texture for a piece of armour.
|
||||
* <p>
|
||||
* This is for modders who want to override the default implementation found in {@link DefaultArmourTextureResolver}.
|
||||
*/
|
||||
public interface IArmourTextureResolver {
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
package com.minelittlepony.client.model.armour;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.texture.TextureManager;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.item.ArmorItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
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.minelittlepony.api.model.armour.ArmourLayer;
|
||||
import com.minelittlepony.api.model.armour.ArmourVariant;
|
||||
import com.minelittlepony.api.model.armour.IArmourTextureResolver;
|
||||
|
@ -16,96 +18,109 @@ import com.minelittlepony.util.ResourceUtil;
|
|||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* The default texture resolver used by Mine Little Pony.
|
||||
* <p>
|
||||
* The search order is as follows:
|
||||
* <p>
|
||||
* namespace:textures/models/armor/material_layer_[outer|inner](_overlay)(_custom_[0-9]+)_pony.png
|
||||
* namespace:textures/models/armor/material_layer_[outer|inner](_overlay)(_custom_[0-9]+).png
|
||||
* namespace:textures/models/armor/material_layer_[1|2](_overlay)(_custom_[0-9]+)_pony.png
|
||||
* namespace:textures/models/armor/material_layer_[1|2](_overlay)(_custom_[0-9]+).png
|
||||
* namespace:textures/models/armor/material_layer_[outer|inner](_overlay)_pony.png
|
||||
* namespace:textures/models/armor/material_layer_[outer|inner](_overlay).png
|
||||
* namespace:textures/models/armor/material_layer_[1|2](_overlay)_pony.png
|
||||
* namespace:textures/models/armor/material_layer_[1|2](_overlay).png
|
||||
* <p>
|
||||
* - if namespace is "minecraft" will be rewritten to "minelittlepony"
|
||||
* <p>
|
||||
* For how modders can customise both the model and texture please refer to {@link IArmour} and {@link IArmourTextureResolver}.
|
||||
*
|
||||
* @see IArmour
|
||||
* @see IArmourTextureResolver
|
||||
*/
|
||||
public class DefaultArmourTextureResolver implements IArmourTextureResolver {
|
||||
|
||||
private final Map<String, Identifier> HUMAN_ARMOUR = new HashMap<>();
|
||||
private final Map<Identifier, Identifier> PONY_ARMOUR = new HashMap<>();
|
||||
private final Cache<String, Identifier> cache = CacheBuilder.newBuilder()
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.<String, Identifier>build();
|
||||
|
||||
@Override
|
||||
public Identifier getTexture(LivingEntity entity, ItemStack itemstack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) {
|
||||
type = Strings.nullToEmpty(type);
|
||||
public Identifier getTexture(LivingEntity entity, ItemStack stack, EquipmentSlot slot, ArmourLayer layer, @Nullable String type) {
|
||||
Identifier material = new Identifier(((ArmorItem) stack.getItem()).getMaterial().getName());
|
||||
String custom = getCustom(stack);
|
||||
|
||||
ArmorItem item = (ArmorItem) itemstack.getItem();
|
||||
String texture = item.getMaterial().getName();
|
||||
try {
|
||||
return cache.get(String.format("%s#%s#%s#%s", material, layer, type, custom), () -> {
|
||||
String typed = Strings.nullToEmpty(type);
|
||||
String extra = typed.isEmpty() ? "" : "_" + typed;
|
||||
|
||||
String domain = "minecraft";
|
||||
Identifier texture;
|
||||
|
||||
int idx = texture.indexOf(':');
|
||||
if (idx > -1) {
|
||||
domain = texture.substring(0, idx);
|
||||
texture = texture.substring(idx + 1);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
private String getCustom(ItemStack stack) {
|
||||
if (stack.hasTag() && stack.getTag().contains("CustomModelData", NbtElement.NUMBER_TYPE)) {
|
||||
return "custom_" + stack.getTag().getInt("CustomModelData");
|
||||
}
|
||||
return "none";
|
||||
}
|
||||
|
||||
@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);
|
||||
|
||||
if (texture != null) {
|
||||
return texture;
|
||||
}
|
||||
|
||||
String customType = type.isEmpty() ? "" : "_" + type;
|
||||
|
||||
String res = ResourceUtil.format("%s:textures/models/armor/%s_layer_%s%s.png", domain, texture, layer, customType);
|
||||
String oldRes = ResourceUtil.format("%s:textures/models/armor/%s_layer_%d%s.png", domain, texture, layer.getLegacyId(), customType);
|
||||
|
||||
Identifier human = getArmorTexture(res, type);
|
||||
Identifier pony = ponifyResource(human);
|
||||
|
||||
Identifier oldHuman = getArmorTexture(oldRes, type);
|
||||
Identifier oldPony = ponifyResource(oldHuman);
|
||||
|
||||
return resolve(oldPony, pony, oldHuman, human);
|
||||
return resolveHumanOrPony(ResourceUtil.format("%s:textures/models/armor/%s_layer_%d%s.png", material.getNamespace(), material.getPath(), layer.getLegacyId(), extra), type);
|
||||
}
|
||||
|
||||
private Identifier resolve(Identifier... resources) {
|
||||
// check resource packs for either texture.
|
||||
@Nullable
|
||||
private Identifier resolveHumanOrPony(String res, String type) {
|
||||
Identifier human = new Identifier(res);
|
||||
|
||||
ResourceManager manager = MinecraftClient.getInstance().getResourceManager();
|
||||
|
||||
for (Identifier i : resources) {
|
||||
if (manager.containsResource(i)) {
|
||||
return i;
|
||||
}
|
||||
String domain = human.getNamespace();
|
||||
if ("minecraft".equals(domain)) {
|
||||
domain = "minelittlepony"; // it's a vanilla armor. I provide these.
|
||||
}
|
||||
|
||||
return resources[resources.length - 1];
|
||||
Identifier pony = new Identifier(domain, human.getPath().replace(".png", "_pony.png"));
|
||||
|
||||
if (isValid(pony)) {
|
||||
return pony;
|
||||
}
|
||||
|
||||
if (isValid(human)) {
|
||||
return human;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Identifier ponifyResource(Identifier human) {
|
||||
return PONY_ARMOUR.computeIfAbsent(human, key -> {
|
||||
String domain = key.getNamespace();
|
||||
if ("minecraft".equals(domain)) {
|
||||
domain = "minelittlepony"; // it's a vanilla armor. I provide these.
|
||||
}
|
||||
|
||||
return new Identifier(domain, key.getPath().replace(".png", "_pony.png"));
|
||||
});
|
||||
}
|
||||
|
||||
private Identifier getArmorTexture(String def, String type) {
|
||||
return HUMAN_ARMOUR.computeIfAbsent(def + "#" + type, s -> {
|
||||
Identifier defId = new Identifier(def);
|
||||
|
||||
if (type.isEmpty() || type.equals(def)) {
|
||||
return defId;
|
||||
}
|
||||
|
||||
Identifier modId = new Identifier(type);
|
||||
|
||||
Path modPath = Paths.get(modId.getPath()).getParent();
|
||||
|
||||
if (modPath == null) {
|
||||
return defId;
|
||||
}
|
||||
|
||||
Path path = modPath.resolve(Paths.get(defId.getPath()).getFileName());
|
||||
|
||||
Identifier interemId = new Identifier(modId.getNamespace(), path.toString().replace('\\', '/'));
|
||||
|
||||
if (MinecraftClient.getInstance().getResourceManager().containsResource(interemId)) {
|
||||
return interemId;
|
||||
}
|
||||
|
||||
return modId;
|
||||
});
|
||||
private final boolean isValid(Identifier texture) {
|
||||
return MinecraftClient.getInstance().getResourceManager().containsResource(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -62,6 +62,9 @@ public class ArmourFeature<T extends LivingEntity, M extends EntityModel<T> & IP
|
|||
armour.applyMetadata(pony.getBody().getMetadata());
|
||||
|
||||
V model = armour.getModel(layer);
|
||||
if (model == null) {
|
||||
model = pony.<V>getArmor().getModel(layer);
|
||||
}
|
||||
|
||||
if (model.prepareToRender(armorSlot, layer)) {
|
||||
pony.getBody().copyAttributes(model);
|
||||
|
|
|
@ -21,5 +21,4 @@ public class VillagerPonyRenderer extends AbstractNpcRenderer<VillagerEntity, Vi
|
|||
super.scale(villager, stack, ticks);
|
||||
stack.scale(BASE_MODEL_SCALE, BASE_MODEL_SCALE, BASE_MODEL_SCALE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue