Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/api/java/net/optifine/shaders/ShadersRender.java
Original file line number Diff line number Diff line change
@@ -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() {}

}
Original file line number Diff line number Diff line change
@@ -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<BlockPos> 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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<BlockPos, TileEntity> TILE_ENTITIES = new Object2ObjectArrayMap<>();
public final World world;
public final Collection<BlockPos> renderedBlocks = new ObjectOpenHashSet<>();
private Consumer<WorldSceneRenderer> beforeRender;
private Consumer<WorldSceneRenderer> afterRender;
protected Consumer<WorldSceneRenderer> beforeRender;
protected Consumer<WorldSceneRenderer> afterRender;
private Consumer<RayTraceResult> onLookingAt;
private int clearColor;
private RayTraceResult lastTraceResult;
Expand All @@ -88,6 +94,15 @@ public WorldSceneRenderer setAfterWorldRender(Consumer<WorldSceneRenderer> callb
public WorldSceneRenderer addRenderedBlocks(@Nullable Collection<BlockPos> 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a spot where this should be cleared? Besides when recalculating the TEs?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know, actually. The renderer itself won't get GC'ed when used in JEI plugin, neither did I found a proper place to call clear on this, so I just made this cleared where renderedBlocks got cleared.
I think it might be ok since there's < 1 elements in the map by average?
Or should I use a Cache so it got automatically invalidated?

}
});
}
return this;
}
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be a call to Block.isAir() instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was == before, and I think it's because that a custom block that has block#isAir to return true could still have custom textures.
Generally I think this won't be that much of a difference here. But I can change it to smth like world#isAirBlock.

state = state.getActualState(world, pos);
if (block.canRenderInLayer(state, layer)) {
blockrendererdispatcher.renderBlock(state, pos, world, buffer);
}
}
}

protected void renderTileEntities() {
RenderHelper.enableStandardItemLighting();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this not also call GLSM lighting like the original method?

Copy link
Contributor Author

@MCTian-mi MCTian-mi Oct 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the GlStateManager#enableLighting() is already called in RenderHelper#enableStandardItemLighting() so ig it's fine? Other GLSM calls are called before this method got invoked

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
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/gregtech/client/utils/OptiFineHelper.java
Original file line number Diff line number Diff line change
@@ -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));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -585,7 +586,7 @@ private MBPattern initializePattern(@NotNull MultiblockShapeInfo shapeInfo, @Not
}

TrackedDummyWorld world = new TrackedDummyWorld();
ImmediateWorldSceneRenderer worldSceneRenderer = new ImmediateWorldSceneRenderer(world);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this was the only place where ImmediateWorldSceneRenderer was used, should we go ahead and fold the methods into VBOWorldSceneRenderer and delete the class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was used in terminal I think, but yeah ig we could safely delete this since mui has its own world renderer impl that is exactly the same as ImmediateWorldSceneRenderer (at least the last time I checked)

ImmediateWorldSceneRenderer worldSceneRenderer = new VBOWorldSceneRenderer(world);
worldSceneRenderer.setClearColor(ConfigHolder.client.multiblockPreviewColor);
world.addBlocks(blockMap);

Expand Down
Loading