View Javadoc
1   package org.newdawn.slick.opengl.renderer;
2   
3   import javax.annotation.Nonnull;
4   
5   /**
6    * A line strip renderer that uses quads to generate lines
7    * 
8    * @author kevin
9    */
10  public class QuadBasedLineStripRenderer implements LineStripRenderer {
11      /** The renderer used to interact with GL */
12      private final SGL GL = Renderer.get();
13  
14      /** Maximum number of points allowed in a single strip */
15      private static final int MAX_POINTS = 10000;
16      /** True if antialiasing is currently enabled */
17      private boolean antialias;
18      /** The width of the lines to draw */
19      private float width = 1;
20      /** The points to draw */
21      private final float[] points;
22      /** The colours to draw */
23      private final float[] colours;
24      /** The number of points to draw */
25      private int pts;
26      /** The number of colour points recorded */
27      private int cpt;
28  
29      /** The default renderer used when width = 1 */
30      @Nonnull
31      private final DefaultLineStripRenderer def = new DefaultLineStripRenderer();
32      /** Indicates need to render half colour */
33      private boolean renderHalf;
34  
35      /** True if we shoudl render end caps */
36      private boolean lineCaps = false;
37  
38      /**
39       * Create a new strip renderer
40       */
41      public QuadBasedLineStripRenderer() {
42          points = new float[MAX_POINTS * 2];
43          colours = new float[MAX_POINTS * 4];
44      }
45  
46      /**
47       * Indicate if we should render end caps
48       *
49       * @param caps True if we should render end caps
50       */
51      public void setLineCaps(boolean caps) {
52          this.lineCaps = caps;
53      }
54  
55      /**
56       * @see org.newdawn.slick.opengl.renderer.LineStripRenderer#start()
57       */
58      public void start() {
59          if (width == 1) {
60              def.start();
61              return;
62          }
63  
64          pts = 0;
65          cpt = 0;
66          GL.flush();
67  
68          float[] col = GL.getCurrentColor();
69          color(col[0],col[1],col[2],col[3]);
70      }
71  
72      /**
73       * @see org.newdawn.slick.opengl.renderer.LineStripRenderer#end()
74       */
75      public void end() {
76          if (width == 1) {
77              def.end();
78              return;
79          }
80  
81          renderLines(points, pts);
82      }
83  
84      /**
85       * @see org.newdawn.slick.opengl.renderer.LineStripRenderer#vertex(float, float)
86       */
87      public void vertex(float x, float y) {
88          if (width == 1) {
89              def.vertex(x,y);
90              return;
91          }
92  
93          points[(pts*2)] = x;
94          points[(pts*2)+1] = y;
95          pts++;
96  
97          int index = pts-1;
98          color(colours[(index*4)], colours[(index*4)+1], colours[(index*4)+2], colours[(index*4)+3]);
99      }
100 
101     /**
102      * @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setWidth(float)
103      */
104     public void setWidth(float width) {
105         this.width = width;
106     }
107 
108     /**
109      * @see org.newdawn.slick.opengl.renderer.LineStripRenderer#setAntiAlias(boolean)
110      */
111     public void setAntiAlias(boolean antialias) {
112         def.setAntiAlias(antialias);
113         this.antialias = antialias;
114     }
115 
116     /**
117      * Render the lines applying antialiasing if required
118      *
119      * @param points The points to be rendered as lines
120      * @param count The number of points to render
121      */
122     void renderLines(float[] points, int count) {
123         if (antialias) {
124             GL.glEnable(SGL.GL_POLYGON_SMOOTH);
125             renderLinesImpl(points,count,width+1f);
126         }
127 
128         GL.glDisable(SGL.GL_POLYGON_SMOOTH);
129         renderLinesImpl(points,count,width);
130 
131         if (antialias) {
132             GL.glEnable(SGL.GL_POLYGON_SMOOTH);
133         }
134     }
135 
136     /**
137      * Render the lines given
138      *
139      * @param points The points building up the lines
140      * @param count The number of points to render
141      * @param w The width to render at
142      */
143     void renderLinesImpl(float[] points, int count, float w) {
144         float width = w / 2;
145 
146         float lastx1 = 0;
147         float lasty1 = 0;
148         float lastx2 = 0;
149         float lasty2 = 0;
150 
151         GL.glBegin(SGL.GL_QUADS);
152         for (int i=0;i<count+1;i++) {
153             int current = i;
154             int next = i+1;
155             int prev = i-1;
156             if (prev < 0) {
157                 prev += count;
158             }
159             if (next >= count) {
160                 next -= count;
161             }
162             if (current >= count) {
163                 current -= count;
164             }
165 
166             float x1 = points[(current*2)];
167             float y1 = points[(current*2)+1];
168             float x2 = points[(next*2)];
169             float y2 = points[(next*2)+1];
170 
171             // draw the next segment
172             float dx = x2 - x1;
173             float dy = y2 - y1;
174 
175             if ((dx == 0) && (dy == 0)) {
176                 continue;
177             }
178 
179             float d2 = (dx*dx)+(dy*dy);
180             float d = (float) Math.sqrt(d2);
181             dx *= width;
182             dy *= width;
183             dx /= d;
184             dy /= d;
185 
186             float tx = dy;
187             float ty = -dx;
188 
189             if (i != 0) {
190                 bindColor(prev);
191                 GL.glVertex3f(lastx1,lasty1,0);
192                 GL.glVertex3f(lastx2,lasty2,0);
193                 bindColor(current);
194                 GL.glVertex3f(x1+tx,y1+ty,0);
195                 GL.glVertex3f(x1-tx,y1-ty,0);
196             }
197 
198             lastx1 = x2-tx;
199             lasty1 = y2-ty;
200             lastx2 = x2+tx;
201             lasty2 = y2+ty;
202 
203             if (i < count-1) {
204                 bindColor(current);
205                 GL.glVertex3f(x1+tx,y1+ty,0);
206                 GL.glVertex3f(x1-tx,y1-ty,0);
207                 bindColor(next);
208                 GL.glVertex3f(x2-tx,y2-ty,0);
209                 GL.glVertex3f(x2+tx,y2+ty,0);
210             }
211         }
212 
213         GL.glEnd();
214 
215         float step = width <= 12.5f ?  5 : 180 / (float)Math.ceil(width / 2.5);
216 
217         // start cap
218         if (lineCaps) {
219             float dx = points[2] - points[0];
220             float dy = points[3] - points[1];
221             float fang = (float) Math.toDegrees(Math.atan2(dy,dx)) + 90;
222 
223             if ((dx != 0) || (dy != 0)) {
224                 GL.glBegin(SGL.GL_TRIANGLE_FAN);
225                 bindColor(0);
226                 GL.glVertex2f(points[0], points[1]);
227                 for (int i=0;i<180+step;i+=step) {
228                     float ang = (float) Math.toRadians(fang+i);
229                     GL.glVertex2f(points[0]+((float) (Math.cos(ang) * width)),
230                                   points[1]+((float) (Math.sin(ang) * width)));
231                 }
232                 GL.glEnd();
233             }
234         }
235 
236         // end cap
237         if (lineCaps) {
238             float dx = points[(count*2)-2] - points[(count*2)-4];
239             float dy = points[(count*2)-1] - points[(count*2)-3];
240             float fang = (float) Math.toDegrees(Math.atan2(dy,dx)) - 90;
241 
242             if ((dx != 0) || (dy != 0)) {
243                 GL.glBegin(SGL.GL_TRIANGLE_FAN);
244                 bindColor(count-1);
245                 GL.glVertex2f(points[(count*2)-2], points[(count*2)-1]);
246                 for (int i=0;i<180+step;i+=step) {
247                     float ang = (float) Math.toRadians(fang+i);
248                     GL.glVertex2f(points[(count*2)-2]+((float) (Math.cos(ang) * width)),
249                                   points[(count*2)-1]+((float) (Math.sin(ang) * width)));
250                 }
251                 GL.glEnd();
252             }
253         }
254     }
255 
256     /**
257      * Bind the colour at a given index in the array
258      *
259      * @param index The index of the colour to bind
260      */
261     private void bindColor(int index) {
262         if (index < cpt) {
263             if (renderHalf) {
264                 GL.glColor4f(colours[(index*4)]*0.5f, colours[(index*4)+1]*0.5f,
265                               colours[(index*4)+2]*0.5f, colours[(index*4)+3]*0.5f);
266             } else {
267                 GL.glColor4f(colours[(index*4)], colours[(index*4)+1],
268                              colours[(index*4)+2], colours[(index*4)+3]);
269             }
270         }
271     }
272 
273     /**
274      * @see org.newdawn.slick.opengl.renderer.LineStripRenderer#color(float, float, float, float)
275      */
276     public void color(float r, float g, float b, float a) {
277         if (width == 1) {
278             def.color(r,g,b,a);
279             return;
280         }
281 
282         colours[(pts*4)] = r;
283         colours[(pts*4)+1] = g;
284         colours[(pts*4)+2] = b;
285         colours[(pts*4)+3] = a;
286         cpt++;
287     }
288 
289     public boolean applyGLLineFixes() {
290         if (width == 1) {
291             return def.applyGLLineFixes();
292         }
293 
294         return def.applyGLLineFixes();
295     }
296 }