Sunday, October 9, 2022
HomeGame Developmentjava - Drawing fonts with LWJGL3 with OpenGL

java – Drawing fonts with LWJGL3 with OpenGL


I’m very sorry if this query has already been answered, however since I’ve been battling this for a lot of days I assumed I’d give it a shot right here.

My aim is to by some means render some textual content on the display, so I might make a rating counter for my sport and in addition perhaps add some informative textual content. If somebody might a minimum of level me in the proper path, then I’d be very grateful.

Tried to attract textual content utilizing Bitmaps, however have not had any luck doing it.

I’m utilizing LWJGL3 and lwjgl-utils.jar for GLU.

So I acquired this code

bundle com.thecherno.flappy.font;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.picture.BufferedImage;
import java.awt.picture.DataBuffer;
import java.awt.picture.DataBufferByte;
import java.awt.picture.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Map;
import java.awt.GraphicsEnvironment;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

import static org.lwjgl.glfw.GLFW.glfwGetCurrentContext;


/**
 * A TrueType font implementation initially for Slick, edited for Bobjob's Engine
 *
 * @authentic creator James Chambers (Jimmy)
 * @authentic creator Jeremy Adams (elias4444)
 * @authentic creator Kevin Glass (kevglass)
 * @authentic creator Peter Korzuszek (genail)
 *
 * @new model edited by David Aaron Muhar (bobjob)
 */
public class TrueTypeFont {
public closing static int
        ALIGN_LEFT = 0,
        ALIGN_RIGHT = 1,
        ALIGN_CENTER = 2;
/** Array that holds needed details about the font characters */
personal IntObject[] charArray = new IntObject[256];

/** Map of consumer outlined font characters (Character <-> IntObject) */
personal Map customChars = new HashMap();

/** Boolean flag on whether or not AntiAliasing is enabled or not */
personal boolean antiAlias;

/** Font's measurement */
personal int fontSize = 0;

/** Font's top */
personal int fontHeight = 0;

/** Texture used to cache the font 0-255 characters */
personal int fontTextureID;

/** Default font texture width */
personal int textureWidth = 512;

/** Default font texture top */
personal int textureHeight = 512;

/** A reference to Java's AWT Font that we create our font texture from */
personal Font font;

/** The font metrics for our Java AWT font */
personal FontMetrics fontMetrics;


personal int correctL = 9, correctR = 8;

personal class IntObject {
    /** Character's width */
    public int width;

    /** Character's top */
    public int top;

    /** Character's saved x place */
    public int storedX;

    /** Character's saved y place */
    public int storedY;
}


public TrueTypeFont(Font font, boolean antiAlias, char[] additionalChars) {
    this.font = font;
    this.fontSize = font.getSize()+3;
    this.antiAlias = antiAlias;

    createSet( additionalChars );

    fontHeight -= 1;
    if (fontHeight <= 0) fontHeight = 1;
}

public TrueTypeFont(Font font, boolean antiAlias) {
    this( font, antiAlias, null );
}
public void setCorrection(boolean on) {
    if (on) {
        correctL = 2;
        correctR = 1;
    } else {
        correctL = 0;
        correctR = 0;
    }
}
personal BufferedImage getFontPicture(char ch) {
    // Create a short lived picture to extract the character's measurement
    BufferedImage tempfontImage = new BufferedImage(1, 1,
            BufferedImage.TYPE_INT_ARGB);
    Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
    if (antiAlias == true) {
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
    }
    g.setFont(font);
    fontMetrics = g.getFontMetrics();
    int charwidth = fontMetrics.charWidth(ch)+8;

    if (charwidth <= 0) {
        charwidth = 7;
    }
    int charheight = fontMetrics.getHeight()+3;
    if (charheight <= 0) {
        charheight = fontSize;
    }

    // Create one other picture holding the character we're creating
    BufferedImage fontImage;
    fontImage = new BufferedImage(charwidth, charheight,
            BufferedImage.TYPE_INT_ARGB);
    Graphics2D gt = (Graphics2D) fontImage.getGraphics();
    if (antiAlias == true) {
        gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
    }
    gt.setFont(font);

    gt.setColor(Color.WHITE);
    int charx = 3;
    int chary = 1;
    gt.drawString(String.worthOf(ch), (charx), (chary)
            + fontMetrics.getAscent());

    return fontImage;

}

personal void createSet( char[] customCharsArray ) {
    // If there are customized chars then I develop the font texture twice
    if  (customCharsArray != null && customCharsArray.size > 0) {
        textureWidth *= 2;
    }

    // In any case this ought to be carried out in different means. Texture with measurement 512x512
    // can keep solely 256 characters with decision of 32x32. The texture
    // measurement ought to be calculated dynamicaly by  character sizes.

    strive {

        BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = (Graphics2D) imgTemp.getGraphics();

        g.setColor(new Color(0,0,0,1));
        g.fillRect(0,0,textureWidth,textureHeight);

        int rowHeight = 0;
        int positionX = 0;
        int positionY = 0;

        int customCharsLength = ( customCharsArray != null ) ? customCharsArray.size : 0;

        for (int i = 0; i < 256 + customCharsLength; i++) {

            // get 0-255 characters after which customized characters
            char ch = ( i < 256 ) ? (char) i : customCharsArray[i-256];

            BufferedImage fontImage = getFontPicture(ch);

            IntObject newIntObject = new IntObject();

            newIntObject.width = fontImage.getWidth();
            newIntObject.top = fontImage.getHeight();

            if (positionX + newIntObject.width >= textureWidth) {
                positionX = 0;
                positionY += rowHeight;
                rowHeight = 0;
            }

            newIntObject.storedX = positionX;
            newIntObject.storedY = positionY;

            if (newIntObject.top > fontHeight) {
                fontHeight = newIntObject.top;
            }

            if (newIntObject.top > rowHeight) {
                rowHeight = newIntObject.top;
            }

            // Draw it right here
            g.drawImage(fontImage, positionX, positionY, null);

            positionX += newIntObject.width;

            if( i < 256 ) { // normal characters
                charArray[i] = newIntObject;
            } else { // customized characters
                customChars.put( new Character( ch ), newIntObject );
            }

            fontImage = null;
        }

        fontTextureID = loadImage(imgTemp);



        //.getTexture(font.toString(), imgTemp);

    } catch (Exception e) {
        System.err.println("Failed to create font.");
        e.printStackTrace();
    }
}

personal void drawQuad(float drawX, float drawY, float drawX2, float drawY2,
                      float srcX, float srcY, float srcX2, float srcY2) {
    float DrawWidth = drawX2 - drawX;
    float DrawPeak = drawY2 - drawY;
    float TextureSrcX = srcX / textureWidth;
    float TextureSrcY = srcY / textureHeight;
    float SrcWidth = srcX2 - srcX;
    float SrcHeight = srcY2 - srcY;
    float RenderWidth = (SrcWidth / textureWidth);
    float RenderHeight = (SrcHeight / textureHeight);

    GL11.glTexCoord2f(TextureSrcX, TextureSrcY);
    GL11.glVertex2f(drawX, drawY);
    GL11.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight);
    GL11.glVertex2f(drawX, drawY + DrawPeak);
    GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
    GL11.glVertex2f(drawX + DrawWidth, drawY + DrawPeak);
    GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY);
    GL11.glVertex2f(drawX + DrawWidth, drawY);
}

public int getWidth(String whatchars) {
    int totalwidth = 0;
    IntObject intObject = null;
    int currentChar = 0;
    for (int i = 0; i < whatchars.size(); i++) {
        currentChar = whatchars.charAt(i);
        if (currentChar < 256) {
            intObject = charArray[currentChar];
        } else {
            intObject = (IntObject)customChars.get( new Character( (char) currentChar ) );
        }

        if( intObject != null )
            totalwidth += intObject.width;
    }
    return totalwidth;
}

public int getHeight() {
    return fontHeight;
}


public int getHeight(String PeakString) {
    return fontHeight;
}

public int getLineHeight() {
    return fontHeight;
}

public void drawString(float x, float y,
                       String whatchars, float scaleX, float scaleY) {
    drawString(x,y,whatchars, 0, whatchars.size()-1, scaleX, scaleY, ALIGN_LEFT);
}
public void drawString(float x, float y,
                       String whatchars, float scaleX, float scaleY, int format) {
    drawString(x,y,whatchars, 0, whatchars.size()-1, scaleX, scaleY, format);
}


public void drawString(float x, float y,
                       String whatchars, int beginIndex, int finishIndex,
                       float scaleX, float scaleY,
                       int format
) {

    IntObject intObject = null;
    int charCurrent;


    int totalwidth = 0;
    int i = beginIndex, d, c;
    float startY = 0;



    change (format) {
        case ALIGN_RIGHT: {
            d = -1;
            c = correctR;

            whereas (i < finishIndex) {
                if (whatchars.charAt(i) == 'n') startY -= fontHeight;
                i++;
            }
            break;
        }
        case ALIGN_CENTER: {
            for (int l = beginIndex; l <= finishIndex; l++) {
                charCurrent = whatchars.charAt(l);
                if (charCurrent == 'n') break;
                if (charCurrent < 256) {
                    intObject = charArray[charCurrent];
                } else {
                    intObject = (IntObject)customChars.get( new Character( (char) charCurrent ) );
                }
                totalwidth += intObject.width-correctL;
            }
            totalwidth /= -2;
        }
        case ALIGN_LEFT:
        default: {
            d = 1;
            c = correctL;
            break;
        }

    }

    GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
    GL11.glBegin(GL11.GL_QUADS);

    whereas (i >= beginIndex && i <= finishIndex) {

        charCurrent = whatchars.charAt(i);
        if (charCurrent < 256) {
            intObject = charArray[charCurrent];
        } else {
            intObject = (IntObject)customChars.get( new Character( (char) charCurrent ) );
        }

        if( intObject != null ) {
            if (d < 0) totalwidth += (intObject.width-c) * d;
            if (charCurrent == 'n') {
                startY -= fontHeight * d;
                totalwidth = 0;
                if (format == ALIGN_CENTER) {
                    for (int l = i+1; l <= finishIndex; l++) {
                        charCurrent = whatchars.charAt(l);
                        if (charCurrent == 'n') break;
                        if (charCurrent < 256) {
                            intObject = charArray[charCurrent];
                        } else {
                            intObject = (IntObject)customChars.get( new Character( (char) charCurrent ) );
                        }
                        totalwidth += intObject.width-correctL;
                    }
                    totalwidth /= -2;
                }
                //if middle get subsequent traces complete width/2;
            }
            else {
                drawQuad((totalwidth + intObject.width) * scaleX + x, startY * scaleY + y,
                        totalwidth * scaleX + x,
                        (startY + intObject.top) * scaleY + y, intObject.storedX + intObject.width,
                        intObject.storedY + intObject.top,intObject.storedX,
                        intObject.storedY);
                if (d > 0) totalwidth += (intObject.width-c) * d ;
            }
            i += d;

        }
    }
    GL11.glEnd();
}
public static int loadImage(BufferedImage bufferedImage) {
    strive {
        brief width       = (brief)bufferedImage.getWidth();
        brief top      = (brief)bufferedImage.getHeight();
        //textureLoader.bpp = bufferedImage.getColorMannequin().hasAlpha() ? (byte)32 : (byte)24;
        int bpp = (byte)bufferedImage.getColorMannequin().getPixelSize();
        ByteBuffer byteBuffer;
        DataBuffer db = bufferedImage.getData().getDataBuffer();
        if (db instanceof DataBufferInt) {
            int intI[] = ((DataBufferInt)(bufferedImage.getData().getDataBuffer())).getData();
            byte newI[] = new byte[intI.length * 4];
            for (int i = 0; i < intI.size; i++) {
                byte b[] = intToByteArray(intI[i]);
                int newIndex = i*4;

                newI[newIndex]   = b[1];
                newI[newIndex+1] = b[2];
                newI[newIndex+2] = b[3];
                newI[newIndex+3] = b[0];
            }

            byteBuffer  = ByteBuffer.allocateDirect(
                    width*top*(bpp/8))
                    .order(ByteOrder.nativeOrder())
                    .put(newI);
        } else {
            byteBuffer  = ByteBuffer.allocateDirect(
                    width*top*(bpp/8))
                    .order(ByteOrder.nativeOrder())
                    .put(((DataBufferByte)(bufferedImage.getData().getDataBuffer())).getData());
        }
        byteBuffer.flip();


        int insideFormat = GL11.GL_RGBA8,
                format = GL11.GL_RGBA;
        IntBuffer   textureId =  BufferUtils.createIntBuffer(1);;
        GL11.glGenTextures(textureId);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0));


        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);

        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);

        GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);



        GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D,
                insideFormat,
                width,
                top,
                format,
                GL11.GL_UNSIGNED_BYTE,
                byteBuffer);
        return textureId.get(0);

    } catch (Exception e) {
        e.printStackTrace();
        System.exit(-1);
    }

    return -1;
}
public static boolean isSupported(String fontname) {
    Font font[] = getFonts();
    for (int i = font.length-1; i >= 0; i--) {
        if (font[i].getName().equalsIgnoreCase(fontname))
            return true;
    }
    return false;
}
public static Font[] getFonts() {
    return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
}
public static byte[] intToByteArray(int worth) {
    return new byte[] {
            (byte)(worth >>> 24),
            (byte)(worth >>> 16),
            (byte)(worth >>> 8),
            (byte)worth};
}

public void destroy() {
    IntBuffer scratch = BufferUtils.createIntBuffer(1);
    scratch.put(0, fontTextureID);
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
    GL11.glDeleteTextures(scratch);
}
}

And my Main class appears like this

bundle com.thecherno.flappy;

import com.thecherno.flappy.font.BitmapFonts;
import com.thecherno.flappy.font.Ley2dFontLWJGL3;
import com.thecherno.flappy.font.TrueTypeFont;
import com.thecherno.flappy.graphics.Shader;
import com.thecherno.flappy.enter.Input;
import com.thecherno.flappy.degree.Level;
import com.thecherno.flappy.maths.Matrix4f;
import org.lwjgl.glfw.GLFWKeyCallback;
import org.lwjgl.glfw.GLFWvidmode;
import org.lwjgl.opengl.GLContext;
import org.lwjgl.util.glu.GLU;

import java.awt.*;
import java.io.IOException;
import java.nio.ByteBuffer;

import static  org.lwjgl.opengl.GL11.*;
import static  org.lwjgl.opengl.GL13.*;
import static  org.lwjgl.system.MemoryUtil.*;

import static org.lwjgl.glfw.GLFW.*;

public class Main implements Runnable {

personal int width = 1280;
personal int top = 720;

personal Thread thread;
personal boolean operating = false;

// Setting key callback
personal GLFWKeyCallback keyCallback;

personal lengthy window; // A "pointer" to reminiscence tackle of window(id)

personal Level degree;

public void begin() {
    operating = true;
    thread = new Thread(this, "Game");
    thread.begin(); // This will run the run() methodology.
}

personal void init() {

    if (glfwInit() != GL_TRUE) {
        System.err.println("Could not initialize GLFW!");
        return;
    }

    glfwWindowTrace(GLFW_RESIZABLE, GL_TRUE);
    window = glfwCreateWindow(width, top, "Flappy", NULL, NULL);
    if (window == NULL) {
        System.err.println("Could not  create GLFW window!");
        return;
    }

    glfwSetKeyCallback(window, keyCallback = new Input());

    ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
    glfwSetWindowPos(window, (GLFWvidmode.width(vidmode) - width) / 2, (GLFWvidmode.top(vidmode) - top) / 2);

    glfwMakeContextCurrent(window);
    glfwShowWindow(window);
    GLContext.createFromCurrent();

    // GLInit
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
    glClearColor(0.5f,0.5f,0.5f,0f); // Black Background
    glDisable(GL_DITHER);
    glDepthFunc(GL_LESS); // Depth perform much less or equal
    glEnable(GL_NORMALIZE); // calculated normals when scaling
//        glEnable(GL_CULL_FACE); // forestall render of again floor
    glEnable(GL_ALPHA_TEST); // permits alpha channels or transperancy
    glAlphaFunc(GL_GREATER, 0.1f); // units aplha perform
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // High high quality visuals
    glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); //  Really Nice Perspective Calculations
    glShadeModel(GL_SMOOTH); // Enable Smooth Shading
//        glViewport(0, 0, 800, 600);
    glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
    glLoadIdentity(); // Reset The Projection Matrix
    GLU.gluPerspective(30, width / (float) top, 1f, 300f);  //Aspect Ratio Of The Window
    glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
    glDepthMask(true);                             // Enable Depth Mask
    //

    glEnable(GL_DEPTH_TEST);
    glActiveTexture(GL_TEXTURE1);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    System.out.println("OpenGL: " + glGetString(GL_VERSION));

    Shader.loadAll();
    Shader.BG.allow();

    Matrix4f pr_matrix = Matrix4f.orthographic(-10.0f, 10.0f, -10.0f * 9.0f / 16.0f, 10.0f * 9.0f / 16.0f, -1.0f, 1.0f);
    Shader.BG.setUniformMat4f("pr_matrix", pr_matrix);
    Shader.BG.setUniform1i("tex", 1);

    Shader.BIRD.setUniformMat4f("pr_matrix", pr_matrix);
    Shader.BIRD.setUniform1i("tex", 1);

    Shader.PIPE.setUniformMat4f("pr_matrix", pr_matrix);
    Shader.PIPE.setUniform1i("tex", 1);

    Shader.IGTITLE.setUniformMat4f("pr_matrix", pr_matrix);
    Shader.IGTITLE.setUniform1i("tex", 1);

    Shader.BG.disable();

    degree = new Level();
}

public void run() {
    init();

    lengthy lastTime = System.nanoTime();
    double delta = 0.0;
    double ns = 1000000000.0 / 60.0;
    lengthy timer = System.currentTimeMillis();
    int updates = 0;
    int frames = 0;

    whereas (operating) {
        lengthy now = System.nanoTime();
        delta += (now - lastTime) / ns;
        lastTime = now;
        if (delta >= 1.0) {
            replace();
            updates++;
            delta--;
        }
        render();
        frames++;
        if (System.currentTimeMillis() - timer > 1000) {
            timer += 1000;
            System.out.println(updates + " ups, " + frames + " fps");
            updates = 0;
            frames = 0;
        }

        if (glfwWindowShouldClose(window) == GL_TRUE) {
            operating = false;
        }

    }
    // Key callback thingy.
    keyCallback.launch();
    glfwDestroyWindow(window);
    glfwTerminate();
}

personal void replace() {
    glfwPollEvents();
    /*if (Input.keys[GLFW_KEY_SPACE]) {
        System.out.println("FLAP!");
    }*/
    degree.replace();

    // Press Escape to exit the sport.
    if (Input.isKeyDown(GLFW_KEY_ESCAPE)) {
        glfwDestroyWindow(window);
        glfwTerminate();
        System.exit(0);
    }

    if (degree.isGameOver()) {
        degree = new Level();
    }
}

personal void render() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    degree.render();

    glMatrixMode(GL_PROJECTION);                        // Select The Projection Matrix
    glPushMatrix();                                     // Store The Projection Matrix
    glLoadIdentity();                                   // Reset The Projection Matrix
    glOrtho(0, width, 0, top, -1, 1);                          // Set Up An Ortho Screen
    glMatrixMode(GL_MODELVIEW);                         // Select The Modelview Matrix
    glPushMatrix();                                     // Store The Modelview Matrix
    glLoadIdentity();

    TrueTypeFont trueTypeFont;
    String fontName = "Agent Orange";
    if (!TrueTypeFont.isSupported(fontName)) fontName = "serif";
    Font font = new Font(fontName, Font.ITALIC | Font.BOLD, 40);
    trueTypeFont = new TrueTypeFont(font, true);
    //DRAW STRING FROM THE - CENTER
    //HALF SCALE OF 0.5f
    trueTypeFont.drawString(width / 2, trueTypeFont.getHeight() * 3,
            "on the doable sound of your title.n" +
                    "No I wouldnt go that far.n" +
                    "No.", 0.5f, 0.5f, TrueTypeFont.ALIGN_CENTER);

    int error = glGetError();

    if (error != GL_NO_ERROR) {
        System.out.println(error);
    }
    glfwSwapBuffers(window);
}

public static void important(String[] args) {
    new Main().begin();
}
}

For rendering textual content I added this code within the Main class

    TrueTypeFont trueTypeFont;
    String fontName = "Agent Orange";
    if (!TrueTypeFont.isSupported(fontName)) fontName = "serif";
    Font font = new Font(fontName, Font.ITALIC | Font.BOLD, 40);
    trueTypeFont = new TrueTypeFont(font, true);
    //DRAW STRING FROM THE - CENTER
    //HALF SCALE OF 0.5f
    trueTypeFont.drawString(width / 2, trueTypeFont.getHeight() * 3,
            "on the doable sound of your title.n" +
                    "No I wouldnt go that far.n" +
                    "No.", 0.5f, 0.5f, TrueTypeFont.ALIGN_CENTER);

At the second all I get are some white rectangles, so I assume that one thing is rendering. I’ve learn that rendering white packing containers is by some means a typical drawback, however I am unable to discover a answer. I’m not utilizing any assets like .ttf or .png fonts/bitmaps for this code, ought to I? How?

Also, sorry for my english. I’d be perpetually grateful if somebody might assist me out right here.

Best needs to all of You!

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments