package net.coderbot.iris.block_rendering; import com.gtnewhorizons.angelica.rendering.celeritas.BlockRenderLayer; import it.unimi.dsi.fastutil.ints.Int2IntMap; import it.unimi.dsi.fastutil.longs.Long2LongLinkedOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntFunction; import it.unimi.dsi.fastutil.objects.Reference2ObjectMap; import it.unimi.dsi.fastutil.objects.ReferenceSet; import it.unimi.dsi.fastutil.objects.ReferenceSets; import lombok.Getter; import lombok.Setter; import net.coderbot.iris.shaderpack.materialmap.NamespacedId; import net.minecraft.block.Block; import net.minecraft.client.Minecraft; import org.jetbrains.annotations.Nullable; import java.util.Map; public class BlockRenderingSettings { public static final BlockRenderingSettings INSTANCE = new BlockRenderingSettings(); public static final int CACHE_MISS = Integer.MIN_VALUE; private static final int NBT_CACHE_INTERVAL_TICKS = 20; private static final int TE_NBT_CACHE_MAX = 8192; private static final Long2LongLinkedOpenHashMap teNbtIdCache = new Long2LongLinkedOpenHashMap(256); private static final Object teNbtIdCacheLock = new Object(); static { teNbtIdCache.defaultReturnValue(-1L); } public static long packBlockPos(int x, int y, int z) { return ((long) x & 0x3FFFFFFL) << 38 | ((long) y & 0xFFFL) << 26 | ((long) z & 0x3FFFFFFL); } public static void invalidateTeNbtCache(int x, int y, int z) { final long key = packBlockPos(x, y, z); synchronized (teNbtIdCacheLock) { teNbtIdCache.remove(key); } } public static int getCachedTeNbtId(long packedPos, long currentTick) { final long entry; synchronized (teNbtIdCacheLock) { entry = teNbtIdCache.get(packedPos); } if (entry == -1L) { return CACHE_MISS; } final long entryTick = entry >>> 32; if (currentTick - entryTick >= NBT_CACHE_INTERVAL_TICKS) { return CACHE_MISS; } return (int) entry; } public static void cacheTeNbtId(long packedPos, int shaderId, long currentTick) { // Mask tick into low 31 bits so packed value never collides with the -1L sentinel. final long packed = ((currentTick & 0x7FFFFFFFL) << 32) | (shaderId & 0xFFFFFFFFL); synchronized (teNbtIdCacheLock) { teNbtIdCache.put(packedPos, packed); while (teNbtIdCache.size() > TE_NBT_CACHE_MAX) { teNbtIdCache.removeFirstLong(); } } } public static void clearTeNbtCache() { synchronized (teNbtIdCacheLock) { teNbtIdCache.clear(); } } @Getter private boolean reloadRequired; private Reference2ObjectMap blockMetaMatches; private NbtConditionalIdMap blockNbtMap; private Map blockTypeIds; // note: no reload needed, entities are rebuilt every frame. @Setter private Object2IntFunction entityIds; @Setter private NbtConditionalIdMap entityNbtMap; // note: no reload needed, items are rendered every frame. @Setter private Object2IntFunction itemIds; @Setter private NbtConditionalIdMap itemNbtMap; @Getter private float ambientOcclusionLevel; private boolean disableDirectionalShading; private boolean useSeparateAo; private boolean useExtendedVertexFormat; @Setter private boolean hasSnowyEntries; @Getter private ReferenceSet snowyBlocks = ReferenceSets.emptySet(); public BlockRenderingSettings() { reloadRequired = false; blockMetaMatches = null; blockNbtMap = null; blockTypeIds = null; ambientOcclusionLevel = 1.0F; disableDirectionalShading = false; useSeparateAo = false; useExtendedVertexFormat = false; hasSnowyEntries = false; } public void clearReloadRequired() { reloadRequired = false; } public void reloadRendererIfRequired() { if (isReloadRequired()) { if (Minecraft.getMinecraft().renderGlobal != null) { Minecraft.getMinecraft().renderGlobal.loadRenderers(); } clearReloadRequired(); } } @Nullable public Reference2ObjectMap getBlockMetaMatches() { return blockMetaMatches; } @Nullable public Map getBlockTypeIds() { return blockTypeIds; } @Nullable public Object2IntFunction getEntityIds() { return entityIds; } @Nullable public Object2IntFunction getItemIds() { return itemIds; } @Nullable public NbtConditionalIdMap getBlockNbtMap() { return blockNbtMap; } @Nullable public NbtConditionalIdMap getItemNbtMap() { return itemNbtMap; } @Nullable public NbtConditionalIdMap getEntityNbtMap() { return entityNbtMap; } public void setBlockMetaMatches(Reference2ObjectMap blockMetaIds) { this.reloadRequired = true; this.blockMetaMatches = blockMetaIds; } public void setBlockNbtMap(NbtConditionalIdMap blockNbtMap) { this.reloadRequired = true; this.blockNbtMap = blockNbtMap; clearTeNbtCache(); } public boolean hasSnowyEntries() { return hasSnowyEntries; } public void setSnowyBlocks(ReferenceSet snowyBlocks) { this.snowyBlocks = snowyBlocks != null ? snowyBlocks : ReferenceSets.emptySet(); } public void setBlockTypeIds(Map blockTypeIds) { if (this.blockTypeIds != null && this.blockTypeIds.equals(blockTypeIds)) { return; } this.reloadRequired = true; this.blockTypeIds = blockTypeIds; } public void setAmbientOcclusionLevel(float ambientOcclusionLevel) { if (ambientOcclusionLevel == this.ambientOcclusionLevel) { return; } this.reloadRequired = true; this.ambientOcclusionLevel = ambientOcclusionLevel; } public boolean shouldDisableDirectionalShading() { return disableDirectionalShading; } public void setDisableDirectionalShading(boolean disableDirectionalShading) { if (disableDirectionalShading == this.disableDirectionalShading) { return; } this.reloadRequired = true; this.disableDirectionalShading = disableDirectionalShading; } public boolean shouldUseSeparateAo() { return useSeparateAo; } public void setUseSeparateAo(boolean useSeparateAo) { if (useSeparateAo == this.useSeparateAo) { return; } this.reloadRequired = true; this.useSeparateAo = useSeparateAo; } public boolean shouldUseExtendedVertexFormat() { return useExtendedVertexFormat; } public void setUseExtendedVertexFormat(boolean useExtendedVertexFormat) { if (useExtendedVertexFormat == this.useExtendedVertexFormat) { return; } this.reloadRequired = true; this.useExtendedVertexFormat = useExtendedVertexFormat; } }