mirror of
https://github.com/MineLittlePony/MineLittlePony.git
synced 2024-11-25 13:57:59 +01:00
Optimise villager rendering by first combining the textures so we can render the model once.
This commit is contained in:
parent
8911cbf337
commit
6cb383952c
2 changed files with 111 additions and 64 deletions
|
@ -2,23 +2,16 @@ package com.minelittlepony.client.render.entity.npc;
|
|||
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.render.VertexConsumerProvider;
|
||||
import net.minecraft.client.render.entity.feature.FeatureRendererContext;
|
||||
import net.minecraft.client.render.entity.feature.VillagerResourceMetadata;
|
||||
import net.minecraft.client.render.entity.feature.VillagerResourceMetadata.HatType;
|
||||
import net.minecraft.client.render.entity.model.EntityModel;
|
||||
import net.minecraft.client.render.entity.model.ModelWithHat;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.resource.Resource;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.registry.DefaultedRegistry;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.village.VillagerData;
|
||||
import net.minecraft.village.VillagerDataContainer;
|
||||
|
@ -28,9 +21,9 @@ import net.minecraft.village.VillagerType;
|
|||
import com.minelittlepony.client.model.IPonyModel;
|
||||
import com.minelittlepony.client.render.IPonyRenderContext;
|
||||
import com.minelittlepony.client.render.entity.feature.AbstractPonyFeature;
|
||||
import com.minelittlepony.client.util.render.TextureFlattener;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
class NpcClothingFeature<
|
||||
T extends LivingEntity & VillagerDataContainer,
|
||||
|
@ -45,13 +38,8 @@ class NpcClothingFeature<
|
|||
a.put(5, new Identifier("diamond"));
|
||||
});
|
||||
|
||||
private final Object2ObjectMap<VillagerType, HatType> typeHatCache = new Object2ObjectOpenHashMap<>();
|
||||
private final Object2ObjectMap<VillagerProfession, HatType> profHatCache = new Object2ObjectOpenHashMap<>();
|
||||
|
||||
private final String entityType;
|
||||
|
||||
private final ResourceManager resourceManager = MinecraftClient.getInstance().getResourceManager();
|
||||
|
||||
public NpcClothingFeature(C context, String type) {
|
||||
super(context);
|
||||
entityType = type;
|
||||
|
@ -60,72 +48,61 @@ class NpcClothingFeature<
|
|||
public static Identifier getClothingTexture(VillagerDataContainer entity, String entityType) {
|
||||
VillagerProfession profession = entity.getVillagerData().getProfession();
|
||||
|
||||
return createTexture("minelittlepony", "profession", entityType, Registry.VILLAGER_PROFESSION.getId(profession));
|
||||
return createTexture("minelittlepony", entityType, "profession", Registry.VILLAGER_PROFESSION.getId(profession));
|
||||
}
|
||||
|
||||
public static Identifier createTexture(String namespace, String type, String entityType, Identifier profession) {
|
||||
return new Identifier(namespace, String.format("textures/entity/%s/%s/%s.png", entityType, type, profession.getPath()));
|
||||
public static Identifier createTexture(String namespace, String entityType, String category, Identifier identifier) {
|
||||
return new Identifier(namespace, String.format("textures/entity/%s/%s/%s.png", entityType, category, identifier.getPath()));
|
||||
}
|
||||
|
||||
public Identifier createTexture(String category, Identifier identifier) {
|
||||
return createTexture("minelittlepony", entityType, category, identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(MatrixStack matrixStack, VertexConsumerProvider provider, int i, T entity, float f, float g, float h, float j, float k, float l) {
|
||||
if (!entity.isInvisible()) {
|
||||
if (entity.isInvisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
VillagerData data = entity.getVillagerData();
|
||||
VillagerType type = data.getType();
|
||||
VillagerProfession profession = data.getProfession();
|
||||
VillagerData data = entity.getVillagerData();
|
||||
M entityModel = getContextModel();
|
||||
|
||||
HatType typeHatLayer = getHatType(typeHatCache, "type", Registry.VILLAGER_TYPE, type);
|
||||
HatType profHatLayer = getHatType(profHatCache, "profession", Registry.VILLAGER_PROFESSION, profession);
|
||||
M entityModel = getContextModel();
|
||||
|
||||
entityModel.setHatVisible(
|
||||
profHatLayer == VillagerResourceMetadata.HatType.NONE
|
||||
|| (profHatLayer == VillagerResourceMetadata.HatType.PARTIAL && typeHatLayer != VillagerResourceMetadata.HatType.FULL)
|
||||
);
|
||||
|
||||
Identifier typeSkin = findTexture("type", Registry.VILLAGER_TYPE.getId(type));
|
||||
if (entity.isBaby() || data.getProfession() == VillagerProfession.NONE) {
|
||||
Identifier typeSkin = createTexture("type", Registry.VILLAGER_TYPE.getId(data.getType()));
|
||||
|
||||
renderModel(entityModel, typeSkin, matrixStack, provider, i, entity, 1, 1, 1);
|
||||
|
||||
entityModel.setHatVisible(true);
|
||||
|
||||
if (profession != VillagerProfession.NONE && !entity.isBaby()) {
|
||||
Identifier professionSkin = findTexture("profession", Registry.VILLAGER_PROFESSION.getId(profession));
|
||||
|
||||
renderModel(entityModel, professionSkin, matrixStack, provider, i, entity, 1, 1, 1);
|
||||
|
||||
if (profession != VillagerProfession.NITWIT) {
|
||||
Identifier levelSkin = findTexture("profession_level", LEVEL_TO_ID.get(MathHelper.clamp(data.getLevel(), 1, LEVEL_TO_ID.size())));
|
||||
|
||||
renderModel(entityModel, levelSkin, matrixStack, provider, i, entity, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
renderModel(entityModel, getMergedTexture(data), matrixStack, provider, i, entity, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public <K> VillagerResourceMetadata.HatType getHatType(Object2ObjectMap<K, HatType> cache, String type, DefaultedRegistry<K> registry, K key) {
|
||||
if (cache.containsKey(key)) {
|
||||
return cache.get(key); // People often complain that villagers cause lag,
|
||||
// so let's do better than Mojang and rather NOT go
|
||||
// through all the lambda generations if we can avoid it.
|
||||
public Identifier getMergedTexture(VillagerData data) {
|
||||
VillagerType type = data.getType();
|
||||
VillagerProfession profession = data.getProfession();
|
||||
int level = MathHelper.clamp(data.getLevel(), 1, LEVEL_TO_ID.size());
|
||||
|
||||
Identifier typeId = Registry.VILLAGER_TYPE.getId(type);
|
||||
Identifier profId = Registry.VILLAGER_PROFESSION.getId(profession);
|
||||
|
||||
Identifier key = new Identifier((typeId + "/" + profId + "/" + level).replace(':', '_'));
|
||||
|
||||
if (MinecraftClient.getInstance().getTextureManager().getOrDefault(key, null) == null) {
|
||||
TextureFlattener.flatten(computeTextures(type, profession, typeId, profId, level), (Identifier)key);
|
||||
}
|
||||
return loadHatType(cache, type, registry, key);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
private <K> VillagerResourceMetadata.HatType loadHatType(Map<K, HatType> cache, String type, DefaultedRegistry<K> registry, K key) {
|
||||
return cache.computeIfAbsent(key, k -> {
|
||||
try (Resource res = resourceManager.getResource(findTexture(type, registry.getId(k)))) {
|
||||
VillagerResourceMetadata meta = res.getMetadata(VillagerResourceMetadata.READER);
|
||||
if (meta != null) {
|
||||
return meta.getHatType();
|
||||
}
|
||||
} catch (IOException e) { }
|
||||
return HatType.NONE;
|
||||
});
|
||||
}
|
||||
private List<Identifier> computeTextures(VillagerType type, VillagerProfession profession, Identifier typeId, Identifier profId, int level) {
|
||||
List<Identifier> skins = new ArrayList<>();
|
||||
|
||||
public Identifier findTexture(String category, Identifier identifier) {
|
||||
return new Identifier("minelittlepony", "textures/entity/" + entityType + "/" + category + "/" + identifier.getPath() + ".png");
|
||||
skins.add(createTexture("type", typeId));
|
||||
skins.add(createTexture("profession", Registry.VILLAGER_PROFESSION.getId(profession)));
|
||||
if (profession != VillagerProfession.NITWIT) {
|
||||
skins.add(createTexture("profession_level", LEVEL_TO_ID.get(level)));
|
||||
}
|
||||
|
||||
return skins;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package com.minelittlepony.client.util.render;
|
||||
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.texture.*;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.mojang.blaze3d.platform.TextureUtil;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class TextureFlattener {
|
||||
|
||||
public static void flatten(List<Identifier> textures, Identifier output) {
|
||||
Preconditions.checkArgument(textures.size() > 0, "Must have at least one image to flatten");
|
||||
MinecraftClient.getInstance().getTextureManager().registerTexture(output, new ResourceTexture(output) {
|
||||
@Override
|
||||
public void load(ResourceManager resManager) throws IOException {
|
||||
NativeImage image = null;
|
||||
|
||||
for (int i = 0; i < textures.size(); i++) {
|
||||
TextureData data = TextureData.load(resManager, textures.get(0));
|
||||
data.checkException();
|
||||
if (image == null) {
|
||||
image = data.getImage();
|
||||
} else {
|
||||
copyOver(data.getImage(), image);
|
||||
}
|
||||
}
|
||||
|
||||
if (!RenderSystem.isOnRenderThreadOrInit()) {
|
||||
final NativeImage i = image;
|
||||
RenderSystem.recordRenderCall(() -> upload(i));
|
||||
} else {
|
||||
upload(image);
|
||||
}
|
||||
}
|
||||
|
||||
private void upload(NativeImage image) {
|
||||
TextureUtil.prepareImage(getGlId(), 0, image.getWidth(), image.getHeight());
|
||||
image.upload(0, 0, 0, 0, 0, image.getWidth(), image.getHeight(), false, false, false, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void copyOver(NativeImage from, NativeImage to) {
|
||||
copyOver(from, to, 0, 0,
|
||||
Math.min(from.getWidth(), to.getWidth()),
|
||||
Math.min(from.getHeight(), to.getHeight())
|
||||
);
|
||||
}
|
||||
|
||||
public static void copyOver(NativeImage from, NativeImage to, int x, int y, int w, int h) {
|
||||
for (; x < w; x++) {
|
||||
for (; y < h; y++) {
|
||||
copy(from, to, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void copy(NativeImage from, NativeImage to, int x, int y) {
|
||||
int color = from.getColor(x, y);
|
||||
if (NativeImage.getAlpha(color) > 0) {
|
||||
to.setColor(x, y, color);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue