Last active
May 20, 2019 02:28
-
-
Save Cadiboo/753607e41ca4e2ca9e0ce3b928bab5ef to your computer and use it in GitHub Desktop.
Cache Rendering Blocks in RenderWorldLastEvent
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.cadiboo.testing.client; | |
import net.minecraft.block.state.IBlockState; | |
import net.minecraft.client.Minecraft; | |
import net.minecraft.client.renderer.BlockRendererDispatcher; | |
import net.minecraft.client.renderer.BufferBuilder; | |
import net.minecraft.client.renderer.GlStateManager; | |
import net.minecraft.client.renderer.RegionRenderCacheBuilder; | |
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; | |
import net.minecraft.client.renderer.vertex.VertexFormat; | |
import net.minecraft.client.renderer.vertex.VertexFormatElement; | |
import net.minecraft.entity.Entity; | |
import net.minecraft.util.BlockRenderLayer; | |
import net.minecraft.util.math.BlockPos; | |
import net.minecraft.world.IBlockAccess; | |
import net.minecraftforge.client.ForgeHooksClient; | |
import net.minecraftforge.client.event.RenderWorldLastEvent; | |
import net.minecraftforge.fml.common.Mod; | |
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; | |
import net.minecraftforge.fml.common.gameevent.TickEvent; | |
import org.lwjgl.opengl.GL11; | |
import java.nio.ByteBuffer; | |
import java.util.Arrays; | |
import java.util.List; | |
import static io.github.cadiboo.nocubes.util.ModReference.MOD_ID; | |
import static net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent; | |
import static net.minecraftforge.fml.relauncher.Side.CLIENT; | |
/** | |
* Subscribe to events that should be handled on the PHYSICAL CLIENT in this class | |
* | |
* @author Cadiboo | |
*/ | |
@Mod.EventBusSubscriber(modid = MOD_ID, value = CLIENT) | |
public final class ClientEventSubscriber { | |
// These buffers are large enough for an entire chunk, consider using smaller buffers | |
private static final RegionRenderCacheBuilder bufferCache = new RegionRenderCacheBuilder(); | |
private static final boolean[] usedBlockRenderLayers = new boolean[BlockRenderLayer.values().length]; | |
private static final boolean[] startedBufferBuilders = new boolean[BlockRenderLayer.values().length]; | |
@SubscribeEvent | |
public static void onClientTickEvent(final ClientTickEvent event) { | |
if (event.phase != TickEvent.Phase.END) { | |
return; | |
} | |
final Minecraft minecraft = Minecraft.getMinecraft(); | |
if (minecraft.world == null) { | |
return; | |
} | |
// only update once every 5 seconds, to show the caching | |
if (minecraft.world.getTotalWorldTime() % 100 != 0) { | |
return; | |
} | |
if (minecraft.player == null) { | |
return; | |
} | |
// Reset values | |
Arrays.fill(usedBlockRenderLayers, false); | |
Arrays.fill(startedBufferBuilders, false); | |
final IBlockAccess blockAccess = minecraft.world; | |
final BlockRendererDispatcher blockRendererDispatcher = minecraft.getBlockRendererDispatcher(); | |
final BlockPos startPos = minecraft.player.getPosition().add(-15, -15, -15); | |
final BlockPos endPos = minecraft.player.getPosition().add(15, -1, 15); | |
for (BlockPos pos : BlockPos.getAllInBoxMutable(startPos, endPos)) { | |
final IBlockState state = blockAccess.getBlockState(pos); | |
for (BlockRenderLayer blockRenderLayer : BlockRenderLayer.values()) { | |
if (!state.getBlock().canRenderInLayer(state, blockRenderLayer)) { | |
continue; | |
} | |
ForgeHooksClient.setRenderLayer(blockRenderLayer); | |
final int blockRenderLayerId = blockRenderLayer.ordinal(); | |
final BufferBuilder bufferBuilder = bufferCache.getWorldRendererByLayerId(blockRenderLayerId); | |
if (!startedBufferBuilders[blockRenderLayerId]) { | |
startedBufferBuilders[blockRenderLayerId] = true; | |
// Copied from RenderChunk | |
{ | |
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); | |
} | |
} | |
// OptiFine Shaders compatibility | |
// if (Config.isShaders()) SVertexBuilder.pushEntity(state, pos, blockAccess, bufferBuilder); | |
usedBlockRenderLayers[blockRenderLayerId] |= blockRendererDispatcher.renderBlock(state, pos, blockAccess, bufferBuilder); | |
// if (Config.isShaders()) SVertexBuilder.popEntity(bufferBuilder); | |
} | |
ForgeHooksClient.setRenderLayer(null); | |
} | |
// finishDrawing | |
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) { | |
if (!startedBufferBuilders[blockRenderLayerId]) { | |
continue; | |
} | |
bufferCache.getWorldRendererByLayerId(blockRenderLayerId).finishDrawing(); | |
} | |
} | |
@SubscribeEvent | |
public static void onRenderWorldLastEvent(final RenderWorldLastEvent event) { | |
// Code to draw the buffer | |
{ | |
final Entity entity = Minecraft.getMinecraft().getRenderViewEntity(); | |
if (entity == null) { | |
return; | |
} | |
final float partialTicks = event.getPartialTicks(); | |
// Copied from EntityRenderer. This code can be found by looking at usages of Entity.prevPosX. | |
// It also appears in many other places throughout Minecraft's rendering | |
double renderPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double) partialTicks; | |
double renderPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * (double) partialTicks; | |
double renderPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * (double) partialTicks; | |
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) { | |
if (!usedBlockRenderLayers[blockRenderLayerId]) { | |
continue; | |
} | |
final BufferBuilder bufferBuilder = bufferCache.getWorldRendererByLayerId(blockRenderLayerId); | |
GlStateManager.pushMatrix(); | |
//Render everything 1 block higher as an example | |
GlStateManager.translate(-renderPosX, -renderPosY + 1, -renderPosZ); | |
drawBuffer(bufferBuilder); | |
GlStateManager.popMatrix(); | |
} | |
} | |
} | |
// Coppied from the Tesselator's vboUploader - Draw everything but don't reset the buffer | |
private static void drawBuffer(final BufferBuilder bufferBuilder) { | |
if (bufferBuilder.getVertexCount() > 0) { | |
VertexFormat vertexformat = bufferBuilder.getVertexFormat(); | |
int i = vertexformat.getSize(); | |
ByteBuffer bytebuffer = bufferBuilder.getByteBuffer(); | |
List<VertexFormatElement> list = vertexformat.getElements(); | |
for (int j = 0; j < list.size(); ++j) { | |
VertexFormatElement vertexformatelement = list.get(j); | |
VertexFormatElement.EnumUsage vertexformatelement$enumusage = vertexformatelement.getUsage(); | |
int k = vertexformatelement.getType().getGlConstant(); | |
int l = vertexformatelement.getIndex(); | |
bytebuffer.position(vertexformat.getOffset(j)); | |
// moved to VertexFormatElement.preDraw | |
vertexformatelement.getUsage().preDraw(vertexformat, j, i, bytebuffer); | |
} | |
GlStateManager.glDrawArrays(bufferBuilder.getDrawMode(), 0, bufferBuilder.getVertexCount()); | |
int i1 = 0; | |
for (int j1 = list.size(); i1 < j1; ++i1) { | |
VertexFormatElement vertexformatelement1 = list.get(i1); | |
VertexFormatElement.EnumUsage vertexformatelement$enumusage1 = vertexformatelement1.getUsage(); | |
int k1 = vertexformatelement1.getIndex(); | |
// moved to VertexFormatElement.postDraw | |
vertexformatelement1.getUsage().postDraw(vertexformat, i1, i, bytebuffer); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.cadiboo.testing.client; | |
import net.minecraft.block.state.IBlockState; | |
import net.minecraft.client.Minecraft; | |
import net.minecraft.client.entity.EntityPlayerSP; | |
import net.minecraft.client.multiplayer.WorldClient; | |
import net.minecraft.client.renderer.BlockRendererDispatcher; | |
import net.minecraft.client.renderer.BufferBuilder; | |
import net.minecraft.client.renderer.GlStateManager; | |
import net.minecraft.client.renderer.RegionRenderCacheBuilder; | |
import net.minecraft.client.renderer.vertex.DefaultVertexFormats; | |
import net.minecraft.client.renderer.vertex.VertexFormat; | |
import net.minecraft.client.renderer.vertex.VertexFormatElement; | |
import net.minecraft.entity.Entity; | |
import net.minecraft.util.BlockRenderLayer; | |
import net.minecraft.util.math.BlockPos; | |
import net.minecraftforge.client.ForgeHooksClient; | |
import net.minecraftforge.client.event.RenderWorldLastEvent; | |
import net.minecraftforge.eventbus.api.SubscribeEvent; | |
import net.minecraftforge.fml.common.Mod; | |
import net.minecraftforge.fml.common.gameevent.TickEvent; | |
import org.lwjgl.opengl.GL11; | |
import java.nio.ByteBuffer; | |
import java.util.Arrays; | |
import java.util.List; | |
import java.util.Random; | |
import static io.github.cadiboo.nocubes.NoCubes.MOD_ID; | |
import static net.minecraftforge.api.distmarker.Dist.CLIENT; | |
import static net.minecraftforge.fml.common.gameevent.TickEvent.ClientTickEvent; | |
/** | |
* Subscribe to events that should be handled on the PHYSICAL CLIENT in this class | |
* | |
* @author Cadiboo | |
*/ | |
@Mod.EventBusSubscriber(modid = MOD_ID, value = CLIENT) | |
public final class ClientEventSubscriber { | |
// These buffers are large enough for an entire chunk, consider using smaller buffers | |
private static final RegionRenderCacheBuilder bufferCache = new RegionRenderCacheBuilder(); | |
private static final boolean[] usedBlockRenderLayers = new boolean[BlockRenderLayer.values().length]; | |
@SubscribeEvent | |
public static void onClientTickEvent(final ClientTickEvent event) { | |
if (event.phase != TickEvent.Phase.END) { | |
return; | |
} | |
final Minecraft minecraft = Minecraft.getInstance(); | |
final WorldClient world = minecraft.world; | |
if (world == null) { | |
return; | |
} | |
// only update once every 5 seconds, to show the caching | |
if (world.getGameTime() % 100 != 0) { | |
return; | |
} | |
final EntityPlayerSP player = minecraft.player; | |
if (player == null) { | |
return; | |
} | |
// Reset values | |
Arrays.fill(usedBlockRenderLayers, false); | |
final boolean[] startedBufferBuilders = new boolean[BlockRenderLayer.values().length]; | |
final BlockRendererDispatcher blockRendererDispatcher = minecraft.getBlockRendererDispatcher(); | |
final BlockPos startPos = player.getPosition().add(-15, -15, -15); | |
final BlockPos endPos = player.getPosition().add(15, -1, 15); | |
final Random random = new Random(); | |
for (BlockPos pos : BlockPos.getAllInBoxMutable(startPos, endPos)) { | |
final IBlockState state = world.getBlockState(pos); | |
for (BlockRenderLayer blockRenderLayer : BlockRenderLayer.values()) { | |
if (!state.getBlock().canRenderInLayer(state, blockRenderLayer)) { | |
continue; | |
} | |
ForgeHooksClient.setRenderLayer(blockRenderLayer); | |
final int blockRenderLayerId = blockRenderLayer.ordinal(); | |
final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId); | |
if (!startedBufferBuilders[blockRenderLayerId]) { | |
startedBufferBuilders[blockRenderLayerId] = true; | |
// Copied from RenderChunk | |
{ | |
bufferBuilder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); | |
} | |
} | |
// OptiFine Shaders compatibility | |
// if (Config.isShaders()) SVertexBuilder.pushEntity(state, pos, reader, bufferBuilder); | |
usedBlockRenderLayers[blockRenderLayerId] |= blockRendererDispatcher.renderBlock(state, pos, world, bufferBuilder, random); | |
// if (Config.isShaders()) SVertexBuilder.popEntity(bufferBuilder); | |
} | |
ForgeHooksClient.setRenderLayer(null); | |
} | |
// finishDrawing | |
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) { | |
if (!startedBufferBuilders[blockRenderLayerId]) { | |
continue; | |
} | |
bufferCache.getBuilder(blockRenderLayerId).finishDrawing(); | |
} | |
} | |
@SubscribeEvent | |
public static void onRenderWorldLastEvent(final RenderWorldLastEvent event) { | |
// Code to draw the buffer | |
{ | |
final Entity entity = Minecraft.getInstance().getRenderViewEntity(); | |
if (entity == null) { | |
return; | |
} | |
final float partialTicks = event.getPartialTicks(); | |
// Copied from EntityRenderer. This code can be found by looking at usages of Entity.prevPosX. | |
// It also appears in many other places throughout Minecraft's rendering | |
double renderPosX = entity.lastTickPosX + (entity.posX - entity.lastTickPosX) * (double) partialTicks; | |
double renderPosY = entity.lastTickPosY + (entity.posY - entity.lastTickPosY) * (double) partialTicks; | |
double renderPosZ = entity.lastTickPosZ + (entity.posZ - entity.lastTickPosZ) * (double) partialTicks; | |
for (int blockRenderLayerId = 0; blockRenderLayerId < usedBlockRenderLayers.length; blockRenderLayerId++) { | |
if (!usedBlockRenderLayers[blockRenderLayerId]) { | |
continue; | |
} | |
final BufferBuilder bufferBuilder = bufferCache.getBuilder(blockRenderLayerId); | |
GlStateManager.pushMatrix(); | |
// Render everything 1 block higher as an example | |
// You can add blockpos coords to -renderPos to render at a specific block | |
GlStateManager.translated(-renderPosX, -renderPosY + 1, -renderPosZ); | |
drawBuffer(bufferBuilder); | |
GlStateManager.popMatrix(); | |
} | |
} | |
} | |
// Coppied from the Tesselator's vboUploader - Draw everything but don't reset the buffer | |
// Unused code from vanilla is commented out, along with the code that resets the buffer | |
private static void drawBuffer(final BufferBuilder bufferBuilder) { | |
if (bufferBuilder.getVertexCount() > 0) { | |
VertexFormat vertexformat = bufferBuilder.getVertexFormat(); | |
int i = vertexformat.getSize(); | |
ByteBuffer bytebuffer = bufferBuilder.getByteBuffer(); | |
List<VertexFormatElement> list = vertexformat.getElements(); | |
for (int j = 0; j < list.size(); ++j) { | |
VertexFormatElement vertexformatelement = list.get(j); | |
// VertexFormatElement.EnumUsage vertexformatelement$enumusage = vertexformatelement.getUsage(); | |
// int k = vertexformatelement.getType().getGlConstant(); | |
// int l = vertexformatelement.getIndex(); | |
bytebuffer.position(vertexformat.getOffset(j)); | |
// moved to VertexFormatElement.preDraw | |
vertexformatelement.getUsage().preDraw(vertexformat, j, i, bytebuffer); | |
} | |
GlStateManager.drawArrays(bufferBuilder.getDrawMode(), 0, bufferBuilder.getVertexCount()); | |
int i1 = 0; | |
for (int j1 = list.size(); i1 < j1; ++i1) { | |
VertexFormatElement vertexformatelement1 = list.get(i1); | |
// VertexFormatElement.EnumUsage vertexformatelement$enumusage1 = vertexformatelement1.getUsage(); | |
// int k1 = vertexformatelement1.getIndex(); | |
// moved to VertexFormatElement.postDraw | |
vertexformatelement1.getUsage().postDraw(vertexformat, i1, i, bytebuffer); | |
} | |
} | |
// bufferBuilder.reset(); // Commented out from the tesselator's vboUploader. Don't reset the buffer | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment