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) {
676 ensureTriangles(divisions + 2, divisions);
678 final float ao = MathUtils.degreesToRadians * angleFrom;
679 final float step = (MathUtils.degreesToRadians * (angleTo - angleFrom)) / divisions;
680 final Vector3 sx = tempV1.set(tangentX, tangentY, tangentZ).scl(width * 0.5f);
681 final Vector3 sy = tempV2.set(binormalX, binormalY, binormalZ).scl(height * 0.5f);
682 VertexInfo curr = vertTmp3.set(null, null, null, null);
683 curr.hasUV = curr.hasPosition = curr.hasNormal = true;
684 curr.uv.set(.5f, .5f);
685 curr.position.set(centerX, centerY, centerZ);
686 curr.normal.set(normalX, normalY, normalZ);
687 final short center = vertex(curr);
689 for (int i = 0; i <= divisions; i++) {
690 angle = ao + step * i;
691 final float x = MathUtils.cos(angle);
692 final float y = MathUtils.sin(angle);
693 curr.uv.set(.5f + .5f * x, .5f + .5f * y);
694 curr.position.set(centerX, centerY, centerZ).add(sx.x*x+sy.x*y, sx.y*x+sy.y*y, sx.z*x+sy.z*y);
697 triangle((short)(vindex - 1), (short)(vindex - 2), center);
702 public void cylinder(float width, float height, float depth, int divisions) {
703 cylinder(width, height, depth, divisions, 0, 360);
707 public void cylinder(float width, float height, float depth, int divisions, float angleFrom, float angleTo) {
708 cylinder(width, height, depth, divisions, angleFrom, angleTo, true);
711 /** Add a cylinder */
712 public void cylinder(float width, float height, float depth, int divisions, float angleFrom, float angleTo, boolean close) {
713 // FIXME create better cylinder method (- axis on which to create the cylinder (matrix?))
714 final float hw = width * 0.5f;
715 final float hh = height * 0.5f;
716 final float hd = depth * 0.5f;
717 final float ao = MathUtils.degreesToRadians * angleFrom;
718 final float step = (MathUtils.degreesToRadians * (angleTo - angleFrom)) / divisions;
719 final float us = 1f / divisions;
722 VertexInfo curr1 = vertTmp3.set(null, null, null, null);
723 curr1.hasUV = curr1.hasPosition = curr1.hasNormal = true;
724 VertexInfo curr2 = vertTmp4.set(null, null, null, null);
725 curr2.hasUV = curr2.hasPosition = curr2.hasNormal = true;
727 ensureRectangles(2 * (divisions + 1), divisions);
728 for (int i = 0; i <= divisions; i++) {
729 angle = ao + step * i;
731 curr1.position.set(MathUtils.cos(angle) * hw, 0f, MathUtils.sin(angle) * hd);
732 curr1.normal.set(curr1.position).nor();
733 curr1.position.y = -hh;
735 curr2.position.set(curr1.position);
736 curr2.normal.set(curr1.normal);
737 curr2.position.y = hh;
742 rect((short)(vindex-3), (short)(vindex-1), (short)(vindex-2), (short)(vindex-4)); // FIXME don't duplicate lines and points
745 circle(width, depth, 0, hh, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, divisions, angleFrom, angleTo);
746 circle(width, depth, 0, -hh, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, divisions, 180f-angleTo, 180f-angleFrom);
751 public void cone(float width, float height, float depth, int divisions) {
752 cone(width, height, depth, divisions, 0, 360);
756 public void cone(float width, float height, float depth, int divisions, float angleFrom, float angleTo) {
757 // FIXME create better cylinder method (- axis on which to create the cone (matrix?))
758 ensureTriangles(divisions + 2, divisions);
760 final float hw = width * 0.5f;
761 final float hh = height * 0.5f;
762 final float hd = depth * 0.5f;
763 final float ao = MathUtils.degreesToRadians * angleFrom;
764 final float step = (MathUtils.degreesToRadians * (angleTo - angleFrom)) / divisions;
765 final float us = 1f / divisions;
768 VertexInfo curr1 = vertTmp3.set(null, null, null, null);
769 curr1.hasUV = curr1.hasPosition = curr1.hasNormal = true;
770 VertexInfo curr2 = vertTmp4.set(null, null, null, null).setPos(0,hh,0).setNor(0,1,0).setUV(0.5f, 0);
771 final int base = vertex(curr2);
772 for (int i = 0; i <= divisions; i++) {
773 angle = ao + step * i;
775 curr1.position.set(MathUtils.cos(angle) * hw, 0f, MathUtils.sin(angle) * hd);
776 curr1.normal.set(curr1.position).nor();
777 curr1.position.y = -hh;
782 triangle((short)base, (short)(vindex-1), (short)(vindex-2)); // FIXME don't duplicate lines and points
784 circle(width, depth, 0, -hh, 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, divisions, 180f-angleTo, 180f-angleFrom);
788 public void sphere(float width, float height, float depth, int divisionsU, int divisionsV) {
789 sphere(width, height, depth, divisionsU, divisionsV, 0, 360, 0, 180);
793 public void sphere(final Matrix4 transform, float width, float height, float depth, int divisionsU, int divisionsV) {
794 sphere(transform, width, height, depth, divisionsU, divisionsV, 0, 360, 0, 180);
798 public void sphere(float width, float height, float depth, int divisionsU, int divisionsV, float angleUFrom, float angleUTo, float angleVFrom, float angleVTo) {
799 sphere(matTmp1.idt(), width, height, depth, divisionsU, divisionsV, angleUFrom, angleUTo, angleVFrom, angleVTo);
803 public void sphere(final Matrix4 transform, float width, float height, float depth, int divisionsU, int divisionsV, float angleUFrom, float angleUTo, float angleVFrom, float angleVTo) {
804 // FIXME create better sphere method (- only one vertex for each pole, - position)
805 final float hw = width * 0.5f;
806 final float hh = height * 0.5f;
807 final float hd = depth * 0.5f;
808 final float auo = MathUtils.degreesToRadians * angleUFrom;
809 final float stepU = (MathUtils.degreesToRadians * (angleUTo - angleUFrom)) / divisionsU;
810 final float avo = MathUtils.degreesToRadians * angleVFrom;
811 final float stepV = (MathUtils.degreesToRadians * (angleVTo - angleVFrom)) / divisionsV;
812 final float us = 1f / divisionsU;
813 final float vs = 1f / divisionsV;
818 VertexInfo curr1 = vertTmp3.set(null, null, null, null);
819 curr1.hasUV = curr1.hasPosition = curr1.hasNormal = true;
821 ensureRectangles((divisionsV + 1) * (divisionsU + 1), divisionsV * divisionsU);
822 for (int iv = 0; iv <= divisionsV; iv++) {
823 angleV = avo + stepV * iv;
825 final float t = MathUtils.sin(angleV);
826 final float h = MathUtils.cos(angleV) * hh;
827 for (int iu = 0; iu <= divisionsU; iu++) {
828 angleU = auo + stepU * iu;
830 curr1.position.set(MathUtils.cos(angleU) * hw * t, h, MathUtils.sin(angleU) * hd * t).mul(transform);
831 curr1.normal.set(curr1.position).nor();
834 if ((iv > 0) && (iu > 0)) // FIXME don't duplicate lines and points
835 rect((short)(vindex-1), (short)(vindex-2), (short)(vindex-(divisionsU+3)), (short)(vindex-(divisionsU+2)));
841 public void capsule(float radius, float height, int divisions) {
842 if (height < 2f * radius)
843 throw new GdxRuntimeException("Height must be at least twice the radius");
844 final float d = 2f * radius;
845 cylinder(d, height - d, d, divisions, 0, 360, false);
846 sphere(matTmp1.setToTranslation(0, .5f*(height-d), 0), d, d, d, divisions, divisions, 0, 360, 0, 90);
847 sphere(matTmp1.setToTranslation(0, -.5f*(height-d), 0), d, d, d, divisions, divisions, 0, 360, 90, 180);