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