Skip to content

E.g.1: fixed-function 3D shapes

Render a wired cube

java
@SubscribeEvent
public static void onRenderWorldLast(RenderWorldLastEvent event)
{
    EntityPlayerSP player = Minecraft.getMinecraft().player;
    if (player == null) return;
    
    storeCommonGlStates();

    GL11.glPushMatrix();

    GlStateManager.disableCull();
    GlStateManager.enableDepth();
    GlStateManager.disableTexture2D();
    GlStateManager.disableLighting();
    GlStateManager.disableBlend();

    double playerX = player.lastTickPosX + (player.posX - player.lastTickPosX) * event.getPartialTicks();
    double playerY = player.lastTickPosY + (player.posY - player.lastTickPosY) * event.getPartialTicks();
    double playerZ = player.lastTickPosZ + (player.posZ - player.lastTickPosZ) * event.getPartialTicks();

    // minus camera pos and then plus target pos
    // target pos here: 0, 100, 0
    GlStateManager.translate(-(float)(playerX), -(float)(playerY) + 100f, -(float)(playerZ));
    GlStateManager.scale(10f, 10f, 10f);

    GlStateManager.glLineWidth(3f);

    Tessellator tessellator = Tessellator.getInstance();
    BufferBuilder buffer = tessellator.getBuffer();

    buffer.begin(GL11.GL_LINES, DefaultVertexFormats.POSITION_COLOR);

    double size = 1f;

    buffer.pos(0f, 0f, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, 0f, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, 0f, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, size, 0f).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(size, size, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, size, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, size, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, 0f, 0f).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(0f, 0f, size).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, 0f, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(size, 0f, size).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, size, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(size, size, size).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, size, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(0f, size, size).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, 0f, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(0f, 0f, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, 0f, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(size, 0f, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, 0f, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(size, size, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(size, size, size).color(1f, 1f, 1f, 1f).endVertex();

    buffer.pos(0f, size, 0f).color(1f, 1f, 1f, 1f).endVertex();
    buffer.pos(0f, size, size).color(1f, 1f, 1f, 1f).endVertex();

    tessellator.draw();

    GL11.glPopMatrix();

    restoreCommonGlStates();
}

Snipaste_2025-02-02_13-58-02

Try toggle off/on each state and see how things change.

Drawing 2D/3D objects using the fixed-func pipeline is generally considered easy, but less powerful, less streamlined, and boring.

Here are the issues of the code snippet above.

GL Level:

  • Pipeline stall due to state fetching (read "GL States Related Concerns" for details)
  • Potential write-after-read hazard

Design Level:

  • Global singleton Tessellator access which blurs phase restrictions
  • Redundant buffer building in every frame while the geometry is fixed
  • Render code can't be reordered/composed
  • Immediate mode thinking which is bad when it comes to a large scale scene

Contributors

© 2024 CleanroomMC. All Rights Reserved.