View Javadoc
1   package org.newdawn.slick.opengl.renderer;
2   
3   import java.nio.DoubleBuffer;
4   import java.nio.FloatBuffer;
5   
6   import org.lwjgl.BufferUtils;
7   import org.lwjgl.opengl.GL11;
8   
9   import javax.annotation.Nonnull;
10  
11  /**
12   * A renderer that caches all operations into an array, creates an opengl vertex array when
13   * required and spits the data down to the card in batch mode
14   * 
15   * @author kevin
16   */
17  public class VAOGLRenderer extends ImmediateModeOGLRenderer {
18      /** The tolerance to rendering immediate */
19      private static final int TOLERANCE = 20;
20      /** Indicates there is no current geometry buffer */
21      private static final int NONE = -1;
22      /** The maximum number of vertices draw in one batch */
23      private static final int MAX_VERTS = 5000;
24  
25      /** The type of the geometry array currently being built - i.e. GL_QUADS */
26      private int currentType = NONE;
27      /** The last colour applied */
28      @Nonnull
29      private final float[] color = new float[] {1f,1f,1f,1f};
30      /** The last texture applied */
31      @Nonnull
32      private final float[] tex = new float[] {0f,0f};
33      /** The index of the next vertex to be created */
34      private int vertIndex;
35  
36      /** The vertex data cached */
37      @Nonnull
38      private final float[] verts = new float[MAX_VERTS*3];
39      /** The vertex colour data cached */
40      @Nonnull
41      private final float[] cols = new float[MAX_VERTS*4];
42      /** The vertex texture coordiante data cached */
43      @Nonnull
44      private final float[] texs = new float[MAX_VERTS*3];
45  
46      /** The buffer used to pass the vertex data to the card */
47      private final FloatBuffer vertices = BufferUtils.createFloatBuffer(MAX_VERTS * 3);
48      /** The buffer used to pass the vertex color data to the card */
49      private final FloatBuffer colors = BufferUtils.createFloatBuffer(MAX_VERTS * 4);
50      /** The buffer used to pass the vertex texture coordinate data to the card */
51      private final FloatBuffer textures = BufferUtils.createFloatBuffer(MAX_VERTS * 2);
52  
53      /** The stack for entering list creation mode - when we're creating a list we can't use our VAs */
54      private int listMode = 0;
55  
56      /**
57       * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#initDisplay(int, int)
58       */
59      public void initDisplay(int width, int height) {
60          super.initDisplay(width, height);
61  
62          startBuffer();
63          GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
64          GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
65          GL11.glEnableClientState(GL11.GL_COLOR_ARRAY);
66      }
67  
68      /**
69       * Start a new buffer for a vertex array
70       */
71      private void startBuffer() {
72          vertIndex = 0;
73      }
74  
75      /**
76       * Flush the currently cached data down to the card
77       */
78      private void flushBuffer() {
79          if (vertIndex == 0) {
80              return;
81          }
82          if (currentType == NONE) {
83              return;
84          }
85  
86          if (vertIndex < TOLERANCE) {
87              GL11.glBegin(currentType);
88              for (int i=0;i<vertIndex;i++) {
89                  GL11.glColor4f(cols[(i * 4)], cols[(i*4)+1], cols[(i*4)+2], cols[(i*4)+3]);
90                  GL11.glTexCoord2f(texs[(i * 2)], texs[(i*2)+1]);
91                  GL11.glVertex3f(verts[(i * 3)], verts[(i*3)+1], verts[(i*3)+2]);
92              }
93              GL11.glEnd();
94              currentType = NONE;
95              return;
96          }
97          vertices.clear();
98          colors.clear();
99          textures.clear();
100 
101         vertices.put(verts,0,vertIndex*3);
102         colors.put(cols,0,vertIndex*4);
103         textures.put(texs,0,vertIndex*2);
104 
105         vertices.flip();
106         colors.flip();
107         textures.flip();
108 
109         GL11.glVertexPointer(3,0,vertices);
110         GL11.glColorPointer(4,0,colors);
111         GL11.glTexCoordPointer(2,0,textures);
112 
113         GL11.glDrawArrays(currentType, 0, vertIndex);
114         currentType = NONE;
115     }
116 
117     /**
118      * Apply the current buffer and restart it
119      */
120     private void applyBuffer() {
121         if (listMode > 0) {
122             return;
123         }
124 
125         if (vertIndex != 0) {
126             flushBuffer();
127             startBuffer();
128         }
129 
130         super.glColor4f(color[0], color[1], color[2], color[3]);
131     }
132 
133     /**
134      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#flush()
135      */
136     public void flush() {
137         super.flush();
138 
139         applyBuffer();
140     }
141 
142     /**
143      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glBegin(int)
144      */
145     public void glBegin(int geomType) {
146         if (listMode > 0) {
147             super.glBegin(geomType);
148             return;
149         }
150 
151         if (currentType != geomType) {
152             applyBuffer();
153             currentType = geomType;
154         }
155     }
156 
157     /**
158      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glColor4f(float, float, float, float)
159      */
160     public void glColor4f(float r, float g, float b, float a) {
161         a *= alphaScale;
162 
163         color[0] = r;
164         color[1] = g;
165         color[2] = b;
166         color[3] = a;
167 
168         if (listMode > 0) {
169             super.glColor4f(r,g,b,a);
170         }
171     }
172 
173     /**
174      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glEnd()
175      */
176     public void glEnd() {
177         if (listMode > 0) {
178             super.glEnd();
179         }
180     }
181 
182     /**
183      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glTexCoord2f(float, float)
184      */
185     public void glTexCoord2f(float u, float v) {
186         if (listMode > 0) {
187             super.glTexCoord2f(u,v);
188             return;
189         }
190 
191         tex[0] = u;
192         tex[1] = v;
193     }
194 
195     /**
196      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glVertex2f(float, float)
197      */
198     public void glVertex2f(float x, float y) {
199         if (listMode > 0) {
200             super.glVertex2f(x,y);
201             return;
202         }
203 
204         glVertex3f(x,y,0);
205     }
206 
207     /**
208      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glVertex3f(float, float, float)
209      */
210     public void glVertex3f(float x, float y, float z) {
211         if (listMode > 0) {
212             super.glVertex3f(x,y,z);
213             return;
214         }
215 
216         verts[(vertIndex * 3)] = x;
217         verts[(vertIndex*3)+1] = y;
218         verts[(vertIndex*3)+2] = z;
219         cols[(vertIndex * 4)] = color[0];
220         cols[(vertIndex*4)+1] = color[1];
221         cols[(vertIndex*4)+2] = color[2];
222         cols[(vertIndex*4)+3] = color[3];
223         texs[(vertIndex * 2)] = tex[0];
224         texs[(vertIndex*2)+1] = tex[1];
225         vertIndex++;
226 
227         if (vertIndex > MAX_VERTS - 50) {
228             if (isSplittable(vertIndex, currentType)) {
229                 int type = currentType;
230                 applyBuffer();
231                 currentType = type;
232             }
233         }
234     }
235 
236     /**
237      * Check if the geometry being created can be split at the current index
238      *
239      * @param count The current index
240      * @param type The type of geometry being built
241      * @return True if the geometry can be split at the current index
242      */
243     private boolean isSplittable(int count, int type) {
244         switch (type) {
245         case GL11.GL_QUADS:
246             return count % 4 == 0;
247         case GL11.GL_TRIANGLES:
248             return count % 3 == 0;
249         case GL11.GL_LINE:
250             return count % 2 == 0;
251         }
252 
253         return false;
254     }
255 
256     /**
257      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glBindTexture(int, int)
258      */
259     public void glBindTexture(int target, int id) {
260         applyBuffer();
261         super.glBindTexture(target, id);
262     }
263 
264     /**
265      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glBlendFunc(int, int)
266      */
267     public void glBlendFunc(int src, int dest) {
268         applyBuffer();
269         super.glBlendFunc(src, dest);
270     }
271 
272     /**
273      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glCallList(int)
274      */
275     public void glCallList(int id) {
276         applyBuffer();
277         super.glCallList(id);
278     }
279 
280     /**
281      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glClear(int)
282      */
283     public void glClear(int value) {
284         applyBuffer();
285         super.glClear(value);
286     }
287 
288     /**
289      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glClipPlane(int, java.nio.DoubleBuffer)
290      */
291     public void glClipPlane(int plane, DoubleBuffer buffer) {
292         applyBuffer();
293         super.glClipPlane(plane, buffer);
294     }
295 
296     /**
297      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glColorMask(boolean, boolean, boolean, boolean)
298      */
299     public void glColorMask(boolean red, boolean green, boolean blue, boolean alpha) {
300         applyBuffer();
301         super.glColorMask(red, green, blue, alpha);
302     }
303 
304     /**
305      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glDisable(int)
306      */
307     public void glDisable(int item) {
308         applyBuffer();
309         super.glDisable(item);
310     }
311 
312     /**
313      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glEnable(int)
314      */
315     public void glEnable(int item) {
316         applyBuffer();
317         super.glEnable(item);
318     }
319 
320     /**
321      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glLineWidth(float)
322      */
323     public void glLineWidth(float width) {
324         applyBuffer();
325         super.glLineWidth(width);
326     }
327 
328     /**
329      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glPointSize(float)
330      */
331     public void glPointSize(float size) {
332         applyBuffer();
333         super.glPointSize(size);
334     }
335 
336     /**
337      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glPopMatrix()
338      */
339     public void glPopMatrix() {
340         applyBuffer();
341         super.glPopMatrix();
342     }
343 
344     /**
345      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glPushMatrix()
346      */
347     public void glPushMatrix() {
348         applyBuffer();
349         super.glPushMatrix();
350     }
351 
352     /**
353      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glRotatef(float, float, float, float)
354      */
355     public void glRotatef(float angle, float x, float y, float z) {
356         applyBuffer();
357         super.glRotatef(angle, x, y, z);
358     }
359 
360     /**
361      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glScalef(float, float, float)
362      */
363     public void glScalef(float x, float y, float z) {
364         applyBuffer();
365         super.glScalef(x, y, z);
366     }
367 
368     /**
369      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glScissor(int, int, int, int)
370      */
371     public void glScissor(int x, int y, int width, int height) {
372         applyBuffer();
373         super.glScissor(x, y, width, height);
374     }
375 
376     /**
377      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glTexEnvi(int, int, int)
378      */
379     public void glTexEnvi(int target, int mode, int value) {
380         applyBuffer();
381         super.glTexEnvi(target, mode, value);
382     }
383 
384     /**
385      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glTranslatef(float, float, float)
386      */
387     public void glTranslatef(float x, float y, float z) {
388         applyBuffer();
389         super.glTranslatef(x, y, z);
390     }
391 
392     /**
393      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glEndList()
394      */
395     public void glEndList() {
396         listMode--;
397         super.glEndList();
398     }
399 
400     /**
401      * @see org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer#glNewList(int, int)
402      */
403     public void glNewList(int id, int option) {
404         listMode++;
405         super.glNewList(id, option);
406     }
407 
408     /**
409      * @see org.newdawn.slick.opengl.renderer.SGL#getCurrentColor()
410      */
411     @Nonnull
412     public float[] getCurrentColor() {
413         return color;
414     }
415 
416     /**
417      * @see org.newdawn.slick.opengl.renderer.SGL#glLoadMatrix(java.nio.FloatBuffer)
418      */
419     public void glLoadMatrix(FloatBuffer buffer) {
420         flushBuffer();
421         super.glLoadMatrix(buffer);
422     }
423 }