package org.newdawn.slick.geom; import org.newdawn.slick.Image; import org.newdawn.slick.ShapeFill; import org.newdawn.slick.opengl.Texture; import org.newdawn.slick.opengl.TextureImpl; import org.newdawn.slick.opengl.renderer.LineStripRenderer; import org.newdawn.slick.opengl.renderer.Renderer; import org.newdawn.slick.opengl.renderer.SGL; /** * @author Mark Bernard * * Use this class to render shpaes directly to OpenGL. Allows you to bypass the Graphics class. */ public final class ShapeRenderer { /** The renderer to use for all GL operations */ private static SGL GL = Renderer.get(); /** The renderer to use line strips */ private static LineStripRenderer LSR = Renderer.getLineStripRenderer(); /** * Draw the outline of the given shape. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to draw. */ public static final void draw(Shape shape) { Texture t = TextureImpl.getLastBind(); TextureImpl.bindNone(); float points[] = shape.getPoints(); LSR.start(); for(int i=0;i<points.length;i+=2) { LSR.vertex(points[i], points[i + 1]); } if (shape.closed()) { LSR.vertex(points[0], points[1]); } LSR.end(); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Draw the outline of the given shape. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to draw. * @param fill The fill to apply */ public static final void draw(Shape shape, ShapeFill fill) { float points[] = shape.getPoints(); Texture t = TextureImpl.getLastBind(); TextureImpl.bindNone(); float center[] = shape.getCenter(); GL.glBegin(SGL.GL_LINE_STRIP); for(int i=0;i<points.length;i+=2) { fill.colorAt(shape, points[i], points[i + 1]).bind(); Vector2f offset = fill.getOffsetAt(shape, points[i], points[i + 1]); GL.glVertex2f(points[i] + offset.x, points[i + 1] + offset.y); } if (shape.closed()) { fill.colorAt(shape, points[0], points[1]).bind(); Vector2f offset = fill.getOffsetAt(shape, points[0], points[1]); GL.glVertex2f(points[0] + offset.x, points[1] + offset.y); } GL.glEnd(); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Check there are enough points to fill * * @param shape THe shape we're drawing * @return True if the fill is valid */ public static boolean validFill(Shape shape) { if (shape.getTriangles() == null) { return false; } return shape.getTriangles().getTriangleCount() != 0; } /** * Draw the the given shape filled in. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to fill. */ public static final void fill(Shape shape) { if (!validFill(shape)) { return; } Texture t = TextureImpl.getLastBind(); TextureImpl.bindNone(); fill(shape, new PointCallback() { public float[] preRenderPoint(Shape shape, float x, float y) { // do nothing, we're just filling the shape this time return null; } }); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Draw the the given shape filled in. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to fill. * @param callback The callback that will be invoked for each shape point */ private static final void fill(Shape shape, PointCallback callback) { Triangulator tris = shape.getTriangles(); GL.glBegin(SGL.GL_TRIANGLES); for (int i=0;i<tris.getTriangleCount();i++) { for (int p=0;p<3;p++) { float[] pt = tris.getTrianglePoint(i, p); float[] np = callback.preRenderPoint(shape, pt[0],pt[1]); if (np == null) { GL.glVertex2f(pt[0],pt[1]); } else { GL.glVertex2f(np[0],np[1]); } } } GL.glEnd(); } /** * Draw the the given shape filled in with a texture. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to texture. * @param image The image to tile across the shape */ public static final void texture(Shape shape, Image image) { texture(shape, image, 0.01f, 0.01f); } /** * Draw the the given shape filled in with a texture. Only the vertices are set. * The colour has to be set independently of this method. This method is required to * fit the texture once across the shape. * * @param shape The shape to texture. * @param image The image to tile across the shape */ public static final void textureFit(Shape shape, Image image) { textureFit(shape, image,1f,1f); } /** * Draw the the given shape filled in with a texture. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to texture. * @param image The image to tile across the shape * @param scaleX The scale to apply on the x axis for texturing * @param scaleY The scale to apply on the y axis for texturing */ public static final void texture(Shape shape, final Image image, final float scaleX, final float scaleY) { if (!validFill(shape)) { return; } final Texture t = TextureImpl.getLastBind(); image.getTexture().bind(); fill(shape, new PointCallback() { public float[] preRenderPoint(Shape shape, float x, float y) { float tx = x * scaleX; float ty = y * scaleY; tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx); ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty); GL.glTexCoord2f(tx, ty); return null; } }); float points[] = shape.getPoints(); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Draw the the given shape filled in with a texture. Only the vertices are set. * The colour has to be set independently of this method. This method is required to * fit the texture scaleX times across the shape and scaleY times down the shape. * * @param shape The shape to texture. * @param image The image to tile across the shape * @param scaleX The scale to apply on the x axis for texturing * @param scaleY The scale to apply on the y axis for texturing */ public static final void textureFit(Shape shape, final Image image, final float scaleX, final float scaleY) { if (!validFill(shape)) { return; } float points[] = shape.getPoints(); Texture t = TextureImpl.getLastBind(); image.getTexture().bind(); final float minX = shape.getX(); final float minY = shape.getY(); final float maxX = shape.getMaxX() - minX; final float maxY = shape.getMaxY() - minY; fill(shape, new PointCallback() { public float[] preRenderPoint(Shape shape, float x, float y) { x -= shape.getMinX(); y -= shape.getMinY(); x /= (shape.getMaxX() - shape.getMinX()); y /= (shape.getMaxY() - shape.getMinY()); float tx = x * scaleX; float ty = y * scaleY; tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx); ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty); GL.glTexCoord2f(tx, ty); return null; } }); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Draw the the given shape filled in. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to fill. * @param fill The fill to apply */ public static final void fill(final Shape shape, final ShapeFill fill) { if (!validFill(shape)) { return; } Texture t = TextureImpl.getLastBind(); TextureImpl.bindNone(); final float center[] = shape.getCenter(); fill(shape, new PointCallback() { public float[] preRenderPoint(Shape shape, float x, float y) { fill.colorAt(shape, x, y).bind(); Vector2f offset = fill.getOffsetAt(shape, x, y); return new float[] {offset.x + x,offset.y + y}; } }); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Draw the the given shape filled in with a texture. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to texture. * @param image The image to tile across the shape * @param scaleX The scale to apply on the x axis for texturing * @param scaleY The scale to apply on the y axis for texturing * @param fill The fill to apply */ public static final void texture(final Shape shape, final Image image, final float scaleX, final float scaleY, final ShapeFill fill) { if (!validFill(shape)) { return; } Texture t = TextureImpl.getLastBind(); image.getTexture().bind(); final float center[] = shape.getCenter(); fill(shape, new PointCallback() { public float[] preRenderPoint(Shape shape, float x, float y) { fill.colorAt(shape, x - center[0], y - center[1]).bind(); Vector2f offset = fill.getOffsetAt(shape, x, y); x += offset.x; y += offset.y; float tx = x * scaleX; float ty = y * scaleY; tx = image.getTextureOffsetX() + (image.getTextureWidth() * tx); ty = image.getTextureOffsetY() + (image.getTextureHeight() * ty); GL.glTexCoord2f(tx, ty); return new float[] {offset.x + x,offset.y + y}; } }); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Draw the the given shape filled in with a texture. Only the vertices are set. * The colour has to be set independently of this method. * * @param shape The shape to texture. * @param image The image to tile across the shape * @param gen The texture coordinate generator to create coordiantes for the shape */ public static final void texture(final Shape shape, Image image, final TexCoordGenerator gen) { Texture t = TextureImpl.getLastBind(); image.getTexture().bind(); final float center[] = shape.getCenter(); fill(shape, new PointCallback() { public float[] preRenderPoint(Shape shape, float x, float y) { Vector2f tex = gen.getCoordFor(x, y); GL.glTexCoord2f(tex.x, tex.y); return new float[] {x,y}; } }); if (t == null) { TextureImpl.bindNone(); } else { t.bind(); } } /** * Description of some feature that will be applied to each point render * * @author kevin */ private static interface PointCallback { /** * Apply feature before the call to glVertex * * @param shape The shape the point belongs to * @param x The x poisiton the vertex will be at * @param y The y position the vertex will be at * @return The new coordinates of null */ float[] preRenderPoint(Shape shape, float x, float y); } }