1 package com.badlogic.gdx.graphics.g3d.utils;
3 import com.badlogic.gdx.Gdx;
4 import com.badlogic.gdx.graphics.Color;
5 import com.badlogic.gdx.graphics.GL10;
6 import com.badlogic.gdx.graphics.Mesh;
7 import com.badlogic.gdx.graphics.VertexAttribute;
8 import com.badlogic.gdx.graphics.VertexAttributes;
9 import com.badlogic.gdx.graphics.VertexAttributes.Usage;
10 import com.badlogic.gdx.graphics.g3d.Model;
11 import com.badlogic.gdx.graphics.g3d.model.MeshPart;
12 import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder.VertexInfo;
13 import com.badlogic.gdx.graphics.glutils.ShaderProgram;
14 import com.badlogic.gdx.math.MathUtils;
15 import com.badlogic.gdx.math.Matrix4;
16 import com.badlogic.gdx.math.Vector2;
17 import com.badlogic.gdx.math.Vector3;
18 import com.badlogic.gdx.utils.Array;
19 import com.badlogic.gdx.utils.FloatArray;
20 import com.badlogic.gdx.utils.GdxRuntimeException;
21 import com.badlogic.gdx.utils.Pool;
22 import com.badlogic.gdx.utils.Pool.Poolable;
23 import com.badlogic.gdx.utils.ShortArray;
25 /** Class to construct a mesh, optionally splitting it into one or more mesh parts.
26 * Before you can call any other method you must call {@link #begin(VertexAttributes)} or {@link #begin(VertexAttributes, int)}.
27 * To use mesh parts you must call {@link #part(String, int)} before you start building the part.
28 * The MeshPart itself is only valid after the call to {@link #end()}.
30 public class MeshBuilder implements MeshPartBuilder {
31 private final VertexInfo vertTmp1 = new VertexInfo();
32 private final VertexInfo vertTmp2 = new VertexInfo();
33 private final VertexInfo vertTmp3 = new VertexInfo();
34 private final VertexInfo vertTmp4 = new VertexInfo();
35 private final VertexInfo vertTmp5 = new VertexInfo();
36 private final VertexInfo vertTmp6 = new VertexInfo();
37 private final VertexInfo vertTmp7 = new VertexInfo();
38 private final VertexInfo vertTmp8 = new VertexInfo();
40 private final Matrix4 matTmp1 = new Matrix4();
42 private final Vector3 tempV1 = new Vector3();
43 private final Vector3 tempV2 = new Vector3();
44 private final Vector3 tempV3 = new Vector3();
45 private final Vector3 tempV4 = new Vector3();
46 private final Vector3 tempV5 = new Vector3();
47 private final Vector3 tempV6 = new Vector3();
48 private final Vector3 tempV7 = new Vector3();
49 private final Vector3 tempV8 = new Vector3();
51 /** The vertex attributes of the resulting mesh */
52 private VertexAttributes attributes;
53 /** The vertices to construct, no size checking is done */
54 private FloatArray vertices = new FloatArray();
55 /** The indices to construct, no size checking is done */
56 private ShortArray indices = new ShortArray();
57 /** The size (in number of floats) of each vertex */
59 /** The current vertex index, used for indexing */
61 /** The offset in the indices array when begin() was called, used to define a meshpart. */
63 /** The offset within an vertex to position */
64 private int posOffset;
65 /** The size (in number of floats) of the position attribute */
67 /** The offset within an vertex to normal, or -1 if not available */
68 private int norOffset;
69 /** The offset within an vertex to color, or -1 if not available */
70 private int colOffset;
71 /** The size (in number of floats) of the color attribute */
73 /** The offset within an vertex to packed color, or -1 if not available */
75 /** The offset within an vertex to texture coordinates, or -1 if not available */
77 /** The meshpart currently being created */
78 private MeshPart part;
79 /** The parts created between begin and end */
80 private Array<MeshPart> parts = new Array<MeshPart>();
81 /** The color used if no vertex color is specified. */
82 private final Color color = new Color();
83 /** Whether to apply the default color. */
84 private boolean colorSet;
85 /** The current primitiveType */
86 private int primitiveType;
87 // FIXME makes this configurable
88 private float uMin = 0, uMax = 1, vMin = 0, vMax = 1;
89 private float[] vertex;
91 /** @param usage bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage},
92 * only Position, Color, Normal and TextureCoordinates is supported. */
93 public static VertexAttributes createAttributes(long usage) {
94 final Array<VertexAttribute> attrs = new Array<VertexAttribute>();
95 if ((usage & Usage.Position) == Usage.Position)
96 attrs.add(new VertexAttribute(Usage.Position, 3, ShaderProgram.POSITION_ATTRIBUTE));
97 if ((usage & Usage.Color) == Usage.Color)
98 attrs.add(new VertexAttribute(Usage.Color, 4, ShaderProgram.COLOR_ATTRIBUTE));
99 if ((usage & Usage.Normal) == Usage.Normal)
100 attrs.add(new VertexAttribute(Usage.Normal, 3, ShaderProgram.NORMAL_ATTRIBUTE));
101 if ((usage & Usage.TextureCoordinates) == Usage.TextureCoordinates)
102 attrs.add(new VertexAttribute(Usage.TextureCoordinates, 2, ShaderProgram.TEXCOORD_ATTRIBUTE+"0"));
103 final VertexAttribute attributes[] = new VertexAttribute[attrs.size];
104 for (int i = 0; i < attributes.length; i++)
105 attributes[i] = attrs.get(i);
106 return new VertexAttributes(attributes);
109 /** Begin building a mesh. Call {@link #part(String, int)} to start a {@link MeshPart}.
110 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage},
111 * only Position, Color, Normal and TextureCoordinates is supported. */
112 public void begin(final long attributes) {
113 begin(createAttributes(attributes), 0);
116 /** Begin building a mesh. Call {@link #part(String, int)} to start a {@link MeshPart}. */
117 public void begin(final VertexAttributes attributes) {
118 begin(attributes, 0);
121 /** Begin building a mesh.
122 * @param attributes bitwise mask of the {@link com.badlogic.gdx.graphics.VertexAttributes.Usage},
123 * only Position, Color, Normal and TextureCoordinates is supported. */
124 public void begin(final long attributes, int primitiveType) {
125 begin(createAttributes(attributes), primitiveType);
128 /** Begin building a mesh */
129 public void begin(final VertexAttributes attributes, int primitiveType) {
130 if (this.attributes != null)
131 throw new RuntimeException("Call end() first");
132 this.attributes = attributes;
133 this.vertices.clear();
134 this.indices.clear();
139 this.stride = attributes.vertexSize / 4;
140 this.vertex = new float[stride];
141 VertexAttribute a = attributes.findByUsage(Usage.Position);
143 throw new GdxRuntimeException("Cannot build mesh without position attribute");
144 posOffset = a.offset / 4;
145 posSize = a.numComponents;
146 a = attributes.findByUsage(Usage.Normal);
147 norOffset = a == null ? -1 : a.offset / 4;
148 a = attributes.findByUsage(Usage.Color);
149 colOffset = a == null ? -1 : a.offset / 4;
150 colSize = a == null ? 0 : a.numComponents;
151 a = attributes.findByUsage(Usage.ColorPacked);
152 cpOffset = a == null ? -1 : a.offset / 4;
153 a = attributes.findByUsage(Usage.TextureCoordinates);
154 uvOffset = a == null ? -1 : a.offset / 4;
156 this.primitiveType = primitiveType;
159 private void endpart() {
161 part.indexOffset = istart;
162 part.numVertices = indices.size - istart;
163 istart = indices.size;
168 /** Starts a new MeshPart. The mesh part is not usable until end() is called */
169 public MeshPart part(final String id, int primitiveType) {
170 if (this.attributes == null)
171 throw new RuntimeException("Call begin() first");
174 part = new MeshPart();
176 this.primitiveType = part.primitiveType = primitiveType;
184 /** End building the mesh and returns the mesh */
186 if (this.attributes == null)
187 throw new RuntimeException("Call begin() first");
190 final Mesh mesh = new Mesh(true, vertices.size, indices.size, attributes);
191 mesh.setVertices(vertices.items, 0, vertices.size);
192 mesh.setIndices(indices.items, 0, indices.size);
194 for (MeshPart p : parts)
206 public VertexAttributes getAttributes() {
211 public MeshPart getMeshPart() {
215 private final static Pool<Vector3> vectorPool = new Pool<Vector3>() {
217 protected Vector3 newObject () {
218 return new Vector3();
222 private final static Array<Vector3> vectorArray = new Array<Vector3>();
224 private Vector3 tmp(float x, float y, float z) {
225 final Vector3 result = vectorPool.obtain().set(x, y, z);
226 vectorArray.add(result);
230 private Vector3 tmp(Vector3 copyFrom) {
231 return tmp(copyFrom.x, copyFrom.y, copyFrom.z);
234 private void cleanup() {
235 vectorPool.freeAll(vectorArray);
240 public void setColor(float r, float g, float b, float a) {
241 color.set(r, g, b, a);
246 public void setColor(final Color color) {
247 if ((colorSet = color != null)==true)
248 this.color.set(color);
252 public void setUVRange(float u1, float v1, float u2, float v2) {
259 /** Increases the size of the backing vertices array to accommodate the specified number of additional vertices.
260 * Useful before adding many vertices to avoid multiple backing array resizes.
261 * @param numVertices The number of vertices you are about to add */
262 public void ensureVertices(int numVertices) {
263 vertices.ensureCapacity(vertex.length * numVertices);
266 /** Increases the size of the backing indices array to accommodate the specified number of additional indices.
267 * Useful before adding many indices to avoid multiple backing array resizes.
268 * @param numIndices The number of indices you are about to add */
269 public void ensureIndices(int numIndices) {
270 indices.ensureCapacity(numIndices);
273 /** Increases the size of the backing vertices and indices arrays to accommodate the specified number of additional
274 * vertices and indices. Useful before adding many vertices and indices to avoid multiple backing array resizes.
275 * @param numVertices The number of vertices you are about to add
276 * @param numIndices The number of indices you are about to add */
277 public void ensureCapacity(int numVertices, int numIndices) {
278 ensureVertices(numVertices);
279 ensureIndices(numIndices);
282 /** Increases the size of the backing indices array to accommodate the specified number of additional triangles.
283 * Useful before adding many triangles to avoid multiple backing array resizes.
284 * @param numTriangles The number of triangles you are about to add */
285 public void ensureTriangleIndices(int numTriangles) {
286 if (primitiveType == GL10.GL_LINES)
287 ensureIndices(6 * numTriangles);
288 else // GL_TRIANGLES || GL_POINTS
289 ensureIndices(3 * numTriangles);
292 /** Increases the size of the backing vertices and indices arrays to accommodate the specified number of additional
293 * vertices and triangles. Useful before adding many triangles to avoid multiple backing array resizes.
294 * @param numVertices The number of vertices you are about to add
295 * @param numTriangles The number of triangles you are about to add */
296 public void ensureTriangles(int numVertices, int numTriangles) {
297 ensureVertices(numVertices);
298 ensureTriangleIndices(numTriangles);
301 /** Increases the size of the backing vertices and indices arrays to accommodate the specified number of additional
302 * vertices and triangles. Useful before adding many triangles to avoid multiple backing array resizes.
303 * Assumes each triangles adds 3 vertices.
304 * @param numTriangles The number of triangles you are about to add */
305 public void ensureTriangles(int numTriangles) {
306 ensureTriangles(3 * numTriangles, numTriangles);
309 /** Increases the size of the backing indices array to accommodate the specified number of additional rectangles.
310 * Useful before adding many rectangles to avoid multiple backing array resizes.
311 * @param numRectangles The number of rectangles you are about to add */
312 public void ensureRectangleIndices(int numRectangles) {
313 if (primitiveType == GL10.GL_POINTS)
314 ensureIndices(4 * numRectangles);
315 else if (primitiveType == GL10.GL_LINES)
316 ensureIndices(8 * numRectangles);
318 ensureIndices(6 * numRectangles);
321 /** Increases the size of the backing vertices and indices arrays to accommodate the specified number of additional
322 * vertices and rectangles. Useful before adding many rectangles to avoid multiple backing array resizes.
323 * @param numVertices The number of vertices you are about to add
324 * @param numRectangles The number of rectangles you are about to add */
325 public void ensureRectangles(int numVertices, int numRectangles) {
326 ensureVertices(numVertices);
327 ensureRectangleIndices(numRectangles);
330 /** Increases the size of the backing vertices and indices arrays to accommodate the specified number of additional
331 * vertices and rectangles. Useful before adding many rectangles to avoid multiple backing array resizes.
332 * Assumes each rectangles adds 4 vertices
333 * @param numRectangles The number of rectangles you are about to add */
334 public void ensureRectangles(int numRectangles) {
335 ensureRectangles(4 * numRectangles, numRectangles);
339 public short vertex(Vector3 pos, Vector3 nor, Color col, Vector2 uv) {
340 if (vindex >= Short.MAX_VALUE)
341 throw new GdxRuntimeException("Too many vertices used");
342 if (col == null && colorSet)
345 vertex[posOffset ] = pos.x;
346 if (posSize > 1) vertex[posOffset+1] = pos.y;
347 if (posSize > 2) vertex[posOffset+2] = pos.z;
349 if (nor != null && norOffset >= 0) {
350 vertex[norOffset ] = nor.x;
351 vertex[norOffset+1] = nor.y;
352 vertex[norOffset+2] = nor.z;
355 if (colOffset >= 0) {
356 vertex[colOffset ] = col.r;
357 vertex[colOffset+1] = col.g;
358 vertex[colOffset+2] = col.b;
359 if (colSize > 3) vertex[colOffset+3] = col.a;
360 } else if (cpOffset > 0)
361 vertex[cpOffset] = col.toFloatBits(); // FIXME cache packed color?
363 if (uv != null && uvOffset >= 0) {
364 vertex[uvOffset ] = uv.x;
365 vertex[uvOffset+1] = uv.y;
367 vertices.addAll(vertex);
368 return (short)(vindex++);
372 public short lastIndex() {
373 return (short)(vindex-1);
377 public short vertex(final float... values) {
378 vertices.addAll(values);
379 vindex += values.length / stride;
380 return (short)(vindex-1);
384 public short vertex(final VertexInfo info) {
385 return vertex(info.hasPosition ? info.position : null, info.hasNormal ? info.normal : null,
386 info.hasColor ? info.color : null, info.hasUV ? info.uv : null);
390 public void index(final short value) {
395 public void index(final short value1, final short value2) {
402 public void index(final short value1, final short value2, final short value3) {
410 public void index(final short value1, final short value2, final short value3, final short value4) {
419 public void index(short value1, short value2, short value3, short value4, short value5, short value6) {
430 public void index(short value1, short value2, short value3, short value4, short value5, short value6, short value7, short value8) {
443 public void line(short index1, short index2) {
444 if (primitiveType != GL10.GL_LINES)
445 throw new GdxRuntimeException("Incorrect primitive type");
446 index(index1, index2);
450 public void line(VertexInfo p1, VertexInfo p2) {
452 line(vertex(p1), vertex(p2));
456 public void line(Vector3 p1, Vector3 p2) {
457 line(vertTmp1.set(p1, null, null, null), vertTmp2.set(p2, null, null, null));
461 public void line(float x1, float y1, float z1, float x2, float y2, float z2) {
462 line(vertTmp1.set(null, null, null, null).setPos(x1, y1, z1), vertTmp2.set(null, null, null, null).setPos(x2, y2, z2));
466 public void line(Vector3 p1, Color c1, Vector3 p2, Color c2) {
467 line(vertTmp1.set(p1, null, c1, null), vertTmp2.set(p2, null, c2, null));
471 public void triangle(short index1, short index2, short index3) {
472 if (primitiveType == GL10.GL_TRIANGLES || primitiveType == GL10.GL_POINTS) {
473 index(index1, index2, index3);
474 } else if (primitiveType == GL10.GL_LINES) {
475 index(index1, index2, index2, index3, index3, index1);
477 throw new GdxRuntimeException("Incorrect primitive type");
481 public void triangle(VertexInfo p1, VertexInfo p2, VertexInfo p3) {
483 triangle(vertex(p1), vertex(p2), vertex(p3));
487 public void triangle(Vector3 p1, Vector3 p2, Vector3 p3) {
488 triangle(vertTmp1.set(p1, null, null, null), vertTmp2.set(p2, null, null, null), vertTmp3.set(p3, null, null, null));
492 public void triangle(Vector3 p1, Color c1, Vector3 p2, Color c2, Vector3 p3, Color c3) {
493 triangle(vertTmp1.set(p1, null, c1, null), vertTmp2.set(p2, null, c2, null), vertTmp3.set(p3, null, c3, null));
497 public void rect(short corner00, short corner10, short corner11, short corner01) {
498 if (primitiveType == GL10.GL_TRIANGLES) {
499 index(corner00, corner10, corner11, corner11, corner01, corner00);
500 } else if (primitiveType == GL10.GL_LINES) {
501 index(corner00, corner10, corner10, corner11, corner11, corner01, corner01, corner00);
502 } else if (primitiveType == GL10.GL_POINTS) {
503 index(corner00, corner10, corner11, corner01);
505 throw new GdxRuntimeException("Incorrect primitive type");
509 public void rect(VertexInfo corner00, VertexInfo corner10, VertexInfo corner11, VertexInfo corner01) {
511 rect(vertex(corner00), vertex(corner10), vertex(corner11), vertex(corner01));
515 public void rect(Vector3 corner00, Vector3 corner10, Vector3 corner11, Vector3 corner01, Vector3 normal) {
516 rect(vertTmp1.set(corner00, normal, null, null).setUV(uMin,vMin),
517 vertTmp2.set(corner10, normal, null, null).setUV(uMax,vMin),
518 vertTmp3.set(corner11, normal, null, null).setUV(uMax,vMax),
519 vertTmp4.set(corner01, normal, null, null).setUV(uMin,vMax));
523 public void rect(float x00, float y00, float z00, float x10, float y10, float z10, float x11, float y11, float z11, float x01, float y01, float z01, float normalX, float normalY, float normalZ) {
524 rect(vertTmp1.set(null, null, null, null).setPos(x00,y00,z00).setNor(normalX,normalY,normalZ).setUV(uMin,vMin),
525 vertTmp2.set(null, null, null, null).setPos(x10,y10,z10).setNor(normalX,normalY,normalZ).setUV(uMax,vMin),
526 vertTmp3.set(null, null, null, null).setPos(x11,y11,z11).setNor(normalX,normalY,normalZ).setUV(uMax,vMax),
527 vertTmp4.set(null, null, null, null).setPos(x01,y01,z01).setNor(normalX,normalY,normalZ).setUV(uMin,vMax));
531 public void patch(VertexInfo corner00, VertexInfo corner10, VertexInfo corner11, VertexInfo corner01, int divisionsU, int divisionsV) {
532 ensureRectangles((divisionsV + 1) * (divisionsU + 1), divisionsV * divisionsU);
533 for (int u = 0; u <= divisionsU; u++) {
534 final float alphaU = (float)u / (float)divisionsU;
535 vertTmp5.set(corner00).lerp(corner10, alphaU);
536 vertTmp6.set(corner01).lerp(corner11, alphaU);
537 for (int v = 0; v <= divisionsV; v++) {
538 final short idx = vertex(vertTmp7.set(vertTmp5).lerp(vertTmp6, (float)v / (float)divisionsV));
540 rect((short)(idx-divisionsV-2), (short)(idx-1), idx, (short)(idx-divisionsV-1));
546 public void patch(Vector3 corner00, Vector3 corner10, Vector3 corner11, Vector3 corner01, Vector3 normal, int divisionsU, int divisionsV) {
547 patch(vertTmp1.set(corner00, normal, null, null).setUV(uMin,vMin),
548 vertTmp2.set(corner10, normal, null, null).setUV(uMax,vMin),
549 vertTmp3.set(corner11, normal, null, null).setUV(uMax,vMax),
550 vertTmp4.set(corner01, normal, null, null).setUV(uMin,vMax),
551 divisionsU, divisionsV);
554 public void patch(float x00, float y00, float z00, float x10, float y10, float z10, float x11, float y11, float z11, float x01, float y01, float z01, float normalX, float normalY, float normalZ, int divisionsU, int divisionsV) {
555 patch(vertTmp1.set(null).setPos(x00, y00, z00).setNor(normalX, normalY, normalZ).setUV(uMin,vMin),
556 vertTmp2.set(null).setPos(x10, y10, z10).setNor(normalX, normalY, normalZ).setUV(uMax,vMin),
557 vertTmp3.set(null).setPos(x11, y11, z11).setNor(normalX, normalY, normalZ).setUV(uMax,vMax),
558 vertTmp4.set(null).setPos(x01, y01, z01).setNor(normalX, normalY, normalZ).setUV(uMin,vMax),
559 divisionsU, divisionsV);
563 public void box(VertexInfo corner000, VertexInfo corner010, VertexInfo corner100, VertexInfo corner110,
564 VertexInfo corner001, VertexInfo corner011, VertexInfo corner101, VertexInfo corner111) {
566 final short i000 = vertex(corner000);
567 final short i100 = vertex(corner100);
568 final short i110 = vertex(corner110);
569 final short i010 = vertex(corner010);
570 final short i001 = vertex(corner001);
571 final short i101 = vertex(corner101);
572 final short i111 = vertex(corner111);
573 final short i011 = vertex(corner011);
575 if (primitiveType == GL10.GL_LINES) {
577 rect(i000, i100, i110, i010);
578 rect(i101, i001, i011, i111);
579 index(i000, i001, i010, i011, i110, i111, i100, i101);
580 } else if (primitiveType != GL10.GL_POINTS) {
581 ensureRectangleIndices(2);
582 rect(i000, i100, i110, i010);
583 rect(i101, i001, i011, i111);
584 } else { // GL10.GL_TRIANGLES
585 ensureRectangleIndices(6);
586 rect(i000, i100, i110, i010);
587 rect(i101, i001, i011, i111);
588 rect(i000, i010, i011, i001);
589 rect(i101, i111, i110, i100);
590 rect(i101, i100, i000, i001);
591 rect(i110, i111, i011, i010);
596 public void box(Vector3 corner000, Vector3 corner010, Vector3 corner100, Vector3 corner110,
597 Vector3 corner001, Vector3 corner011, Vector3 corner101, Vector3 corner111) {
598 if (norOffset < 0 && uvOffset < 0) {
599 box(vertTmp1.set(corner000, null, null, null), vertTmp2.set(corner010, null, null, null),
600 vertTmp3.set(corner100, null, null, null), vertTmp4.set(corner110, null, null, null),
601 vertTmp5.set(corner001, null, null, null), vertTmp6.set(corner011, null, null, null),
602 vertTmp7.set(corner101, null, null, null), vertTmp8.set(corner111, null, null, null));
605 Vector3 nor = tempV1.set(corner000).lerp(corner110, 0.5f).sub(tempV2.set(corner001).lerp(corner111, 0.5f)).nor();
606 rect(corner000, corner010, corner110, corner100, nor);
607 rect(corner011, corner001, corner101, corner111, nor.scl(-1));
608 nor = tempV1.set(corner000).lerp(corner101, 0.5f).sub(tempV2.set(corner010).lerp(corner111, 0.5f)).nor();
609 rect(corner001, corner000, corner100, corner101, nor);
610 rect(corner010, corner011, corner111, corner110, nor.scl(-1));
611 nor = tempV1.set(corner000).lerp(corner011, 0.5f).sub(tempV2.set(corner100).lerp(corner111, 0.5f)).nor();
612 rect(corner001, corner011, corner010, corner000, nor);
613 rect(corner100, corner110, corner111, corner101, nor.scl(-1));
618 public void box(Matrix4 transform) {
619 box(tmp(-0.5f,-0.5f,-0.5f).mul(transform),tmp(-0.5f,0.5f,-0.5f).mul(transform),tmp(0.5f,-0.5f,-0.5f).mul(transform),tmp(0.5f,0.5f,-0.5f).mul(transform),
620 tmp(-0.5f,-0.5f,0.5f).mul(transform),tmp(-0.5f,0.5f,0.5f).mul(transform),tmp(0.5f,-0.5f,0.5f).mul(transform),tmp(0.5f,0.5f,0.5f).mul(transform));
625 public void box(float width, float height, float depth) {
626 box(matTmp1.setToScaling(width, height, depth));
630 public void box(float x, float y, float z, float width, float height, float depth) {
631 box(matTmp1.setToScaling(width, height, depth).trn(x, y, z));
635 public void circle(float width, float height, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, int divisions) {
636 circle(width, height, centerX, centerY, centerZ, normalX, normalY, normalZ, divisions, 0, 360);
640 public void circle(float width, float height, final Vector3 center, final Vector3 normal, int divisions) {
641 circle(width, height, center.x, center.y, center.z, normal.x, normal.y, normal.z, divisions);
645 public void circle(float width, float height, final Vector3 center, final Vector3 normal, final Vector3 tangent, final Vector3 binormal, int divisions) {
646 circle(width, height, center.x, center.y, center.z, normal.x, normal.y, normal.z, tangent.x, tangent.y, tangent.z, binormal.x, binormal.y, binormal.z, divisions);
650 public void circle(float width, float height, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, float tangentX, float tangentY, float tangentZ, float binormalX, float binormalY, float binormalZ, int divisions) {
651 circle(width, height, centerX, centerY, centerZ, normalX, normalY, normalZ, tangentX, tangentY, tangentZ, binormalX, binormalY, binormalZ, divisions, 0, 360);
655 public void circle(float width, float height, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, int divisions, float angleFrom, float angleTo) {
656 tempV1.set(normalX, normalY, normalZ).crs(0, 0, 1);
657 tempV2.set(normalX, normalY, normalZ).crs(0, 1, 0);
658 if (tempV2.len2() > tempV1.len2())
660 tempV2.set(tempV1.nor()).crs(normalX, normalY, normalZ).nor();
661 circle(width, height, centerX, centerY, centerZ, normalX, normalY, normalZ, tempV1.x, tempV1.y, tempV1.z, tempV2.x, tempV2.y, tempV2.z, divisions, angleFrom, angleTo);
665 public void circle(float width, float height, final Vector3 center, final Vector3 normal, int divisions, float angleFrom, float angleTo) {
666 circle(width, height, center.x, center.y, center.z, normal.x, normal.y, normal.z, divisions, angleFrom, angleTo);
670 public void circle(float width, float height, final Vector3 center, final Vector3 normal, final Vector3 tangent, final Vector3 binormal, int divisions, float angleFrom, float angleTo) {
671 circle(width, height, center.x, center.y, center.z, normal.x, normal.y, normal.z, tangent.x, tangent.y, tangent.z, binormal.x, binormal.y, binormal.z, divisions, angleFrom, angleTo);
675 public void circle(float width, float height, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, float tangentX, float tangentY, float tangentZ, float binormalX, float binormalY, float binormalZ, int divisions, float angleFrom, float angleTo) {
677 ellipse(width, height, 0, 0, centerX, centerY, centerZ, normalX, normalY, normalZ, tangentX, tangentY, tangentZ, binormalX, binormalY, binormalZ, divisions, angleFrom, angleTo);
681 public void ellipse(float width, float height, float innerWidth, float innerHeight, Vector3 center, Vector3 normal, int divisions) {
682 ellipse(width, height, innerWidth, innerHeight, center.x, center.y, center.z, normal.x, normal.y, normal.z, divisions, 0, 360);
686 public void ellipse(float width, float height, float innerWidth, float innerHeight, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, int divisions) {
687 ellipse(width, height, innerWidth, innerHeight, centerX, centerY, centerZ, normalX, normalY, normalZ, divisions, 0, 360);
691 public void ellipse(float width, float height, float innerWidth, float innerHeight, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, int divisions, float angleFrom, float angleTo) {
692 tempV1.set(normalX, normalY, normalZ).crs(0, 0, 1);
693 tempV2.set(normalX, normalY, normalZ).crs(0, 1, 0);
694 if (tempV2.len2() > tempV1.len2())
696 tempV2.set(tempV1.nor()).crs(normalX, normalY, normalZ).nor();
697 ellipse(width, height, innerWidth, innerHeight, centerX, centerY, centerZ, normalX, normalY, normalZ, tempV1.x, tempV1.y, tempV1.z, tempV2.x, tempV2.y, tempV2.z, divisions, angleFrom, angleTo);
701 public void ellipse(float width, float height, float innerWidth, float innerHeight, float centerX, float centerY, float centerZ, float normalX, float normalY, float normalZ, float tangentX, float tangentY, float tangentZ, float binormalX, float binormalY, float binormalZ, int divisions, float angleFrom, float angleTo) {
703 if(innerWidth <= 0 || innerHeight <= 0) {
704 ensureTriangles(divisions + 2, divisions);
706 else if (innerWidth == width && innerHeight == height){
707 ensureVertices(divisions + 1);
708 ensureIndices(divisions + 1);
709 if(primitiveType != GL10.GL_LINES)
710 throw new GdxRuntimeException("Incorrect primitive type : expect GL_LINES because innerWidth == width && innerHeight == height");
713 ensureRectangles((divisions + 1)*2, divisions + 1);
716 final float ao = MathUtils.degreesToRadians * angleFrom;
717 final float step = (MathUtils.degreesToRadians * (angleTo - angleFrom)) / divisions;
718 final Vector3 sxEx = tempV1.set(tangentX, tangentY, tangentZ).scl(width * 0.5f);
719 final Vector3 syEx = tempV2.set(binormalX, binormalY, binormalZ).scl(height * 0.5f);
720 final Vector3 sxIn = tempV3.set(tangentX, tangentY, tangentZ).scl(innerWidth * 0.5f);
721 final Vector3 syIn = tempV4.set(binormalX, binormalY, binormalZ).scl(innerHeight * 0.5f);
722 VertexInfo currIn = vertTmp3.set(null, null, null, null);
724 currIn.uv.set(.5f, .5f);
725 currIn.hasPosition = currIn.hasNormal = true;
726 currIn.position.set(centerX, centerY, centerZ);
727 currIn.normal.set(normalX, normalY, normalZ);
728 VertexInfo currEx = vertTmp4.set(null, null, null, null);
730 currEx.uv.set(.5f, .5f);
731 currEx.hasPosition = currEx.hasNormal = true;
732 currEx.position.set(centerX, centerY, centerZ);
733 currEx.normal.set(normalX, normalY, normalZ);
734 final short center = vertex(currEx);
736 for (int i = 0; i <= divisions; i++) {
737 angle = ao + step * i;
738 final float x = MathUtils.cos(angle);
739 final float y = MathUtils.sin(angle);
740 currEx.position.set(centerX, centerY, centerZ).add(sxEx.x*x+syEx.x*y, sxEx.y*x+syEx.y*y, sxEx.z*x+syEx.z*y);
741 currEx.uv.set(.5f + .5f * x, .5f + .5f * y);
744 if(innerWidth <= 0 || innerHeight <= 0) {
746 triangle((short)(vindex - 1), (short)(vindex - 2), center);
748 else if (innerWidth == width && innerHeight == height){
750 line((short)(vindex - 1), (short)(vindex - 2));
753 currIn.position.set(centerX, centerY, centerZ).add(sxIn.x*x+syIn.x*y, sxIn.y*x+syIn.y*y, sxIn.z*x+syIn.z*y);
754 currIn.uv.set(.5f + .5f * x, .5f + .5f * y);
758 rect((short)(vindex - 1), (short)(vindex - 2),(short)(vindex - 4), (short)(vindex - 3));
764 public void cylinder(float width, float height, float depth, int divisions) {
765 cylinder(width, height, depth, divisions, 0, 360);
769 public void cylinder(float width, float height, float depth, int divisions, float angleFrom, float angleTo) {
770 cylinder(width, height, depth, divisions, angleFrom, angleTo, true);
773 /** Add a cylinder */
774 public void cylinder(float width, float height, float depth, int divisions, float angleFrom, float angleTo, boolean close) {
775 // FIXME create better cylinder method (- axis on which to create the cylinder (matrix?))
776 final float hw = width * 0.5f;
777 final float hh = height * 0.5f;
778 final float hd = depth * 0.5f;
779 final float ao = MathUtils.degreesToRadians * angleFrom;
780 final float step = (MathUtils.degreesToRadians * (angleTo - angleFrom)) / divisions;
781 final float us = 1f / divisions;
784 VertexInfo curr1 = vertTmp3.set(null, null, null, null);
785 curr1.hasUV = curr1.hasPosition = curr1.hasNormal = true;
786 VertexInfo curr2 = vertTmp4.set(null, null, null, null);
787 curr2.hasUV = curr2.hasPosition = curr2.hasNormal = true;
789 ensureRectangles(2 * (divisions + 1), divisions);
790 for (int i = 0; i <= divisions; i++) {
791 angle = ao + step * i;
793 curr1.position.set(MathUtils.cos(angle) * hw, 0f, MathUtils.sin(angle) * hd);
794 curr1.normal.set(curr1.position).nor();
795 curr1.position.y = -hh;
797 curr2.position.set(curr1.position);
798 curr2.normal.set(curr1.normal);
799 curr2.position.y = hh;
804 rect((short)(vindex-3), (short)(vindex-1), (short)(vindex-2), (short)(vindex-4)); // FIXME don't duplicate lines and points
807 circle(width, depth, 0, hh, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, divisions, angleFrom, angleTo);
808 circle(width, depth, 0, -hh, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, divisions, 180f-angleTo, 180f-angleFrom);
813 public void cone(float width, float height, float depth, int divisions) {
814 cone(width, height, depth, divisions, 0, 360);
818 public void cone(float width, float height, float depth, int divisions, float angleFrom, float angleTo) {
819 // FIXME create better cylinder method (- axis on which to create the cone (matrix?))
820 ensureTriangles(divisions + 2, divisions);
822 final float hw = width * 0.5f;
823 final float hh = height * 0.5f;
824 final float hd = depth * 0.5f;
825 final float ao = MathUtils.degreesToRadians * angleFrom;
826 final float step = (MathUtils.degreesToRadians * (angleTo - angleFrom)) / divisions;
827 final float us = 1f / divisions;
830 VertexInfo curr1 = vertTmp3.set(null, null, null, null);
831 curr1.hasUV = curr1.hasPosition = curr1.hasNormal = true;
832 VertexInfo curr2 = vertTmp4.set(null, null, null, null).setPos(0,hh,0).setNor(0,1,0).setUV(0.5f, 0);
833 final int base = vertex(curr2);
834 for (int i = 0; i <= divisions; i++) {
835 angle = ao + step * i;
837 curr1.position.set(MathUtils.cos(angle) * hw, 0f, MathUtils.sin(angle) * hd);
838 curr1.normal.set(curr1.position).nor();
839 curr1.position.y = -hh;
844 triangle((short)base, (short)(vindex-1), (short)(vindex-2)); // FIXME don't duplicate lines and points
846 circle(width, depth, 0, -hh, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, divisions, 180f-angleTo, 180f-angleFrom);
850 public void sphere(float width, float height, float depth, int divisionsU, int divisionsV) {
851 sphere(width, height, depth, divisionsU, divisionsV, 0, 360, 0, 180);
855 public void sphere(final Matrix4 transform, float width, float height, float depth, int divisionsU, int divisionsV) {
856 sphere(transform, width, height, depth, divisionsU, divisionsV, 0, 360, 0, 180);
860 public void sphere(float width, float height, float depth, int divisionsU, int divisionsV, float angleUFrom, float angleUTo, float angleVFrom, float angleVTo) {
861 sphere(matTmp1.idt(), width, height, depth, divisionsU, divisionsV, angleUFrom, angleUTo, angleVFrom, angleVTo);
865 public void sphere(final Matrix4 transform, float width, float height, float depth, int divisionsU, int divisionsV, float angleUFrom, float angleUTo, float angleVFrom, float angleVTo) {
866 // FIXME create better sphere method (- only one vertex for each pole, - position)
867 final float hw = width * 0.5f;
868 final float hh = height * 0.5f;
869 final float hd = depth * 0.5f;
870 final float auo = MathUtils.degreesToRadians * angleUFrom;
871 final float stepU = (MathUtils.degreesToRadians * (angleUTo - angleUFrom)) / divisionsU;
872 final float avo = MathUtils.degreesToRadians * angleVFrom;
873 final float stepV = (MathUtils.degreesToRadians * (angleVTo - angleVFrom)) / divisionsV;
874 final float us = 1f / divisionsU;
875 final float vs = 1f / divisionsV;
880 VertexInfo curr1 = vertTmp3.set(null, null, null, null);
881 curr1.hasUV = curr1.hasPosition = curr1.hasNormal = true;
883 ensureRectangles((divisionsV + 1) * (divisionsU + 1), divisionsV * divisionsU);
884 for (int iv = 0; iv <= divisionsV; iv++) {
885 angleV = avo + stepV * iv;
887 final float t = MathUtils.sin(angleV);
888 final float h = MathUtils.cos(angleV) * hh;
889 for (int iu = 0; iu <= divisionsU; iu++) {
890 angleU = auo + stepU * iu;
892 curr1.position.set(MathUtils.cos(angleU) * hw * t, h, MathUtils.sin(angleU) * hd * t).mul(transform);
893 curr1.normal.set(curr1.position).nor();
896 if ((iv > 0) && (iu > 0)) // FIXME don't duplicate lines and points
897 rect((short)(vindex-1), (short)(vindex-2), (short)(vindex-(divisionsU+3)), (short)(vindex-(divisionsU+2)));
903 public void capsule(float radius, float height, int divisions) {
904 if (height < 2f * radius)
905 throw new GdxRuntimeException("Height must be at least twice the radius");
906 final float d = 2f * radius;
907 cylinder(d, height - d, d, divisions, 0, 360, false);
908 sphere(matTmp1.setToTranslation(0, .5f*(height-d), 0), d, d, d, divisions, divisions, 0, 360, 0, 90);
909 sphere(matTmp1.setToTranslation(0, -.5f*(height-d), 0), d, d, d, divisions, divisions, 0, 360, 90, 180);