com.grillecube.engine.renderer.gui.font.FontModel.java Source code

Java tutorial

Introduction

Here is the source code for com.grillecube.engine.renderer.gui.font.FontModel.java

Source

/**
**   This file is part of the project https://github.com/toss-dev/VoxelEngine
**
**   License is available here: https://raw.githubusercontent.com/toss-dev/VoxelEngine/master/LICENSE.md
**
**   PEREIRA Romain
**                                       4-----7          
**                                      /|    /|
**                                     0-----3 |
**                                     | 5___|_6
**                                     |/    | /
**                                     1-----2
*/

package com.grillecube.engine.renderer.gui.font;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL15;

import com.grillecube.engine.maths.Matrix4f;
import com.grillecube.engine.maths.Vector2f;
import com.grillecube.engine.maths.Vector3f;
import com.grillecube.engine.maths.Vector4f;
import com.grillecube.engine.opengl.GLH;
import com.grillecube.engine.opengl.object.GLVertexArray;
import com.grillecube.engine.opengl.object.GLVertexBuffer;

public class FontModel {
    /** opengl IDs */
    private GLVertexArray _vao;
    private GLVertexBuffer _vbo;
    private int _vertex_count;

    /** model data */
    private Vector3f _pos;
    private Vector3f _scale;
    private Vector3f _rot;
    private Font _font;

    /** status */
    public static final int STATE_INITIALIZED = 1;
    public static final int STATE_TEXT_UP_TO_DATE = 2;
    public static final Vector2f DEFAULT_FONT_SIZE2 = new Vector2f(1.0f, 1.0f);
    public static final Vector3f DEFAULT_FONT_SIZE3 = new Vector3f(1.0f, 1.0f, 1.0f);
    private int _state;

    /** transf matrix */
    private Matrix4f _matrix;

    /** text */
    private String _text;

    /** parameters */
    private Vector4f _color;
    private float _border_width;
    private float _border_edge;
    private Vector2f _outline_offset;
    private Vector3f _outline_color;

    public FontModel(Font font) {
        this._state = 0;
        this._text = new String();
        this._font = font;
        this._matrix = new Matrix4f();
        this._pos = new Vector3f(0, 0, 0);
        this._rot = new Vector3f(0, 0, 0);
        this._scale = new Vector3f(1, 1, 1);
        this._color = new Vector4f(1.0f, 0.0f, 0.0f, 1.0f);
        this._border_width = 0.0f;
        this._border_edge = 0.5f;
        this._outline_color = new Vector3f(0, 0, 0);
        this._outline_offset = new Vector2f(0, 0);
        this.updateTransformationMatrix();
    }

    private void initialize() {
        this._vao = GLH.glhGenVAO();
        this._vbo = GLH.glhGenVBO();

        this._vao.bind();
        {
            this._vbo.bind(GL15.GL_ARRAY_BUFFER);

            this._vao.setAttribute(0, 3, GL11.GL_FLOAT, false, (3 + 2 + 4) * 4, 0);
            this._vao.setAttribute(1, 2, GL11.GL_FLOAT, false, (3 + 2 + 4) * 4, 3 * 4);
            this._vao.setAttribute(2, 4, GL11.GL_FLOAT, false, (3 + 2 + 4) * 4, (3 + 2) * 4);

            this._vbo.unbind(GL15.GL_ARRAY_BUFFER);

            this._vao.enableAttribute(0);
            this._vao.enableAttribute(1);
            this._vao.enableAttribute(2);
        }
        this._vao.unbind();

        this.setState(FontModel.STATE_INITIALIZED);
    }

    /** destroy the model */
    public void destroy() {
        if (this.hasState(FontModel.STATE_INITIALIZED)) {
            GLH.glhDeleteObject(this._vao);
            GLH.glhDeleteObject(this._vbo);
            this.unsetState(FontModel.STATE_INITIALIZED);
        }
    }

    /** add a string to the model */
    public void addText(String str) {
        this.setText(this._text == null ? str : this._text + str);
    }

    /** rebuild the mesh depending on the given string */
    public void setText(String str) {
        this._text = str;
        this.requestUpdate();
    }

    public void setFont(Font font) {
        this._font = font;
        this.requestUpdate();
    }

    /** set the font color */
    public void setFontColor(float r, float g, float b, float a) {
        this._color.set(r, g, b, a);
    }

    /** update FontModel GLVertexBuffer vertices depending on 'this._text' */
    private void updateText() {
        float[] vertices = this.generateFontBuffer(this._text);
        this._vbo.bind(GL15.GL_ARRAY_BUFFER);
        if (vertices != null) {
            this._vbo.bufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
            this._vertex_count = vertices.length / 5;
        } else {
            this._vbo.bufferSize(GL15.GL_ARRAY_BUFFER, 0, GL15.GL_STATIC_DRAW);
            this._vertex_count = 0;
        }
        this.setState(FontModel.STATE_TEXT_UP_TO_DATE);
    }

    /** each char is a quad (4 vertex) of 5 floats (pos + uv) */
    private float[] generateFontBuffer(String str) {
        if (str == null || str.length() == 0 || this._font == null) {
            return (null);
        }

        Vector4f color = this._color;
        float[] vertices = new float[str.length() * (6 * (3 + 2 + 4))];

        float posx = 0;
        float posy = 0;
        float posz = 0;
        int index = 0;

        int length = str.length();
        for (int i = 0; i < length; i++) {
            char c = str.charAt(i);
            if (c == '\n') {
                posy = posy - FontFile.LINEHEIGHT;
                posx = 0;
                continue;
            }

            FontChar fchar = this._font.getFile().getCharData(str.charAt(i));
            float xsize = fchar.width;
            float ysize = fchar.height;

            // first vertex
            vertices[index++] = posx + fchar.xoffset;
            vertices[index++] = posy - fchar.yoffset;
            vertices[index++] = posz;
            vertices[index++] = fchar.uvx;
            vertices[index++] = fchar.uvy;
            vertices[index++] = color.x;
            vertices[index++] = color.y;
            vertices[index++] = color.z;
            vertices[index++] = color.w;

            // second vertex
            vertices[index++] = posx + fchar.xoffset;
            vertices[index++] = posy - fchar.yoffset - ysize;
            vertices[index++] = posz;
            vertices[index++] = fchar.uvx;
            vertices[index++] = fchar.uvy + fchar.uvheight;
            vertices[index++] = color.x;
            vertices[index++] = color.y;
            vertices[index++] = color.z;
            vertices[index++] = color.w;

            // third vertex
            vertices[index++] = posx + fchar.xoffset + xsize;
            vertices[index++] = posy - fchar.yoffset - ysize;
            vertices[index++] = posz;
            vertices[index++] = fchar.uvx + fchar.uvwidth;
            vertices[index++] = fchar.uvy + fchar.uvheight;
            vertices[index++] = color.x;
            vertices[index++] = color.y;
            vertices[index++] = color.z;
            vertices[index++] = color.w;

            // first vertex
            vertices[index++] = posx + fchar.xoffset;
            vertices[index++] = posy - fchar.yoffset;
            vertices[index++] = posz;
            vertices[index++] = fchar.uvx;
            vertices[index++] = fchar.uvy;
            vertices[index++] = color.x;
            vertices[index++] = color.y;
            vertices[index++] = color.z;
            vertices[index++] = color.w;

            // third vertex
            vertices[index++] = posx + fchar.xoffset + xsize;
            vertices[index++] = posy - fchar.yoffset - ysize;
            vertices[index++] = posz;
            vertices[index++] = fchar.uvx + fchar.uvwidth;
            vertices[index++] = fchar.uvy + fchar.uvheight;
            vertices[index++] = color.x;
            vertices[index++] = color.y;
            vertices[index++] = color.z;
            vertices[index++] = color.w;

            // fourth vertex
            vertices[index++] = posx + fchar.xoffset + xsize;
            vertices[index++] = posy - fchar.yoffset;
            vertices[index++] = posz;
            vertices[index++] = fchar.uvx + fchar.uvwidth;
            vertices[index++] = fchar.uvy;
            vertices[index++] = color.x;
            vertices[index++] = color.y;
            vertices[index++] = color.z;
            vertices[index++] = color.w;

            posx = posx + fchar.xadvance + fchar.xoffset;
        }

        return (vertices);
    }

    /** in percent depending on screen position (0 to 100 %) */
    public void setPosition(float x, float y, float z) {
        this._pos.x = x;
        this._pos.y = y;
        this._pos.z = z;
        this.updateTransformationMatrix();
    }

    public void setX(float x) {
        this.setPosition(x, this._pos.y, this._pos.z);
    }

    public void setY(float y) {
        this.setPosition(this._pos.x, y, this._pos.z);
    }

    public void setZ(float z) {
        this.setPosition(this._pos.x, this._pos.y, z);
    }

    public void setRotation(float x, float y, float z) {
        this._rot.x = x;
        this._rot.y = y;
        this._rot.z = z;
        this.updateTransformationMatrix();
    }

    public void setScale(float x, float y, float z) {
        this._scale.set(x, y, z);
        this.updateTransformationMatrix();
    }

    private void updateTransformationMatrix() {
        this._matrix.setIdentity();
        this._matrix.translate(this._pos);
        this._matrix.rotate(this._rot.x, new Vector3f(1, 0, 0));
        this._matrix.rotate(this._rot.y, new Vector3f(0, 1, 0));
        this._matrix.rotate(this._rot.z, new Vector3f(0, 0, 1));
        this._matrix.scale(this._scale);
    }

    /** render this font model */
    public void render() {
        if (this.hasState(FontModel.STATE_INITIALIZED) == false) {
            this.initialize();
        }

        if (this.hasState(FontModel.STATE_TEXT_UP_TO_DATE) == false) {
            this.updateText();
        }

        if (this._vertex_count == 0 || this._font == null) {
            return;
        }

        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

        this._vao.bind();

        this._font.getTexture().bind(GL13.GL_TEXTURE0, GL11.GL_TEXTURE_2D);
        this._vao.draw(GL11.GL_TRIANGLES, 0, this._vertex_count);
    }

    public boolean hasState(int state) {
        return ((this._state & state) == state);
    }

    private void setState(int state) {
        this._state = this._state | state;
    }

    private void unsetState(int state) {
        this._state = this._state & ~(state);
    }

    public Matrix4f getTransformationMatrix() {
        return (this._matrix);
    }

    public String getText() {
        return (this._text);
    }

    @Override
    public String toString() {
        return ("FontModel : " + this.getText());
    }

    public Font getFont() {
        return (this._font);
    }

    /** return text height in gl coordinate system */
    public float getTextWidth() {
        float maxwidth = 0;
        float width = 0;

        for (int i = 0; i < this.getText().length(); i++) {

            char c = this.getText().charAt(i);

            if (c == '\n') {
                if (maxwidth < width) {
                    maxwidth = width;
                }
                width = 0;
                continue;
            }
            FontChar fchar = this.getFont().getFile().getCharData(c);
            if (fchar == null) {
                continue;
            }
            width += fchar.xadvance + fchar.xoffset;
        }

        return (maxwidth == 0 ? width : maxwidth);
    }

    /** return text height in gl coordinate system */
    public float getTextHeight() {
        int linescount = this.getText().length() - this.getText().replace("\n", "").length() + 1;
        return (linescount * FontFile.LINEHEIGHT);
    }

    public Vector3f getScale() {
        return (this._scale);
    }

    /** request for an update of this font model */
    public void requestUpdate() {
        this.unsetState(FontModel.STATE_TEXT_UP_TO_DATE);
    }

    public Vector4f getFontColor() {
        return (this._color);
    }

    public Vector3f getPosition() {
        return (this._pos);
    }

    public float getX() {
        return (this._pos.x);
    }

    public float getY() {
        return (this._pos.y);
    }

    public float getZ() {
        return (this._pos.z);
    }

    public float getBorderWidth() {
        return (this._border_width);
    }

    public void setBorderWidth(float value) {
        this._border_width = value;
    }

    public float getBorderEdge() {
        return (this._border_edge);
    }

    public void setBorderEdge(float value) {
        this._border_edge = value;
    }

    public Vector2f getOutlineOffset() {
        return (this._outline_offset);
    }

    public void setOutlineOffset(float x, float y) {
        this._outline_offset.set(x, y);
    }

    public Vector3f getOutlineColor() {
        return (this._outline_color);
    }

    public void setOutlineColor(float r, float g, float b) {
        this._outline_color.set(r, g, b);
    }

    /** reset the outline and border parameters to default, to remove any effects */
    public void clearEffects() {
        this.setBorderWidth(0.0f);
        this.setOutlineOffset(0, 0);
        this.setOutlineColor(0, 0, 0);
    }
}