diff --git a/src/api/java/net/optifine/shaders/ShadersRender.java b/src/api/java/net/optifine/shaders/ShadersRender.java new file mode 100644 index 00000000000..ceedfee366b --- /dev/null +++ b/src/api/java/net/optifine/shaders/ShadersRender.java @@ -0,0 +1,14 @@ +package net.optifine.shaders; + +import net.minecraft.util.BlockRenderLayer; + +/// Adapted and minimized from OptiFine +public class ShadersRender { + + public static void preRenderChunkLayer(BlockRenderLayer blockLayerIn) {} + + public static void postRenderChunkLayer(BlockRenderLayer blockLayerIn) {} + + public static void setupArrayPointersVbo() {} + +} diff --git a/src/main/java/gregtech/client/renderer/scene/VBOWorldSceneRenderer.java b/src/main/java/gregtech/client/renderer/scene/VBOWorldSceneRenderer.java new file mode 100644 index 00000000000..b4524ab576e --- /dev/null +++ b/src/main/java/gregtech/client/renderer/scene/VBOWorldSceneRenderer.java @@ -0,0 +1,166 @@ +package gregtech.client.renderer.scene; + +import gregtech.api.util.Mods; +import gregtech.client.utils.OptiFineHelper; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.*; +import net.minecraft.client.renderer.texture.TextureMap; +import net.minecraft.client.renderer.vertex.DefaultVertexFormats; +import net.minecraft.client.renderer.vertex.VertexBuffer; +import net.minecraft.client.renderer.vertex.VertexFormatElement; +import net.minecraft.util.BlockRenderLayer; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraftforge.client.ForgeHooksClient; +import net.minecraftforge.client.MinecraftForgeClient; +import net.minecraftforge.fml.relauncher.Side; +import net.minecraftforge.fml.relauncher.SideOnly; +import net.optifine.shaders.ShadersRender; + +import org.lwjgl.opengl.GL11; + +import java.util.Collection; + +@SideOnly(Side.CLIENT) +public class VBOWorldSceneRenderer extends ImmediateWorldSceneRenderer { + + protected static final VertexBuffer[] VBOS = new VertexBuffer[BlockRenderLayer.values().length]; + protected boolean isDirty = true; + + public VBOWorldSceneRenderer(World world) { + super(world); + } + + private void uploadVBO() { + BlockRenderLayer oldRenderLayer = MinecraftForgeClient.getRenderLayer(); + + try { // render block in each layer + for (BlockRenderLayer layer : BlockRenderLayer.values()) { + + OptiFineHelper.preRenderChunkLayer(layer); + + renderBlockLayer(layer); + + // Get the buffer again + BufferBuilder buffer = Tessellator.getInstance().getBuffer(); + buffer.finishDrawing(); + buffer.reset(); + + int i = layer.ordinal(); + var vbo = VBOS[i]; + if (vbo == null) vbo = VBOS[i] = new VertexBuffer(DefaultVertexFormats.BLOCK); + vbo.bufferData(buffer.getByteBuffer()); + + OptiFineHelper.postRenderChunkLayer(layer); + } + } finally { + ForgeHooksClient.setRenderLayer(oldRenderLayer); + } + this.isDirty = false; + } + + @Override + protected void drawWorld() { + if (this.isDirty) { + uploadVBO(); + } + if (beforeRender != null) { + beforeRender.accept(this); + } + + Minecraft mc = Minecraft.getMinecraft(); + GlStateManager.enableCull(); + GlStateManager.enableRescaleNormal(); + RenderHelper.disableStandardItemLighting(); + mc.entityRenderer.disableLightmap(); + mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE); + GlStateManager.disableLighting(); + GlStateManager.enableTexture2D(); + GlStateManager.enableAlpha(); + + var oldRenderLayer = MinecraftForgeClient.getRenderLayer(); + for (var layer : BlockRenderLayer.values()) { + + ForgeHooksClient.setRenderLayer(layer); + + int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; + setDefaultPassRenderState(pass); + + OptiFineHelper.preRenderChunkLayer(layer); + + GlStateManager.pushMatrix(); + { + int i = layer.ordinal(); + var vbo = VBOS[i]; + vbo.bindBuffer(); + enableClientStates(); + setupArrayPointers(); + vbo.drawArrays(GL11.GL_QUADS); + disableClientStates(); + vbo.unbindBuffer(); + } + GlStateManager.popMatrix(); + + OptiFineHelper.postRenderChunkLayer(layer); + } + ForgeHooksClient.setRenderLayer(oldRenderLayer); + + renderTileEntities(); // Handle TileEntities + + GlStateManager.shadeModel(GL11.GL_SMOOTH); + RenderHelper.enableStandardItemLighting(); + GlStateManager.enableDepth(); + GlStateManager.disableBlend(); + GlStateManager.depthMask(true); + + if (afterRender != null) { + afterRender.accept(this); + } + } + + @Override + public WorldSceneRenderer addRenderedBlocks(Collection blocks) { + this.isDirty = true; + return super.addRenderedBlocks(blocks); + } + + protected void enableClientStates() { + GlStateManager.glEnableClientState(GL11.GL_VERTEX_ARRAY); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit); + GlStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.lightmapTexUnit); + GlStateManager.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit); + GlStateManager.glEnableClientState(GL11.GL_COLOR_ARRAY); + } + + protected void disableClientStates() { + for (VertexFormatElement element : DefaultVertexFormats.BLOCK.getElements()) { + switch (element.getUsage()) { + case POSITION -> GlStateManager.glDisableClientState(GL11.GL_VERTEX_ARRAY); + case COLOR -> GlStateManager.glDisableClientState(GL11.GL_COLOR_ARRAY); + case UV -> { + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit + element.getIndex()); + GlStateManager.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit); + } + default -> {} + } + } + } + + protected void setupArrayPointers() { + if (Mods.ShadersMod.isModLoaded()) { + ShadersRender.setupArrayPointersVbo(); + } else { + // 28 == DefaultVertexFormats.BLOCK.getSize(); + GlStateManager.glVertexPointer(3, GL11.GL_FLOAT, 28, 0); + GlStateManager.glColorPointer(4, GL11.GL_UNSIGNED_BYTE, 28, 12); + GlStateManager.glTexCoordPointer(2, GL11.GL_FLOAT, 28, 16); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.lightmapTexUnit); + GlStateManager.glTexCoordPointer(2, GL11.GL_SHORT, 28, 24); + OpenGlHelper.setClientActiveTexture(OpenGlHelper.defaultTexUnit); + } + } +} diff --git a/src/main/java/gregtech/client/renderer/scene/WorldSceneRenderer.java b/src/main/java/gregtech/client/renderer/scene/WorldSceneRenderer.java index cb0dacd1c63..b3716c07929 100644 --- a/src/main/java/gregtech/client/renderer/scene/WorldSceneRenderer.java +++ b/src/main/java/gregtech/client/renderer/scene/WorldSceneRenderer.java @@ -1,5 +1,7 @@ package gregtech.client.renderer.scene; +import gregtech.api.metatileentity.IFastRenderMetaTileEntity; +import gregtech.api.metatileentity.interfaces.IGregTechTileEntity; import gregtech.api.util.Position; import gregtech.api.util.PositionedRect; import gregtech.api.util.Size; @@ -25,6 +27,7 @@ import net.minecraftforge.fml.relauncher.SideOnly; import codechicken.lib.vec.Vector3; +import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import org.jetbrains.annotations.Nullable; import org.lwjgl.opengl.GL11; @@ -35,6 +38,7 @@ import java.nio.FloatBuffer; import java.nio.IntBuffer; import java.util.Collection; +import java.util.Map; import java.util.function.Consumer; import javax.vecmath.Vector3f; @@ -60,10 +64,12 @@ public abstract class WorldSceneRenderer { protected static final FloatBuffer OBJECT_POS_BUFFER = ByteBuffer.allocateDirect(3 * 4) .order(ByteOrder.nativeOrder()).asFloatBuffer(); + // In most cases this would be empty + protected static final Map TILE_ENTITIES = new Object2ObjectArrayMap<>(); public final World world; public final Collection renderedBlocks = new ObjectOpenHashSet<>(); - private Consumer beforeRender; - private Consumer afterRender; + protected Consumer beforeRender; + protected Consumer afterRender; private Consumer onLookingAt; private int clearColor; private RayTraceResult lastTraceResult; @@ -88,6 +94,15 @@ public WorldSceneRenderer setAfterWorldRender(Consumer callb public WorldSceneRenderer addRenderedBlocks(@Nullable Collection blocks) { if (blocks != null) { this.renderedBlocks.addAll(blocks); + TILE_ENTITIES.clear(); + blocks.forEach(pos -> { + TileEntity tile = world.getTileEntity(pos); + if (tile != null && (!(tile instanceof IGregTechTileEntity gtte) || + // Put MTEs only when it has FastRenderer + gtte.getMetaTileEntity() instanceof IFastRenderMetaTileEntity)) { + TILE_ENTITIES.put(pos, tile); + } + }); } return this; } @@ -237,23 +252,8 @@ protected void drawWorld() { try { // render block in each layer for (BlockRenderLayer layer : BlockRenderLayer.values()) { - ForgeHooksClient.setRenderLayer(layer); - int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; - setDefaultPassRenderState(pass); - - BufferBuilder buffer = Tessellator.getInstance().getBuffer(); - buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); - BlockRendererDispatcher blockrendererdispatcher = mc.getBlockRendererDispatcher(); - - for (BlockPos pos : renderedBlocks) { - IBlockState state = world.getBlockState(pos); - Block block = state.getBlock(); - if (block == Blocks.AIR) continue; - state = state.getActualState(world, pos); - if (block.canRenderInLayer(state, layer)) { - blockrendererdispatcher.renderBlock(state, pos, world, buffer); - } - } + + renderBlockLayer(layer); Tessellator.getInstance().draw(); Tessellator.getInstance().getBuffer().setTranslation(0, 0, 0); @@ -262,23 +262,8 @@ protected void drawWorld() { ForgeHooksClient.setRenderLayer(oldRenderLayer); } - RenderHelper.enableStandardItemLighting(); - GlStateManager.enableLighting(); + renderTileEntities(); // Handle TileEntities - // render TESR - for (int pass = 0; pass < 2; pass++) { - ForgeHooksClient.setRenderPass(pass); - setDefaultPassRenderState(pass); - for (BlockPos pos : renderedBlocks) { - TileEntity tile = world.getTileEntity(pos); - if (tile != null) { - if (tile.shouldRenderInPass(pass)) { - TileEntityRendererDispatcher.instance.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); - } - } - } - } - ForgeHooksClient.setRenderPass(-1); GlStateManager.enableDepth(); GlStateManager.disableBlend(); GlStateManager.depthMask(true); @@ -288,6 +273,44 @@ protected void drawWorld() { } } + protected void renderBlockLayer(BlockRenderLayer layer) { + ForgeHooksClient.setRenderLayer(layer); + int pass = layer == BlockRenderLayer.TRANSLUCENT ? 1 : 0; + setDefaultPassRenderState(pass); + + BufferBuilder buffer = Tessellator.getInstance().getBuffer(); + buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); + BlockRendererDispatcher blockrendererdispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher(); + + for (BlockPos pos : renderedBlocks) { + IBlockState state = world.getBlockState(pos); + Block block = state.getBlock(); + if (block == Blocks.AIR) continue; + state = state.getActualState(world, pos); + if (block.canRenderInLayer(state, layer)) { + blockrendererdispatcher.renderBlock(state, pos, world, buffer); + } + } + } + + protected void renderTileEntities() { + RenderHelper.enableStandardItemLighting(); + var dispatcher = TileEntityRendererDispatcher.instance; + for (int pass = 0; pass < 2; pass++) { + ForgeHooksClient.setRenderPass(pass); + setDefaultPassRenderState(pass); + + int finalPass = pass; + TILE_ENTITIES.forEach((pos, tile) -> { + if (tile.shouldRenderInPass(finalPass)) { + dispatcher.render(tile, pos.getX(), pos.getY(), pos.getZ(), 0); + } + }); + } + ForgeHooksClient.setRenderPass(-1); + RenderHelper.disableStandardItemLighting(); + } + public static void setDefaultPassRenderState(int pass) { GlStateManager.color(1, 1, 1, 1); if (pass == 0) { // SOLID diff --git a/src/main/java/gregtech/client/utils/OptiFineHelper.java b/src/main/java/gregtech/client/utils/OptiFineHelper.java new file mode 100644 index 00000000000..f09b0f304d2 --- /dev/null +++ b/src/main/java/gregtech/client/utils/OptiFineHelper.java @@ -0,0 +1,26 @@ +package gregtech.client.utils; + +import gregtech.api.util.Mods; + +import net.minecraft.util.BlockRenderLayer; +import net.optifine.shaders.ShadersRender; + +public class OptiFineHelper { + + public static BlockRenderLayer getOFSafeLayer(BlockRenderLayer layer) { + if (!Mods.ShadersMod.isModLoaded()) return layer; + return layer == BloomEffectUtil.getBloomLayer() ? BloomEffectUtil.getEffectiveBloomLayer() : layer; + } + + public static void preRenderChunkLayer(BlockRenderLayer layer) { + if (Mods.ShadersMod.isModLoaded()) { + ShadersRender.preRenderChunkLayer(getOFSafeLayer(layer)); + } + } + + public static void postRenderChunkLayer(BlockRenderLayer layer) { + if (Mods.ShadersMod.isModLoaded()) { + ShadersRender.postRenderChunkLayer(getOFSafeLayer(layer)); + } + } +} diff --git a/src/main/java/gregtech/integration/jei/multiblock/MultiblockInfoRecipeWrapper.java b/src/main/java/gregtech/integration/jei/multiblock/MultiblockInfoRecipeWrapper.java index 1e5dd465b99..2bca0ef6bf1 100644 --- a/src/main/java/gregtech/integration/jei/multiblock/MultiblockInfoRecipeWrapper.java +++ b/src/main/java/gregtech/integration/jei/multiblock/MultiblockInfoRecipeWrapper.java @@ -13,6 +13,7 @@ import gregtech.api.util.GregFakePlayer; import gregtech.api.util.ItemStackHashStrategy; import gregtech.client.renderer.scene.ImmediateWorldSceneRenderer; +import gregtech.client.renderer.scene.VBOWorldSceneRenderer; import gregtech.client.renderer.scene.WorldSceneRenderer; import gregtech.client.utils.RenderUtil; import gregtech.client.utils.TrackedDummyWorld; @@ -585,7 +586,7 @@ private MBPattern initializePattern(@NotNull MultiblockShapeInfo shapeInfo, @Not } TrackedDummyWorld world = new TrackedDummyWorld(); - ImmediateWorldSceneRenderer worldSceneRenderer = new ImmediateWorldSceneRenderer(world); + ImmediateWorldSceneRenderer worldSceneRenderer = new VBOWorldSceneRenderer(world); worldSceneRenderer.setClearColor(ConfigHolder.client.multiblockPreviewColor); world.addBlocks(blockMap);