Refactor armor texture management to streamline player rendering and remove redundant classes. Update mod version to 1.2.1.

This commit is contained in:
2026-03-29 17:07:14 +02:00
parent 6ef7e52a37
commit 2040025e3c
7 changed files with 80 additions and 131 deletions

View File

@@ -4,7 +4,7 @@ plugins {
id 'org.spongepowered.mixin' version '0.7.+' id 'org.spongepowered.mixin' version '0.7.+'
} }
version = '1.2.0' version = '1.2.1'
group = 'dev.azimkin.armoryarmor' group = 'dev.azimkin.armoryarmor'
archivesBaseName = 'ArmoryArmor' archivesBaseName = 'ArmoryArmor'
def mod_id = "armory_armor" def mod_id = "armory_armor"

View File

@@ -1,22 +1,47 @@
package top.azimkin.armoryarmor.client; package top.azimkin.armoryarmor.client;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.Item;
import net.minecraftforge.registries.RegistryObject;
import top.azimkin.armoryarmor.init.ArmoryArmorModItems;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
public class ArmorTextureManager { public class ArmorTextureManager {
private static final Map<Item, ArmorTextureManager.ArmorData> armorItems = new LinkedHashMap<>();
private static final ResourceLocation DEFAULT_TEXTURE = new ResourceLocation("armory_armor:textures/models/armor/voidarmor_layer_2.png");
private final Map<String, PreparableTexture> textures = new ConcurrentHashMap<>(); private final Map<String, PreparableTexture> textures = new ConcurrentHashMap<>();
public ArmorTextureManager() {
computeArmorItems();
}
public ResourceLocation getTexture(ResourceLocation playerSkin, ArmorData armorSkin) { public ResourceLocation getTexture(ResourceLocation playerSkin, ArmorData armorSkin) {
//textures.clear(); //textures.clear();
PreparableTexture data = textures.get(getKey(playerSkin, armorSkin.resourceLocation())); PreparableTexture data = textures.get(getKey(playerSkin, armorSkin.resourceLocation()));
return data == null ? prepareTextureInMap(playerSkin, armorSkin) : data.rl(); return data == null ? prepareTextureInMap(playerSkin, armorSkin) : data.rl();
} }
public ResourceLocation getPlayerSkinTexture(AbstractClientPlayer player) {
return getPlayerSkinTexture(player, player.getSkinTextureLocation());
}
public ResourceLocation getPlayerSkinTexture(AbstractClientPlayer player, ResourceLocation originalTexture) {
Item slotItem = player.getItemBySlot(EquipmentSlot.LEGS).getItem();
ArmorData data = armorItems.get(slotItem);
if (data == null) {
return originalTexture;
}
return getTexture(originalTexture, data);
}
public ResourceLocation prepareTextureInMap(ResourceLocation playerSkin, ArmorData armorSkin) { public ResourceLocation prepareTextureInMap(ResourceLocation playerSkin, ArmorData armorSkin) {
try (NativeImage img = TextureUtil.fromResourceLocation(playerSkin); try (NativeImage img = TextureUtil.fromResourceLocation(playerSkin);
NativeImage img2 = TextureUtil.fromResourceLocation(armorSkin.resourceLocation());) { NativeImage img2 = TextureUtil.fromResourceLocation(armorSkin.resourceLocation());) {
@@ -28,8 +53,9 @@ public class ArmorTextureManager {
TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.BODY); TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.BODY);
TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.LEFT_HAND); TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.LEFT_HAND);
TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.RIGHT_HAND); TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.RIGHT_HAND);
if (armorSkin.hideHat) if (armorSkin.hideHat) {
TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.HAT); TextureUtil.clearLayerPart(first, TextureUtil.SkinLayer.HAT);
}
ResourceLocation rl = TextureUtil.toResourceLocation(TextureUtil.combine(first, second)); ResourceLocation rl = TextureUtil.toResourceLocation(TextureUtil.combine(first, second));
textures.put(getKey(playerSkin, armorSkin.resourceLocation()), new PreparableTexture(rl, false)); textures.put(getKey(playerSkin, armorSkin.resourceLocation()), new PreparableTexture(rl, false));
return rl; return rl;
@@ -40,6 +66,25 @@ public class ArmorTextureManager {
return playerSkin.toString() + "|" + armorSkin.toString(); return playerSkin.toString() + "|" + armorSkin.toString();
} }
private static void fastCmpFunc(RegistryObject<? extends Item> i, String loc, boolean hideHat) {
ResourceLocation t = ResourceLocation.tryParse(loc);
if (t == null) t = DEFAULT_TEXTURE;
armorItems.put(i.get(), new ArmorTextureManager.ArmorData(t, hideHat));
}
private static void computeArmorItems() {
if (!armorItems.isEmpty()) return;
fastCmpFunc(ArmoryArmorModItems.SEAL_LEGGINGS, "armory_armor:textures/entities/seal.png", false);
fastCmpFunc(ArmoryArmorModItems.USMC_LEGGINGS, "armory_armor:textures/entities/usmc.png", false);
fastCmpFunc(ArmoryArmorModItems.ALQAIDA_LEGGINGS, "armory_armor:textures/entities/al-qaida.png", true);
fastCmpFunc(ArmoryArmorModItems.ISLAMIC_LEGGINGS, "armory_armor:textures/entities/islamic_army.png", true);
fastCmpFunc(ArmoryArmorModItems.USMC_2_LEGGINGS, "armory_armor:textures/entities/usmc2.png", false);
fastCmpFunc(ArmoryArmorModItems.CHECHEN_1, "armory_armor:textures/entities/chechen_1.png", true);
fastCmpFunc(ArmoryArmorModItems.CHECHEN_2, "armory_armor:textures/entities/chechen_2.png", true);
fastCmpFunc(ArmoryArmorModItems.AL_QUAIDA_2, "armory_armor:textures/entities/al_quaida_2.png", true);
fastCmpFunc(ArmoryArmorModItems.AL_QUAIDA_3, "armory_armor:textures/entities/al_quaida_3.png", true);
}
public record PreparableTexture(ResourceLocation rl, boolean isCompleted) {} public record PreparableTexture(ResourceLocation rl, boolean isCompleted) {}
public record ArmorData(ResourceLocation resourceLocation, boolean hideHat) {} public record ArmorData(ResourceLocation resourceLocation, boolean hideHat) {}
} }

View File

@@ -79,37 +79,6 @@ public class TextureUtil {
} }
} }
return null; return null;
/*int texId = texture.getId();
GlStateManager._bindTexture(texId);
int width = 64; // standard skin width
int height = 64; // standard skin height
ByteBuffer buffer = MemoryUtil.memAlloc(width * height * 4);
GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, GL12.GL_BGRA, GL11.GL_UNSIGNED_BYTE, buffer);
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes);
MemoryUtil.memFree(buffer);
// Convert bytes to NativeImage if needed
NativeImage image = new NativeImage(width, height, false);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int i = (y * width + x) * 4;
int b = buffer.get(i) & 0xFF;
int g = buffer.get(i+1) & 0xFF;
int r = buffer.get(i+2) & 0xFF;
int a = buffer.get(i+3) & 0xFF;
int argb = (a << 24) | (r << 16) | (g << 8) | b;
image.setPixelRGBA(x, y, argb);
}
}
MemoryUtil.memFree(buffer);
return image;*/
} }
public static BufferedImage combine(BufferedImage image1, BufferedImage image2) { public static BufferedImage combine(BufferedImage image1, BufferedImage image2) {
@@ -120,20 +89,17 @@ public class TextureUtil {
} }
public static void clearLayerPart(BufferedImage image, SkinLayer layer) { public static void clearLayerPart(BufferedImage image, SkinLayer layer) {
//Graphics2D g2d = image.createGraphics(); clearArea(image, layer.areas);
//g2d.setComposite(AlphaComposite.Clear);
clearArea(image, layer.firstArea);
clearArea(image, layer.secondArea);
//g2d.dispose();
} }
private static void clearArea(BufferedImage image, Area area) { private static void clearArea(BufferedImage image, Area... areas) {
for (int x = area.x1; x < area.x2; x++) { for (var area : areas) {
for (int y = area.y1; y < area.y2; y++) { for (int x = area.x1; x <= area.x2; x++) {
image.setRGB(x, y, 0); for (int y = area.y1; y <= area.y2; y++) {
image.setRGB(x, y, 0);
}
} }
} }
//g2d.fillRect(area.x1, area.y1, area.x1-area.x2, area.y1-area.y2);
} }
public static ResourceLocation toResourceLocation(BufferedImage image) { public static ResourceLocation toResourceLocation(BufferedImage image) {
@@ -154,10 +120,9 @@ public class TextureUtil {
LEFT_FEET(new Area(4, 48, 11, 51), new Area(0, 52, 15, 63)), LEFT_FEET(new Area(4, 48, 11, 51), new Area(0, 52, 15, 63)),
RIGHT_FEET(new Area(4, 32, 11, 35), new Area(0, 36, 15, 47)), RIGHT_FEET(new Area(4, 32, 11, 35), new Area(0, 36, 15, 47)),
HAT(new Area(40, 0, 55, 7), new Area(32, 8, 63, 15)); HAT(new Area(40, 0, 55, 7), new Area(32, 8, 63, 15));
public final Area firstArea, secondArea; public final Area[] areas;
SkinLayer(Area firstArea, Area secondArea) { SkinLayer(Area... areas) {
this.firstArea = firstArea; this.areas = areas;
this.secondArea = secondArea;
} }
} }

View File

@@ -0,0 +1,21 @@
package top.azimkin.armoryarmor.mixin;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import top.azimkin.armoryarmor.client.ArmorTextureManager;
@Mixin(AbstractClientPlayer.class)
public class AbstractClientPlayerMixin {
@Unique
private final ArmorTextureManager armoryArmor$manager = new ArmorTextureManager();
@Inject(method = "getSkinTextureLocation", at = {@At("TAIL")}, cancellable = true)
public void getSkinTextureLocation(CallbackInfoReturnable<ResourceLocation> cir) {
cir.setReturnValue(armoryArmor$manager.getPlayerSkinTexture((AbstractClientPlayer) (Object) this, cir.getReturnValue()));
}
}

View File

@@ -1,19 +0,0 @@
package top.azimkin.armoryarmor.mixin;
import net.minecraft.client.player.AbstractClientPlayer;
import net.minecraft.client.renderer.entity.player.PlayerRenderer;
import net.minecraft.resources.ResourceLocation;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import top.azimkin.armoryarmor.client.InfoHolder;
@Mixin(PlayerRenderer.class)
public class PlayerRenderMixin {
@Inject(method = "getTextureLocation(Lnet/minecraft/client/player/AbstractClientPlayer;)Lnet/minecraft/resources/ResourceLocation;", at = @At(value = "HEAD"), cancellable = true)
public void getTextureLocation(AbstractClientPlayer p_117783_, CallbackInfoReturnable<ResourceLocation> cir) {
if (InfoHolder.TOP_TEXTURE == null) return;
cir.setReturnValue(InfoHolder.TOP_TEXTURE);
}
}

View File

@@ -1,63 +0,0 @@
package top.azimkin.armoryarmor.procedures;
import net.minecraft.world.item.Item;
import net.minecraftforge.client.event.RenderPlayerEvent;
import net.minecraftforge.eventbus.api.EventPriority;
import net.minecraftforge.registries.RegistryObject;
import top.azimkin.armoryarmor.client.ArmorTextureManager;
import top.azimkin.armoryarmor.client.InfoHolder;
import top.azimkin.armoryarmor.init.ArmoryArmorModItems;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.client.event.RenderLivingEvent;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.client.player.AbstractClientPlayer;
import java.util.LinkedHashMap;
import java.util.Map;
@Mod.EventBusSubscriber(value = {Dist.CLIENT})
public class RenderEventProcedure {
private static final Map<Item, ArmorTextureManager.ArmorData> armorItems = new LinkedHashMap<>();
private static final ResourceLocation DEFAULT_TEXTURE = new ResourceLocation("armory_armor:textures/models/armor/voidarmor_layer_2.png");
private static final ArmorTextureManager manager = new ArmorTextureManager();
@OnlyIn(Dist.CLIENT)
@SubscribeEvent(priority = EventPriority.HIGHEST)
public static void onEventTriggeredPre(RenderPlayerEvent.Pre event) {
computeArmorItems();
AbstractClientPlayer player = (AbstractClientPlayer) event.getEntity();
Item slotItem = player.getItemBySlot(EquipmentSlot.LEGS).getItem();
ArmorTextureManager.ArmorData data = armorItems.get(slotItem);
if (data == null) {
InfoHolder.TOP_TEXTURE = null;
return;
}
InfoHolder.TOP_TEXTURE = manager.getTexture(player.getSkinTextureLocation(), data);
}
private static void fastCmpFunc(RegistryObject<? extends Item> i, String loc, boolean hideHat) {
ResourceLocation t = ResourceLocation.tryParse(loc);
if (t == null) t = DEFAULT_TEXTURE;
RenderEventProcedure.armorItems.put(i.get(), new ArmorTextureManager.ArmorData(t, hideHat));
}
private static void computeArmorItems() {
if (!armorItems.isEmpty()) return;
fastCmpFunc(ArmoryArmorModItems.SEAL_LEGGINGS,"armory_armor:textures/entities/seal.png", false);
fastCmpFunc(ArmoryArmorModItems.USMC_LEGGINGS,"armory_armor:textures/entities/usmc.png", false);
fastCmpFunc(ArmoryArmorModItems.ALQAIDA_LEGGINGS,"armory_armor:textures/entities/al-qaida.png", true);
fastCmpFunc(ArmoryArmorModItems.ISLAMIC_LEGGINGS,"armory_armor:textures/entities/islamic_army.png", true);
fastCmpFunc(ArmoryArmorModItems.USMC_2_LEGGINGS,"armory_armor:textures/entities/usmc2.png", false);
fastCmpFunc(ArmoryArmorModItems.CHECHEN_1,"armory_armor:textures/entities/chechen_1.png", true);
fastCmpFunc(ArmoryArmorModItems.CHECHEN_2,"armory_armor:textures/entities/chechen_2.png", true);
fastCmpFunc(ArmoryArmorModItems.AL_QUAIDA_2,"armory_armor:textures/entities/al_quaida_2.png", true);
fastCmpFunc(ArmoryArmorModItems.AL_QUAIDA_3,"armory_armor:textures/entities/al_quaida_3.png", true);
}
}

View File

@@ -6,7 +6,7 @@
"refmap": "armory_armor.refmap.json", "refmap": "armory_armor.refmap.json",
"mixins": [], "mixins": [],
"client": [ "client": [
"PlayerRenderMixin" "AbstractClientPlayerMixin"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1