3
\$\begingroup\$

Note: I am using Minecraft Coder Pack 9.10 for game version 1.8.0 (official release).

I am trying to make a multiline text field similar to the game's original net.minecraft.client.gui.GuiTextField. I have a class extending GuiTextField and overriding almost all of the methods to provide multiline support. I have just about everything working the way it needs to except for rendering.

I want to render the text similarly to the swing JTextArea (where it sets a clip area then renders all the text) but I'm unsure how to do this, or if its even possible to do alongside the game's GlStateManager and WorldRenderer. After some googling I think what I'm looking for is OpenGL stencils. I made sure that the display is created with a pixel format that supports stencils (In net.minecraft.client.Minecraft I changed Display.create((new PixelFormat()).withDepthBits(24)); to Display.create((new PixelFormat()).withDepthBits(24).withStencilBits(8)); to make sure there is a stencil buffer), and have been playing around with a bit of code and so far I have gotten 2 results:

  1. All other buttons on my gui (which render after the textfield) render perfectly fine, just as they should. The text field and the dirt background drawn by GuiScreen.drawDefaultBackgroudn() are all blacked out even if the stencil rectangle I draw covers them completely.

  2. Everything renders just as it did before I had the stencil code.

I have a feeling that the direct calls to the GL class are conflicting with the game's render managers but with the highly obfuscated code in the render manager I have no clue. (I also know that the game's GlStateManager has inner classes called StencilState and StencilFunc, but after some Java Searches in eclipse I can only find variable declarations/instantiations, but the vars are never referenced).

Here is the code I have so far to render the text field (irrelevant code trimmed):

@Override
public void drawTextBox() {
    if (this.getVisible()) {


        // trimmed: create x and y variables & draw border/background of textfield
        //     border/background drawn with GlStateManager and WorldRenderer


        // stencil code I found online
        glClear(GL_DEPTH_BUFFER_BIT);
        glEnable(GL_STENCIL_TEST);
        glColorMask(false, false, false, false);
        glDepthMask(false);
        glStencilFunc(GL_NEVER, 1, 0xFF);
        glStencilOp(GL_REPLACE, GL_KEEP, GL_KEEP);  // draw 1s on test fail (always)

        // draw stencil pattern
        glStencilMask(0xFF);
        glClear(GL_STENCIL_BUFFER_BIT);  // needs mask=0xFF

        // X1 is just inside the left border
        // X4 is just inside the right border
        // Y1 is just inside the top border
        // Y4 is just inside the bottom border
        glBegin(GL_QUADS);
        glVertex3f(x4, y4, 0.0f);
        glVertex3f(x4, y1, 0.0f);
        glVertex3f(x1, y1, 0.0f);
        glVertex3f(x1, y4, 0.0f);
        glEnd();

        glColorMask(true, true, true, true);
        glDepthMask(true);
        glStencilMask(0x00);
        // draw where stencil's value is 0
        glStencilFunc(GL_EQUAL, 0, 0xFF);
        /* (nothing to draw) */
        // draw only where stencil's value is 1
        glStencilFunc(GL_EQUAL, 1, 0xFF);

        //offset drawing location of text
        GlStateManager.translate(-colViewOffset, -lineViewOffset * 10, 0.0);

        // main drawtext
        int xOff = hShadow ? 10 : 0;
        for (int lin = lineViewOffset; lin < lineViewOffset + height / 10 && lin < lines.size(); lin++) {
            fontRenderer.drawString(lines.get(lin), x, y + (lin) * 10, getEnabled() ? 0xE0E0E0 : 0x707070);
        }

        // draw text under vertical shadows
        if (vShadow) {
            if (lineViewOffset - 1 >= 0) {
                fontRenderer.drawString(lines.get(lineViewOffset - 1), x, y + lineViewOffset * 10 - 10, getEnabled() ? 0xE0E0E0 : 0x707070);
            }
            if (lineViewOffset + height / 10 < lines.size()) {
                fontRenderer.drawString(lines.get(lineViewOffset + height / 10), x, y + height + lineViewOffset * 10, getEnabled() ? 0xE0E0E0 : 0x707070);
            }
        }


        // trimmed: draw cursor


        GlStateManager.translate(colViewOffset, lineViewOffset * 10, 0.0);

        // disable stencils
        glDisable(GL_STENCIL_TEST);


        // trimmed: draw bordershadows
        //     bordershadows drawn with GlStateManager and WorldRenderer
    }
}

With everything I have right now this is what I get:

The multiline text field

What can I do to my code/the games render code to only draw text inside the white/gray rectangle bordering the textfield?

\$\endgroup\$
  • \$\begingroup\$ afaik glClear is affected by the stencil test. If func is gl_never will it ever pass? Ie does your clear have intended effect? Afaik the value put in stencil is set with glclearstencil, not glstencilfunc/glstencilop. \$\endgroup\$ – Andreas Apr 26 '16 at 16:15
1
\$\begingroup\$

Happy new years, this will solve your problem:

float x1 = //left bound
        float x2 = //right bound
        float y1 = //up bound
        float y2 = //down bound
        ScaledResolution sr = new ScaledResolution(Minecraft.getMinecraft());
                x1 *= sr.getScaleFactor();
                y1 *= sr.getScaleFactor();
                x2 *= sr.getScaleFactor();
                y2 *= sr.getScaleFactor();
                float temp;
                if (y1 > y2) {
                    temp = y2;
                    y2 = y1;
                    y1 = temp;
                }
                GL11.glScissor((int)x1, (int)(Display.getHeight()-y2), (int)(x2-x1), (int)(y2-y1));
                GL11.glEnable(GL11.GL_SCISSOR_TEST);


        //DRAW TEXT THAT NEEDS TO BE CLIPPED OUTSIDE THE ABOVE SPECIFIED AREA

        GL11.glDisable(GL11.GL_SCISSOR_TEST);
\$\endgroup\$
  • 1
    \$\begingroup\$ Mind explaining your answer? \$\endgroup\$ – Draco18s Jan 1 at 1:19
  • \$\begingroup\$ Get rid of all the other GL stuff and replace it with that. It the scissor is like a rectangular stencil and fully compatible with minecraft. I included code that scales the size to the current screen scaleFactor. \$\endgroup\$ – Journey Appelcede Jan 1 at 1:24
  • \$\begingroup\$ @JourneyAppelcede keep in mind that in most cases, people want to know why something works. So just providing a solution without an explanation might not be enough. \$\endgroup\$ – TomTsagk Feb 1 at 15:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.