minecraft-src/net/minecraft/client/renderer/block/model/TextureSlots.java
2025-07-04 03:45:38 +03:00

166 lines
5.4 KiB
Java

package net.minecraft.client.renderer.block.model;
import com.google.common.collect.Lists;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.mojang.logging.LogUtils;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectMaps;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.renderer.block.model.TextureSlots.Data.Builder;
import net.minecraft.client.resources.model.Material;
import net.minecraft.client.resources.model.ModelDebugName;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
@Environment(EnvType.CLIENT)
public class TextureSlots {
public static final TextureSlots EMPTY = new TextureSlots(Map.of());
private static final char REFERENCE_CHAR = '#';
private final Map<String, Material> resolvedValues;
TextureSlots(Map<String, Material> resolvedValues) {
this.resolvedValues = resolvedValues;
}
@Nullable
public Material getMaterial(String name) {
if (isTextureReference(name)) {
name = name.substring(1);
}
return (Material)this.resolvedValues.get(name);
}
private static boolean isTextureReference(String name) {
return name.charAt(0) == '#';
}
public static TextureSlots.Data parseTextureMap(JsonObject json, ResourceLocation atlas) {
Builder builder = new Builder();
for (Entry<String, JsonElement> entry : json.entrySet()) {
parseEntry(atlas, (String)entry.getKey(), ((JsonElement)entry.getValue()).getAsString(), builder);
}
return builder.build();
}
private static void parseEntry(ResourceLocation atlas, String name, String material, Builder builder) {
if (isTextureReference(material)) {
builder.addReference(name, material.substring(1));
} else {
ResourceLocation resourceLocation = ResourceLocation.tryParse(material);
if (resourceLocation == null) {
throw new JsonParseException(material + " is not valid resource location");
}
builder.addTexture(name, new Material(atlas, resourceLocation));
}
}
@Environment(EnvType.CLIENT)
public record Data(Map<String, TextureSlots.SlotContents> values) {
public static final TextureSlots.Data EMPTY = new TextureSlots.Data(Map.of());
}
@Environment(EnvType.CLIENT)
record Reference(String target) implements TextureSlots.SlotContents {
}
@Environment(EnvType.CLIENT)
public static class Resolver {
private static final Logger LOGGER = LogUtils.getLogger();
private final List<TextureSlots.Data> entries = new ArrayList();
public TextureSlots.Resolver addLast(TextureSlots.Data data) {
this.entries.addLast(data);
return this;
}
public TextureSlots.Resolver addFirst(TextureSlots.Data data) {
this.entries.addFirst(data);
return this;
}
public TextureSlots resolve(ModelDebugName name) {
if (this.entries.isEmpty()) {
return TextureSlots.EMPTY;
} else {
Object2ObjectMap<String, Material> object2ObjectMap = new Object2ObjectArrayMap<>();
Object2ObjectMap<String, TextureSlots.Reference> object2ObjectMap2 = new Object2ObjectArrayMap<>();
for (TextureSlots.Data data : Lists.reverse(this.entries)) {
data.values.forEach((string, slotContents) -> {
switch (slotContents) {
case TextureSlots.Value value:
object2ObjectMap2.remove(string);
object2ObjectMap.put(string, value.material());
break;
case TextureSlots.Reference reference:
object2ObjectMap.remove(string);
object2ObjectMap2.put(string, reference);
break;
default:
throw new MatchException(null, null);
}
});
}
if (object2ObjectMap2.isEmpty()) {
return new TextureSlots(object2ObjectMap);
} else {
boolean bl = true;
while (bl) {
bl = false;
ObjectIterator<it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry<String, TextureSlots.Reference>> objectIterator = Object2ObjectMaps.fastIterator(
object2ObjectMap2
);
while (objectIterator.hasNext()) {
it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry<String, TextureSlots.Reference> entry = (it.unimi.dsi.fastutil.objects.Object2ObjectMap.Entry<String, TextureSlots.Reference>)objectIterator.next();
Material material = object2ObjectMap.get(((TextureSlots.Reference)entry.getValue()).target);
if (material != null) {
object2ObjectMap.put((String)entry.getKey(), material);
objectIterator.remove();
bl = true;
}
}
}
if (!object2ObjectMap2.isEmpty()) {
LOGGER.warn(
"Unresolved texture references in {}:\n{}",
name.debugName(),
object2ObjectMap2.entrySet()
.stream()
.map(entryx -> "\t#" + (String)entryx.getKey() + "-> #" + ((TextureSlots.Reference)entryx.getValue()).target + "\n")
.collect(Collectors.joining())
);
}
return new TextureSlots(object2ObjectMap);
}
}
}
}
@Environment(EnvType.CLIENT)
public sealed interface SlotContents permits TextureSlots.Value, TextureSlots.Reference {
}
@Environment(EnvType.CLIENT)
record Value(Material material) implements TextureSlots.SlotContents {
}
}