package org.newdawn.slick.opengl.renderer;
import org.lwjgl.opengl.GL11;
/**
* A line strip renderer that uses quads to generate lines
*
* @author kevin
*/
public class QuadBasedLineStripRenderer implements LineStripRenderer {
/** The renderer used to interact with GL */
private static SGL GL = Renderer.get();
/** Maximum number of points allowed in a single strip */
public static int MAX_POINTS = 10000;
/** True if antialiasing is currently enabled */
private boolean antialias;
/** The width of the lines to draw */
private float width = 1;
/** The points to draw */
private float[] points;
/** The colours to draw */
private float[] colours;
/** The number of points to draw */
private int pts;
/** The number of colour points recorded */
private int cpt;
/** The default renderer used when width = 1 */
private DefaultLineStripRenderer def = new DefaultLineStripRenderer();
/** Indicates need to render half colour */
private boolean renderHalf;
/** True if we shoudl render end caps */
private boolean lineCaps = false;
/**
* Create a new strip renderer
*/
public QuadBasedLineStripRenderer() {
points = new float[MAX_POINTS * 2];
colours = new float[MAX_POINTS * 4];
}
/**
* Indicate if we should render end caps
*
* @param caps True if we should render end caps
*/
public void setLineCaps(boolean caps) {
this.lineCaps = caps;
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#start()
*/
public void start() {
if (width == 1) {
def.start();
return;
}
pts = 0;
cpt = 0;
GL.flush();
float[] col = GL.getCurrentColor();
color(col[0],col[1],col[2],col[3]);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#end()
*/
public void end() {
if (width == 1) {
def.end();
return;
}
renderLines(points, pts);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#vertex(float, float)
*/
public void vertex(float x, float y) {
if (width == 1) {
def.vertex(x,y);
return;
}
points[(pts*2)] = x;
points[(pts*2)+1] = y;
pts++;
int index = pts-1;
color(colours[(index*4)], colours[(index*4)+1], colours[(index*4)+2], colours[(index*4)+3]);
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setWidth(float)
*/
public void setWidth(float width) {
this.width = width;
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setAntiAlias(boolean)
*/
public void setAntiAlias(boolean antialias) {
def.setAntiAlias(antialias);
this.antialias = antialias;
}
/**
* Render the lines applying antialiasing if required
*
* @param points The points to be rendered as lines
* @param count The number of points to render
*/
public void renderLines(float[] points, int count) {
if (antialias) {
GL.glEnable(SGL.GL_POLYGON_SMOOTH);
renderLinesImpl(points,count,width+1f);
}
GL.glDisable(SGL.GL_POLYGON_SMOOTH);
renderLinesImpl(points,count,width);
if (antialias) {
GL.glEnable(SGL.GL_POLYGON_SMOOTH);
}
}
/**
* Render the lines given
*
* @param points The points building up the lines
* @param count The number of points to render
* @param w The width to render at
*/
public void renderLinesImpl(float[] points, int count, float w) {
float width = w / 2;
float lastx1 = 0;
float lasty1 = 0;
float lastx2 = 0;
float lasty2 = 0;
GL.glBegin(GL11.GL_QUADS);
for (int i=0;i<count+1;i++) {
int current = i;
int next = i+1;
int prev = i-1;
if (prev < 0) {
prev += count;
}
if (next >= count) {
next -= count;
}
if (current >= count) {
current -= count;
}
float x1 = points[(current*2)];
float y1 = points[(current*2)+1];
float x2 = points[(next*2)];
float y2 = points[(next*2)+1];
// draw the next segment
float dx = x2 - x1;
float dy = y2 - y1;
if ((dx == 0) && (dy == 0)) {
continue;
}
float d2 = (dx*dx)+(dy*dy);
float d = (float) Math.sqrt(d2);
dx *= width;
dy *= width;
dx /= d;
dy /= d;
float tx = dy;
float ty = -dx;
if (i != 0) {
bindColor(prev);
GL.glVertex3f(lastx1,lasty1,0);
GL.glVertex3f(lastx2,lasty2,0);
bindColor(current);
GL.glVertex3f(x1+tx,y1+ty,0);
GL.glVertex3f(x1-tx,y1-ty,0);
}
lastx1 = x2-tx;
lasty1 = y2-ty;
lastx2 = x2+tx;
lasty2 = y2+ty;
if (i < count-1) {
bindColor(current);
GL.glVertex3f(x1+tx,y1+ty,0);
GL.glVertex3f(x1-tx,y1-ty,0);
bindColor(next);
GL.glVertex3f(x2-tx,y2-ty,0);
GL.glVertex3f(x2+tx,y2+ty,0);
}
}
GL.glEnd();
float step = width <= 12.5f ? 5 : 180 / (float)Math.ceil(width / 2.5);
// start cap
if (lineCaps) {
float dx = points[2] - points[0];
float dy = points[3] - points[1];
float fang = (float) Math.toDegrees(Math.atan2(dy,dx)) + 90;
if ((dx != 0) || (dy != 0)) {
GL.glBegin(GL11.GL_TRIANGLE_FAN);
bindColor(0);
GL.glVertex2f(points[0], points[1]);
for (int i=0;i<180+step;i+=step) {
float ang = (float) Math.toRadians(fang+i);
GL.glVertex2f(points[0]+((float) (Math.cos(ang) * width)),
points[1]+((float) (Math.sin(ang) * width)));
}
GL.glEnd();
}
}
// end cap
if (lineCaps) {
float dx = points[(count*2)-2] - points[(count*2)-4];
float dy = points[(count*2)-1] - points[(count*2)-3];
float fang = (float) Math.toDegrees(Math.atan2(dy,dx)) - 90;
if ((dx != 0) || (dy != 0)) {
GL.glBegin(GL11.GL_TRIANGLE_FAN);
bindColor(count-1);
GL.glVertex2f(points[(count*2)-2], points[(count*2)-1]);
for (int i=0;i<180+step;i+=step) {
float ang = (float) Math.toRadians(fang+i);
GL.glVertex2f(points[(count*2)-2]+((float) (Math.cos(ang) * width)),
points[(count*2)-1]+((float) (Math.sin(ang) * width)));
}
GL.glEnd();
}
}
}
/**
* Bind the colour at a given index in the array
*
* @param index The index of the colour to bind
*/
private void bindColor(int index) {
if (index < cpt) {
if (renderHalf) {
GL.glColor4f(colours[(index*4)]*0.5f, colours[(index*4)+1]*0.5f,
colours[(index*4)+2]*0.5f, colours[(index*4)+3]*0.5f);
} else {
GL.glColor4f(colours[(index*4)], colours[(index*4)+1],
colours[(index*4)+2], colours[(index*4)+3]);
}
}
}
/**
* @see org.newdawn.slick.opengl.renderer.LineStripRenderer#color(float, float, float, float)
*/
public void color(float r, float g, float b, float a) {
if (width == 1) {
def.color(r,g,b,a);
return;
}
colours[(pts*4)] = r;
colours[(pts*4)+1] = g;
colours[(pts*4)+2] = b;
colours[(pts*4)+3] = a;
cpt++;
}
public boolean applyGLLineFixes() {
if (width == 1) {
return def.applyGLLineFixes();
}
return def.applyGLLineFixes();
}
}