diff --git a/dependencies.gradle b/dependencies.gradle index b8e9049..6482f49 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -18,4 +18,5 @@ dependencies { compileOnly('curse.maven:automagy-222153:2285272') compileOnly('com.github.GTNewHorizons:EnderIO:2.10.25:dev') + runtimeOnly("com.github.GTNewHorizons:NotEnoughItems:2.8.118-GTNH:dev") } diff --git a/src/main/java/com/brandon3055/draconicevolution/client/render/item/RenderMobSoul.java b/src/main/java/com/brandon3055/draconicevolution/client/render/item/RenderMobSoul.java index 0ccb36b..3beb5f0 100644 --- a/src/main/java/com/brandon3055/draconicevolution/client/render/item/RenderMobSoul.java +++ b/src/main/java/com/brandon3055/draconicevolution/client/render/item/RenderMobSoul.java @@ -15,6 +15,12 @@ import com.brandon3055.draconicevolution.common.utils.LogHelper; public class RenderMobSoul implements IItemRenderer { + public static volatile boolean applyTimeRotation = true; + + public static float getRotationAngle() { + return (float) (Minecraft.getSystemTime() / -10); + } + private final Minecraft mc; private final String[] randomEntities = new String[] { "Pig", "Sheep", "Enderman", "Zombie", "Creeper", "Cow", "Chicken", "Ozelot", "Witch", "Wolf", "MushroomCow", "Squid", "EntityHorse", "Spider", "Skeleton", "Blaze", @@ -58,7 +64,7 @@ public class RenderMobSoul implements IItemRenderer { GL11.glScalef(13F, 13F, 13F); GL11.glTranslated(1.2, 2.2, 0); GL11.glRotatef(180F, 1F, 0F, 0F); - GL11.glRotatef(mc.getSystemTime() / -10, 0F, 1F, 0F); + if (applyTimeRotation) GL11.glRotatef(getRotationAngle(), 0F, 1F, 0F); GL11.glRotatef(-20F, 1F, 0F, 0F); RenderManager.instance.renderEntityWithPosYaw(mob, 0, 0, 0, 0F, 1F); GL11.glPopMatrix(); @@ -67,7 +73,7 @@ public class RenderMobSoul implements IItemRenderer { GL11.glScalef(0.8F, 0.8F, 0.8F); GL11.glTranslated(2, 0.5, 0); GL11.glRotatef(20F, 0F, 0F, 1F); - GL11.glRotatef(mc.getSystemTime() / -10, 0F, 1F, 0F); + if (applyTimeRotation) GL11.glRotatef(getRotationAngle(), 0F, 1F, 0F); GL11.glRotatef(-20F, 1F, 0F, 0F); RenderManager.instance.renderEntityWithPosYaw(mob, 0, 0, 0, 0F, 1F); GL11.glPopMatrix(); @@ -76,14 +82,14 @@ public class RenderMobSoul implements IItemRenderer { GL11.glScalef(0.8F, 0.8F, 0.8F); GL11.glTranslated(1, 0.5, 0); GL11.glRotatef(20F, 0F, 0F, 1F); - GL11.glRotatef(mc.getSystemTime() / -10, 0F, 1F, 0F); + if (applyTimeRotation) GL11.glRotatef(getRotationAngle(), 0F, 1F, 0F); GL11.glRotatef(-20F, 1F, 0F, 0F); RenderManager.instance.renderEntityWithPosYaw(mob, 0, 0, 0, 0F, 1F); GL11.glPopMatrix(); } else { GL11.glPushMatrix(); GL11.glScalef(1.5F, 1.5F, 1.5F); - GL11.glRotatef(mc.getSystemTime() / -10, 0F, 1F, 0F); + if (applyTimeRotation) GL11.glRotatef(getRotationAngle(), 0F, 1F, 0F); GL11.glRotatef(-20F, 1F, 0F, 0F); GL11.glEnable(GL11.GL_BLEND); GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/main/java/com/brandon3055/draconicevolution/client/render/tile/PlacedItemDisplayListCache.java b/src/main/java/com/brandon3055/draconicevolution/client/render/tile/PlacedItemDisplayListCache.java new file mode 100644 index 0000000..695bd14 --- /dev/null +++ b/src/main/java/com/brandon3055/draconicevolution/client/render/tile/PlacedItemDisplayListCache.java @@ -0,0 +1,194 @@ +package com.brandon3055.draconicevolution.client.render.tile; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.entity.RenderItem; +import net.minecraft.client.renderer.texture.ITickable; +import net.minecraft.item.ItemStack; +import net.minecraft.world.chunk.Chunk; +import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.client.event.TextureStitchEvent; +import net.minecraftforge.common.MinecraftForge; +import net.minecraftforge.event.world.ChunkEvent; +import net.minecraftforge.event.world.WorldEvent; + +import org.lwjgl.opengl.GL11; + +import com.brandon3055.draconicevolution.client.render.item.RenderMobSoul; +import com.brandon3055.draconicevolution.common.items.MobSoul; +import com.brandon3055.draconicevolution.common.tileentities.TilePlacedItem; +import com.brandon3055.draconicevolution.common.utils.ItemNBTHelper; + +import cpw.mods.fml.common.eventhandler.SubscribeEvent; + +public class PlacedItemDisplayListCache { + + private static final long GLINT_TTL_MS = 33L; + private static final long ANIMATED_TTL_MS = 50L; + private static final long MOBSOUL_TTL_MS = 1000L; + private static final long PRUNE_INTERVAL_MS = 1000L; + + private final Map displayListCache = new HashMap<>(); + private int atlasVersion; + private long nextPruneMs; + + public PlacedItemDisplayListCache() { + MinecraftForge.EVENT_BUS.register(this); + } + + public CacheEntry getOrCompile(TilePlacedItem tile, ItemStack stack, int meta, Runnable renderBody) { + CacheEntry entry = displayListCache.get(tile); + if (entry == null || !entry.isValid(stack, meta, tile.rotation, atlasVersion) || entry.needsRefresh()) { + dispose(tile); + entry = compile(tile, stack, meta, renderBody); + } + return entry; + } + + private CacheEntry compile(TilePlacedItem tile, ItemStack stack, int meta, Runnable renderBody) { + int listId = GL11.glGenLists(1); + boolean mobSoul = stack.getItem() instanceof MobSoul; + boolean previousRotationFlag = RenderMobSoul.applyTimeRotation; + GL11.glNewList(listId, GL11.GL_COMPILE); + try { + if (mobSoul) RenderMobSoul.applyTimeRotation = false; + renderBody.run(); + GL11.glEndList(); + } catch (RuntimeException | Error t) { + GL11.glEndList(); + GL11.glDeleteLists(listId, 1); + RenderItem.renderInFrame = false; + throw t; + } finally { + RenderMobSoul.applyTimeRotation = previousRotationFlag; + } + CacheEntry entry = new CacheEntry( + listId, + stack, + stack.stackSize, + meta, + tile.rotation, + getTTL(stack, mobSoul), + mobSoul, + atlasVersion); + displayListCache.put(tile, entry); + return entry; + } + + private long getTTL(ItemStack stack, boolean mobSoul) { + if (mobSoul) { + // "Any" soul swaps mob every second while named souls only need per-frame rotation outside the list + return isAnyMobSoul(stack) ? MOBSOUL_TTL_MS : Long.MAX_VALUE; + } + + if (stack.hasEffect()) return GLINT_TTL_MS; // Glint scroll (~30 fps) + if (stack.getItem().getIconIndex(stack) instanceof ITickable) return ANIMATED_TTL_MS; // 20 Hz sprite ticks + return Long.MAX_VALUE; // static (event-invalidated) + } + + private boolean isAnyMobSoul(ItemStack stack) { + return "Any".equals(ItemNBTHelper.getString(stack, "Name", "Pig")); + } + + public void dispose(TilePlacedItem tile) { + CacheEntry entry = displayListCache.remove(tile); + if (entry != null) GL11.glDeleteLists(entry.listId, 1); + } + + private void pruneInvalidEntries() { + if (displayListCache.isEmpty()) return; + long now = Minecraft.getSystemTime(); + if (now < nextPruneMs) return; + nextPruneMs = now + PRUNE_INTERVAL_MS; + + Iterator> it = displayListCache.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + TilePlacedItem tile = entry.getKey(); + if (tile.isInvalid() || tile.getWorldObj() == null || tile.getStack() == null) { + GL11.glDeleteLists(entry.getValue().listId, 1); + it.remove(); + } + } + } + + private void clearAll() { + for (CacheEntry entry : displayListCache.values()) { + GL11.glDeleteLists(entry.listId, 1); + } + displayListCache.clear(); + } + + @SubscribeEvent + public void onRenderWorldLast(RenderWorldLastEvent event) { + pruneInvalidEntries(); + } + + @SubscribeEvent + public void onTextureStitch(TextureStitchEvent.Post event) { + atlasVersion++; + clearAll(); + } + + @SubscribeEvent + public void onChunkUnload(ChunkEvent.Unload event) { + if (!event.world.isRemote) return; + Chunk chunk = event.getChunk(); + Iterator> it = displayListCache.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry e = it.next(); + TilePlacedItem tile = e.getKey(); + if ((tile.xCoord >> 4) == chunk.xPosition && (tile.zCoord >> 4) == chunk.zPosition) { + GL11.glDeleteLists(e.getValue().listId, 1); + it.remove(); + } + } + } + + @SubscribeEvent + public void onWorldUnload(WorldEvent.Unload event) { + if (!event.world.isRemote) return; + clearAll(); + } + + public static class CacheEntry { + + public final int listId; + public final ItemStack stack; + public final int stackSize; + public final int meta; + public final float rotation; + public final long ttlMs; + public final boolean mobSoul; + public final int atlasVersion; + private long lastCompileMs; + + private CacheEntry(int listId, ItemStack stack, int stackSize, int meta, float rotation, long ttlMs, + boolean mobSoul, int atlasVersion) { + this.listId = listId; + this.stack = stack; + this.stackSize = stackSize; + this.meta = meta; + this.rotation = rotation; + this.ttlMs = ttlMs; + this.mobSoul = mobSoul; + this.atlasVersion = atlasVersion; + this.lastCompileMs = System.currentTimeMillis(); + } + + private boolean isValid(ItemStack stack, int meta, float rotation, int atlasVersion) { + // checking the pointer of the stack is enough + return this.stack == stack && this.stackSize == stack.stackSize + && this.meta == meta + && this.rotation == rotation + && this.atlasVersion == atlasVersion; + } + + private boolean needsRefresh() { + return ttlMs != Long.MAX_VALUE && System.currentTimeMillis() - lastCompileMs >= ttlMs; + } + } +} diff --git a/src/main/java/com/brandon3055/draconicevolution/client/render/tile/RenderTilePlacedItem.java b/src/main/java/com/brandon3055/draconicevolution/client/render/tile/RenderTilePlacedItem.java index 05ffdd0..3e18fc6 100644 --- a/src/main/java/com/brandon3055/draconicevolution/client/render/tile/RenderTilePlacedItem.java +++ b/src/main/java/com/brandon3055/draconicevolution/client/render/tile/RenderTilePlacedItem.java @@ -14,6 +14,8 @@ import net.minecraft.tileentity.TileEntity; import org.lwjgl.opengl.GL11; +import com.brandon3055.draconicevolution.client.render.item.RenderMobSoul; +import com.brandon3055.draconicevolution.client.render.tile.PlacedItemDisplayListCache.CacheEntry; import com.brandon3055.draconicevolution.common.items.MobSoul; import com.brandon3055.draconicevolution.common.tileentities.TilePlacedItem; @@ -22,16 +24,33 @@ import com.brandon3055.draconicevolution.common.tileentities.TilePlacedItem; */ public class RenderTilePlacedItem extends TileEntitySpecialRenderer { + private static final float MOBSOUL_CENTER_X = 0.5F; + private static final float MOBSOUL_CENTER_Y = 0.3F; + private static final float MOBSOUL_CENTER_Z = 0.5F; + private final EntityItem cachedEntity = new EntityItem(null, 0, 0, 0, new ItemStack(Items.apple)); + private final PlacedItemDisplayListCache displayListCache = new PlacedItemDisplayListCache(); @Override public void renderTileEntityAt(TileEntity te, double x, double y, double z, float timeSinceLastTick) { if (!(te instanceof TilePlacedItem tile)) return; - if (tile.getStack() == null) return; + ItemStack stack = tile.getStack(); + if (stack == null) { + displayListCache.dispose(tile); + return; + } + if (tile.getWorldObj() == null) return; + int meta = tile.getWorldObj().getBlockMetadata(tile.xCoord, tile.yCoord, tile.zCoord); + CacheEntry entry = displayListCache.getOrCompile(tile, stack, meta, () -> renderItem(tile)); GL11.glPushMatrix(); GL11.glPushAttrib(GL11.GL_ENABLE_BIT); GL11.glTranslated(x, y, z); - renderItem(tile); + if (entry.mobSoul) { + GL11.glTranslated(MOBSOUL_CENTER_X, MOBSOUL_CENTER_Y, MOBSOUL_CENTER_Z); + GL11.glRotatef(RenderMobSoul.getRotationAngle(), 0F, 1F, 0F); + GL11.glTranslated(-MOBSOUL_CENTER_X, -MOBSOUL_CENTER_Y, -MOBSOUL_CENTER_Z); + } + GL11.glCallList(entry.listId); GL11.glPopAttrib(); GL11.glPopMatrix(); } @@ -39,7 +58,6 @@ public class RenderTilePlacedItem extends TileEntitySpecialRenderer { public void renderItem(TilePlacedItem tile) { ItemStack stack = tile.getStack(); int meta = tile.getWorldObj().getBlockMetadata(tile.xCoord, tile.yCoord, tile.zCoord); - // itemEntity.getEntityItem().stackSize = 1; final Item item = stack.getItem(); boolean is3D = item.isFull3D(); boolean isBlock = item instanceof ItemBlock; @@ -58,7 +76,7 @@ public class RenderTilePlacedItem extends TileEntitySpecialRenderer { GL11.glRotatef(tile.rotation, 0F, 0F, 1F); GL11.glTranslatef(0.0F, -0.21F, 0.0F); } else if (item instanceof MobSoul) { - GL11.glTranslatef(0.5F, 0.3F, 0.5F); + GL11.glTranslatef(MOBSOUL_CENTER_X, MOBSOUL_CENTER_Y, MOBSOUL_CENTER_Z); } else { GL11.glRotatef(90F, -1F, 0F, 0F); GL11.glTranslatef(0.5F, -0.65F, 0.02F); diff --git a/src/main/java/com/brandon3055/draconicevolution/common/tileentities/TilePlacedItem.java b/src/main/java/com/brandon3055/draconicevolution/common/tileentities/TilePlacedItem.java index c6e9d6d..f24e128 100644 --- a/src/main/java/com/brandon3055/draconicevolution/common/tileentities/TilePlacedItem.java +++ b/src/main/java/com/brandon3055/draconicevolution/common/tileentities/TilePlacedItem.java @@ -7,6 +7,7 @@ import net.minecraft.network.NetworkManager; import net.minecraft.network.Packet; import net.minecraft.network.play.server.S35PacketUpdateTileEntity; import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.Vec3; import com.brandon3055.draconicevolution.common.ModBlocks; @@ -20,6 +21,16 @@ public class TilePlacedItem extends TileEntity { public ItemStack stack; public float rotation = 0F; private boolean hasUpdated = false; + private AxisAlignedBB renderBoundingBox; + + @Override + public AxisAlignedBB getRenderBoundingBox() { + if (renderBoundingBox == null) { + renderBoundingBox = AxisAlignedBB + .getBoundingBox(xCoord - 2, yCoord - 2, zCoord - 2, xCoord + 2, yCoord + 2, zCoord + 2); + } + return renderBoundingBox; + } @Override public void updateEntity() {