package loon.opengl;
import loon.LRelease;
import loon.LSystem;
import loon.canvas.LColor;
import loon.geom.Affine2f;
import loon.geom.Shape;
import loon.geom.Triangle;
import loon.utils.MathUtils;
public class GLRenderer implements LRelease {
public final static class GLType {
public static final GLType Point = new GLType(GL20.GL_POINTS);
public static final GLType Line = new GLType(GL20.GL_LINES);
public static final GLType Filled = new GLType(GL20.GL_TRIANGLES);
final int glType;
GLType(int glType) {
this.glType = glType;
}
}
private GLBatch _renderer;
private LColor _color = new LColor(1f, 1f, 1f, 1f);
private GLType _currType = null;
private Affine2f _affine;
private GLEx _gl;
public GLRenderer() {
this(null, 3000);
}
public GLRenderer(GLEx gl) {
this(gl, 3000);
}
public GLRenderer(GLEx gl, int maxVertices) {
_renderer = new GLBatch(maxVertices, false, true, 0);
_gl = gl;
}
public void begin(Affine2f affine, GLType type) {
if (_currType != null) {
throw LSystem.runThrow(
"Call end() before beginning a new shape batch !");
}
_currType = type;
_affine = affine;
if (_affine == null) {
_affine = LSystem.base().display().GL().tx();
}
_renderer.begin(_affine, _currType.glType);
}
public void setColor(int argb) {
this._color.setColor(argb);
}
public void setColor(LColor color) {
this._color.setColor(color);
}
public void setColor(float r, float g, float b, float a) {
this._color.setColor(r, g, b, a);
}
public void point(float x, float y) {
point(x, y, 1);
}
public void point(float x, float y, float z) {
if (_currType != GLType.Point) {
throw LSystem.runThrow("Must call begin(GLType.Point)");
}
checkFlush(1);
_renderer.color(_color);
_renderer.vertex(x, y, z);
}
public void line(float x, float y, float z, float x2, float y2, float z2) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
checkFlush(2);
float colorFloat = _color.toFloatBits();
_renderer.color(colorFloat);
_renderer.vertex(x, y, z);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, z2);
}
public void line(float x, float y, float x2, float y2) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
checkFlush(2);
float colorFloat = _color.toFloatBits();
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
}
public void curve(float x1, float y1, float cx1, float cy1, float cx2,
float cy2, float x2, float y2, int segments) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
checkFlush(segments * 2 + 2);
float subdiv_step = 1f / segments;
float subdiv_step2 = subdiv_step * subdiv_step;
float subdiv_step3 = subdiv_step * subdiv_step * subdiv_step;
float pre1 = 3 * subdiv_step;
float pre2 = 3 * subdiv_step2;
float pre4 = 6 * subdiv_step2;
float pre5 = 6 * subdiv_step3;
float tmp1x = x1 - cx1 * 2 + cx2;
float tmp1y = y1 - cy1 * 2 + cy2;
float tmp2x = (cx1 - cx2) * 3 - x1 + x2;
float tmp2y = (cy1 - cy2) * 3 - y1 + y2;
float fx = x1;
float fy = y1;
float dfx = (cx1 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
float dfy = (cy1 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
float ddfx = tmp1x * pre4 + tmp2x * pre5;
float ddfy = tmp1y * pre4 + tmp2y * pre5;
float dddfx = tmp2x * pre5;
float dddfy = tmp2y * pre5;
float colorFloat = _color.toFloatBits();
for (; segments-- > 0;) {
_renderer.color(colorFloat);
_renderer.vertex(fx, fy, 0);
fx += dfx;
fy += dfy;
dfx += ddfx;
dfy += ddfy;
ddfx += dddfx;
ddfy += dddfy;
_renderer.color(colorFloat);
_renderer.vertex(fx, fy, 0);
}
_renderer.color(colorFloat);
_renderer.vertex(fx, fy, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
}
public void triangle(float x1, float y1, float x2, float y2, float x3,
float y3) {
if (_currType != GLType.Filled && _currType != GLType.Line) {
throw LSystem.runThrow(
"Must call begin(GLType.Filled) or begin(GLType.Line)");
}
checkFlush(6);
float colorFloat = _color.toFloatBits();
if (_currType == GLType.Line) {
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
_renderer.color(colorFloat);
_renderer.vertex(x3, y3, 0);
_renderer.color(colorFloat);
_renderer.vertex(x3, y3, 0);
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
} else {
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
_renderer.color(colorFloat);
_renderer.vertex(x3, y3, 0);
}
}
public void rect(float x, float y, float width, float height) {
if (_currType != GLType.Filled && _currType != GLType.Line) {
throw LSystem.runThrow(
"Must call begin(GLType.Filled) or begin(GLType.Line)");
}
checkFlush(8);
float colorFloat = _color.toFloatBits();
if (_currType == GLType.Line) {
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
} else {
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x, y + height, 0);
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
}
}
public void rect(float x, float y, float width, float height, LColor col1,
LColor col2, LColor col3, LColor col4) {
if (_currType != GLType.Filled && _currType != GLType.Line) {
throw LSystem.runThrow(
"Must call begin(GLType.Filled) or begin(GLType.Line)");
}
checkFlush(8);
if (_currType == GLType.Line) {
_renderer.color(col1.r, col1.g, col1.b, col1.a);
_renderer.vertex(x, y, 0);
_renderer.color(col2.r, col2.g, col2.b, col2.a);
_renderer.vertex(x + width, y, 0);
_renderer.color(col2.r, col2.g, col2.b, col2.a);
_renderer.vertex(x + width, y, 0);
_renderer.color(col3.r, col3.g, col3.b, col3.a);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(col3.r, col3.g, col3.b, col3.a);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(col4.r, col4.g, col4.b, col4.a);
_renderer.vertex(x, y + height, 0);
_renderer.color(col4.r, col4.g, col4.b, col4.a);
_renderer.vertex(x, y + height, 0);
_renderer.color(col1.r, col1.g, col1.b, col1.a);
_renderer.vertex(x, y, 0);
} else {
_renderer.color(col1.r, col1.g, col1.b, col1.a);
_renderer.vertex(x, y, 0);
_renderer.color(col2.r, col2.g, col2.b, col2.a);
_renderer.vertex(x + width, y, 0);
_renderer.color(col3.r, col3.g, col3.b, col3.a);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(col3.r, col3.g, col3.b, col3.a);
_renderer.vertex(x + width, y + height, 0);
_renderer.color(col4.r, col4.g, col4.b, col4.a);
_renderer.vertex(x, y + height, 0);
_renderer.color(col1.r, col1.g, col1.b, col1.a);
_renderer.vertex(x, y, 0);
}
}
public void oval(float x, float y, float radius) {
oval(x, y, radius, 32);
}
public void oval(float x, float y, float radius, int segments) {
if (segments <= 0)
throw new IllegalArgumentException("segments must be >= 0.");
if (_currType != GLType.Filled && _currType != GLType.Line)
throw LSystem.runThrow(
"Must call begin(GLType.Filled) or begin(GLType.Line)");
checkFlush(segments * 2 + 2);
float angle = 2 * 3.1415926f / segments;
float cos = MathUtils.cos(angle);
float sin = MathUtils.sin(angle);
float cx = radius, cy = 0;
float colorFloat = _color.toFloatBits();
if (_currType == GLType.Line) {
for (int i = 0; i < segments; i++) {
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
float temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
} else {
segments--;
for (int i = 0; i < segments; i++) {
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
float temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
cx = radius;
cy = 0;
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
public void polygon(float[] vertices) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
if (vertices.length < 6) {
throw new IllegalArgumentException(
"Polygons must contain at least 3 points.");
}
if (vertices.length % 2 != 0) {
throw new IllegalArgumentException(
"Polygons must have a pair number of vertices.");
}
final int numFloats = vertices.length;
float colorFloat = _color.toFloatBits();
checkFlush(numFloats);
float firstX = vertices[0];
float firstY = vertices[1];
for (int i = 0; i < numFloats; i += 2) {
float x1 = vertices[i];
float y1 = vertices[i + 1];
float x2;
float y2;
if (i + 2 >= numFloats) {
x2 = firstX;
y2 = firstY;
} else {
x2 = vertices[i + 2];
y2 = vertices[i + 3];
}
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
}
}
public void polygon(float[] xs, float[] ys, int size) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
if (xs.length < 3 || ys.length < 3) {
throw new IllegalArgumentException(
"Polygons must contain at least 3 points.");
}
if (xs.length % 2 != 0 || ys.length % 2 != 0) {
throw new IllegalArgumentException(
"Polygons must have a pair number of vertices.");
}
final int numFloats = size;
float colorFloat = _color.toFloatBits();
checkFlush(numFloats);
float firstX = xs[0];
float firstY = ys[0];
for (int i = 0; i < numFloats; i++) {
float x1 = xs[i];
float y1 = ys[i];
float x2;
float y2;
if (i + 2 >= numFloats) {
x2 = firstX;
y2 = firstY;
} else {
x2 = xs[i + 2];
y2 = ys[i + 2];
}
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
}
}
public void polyline(float[] vertices) {
polyline(vertices, 0, vertices.length);
}
public void polyline(float[] vertices, int offset, int count) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
if (count < 4) {
if (count == 2) {
line(vertices[0], vertices[1], vertices[0] + 1, vertices[1] + 1);
return;
}
throw new IllegalArgumentException(
"Polylines must contain at least 2 points.");
}
if (count % 2 != 0) {
throw new IllegalArgumentException(
"Polylines must have an even number of vertices.");
}
checkFlush(count);
float colorFloat = _color.toFloatBits();
for (int i = offset, n = offset + count - 2; i < n; i += 2) {
float x1 = vertices[i];
float y1 = vertices[i + 1];
float x2;
float y2;
x2 = vertices[i + 2];
y2 = vertices[i + 3];
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
}
}
public void polyline(float[] xs, float[] ys, int count) {
if (_currType != GLType.Line) {
throw LSystem.runThrow("Must call begin(GLType.Line)");
}
checkFlush(count);
float colorFloat = _color.toFloatBits();
for (int i = 0; i < count; i++) {
float x1 = xs[i];
float y1 = ys[i];
float x2;
float y2;
x2 = xs[i + 2];
y2 = ys[i + 2];
_renderer.color(colorFloat);
_renderer.vertex(x1, y1, 0);
_renderer.color(colorFloat);
_renderer.vertex(x2, y2, 0);
}
}
public void drawShape(Shape shape, float x, float y) {
Triangle tris = shape.getTriangles();
if (tris == null || tris.getTriangleCount() == 0) {
return;
}
float[] points = shape.getPoints();
if (points.length == 0) {
return;
}
float colorFloat = _color.toFloatBits();
for (int i = 0; i < tris.getTriangleCount(); i++) {
for (int p = 0; p < 3; p++) {
float[] pt = tris.getTrianglePoint(i, p);
_renderer.color(colorFloat);
_renderer.vertex(pt[0] + x, pt[1] + y);
}
}
}
public void arc(float x, float y, float radius, float start, float degrees) {
arc(x, y, radius, start, degrees, 32);
}
public void arc(float x, float y, float radius, float start, float end,
int segments) {
if (segments <= 0) {
throw new IllegalArgumentException("segments must be >= 0.");
}
if (_currType != GLType.Filled && _currType != GLType.Line) {
throw LSystem.runThrow(
"Must call begin(GLType.Filled) or begin(GLType.Line)");
}
float arcAngle = end - start;
if (arcAngle < 0) {
start = 360 - arcAngle;
arcAngle = 360 + arcAngle;
}
start %= 360;
if (start < 0) {
start += 360;
}
float theta = (2 * MathUtils.PI * (arcAngle / 360.0f)) / segments;
float cos = MathUtils.cos(theta);
float sin = MathUtils.sin(theta);
float cx = radius * MathUtils.cos(start * MathUtils.DEG_TO_RAD);
float cy = radius * MathUtils.sin(start * MathUtils.DEG_TO_RAD);
float colorFloat = _color.toFloatBits();
if (_currType == GLType.Line) {
checkFlush(segments * 2 + 2);
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
for (int i = 0; i < segments; i++) {
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
float temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
} else {
checkFlush(segments * 3 + 3);
for (int i = 0; i < segments; i++) {
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
float temp = cx;
cx = cos * cx - sin * cy;
cy = sin * temp + cos * cy;
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
_renderer.color(colorFloat);
_renderer.vertex(x, y, 0);
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
cx = 0;
cy = 0;
_renderer.color(colorFloat);
_renderer.vertex(x + cx, y + cy, 0);
}
private void checkFlush(int newVertices) {
if (_renderer.getMaxVertices() - _renderer.getNumVertices() >= newVertices) {
return;
}
GLType type = _currType;
end();
begin(_affine, type);
}
public void end() {
if (_renderer != null) {
LSystem.mainEndDraw();
if (_gl == null) {
_gl = LSystem.base().display().GL();
}
int tmp = _gl.getBlendMode();
if (!LColor.white.equals(_color)) {
_gl.setBlendMode(LSystem.MODE_SPEED);
}
_renderer.end();
_gl.setBlendMode(tmp);
_currType = null;
LSystem.mainBeginDraw();
}
}
public void flush() {
GLType type = _currType;
end();
begin(_affine, type);
}
public GLType getCurrentType() {
return _currType;
}
@Override
public void close() {
if (_renderer != null) {
_renderer.close();
}
}
}