• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.graphics.glutils;
18 
19 import com.badlogic.gdx.Gdx;
20 import com.badlogic.gdx.graphics.Camera;
21 import com.badlogic.gdx.graphics.Color;
22 import com.badlogic.gdx.graphics.GL20;
23 import com.badlogic.gdx.math.MathUtils;
24 import com.badlogic.gdx.math.Matrix4;
25 import com.badlogic.gdx.math.Vector2;
26 import com.badlogic.gdx.math.Vector3;
27 import com.badlogic.gdx.utils.Disposable;
28 
29 /** Renders points, lines, shape outlines and filled shapes.
30  * <p>
31  * By default a 2D orthographic projection with the origin in the lower left corner is used and units are specified in screen
32  * pixels. This can be changed by configuring the projection matrix, usually using the {@link Camera#combined} matrix. If the
33  * screen resolution changes, the projection matrix may need to be updated.
34  * <p>
35  * Shapes are rendered in batches to increase performance. Standard usage pattern looks as follows:
36  *
37  * <pre>
38  * {@code
39  * camera.update();
40  * shapeRenderer.setProjectionMatrix(camera.combined);
41  *
42  * shapeRenderer.begin(ShapeType.Line);
43  * shapeRenderer.setColor(1, 1, 0, 1);
44  * shapeRenderer.line(x, y, x2, y2);
45  * shapeRenderer.rect(x, y, width, height);
46  * shapeRenderer.circle(x, y, radius);
47  * shapeRenderer.end();
48  *
49  * shapeRenderer.begin(ShapeType.Filled);
50  * shapeRenderer.setColor(0, 1, 0, 1);
51  * shapeRenderer.rect(x, y, width, height);
52  * shapeRenderer.circle(x, y, radius);
53  * shapeRenderer.end();
54  * }
55  * </pre>
56  *
57  * ShapeRenderer has a second matrix called the transformation matrix which is used to rotate, scale and translate shapes in a
58  * more flexible manner. The following example shows how to rotate a rectangle around its center using the z-axis as the rotation
59  * axis and placing it's center at (20, 12, 2):
60  *
61  * <pre>
62  * shapeRenderer.begin(ShapeType.Line);
63  * shapeRenderer.identity();
64  * shapeRenderer.translate(20, 12, 2);
65  * shapeRenderer.rotate(0, 0, 1, 90);
66  * shapeRenderer.rect(-width / 2, -height / 2, width, height);
67  * shapeRenderer.end();
68  * </pre>
69  *
70  * Matrix operations all use postmultiplication and work just like glTranslate, glScale and glRotate. The last transformation
71  * specified will be the first that is applied to a shape (rotate then translate in the above example).
72  * <p>
73  * The projection and transformation matrices are a state of the ShapeRenderer, just like the color, and will be applied to all
74  * shapes until they are changed.
75  * @author mzechner
76  * @author stbachmann
77  * @author Nathan Sweet */
78 public class ShapeRenderer implements Disposable {
79 	/** Shape types to be used with {@link #begin(ShapeType)}.
80 	 * @author mzechner, stbachmann */
81 	public enum ShapeType {
82 		Point(GL20.GL_POINTS), Line(GL20.GL_LINES), Filled(GL20.GL_TRIANGLES);
83 
84 		private final int glType;
85 
ShapeType(int glType)86 		ShapeType (int glType) {
87 			this.glType = glType;
88 		}
89 
getGlType()90 		public int getGlType () {
91 			return glType;
92 		}
93 	}
94 
95 	private final ImmediateModeRenderer renderer;
96 	private boolean matrixDirty = false;
97 	private final Matrix4 projectionMatrix = new Matrix4();
98 	private final Matrix4 transformMatrix = new Matrix4();
99 	private final Matrix4 combinedMatrix = new Matrix4();
100 	private final Vector2 tmp = new Vector2();
101 	private final Color color = new Color(1, 1, 1, 1);
102 	private ShapeType shapeType;
103 	private boolean autoShapeType;
104 	private float defaultRectLineWidth = 0.75f;
105 
ShapeRenderer()106 	public ShapeRenderer () {
107 		this(5000);
108 	}
109 
ShapeRenderer(int maxVertices)110 	public ShapeRenderer (int maxVertices) {
111 		this(maxVertices, null);
112 	}
113 
ShapeRenderer(int maxVertices, ShaderProgram defaultShader)114 	public ShapeRenderer (int maxVertices, ShaderProgram defaultShader) {
115 		if (defaultShader == null) {
116 			renderer = new ImmediateModeRenderer20(maxVertices, false, true, 0);
117 		} else {
118 			renderer = new ImmediateModeRenderer20(maxVertices, false, true, 0, defaultShader);
119 		}
120 		projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
121 		matrixDirty = true;
122 	}
123 
124 	/** Sets the color to be used by the next shapes drawn. */
setColor(Color color)125 	public void setColor (Color color) {
126 		this.color.set(color);
127 	}
128 
129 	/** Sets the color to be used by the next shapes drawn. */
setColor(float r, float g, float b, float a)130 	public void setColor (float r, float g, float b, float a) {
131 		this.color.set(r, g, b, a);
132 	}
133 
getColor()134 	public Color getColor () {
135 		return color;
136 	}
137 
updateMatrices()138 	public void updateMatrices () {
139 		matrixDirty = true;
140 	}
141 
142 	/** Sets the projection matrix to be used for rendering. Usually this will be set to {@link Camera#combined}.
143 	 * @param matrix */
setProjectionMatrix(Matrix4 matrix)144 	public void setProjectionMatrix (Matrix4 matrix) {
145 		projectionMatrix.set(matrix);
146 		matrixDirty = true;
147 	}
148 
149 	/** If the matrix is modified, {@link #updateMatrices()} must be called. */
getProjectionMatrix()150 	public Matrix4 getProjectionMatrix () {
151 		return projectionMatrix;
152 	}
153 
setTransformMatrix(Matrix4 matrix)154 	public void setTransformMatrix (Matrix4 matrix) {
155 		transformMatrix.set(matrix);
156 		matrixDirty = true;
157 	}
158 
159 	/** If the matrix is modified, {@link #updateMatrices()} must be called. */
getTransformMatrix()160 	public Matrix4 getTransformMatrix () {
161 		return transformMatrix;
162 	}
163 
164 	/** Sets the transformation matrix to identity. */
identity()165 	public void identity () {
166 		transformMatrix.idt();
167 		matrixDirty = true;
168 	}
169 
170 	/** Multiplies the current transformation matrix by a translation matrix. */
translate(float x, float y, float z)171 	public void translate (float x, float y, float z) {
172 		transformMatrix.translate(x, y, z);
173 		matrixDirty = true;
174 	}
175 
176 	/** Multiplies the current transformation matrix by a rotation matrix. */
rotate(float axisX, float axisY, float axisZ, float degrees)177 	public void rotate (float axisX, float axisY, float axisZ, float degrees) {
178 		transformMatrix.rotate(axisX, axisY, axisZ, degrees);
179 		matrixDirty = true;
180 	}
181 
182 	/** Multiplies the current transformation matrix by a scale matrix. */
scale(float scaleX, float scaleY, float scaleZ)183 	public void scale (float scaleX, float scaleY, float scaleZ) {
184 		transformMatrix.scale(scaleX, scaleY, scaleZ);
185 		matrixDirty = true;
186 	}
187 
188 	/** If true, when drawing a shape cannot be performed with the current shape type, the batch is flushed and the shape type is
189 	 * changed automatically. This can increase the number of batch flushes if care is not taken to draw the same type of shapes
190 	 * together. Default is false. */
setAutoShapeType(boolean autoShapeType)191 	public void setAutoShapeType (boolean autoShapeType) {
192 		this.autoShapeType = autoShapeType;
193 	}
194 
195 	/** Begins a new batch without specifying a shape type.
196 	 * @throws IllegalStateException if {@link #autoShapeType} is false. */
begin()197 	public void begin () {
198 		if (!autoShapeType) throw new IllegalStateException("autoShapeType must be true to use this method.");
199 		begin(ShapeType.Line);
200 	}
201 
202 	/** Starts a new batch of shapes. Shapes drawn within the batch will attempt to use the type specified. The call to this method
203 	 * must be paired with a call to {@link #end()}.
204 	 * @see #setAutoShapeType(boolean) */
begin(ShapeType type)205 	public void begin (ShapeType type) {
206 		if (shapeType != null) throw new IllegalStateException("Call end() before beginning a new shape batch.");
207 		shapeType = type;
208 		if (matrixDirty) {
209 			combinedMatrix.set(projectionMatrix);
210 			Matrix4.mul(combinedMatrix.val, transformMatrix.val);
211 			matrixDirty = false;
212 		}
213 		renderer.begin(combinedMatrix, shapeType.getGlType());
214 	}
215 
set(ShapeType type)216 	public void set (ShapeType type) {
217 		if (shapeType == type) return;
218 		if (shapeType == null) throw new IllegalStateException("begin must be called first.");
219 		if (!autoShapeType) throw new IllegalStateException("autoShapeType must be enabled.");
220 		end();
221 		begin(type);
222 	}
223 
224 	/** Draws a point using {@link ShapeType#Point}, {@link ShapeType#Line} or {@link ShapeType#Filled}. */
point(float x, float y, float z)225 	public void point (float x, float y, float z) {
226 		if (shapeType == ShapeType.Line) {
227 			float size = defaultRectLineWidth * 0.5f;
228 			line(x - size, y - size, z, x + size, y + size, z);
229 			return;
230 		} else if (shapeType == ShapeType.Filled) {
231 			float size = defaultRectLineWidth * 0.5f;
232 			box(x - size, y - size, z - size, defaultRectLineWidth, defaultRectLineWidth, defaultRectLineWidth);
233 			return;
234 		}
235 		check(ShapeType.Point, null, 1);
236 		renderer.color(color);
237 		renderer.vertex(x, y, z);
238 	}
239 
240 	/** Draws a line using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
line(float x, float y, float z, float x2, float y2, float z2)241 	public final void line (float x, float y, float z, float x2, float y2, float z2) {
242 		line(x, y, z, x2, y2, z2, color, color);
243 	}
244 
245 	/** @see #line(float, float, float, float, float, float) */
line(Vector3 v0, Vector3 v1)246 	public final void line (Vector3 v0, Vector3 v1) {
247 		line(v0.x, v0.y, v0.z, v1.x, v1.y, v1.z, color, color);
248 	}
249 
250 	/** @see #line(float, float, float, float, float, float) */
line(float x, float y, float x2, float y2)251 	public final void line (float x, float y, float x2, float y2) {
252 		line(x, y, 0.0f, x2, y2, 0.0f, color, color);
253 	}
254 
255 	/** @see #line(float, float, float, float, float, float) */
line(Vector2 v0, Vector2 v1)256 	public final void line (Vector2 v0, Vector2 v1) {
257 		line(v0.x, v0.y, 0.0f, v1.x, v1.y, 0.0f, color, color);
258 	}
259 
260 	/** @see #line(float, float, float, float, float, float, Color, Color) */
line(float x, float y, float x2, float y2, Color c1, Color c2)261 	public final void line (float x, float y, float x2, float y2, Color c1, Color c2) {
262 		line(x, y, 0.0f, x2, y2, 0.0f, c1, c2);
263 	}
264 
265 	/** Draws a line using {@link ShapeType#Line} or {@link ShapeType#Filled}. The line is drawn with two colors interpolated
266 	 * between the start and end points. */
line(float x, float y, float z, float x2, float y2, float z2, Color c1, Color c2)267 	public void line (float x, float y, float z, float x2, float y2, float z2, Color c1, Color c2) {
268 		if (shapeType == ShapeType.Filled) {
269 			rectLine(x, y, x2, y2, defaultRectLineWidth);
270 			return;
271 		}
272 		check(ShapeType.Line, null, 2);
273 		renderer.color(c1.r, c1.g, c1.b, c1.a);
274 		renderer.vertex(x, y, z);
275 		renderer.color(c2.r, c2.g, c2.b, c2.a);
276 		renderer.vertex(x2, y2, z2);
277 	}
278 
279 	/** Draws a curve using {@link ShapeType#Line}. */
curve(float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, int segments)280 	public void curve (float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, int segments) {
281 		check(ShapeType.Line, null, segments * 2 + 2);
282 		float colorBits = color.toFloatBits();
283 
284 		// Algorithm from: http://www.antigrain.com/research/bezier_interpolation/index.html#PAGE_BEZIER_INTERPOLATION
285 		float subdiv_step = 1f / segments;
286 		float subdiv_step2 = subdiv_step * subdiv_step;
287 		float subdiv_step3 = subdiv_step * subdiv_step * subdiv_step;
288 
289 		float pre1 = 3 * subdiv_step;
290 		float pre2 = 3 * subdiv_step2;
291 		float pre4 = 6 * subdiv_step2;
292 		float pre5 = 6 * subdiv_step3;
293 
294 		float tmp1x = x1 - cx1 * 2 + cx2;
295 		float tmp1y = y1 - cy1 * 2 + cy2;
296 
297 		float tmp2x = (cx1 - cx2) * 3 - x1 + x2;
298 		float tmp2y = (cy1 - cy2) * 3 - y1 + y2;
299 
300 		float fx = x1;
301 		float fy = y1;
302 
303 		float dfx = (cx1 - x1) * pre1 + tmp1x * pre2 + tmp2x * subdiv_step3;
304 		float dfy = (cy1 - y1) * pre1 + tmp1y * pre2 + tmp2y * subdiv_step3;
305 
306 		float ddfx = tmp1x * pre4 + tmp2x * pre5;
307 		float ddfy = tmp1y * pre4 + tmp2y * pre5;
308 
309 		float dddfx = tmp2x * pre5;
310 		float dddfy = tmp2y * pre5;
311 
312 		while (segments-- > 0) {
313 			renderer.color(colorBits);
314 			renderer.vertex(fx, fy, 0);
315 			fx += dfx;
316 			fy += dfy;
317 			dfx += ddfx;
318 			dfy += ddfy;
319 			ddfx += dddfx;
320 			ddfy += dddfy;
321 			renderer.color(colorBits);
322 			renderer.vertex(fx, fy, 0);
323 		}
324 		renderer.color(colorBits);
325 		renderer.vertex(fx, fy, 0);
326 		renderer.color(colorBits);
327 		renderer.vertex(x2, y2, 0);
328 	}
329 
330 	/** Draws a triangle in x/y plane using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
triangle(float x1, float y1, float x2, float y2, float x3, float y3)331 	public void triangle (float x1, float y1, float x2, float y2, float x3, float y3) {
332 		check(ShapeType.Line, ShapeType.Filled, 6);
333 		float colorBits = color.toFloatBits();
334 		if (shapeType == ShapeType.Line) {
335 			renderer.color(colorBits);
336 			renderer.vertex(x1, y1, 0);
337 			renderer.color(colorBits);
338 			renderer.vertex(x2, y2, 0);
339 
340 			renderer.color(colorBits);
341 			renderer.vertex(x2, y2, 0);
342 			renderer.color(colorBits);
343 			renderer.vertex(x3, y3, 0);
344 
345 			renderer.color(colorBits);
346 			renderer.vertex(x3, y3, 0);
347 			renderer.color(colorBits);
348 			renderer.vertex(x1, y1, 0);
349 		} else {
350 			renderer.color(colorBits);
351 			renderer.vertex(x1, y1, 0);
352 			renderer.color(colorBits);
353 			renderer.vertex(x2, y2, 0);
354 			renderer.color(colorBits);
355 			renderer.vertex(x3, y3, 0);
356 		}
357 	}
358 
359 	/** Draws a triangle in x/y plane with colored corners using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
triangle(float x1, float y1, float x2, float y2, float x3, float y3, Color col1, Color col2, Color col3)360 	public void triangle (float x1, float y1, float x2, float y2, float x3, float y3, Color col1, Color col2, Color col3) {
361 		check(ShapeType.Line, ShapeType.Filled, 6);
362 		if (shapeType == ShapeType.Line) {
363 			renderer.color(col1.r, col1.g, col1.b, col1.a);
364 			renderer.vertex(x1, y1, 0);
365 			renderer.color(col2.r, col2.g, col2.b, col2.a);
366 			renderer.vertex(x2, y2, 0);
367 
368 			renderer.color(col2.r, col2.g, col2.b, col2.a);
369 			renderer.vertex(x2, y2, 0);
370 			renderer.color(col3.r, col3.g, col3.b, col3.a);
371 			renderer.vertex(x3, y3, 0);
372 
373 			renderer.color(col3.r, col3.g, col3.b, col3.a);
374 			renderer.vertex(x3, y3, 0);
375 			renderer.color(col1.r, col1.g, col1.b, col1.a);
376 			renderer.vertex(x1, y1, 0);
377 		} else {
378 			renderer.color(col1.r, col1.g, col1.b, col1.a);
379 			renderer.vertex(x1, y1, 0);
380 			renderer.color(col2.r, col2.g, col2.b, col2.a);
381 			renderer.vertex(x2, y2, 0);
382 			renderer.color(col3.r, col3.g, col3.b, col3.a);
383 			renderer.vertex(x3, y3, 0);
384 		}
385 	}
386 
387 	/** Draws a rectangle in the x/y plane using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
rect(float x, float y, float width, float height)388 	public void rect (float x, float y, float width, float height) {
389 		check(ShapeType.Line, ShapeType.Filled, 8);
390 		float colorBits = color.toFloatBits();
391 		if (shapeType == ShapeType.Line) {
392 			renderer.color(colorBits);
393 			renderer.vertex(x, y, 0);
394 			renderer.color(colorBits);
395 			renderer.vertex(x + width, y, 0);
396 
397 			renderer.color(colorBits);
398 			renderer.vertex(x + width, y, 0);
399 			renderer.color(colorBits);
400 			renderer.vertex(x + width, y + height, 0);
401 
402 			renderer.color(colorBits);
403 			renderer.vertex(x + width, y + height, 0);
404 			renderer.color(colorBits);
405 			renderer.vertex(x, y + height, 0);
406 
407 			renderer.color(colorBits);
408 			renderer.vertex(x, y + height, 0);
409 			renderer.color(colorBits);
410 			renderer.vertex(x, y, 0);
411 		} else {
412 			renderer.color(colorBits);
413 			renderer.vertex(x, y, 0);
414 			renderer.color(colorBits);
415 			renderer.vertex(x + width, y, 0);
416 			renderer.color(colorBits);
417 			renderer.vertex(x + width, y + height, 0);
418 
419 			renderer.color(colorBits);
420 			renderer.vertex(x + width, y + height, 0);
421 			renderer.color(colorBits);
422 			renderer.vertex(x, y + height, 0);
423 			renderer.color(colorBits);
424 			renderer.vertex(x, y, 0);
425 		}
426 	}
427 
428 	/** Draws a rectangle in the x/y plane using {@link ShapeType#Line} or {@link ShapeType#Filled}. The x and y specify the lower
429 	 * left corner.
430 	 * @param col1 The color at (x, y).
431 	 * @param col2 The color at (x + width, y).
432 	 * @param col3 The color at (x + width, y + height).
433 	 * @param col4 The color at (x, y + height). */
rect(float x, float y, float width, float height, Color col1, Color col2, Color col3, Color col4)434 	public void rect (float x, float y, float width, float height, Color col1, Color col2, Color col3, Color col4) {
435 		check(ShapeType.Line, ShapeType.Filled, 8);
436 
437 		if (shapeType == ShapeType.Line) {
438 			renderer.color(col1.r, col1.g, col1.b, col1.a);
439 			renderer.vertex(x, y, 0);
440 			renderer.color(col2.r, col2.g, col2.b, col2.a);
441 			renderer.vertex(x + width, y, 0);
442 
443 			renderer.color(col2.r, col2.g, col2.b, col2.a);
444 			renderer.vertex(x + width, y, 0);
445 			renderer.color(col3.r, col3.g, col3.b, col3.a);
446 			renderer.vertex(x + width, y + height, 0);
447 
448 			renderer.color(col3.r, col3.g, col3.b, col3.a);
449 			renderer.vertex(x + width, y + height, 0);
450 			renderer.color(col4.r, col4.g, col4.b, col4.a);
451 			renderer.vertex(x, y + height, 0);
452 
453 			renderer.color(col4.r, col4.g, col4.b, col4.a);
454 			renderer.vertex(x, y + height, 0);
455 			renderer.color(col1.r, col1.g, col1.b, col1.a);
456 			renderer.vertex(x, y, 0);
457 		} else {
458 			renderer.color(col1.r, col1.g, col1.b, col1.a);
459 			renderer.vertex(x, y, 0);
460 			renderer.color(col2.r, col2.g, col2.b, col2.a);
461 			renderer.vertex(x + width, y, 0);
462 			renderer.color(col3.r, col3.g, col3.b, col3.a);
463 			renderer.vertex(x + width, y + height, 0);
464 
465 			renderer.color(col3.r, col3.g, col3.b, col3.a);
466 			renderer.vertex(x + width, y + height, 0);
467 			renderer.color(col4.r, col4.g, col4.b, col4.a);
468 			renderer.vertex(x, y + height, 0);
469 			renderer.color(col1.r, col1.g, col1.b, col1.a);
470 			renderer.vertex(x, y, 0);
471 		}
472 	}
473 
474 	/** Draws a rectangle in the x/y plane using {@link ShapeType#Line} or {@link ShapeType#Filled}. The x and y specify the lower
475 	 * left corner. The originX and originY specify the point about which to rotate the rectangle. */
rect(float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float degrees)476 	public void rect (float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY,
477 		float degrees) {
478 		rect(x, y, originX, originY, width, height, scaleX, scaleY, degrees, color, color, color, color);
479 	}
480 
481 	/** Draws a rectangle in the x/y plane using {@link ShapeType#Line} or {@link ShapeType#Filled}. The x and y specify the lower
482 	 * left corner. The originX and originY specify the point about which to rotate the rectangle.
483 	 * @param col1 The color at (x, y)
484 	 * @param col2 The color at (x + width, y)
485 	 * @param col3 The color at (x + width, y + height)
486 	 * @param col4 The color at (x, y + height) */
rect(float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY, float degrees, Color col1, Color col2, Color col3, Color col4)487 	public void rect (float x, float y, float originX, float originY, float width, float height, float scaleX, float scaleY,
488 		float degrees, Color col1, Color col2, Color col3, Color col4) {
489 		check(ShapeType.Line, ShapeType.Filled, 8);
490 
491 		float cos = MathUtils.cosDeg(degrees);
492 		float sin = MathUtils.sinDeg(degrees);
493 		float fx = -originX;
494 		float fy = -originY;
495 		float fx2 = width - originX;
496 		float fy2 = height - originY;
497 
498 		if (scaleX != 1 || scaleY != 1) {
499 			fx *= scaleX;
500 			fy *= scaleY;
501 			fx2 *= scaleX;
502 			fy2 *= scaleY;
503 		}
504 
505 		float worldOriginX = x + originX;
506 		float worldOriginY = y + originY;
507 
508 		float x1 = cos * fx - sin * fy + worldOriginX;
509 		float y1 = sin * fx + cos * fy + worldOriginY;
510 
511 		float x2 = cos * fx2 - sin * fy + worldOriginX;
512 		float y2 = sin * fx2 + cos * fy + worldOriginY;
513 
514 		float x3 = cos * fx2 - sin * fy2 + worldOriginX;
515 		float y3 = sin * fx2 + cos * fy2 + worldOriginY;
516 
517 		float x4 = x1 + (x3 - x2);
518 		float y4 = y3 - (y2 - y1);
519 
520 		if (shapeType == ShapeType.Line) {
521 			renderer.color(col1.r, col1.g, col1.b, col1.a);
522 			renderer.vertex(x1, y1, 0);
523 			renderer.color(col2.r, col2.g, col2.b, col2.a);
524 			renderer.vertex(x2, y2, 0);
525 
526 			renderer.color(col2.r, col2.g, col2.b, col2.a);
527 			renderer.vertex(x2, y2, 0);
528 			renderer.color(col3.r, col3.g, col3.b, col3.a);
529 			renderer.vertex(x3, y3, 0);
530 
531 			renderer.color(col3.r, col3.g, col3.b, col3.a);
532 			renderer.vertex(x3, y3, 0);
533 			renderer.color(col4.r, col4.g, col4.b, col4.a);
534 			renderer.vertex(x4, y4, 0);
535 
536 			renderer.color(col4.r, col4.g, col4.b, col4.a);
537 			renderer.vertex(x4, y4, 0);
538 			renderer.color(col1.r, col1.g, col1.b, col1.a);
539 			renderer.vertex(x1, y1, 0);
540 		} else {
541 			renderer.color(col1.r, col1.g, col1.b, col1.a);
542 			renderer.vertex(x1, y1, 0);
543 			renderer.color(col2.r, col2.g, col2.b, col2.a);
544 			renderer.vertex(x2, y2, 0);
545 			renderer.color(col3.r, col3.g, col3.b, col3.a);
546 			renderer.vertex(x3, y3, 0);
547 
548 			renderer.color(col3.r, col3.g, col3.b, col3.a);
549 			renderer.vertex(x3, y3, 0);
550 			renderer.color(col4.r, col4.g, col4.b, col4.a);
551 			renderer.vertex(x4, y4, 0);
552 			renderer.color(col1.r, col1.g, col1.b, col1.a);
553 			renderer.vertex(x1, y1, 0);
554 		}
555 
556 	}
557 
558 	/** Draws a line using a rotated rectangle, where with one edge is centered at x1, y1 and the opposite edge centered at x2, y2. */
rectLine(float x1, float y1, float x2, float y2, float width)559 	public void rectLine (float x1, float y1, float x2, float y2, float width) {
560 		check(ShapeType.Line, ShapeType.Filled, 8);
561 		float colorBits = color.toFloatBits();
562 		Vector2 t = tmp.set(y2 - y1, x1 - x2).nor();
563 		width *= 0.5f;
564 		float tx = t.x * width;
565 		float ty = t.y * width;
566 		if (shapeType == ShapeType.Line) {
567 			renderer.color(colorBits);
568 			renderer.vertex(x1 + tx, y1 + ty, 0);
569 			renderer.color(colorBits);
570 			renderer.vertex(x1 - tx, y1 - ty, 0);
571 
572 			renderer.color(colorBits);
573 			renderer.vertex(x2 + tx, y2 + ty, 0);
574 			renderer.color(colorBits);
575 			renderer.vertex(x2 - tx, y2 - ty, 0);
576 
577 			renderer.color(colorBits);
578 			renderer.vertex(x2 + tx, y2 + ty, 0);
579 			renderer.color(colorBits);
580 			renderer.vertex(x1 + tx, y1 + ty, 0);
581 
582 			renderer.color(colorBits);
583 			renderer.vertex(x2 - tx, y2 - ty, 0);
584 			renderer.color(colorBits);
585 			renderer.vertex(x1 - tx, y1 - ty, 0);
586 		} else {
587 			renderer.color(colorBits);
588 			renderer.vertex(x1 + tx, y1 + ty, 0);
589 			renderer.color(colorBits);
590 			renderer.vertex(x1 - tx, y1 - ty, 0);
591 			renderer.color(colorBits);
592 			renderer.vertex(x2 + tx, y2 + ty, 0);
593 
594 			renderer.color(colorBits);
595 			renderer.vertex(x2 - tx, y2 - ty, 0);
596 			renderer.color(colorBits);
597 			renderer.vertex(x2 + tx, y2 + ty, 0);
598 			renderer.color(colorBits);
599 			renderer.vertex(x1 - tx, y1 - ty, 0);
600 		}
601 	}
602 
603 	/** @see #rectLine(float, float, float, float, float) */
rectLine(Vector2 p1, Vector2 p2, float width)604 	public void rectLine (Vector2 p1, Vector2 p2, float width) {
605 		rectLine(p1.x, p1.y, p2.x, p2.y, width);
606 	}
607 
608 	/** Draws a cube using {@link ShapeType#Line} or {@link ShapeType#Filled}. The x, y and z specify the bottom, left, front corner
609 	 * of the rectangle. */
box(float x, float y, float z, float width, float height, float depth)610 	public void box (float x, float y, float z, float width, float height, float depth) {
611 		depth = -depth;
612 		float colorBits = color.toFloatBits();
613 		if (shapeType == ShapeType.Line) {
614 			check(ShapeType.Line, ShapeType.Filled, 24);
615 
616 			renderer.color(colorBits);
617 			renderer.vertex(x, y, z);
618 			renderer.color(colorBits);
619 			renderer.vertex(x + width, y, z);
620 
621 			renderer.color(colorBits);
622 			renderer.vertex(x + width, y, z);
623 			renderer.color(colorBits);
624 			renderer.vertex(x + width, y, z + depth);
625 
626 			renderer.color(colorBits);
627 			renderer.vertex(x + width, y, z + depth);
628 			renderer.color(colorBits);
629 			renderer.vertex(x, y, z + depth);
630 
631 			renderer.color(colorBits);
632 			renderer.vertex(x, y, z + depth);
633 			renderer.color(colorBits);
634 			renderer.vertex(x, y, z);
635 
636 			renderer.color(colorBits);
637 			renderer.vertex(x, y, z);
638 			renderer.color(colorBits);
639 			renderer.vertex(x, y + height, z);
640 
641 			renderer.color(colorBits);
642 			renderer.vertex(x, y + height, z);
643 			renderer.color(colorBits);
644 			renderer.vertex(x + width, y + height, z);
645 
646 			renderer.color(colorBits);
647 			renderer.vertex(x + width, y + height, z);
648 			renderer.color(colorBits);
649 			renderer.vertex(x + width, y + height, z + depth);
650 
651 			renderer.color(colorBits);
652 			renderer.vertex(x + width, y + height, z + depth);
653 			renderer.color(colorBits);
654 			renderer.vertex(x, y + height, z + depth);
655 
656 			renderer.color(colorBits);
657 			renderer.vertex(x, y + height, z + depth);
658 			renderer.color(colorBits);
659 			renderer.vertex(x, y + height, z);
660 
661 			renderer.color(colorBits);
662 			renderer.vertex(x + width, y, z);
663 			renderer.color(colorBits);
664 			renderer.vertex(x + width, y + height, z);
665 
666 			renderer.color(colorBits);
667 			renderer.vertex(x + width, y, z + depth);
668 			renderer.color(colorBits);
669 			renderer.vertex(x + width, y + height, z + depth);
670 
671 			renderer.color(colorBits);
672 			renderer.vertex(x, y, z + depth);
673 			renderer.color(colorBits);
674 			renderer.vertex(x, y + height, z + depth);
675 		} else {
676 			check(ShapeType.Line, ShapeType.Filled, 36);
677 
678 			// Front
679 			renderer.color(colorBits);
680 			renderer.vertex(x, y, z);
681 			renderer.color(colorBits);
682 			renderer.vertex(x + width, y, z);
683 			renderer.color(colorBits);
684 			renderer.vertex(x + width, y + height, z);
685 
686 			renderer.color(colorBits);
687 			renderer.vertex(x, y, z);
688 			renderer.color(colorBits);
689 			renderer.vertex(x + width, y + height, z);
690 			renderer.color(colorBits);
691 			renderer.vertex(x, y + height, z);
692 
693 			// Back
694 			renderer.color(colorBits);
695 			renderer.vertex(x + width, y, z + depth);
696 			renderer.color(colorBits);
697 			renderer.vertex(x, y, z + depth);
698 			renderer.color(colorBits);
699 			renderer.vertex(x + width, y + height, z + depth);
700 
701 			renderer.color(colorBits);
702 			renderer.vertex(x, y + height, z + depth);
703 			renderer.color(colorBits);
704 			renderer.vertex(x, y, z + depth);
705 			renderer.color(colorBits);
706 			renderer.vertex(x + width, y + height, z + depth);
707 
708 			// Left
709 			renderer.color(colorBits);
710 			renderer.vertex(x, y, z + depth);
711 			renderer.color(colorBits);
712 			renderer.vertex(x, y, z);
713 			renderer.color(colorBits);
714 			renderer.vertex(x, y + height, z);
715 
716 			renderer.color(colorBits);
717 			renderer.vertex(x, y, z + depth);
718 			renderer.color(colorBits);
719 			renderer.vertex(x, y + height, z);
720 			renderer.color(colorBits);
721 			renderer.vertex(x, y + height, z + depth);
722 
723 			// Right
724 			renderer.color(colorBits);
725 			renderer.vertex(x + width, y, z);
726 			renderer.color(colorBits);
727 			renderer.vertex(x + width, y, z + depth);
728 			renderer.color(colorBits);
729 			renderer.vertex(x + width, y + height, z + depth);
730 
731 			renderer.color(colorBits);
732 			renderer.vertex(x + width, y, z);
733 			renderer.color(colorBits);
734 			renderer.vertex(x + width, y + height, z + depth);
735 			renderer.color(colorBits);
736 			renderer.vertex(x + width, y + height, z);
737 
738 			// Top
739 			renderer.color(colorBits);
740 			renderer.vertex(x, y + height, z);
741 			renderer.color(colorBits);
742 			renderer.vertex(x + width, y + height, z);
743 			renderer.color(colorBits);
744 			renderer.vertex(x + width, y + height, z + depth);
745 
746 			renderer.color(colorBits);
747 			renderer.vertex(x, y + height, z);
748 			renderer.color(colorBits);
749 			renderer.vertex(x + width, y + height, z + depth);
750 			renderer.color(colorBits);
751 			renderer.vertex(x, y + height, z + depth);
752 
753 			// Bottom
754 			renderer.color(colorBits);
755 			renderer.vertex(x, y, z + depth);
756 			renderer.color(colorBits);
757 			renderer.vertex(x + width, y, z + depth);
758 			renderer.color(colorBits);
759 			renderer.vertex(x + width, y, z);
760 
761 			renderer.color(colorBits);
762 			renderer.vertex(x, y, z + depth);
763 			renderer.color(colorBits);
764 			renderer.vertex(x + width, y, z);
765 			renderer.color(colorBits);
766 			renderer.vertex(x, y, z);
767 		}
768 
769 	}
770 
771 	/** Draws two crossed lines using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
x(float x, float y, float size)772 	public void x (float x, float y, float size) {
773 		line(x - size, y - size, x + size, y + size);
774 		line(x - size, y + size, x + size, y - size);
775 	}
776 
777 	/** @see #x(float, float, float) */
x(Vector2 p, float size)778 	public void x (Vector2 p, float size) {
779 		x(p.x, p.y, size);
780 	}
781 
782 	/** Calls {@link #arc(float, float, float, float, float, int)} by estimating the number of segments needed for a smooth arc. */
arc(float x, float y, float radius, float start, float degrees)783 	public void arc (float x, float y, float radius, float start, float degrees) {
784 		arc(x, y, radius, start, degrees, Math.max(1, (int)(6 * (float)Math.cbrt(radius) * (degrees / 360.0f))));
785 	}
786 
787 	/** Draws an arc using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
arc(float x, float y, float radius, float start, float degrees, int segments)788 	public void arc (float x, float y, float radius, float start, float degrees, int segments) {
789 		if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
790 		float colorBits = color.toFloatBits();
791 		float theta = (2 * MathUtils.PI * (degrees / 360.0f)) / segments;
792 		float cos = MathUtils.cos(theta);
793 		float sin = MathUtils.sin(theta);
794 		float cx = radius * MathUtils.cos(start * MathUtils.degreesToRadians);
795 		float cy = radius * MathUtils.sin(start * MathUtils.degreesToRadians);
796 
797 		if (shapeType == ShapeType.Line) {
798 			check(ShapeType.Line, ShapeType.Filled, segments * 2 + 2);
799 
800 			renderer.color(colorBits);
801 			renderer.vertex(x, y, 0);
802 			renderer.color(colorBits);
803 			renderer.vertex(x + cx, y + cy, 0);
804 			for (int i = 0; i < segments; i++) {
805 				renderer.color(colorBits);
806 				renderer.vertex(x + cx, y + cy, 0);
807 				float temp = cx;
808 				cx = cos * cx - sin * cy;
809 				cy = sin * temp + cos * cy;
810 				renderer.color(colorBits);
811 				renderer.vertex(x + cx, y + cy, 0);
812 			}
813 			renderer.color(colorBits);
814 			renderer.vertex(x + cx, y + cy, 0);
815 		} else {
816 			check(ShapeType.Line, ShapeType.Filled, segments * 3 + 3);
817 
818 			for (int i = 0; i < segments; i++) {
819 				renderer.color(colorBits);
820 				renderer.vertex(x, y, 0);
821 				renderer.color(colorBits);
822 				renderer.vertex(x + cx, y + cy, 0);
823 				float temp = cx;
824 				cx = cos * cx - sin * cy;
825 				cy = sin * temp + cos * cy;
826 				renderer.color(colorBits);
827 				renderer.vertex(x + cx, y + cy, 0);
828 			}
829 			renderer.color(colorBits);
830 			renderer.vertex(x, y, 0);
831 			renderer.color(colorBits);
832 			renderer.vertex(x + cx, y + cy, 0);
833 		}
834 
835 		float temp = cx;
836 		cx = 0;
837 		cy = 0;
838 		renderer.color(colorBits);
839 		renderer.vertex(x + cx, y + cy, 0);
840 	}
841 
842 	/** Calls {@link #circle(float, float, float, int)} by estimating the number of segments needed for a smooth circle. */
circle(float x, float y, float radius)843 	public void circle (float x, float y, float radius) {
844 		circle(x, y, radius, Math.max(1, (int)(6 * (float)Math.cbrt(radius))));
845 	}
846 
847 	/** Draws a circle using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
circle(float x, float y, float radius, int segments)848 	public void circle (float x, float y, float radius, int segments) {
849 		if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
850 		float colorBits = color.toFloatBits();
851 		float angle = 2 * MathUtils.PI / segments;
852 		float cos = MathUtils.cos(angle);
853 		float sin = MathUtils.sin(angle);
854 		float cx = radius, cy = 0;
855 		if (shapeType == ShapeType.Line) {
856 			check(ShapeType.Line, ShapeType.Filled, segments * 2 + 2);
857 			for (int i = 0; i < segments; i++) {
858 				renderer.color(colorBits);
859 				renderer.vertex(x + cx, y + cy, 0);
860 				float temp = cx;
861 				cx = cos * cx - sin * cy;
862 				cy = sin * temp + cos * cy;
863 				renderer.color(colorBits);
864 				renderer.vertex(x + cx, y + cy, 0);
865 			}
866 			// Ensure the last segment is identical to the first.
867 			renderer.color(colorBits);
868 			renderer.vertex(x + cx, y + cy, 0);
869 		} else {
870 			check(ShapeType.Line, ShapeType.Filled, segments * 3 + 3);
871 			segments--;
872 			for (int i = 0; i < segments; i++) {
873 				renderer.color(colorBits);
874 				renderer.vertex(x, y, 0);
875 				renderer.color(colorBits);
876 				renderer.vertex(x + cx, y + cy, 0);
877 				float temp = cx;
878 				cx = cos * cx - sin * cy;
879 				cy = sin * temp + cos * cy;
880 				renderer.color(colorBits);
881 				renderer.vertex(x + cx, y + cy, 0);
882 			}
883 			// Ensure the last segment is identical to the first.
884 			renderer.color(colorBits);
885 			renderer.vertex(x, y, 0);
886 			renderer.color(colorBits);
887 			renderer.vertex(x + cx, y + cy, 0);
888 		}
889 
890 		float temp = cx;
891 		cx = radius;
892 		cy = 0;
893 		renderer.color(colorBits);
894 		renderer.vertex(x + cx, y + cy, 0);
895 	}
896 
897 	/** Calls {@link #ellipse(float, float, float, float, int)} by estimating the number of segments needed for a smooth ellipse. */
ellipse(float x, float y, float width, float height)898 	public void ellipse (float x, float y, float width, float height) {
899 		ellipse(x, y, width, height, Math.max(1, (int)(12 * (float)Math.cbrt(Math.max(width * 0.5f, height * 0.5f)))));
900 	}
901 
902 	/** Draws an ellipse using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
ellipse(float x, float y, float width, float height, int segments)903 	public void ellipse (float x, float y, float width, float height, int segments) {
904 		if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
905 		check(ShapeType.Line, ShapeType.Filled, segments * 3);
906 		float colorBits = color.toFloatBits();
907 		float angle = 2 * MathUtils.PI / segments;
908 
909 		float cx = x + width / 2, cy = y + height / 2;
910 		if (shapeType == ShapeType.Line) {
911 			for (int i = 0; i < segments; i++) {
912 				renderer.color(colorBits);
913 				renderer.vertex(cx + (width * 0.5f * MathUtils.cos(i * angle)), cy + (height * 0.5f * MathUtils.sin(i * angle)), 0);
914 
915 				renderer.color(colorBits);
916 				renderer.vertex(cx + (width * 0.5f * MathUtils.cos((i + 1) * angle)),
917 					cy + (height * 0.5f * MathUtils.sin((i + 1) * angle)), 0);
918 			}
919 		} else {
920 			for (int i = 0; i < segments; i++) {
921 				renderer.color(colorBits);
922 				renderer.vertex(cx + (width * 0.5f * MathUtils.cos(i * angle)), cy + (height * 0.5f * MathUtils.sin(i * angle)), 0);
923 
924 				renderer.color(colorBits);
925 				renderer.vertex(cx, cy, 0);
926 
927 				renderer.color(colorBits);
928 				renderer.vertex(cx + (width * 0.5f * MathUtils.cos((i + 1) * angle)),
929 					cy + (height * 0.5f * MathUtils.sin((i + 1) * angle)), 0);
930 			}
931 		}
932 	}
933 
934 	/** Calls {@link #ellipse(float, float, float, float, float, int)} by estimating the number of segments needed for a smooth ellipse. */
ellipse(float x, float y, float width, float height, float rotation)935 	public void ellipse (float x, float y, float width, float height, float rotation) {
936 		ellipse(x, y, width, height, rotation, Math.max(1, (int)(12 * (float)Math.cbrt(Math.max(width * 0.5f, height * 0.5f)))));
937 	}
938 
939 	/** Draws an ellipse using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
ellipse(float x, float y, float width, float height, float rotation, int segments)940 	public void ellipse (float x, float y, float width, float height, float rotation, int segments) {
941 		if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
942 		check(ShapeType.Line, ShapeType.Filled, segments * 3);
943 		float colorBits = color.toFloatBits();
944 		float angle = 2 * MathUtils.PI / segments;
945 
946 		rotation = MathUtils.PI * rotation / 180f;
947 		float sin = MathUtils.sin(rotation);
948 		float cos = MathUtils.cos(rotation);
949 
950 		float cx = x + width / 2, cy = y + height / 2;
951 		float x1 = width * 0.5f;
952 		float y1 = 0;
953 		if (shapeType == ShapeType.Line) {
954 			for (int i = 0; i < segments; i++) {
955 				renderer.color(colorBits);
956 				renderer.vertex(cx + cos * x1 - sin * y1, cy + sin * x1 + cos * y1, 0);
957 
958 				x1 = (width * 0.5f * MathUtils.cos((i + 1) * angle));
959 				y1 = (height * 0.5f * MathUtils.sin((i + 1) * angle));
960 
961 				renderer.color(colorBits);
962 				renderer.vertex(cx + cos * x1 - sin * y1, cy + sin * x1 + cos * y1, 0);
963 			}
964 		} else {
965 			for (int i = 0; i < segments; i++) {
966 				renderer.color(colorBits);
967 				renderer.vertex(cx + cos * x1 - sin * y1, cy + sin * x1 + cos * y1, 0);
968 
969 				renderer.color(colorBits);
970 				renderer.vertex(cx, cy, 0);
971 
972 				x1 = (width * 0.5f * MathUtils.cos((i + 1) * angle));
973 				y1 = (height * 0.5f * MathUtils.sin((i + 1) * angle));
974 
975 				renderer.color(colorBits);
976 				renderer.vertex(cx + cos * x1 - sin * y1, cy + sin * x1 + cos * y1, 0);
977 			}
978 		}
979 	}
980 
981 	/** Calls {@link #cone(float, float, float, float, float, int)} by estimating the number of segments needed for a smooth
982 	 * circular base. */
cone(float x, float y, float z, float radius, float height)983 	public void cone (float x, float y, float z, float radius, float height) {
984 		cone(x, y, z, radius, height, Math.max(1, (int)(4 * (float)Math.sqrt(radius))));
985 	}
986 
987 	/** Draws a cone using {@link ShapeType#Line} or {@link ShapeType#Filled}. */
cone(float x, float y, float z, float radius, float height, int segments)988 	public void cone (float x, float y, float z, float radius, float height, int segments) {
989 		if (segments <= 0) throw new IllegalArgumentException("segments must be > 0.");
990 		check(ShapeType.Line, ShapeType.Filled, segments * 4 + 2);
991 		float colorBits = color.toFloatBits();
992 		float angle = 2 * MathUtils.PI / segments;
993 		float cos = MathUtils.cos(angle);
994 		float sin = MathUtils.sin(angle);
995 		float cx = radius, cy = 0;
996 		if (shapeType == ShapeType.Line) {
997 			for (int i = 0; i < segments; i++) {
998 				renderer.color(colorBits);
999 				renderer.vertex(x + cx, y + cy, z);
1000 				renderer.color(colorBits);
1001 				renderer.vertex(x, y, z + height);
1002 				renderer.color(colorBits);
1003 				renderer.vertex(x + cx, y + cy, z);
1004 				float temp = cx;
1005 				cx = cos * cx - sin * cy;
1006 				cy = sin * temp + cos * cy;
1007 				renderer.color(colorBits);
1008 				renderer.vertex(x + cx, y + cy, z);
1009 			}
1010 			// Ensure the last segment is identical to the first.
1011 			renderer.color(colorBits);
1012 			renderer.vertex(x + cx, y + cy, z);
1013 		} else {
1014 			segments--;
1015 			for (int i = 0; i < segments; i++) {
1016 				renderer.color(colorBits);
1017 				renderer.vertex(x, y, z);
1018 				renderer.color(colorBits);
1019 				renderer.vertex(x + cx, y + cy, z);
1020 				float temp = cx;
1021 				float temp2 = cy;
1022 				cx = cos * cx - sin * cy;
1023 				cy = sin * temp + cos * cy;
1024 				renderer.color(colorBits);
1025 				renderer.vertex(x + cx, y + cy, z);
1026 
1027 				renderer.color(colorBits);
1028 				renderer.vertex(x + temp, y + temp2, z);
1029 				renderer.color(colorBits);
1030 				renderer.vertex(x + cx, y + cy, z);
1031 				renderer.color(colorBits);
1032 				renderer.vertex(x, y, z + height);
1033 			}
1034 			// Ensure the last segment is identical to the first.
1035 			renderer.color(colorBits);
1036 			renderer.vertex(x, y, z);
1037 			renderer.color(colorBits);
1038 			renderer.vertex(x + cx, y + cy, z);
1039 		}
1040 		float temp = cx;
1041 		float temp2 = cy;
1042 		cx = radius;
1043 		cy = 0;
1044 		renderer.color(colorBits);
1045 		renderer.vertex(x + cx, y + cy, z);
1046 		if (shapeType != ShapeType.Line) {
1047 			renderer.color(colorBits);
1048 			renderer.vertex(x + temp, y + temp2, z);
1049 			renderer.color(colorBits);
1050 			renderer.vertex(x + cx, y + cy, z);
1051 			renderer.color(colorBits);
1052 			renderer.vertex(x, y, z + height);
1053 		}
1054 	}
1055 
1056 	/** Draws a polygon in the x/y plane using {@link ShapeType#Line}. The vertices must contain at least 3 points (6 floats x,y). */
polygon(float[] vertices, int offset, int count)1057 	public void polygon (float[] vertices, int offset, int count) {
1058 		if (count < 6) throw new IllegalArgumentException("Polygons must contain at least 3 points.");
1059 		if (count % 2 != 0) throw new IllegalArgumentException("Polygons must have an even number of vertices.");
1060 
1061 		check(ShapeType.Line, null, count);
1062 		float colorBits = color.toFloatBits();
1063 		float firstX = vertices[0];
1064 		float firstY = vertices[1];
1065 
1066 		for (int i = offset, n = offset + count; i < n; i += 2) {
1067 			float x1 = vertices[i];
1068 			float y1 = vertices[i + 1];
1069 
1070 			float x2;
1071 			float y2;
1072 
1073 			if (i + 2 >= count) {
1074 				x2 = firstX;
1075 				y2 = firstY;
1076 			} else {
1077 				x2 = vertices[i + 2];
1078 				y2 = vertices[i + 3];
1079 			}
1080 
1081 			renderer.color(colorBits);
1082 			renderer.vertex(x1, y1, 0);
1083 			renderer.color(colorBits);
1084 			renderer.vertex(x2, y2, 0);
1085 		}
1086 	}
1087 
1088 	/** @see #polygon(float[], int, int) */
polygon(float[] vertices)1089 	public void polygon (float[] vertices) {
1090 		polygon(vertices, 0, vertices.length);
1091 	}
1092 
1093 	/** Draws a polyline in the x/y plane using {@link ShapeType#Line}. The vertices must contain at least 2 points (4 floats x,y). */
polyline(float[] vertices, int offset, int count)1094 	public void polyline (float[] vertices, int offset, int count) {
1095 		if (count < 4) throw new IllegalArgumentException("Polylines must contain at least 2 points.");
1096 		if (count % 2 != 0) throw new IllegalArgumentException("Polylines must have an even number of vertices.");
1097 
1098 		check(ShapeType.Line, null, count);
1099 		float colorBits = color.toFloatBits();
1100 		for (int i = offset, n = offset + count - 2; i < n; i += 2) {
1101 			float x1 = vertices[i];
1102 			float y1 = vertices[i + 1];
1103 
1104 			float x2;
1105 			float y2;
1106 
1107 			x2 = vertices[i + 2];
1108 			y2 = vertices[i + 3];
1109 
1110 			renderer.color(colorBits);
1111 			renderer.vertex(x1, y1, 0);
1112 			renderer.color(colorBits);
1113 			renderer.vertex(x2, y2, 0);
1114 		}
1115 	}
1116 
1117 	/** @see #polyline(float[], int, int) */
polyline(float[] vertices)1118 	public void polyline (float[] vertices) {
1119 		polyline(vertices, 0, vertices.length);
1120 	}
1121 
1122 	/** @param other May be null. */
check(ShapeType preferred, ShapeType other, int newVertices)1123 	private void check (ShapeType preferred, ShapeType other, int newVertices) {
1124 		if (shapeType == null) throw new IllegalStateException("begin must be called first.");
1125 
1126 		if (shapeType != preferred && shapeType != other) {
1127 			// Shape type is not valid.
1128 			if (!autoShapeType) {
1129 				if (other == null)
1130 					throw new IllegalStateException("Must call begin(ShapeType." + preferred + ").");
1131 				else
1132 					throw new IllegalStateException("Must call begin(ShapeType." + preferred + ") or begin(ShapeType." + other + ").");
1133 			}
1134 			end();
1135 			begin(preferred);
1136 		} else if (matrixDirty) {
1137 			// Matrix has been changed.
1138 			ShapeType type = shapeType;
1139 			end();
1140 			begin(type);
1141 		} else if (renderer.getMaxVertices() - renderer.getNumVertices() < newVertices) {
1142 			// Not enough space.
1143 			ShapeType type = shapeType;
1144 			end();
1145 			begin(type);
1146 		}
1147 	}
1148 
1149 	/** Finishes the batch of shapes and ensures they get rendered. */
end()1150 	public void end () {
1151 		renderer.end();
1152 		shapeType = null;
1153 	}
1154 
flush()1155 	public void flush () {
1156 		ShapeType type = shapeType;
1157 		end();
1158 		begin(type);
1159 	}
1160 
1161 	/** Returns the current shape type. */
getCurrentType()1162 	public ShapeType getCurrentType () {
1163 		return shapeType;
1164 	}
1165 
getRenderer()1166 	public ImmediateModeRenderer getRenderer () {
1167 		return renderer;
1168 	}
1169 
1170 	/** @return true if currently between begin and end. */
isDrawing()1171 	public boolean isDrawing () {
1172 		return shapeType != null;
1173 	}
1174 
dispose()1175 	public void dispose () {
1176 		renderer.dispose();
1177 	}
1178 }
1179