package com.igorcrevar.rolloverchuck.utils;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Mesh;
import com.badlogic.gdx.graphics.VertexAttribute;
import com.badlogic.gdx.graphics.VertexAttributes.Usage;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Disposable;
/// Does not work with wrapped textures!
public class MyFontDrawer implements Disposable {
private String value;
private Mesh mesh;
private float partWidth;
private float partHeight;
private float letterPadding;
private float cellPadding;
private int visibleRectCnt = -1;
private Matrix4 viewModelMatrix = new Matrix4();
private Matrix4 rotTmpMatrix = new Matrix4();
private float xPosition;
private float yPosition;
private float width;
private float height;
private boolean isEnabled;
private Vector2 uvMin = new Vector2(0f, 0f);
private Vector2 uvMax = new Vector2(1f, 1f);
private float uCellLen;
private float vCellLen;
public MyFontDrawer(String value,
float width, float height, float letterPadding, float cellPadding) {
this(value, width, height, letterPadding, cellPadding, 1f, 1f);
}
public MyFontDrawer(String value,
float width, float height, float letterPadding, float cellPadding, float uCellLen, float vCellLen) {
this.value = value;
this.partWidth= width;
this.partHeight = height;
this.letterPadding = letterPadding;
this.cellPadding = cellPadding;
setUVCelLen(uCellLen, vCellLen);
this.xPosition = 0f;
this.yPosition = 0f;
this.isEnabled = true;
}
public MyFontDrawer(String value,
float width, float height, float letterPadding, float cellPadding, TextureRegion tr, float xPosition, float yPosition, float uCellLen, float vCellLen) {
this(value, width, height, letterPadding, cellPadding, uCellLen, vCellLen);
setUVMinMax(tr);
this.xPosition = xPosition;
this.yPosition = yPosition;
}
public MyFontDrawer init(IMyFontDrawerFont font) {
if (Float.isNaN(uCellLen)) {
uCellLen = 1.0f / font.getCharWidth();
}
if (Float.isNaN(vCellLen)) {
vCellLen = 1.0f / font.getCharHeight();
}
float minX = Float.MAX_VALUE;
float maxX = Float.MIN_VALUE;
float minY = Float.MAX_VALUE;
float maxY = Float.MIN_VALUE;
this.calculateVisibleRectangles(font);
mesh = new Mesh(true, getVertexCount(), getIndicesCount(),
new VertexAttribute(Usage.Position, 2, ShaderProgram.POSITION_ATTRIBUTE),
new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE + "0"));
int vertexCount = getVertexCount();
int indicesCount = getIndicesCount();
short vIndex = 0;
short iIndex = 0;
float[] vertices = new float[vertexCount * 4];
short[] indices = new short[indicesCount];
byte numberOfSpaces = 0;
float x = 0, y = 0;
for (int i = 0; i < value.length(); ++i) {
char c = value.charAt(i);
if (c == ' ') {
++numberOfSpaces;
continue;
}
for (int row = 0; row < font.getCharHeight(); ++row) {
for (int col = 0; col < font.getCharWidth(); ++col) {
if (font.isSet(c, row, col)) {
x = xPosition;
if (col > 0) {
x += (cellPadding + partWidth) * col;
}
if (i > 0) {
x += (letterPadding + (font.getCharWidth() - 1) * (cellPadding + partWidth) + partWidth) * (i - numberOfSpaces);
x += numberOfSpaces * partWidth * 2;
}
y = yPosition;
if (row > 0) {
y -= (cellPadding + partHeight) * row;
}
float uStart = col * uCellLen;
float uEnd = uStart + uCellLen;
if (uEnd > 1.0f) {
uEnd = 1.0f;
uStart = 1.0f - uCellLen;
}
float vStart = row * vCellLen;
float vEnd = vStart + vCellLen;
if (vEnd > 1.0f) {
vEnd = 1.0f;
vStart = 1.0f - vCellLen;
}
short vIndexReal = (short)(4 * vIndex);
vertices[vIndexReal++] = x;
vertices[vIndexReal++] = y;
vertices[vIndexReal++] = uStart;
vertices[vIndexReal++] = vStart;
vertices[vIndexReal++] = x + partWidth;
vertices[vIndexReal++] = y;
vertices[vIndexReal++] = uEnd;
vertices[vIndexReal++] = vStart;
vertices[vIndexReal++] = x + partWidth;
vertices[vIndexReal++] = y - partHeight;
vertices[vIndexReal++] = uEnd;
vertices[vIndexReal++] = vEnd;
vertices[vIndexReal++] = x;
vertices[vIndexReal++] = y - partHeight;
vertices[vIndexReal++] = uStart;
vertices[vIndexReal++] = vEnd;
indices[iIndex] = vIndex;
indices[iIndex + 1] = (short)(vIndex + 1);
indices[iIndex + 2] = (short)(vIndex + 2);
indices[iIndex + 3] = (short)(vIndex);
indices[iIndex + 4] = (short)(vIndex + 2);
indices[iIndex + 5] = (short)(vIndex + 3);
vIndex += 4;
iIndex += 6;
minX = Math.min(x, minX);
maxX = Math.max(x + partWidth, maxX);
minY = Math.min(y - partHeight, minY);
maxY = Math.max(y, maxY);
}
}
}
}
if (minX == Float.MAX_VALUE) {
width = height = 0.0f;
}
else {
width = Math.abs(maxX - minX);
height = Math.abs(maxY - minY);
}
// set vertices and indexes
mesh.setIndices(indices);
mesh.setVertices(vertices);
return this;
}
public float getWidth() {
return width;
}
public float getHeight() {
return height;
}
public String getValue() {
return value;
}
public MyFontDrawer idt() {
viewModelMatrix.idt();
return this;
}
public MyFontDrawer translate(float x, float y, float z) {
viewModelMatrix.translate(x, y, z);
return this;
}
public MyFontDrawer scale(float x, float y, float z) {
viewModelMatrix.scale(x, y, z);
return this;
}
/**
* Rotate text around desired axis for desired angle in degrees
* @param x axis of rotation x coord
* @param y axis of rotation Y coord
* @param z axis of rotation Z coord
* @param degrees angle in degrees
* @return this object
*/
public MyFontDrawer rotateAround(float x, float y, float z, float degrees) {
rotTmpMatrix.setToRotation(x, y, z, degrees);
viewModelMatrix.mul(rotTmpMatrix);
return this;
}
public void draw(ShaderProgram sp) {
sp.setUniformf("u_uvMin", this.uvMin);
sp.setUniformf("u_uvMax", this.uvMax);
mesh.render(sp, GL20.GL_TRIANGLES);
}
private int getVertexCount() {
return visibleRectCnt * 4;
}
private int getIndicesCount() {
return visibleRectCnt * 6;
}
private void calculateVisibleRectangles(IMyFontDrawerFont font) {
visibleRectCnt = 0;
for (int i = 0; i < value.length(); ++i) {
char c = value.charAt(i);
for (int j = 0; j < font.getCharHeight(); ++j) {
for (int k = 0; k < font.getCharWidth(); ++k) {
if (font.isSet(c, j, k)) {
++visibleRectCnt;
}
}
}
}
}
public void dispose() {
mesh.dispose();
}
public Matrix4 getViewModelMatrix() {
return viewModelMatrix;
}
public void setIsEnabled(boolean isEnabled) {
this.isEnabled = isEnabled;
}
public boolean getIsEnabled() {
return this.isEnabled;
}
public MyFontDrawer setUVMinMax(float u1, float v1, float u2, float v2) {
uvMin.set(u1, v1);
uvMax.set(u2, v2);
return this;
}
public MyFontDrawer setUVMinMax(TextureRegion tr) {
uvMin.set(tr.getU(), tr.getV());
uvMax.set(tr.getU2(), tr.getV2());
return this;
}
public MyFontDrawer setUVCelLen(float uCellLen, float vCellLen) {
this.uCellLen = uCellLen;
this.vCellLen = vCellLen;
if (uCellLen < 0.01f || uCellLen > 1.0f || vCellLen < 0.01f || vCellLen > 1.0f) {
throw new IllegalArgumentException("MyFontDrawer");
}
return this;
}
public void setUVMinMaxScrollU(TextureRegion tr, float uPos, float uScroll) {
setUVMinMax(tr);
float dist = uvMax.x - uvMin.x;
float newMin = uPos * dist + uvMin.x;
float newMax = uScroll * dist + newMin;
uvMin.x = newMin;
uvMax.x = newMax;
}
public void setUVMinMaxScrollV(TextureRegion tr, float vPos, float vScroll) {
setUVMinMax(tr);
float dist = uvMax.y - uvMin.y;
float newMin = vPos * dist + uvMin.y;
float newMax = vScroll * dist + newMin;
uvMin.y = newMin;
uvMax.y = newMax;
}
}