View Javadoc
1   package org.newdawn.slick.geom;
2   
3   import org.newdawn.slick.Image;
4   import org.newdawn.slick.ShapeFill;
5   import org.newdawn.slick.opengl.Texture;
6   import org.newdawn.slick.opengl.TextureImpl;
7   import org.newdawn.slick.opengl.renderer.LineStripRenderer;
8   import org.newdawn.slick.opengl.renderer.Renderer;
9   import org.newdawn.slick.opengl.renderer.SGL;
10  
11  import javax.annotation.Nonnull;
12  import javax.annotation.Nullable;
13  
14  /**
15   * @author Mark Bernard
16   *
17   * Use this class to render shpaes directly to OpenGL.  Allows you to bypass the Graphics class.
18   */
19  public final class ShapeRenderer {
20      /** The renderer to use for all GL operations */
21      private static final SGL GL = Renderer.get();
22      /** The renderer to use line strips */
23      private static final LineStripRenderer LSR = Renderer.getLineStripRenderer();
24  
25      /**
26       * Draw the outline of the given shape.  Only the vertices are set.  
27       * The colour has to be set independently of this method.
28       * 
29       * @param shape The shape to draw.
30       */
31      public static final void draw(@Nonnull Shape shape) {
32          Texture t = TextureImpl.getLastBind();
33          TextureImpl.bindNone();
34          
35          float points[] = shape.getPoints();
36          
37          LSR.start();
38          for(int i=0;i<points.length;i+=2) {
39              LSR.vertex(points[i], points[i + 1]);
40          }
41          
42          if (shape.closed()) {
43              LSR.vertex(points[0], points[1]);
44          }
45          
46          LSR.end();
47          
48          if (t == null) {
49              TextureImpl.bindNone();
50          } else {
51              t.bind();
52          }
53      }
54  
55      /**
56       * Draw the outline of the given shape.  Only the vertices are set.  
57       * The colour has to be set independently of this method.
58       * 
59       * @param shape The shape to draw.
60       * @param fill The fill to apply
61       */
62      public static final void draw(@Nonnull Shape shape, @Nonnull ShapeFill fill) {
63          float points[] = shape.getPoints();
64          
65          Texture t = TextureImpl.getLastBind();
66          TextureImpl.bindNone();
67  
68          shape.getCenter();
69          GL.glBegin(SGL.GL_LINE_STRIP);
70          for(int i=0;i<points.length;i+=2) {
71              fill.colorAt(shape, points[i], points[i + 1]).bind();
72              Vector2f offset = fill.getOffsetAt(shape, points[i], points[i + 1]);
73              GL.glVertex2f(points[i] + offset.x, points[i + 1] + offset.y);
74          }
75          
76          if (shape.closed()) {
77              fill.colorAt(shape, points[0], points[1]).bind();
78              Vector2f offset = fill.getOffsetAt(shape, points[0], points[1]);
79              GL.glVertex2f(points[0] + offset.x, points[1] + offset.y);
80          }
81          GL.glEnd();
82          
83          if (t == null) {
84              TextureImpl.bindNone();
85          } else {
86              t.bind();
87          }
88      }
89      
90      /**
91       * Check there are enough points to fill
92       * 
93       * @param shape THe shape we're drawing
94       * @return True if the fill is valid
95       */
96      private static boolean validFill(@Nonnull Shape shape) {
97          if (shape.getTriangles() == null) {
98              return false;
99          }
100         return shape.getTriangles().getTriangleCount() != 0;
101     }
102 
103     /**
104      * Draw the the given shape filled in.  Only the vertices are set.  
105      * The colour has to be set independently of this method.
106      * 
107      * @param shape The shape to fill.
108      */
109     public static final void fill(@Nonnull Shape shape) {
110         if (!validFill(shape)) {
111             return;
112         }
113 
114         Texture t = TextureImpl.getLastBind();
115         TextureImpl.bindNone();
116         
117         fill(shape, (shape1, x, y) -> null);
118         
119         if (t == null) {
120             TextureImpl.bindNone();
121         } else {
122             t.bind();
123         }
124     }
125     
126     /**
127      * Draw the the given shape filled in.  Only the vertices are set.  
128      * The colour has to be set independently of this method.
129      * 
130      * @param shape The shape to fill.
131      * @param callback The callback that will be invoked for each shape point
132      */
133     private static final void fill(@Nonnull Shape shape, @Nonnull PointCallback callback) {
134         Triangulator tris = shape.getTriangles();
135 
136         GL.glBegin(SGL.GL_TRIANGLES);
137         for (int i=0;i<tris.getTriangleCount();i++) {
138             for (int p=0;p<3;p++) {
139                 float[] pt = tris.getTrianglePoint(i, p);
140                 float[] np = callback.preRenderPoint(shape, pt[0],pt[1]);
141 
142                 if (np == null) {
143                     GL.glVertex2f(pt[0],pt[1]);
144                 } else {
145                     GL.glVertex2f(np[0],np[1]);
146                 }
147             }
148         }
149         GL.glEnd();
150     }
151 
152     /**
153      * Draw the the given shape filled in with a texture.  Only the vertices are set.  
154      * The colour has to be set independently of this method.
155      * 
156      * @param shape The shape to texture.
157      * @param image The image to tile across the shape
158      */
159     public static final void texture(@Nonnull Shape shape, @Nonnull Image image) {
160         texture(shape, image, 0.01f, 0.01f);
161     }
162 
163     /**
164      * Draw the the given shape filled in with a texture.  Only the vertices are set.  
165      * The colour has to be set independently of this method. This method is required to 
166      * fit the texture once across the shape.
167      * 
168      * @param shape The shape to texture.
169      * @param image The image to tile across the shape
170      */
171     public static final void textureFit(@Nonnull Shape shape, @Nonnull Image image) {
172         textureFit(shape, image,1f,1f);
173     }
174     
175     /**
176      * Draw the the given shape filled in with a texture.  Only the vertices are set.  
177      * The colour has to be set independently of this method.
178      * 
179      * @param shape The shape to texture.
180      * @param image The image to tile across the shape
181      * @param scaleX The scale to apply on the x axis for texturing
182      * @param scaleY The scale to apply on the y axis for texturing
183      */
184     public static final void texture(@Nonnull Shape shape, @Nonnull final Image image, final float scaleX, final float scaleY) {
185         if (!validFill(shape)) {
186             return;
187         }
188 
189         final Texture t = TextureImpl.getLastBind();
190         image.getTexture().bind();
191         
192         fill(shape, (shape1, x, y) -> {
193             float tx = x * scaleX;
194             float ty = y * scaleY;
195 
196             tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx);
197             ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty);
198 
199             GL.glTexCoord2f(tx, ty);
200             return null;
201         });
202 
203         shape.getPoints();
204         
205         if (t == null) {
206             TextureImpl.bindNone();
207         } else {
208             t.bind();
209         }
210     }
211     
212     /**
213      * Draw the the given shape filled in with a texture.  Only the vertices are set.  
214      * The colour has to be set independently of this method. This method is required to 
215      * fit the texture scaleX times across the shape and scaleY times down the shape.
216      * 
217      * @param shape The shape to texture.
218      * @param image The image to tile across the shape
219      * @param scaleX The scale to apply on the x axis for texturing
220      * @param scaleY The scale to apply on the y axis for texturing
221      */
222     public static final void textureFit(@Nonnull Shape shape, @Nonnull final Image image, final float scaleX, final float scaleY) {
223         if (!validFill(shape)) {
224             return;
225         }
226 
227         shape.getPoints();
228         
229         Texture t = TextureImpl.getLastBind();
230         image.getTexture().bind();
231         
232         shape.getX();
233         shape.getY();
234         shape.getMaxX();
235         shape.getMaxY();
236 
237         fill(shape, (shape1, x, y) -> {
238             x -= shape1.getMinX();
239             y -= shape1.getMinY();
240 
241             x /= (shape1.getMaxX() - shape1.getMinX());
242             y /= (shape1.getMaxY() - shape1.getMinY());
243 
244             float tx = x * scaleX;
245             float ty = y * scaleY;
246 
247             tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx);
248             ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty);
249 
250             GL.glTexCoord2f(tx, ty);
251             return null;
252         });
253         
254         if (t == null) {
255             TextureImpl.bindNone();
256         } else {
257             t.bind();
258         }
259     }
260 
261     /**
262      * Draw the the given shape filled in.  Only the vertices are set.  
263      * The colour has to be set independently of this method.
264      * 
265      * @param shape The shape to fill.
266      * @param fill The fill to apply
267      */
268     public static final void fill(@Nonnull final Shape shape, @Nonnull final ShapeFill fill) {
269         if (!validFill(shape)) {
270             return;
271         }
272         
273         Texture t = TextureImpl.getLastBind();
274         TextureImpl.bindNone();
275 
276         shape.getCenter();
277         fill(shape, (shape1, x, y) -> {
278             fill.colorAt(shape1, x, y).bind();
279             Vector2f offset = fill.getOffsetAt(shape1, x, y);
280 
281             return new float[] {offset.x + x,offset.y + y};
282         });
283         
284         if (t == null) {
285             TextureImpl.bindNone();
286         } else {
287             t.bind();
288         }
289     }
290     
291 
292     /**
293      * Draw the the given shape filled in with a texture.  Only the vertices are set.  
294      * The colour has to be set independently of this method.
295      * 
296      * @param shape The shape to texture.
297      * @param image The image to tile across the shape
298      * @param scaleX The scale to apply on the x axis for texturing
299      * @param scaleY The scale to apply on the y axis for texturing
300      * @param fill The fill to apply
301      */
302     public static final void texture(@Nonnull final Shape shape, @Nonnull final Image image, final float scaleX, final float scaleY, @Nonnull final ShapeFill fill) {
303         if (!validFill(shape)) {
304             return;
305         }
306         
307         Texture t = TextureImpl.getLastBind();
308         image.getTexture().bind();
309         
310         final float center[] = shape.getCenter();
311         fill(shape, (shape1, x, y) -> {
312             fill.colorAt(shape1, x - center[0], y - center[1]).bind();
313             Vector2f offset = fill.getOffsetAt(shape1, x, y);
314 
315             x += offset.x;
316             y += offset.y;
317 
318             float tx = x * scaleX;
319             float ty = y * scaleY;
320 
321             tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx);
322             ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty);
323 
324             GL.glTexCoord2f(tx, ty);
325 
326             return new float[] {offset.x + x,offset.y + y};
327         });
328         
329         if (t == null) {
330             TextureImpl.bindNone();
331         } else {
332             t.bind();
333         }
334     }
335     /**
336      * Draw the the given shape filled in with a texture.  Only the vertices are set.  
337      * The colour has to be set independently of this method.
338      * 
339      * @param shape The shape to texture.
340      * @param image The image to tile across the shape
341      * @param gen The texture coordinate generator to create coordiantes for the shape
342      */
343     public static final void texture(@Nonnull final Shape shape, @Nonnull Image image, @Nonnull final TexCoordGenerator gen) {
344         Texture t = TextureImpl.getLastBind();
345 
346         image.getTexture().bind();
347 
348         shape.getCenter();
349         fill(shape, (shape1, x, y) -> {
350             Vector2f tex = gen.getCoordFor(x, y);
351             GL.glTexCoord2f(tex.x, tex.y);
352 
353             return new float[] {x,y};
354         });
355         
356         if (t == null) {
357             TextureImpl.bindNone();
358         } else {
359             t.bind();
360         }
361     }
362     
363     /**
364      * Description of some feature that will be applied to each point render
365      *
366      * @author kevin
367      */
368     private static interface PointCallback {
369         /**
370          * Apply feature before the call to glVertex
371          *
372          * @param shape The shape the point belongs to
373          * @param x The x poisiton the vertex will be at
374          * @param y The y position the vertex will be at
375          * @return The new coordinates of null
376          */
377         @Nullable
378         float[] preRenderPoint(Shape shape, float x, float y);
379     }
380 }