166 lines
5.4 KiB
Java
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 {
|
|
}
|
|
}
|