src.gov.nasa.worldwind.util.GeometryBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of worldwindx Show documentation
Show all versions of worldwindx Show documentation
World Wind is a collection of components that interactively display 3D geographic information within Java applications or applets.
/*
* Copyright (C) 2012 United States Government as represented by the Administrator of the
* National Aeronautics and Space Administration.
* All Rights Reserved.
*/
package gov.nasa.worldwind.util;
import com.jogamp.common.nio.Buffers;
import gov.nasa.worldwind.geom.*;
import javax.media.opengl.GL;
import javax.media.opengl.glu.*;
import java.nio.*;
import java.util.*;
/**
* @author dcollins
* @version $Id: GeometryBuilder.java 1171 2013-02-11 21:45:02Z dcollins $
*/
public class GeometryBuilder
{
public static final int OUTSIDE = 0;
public static final int INSIDE = 1;
public static final int COUNTER_CLOCKWISE = 0;
public static final int CLOCKWISE = 1;
public static final int TOP = 1;
public static final int BOTTOM = 2;
public static final int LEFT = 4;
public static final int RIGHT = 8;
/**
* Bit code indicating that the leader's location is inside the rectangle. Used by {@link
* #computeLeaderLocationCode(float, float, float, float, float, float)}
.
*/
protected static final int LEADER_LOCATION_INSIDE = 0;
/**
* Bit code indicating that the leader's location is above the rectangle. Used by {@link
* #computeLeaderLocationCode(float, float, float, float, float, float)}
.
*/
protected static final int LEADER_LOCATION_TOP = 1;
/**
* Bit code indicating that the leader's location is below the rectangle. Used by {@link
* #computeLeaderLocationCode(float, float, float, float, float, float)}
.
*/
protected static final int LEADER_LOCATION_BOTTOM = 2;
/**
* Bit code indicating that the leader's location is to the right of the rectangle. Used by {@link
* #computeLeaderLocationCode(float, float, float, float, float, float)}
.
*/
protected static final int LEADER_LOCATION_RIGHT = 4;
/**
* Bit code indicating that the leader's location is to the left of the rectangle. Used by {@link
* #computeLeaderLocationCode(float, float, float, float, float, float)}
.
*/
protected static final int LEADER_LOCATION_LEFT = 8;
private int orientation = OUTSIDE;
public GeometryBuilder()
{
}
public int getOrientation()
{
return this.orientation;
}
public void setOrientation(int orientation)
{
this.orientation = orientation;
}
//**************************************************************//
//******************** Sphere ********************************//
//**************************************************************//
public IndexedTriangleArray tessellateSphere(float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int[] indexArray = new int[ICOSAHEDRON_INDEX_COUNT];
float[] vertexArray = new float[3 * ICOSAHEDRON_VERTEX_COUNT];
System.arraycopy(icosahedronIndexArray, 0, indexArray, 0, ICOSAHEDRON_INDEX_COUNT);
System.arraycopy(icosahedronVertexArray, 0, vertexArray, 0, 3 * ICOSAHEDRON_VERTEX_COUNT);
// The static icosahedron tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (int index = 0; index < ICOSAHEDRON_INDEX_COUNT; index += 3)
{
int tmp = indexArray[index];
indexArray[index] = indexArray[index + 2];
indexArray[index + 2] = tmp;
}
}
// Start with a triangular tessellated icosahedron.
IndexedTriangleArray ita = new IndexedTriangleArray(
ICOSAHEDRON_INDEX_COUNT, indexArray, ICOSAHEDRON_VERTEX_COUNT, vertexArray);
// Subdivide the icosahedron a specified number of times. The subdivison step computes midpoints between
// adjacent vertices. These midpoints are not on the sphere, but must be moved onto the sphere. We normalize
// each midpoint vertex to acheive this.
for (int i = 0; i < subdivisions; i++)
{
this.subdivideIndexedTriangleArray(ita);
vertexArray = ita.getVertices();
for (int vertex = 0; vertex < ita.vertexCount; vertex++)
{
norm3AndSet(vertexArray, 3 * vertex);
}
}
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexArray = ita.getVertices();
for (int vertex = 0; vertex < ita.vertexCount; vertex++)
{
mul3AndSet(vertexArray, 3 * vertex, radius);
}
}
return ita;
}
public IndexedTriangleBuffer tessellateSphereBuffer(float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(ICOSAHEDRON_INDEX_COUNT);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * ICOSAHEDRON_VERTEX_COUNT);
indexBuffer.put(icosahedronIndexArray, 0, ICOSAHEDRON_INDEX_COUNT);
vertexBuffer.put(icosahedronVertexArray, 0, 3 * ICOSAHEDRON_VERTEX_COUNT);
// The static icosahedron tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (int index = 0; index < ICOSAHEDRON_INDEX_COUNT; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated icosahedron.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
ICOSAHEDRON_INDEX_COUNT, indexBuffer, ICOSAHEDRON_VERTEX_COUNT, vertexBuffer);
// Subdivide the icosahedron a specified number of times. The subdivison step computes midpoints between
// adjacent vertices. These midpoints are not on the sphere, but must be moved onto the sphere. We normalize
// each midpoint vertex to achieve this.
for (int i = 0; i < subdivisions; i++)
{
this.subdivideIndexedTriangleBuffer(itb);
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.getVertexCount(); vertex++)
{
norm3AndSet(vertexBuffer, 3 * vertex);
}
}
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
public IndexedTriangleBuffer tessellateEllipsoidBuffer(float a, float b, float c, int subdivisions)
{
IndexedTriangleBuffer itb = tessellateSphereBuffer(a, subdivisions);
// normalize 2nd and 3rd radii in terms of the first one
float bScale = b / a;
float cScale = c / a;
// scale Y and Z components of each vertex by appropriate scaling factor
FloatBuffer vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.getVertexCount(); vertex++)
{
// offset = 0 for x coord, 1 for y coord, etc.
mulAndSet(vertexBuffer, 3 * vertex, bScale, 2);
mulAndSet(vertexBuffer, 3 * vertex, cScale, 1);
}
itb.vertices.rewind();
return itb;
}
// Icosahedron tessellation taken from the
// OpenGL Programming Guide, Chapter 2, Example 2-13: Drawing an Icosahedron.
private static final int ICOSAHEDRON_INDEX_COUNT = 60;
private static final int ICOSAHEDRON_VERTEX_COUNT = 12;
private static final float X = 0.525731112119133606f;
private static final float Z = 0.850650808352039932f;
private static float[] icosahedronVertexArray =
{
-X, 0, Z,
X, 0, Z,
-X, 0, -Z,
X, 0, -Z,
0, Z, X,
0, Z, -X,
0, -Z, X,
0, -Z, -X,
Z, X, 0,
-Z, X, 0,
Z, -X, 0,
-Z, -X, 0
};
private static int[] icosahedronIndexArray =
{
1, 4, 0,
4, 9, 0,
4, 5, 9,
8, 5, 4,
1, 8, 4,
1, 10, 8,
10, 3, 8,
8, 3, 5,
3, 2, 5,
3, 7, 2,
3, 10, 7,
10, 6, 7,
6, 11, 7,
6, 0, 11,
6, 1, 0,
10, 1, 6,
11, 0, 9,
2, 11, 9,
5, 2, 9,
11, 2, 7
};
//**************************************************************//
//*********************** Box ********************************//
//**************************************************************//
// create the entire box
public IndexedTriangleBuffer tessellateBoxBuffer(float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(BOX_INDEX_COUNT);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * BOX_VERTEX_COUNT);
indexBuffer.put(boxIndexArray, 0, BOX_INDEX_COUNT);
vertexBuffer.put(boxVertexArray, 0, 3 * BOX_VERTEX_COUNT);
// The static box tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (int index = 0; index < BOX_INDEX_COUNT; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a tessellated box.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
BOX_INDEX_COUNT, indexBuffer, BOX_VERTEX_COUNT, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
// create only one face of the box
public IndexedTriangleBuffer tessellateBoxBuffer(int face, float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (face < 0 || face >= 6)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "face < 0 or face >= 6");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(BOX_INDEX_COUNT / 6);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * BOX_VERTEX_COUNT / 6);
// fill subset of index buffer
int[] subArray = new int[BOX_INDEX_COUNT / 6];
for (int i = 0; i < BOX_INDEX_COUNT / 6; i++)
{
subArray[i] = boxFacesIndexArray[face * BOX_INDEX_COUNT / 6 + i];
}
indexBuffer.put(subArray, 0, BOX_INDEX_COUNT / 6);
float[] vertexSubset = new float[3 * BOX_VERTEX_COUNT / 6];
for (int i = 0; i < 3 * BOX_VERTEX_COUNT / 6; i++)
{
vertexSubset[i] = boxVertexArray[face * 3 * BOX_VERTEX_COUNT / 6 + i];
}
vertexBuffer.put(vertexSubset, 0, 3 * BOX_VERTEX_COUNT / 6);
// The static box tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (int index = 0; index < BOX_INDEX_COUNT / 6; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a tessellated box.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
BOX_INDEX_COUNT / 6, indexBuffer, BOX_VERTEX_COUNT / 6, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
private static final int BOX_INDEX_COUNT = 36;
private static final int BOX_VERTEX_COUNT = 24;
private static final float B = 1.0f;
private static float[] boxVertexArray =
{ // right
B, -B, B, // 0
B, B, B, // 1
B, -B, -B, // 2
B, B, -B, // 3
// front
-B, B, B, // 4
B, B, B, // 5
-B, -B, B, // 6
B, -B, B, // 7
// left
-B, B, B, // 8
-B, -B, B, // 9
-B, B, -B, // 10
-B, -B, -B, // 11
// back
B, B, -B, // 12
-B, B, -B, // 13
B, -B, -B, // 14
-B, -B, -B, // 15
// top
B, B, B, // 16
-B, B, B, // 17
B, B, -B, // 18
-B, B, -B, // 19
// bottom
-B, -B, B, // 20
B, -B, B, // 21
-B, -B, -B, // 22
B, -B, -B // 23
};
private static int[] boxIndexArray =
{
2, 3, 1, // right
2, 1, 0,
4, 6, 7, // front
4, 7, 5,
8, 10, 11, // left
8, 11, 9,
12, 14, 15, // back
12, 15, 13,
16, 18, 19, // top
16, 19, 17,
20, 22, 23, // bottom
20, 23, 21,
};
private static int[] boxFacesIndexArray =
{
2, 3, 1, // right
2, 1, 0,
0, 2, 3, // front
0, 3, 1,
0, 2, 3, // left
0, 3, 1,
0, 2, 3, // back
0, 3, 1,
0, 2, 3, // top
0, 3, 1,
0, 2, 3, // bottom
0, 3, 1,
};
//**************************************************************//
//*********************** Pyramid ****************************//
//**************************************************************//
public IndexedTriangleBuffer tessellatePyramidBuffer(float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(PYRAMID_INDEX_COUNT);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * PYRAMID_VERTEX_COUNT);
indexBuffer.put(pyramidIndexArray, 0, PYRAMID_INDEX_COUNT);
vertexBuffer.put(pyramidVertexArray, 0, 3 * PYRAMID_VERTEX_COUNT);
// The static box tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (int index = 0; index < PYRAMID_INDEX_COUNT; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a tessellated pyramid.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
PYRAMID_INDEX_COUNT, indexBuffer, PYRAMID_VERTEX_COUNT, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
public IndexedTriangleBuffer tessellatePyramidBuffer(int face, float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// default values for pyramid side
int faceIndexCount = 3;
int faceVertexCount = 3;
int faceIndicesOffset = face * faceIndexCount;
int faceVerticesOffset = face * 3 * faceVertexCount;
if (face == 4) // the pyramid base
{
faceIndicesOffset = 4 * faceIndexCount;
faceVerticesOffset = 4 * 3 * faceVertexCount;
faceIndexCount = 6;
faceVertexCount = 4;
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(faceIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * faceVertexCount);
// fill subset of index buffer
int[] subArray = new int[faceIndexCount];
for (int i = 0; i < faceIndexCount; i++)
{
subArray[i] = pyramidFacesIndexArray[faceIndicesOffset + i];
}
indexBuffer.put(subArray, 0, faceIndexCount);
float[] vertexSubset = new float[3 * faceVertexCount];
for (int i = 0; i < 3 * faceVertexCount; i++)
{
vertexSubset[i] = pyramidVertexArray[faceVerticesOffset + i];
}
vertexBuffer.put(vertexSubset, 0, 3 * faceVertexCount);
// The static box tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (int index = 0; index < faceIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a tessellated pyramid.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
faceIndexCount, indexBuffer, faceVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
private static final int PYRAMID_INDEX_COUNT = 18;
private static final int PYRAMID_VERTEX_COUNT = 16;
private static final float P = 1.0f;
private static float[] pyramidVertexArray =
{ // right
0, 0, P, // 0 (point)
P, -P, -P, // 1
P, P, -P, // 2
// front
0, 0, P, // 3 (point)
-P, -P, -P, // 4
P, -P, -P, // 5
// left
0, 0, P, // 6 (point)
-P, P, -P, // 7
-P, -P, -P, // 8
// back
0, 0, P, // 9 (point)
P, P, -P, // 10
-P, P, -P, // 11
// bottom (base) face
P, P, -P, // 12
-P, P, -P, // 13
P, -P, -P, // 14
-P, -P, -P // 15
};
private static int[] pyramidIndexArray =
{
0, 1, 2, // right
3, 4, 5, // front
6, 7, 8, // left
9, 10, 11, // back
12, 14, 15, // base
12, 15, 13,
};
private static int[] pyramidFacesIndexArray =
{
0, 1, 2, // right
0, 1, 2, // front
0, 1, 2, // left
0, 1, 2, // back
0, 2, 3, // base
0, 3, 1,
};
//**************************************************************//
//******************** Unit Cylinder *******************//
//**************************************************************//
public IndexedTriangleBuffer tessellateCylinderBuffer(float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, index;
float x, y, z, a;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
int cylinderIndexCount = 12 * slices;
int cylinderVertexCount = 4 * slices + 4;
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(cylinderIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * cylinderVertexCount);
// VERTICES
// top and bottom center points
vertexBuffer.put(0, 0f);
vertexBuffer.put(1, 0f);
vertexBuffer.put(2, 1.0f);
vertexBuffer.put(3 * (slices + 1), 0f);
vertexBuffer.put(3 * (slices + 1) + 1, 0f);
vertexBuffer.put(3 * (slices + 1) + 2, -1.0f);
// rim points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * i + 3;
// cylinder top
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, z);
index += 3; // add 3 for the second center point
// cylinder bottom
vertexBuffer.put(index + 3 * slices, x * radius);
vertexBuffer.put(index + 3 * slices + 1, y * radius);
vertexBuffer.put(index + 3 * slices + 2, -z);
index += 6 * slices + 3 * i;
// core upper rim
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, z);
// core lower rim
vertexBuffer.put(index + 3, x * radius);
vertexBuffer.put(index + 4, y * radius);
vertexBuffer.put(index + 5, -z);
}
// extra vertices for seamless texture mapping
int wrapIndex = 3 * (4 * slices + 2);
x = (float) Math.sin(0);
y = (float) Math.cos(0);
z = 1.0f;
vertexBuffer.put(wrapIndex, x * radius);
vertexBuffer.put(wrapIndex + 1, y * radius);
vertexBuffer.put(wrapIndex + 2, z);
vertexBuffer.put(wrapIndex + 3, x * radius);
vertexBuffer.put(wrapIndex + 4, y * radius);
vertexBuffer.put(wrapIndex + 5, -z);
// INDICES
int coreIndex = (2 * slices) + 2;
int centerPoint = 0;
for (i = 0; i < slices; i++)
{
// cylinder top
index = 3 * i;
indexBuffer.put(index, 0); // center point
indexBuffer.put(index + 1, (i < slices - 1) ? i + 2 : 1);
indexBuffer.put(index + 2, i + 1);
// cylinder bottom
index = 3 * (slices + i);
indexBuffer.put(index, (slices + 1)); // center point
indexBuffer.put(index + 1, (i < slices - 1) ? (slices + 1) + i + 2 : (slices + 1) + 1);
indexBuffer.put(index + 2, (slices + 1) + i + 1);
// cylinder core
index = 6 * (slices + i);
indexBuffer.put(index, coreIndex);
indexBuffer.put(index + 1, coreIndex + 1);
indexBuffer.put(index + 2, coreIndex + 2);
indexBuffer.put(index + 3, coreIndex + 2);
indexBuffer.put(index + 4, coreIndex + 1);
indexBuffer.put(index + 5, coreIndex + 3);
coreIndex += 2;
}
// The static cylinder tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (index = 0; index < cylinderIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated cylinder.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
cylinderIndexCount, indexBuffer, cylinderVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
public IndexedTriangleBuffer tessellateCylinderBuffer(int face, float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, index;
float x, y, z, a;
// face 0 = top
// face 1 = bottom
// face 2 = round cylinder core
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
int cylinderIndexCount = 3 * slices;
int cylinderVertexCount = slices + 1;
if (face == 2) // cylinder core
{
cylinderIndexCount = 6 * slices;
cylinderVertexCount = 2 * slices + 2;
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(cylinderIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * cylinderVertexCount);
// VERTICES
if (face == 0 || face == 1) // top or bottom cylinder face
{
int isTop = 1;
if (face == 1)
isTop = -1;
// top center point
vertexBuffer.put(0, 0f);
vertexBuffer.put(1, 0f);
vertexBuffer.put(2, isTop * 1.0f);
// rim points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * i + 3;
// cylinder top
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, isTop * z);
}
}
else if (face == 2) // cylinder core
{
// rim points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 6 * i;
// core upper rim
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, z);
// core lower rim
vertexBuffer.put(index + 3, x * radius);
vertexBuffer.put(index + 4, y * radius);
vertexBuffer.put(index + 5, -z);
}
// extra vertices for seamless texture mapping
int wrapIndex = 3 * (2 * slices);
x = (float) Math.sin(0);
y = (float) Math.cos(0);
z = 1.0f;
vertexBuffer.put(wrapIndex, x * radius);
vertexBuffer.put(wrapIndex + 1, y * radius);
vertexBuffer.put(wrapIndex + 2, z);
vertexBuffer.put(wrapIndex + 3, x * radius);
vertexBuffer.put(wrapIndex + 4, y * radius);
vertexBuffer.put(wrapIndex + 5, -z);
}
// INDICES
int centerPoint = 0;
if (face == 0 || face == 1) // top or bottom cylinder face
{
for (i = 0; i < slices; i++)
{
index = 3 * i;
indexBuffer.put(index, 0); // center point
indexBuffer.put(index + 1, (i < slices - 1) ? i + 2 : 1);
indexBuffer.put(index + 2, i + 1);
}
}
else if (face == 2) // cylinder core
{
int coreIndex = 0;
for (i = 0; i < slices; i++)
{
index = 6 * i;
indexBuffer.put(index, coreIndex);
indexBuffer.put(index + 1, coreIndex + 1);
indexBuffer.put(index + 2, coreIndex + 2);
indexBuffer.put(index + 3, coreIndex + 2);
indexBuffer.put(index + 4, coreIndex + 1);
indexBuffer.put(index + 5, coreIndex + 3);
coreIndex += 2;
}
}
// The static cylinder tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (index = 0; index < cylinderIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated cylinder.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
cylinderIndexCount, indexBuffer, cylinderVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
//**************************************************************//
//******************** Wedge *******************//
//**************************************************************//
public IndexedTriangleBuffer tessellateWedgeBuffer(float radius, int subdivisions, Angle angle)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle.getRadians() < 0 || angle.getRadians() > 2 * Math.PI)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "angle < 0 or angle > 2 PI");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, index;
float x, y, z, a;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = (float) angle.getRadians() / slices;
int wedgeIndexCount = 12 * slices + 12;
int wedgeVertexCount = 4 * (slices + 1) + 2 + 8;
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(wedgeIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * wedgeVertexCount);
// VERTICES
// top and bottom center points
vertexBuffer.put(0, 0f);
vertexBuffer.put(1, 0f);
vertexBuffer.put(2, 1.0f);
vertexBuffer.put(3 * (slices + 2), 0f);
vertexBuffer.put(3 * (slices + 2) + 1, 0f);
vertexBuffer.put(3 * (slices + 2) + 2, -1.0f);
// rim points
for (i = 0; i <= slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * i + 3;
// wedge top
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, z);
index += 3; // add 3 for the second center point
// wedge bottom
vertexBuffer.put(index + 3 * (slices + 1), x * radius);
vertexBuffer.put(index + 3 * (slices + 1) + 1, y * radius);
vertexBuffer.put(index + 3 * (slices + 1) + 2, -z);
index = 3 * (2 * slices + 4 + 2 * i);
// core upper rim
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, z);
// core lower rim
vertexBuffer.put(index + 3, x * radius);
vertexBuffer.put(index + 4, y * radius);
vertexBuffer.put(index + 5, -z);
}
// wedge sides
for (i = 0; i < 2; i++)
{
x = (float) Math.sin(i * angle.getRadians());
y = (float) Math.cos(i * angle.getRadians());
z = 1.0f;
index = 3 * (4 * (slices + 1 + i) + 2);
// inner points
vertexBuffer.put(index, 0);
vertexBuffer.put(index + 1, 0);
vertexBuffer.put(index + 2, z);
vertexBuffer.put(index + 3, 0);
vertexBuffer.put(index + 4, 0);
vertexBuffer.put(index + 5, -z);
// outer points
vertexBuffer.put(index + 6, x * radius);
vertexBuffer.put(index + 7, y * radius);
vertexBuffer.put(index + 8, z);
vertexBuffer.put(index + 9, x * radius);
vertexBuffer.put(index + 10, y * radius);
vertexBuffer.put(index + 11, -z);
}
// INDICES
int coreIndex = 2 * (slices + 1) + 2;
for (i = 0; i < slices; i++)
{
// wedge top
index = 3 * i;
indexBuffer.put(index, 0); // center point
indexBuffer.put(index + 1, i + 2);
indexBuffer.put(index + 2, i + 1);
// wedge bottom
index = 3 * (slices + i);
indexBuffer.put(index, (slices + 2)); // center point
indexBuffer.put(index + 1, (slices + 2) + i + 2);
indexBuffer.put(index + 2, (slices + 2) + i + 1);
// wedge core
index = 6 * (slices + i);
indexBuffer.put(index + 0, coreIndex + 0);
indexBuffer.put(index + 1, coreIndex + 1);
indexBuffer.put(index + 2, coreIndex + 2);
indexBuffer.put(index + 3, coreIndex + 2);
indexBuffer.put(index + 4, coreIndex + 1);
indexBuffer.put(index + 5, coreIndex + 3);
coreIndex += 2;
}
// wedge sides
for (i = 0; i < 2; i++)
{
index = 3 * (4 * slices) + 6 * i;
coreIndex = 4 * (slices + 1) + 2 + i * 4;
indexBuffer.put(index + 0, coreIndex + 0);
indexBuffer.put(index + 1, coreIndex + 2);
indexBuffer.put(index + 2, coreIndex + 1);
indexBuffer.put(index + 3, coreIndex + 1);
indexBuffer.put(index + 4, coreIndex + 2);
indexBuffer.put(index + 5, coreIndex + 3);
}
// The static wedge tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (index = 0; index < wedgeIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated wedge.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
wedgeIndexCount, indexBuffer, wedgeVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
public IndexedTriangleBuffer tessellateWedgeBuffer(int face, float radius, int subdivisions, Angle angle)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (angle.getRadians() < 0 || angle.getRadians() > 2 * Math.PI)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "angle < 0 or angle > 2 PI");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, index;
float x, y, z, a;
// face 0 = top
// face 1 = bottom
// face 2 = round core wall
// face 3 = first wedge side
// face 4 = second wedge side
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = (float) angle.getRadians() / slices;
int wedgeIndexCount = 6;
int wedgeVertexCount = 4;
if (face == 0 || face == 1)
{
wedgeIndexCount = 3 * slices;
wedgeVertexCount = slices + 2;
}
else if (face == 2)
{
wedgeIndexCount = 6 * slices;
wedgeVertexCount = 2 * slices + 2;
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(wedgeIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * wedgeVertexCount);
// VERTICES
if (face == 0 || face == 1) // wedge top or bottom
{
int isTop = 1;
if (face == 1)
isTop = -1;
// center point
vertexBuffer.put(0, 0f);
vertexBuffer.put(1, 0f);
vertexBuffer.put(2, isTop * 1.0f);
// rim points
for (i = 0; i <= slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * i + 3;
// wedge top
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, isTop * z);
}
}
else if (face == 2) // round core wall
{
// rim points
for (i = 0; i <= slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * (2 * i);
// core upper rim
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, z);
// core lower rim
vertexBuffer.put(index + 3, x * radius);
vertexBuffer.put(index + 4, y * radius);
vertexBuffer.put(index + 5, -z);
}
}
else if (face == 3 || face == 4)
{
// wedge side
i = face - 3;
x = (float) Math.sin(i * angle.getRadians());
y = (float) Math.cos(i * angle.getRadians());
z = 1.0f;
index = 0;
// inner points
vertexBuffer.put(index, 0);
vertexBuffer.put(index + 1, 0);
vertexBuffer.put(index + 2, z);
vertexBuffer.put(index + 3, 0);
vertexBuffer.put(index + 4, 0);
vertexBuffer.put(index + 5, -z);
// outer points
vertexBuffer.put(index + 6, x * radius);
vertexBuffer.put(index + 7, y * radius);
vertexBuffer.put(index + 8, z);
vertexBuffer.put(index + 9, x * radius);
vertexBuffer.put(index + 10, y * radius);
vertexBuffer.put(index + 11, -z);
}
// INDICES
if (face == 0 || face == 1) // top or bottom
{
for (i = 0; i < slices; i++)
{
// wedge top
index = 3 * i;
indexBuffer.put(index, 0); // center point
indexBuffer.put(index + 1, i + 2);
indexBuffer.put(index + 2, i + 1);
}
}
else if (face == 2)
{
int coreIndex = 0;
for (i = 0; i < slices; i++)
{
// wedge core
index = 6 * i;
indexBuffer.put(index + 0, coreIndex + 0);
indexBuffer.put(index + 1, coreIndex + 1);
indexBuffer.put(index + 2, coreIndex + 2);
indexBuffer.put(index + 3, coreIndex + 2);
indexBuffer.put(index + 4, coreIndex + 1);
indexBuffer.put(index + 5, coreIndex + 3);
coreIndex += 2;
}
}
else if (face == 3 || face == 4)
{
// wedge side
indexBuffer.put(0, 0);
indexBuffer.put(1, 2);
indexBuffer.put(2, 1);
indexBuffer.put(3, 1);
indexBuffer.put(4, 2);
indexBuffer.put(5, 3);
}
// The static wedge tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (index = 0; index < wedgeIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated wedge.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
wedgeIndexCount, indexBuffer, wedgeVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
//**************************************************************//
//********************* Cone *******************//
//**************************************************************//
public IndexedTriangleBuffer tessellateConeBuffer(float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, index;
float x, y, z, a;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
int coneIndexCount = 12 * slices;
int coneVertexCount = 4 * slices + 4;
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(coneIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * coneVertexCount);
// VERTICES
// bottom center point
vertexBuffer.put(0, 0f);
vertexBuffer.put(1, 0f);
vertexBuffer.put(2, -1.0f);
// rim points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * i + 3;
// cone bottom
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, -z);
index += 3 * slices + 3 * i;
// core upper rim - all points are at same location
vertexBuffer.put(index, 0);
vertexBuffer.put(index + 1, 0);
vertexBuffer.put(index + 2, z);
// core lower rim
vertexBuffer.put(index + 3, x * radius);
vertexBuffer.put(index + 4, y * radius);
vertexBuffer.put(index + 5, -z);
}
// extra vertices for seamless texture mapping
int wrapIndex = 3 * (3 * slices + 1);
x = (float) Math.sin(0);
y = (float) Math.cos(0);
z = 1.0f;
vertexBuffer.put(wrapIndex, 0);
vertexBuffer.put(wrapIndex + 1, 0);
vertexBuffer.put(wrapIndex + 2, z);
vertexBuffer.put(wrapIndex + 3, x * radius);
vertexBuffer.put(wrapIndex + 4, y * radius);
vertexBuffer.put(wrapIndex + 5, -z);
// INDICES
int coreIndex = slices + 1;
int centerPoint = 0;
for (i = 0; i < slices; i++)
{
index = 3 * i;
// cone bottom
indexBuffer.put(index, 0); // center point
indexBuffer.put(index + 1, (i < slices - 1) ? i + 2 : 1);
indexBuffer.put(index + 2, i + 1);
// cone core
index += 3 * (slices + i);
indexBuffer.put(index, coreIndex);
indexBuffer.put(index + 1, coreIndex + 1);
indexBuffer.put(index + 2, coreIndex + 2);
indexBuffer.put(index + 3, coreIndex + 2);
indexBuffer.put(index + 4, coreIndex + 1);
indexBuffer.put(index + 5, coreIndex + 3);
coreIndex += 2;
}
// The static cone tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (index = 0; index < coneIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated cone.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
coneIndexCount, indexBuffer, coneVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
public IndexedTriangleBuffer tessellateConeBuffer(int face, float radius, int subdivisions)
{
if (radius < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "radius < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// face 0 = base
// face 1 = core
int i, index;
float x, y, z, a;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
int coneIndexCount = 3 * slices;
int coneVertexCount = slices + 1;
if (face == 1) // cone core
{
coneIndexCount = 6 * slices;
coneVertexCount = 2 * slices + 2;
}
IntBuffer indexBuffer = Buffers.newDirectIntBuffer(coneIndexCount);
FloatBuffer vertexBuffer = Buffers.newDirectFloatBuffer(3 * coneVertexCount);
// VERTICES
if (face == 0) // cone base
{
// base center point
vertexBuffer.put(0, 0f);
vertexBuffer.put(1, 0f);
vertexBuffer.put(2, -1.0f);
// base rim points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 3 * i + 3;
vertexBuffer.put(index, x * radius);
vertexBuffer.put(index + 1, y * radius);
vertexBuffer.put(index + 2, -z);
}
}
else if (face == 1) // cone core
{
// rim points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 1.0f;
index = 6 * i;
// core upper rim
vertexBuffer.put(index, 0);
vertexBuffer.put(index + 1, 0);
vertexBuffer.put(index + 2, z);
// core lower rim
vertexBuffer.put(index + 3, x * radius);
vertexBuffer.put(index + 4, y * radius);
vertexBuffer.put(index + 5, -z);
}
// extra vertices for seamless texture mapping
int wrapIndex = 3 * (2 * slices);
x = (float) Math.sin(0);
y = (float) Math.cos(0);
z = 1.0f;
vertexBuffer.put(wrapIndex, 0);
vertexBuffer.put(wrapIndex + 1, 0);
vertexBuffer.put(wrapIndex + 2, z);
vertexBuffer.put(wrapIndex + 3, x * radius);
vertexBuffer.put(wrapIndex + 4, y * radius);
vertexBuffer.put(wrapIndex + 5, -z);
}
// INDICES
int centerPoint = 0;
if (face == 0) // cone base
{
for (i = 0; i < slices; i++)
{
index = 3 * i;
indexBuffer.put(index, 0); // center point
indexBuffer.put(index + 1, (i < slices - 1) ? i + 2 : 1);
indexBuffer.put(index + 2, i + 1);
}
}
else if (face == 1) // cone core
{
int coreIndex = 0;
for (i = 0; i < slices; i++)
{
index = 6 * i;
indexBuffer.put(index, coreIndex);
indexBuffer.put(index + 1, coreIndex + 1);
indexBuffer.put(index + 2, coreIndex + 2);
indexBuffer.put(index + 3, coreIndex + 2);
indexBuffer.put(index + 4, coreIndex + 1);
indexBuffer.put(index + 5, coreIndex + 3);
coreIndex += 2;
}
}
// The static cone tessellation is assumed to be viewed from the outside. If the orientation is set to
// inside, then we must reverse the winding order for each triangle's indices.
if (this.orientation == INSIDE)
{
for (index = 0; index < coneIndexCount; index += 3)
{
int tmp = indexBuffer.get(index);
indexBuffer.put(index, indexBuffer.get(index + 2));
indexBuffer.put(index + 2, tmp);
}
}
// Start with a triangular tessellated cone.
IndexedTriangleBuffer itb = new IndexedTriangleBuffer(
coneIndexCount, indexBuffer, coneVertexCount, vertexBuffer);
// Scale each vertex by the specified radius.
if (radius != 1)
{
vertexBuffer = itb.getVertices();
for (int vertex = 0; vertex < itb.vertexCount; vertex++)
{
mul3AndSet(vertexBuffer, 3 * vertex, radius);
}
}
itb.vertices.rewind();
itb.indices.rewind();
return itb;
}
//**************************************************************//
//******************** Cylinder *******************//
//**************************************************************//
public int getCylinderVertexCount(int slices, int stacks)
{
return slices * (stacks + 1);
}
public int getCylinderIndexCount(int slices, int stacks)
{
return stacks * 2 * (slices + 1) + 2 * (stacks - 1);
}
@SuppressWarnings({"UnusedDeclaration"})
public int getCylinderOutlineIndexCount(int slices, int stacks)
{
return slices * 4;
}
public int getCylinderDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public int getCylinderOutlineDrawMode()
{
return GL.GL_LINES;
}
public void makeCylinderVertices(float radius, float height, int slices, int stacks, float[] dest)
{
int numPoints = this.getCylinderVertexCount(slices, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y, z;
float a;
float dz, da;
int i, j;
int index;
if (stacks != 0.0f)
dz = height / (float) stacks;
else
dz = 0.0f;
da = 2.0f * (float) Math.PI / (float) slices;
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 0.0f;
for (j = 0; j <= stacks; j++)
{
index = j + i * (stacks + 1);
index = 3 * index;
dest[index] = x * radius;
dest[index + 1] = y * radius;
dest[index + 2] = z;
z += dz;
}
}
}
public void makeCylinderNormals(int slices, int stacks, float[] dest)
{
int numPoints = this.getCylinderVertexCount(slices, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a;
float da;
float nsign;
int i, j;
int index;
float[] norm;
da = 2.0f * (float) Math.PI / (float) slices;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
norm[0] = x * nsign;
norm[1] = y * nsign;
norm[2] = 0.0f;
this.norm3AndSet(norm, 0);
for (j = 0; j <= stacks; j++)
{
index = j + i * (stacks + 1);
index = 3 * index;
System.arraycopy(norm, 0, dest, index, 3);
}
}
}
public void makeCylinderIndices(int slices, int stacks, int[] dest)
{
int numIndices = this.getCylinderIndexCount(slices, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, j;
int vertex, index;
index = 0;
for (j = 0; j < stacks; j++)
{
if (j != 0)
{
if (this.orientation == INSIDE)
vertex = j + 1;
else // (this.orientation == OUTSIDE)
vertex = j;
dest[index++] = vertex;
dest[index++] = vertex;
}
for (i = 0; i <= slices; i++)
{
if (i == slices)
vertex = j;
else
vertex = j + i * (stacks + 1);
if (this.orientation == INSIDE)
{
dest[index++] = vertex + 1;
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + 1;
}
}
}
}
public void makeCylinderOutlineIndices(int slices, int stacks, int[] dest)
{
int numIndices = this.getCylinderOutlineIndexCount(slices, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i;
int vertex, index;
index = 0;
// Bottom ring
for (i = 0; i < slices; i++)
{
vertex = i * (stacks + 1);
dest[index++] = vertex;
dest[index++] = (i != slices - 1) ? vertex + stacks + 1 : 0;
}
// Top ring
for (i = 0; i < slices; i++)
{
vertex = i * (stacks + 1) + stacks;
dest[index++] = vertex;
dest[index++] = (i != slices - 1) ? vertex + stacks + 1 : stacks;
}
// // Vertical edges
// for (i = 0; i < slices; i++)
// {
// vertex = i * (stacks + 1);
// dest[index++] = vertex;
// dest[index++] = vertex + stacks;
// }
}
//**************************************************************//
//******************** Partial Cylinder ********************//
//**************************************************************//
public int getPartialCylinderVertexCount(int slices, int stacks)
{
return (slices + 1) * (stacks + 1);
}
public int getPartialCylinderIndexCount(int slices, int stacks)
{
return stacks * 2 * (slices + 1) + 2 * (stacks - 1);
}
@SuppressWarnings({"UnusedDeclaration"})
public int getPartialCylinderOutlineIndexCount(int slices, int stacks)
{
return slices * 4;
}
public int getPartialCylinderDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public int getPartialCylinderOutlineDrawMode()
{
return GL.GL_LINES;
}
public void makePartialCylinderVertices(float radius, float height, int slices, int stacks,
float start, float sweep, float[] dest)
{
int numPoints = this.getPartialCylinderVertexCount(slices, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y, z;
float a;
float dz, da;
int i, j;
int index;
if (stacks != 0.0f)
dz = height / (float) stacks;
else
dz = 0.0f;
da = sweep / (float) slices;
for (i = 0; i <= slices; i++)
{
a = i * da + start;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 0.0f;
for (j = 0; j <= stacks; j++)
{
index = j + i * (stacks + 1);
index = 3 * index;
dest[index] = x * radius;
dest[index + 1] = y * radius;
dest[index + 2] = z;
z += dz;
}
}
}
@SuppressWarnings({"UnusedDeclaration"})
public void makePartialCylinderNormals(float radius, float height, int slices, int stacks,
float start, float sweep, float[] dest)
{
int numPoints = this.getPartialCylinderVertexCount(slices, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a;
float da;
float nsign;
int i, j;
int index;
float[] norm;
da = sweep / (float) slices;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
for (i = 0; i <= slices; i++)
{
a = i * da + start;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
norm[0] = x * nsign;
norm[1] = y * nsign;
norm[2] = 0.0f;
this.norm3AndSet(norm, 0);
for (j = 0; j <= stacks; j++)
{
index = j + i * (stacks + 1);
index = 3 * index;
System.arraycopy(norm, 0, dest, index, 3);
}
}
}
public void makePartialCylinderIndices(int slices, int stacks, int[] dest)
{
int numIndices = this.getPartialCylinderIndexCount(slices, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, j;
int vertex, index;
index = 0;
for (j = 0; j < stacks; j++)
{
if (j != 0)
{
if (this.orientation == INSIDE)
{
vertex = j + slices * (stacks + 1);
dest[index++] = vertex - 1;
vertex = j + 1;
dest[index++] = vertex;
}
else //(this.orientation == OUTSIDE)
{
vertex = j + slices * (stacks + 1);
dest[index++] = vertex;
vertex = j;
dest[index++] = vertex;
}
}
for (i = 0; i <= slices; i++)
{
vertex = j + i * (stacks + 1);
if (this.orientation == INSIDE)
{
dest[index++] = vertex + 1;
dest[index++] = vertex;
}
else //(this.orientation == OUTSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + 1;
}
}
}
}
public void makePartialCylinderOutlineIndices(int slices, int stacks, int[] dest)
{
int numIndices = this.getPartialCylinderOutlineIndexCount(slices, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i;
int vertex, index;
index = 0;
// Bottom ring
for (i = 0; i < slices; i++)
{
vertex = i * (stacks + 1);
dest[index++] = vertex;
dest[index++] = vertex + stacks + 1;
}
// Top ring
for (i = 0; i < slices; i++)
{
vertex = i * (stacks + 1) + stacks;
dest[index++] = vertex;
dest[index++] = vertex + stacks + 1;
}
}
//**************************************************************//
//******************** Disk ********************//
//**************************************************************//
public int getDiskVertexCount(int slices, int loops)
{
return slices * (loops + 1);
}
public int getDiskIndexCount(int slices, int loops)
{
return loops * 2 * (slices + 1) + 2 * (loops - 1);
}
public int getDiskDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public void makeDiskVertices(float innerRadius, float outerRadius, int slices, int loops, float[] dest)
{
int numPoints = this.getDiskVertexCount(slices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a, r;
float da, dr;
int s, l;
int index;
da = 2.0f * (float) Math.PI / (float) slices;
dr = (outerRadius - innerRadius) / (float) loops;
for (s = 0; s < slices; s++)
{
a = s * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
for (l = 0; l <= loops; l++)
{
index = l + s * (loops + 1);
index = 3 * index;
r = innerRadius + l * dr;
dest[index] = r * x;
dest[index + 1] = r * y;
dest[index + 2] = 0.0f;
}
}
}
public void makeDiskNormals(int slices, int loops, float[] dest)
{
int numPoints = this.getDiskVertexCount(slices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int s, l;
int index;
float nsign;
float[] normal;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
normal = new float[3];
normal[0] = 0.0f;
normal[1] = 0.0f;
//noinspection PointlessArithmeticExpression
normal[2] = 1.0f * nsign;
for (s = 0; s < slices; s++)
{
for (l = 0; l <= loops; l++)
{
index = l + s * (loops + 1);
index = 3 * index;
System.arraycopy(normal, 0, dest, index, 3);
}
}
}
@SuppressWarnings({"UnusedDeclaration"})
public void makeDiskVertexNormals(float innerRadius, float outerRadius, int slices, int loops,
float[] srcVerts, float[] dest)
{
int numPoints = this.getDiskVertexCount(slices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (srcVerts == null)
{
String message = "nullValue.SourceVertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int s, l;
int index;
float nsign;
float[] norm, zero, tmp;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
zero = new float[3];
tmp = new float[3];
for (l = 0; l <= loops; l++)
{
// Normal vectors for first and last loops require a special case.
if (l == 0 || l == loops)
{
// Closed disk: all slices share a common center point.
if (l == 0 && innerRadius == 0.0f)
{
// Compute common center point normal.
int nextSlice;
int adjacentLoop;
System.arraycopy(zero, 0, norm, 0, 3);
for (s = 0; s < slices; s++)
{
index = l + s * (loops + 1);
nextSlice = l + (s + 1) * (loops + 1);
if (s == slices - 1)
nextSlice = l;
adjacentLoop = index + 1;
this.facenorm(srcVerts, index, nextSlice + 1, adjacentLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
// Copy common normal to the first point of each slice.
for (s = 0; s < slices; s++)
{
index = l + s * (loops + 1);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
// Open disk: each slice has a unique starting point.
else
{
for (s = 0; s < slices; s++)
{
int prevSlice, nextSlice;
int adjacentLoop;
index = l + s * (loops + 1);
prevSlice = l + (s - 1) * (loops + 1);
nextSlice = l + (s + 1) * (loops + 1);
if (s == 0)
prevSlice = l + (slices - 1) * (loops + 1);
else if (s == slices - 1)
nextSlice = l;
if (l == 0)
adjacentLoop = index + 1;
else
adjacentLoop = index - 1;
System.arraycopy(zero, 0, norm, 0, 3);
// Add clockwise adjacent face.
if (l == 0)
this.facenorm(srcVerts, index, nextSlice, adjacentLoop, tmp);
else
this.facenorm(srcVerts, index, adjacentLoop, nextSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add counter-clockwise adjacent face.
if (l == 0)
this.facenorm(srcVerts, index, adjacentLoop, prevSlice, tmp);
else
this.facenorm(srcVerts, index, prevSlice, adjacentLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
// Normal vectors for internal loops.
else
{
for (s = 0; s < slices; s++)
{
int prevSlice, nextSlice;
int prevLoop, nextLoop;
index = l + s * (loops + 1);
prevSlice = l + (s - 1) * (loops + 1);
nextSlice = l + (s + 1) * (loops + 1);
if (s == 0)
prevSlice = l + (slices - 1) * (loops + 1);
else if (s == slices - 1)
nextSlice = l;
prevLoop = index - 1;
nextLoop = index + 1;
System.arraycopy(zero, 0, norm, 0, 3);
// Add lower-left adjacent face.
this.facenorm(srcVerts, index, prevSlice, prevSlice - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, prevSlice - 1, prevLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add lower-right adjacent face.
this.facenorm(srcVerts, index, prevLoop, nextSlice - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, nextSlice - 1, nextSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add upper-right adjacent face.
this.facenorm(srcVerts, index, nextSlice, nextSlice + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, nextSlice + 1, nextLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add upper-left adjacent face.
this.facenorm(srcVerts, index, nextLoop, prevSlice + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, prevSlice + 1, prevSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
}
public void makeDiskIndices(int slices, int loops, int[] dest)
{
int numIndices = this.getDiskIndexCount(slices, loops);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int s, l;
int vertex, index;
index = 0;
for (l = 0; l < loops; l++)
{
if (l != 0)
{
if (this.orientation == INSIDE)
{
vertex = l;
dest[index++] = vertex;
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
vertex = l - 1;
dest[index++] = vertex;
vertex = l + 1;
dest[index++] = vertex;
}
}
for (s = 0; s <= slices; s++)
{
if (s == slices)
vertex = l;
else
vertex = l + s * (loops + 1);
if (this.orientation == INSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + 1;
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertex + 1;
dest[index++] = vertex;
}
}
}
}
//**************************************************************//
//******************** Partial Disk ********************//
//**************************************************************//
public int getPartialDiskVertexCount(int slices, int loops)
{
return (slices + 1) * (loops + 1);
}
public int getPartialDiskIndexCount(int slices, int loops)
{
return loops * 2 * (slices + 1) + 2 * (loops - 1);
}
public int getPartialDiskDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public void makePartialDiskVertices(float innerRadius, float outerRadius, int slices, int loops,
float start, float sweep, float[] dest)
{
int numPoints = this.getPartialDiskVertexCount(slices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a, r;
float da, dr;
int s, l;
int index;
da = sweep / (float) slices;
dr = (outerRadius - innerRadius) / (float) loops;
for (s = 0; s <= slices; s++)
{
a = s * da + start;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
for (l = 0; l <= loops; l++)
{
index = l + s * (loops + 1);
index = 3 * index;
r = innerRadius + l * dr;
dest[index] = r * x;
dest[index + 1] = r * y;
dest[index + 2] = 0.0f;
}
}
}
public void makePartialDiskNormals(int slices, int loops, float[] dest)
{
int numPoints = this.getPartialDiskVertexCount(slices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int s, l;
int index;
float nsign;
float[] normal;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
normal = new float[3];
normal[0] = 0.0f;
normal[1] = 0.0f;
//noinspection PointlessArithmeticExpression
normal[2] = 1.0f * nsign;
for (s = 0; s <= slices; s++)
{
for (l = 0; l <= loops; l++)
{
index = l + s * (loops + 1);
index = 3 * index;
System.arraycopy(normal, 0, dest, index, 3);
}
}
}
@SuppressWarnings({"UnusedDeclaration"})
public void makePartialDiskVertexNormals(float innerRadius, float outerRadius, int slices, int loops,
float start, float sweep, float[] srcVerts, float[] dest)
{
int numPoints = this.getPartialDiskVertexCount(slices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (srcVerts == null)
{
String message = "nullValue.SourceVertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int s, l;
int index;
float nsign;
float[] norm, zero, tmp;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
zero = new float[3];
tmp = new float[3];
for (l = 0; l <= loops; l++)
{
// Normal vectors for first and last loops require a special case.
if (l == 0 || l == loops)
{
// Closed disk: all slices share a common center point.
if (l == 0 && innerRadius == 0.0f)
{
// Compute common center point normal.
int nextSlice;
int adjacentLoop;
System.arraycopy(zero, 0, norm, 0, 3);
for (s = 0; s < slices; s++)
{
index = l + s * (loops + 1);
nextSlice = l + (s + 1) * (loops + 1);
adjacentLoop = index + 1;
this.facenorm(srcVerts, index, nextSlice + 1, adjacentLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
// Copy common normal to the first point of each slice.
for (s = 0; s <= slices; s++)
{
index = l + s * (loops + 1);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
// Open disk: each slice has a unique starting point.
else
{
for (s = 0; s <= slices; s++)
{
int prevSlice, nextSlice;
int adjacentLoop;
index = l + s * (loops + 1);
if (l == 0)
adjacentLoop = index + 1;
else
adjacentLoop = index - 1;
System.arraycopy(zero, 0, norm, 0, 3);
if (s > 0)
{
prevSlice = l + (s - 1) * (loops + 1);
// Add counter-clockwise adjacent face.
if (l == 0)
this.facenorm(srcVerts, index, adjacentLoop, prevSlice, tmp);
else
this.facenorm(srcVerts, index, prevSlice, adjacentLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
if (s < slices)
{
nextSlice = l + (s + 1) * (loops + 1);
// Add clockwise adjacent face.
if (l == 0)
this.facenorm(srcVerts, index, nextSlice, adjacentLoop, tmp);
else
this.facenorm(srcVerts, index, adjacentLoop, nextSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
// Normal vectors for internal loops.
else
{
for (s = 0; s <= slices; s++)
{
int prevSlice, nextSlice;
int prevLoop, nextLoop;
index = l + s * (loops + 1);
prevLoop = index - 1;
nextLoop = index + 1;
System.arraycopy(zero, 0, norm, 0, 3);
if (s > 0)
{
prevSlice = l + (s - 1) * (loops + 1);
// Add lower-left adjacent face.
this.facenorm(srcVerts, index, prevSlice, prevSlice - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, prevSlice - 1, prevLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add upper-left adjacent face.
this.facenorm(srcVerts, index, nextLoop, prevSlice + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, prevSlice + 1, prevSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
if (s < slices)
{
nextSlice = l + (s + 1) * (loops + 1);
// Add lower-right adjacent face.
this.facenorm(srcVerts, index, prevLoop, nextSlice - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, nextSlice - 1, nextSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add upper-right adjacent face.
this.facenorm(srcVerts, index, nextSlice, nextSlice + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, nextSlice + 1, nextLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
}
public void makePartialDiskIndices(int slices, int loops, int[] dest)
{
int numIndices = this.getPartialDiskIndexCount(slices, loops);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "slices=" + slices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int s, l;
int vertex, index;
index = 0;
for (l = 0; l < loops; l++)
{
if (l != 0)
{
if (this.orientation == INSIDE)
{
vertex = l + slices * (loops + 1);
dest[index++] = vertex;
vertex = l;
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
vertex = (l - 1) + slices * (loops + 1);
dest[index++] = vertex;
vertex = l;
dest[index++] = vertex + 1;
}
}
for (s = 0; s <= slices; s++)
{
vertex = l + s * (loops + 1);
if (this.orientation == INSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + 1;
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertex + 1;
dest[index++] = vertex;
}
}
}
}
//**************************************************************//
//******************** Radial Wall ********************//
//**************************************************************//
public int getRadialWallVertexCount(int pillars, int stacks)
{
return (pillars + 1) * (stacks + 1);
}
public int getRadialWallIndexCount(int pillars, int stacks)
{
return stacks * 2 * (pillars + 1) + 2 * (stacks - 1);
}
@SuppressWarnings({"UnusedDeclaration"})
public int getRadialWallOutlineIndexCount(int pillars, int stacks)
{
return pillars * 4;
}
public int getRadialWallDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public int getRadialWallOutlineDrawMode()
{
return GL.GL_LINES;
}
public void makeRadialWallVertices(float innerRadius, float outerRadius, float height, float angle,
int pillars, int stacks, float[] dest)
{
int numPoints = this.getRadialWallVertexCount(pillars, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "pillars=" + pillars
+ " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y, z;
float a, r;
float dz, dr;
int s, p;
int index;
a = angle;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
z = 0.0f;
if (stacks != 0.0f)
dz = height / (float) stacks;
else
dz = 0.0f;
dr = (outerRadius - innerRadius) / (float) pillars;
for (s = 0; s <= stacks; s++)
{
for (p = 0; p <= pillars; p++)
{
index = p + s * (pillars + 1);
index = 3 * index;
r = innerRadius + p * dr;
dest[index] = r * x;
dest[index + 1] = r * y;
dest[index + 2] = z;
}
z += dz;
}
}
@SuppressWarnings({"UnusedDeclaration"})
public void makeRadialWallNormals(float innerRadius, float outerRadius, float height, float angle,
int pillars, int stacks, float[] dest)
{
int numPoints = this.getRadialWallVertexCount(pillars, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "pillars=" + pillars
+ " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a;
int s, p;
int index;
float nsign;
float[] norm;
a = angle;
x = (float) Math.cos(a);
y = (float) -Math.sin(a);
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
norm[0] = x * nsign;
norm[1] = y * nsign;
norm[2] = 0.0f;
this.norm3AndSet(norm, 0);
for (s = 0; s <= stacks; s++)
{
for (p = 0; p <= pillars; p++)
{
index = p + s * (pillars + 1);
index = 3 * index;
System.arraycopy(norm, 0, dest, index, 3);
}
}
}
public void makeRadialWallIndices(int pillars, int stacks, int[] dest)
{
int numIndices = this.getRadialWallIndexCount(pillars, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "pillars=" + pillars
+ " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int p, s;
int vertex, index;
index = 0;
for (s = 0; s < stacks; s++)
{
if (s != 0)
{
if (this.orientation == INSIDE)
{
vertex = pillars + s * (pillars + 1);
dest[index++] = vertex;
vertex = s * (pillars + 1);
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
vertex = pillars + (s - 1) * (pillars + 1);
dest[index++] = vertex;
vertex = (s + 1) * (pillars + 1);
dest[index++] = vertex;
}
}
for (p = 0; p <= pillars; p++)
{
vertex = p + s * (pillars + 1);
if (this.orientation == INSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + (pillars + 1);
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertex + (pillars + 1);
dest[index++] = vertex;
}
}
}
}
public void makeRadialWallOutlineIndices(int pillars, int stacks, int[] dest)
{
int numIndices = this.getRadialWallOutlineIndexCount(pillars, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "pillars=" + pillars
+ " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int vertex;
int index = 0;
// Bottom
for (int i = 0; i < pillars; i++)
{
vertex = i;
dest[index++] = vertex;
dest[index++] = vertex + 1;
}
// Top
for (int i = 0; i < pillars; i++)
{
vertex = i + stacks * (pillars + 1);
dest[index++] = vertex;
dest[index++] = vertex + 1;
}
}
//**************************************************************//
//******************** Long Cylinder ********************//
//**************************************************************//
public int getLongCylinderVertexCount(int arcSlices, int lengthSlices, int stacks)
{
int slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
return slices * (stacks + 1);
}
public int getLongCylinderIndexCount(int arcSlices, int lengthSlices, int stacks)
{
int slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
return stacks * 2 * (slices + 1) + 2 * (stacks - 1);
}
@SuppressWarnings({"UnusedDeclaration"})
public int getLongCylinderOutlineIndexCount(int arcSlices, int lengthSlices, int stacks)
{
return (arcSlices + lengthSlices) * 2 * 4;
}
public int getLongCylinderDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public int getLongCylinderOutlineDrawMode()
{
return GL.GL_LINES;
}
public void makeLongCylinderVertices(float radius, float length, float height,
int arcSlices, int lengthSlices, int stacks, float[] dest)
{
int numPoints = this.getLongCylinderVertexCount(arcSlices, lengthSlices, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y, z;
float a;
float dy, dz, da;
int i, j;
int index;
da = (float) Math.PI / (float) arcSlices;
dy = length / (float) lengthSlices;
if (stacks != 0.0f)
dz = height / (float) stacks;
else
dz = 0.0f;
z = 0.0f;
index = 0;
for (j = 0; j <= stacks; j++)
{
// Top arc
for (i = 0; i <= arcSlices; i++)
{
a = i * da + (3.0f * (float) Math.PI / 2.0f);
x = (float) Math.sin(a);
y = (float) Math.cos(a);
dest[index++] = x * radius;
dest[index++] = y * radius + length;
dest[index++] = z;
}
// Right side.
for (i = lengthSlices - 1; i >= 1; i--)
{
dest[index++] = radius;
dest[index++] = i * dy;
dest[index++] = z;
}
// Bottom arc
for (i = 0; i <= arcSlices; i++)
{
a = i * da + ((float) Math.PI / 2.0f);
x = (float) Math.sin(a);
y = (float) Math.cos(a);
dest[index++] = x * radius;
dest[index++] = y * radius;
dest[index++] = z;
}
// Left side.
for (i = 1; i < lengthSlices; i++)
{
dest[index++] = -radius;
dest[index++] = i * dy;
dest[index++] = z;
}
z += dz;
}
}
public void makeLongCylinderNormals(int arcSlices, int lengthSlices, int stacks, float[] dest)
{
int numPoints = this.getLongCylinderVertexCount(arcSlices, lengthSlices, stacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a, da;
float nsign;
int i, j;
int index;
da = (float) Math.PI / (float) arcSlices;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
index = 0;
for (j = 0; j <= stacks; j++)
{
// Top arc
for (i = 0; i <= arcSlices; i++)
{
a = i * da + (3.0f * (float) Math.PI / 2.0f);
x = (float) Math.sin(a);
y = (float) Math.cos(a);
dest[index++] = x * nsign;
dest[index++] = y * nsign;
dest[index++] = 0.0f;
}
// Right side.
for (i = lengthSlices - 1; i >= 1; i--)
{
//noinspection PointlessArithmeticExpression
dest[index++] = 1.0f * nsign;
dest[index++] = 0.0f;
dest[index++] = 0.0f;
}
// Bottom arc
for (i = 0; i <= arcSlices; i++)
{
a = i * da + ((float) Math.PI / 2.0f);
x = (float) Math.sin(a);
y = (float) Math.cos(a);
dest[index++] = x * nsign;
dest[index++] = y * nsign;
dest[index++] = 0.0f;
}
// Left side.
for (i = 1; i < lengthSlices; i++)
{
dest[index++] = -1.0f * nsign;
dest[index++] = 0.0f;
dest[index++] = 0.0f;
}
}
}
public void makeLongCylinderIndices(int arcSlices, int lengthSlices, int stacks, int[] dest)
{
int numIndices = this.getLongCylinderIndexCount(arcSlices, lengthSlices, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int slices;
int i, j;
int vertex, index;
slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
index = 0;
for (j = 0; j < stacks; j++)
{
if (j != 0)
{
if (this.orientation == INSIDE)
{
vertex = (j - 1) * slices;
dest[index++] = vertex;
vertex = j * slices;
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
vertex = (j - 1) * slices;
dest[index++] = vertex + slices;
vertex = (j - 1) * slices;
dest[index++] = vertex;
}
}
for (i = 0; i <= slices; i++)
{
if (i == slices)
vertex = j * slices;
else
vertex = i + j * slices;
if (this.orientation == INSIDE)
{
dest[index++] = vertex + slices;
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + slices;
}
}
}
}
public void makeLongCylinderOutlineIndices(int arcSlices, int lengthSlices, int stacks, int[] dest)
{
int numIndices = this.getLongCylinderOutlineIndexCount(arcSlices, lengthSlices, stacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " stacks=" + stacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
int i;
int vertex, index;
index = 0;
// Bottom ring
for (i = 0; i < slices; i++)
{
vertex = i;
dest[index++] = vertex;
dest[index++] = (i != slices - 1) ? vertex + 1 : 0;
}
// Top ring
for (i = 0; i < slices; i++)
{
vertex = i + slices * stacks;
dest[index++] = vertex;
dest[index++] = (i != slices - 1) ? vertex + 1 : slices * stacks;
}
}
//**************************************************************//
//******************** Long Disk ********************//
//**************************************************************//
public int getLongDiskVertexCount(int arcSlices, int lengthSlices, int loops)
{
int slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
return slices * (loops + 1);
}
public int getLongDiskIndexCount(int arcSlices, int lengthSlices, int loops)
{
int slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
return loops * 2 * (slices + 1) + 2 * (loops - 1);
}
public int getLongDiskDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public void makeLongDiskVertices(float innerRadius, float outerRadius, float length,
int arcSlices, int lengthSlices, int loops, float[] dest)
{
int numPoints = this.getLongDiskVertexCount(arcSlices, lengthSlices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y;
float a, r;
float dy, da, dr;
int s, l;
int index;
dy = length / (float) lengthSlices;
da = (float) Math.PI / (float) arcSlices;
dr = (outerRadius - innerRadius) / (float) loops;
index = 0;
for (l = 0; l <= loops; l++)
{
r = innerRadius + l * dr;
// Top arc.
for (s = 0; s <= arcSlices; s++)
{
a = s * da + (3.0f * (float) Math.PI / 2.0f);
x = (float) Math.sin(a);
y = (float) Math.cos(a);
dest[index++] = x * r;
dest[index++] = y * r + length;
dest[index++] = 0.0f;
}
// Right side.
for (s = lengthSlices - 1; s >= 1; s--)
{
dest[index++] = r;
dest[index++] = s * dy;
dest[index++] = 0.0f;
}
// Bottom arc.
for (s = 0; s <= arcSlices; s++)
{
a = s * da + ((float) Math.PI / 2.0f);
x = (float) Math.sin(a);
y = (float) Math.cos(a);
dest[index++] = x * r;
dest[index++] = y * r;
dest[index++] = 0.0f;
}
// Left side.
for (s = 1; s < lengthSlices; s++)
{
dest[index++] = -r;
dest[index++] = s * dy;
dest[index++] = 0.0f;
}
}
}
public void makeLongDiskNormals(int arcSlices, int lengthSlices, int loops, float[] dest)
{
int numPoints = this.getLongDiskVertexCount(arcSlices, lengthSlices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int slices;
int s, l;
int index;
float nsign;
float[] normal;
slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
normal = new float[3];
normal[0] = 0.0f;
normal[1] = 0.0f;
//noinspection PointlessArithmeticExpression
normal[2] = 1.0f * nsign;
for (l = 0; l <= loops; l++)
{
for (s = 0; s < slices; s++)
{
index = s + l * slices;
index = 3 * index;
System.arraycopy(normal, 0, dest, index, 3);
}
}
}
@SuppressWarnings({"UnusedDeclaration"})
public void makeLongDiskVertexNormals(float innerRadius, float outerRadius, float length,
int arcSlices, int lengthSlices, int loops,
float[] srcVerts, float[] dest)
{
int numPoints = this.getLongDiskVertexCount(arcSlices, lengthSlices, loops);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (srcVerts == null)
{
String message = "nullValue.SourceVertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int slices;
int s, l;
int index;
float nsign;
float[] norm, zero, tmp;
slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
zero = new float[3];
tmp = new float[3];
for (l = 0; l <= loops; l++)
{
// Normal vectors for first and last loops require a special case.
if (l == 0 || l == loops)
{
// Closed disk: slices are collapsed.
if (l == 0 && innerRadius == 0.0f)
{
// Top arc.
{
// Compute common normal.
System.arraycopy(zero, 0, norm, 0, 3);
for (s = 0; s <= arcSlices; s++)
{
index = s;
this.facenorm(srcVerts, index, index + slices + 1, index + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
index = arcSlices;
this.facenorm(srcVerts, index, index + 1, index + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
index = 0;
this.facenorm(srcVerts, index, index + slices, index + slices - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
// Copy common normal to the first point of each slice.
for (s = 0; s <= arcSlices; s++)
{
index = s;
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
// Right and left sides.
{
int leftSideIndex;
for (s = 1; s < lengthSlices; s++)
{
// Compute common normal.
index = s + arcSlices;
leftSideIndex = slices - s;
System.arraycopy(zero, 0, norm, 0, 3);
this.facenorm(srcVerts, index, index + slices, index - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, index + 1, index + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
if (s == 1)
this.facenorm(srcVerts, leftSideIndex, leftSideIndex - slices + 1,
leftSideIndex + slices, tmp);
else
this.facenorm(srcVerts, leftSideIndex, leftSideIndex + 1, leftSideIndex + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, leftSideIndex, leftSideIndex + slices, leftSideIndex - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
// Copy common normal to the first point of each slice.
System.arraycopy(norm, 0, dest, 3 * index, 3);
System.arraycopy(norm, 0, dest, 3 * leftSideIndex, 3);
}
}
// Bottom arc.
{
// Compute common normal.
System.arraycopy(zero, 0, norm, 0, 3);
for (s = 0; s <= arcSlices; s++)
{
index = s + arcSlices + lengthSlices;
this.facenorm(srcVerts, index, index + slices + 1, index + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
index = arcSlices + lengthSlices;
this.facenorm(srcVerts, index, index + slices, index - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
index = (2 * arcSlices) + lengthSlices;
this.facenorm(srcVerts, index, index + 1, index + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
// Copy common normal to the first point of each slice.
for (s = 0; s <= arcSlices; s++)
{
index = s + arcSlices + lengthSlices;
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
// Open disk: each slice has a unique starting point.
else
{
for (s = 0; s < slices; s++)
{
int prevSlice, nextSlice;
int adjacentLoop;
index = s + l * slices;
prevSlice = index - 1;
nextSlice = index + 1;
if (s == 0)
prevSlice = l * slices;
else if (s == slices - 1)
nextSlice = l;
if (l == 0)
adjacentLoop = index + slices;
else
adjacentLoop = index - slices;
System.arraycopy(zero, 0, norm, 0, 3);
// Add clockwise adjacent face.
if (l == 0)
this.facenorm(srcVerts, index, nextSlice, adjacentLoop, tmp);
else
this.facenorm(srcVerts, index, adjacentLoop, nextSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add counter-clockwise adjacent face.
if (l == 0)
this.facenorm(srcVerts, index, adjacentLoop, prevSlice, tmp);
else
this.facenorm(srcVerts, index, prevSlice, adjacentLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
// Normal vectors for internal loops.
else
{
for (s = 0; s < slices; s++)
{
int prevSlice, nextSlice;
int prevLoop, nextLoop;
index = s + l * slices;
prevSlice = index - 1;
nextSlice = index + 1;
if (s == 0)
prevSlice = (slices - 1) + l * slices;
else if (s == slices - 1)
nextSlice = l * slices;
prevLoop = index - slices;
nextLoop = index + slices;
System.arraycopy(zero, 0, norm, 0, 3);
// Add lower-left adjacent face.
this.facenorm(srcVerts, index, prevSlice, prevSlice - slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, prevSlice - slices, prevLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add lower-right adjacent face.
this.facenorm(srcVerts, index, prevLoop, nextSlice - slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, nextSlice - slices, nextSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add upper-right adjacent face.
this.facenorm(srcVerts, index, nextSlice, nextSlice + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, nextSlice + slices, nextLoop, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Add upper-left adjacent face.
this.facenorm(srcVerts, index, nextLoop, prevSlice + slices, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, prevSlice + slices, prevSlice, tmp);
this.add3AndSet(norm, 0, tmp, 0);
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
}
public void makeLongDiskIndices(int arcSlices, int lengthSlices, int loops, int[] dest)
{
int numIndices = this.getLongDiskIndexCount(arcSlices, lengthSlices, loops);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "arcSlices=" + arcSlices
+ " lengthSlices=" + lengthSlices + " loops=" + loops);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numIndices)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int slices;
int s, l;
int vertex, index;
slices = 2 * (arcSlices + 1) + 2 * (lengthSlices - 1);
index = 0;
for (l = 0; l < loops; l++)
{
if (l != 0)
{
if (this.orientation == INSIDE)
{
vertex = (l - 1) * slices;
dest[index++] = vertex + slices;
vertex = (l - 1) * slices;
dest[index++] = vertex;
}
else // (this.orientation == OUTSIDE)
{
vertex = (l - 1) * slices;
dest[index++] = vertex;
vertex = l * slices;
dest[index++] = vertex;
}
}
for (s = 0; s <= slices; s++)
{
if (s == slices)
vertex = l * slices;
else
vertex = s + l * slices;
if (this.orientation == INSIDE)
{
dest[index++] = vertex;
dest[index++] = vertex + slices;
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertex + slices;
dest[index++] = vertex;
}
}
}
}
//**************************************************************//
//******************** Polygon ****************//
//**************************************************************//
public int computePolygonWindingOrder2(int pos, int count, Vec4[] points)
{
float area;
int order;
area = this.computePolygonArea2(pos, count, points);
if (area < 0.0f)
order = CLOCKWISE;
else
order = COUNTER_CLOCKWISE;
return order;
}
public float computePolygonArea2(int pos, int count, Vec4[] points)
{
if (pos < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "pos=" + pos);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (count < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "count=" + count);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points == null)
{
String message = "nullValue.PointsIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points.length < (pos + count))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "points.length < " + (pos + count));
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float area;
int i;
int coord, nextCoord;
area = 0.0f;
for (i = 0; i < count; i++)
{
coord = pos + i;
nextCoord = (i == count - 1) ? (pos) : (pos + i + 1);
area += points[coord].x * points[nextCoord].y;
area -= points[nextCoord].x * points[coord].y;
}
area /= 2.0f;
return area;
}
public IndexedTriangleArray tessellatePolygon(int pos, int count, float[] vertices, Vec4 normal)
{
if (count < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "count=" + count);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.length < (pos + count))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
TessellatorCallback cb;
GLUTessellatorSupport glts;
double[] dvertices = new double[3 * count];
int i;
int srcIndex, destIndex;
if (normal == null)
normal = Vec4.UNIT_Z;
cb = new TessellatorCallback(this, count, vertices);
glts = new GLUTessellatorSupport();
glts.beginTessellation(cb, normal);
try
{
GLU.gluTessBeginPolygon(glts.getGLUtessellator(), null);
GLU.gluTessBeginContour(glts.getGLUtessellator());
for (i = 0; i < count; i++)
{
srcIndex = 3 * (pos + i);
destIndex = 3 * i;
dvertices[destIndex] = vertices[srcIndex];
dvertices[destIndex + 1] = vertices[srcIndex + 1];
dvertices[destIndex + 2] = vertices[srcIndex + 2];
GLU.gluTessVertex(glts.getGLUtessellator(), dvertices, destIndex, pos + i);
}
GLU.gluTessEndContour(glts.getGLUtessellator());
GLU.gluTessEndPolygon(glts.getGLUtessellator());
}
finally
{
glts.endTessellation();
}
return new IndexedTriangleArray(
cb.getIndexCount(), cb.getIndices(),
cb.getVertexCount(), cb.getVertices());
}
public IndexedTriangleArray tessellatePolygon2(int pos, int count, float[] vertices)
{
if (count < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "count=" + count);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.length < (pos + count))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
return this.tessellatePolygon(pos, count, vertices, Vec4.UNIT_Z);
}
private static class TessellatorCallback extends GLUtessellatorCallbackAdapter
{
private GeometryBuilder gb;
private int type;
private int indexCount;
private int primIndexCount;
private int vertexCount;
private int[] indices;
private int[] primIndices;
private float[] vertices;
private TessellatorCallback(GeometryBuilder gb, int vertexCount, float[] vertices)
{
this.gb = gb;
this.indexCount = 0;
this.primIndexCount = 0;
this.vertexCount = vertexCount;
int initialCapacity = this.gb.nextPowerOfTwo(3 * vertexCount);
this.indices = new int[initialCapacity];
this.primIndices = new int[initialCapacity];
this.vertices = this.gb.copyOf(vertices, initialCapacity);
}
public int getIndexCount()
{
return this.indexCount;
}
public int[] getIndices()
{
return this.indices;
}
public int getVertexCount()
{
return this.vertexCount;
}
public float[] getVertices()
{
return this.vertices;
}
protected void addTriangle(int i1, int i2, int i3)
{
// Triangle indices will be specified in counter-clockwise order. To reverse the ordering, we
// swap the indices.
int minCapacity, oldCapacity, newCapacity;
minCapacity = this.indexCount + 3;
oldCapacity = this.indices.length;
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
this.indices = this.gb.copyOf(this.indices, newCapacity);
oldCapacity = minCapacity;
}
if (this.gb.orientation == GeometryBuilder.INSIDE)
{
this.indices[this.indexCount++] = this.primIndices[i1];
this.indices[this.indexCount++] = this.primIndices[i3];
this.indices[this.indexCount++] = this.primIndices[i2];
}
else // (this.gb.orientation == GeometryBuilder.OUTSIDE)
{
this.indices[this.indexCount++] = this.primIndices[i1];
this.indices[this.indexCount++] = this.primIndices[i2];
this.indices[this.indexCount++] = this.primIndices[i3];
}
}
public void begin(int type)
{
this.type = type;
this.primIndexCount = 0;
}
public void vertex(Object vertexData)
{
int minCapacity, oldCapacity, newCapacity;
oldCapacity = this.primIndices.length;
minCapacity = this.primIndexCount + 1;
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
this.primIndices = this.gb.copyOf(this.primIndices, newCapacity);
oldCapacity = newCapacity;
}
int index = (Integer) vertexData;
this.primIndices[this.primIndexCount++] = index;
}
public void end()
{
int i;
if (this.type == GL.GL_TRIANGLES)
{
for (i = 2; i < this.primIndexCount; i++)
{
if (((i + 1) % 3) == 0)
this.addTriangle(i - 2, i - 1, i);
}
}
else if (this.type == GL.GL_TRIANGLE_STRIP)
{
for (i = 2; i < this.primIndexCount; i++)
{
if ((i % 2) == 0)
this.addTriangle(i - 2, i - 1, i);
else
this.addTriangle(i - 1, i - 2, i);
}
}
else if (this.type == GL.GL_TRIANGLE_FAN)
{
for (i = 2; i < this.primIndexCount; i++)
{
this.addTriangle(0, i - 1, i);
}
}
}
public void combine(double[] coords, Object[] data, float[] weight, Object[] outData)
{
outData[0] = data[0];
}
}
//**************************************************************//
//******************** Indexed Triangle Buffer ***************//
//**************************************************************//
public int getIndexedTriangleBufferDrawMode()
{
return GL.GL_TRIANGLES;
}
public static class IndexedTriangleBuffer
{
private IntBuffer indices;
private FloatBuffer vertices;
private int indexCount;
private int vertexCount;
public IndexedTriangleBuffer(int indexCount, IntBuffer indices, int vertexCount, FloatBuffer vertices)
{
this.indices = indices;
this.vertices = vertices;
this.indexCount = indexCount;
this.vertexCount = vertexCount;
}
public int getIndexCount()
{
return this.indexCount;
}
public IntBuffer getIndices()
{
return this.indices;
}
public int getVertexCount()
{
return this.vertexCount;
}
public FloatBuffer getVertices()
{
return this.vertices;
}
}
public void subdivideIndexedTriangleBuffer(IndexedTriangleBuffer itb)
{
if (itb == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int indexCount;
int a, b, c;
int ab, bc, ca;
int i, j;
HashMap edgeMap;
Edge e;
Integer split;
indexCount = itb.getIndexCount();
edgeMap = new HashMap();
// Iterate over each triangle, and split the edge of each triangle. Each edge is split exactly once. The
// index of the new vertex created by a split is stored in edgeMap.
for (i = 0; i < indexCount; i += 3)
{
for (j = 0; j < 3; j++)
{
a = itb.indices.get(i + j);
b = itb.indices.get((j < 2) ? (i + j + 1) : i);
e = new Edge(a, b);
split = edgeMap.get(e);
if (split == null)
{
split = this.splitVertex(itb, a, b);
edgeMap.put(e, split);
}
}
}
// Iterate over each triangle, and create indices for four new triangles, replacing indices of the original
// triangle.
for (i = 0; i < indexCount; i += 3)
{
a = itb.indices.get(i);
b = itb.indices.get(i + 1);
c = itb.indices.get(i + 2);
ab = edgeMap.get(new Edge(a, b));
bc = edgeMap.get(new Edge(b, c));
ca = edgeMap.get(new Edge(c, a));
this.indexSplitTriangle(itb, i, a, b, c, ab, bc, ca);
}
}
public void makeIndexedTriangleBufferNormals(IndexedTriangleBuffer itb, FloatBuffer dest)
{
if (itb == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int numCoords = 3 * itb.vertexCount;
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.capacity() < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.capacity();
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.makeIndexedTriangleBufferNormals(0, itb.getIndexCount(), itb.getIndices(),
0, itb.getVertexCount(), itb.getVertices(), dest);
}
public void makeIndexedTriangleBufferNormals(int indexPos, int indexCount, IntBuffer indices,
int vertexPos, int vertexCount, FloatBuffer vertices,
FloatBuffer dest)
{
if (indices == null)
{
String message = "nullValue.IndexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (indices.capacity() < (indexPos + indexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "indices.length=" + indices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.capacity() < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.capacity() < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + dest.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, v;
int index;
float nsign;
float[] norm;
int[] faceIndices;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
faceIndices = new int[3];
// Compute the normal for each face, contributing that normal to each vertex of the face.
for (i = 0; i < indexCount; i += 3)
{
faceIndices[0] = indices.get(indexPos + i);
faceIndices[1] = indices.get(indexPos + i + 1);
faceIndices[2] = indices.get(indexPos + i + 2);
// Compute the normal for this face.
this.facenorm(vertices, faceIndices[0], faceIndices[1], faceIndices[2], norm);
// Add this face normal to the normal at each vertex.
for (v = 0; v < 3; v++)
{
index = 3 * faceIndices[v];
this.add3AndSet(dest, index, norm, 0);
}
}
// Scale and normalize each vertex normal.
for (v = 0; v < vertexCount; v++)
{
index = 3 * (vertexPos + v);
this.mul3AndSet(dest, index, nsign);
this.norm3AndSet(dest, index);
}
dest.rewind();
}
private int splitVertex(IndexedTriangleBuffer itb, int a, int b)
{
int minCapacity, oldCapacity, newCapacity;
oldCapacity = itb.vertices.capacity();
minCapacity = 3 * (itb.getVertexCount() + 1);
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
itb.vertices = this.copyOf(itb.vertices, newCapacity);
oldCapacity = newCapacity;
}
int s = itb.getVertexCount();
int is = 3 * s;
int ia = 3 * a;
int ib = 3 * b;
itb.vertices.put(is, (itb.vertices.get(ia) + itb.vertices.get(ib)) / 2.0f);
itb.vertices.put(is + 1, (itb.vertices.get(ia + 1) + itb.vertices.get(ib + 1)) / 2.0f);
itb.vertices.put(is + 2, (itb.vertices.get(ia + 2) + itb.vertices.get(ib + 2)) / 2.0f);
itb.vertexCount++;
return s;
}
public void makeEllipsoidNormals(IndexedTriangleBuffer itb, FloatBuffer dest)
{
if (itb == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int numCoords = 3 * itb.vertexCount;
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.capacity() < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.capacity();
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.makeEllipsoidNormals(0, itb.getIndexCount(), itb.getIndices(),
0, itb.getVertexCount(), itb.getVertices(), dest);
}
public void makeEllipsoidNormals(int indexPos, int indexCount, IntBuffer indices,
int vertexPos, int vertexCount, FloatBuffer vertices,
FloatBuffer dest)
{
if (indices == null)
{
String message = "nullValue.IndexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (indices.capacity() < (indexPos + indexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "indices.length=" + indices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.capacity() < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.capacity() < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + dest.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, v;
int index;
float nsign;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
// for a sphere, normals are just the normalized vectors of the vertex positions
// first copy all the vertices to the normals buffer
for (i = 0; i < 3 * vertexCount; i++)
{
dest.put(i, vertices.get(i));
}
// Scale and normalize each vertex normal.
for (v = 0; v < vertexCount; v++)
{
index = 3 * (vertexPos + v);
this.mul3AndSet(dest, index, nsign);
this.norm3AndSet(dest, index);
}
dest.rewind();
}
public void makeCylinderNormals(IndexedTriangleBuffer itb, FloatBuffer dest)
{
if (itb == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int numCoords = 3 * itb.vertexCount;
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.capacity() < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.capacity();
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.makeCylinderNormals(0, itb.getIndexCount(), itb.getIndices(),
0, itb.getVertexCount(), itb.getVertices(), dest);
}
public void makeCylinderNormals(int indexPos, int indexCount, IntBuffer indices,
int vertexPos, int vertexCount, FloatBuffer vertices,
FloatBuffer dest)
{
if (indices == null)
{
String message = "nullValue.IndexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (indices.capacity() < (indexPos + indexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "indices.length=" + indices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.capacity() < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.capacity() < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + dest.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, v;
int index;
float nsign;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
// for a cylinder, normals are just the normalized vectors of the (x, y) coords of the vertex positions
// first copy all the vertices to the normals buffer
for (i = 0; i < 3 * vertexCount; i++)
{
if (i % 3 == 2) // set z coord to zero
dest.put(i, 0);
else
dest.put(i, -vertices.get(i));
}
// Scale and normalize each vertex normal.
for (v = 0; v < vertexCount; v++)
{
index = 3 * (vertexPos + v);
this.mul3AndSet(dest, index, nsign);
this.norm3AndSet(dest, index);
}
dest.rewind();
}
private void indexSplitTriangle(IndexedTriangleBuffer itb, int original, int a, int b, int c, int ab, int bc,
int ca)
{
int minCapacity, oldCapacity, newCapacity;
// One of the new triangles will overwrite the original triangles, so we only need enough space to index
// three new triangles.
oldCapacity = itb.indices.capacity();
minCapacity = itb.getIndexCount() + 9;
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
itb.indices = this.copyOf(itb.indices, newCapacity);
oldCapacity = newCapacity;
}
// Lower-left triangle.
// This triangle replaces the original.
itb.indices.put(original, a);
itb.indices.put(original + 1, ab);
itb.indices.put(original + 2, ca);
// Center triangle.
itb.indices.put(itb.indexCount++, ab);
itb.indices.put(itb.indexCount++, bc);
itb.indices.put(itb.indexCount++, ca);
// Lower-right triangle.
itb.indices.put(itb.indexCount++, ab);
itb.indices.put(itb.indexCount++, b);
itb.indices.put(itb.indexCount++, bc);
// Upper triangle.
itb.indices.put(itb.indexCount++, ca);
itb.indices.put(itb.indexCount++, bc);
itb.indices.put(itb.indexCount++, c);
}
// This method finds triangles of the sphere mesh which span the discontinuity at 2 * PI radians.
// It then creates a duplicate of the vertex in each of these triangles that is out of range, and uses this
// duplicate to form the face (replacing the original vertex). This way, the original vertex is used only
// in faces that do not span the discontinuity, while faces that do span the discontinuity use the
// duplicate instead. When it comes time for texture mapping, a different texture coordinate can be
// mapped to the duplicate vertex than to the original, each one falling in the correct range for the face(s) it
// comprises.
public void fixSphereSeam(IndexedTriangleBuffer itb, float wrapThreshold)
{
int vertex0, vertex1, vertex2; // indices of the three vertices of the current face
double x0, y0, x1, y1, x2, y2; // actual x and y point values of those vertices
double phi0, phi1, phi2;
Integer newVertex, wrapVertex;
int wrapIndex = -1; // track index of the vertex that will be replaced by a new "wrapped" vertex
// keep track of newly created duplicate vertices:
Map duplicates = new HashMap();
// for each indexed triangle, determine if phi (longitude) of any of the vertices is on the
// opposite side of 2PI from others (the "wrap" vertex)
int indexCount = itb.getIndexCount();
for (int i = 0; i < indexCount; i += 3)
{
vertex0 = itb.indices.get(i);
vertex1 = itb.indices.get(i + 1);
vertex2 = itb.indices.get(i + 2);
x0 = itb.vertices.get(3 * vertex0);
y0 = itb.vertices.get(3 * vertex0 + 1);
x1 = itb.vertices.get(3 * vertex1);
y1 = itb.vertices.get(3 * vertex1 + 1);
x2 = itb.vertices.get(3 * vertex2);
y2 = itb.vertices.get(3 * vertex2 + 1);
// compute phi of each of the three vertices of the face
phi0 = Math.atan2(y0, x0);
if (phi0 < 0.0d)
phi0 += 2.0d * Math.PI;
phi1 = Math.atan2(y1, x1);
if (phi1 < 0.0d)
phi1 += 2.0d * Math.PI;
phi2 = Math.atan2(y2, x2);
if (phi2 < 0.0d)
phi2 += 2.0d * Math.PI;
// check if face spans phi = 0 (the texture seam), and determine which is the "wrapped" vertex
if (Math.abs(phi0 - phi1) > wrapThreshold)
{
if (Math.abs(phi0 - phi2) > wrapThreshold)
wrapIndex = i; // vertex0 is the wrapped vertex
else
wrapIndex = i + 1; // vertex1 is the wrapped vertex
}
else if (Math.abs(phi1 - phi2) > wrapThreshold)
wrapIndex = i + 2; // vertex2 is the wrapped vertex
if (wrapIndex >= 0) // check if one of the vertices on this face wrapped across 2PI
{
wrapVertex = itb.indices.get(wrapIndex);
//look to see if this vertex has been duplicated already
newVertex = duplicates.get(wrapVertex);
if (newVertex != null)
itb.indices.put(wrapIndex, newVertex); // replace the old vertex with the duplicate
else
{
// create a duplicate of the wrapIndex vertex and get its index newVertex
newVertex = duplicateVertex(itb, wrapVertex);
// place the new vertex in the duplicates structure
duplicates.put(wrapVertex, newVertex);
// now replace the index at the wrapIndex with the index of the new duplicate
itb.indices.put(wrapIndex, newVertex);
}
wrapIndex = -1; // reset the wrapVertex
}
}
}
// append copy of vertex at sourceIndex to end of vertices buffer
private int duplicateVertex(IndexedTriangleBuffer itb, int sourceIndex)
{
if (itb == null)
{
String message = Logging.getMessage("nullValue.IndexedTriangleBufferIsNull");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (sourceIndex >= itb.vertexCount)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "sourceIndex > vertexCount");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// ensure there is room in the vertex buffer for one new vertex
int minCapacity, oldCapacity, newCapacity;
oldCapacity = itb.vertices.capacity();
minCapacity = 3 * itb.getVertexCount() + 3;
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
itb.vertices = this.copyOf(itb.vertices, newCapacity);
oldCapacity = newCapacity;
}
int destIndex = itb.getVertexCount();
// append vertex data to vertices buffer
itb.vertices.put(3 * destIndex, itb.vertices.get(3 * sourceIndex));
itb.vertices.put(3 * destIndex + 1, itb.vertices.get(3 * sourceIndex + 1));
itb.vertices.put(3 * destIndex + 2, itb.vertices.get(3 * sourceIndex + 2));
itb.vertexCount++;
// return index of the newly created vertex
return itb.vertexCount - 1;
}
public void makeUnitSphereTextureCoordinates(IndexedTriangleBuffer itb, FloatBuffer texCoords)
{
if (itb == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int numCoords = 2 * itb.vertexCount;
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + texCoords.capacity();
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.makeUnitSphereTextureCoordinates(itb.getVertexCount(), itb.getVertices(), texCoords, -1);
}
// allow for correction of seam caused by triangles that wrap across tecture bounds
public void makeUnitSphereTextureCoordinates(IndexedTriangleBuffer itb, FloatBuffer texCoords,
int seamVerticesIndex)
{
if (itb == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int numCoords = 2 * itb.vertexCount;
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + texCoords.capacity();
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.makeUnitSphereTextureCoordinates(itb.getVertexCount(), itb.getVertices(),
texCoords, seamVerticesIndex);
}
public void makeUnitSphereTextureCoordinates(int vertexCount, FloatBuffer vertices,
FloatBuffer texCoords, int seamVerticesIndex)
{
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.capacity() < 3 * vertexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < 2 * vertexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + texCoords.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i;
double x, y, z;
double theta, phi, u, v;
// compute uv texture coordinates for each vertex and place them in the texCoords buffer.
for (i = 0; i < vertexCount; i++)
{
x = vertices.get(3 * i);
y = vertices.get(3 * i + 1);
z = vertices.get(3 * i + 2);
phi = Math.atan2(y, x);
theta = Math.acos(z);
if (phi < 0.0d)
phi += 2.0d * Math.PI; // shift phi to be in [0, 2*PI]
u = phi / (2.0d * Math.PI);
v = (Math.PI - theta) / Math.PI;
texCoords.put(2 * i, (float) u);
texCoords.put(2 * i + 1, (float) v);
}
if (seamVerticesIndex > 0) // if the seam of the sphere was fixed
{
for (i = seamVerticesIndex; i < vertexCount; i++)
{
// wrap u (phi) texCoord for all the duplicated vertices
u = texCoords.get(2 * i);
if (u < 0.5)
texCoords.put(2 * i, (float) u + 1);
else
texCoords.put(2 * i, (float) u - 1);
}
}
texCoords.rewind();
}
// single texture version
public void makeUnitBoxTextureCoordinates(FloatBuffer texCoords, int vertexCount)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < 2 * vertexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + texCoords.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for each of the 6 box faces and place them in the texCoords buffer.
for (int i = 0; i < vertexCount; i += 4)
{
// V0 (upper left)
texCoords.put(2 * i, 0);
texCoords.put(2 * i + 1, 1);
// V1 (upper right)
texCoords.put(2 * i + 2, 1);
texCoords.put(2 * i + 3, 1);
// V2 (lower left)
texCoords.put(2 * i + 4, 0);
texCoords.put(2 * i + 5, 0);
// V3 (lower right)
texCoords.put(2 * i + 6, 1);
texCoords.put(2 * i + 7, 0);
}
texCoords.rewind();
}
// multi-texture version
public void makeUnitBoxTextureCoordinates(int index, FloatBuffer texCoords, int vertexCount)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < 2 * vertexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + texCoords.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for each of the 6 box faces and place them in the texCoords buffer.
for (int i = 0; i < vertexCount; i += 4)
{
// V0 (upper left)
texCoords.put(2 * i, 0);
texCoords.put(2 * i + 1, 1);
// V1 (upper right)
texCoords.put(2 * i + 2, 1);
texCoords.put(2 * i + 3, 1);
// V2 (lower left)
texCoords.put(2 * i + 4, 0);
texCoords.put(2 * i + 5, 0);
// V3 (lower right)
texCoords.put(2 * i + 6, 1);
texCoords.put(2 * i + 7, 0);
}
texCoords.rewind();
}
public void makeUnitPyramidTextureCoordinates(FloatBuffer texCoords, int vertexCount)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < 2 * vertexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + texCoords.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for each of the 4 pyramid faces and for the base, and place them
// in the texCoords buffer.
int i;
for (i = 0; i < vertexCount - 4; i += 3) // create texture coords for the 4 sides of the pyramid first
{
// V0 (point)
texCoords.put(2 * i, 0.5f);
texCoords.put(2 * i + 1, 1);
// V1 (lower left)
texCoords.put(2 * i + 2, 0);
texCoords.put(2 * i + 3, 0);
// V2 (lower right)
texCoords.put(2 * i + 4, 1);
texCoords.put(2 * i + 5, 0);
}
// then create coords for the base
// V0 (upper left)
texCoords.put(2 * i, 0);
texCoords.put(2 * i + 1, 1);
// V1 (upper right)
texCoords.put(2 * i + 2, 1);
texCoords.put(2 * i + 3, 1);
// V2 (lower left)
texCoords.put(2 * i + 4, 0);
texCoords.put(2 * i + 5, 0);
// V3 (lower right)
texCoords.put(2 * i + 6, 1);
texCoords.put(2 * i + 7, 0);
texCoords.rewind();
}
public void makeUnitPyramidTextureCoordinates(int index, FloatBuffer texCoords, int vertexCount)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (texCoords.capacity() < 2 * vertexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + texCoords.capacity());
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for either one of the 4 pyramid faces or for the base, and place them
// in the texCoords buffer.
int i = 0;
if (index == 4) // pyramid base
{
// V0 (upper left)
texCoords.put(2 * i, 0);
texCoords.put(2 * i + 1, 1);
// V1 (upper right)
texCoords.put(2 * i + 2, 1);
texCoords.put(2 * i + 3, 1);
// V2 (lower left)
texCoords.put(2 * i + 4, 0);
texCoords.put(2 * i + 5, 0);
// V3 (lower right)
texCoords.put(2 * i + 6, 1);
texCoords.put(2 * i + 7, 0);
}
else // pyramid side
{
for (i = 0; i < vertexCount; i += 3) // create texture coords for the 4 sides of the pyramid first
{
// V0 (point)
texCoords.put(2 * i, 0.5f);
texCoords.put(2 * i + 1, 1);
// V1 (lower left)
texCoords.put(2 * i + 2, 0);
texCoords.put(2 * i + 3, 0);
// V2 (lower right)
texCoords.put(2 * i + 4, 1);
texCoords.put(2 * i + 5, 0);
}
}
texCoords.rewind();
}
public void makeUnitCylinderTextureCoordinates(int face, FloatBuffer texCoords, int subdivisions)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for the cylinder top, bottom and core, and place them
// in the texCoords buffer.
int i, index;
float x, y, z, u, v, a, phi;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
if (face == 2) // cylinder core
{
int coreTexIndex = 0;
for (i = 0; i < slices; i++)
{
a = i * da;
// cylinder core top rim
u = 1 - a / (float) (2 * Math.PI);
texCoords.put(coreTexIndex, u);
texCoords.put(coreTexIndex + 1, 1);
// cylinder core bottom rim
texCoords.put(coreTexIndex + 2, u);
texCoords.put(coreTexIndex + 3, 0);
coreTexIndex += 4;
}
// close the texture seam
texCoords.put(coreTexIndex, 0);
texCoords.put(coreTexIndex + 1, 1);
texCoords.put(coreTexIndex + 2, 0);
texCoords.put(coreTexIndex + 3, 0);
}
else // cylinder top or bottom
{
// center point
texCoords.put(0, 0.5f);
texCoords.put(1, 0.5f);
// perimeter points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
u = x / 2 + 0.5f;
v = y / 2 + 0.5f;
if (face == 1) // Cylinder bottom
u = 1 - u;
texCoords.put(2 * (i + 1), u);
texCoords.put(2 * (i + 1) + 1, v);
}
}
texCoords.rewind();
}
public void makeWedgeTextureCoordinates(FloatBuffer texCoords, int subdivisions, Angle angle)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for the wedge top, bottom, core and sides, and place them
// in the texCoords buffer.
int i, index;
float x, y, u, v, a;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = (float) angle.getRadians() / slices;
int coreTexIndex = 4 * (slices + 2);
// center points
texCoords.put(0, 0.5f);
texCoords.put(1, 0.5f);
texCoords.put(2 * (slices + 2), 0.5f);
texCoords.put(2 * (slices + 2) + 1, 0.5f);
for (i = 0; i <= slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
u = x / 2 + 0.5f;
v = y / 2 + 0.5f;
// wedge top
texCoords.put(2 * (i + 1), u);
texCoords.put(2 * (i + 1) + 1, v);
// wedge bottom
texCoords.put(2 * (slices + i + 3), 1 - u);
texCoords.put(2 * (slices + i + 3) + 1, v);
// wedge core top rim
u = 1 - a / (float) (2 * Math.PI);
texCoords.put(coreTexIndex, u);
texCoords.put(coreTexIndex + 1, 1);
// wedge core bottom rim
texCoords.put(coreTexIndex + 2, u);
texCoords.put(coreTexIndex + 3, 0);
coreTexIndex += 4;
}
// wedge sides
for (i = 0; i < 2; i++)
{
index = 2 * (4 * (slices + 1 + i) + 2);
// inner points
texCoords.put(index, 0);
texCoords.put(index + 1, 1);
texCoords.put(index + 2, 0);
texCoords.put(index + 3, 0);
// outer points
texCoords.put(index + 4, 1);
texCoords.put(index + 5, 1);
texCoords.put(index + 6, 1);
texCoords.put(index + 7, 0);
}
texCoords.rewind();
}
public void makeUnitWedgeTextureCoordinates(int face, FloatBuffer texCoords, int subdivisions, Angle angle)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for the wedge top, bottom, core and sides, and place them
// in the texCoords buffer.
int i, index;
float x, y, u, v, a;
// face 0 = top
// face 1 = bottom
// face 2 = round core wall
// face 3 = first wedge side
// face 4 = second wedge side
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = (float) angle.getRadians() / slices;
if (face == 0 || face == 1) // wedge top or bottom
{
// center point
texCoords.put(0, 0.5f);
texCoords.put(1, 0.5f);
for (i = 0; i <= slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
u = x / 2 + 0.5f;
v = y / 2 + 0.5f;
if (face == 1) // wedge bottom
u = 1 - u;
// rim point
texCoords.put(2 * (i + 1), u);
texCoords.put(2 * (i + 1) + 1, v);
}
}
else if (face == 2) // wedge core
{
int coreTexIndex = 0;
for (i = 0; i <= slices; i++)
{
a = i * da;
// cylinder core top rim
u = 1 - a / (float) (2 * Math.PI);
texCoords.put(coreTexIndex, u);
texCoords.put(coreTexIndex + 1, 1);
// cylinder core bottom rim
texCoords.put(coreTexIndex + 2, u);
texCoords.put(coreTexIndex + 3, 0);
coreTexIndex += 4;
}
}
else if (face == 3) // west-facing wedge side
{
// inner points
texCoords.put(0, 1);
texCoords.put(1, 1);
texCoords.put(2, 1);
texCoords.put(3, 0);
// outer points
texCoords.put(4, 0);
texCoords.put(5, 1);
texCoords.put(6, 0);
texCoords.put(7, 0);
}
else if (face == 4) // adjustable wedge side
{
// inner points
texCoords.put(0, 0);
texCoords.put(1, 1);
texCoords.put(2, 0);
texCoords.put(3, 0);
// outer points
texCoords.put(4, 1);
texCoords.put(5, 1);
texCoords.put(6, 1);
texCoords.put(7, 0);
}
texCoords.rewind();
}
public void makeUnitConeTextureCoordinates(FloatBuffer texCoords, int subdivisions)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for the cone bottom and core, and place them
// in the texCoords buffer.
int i, index;
float x, y, z, u, v, a, phi;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
int coreTexIndex = 2 * (slices + 1);
// center point
texCoords.put(0, 0.5f);
texCoords.put(1, 0.5f);
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
u = x / 2 + 0.5f;
v = y / 2 + 0.5f;
// cone bottom
texCoords.put(2 * (i + 1), 1 - u);
texCoords.put(2 * (i + 1) + 1, v);
// cone core top rim
u = 1 - a / (float) (2 * Math.PI);
texCoords.put(coreTexIndex, u);
texCoords.put(coreTexIndex + 1, 1);
// cone core bottom rim
texCoords.put(coreTexIndex + 2, u);
texCoords.put(coreTexIndex + 3, 0);
coreTexIndex += 4;
}
// close the texture seam
texCoords.put(coreTexIndex, 0);
texCoords.put(coreTexIndex + 1, 1);
texCoords.put(coreTexIndex + 2, 0);
texCoords.put(coreTexIndex + 3, 0);
texCoords.rewind();
}
public void makeUnitConeTextureCoordinates(int face, FloatBuffer texCoords, int subdivisions)
{
if (texCoords == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (subdivisions < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions < 0");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// create uv texture coordinates for the cone base and core, and place them
// in the texCoords buffer.
int i, index;
float x, y, z, u, v, a, phi;
int slices = (int) Math.pow(2, 2 + subdivisions);
float da = 2.0f * (float) Math.PI / (float) slices;
if (face == 1) // cone core
{
int coreTexIndex = 0;
for (i = 0; i < slices; i++)
{
a = i * da;
// cone core top rim
u = 1 - a / (float) (2 * Math.PI);
texCoords.put(coreTexIndex, u);
texCoords.put(coreTexIndex + 1, 1);
// core bottom rim
texCoords.put(coreTexIndex + 2, u);
texCoords.put(coreTexIndex + 3, 0);
coreTexIndex += 4;
}
// close the texture seam
texCoords.put(coreTexIndex, 0);
texCoords.put(coreTexIndex + 1, 1);
texCoords.put(coreTexIndex + 2, 0);
texCoords.put(coreTexIndex + 3, 0);
}
else if (face == 0) // cone base
{
// center point
texCoords.put(0, 0.5f);
texCoords.put(1, 0.5f);
// perimeter points
for (i = 0; i < slices; i++)
{
a = i * da;
x = (float) Math.sin(a);
y = (float) Math.cos(a);
u = x / 2 + 0.5f;
v = y / 2 + 0.5f;
texCoords.put(2 * (i + 1), 1 - u);
texCoords.put(2 * (i + 1) + 1, v);
}
}
texCoords.rewind();
}
//**************************************************************//
//******************** Indexed Triangle Array ****************//
//**************************************************************//
public int getIndexedTriangleArrayDrawMode()
{
return GL.GL_TRIANGLES;
}
public static class IndexedTriangleArray
{
private int indexCount;
private int vertexCount;
private int[] indices;
private float[] vertices;
public IndexedTriangleArray(int indexCount, int[] indices, int vertexCount, float[] vertices)
{
this.indexCount = indexCount;
this.indices = indices;
this.vertexCount = vertexCount;
this.vertices = vertices;
}
public int getIndexCount()
{
return this.indexCount;
}
public int[] getIndices()
{
return this.indices;
}
public int getVertexCount()
{
return this.vertexCount;
}
public float[] getVertices()
{
return this.vertices;
}
}
public void subdivideIndexedTriangleArray(IndexedTriangleArray ita)
{
if (ita == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int indexCount;
int a, b, c;
int ab, bc, ca;
int i, j;
HashMap edgeMap;
Edge e;
Integer split;
indexCount = ita.indexCount;
edgeMap = new HashMap();
// Iterate over each triangle, and split the edge of each triangle. Each edge is split exactly once. The
// index of the new vertex created by a split is stored in edgeMap.
for (i = 0; i < indexCount; i += 3)
{
for (j = 0; j < 3; j++)
{
a = ita.indices[i + j];
b = ita.indices[(j < 2) ? (i + j + 1) : i];
e = new Edge(a, b);
split = edgeMap.get(e);
if (split == null)
{
split = this.splitVertex(ita, a, b);
edgeMap.put(e, split);
}
}
}
// Iterate over each triangle, and create indices for four new triangles, replacing indices of the original
// triangle.
for (i = 0; i < indexCount; i += 3)
{
a = ita.indices[i];
b = ita.indices[i + 1];
c = ita.indices[i + 2];
ab = edgeMap.get(new Edge(a, b));
bc = edgeMap.get(new Edge(b, c));
ca = edgeMap.get(new Edge(c, a));
this.indexSplitTriangle(ita, i, a, b, c, ab, bc, ca);
}
}
public IndexedTriangleArray subdivideIndexedTriangles(int indexCount, int[] indices,
int vertexCount, float[] vertices)
{
int numCoords = 3 * vertexCount;
if (indices == null)
{
String message = "nullValue.IndexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (indices.length < indexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "indices.length=" + indices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.length < numCoords)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
IndexedTriangleArray ita = new IndexedTriangleArray(indexCount, indices, vertexCount, vertices);
this.subdivideIndexedTriangleArray(ita);
return ita;
}
public void makeIndexedTriangleArrayNormals(IndexedTriangleArray ita, float[] dest)
{
if (ita == null)
{
String message = "nullValue.IndexedTriangleArray";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int numCoords = 3 * ita.vertexCount;
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
this.makeIndexedTriangleArrayNormals(0, ita.indexCount, ita.indices, 0, ita.vertexCount, ita.vertices, dest);
}
public void makeIndexedTriangleArrayNormals(int indexPos, int indexCount, int[] indices,
int vertexPos, int vertexCount, float[] vertices,
float[] dest)
{
if (indices == null)
{
String message = "nullValue.IndexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (indices.length < (indexPos + indexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "indices.length=" + indices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.length < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + dest.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, v;
int index;
float nsign;
float[] norm;
int[] faceIndices;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
faceIndices = new int[3];
// Compute the normal for each face, contributing that normal to each vertex of the face.
for (i = 0; i < indexCount; i += 3)
{
faceIndices[0] = indices[indexPos + i];
faceIndices[1] = indices[indexPos + i + 1];
faceIndices[2] = indices[indexPos + i + 2];
// Compute the normal for this face.
this.facenorm(vertices, faceIndices[0], faceIndices[1], faceIndices[2], norm);
// Add this face normal to the normal at each vertex.
for (v = 0; v < 3; v++)
{
index = 3 * faceIndices[v];
this.add3AndSet(dest, index, norm, 0);
}
}
// Scale and normalize each vertex normal.
for (v = 0; v < vertexCount; v++)
{
index = 3 * (vertexPos + v);
this.mul3AndSet(dest, index, nsign);
this.norm3AndSet(dest, index);
}
}
public void makeIndexedTriangleStripNormals(int indexPos, int indexCount, int[] indices,
int vertexPos, int vertexCount, float[] vertices,
float[] dest)
{
if (indices == null)
{
String message = "nullValue.IndexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (indices.length < indexPos + indexCount)
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "indices.length=" + indices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices == null)
{
String message = "nullValue.VertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (vertices.length < 3 * (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "vertices.length=" + vertices.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < 3 * (vertexPos + vertexCount))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "dest.length=" + dest.length);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int i, v;
int index;
float nsign;
float[] norm;
int[] faceIndices;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
faceIndices = new int[3];
// Compute the normal for each face, contributing that normal to each vertex of the face.
for (i = 2; i < indexCount; i++)
{
if ((i % 2) == 0)
{
faceIndices[0] = indices[indexPos + i - 2];
faceIndices[1] = indices[indexPos + i - 1];
faceIndices[2] = indices[indexPos + i];
}
else
{
faceIndices[0] = indices[indexPos + i - 1];
faceIndices[1] = indices[indexPos + i - 2];
faceIndices[2] = indices[indexPos + i];
}
// Compute the normal for this face.
this.facenorm(vertices, faceIndices[0], faceIndices[1], faceIndices[2], norm);
// Add this face normal to the normal at each vertex.
for (v = 0; v < 3; v++)
{
index = 3 * faceIndices[v];
this.add3AndSet(dest, index, norm, 0);
}
}
// Scale and normalize each vertex normal.
for (v = 0; v < vertexCount; v++)
{
index = 3 * (vertexPos + v);
this.mul3AndSet(dest, index, nsign);
this.norm3AndSet(dest, index);
}
}
private int splitVertex(IndexedTriangleArray ita, int a, int b)
{
int minCapacity, oldCapacity, newCapacity;
oldCapacity = ita.vertices.length;
minCapacity = 3 * (ita.vertexCount + 1);
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
ita.vertices = this.copyOf(ita.vertices, newCapacity);
oldCapacity = newCapacity;
}
int s = ita.vertexCount;
int is = 3 * s;
int ia = 3 * a;
int ib = 3 * b;
ita.vertices[is] = (ita.vertices[ia] + ita.vertices[ib]) / 2.0f;
ita.vertices[is + 1] = (ita.vertices[ia + 1] + ita.vertices[ib + 1]) / 2.0f;
ita.vertices[is + 2] = (ita.vertices[ia + 2] + ita.vertices[ib + 2]) / 2.0f;
ita.vertexCount++;
return s;
}
private void indexSplitTriangle(IndexedTriangleArray ita, int original, int a, int b, int c, int ab, int bc, int ca)
{
int minCapacity, oldCapacity, newCapacity;
// One of the new triangles will overwrite the original triangles, so we only need enough space to index
// three new triangles.
oldCapacity = ita.indices.length;
minCapacity = ita.indexCount + 9;
while (minCapacity > oldCapacity)
{
newCapacity = 2 * oldCapacity;
ita.indices = this.copyOf(ita.indices, newCapacity);
oldCapacity = newCapacity;
}
// Lower-left triangle.
// This triangle replaces the original.
ita.indices[original] = a;
ita.indices[original + 1] = ab;
ita.indices[original + 2] = ca;
// Center triangle.
ita.indices[ita.indexCount++] = ab;
ita.indices[ita.indexCount++] = bc;
ita.indices[ita.indexCount++] = ca;
// Lower-right triangle.
ita.indices[ita.indexCount++] = ab;
ita.indices[ita.indexCount++] = b;
ita.indices[ita.indexCount++] = bc;
// Upper triangle.
ita.indices[ita.indexCount++] = ca;
ita.indices[ita.indexCount++] = bc;
ita.indices[ita.indexCount++] = c;
}
private static class Edge
{
public final int a;
public final int b;
public Edge(int a, int b)
{
this.a = a;
this.b = b;
}
public boolean equals(Object o)
{
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
// Compares a non directed edge between two points. Therefore we must treat edge equivalence as
// edge(ab)=edge(ab) OR edge(ab)=edge(ba).
Edge that = (Edge) o;
return (this.a == that.a && this.b == that.b)
|| (this.a == that.b && this.b == that.a);
}
public int hashCode()
{
// Represents the hash for a a non directed edge between two points. Therefore we use a non-commutative
// hash so that hash(ab)=hash(ba).
return this.a + this.b;
}
}
//**************************************************************//
//******************** Subdivision Points ********************//
//**************************************************************//
public int getSubdivisionPointsVertexCount(int subdivisions)
{
return (1 << subdivisions) + 1;
}
public void makeSubdivisionPoints(float x1, float y1, float z1, float x2, float y2, float z2,
int subdivisions, float[] dest)
{
int numPoints = this.getSubdivisionPointsVertexCount(subdivisions);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "subdivisions=" + subdivisions);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < numCoords)
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int first, last;
int index;
first = 0;
last = numPoints - 1;
index = 3 * first;
dest[index] = x1;
dest[index + 1] = y1;
dest[index + 2] = z1;
index = 3 * last;
dest[index] = x2;
dest[index + 1] = y2;
dest[index + 2] = z2;
this.subdivide(x1, y1, z1, x2, y2, z2, subdivisions, dest, first, last);
}
private void subdivide(float x1, float y1, float z1, float x2, float y2, float z2, int subdivisions,
float[] dest, int first, int last)
{
float x, y, z;
int mid, index;
if (subdivisions <= 0)
return;
x = (x1 + x2) / 2.0f;
y = (y1 + y2) / 2.0f;
z = (z1 + z2) / 2.0f;
mid = (first + last) / 2;
index = mid * 3;
dest[index] = x;
dest[index + 1] = y;
dest[index + 2] = z;
if (subdivisions > 1)
{
this.subdivide(x1, y1, z1, x, y, z, subdivisions - 1, dest, first, mid);
this.subdivide(x, y, z, x2, y2, z2, subdivisions - 1, dest, mid, last);
}
}
//**************************************************************//
//******************** Bilinear Surface ********************//
//**************************************************************//
public int getBilinearSurfaceFillIndexCount(int uStacks, int vStacks)
{
return vStacks * 2 * (uStacks + 1) + 2 * (vStacks - 1);
}
public int getBilinearSurfaceOutlineIndexCount(int uStacks, int vStacks, int mask)
{
int count = 0;
if ((mask & TOP) != 0)
count += 2 * uStacks;
if ((mask & BOTTOM) != 0)
count += 2 * uStacks;
if ((mask & LEFT) != 0)
count += 2 * vStacks;
if ((mask & RIGHT) != 0)
count += 2 * vStacks;
return count;
}
public int getBilinearSurfaceVertexCount(int uStacks, int vStacks)
{
return (uStacks + 1) * (vStacks + 1);
}
public int getBilinearSurfaceFillDrawMode()
{
return GL.GL_TRIANGLE_STRIP;
}
public int getBilinearSurfaceOutlineDrawMode()
{
return GL.GL_LINES;
}
public void makeBilinearSurfaceFillIndices(int vertexPos, int uStacks, int vStacks, int destPos, int[] dest)
{
int numIndices = this.getBilinearSurfaceFillIndexCount(uStacks, vStacks);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "uStacks=" + uStacks
+ " vStacks=" + vStacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < (numIndices + destPos))
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int ui, vi;
int vertex, index;
index = destPos;
for (vi = 0; vi < vStacks; vi++)
{
if (vi != 0)
{
if (this.orientation == INSIDE)
{
vertex = uStacks + vi * (uStacks + 1);
dest[index++] = vertexPos + vertex;
vertex = vi * (uStacks + 1);
dest[index++] = vertexPos + vertex;
}
else // (this.orientation == OUTSIDE)
{
vertex = uStacks + (vi - 1) * (uStacks + 1);
dest[index++] = vertexPos + vertex;
vertex = vi * (uStacks + 1) + (uStacks + 1);
dest[index++] = vertexPos + vertex;
}
}
for (ui = 0; ui <= uStacks; ui++)
{
vertex = ui + vi * (uStacks + 1);
if (this.orientation == INSIDE)
{
dest[index++] = vertexPos + vertex;
dest[index++] = vertexPos + vertex + (uStacks + 1);
}
else // (this.orientation == OUTSIDE)
{
dest[index++] = vertexPos + vertex + (uStacks + 1);
dest[index++] = vertexPos + vertex;
}
}
}
}
public void makeBilinearSurfaceOutlineIndices(int vertexPos, int uStacks, int vStacks, int mask, int destPos,
int[] dest)
{
int numIndices = this.getBilinearSurfaceOutlineIndexCount(uStacks, vStacks, mask);
if (numIndices < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "uStacks=" + uStacks
+ " vStacks=" + vStacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < (numIndices + destPos))
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int ui, vi;
int vertex, index;
index = destPos;
// Bottom row.
if ((mask & BOTTOM) != 0)
{
for (ui = 0; ui < uStacks; ui++)
{
vertex = ui;
dest[index++] = vertexPos + vertex;
vertex = ui + 1;
dest[index++] = vertexPos + vertex;
}
}
// Right side.
if ((mask & RIGHT) != 0)
{
for (vi = 0; vi < vStacks; vi++)
{
vertex = uStacks + vi * (uStacks + 1);
dest[index++] = vertexPos + vertex;
vertex = uStacks + (vi + 1) * (uStacks + 1);
dest[index++] = vertexPos + vertex;
}
}
// Top side.
if ((mask & TOP) != 0)
{
for (ui = uStacks; ui > 0; ui--)
{
vertex = ui + vStacks * (uStacks + 1);
dest[index++] = vertexPos + vertex;
vertex = (ui - 1) + vStacks * (uStacks + 1);
dest[index++] = vertexPos + vertex;
}
}
// Left side.
if ((mask & LEFT) != 0)
{
for (vi = vStacks; vi > 0; vi--)
{
vertex = vi * (uStacks + 1);
dest[index++] = vertexPos + vertex;
vertex = (vi - 1) * (uStacks + 1);
dest[index++] = vertexPos + vertex;
}
}
}
public void makeBilinearSurfaceVertices(float[] control, int destPos, int uStacks, int vStacks, float[] dest)
{
int numPoints = this.getBilinearSurfaceVertexCount(uStacks, vStacks);
int numCoords = 3 * numPoints;
if (control == null)
{
String message = "nullValue.ControlPointArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (control.length < 12)
{
String message = "generic.ControlPointArrayInvalidLength " + control.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "uStacks=" + uStacks
+ " vStacks=" + vStacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < (numCoords + 3 * destPos))
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
float x, y, z;
float u, v;
float du, dv;
float oneMinusU, oneMinusV;
int ui, vi;
int index;
du = 1.0f / (float) uStacks;
dv = 1.0f / (float) vStacks;
for (vi = 0; vi <= vStacks; vi++)
{
v = vi * dv;
oneMinusV = 1.0f - v;
for (ui = 0; ui <= uStacks; ui++)
{
u = ui * du;
oneMinusU = 1.0f - u;
index = ui + vi * (uStacks + 1);
index = 3 * (destPos + index);
x = oneMinusU * oneMinusV * control[0] // Lower left control point
+ u * oneMinusV * control[3] // Lower right control point
+ u * v * control[6] // Upper right control point
+ oneMinusU * v * control[9]; // Upper left control point
y = oneMinusU * oneMinusV * control[1]
+ u * oneMinusV * control[4]
+ u * v * control[7]
+ oneMinusU * v * control[10];
z = oneMinusU * oneMinusV * control[2]
+ u * oneMinusV * control[5]
+ u * v * control[8]
+ oneMinusU * v * control[11];
dest[index] = x;
dest[index + 1] = y;
dest[index + 2] = z;
}
}
}
public void makeBilinearSurfaceVertexNormals(int srcPos, int uStacks, int vStacks, float[] srcVerts,
int destPos, float dest[])
{
int numPoints = this.getBilinearSurfaceVertexCount(uStacks, vStacks);
int numCoords = 3 * numPoints;
if (numPoints < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "uStacks=" + uStacks
+ " vStacks=" + vStacks);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (srcVerts == null)
{
String message = "nullValue.SourceVertexArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest == null)
{
String message = "nullValue.DestinationArrayIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (dest.length < (numCoords + 3 * destPos))
{
String message = "generic.DestinationArrayInvalidLength " + dest.length;
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
int ui, vi;
int index;
int vprev, vnext;
float nsign;
float[] norm, zero, tmp;
nsign = (this.orientation == OUTSIDE) ? 1.0f : -1.0f;
norm = new float[3];
zero = new float[3];
tmp = new float[3];
for (vi = 0; vi <= vStacks; vi++)
{
for (ui = 0; ui <= uStacks; ui++)
{
index = ui + vi * (uStacks + 1);
index = srcPos + index;
vprev = index - (uStacks + 1);
vnext = index + (uStacks + 1);
System.arraycopy(zero, 0, norm, 0, 3);
// Adjacent faces below.
if (vi > 0)
{
// Adjacent faces below and to the left.
if (ui > 0)
{
this.facenorm(srcVerts, index, index - 1, vprev - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, vprev - 1, vprev, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
// Adjacent faces below and to the right.
if (ui < uStacks)
{
this.facenorm(srcVerts, index, vprev, vprev + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, vprev + 1, index + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
}
// Adjacent faces above.
if (vi < vStacks)
{
// Adjacent faces above and to the left.
if (ui > 0)
{
this.facenorm(srcVerts, index, vnext, vnext - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, vnext - 1, index - 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
// Adjacent faces above and to the right.
if (ui < uStacks)
{
this.facenorm(srcVerts, index, index + 1, vnext + 1, tmp);
this.add3AndSet(norm, 0, tmp, 0);
this.facenorm(srcVerts, index, vnext + 1, vnext, tmp);
this.add3AndSet(norm, 0, tmp, 0);
}
}
// Normalize and place in output.
this.mul3AndSet(norm, 0, nsign);
this.norm3AndSet(norm, 0);
System.arraycopy(norm, 0, dest, 3 * index, 3);
}
}
}
//**************************************************************//
//******************** 2D Shapes *****************************//
//**************************************************************//
/**
* Creates a vertex buffer for a two-dimensional ellipse centered at the specified location and with the specified
* radii. The ellipse's center is placed at (x, y)
, it has a width of 2 * majorRadius
, and
* a height of 2 * minorRadius
.
*
* If the specified slices
is greater than 1 this returns a buffer with vertices evenly spaced along
* the circumference of the ellipse. Otherwise this returns a buffer with one vertex.
*
* The returned buffer contains pairs of xy coordinates representing the location of each vertex in the ellipse in a
* counter-clockwise winding order relative to the z axis. The buffer may be rendered in OpenGL as either a triangle
* fan or a line loop.
*
* @param x the x-coordinate of the ellipse's center.
* @param y the y-coordinate of the ellipse's center.
* @param majorRadius the ellipse's radius along the x axis.
* @param minorRadius the ellipse's radius along the y axis.
* @param slices the number of slices in the ellipse.
*
* @return a buffer containing the ellipse's x and y locations.
*
* @throws IllegalArgumentException if any of majorRadius
, minorRadius
, or
* slices
are less than zero.
*/
public FloatBuffer makeEllipse(float x, float y, float majorRadius, float minorRadius, int slices)
{
if (majorRadius < 0)
{
String message = Logging.getMessage("Geom.RadiusIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (minorRadius < 0)
{
String message = Logging.getMessage("Geom.RadiusIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (slices < 0)
{
String message = Logging.getMessage("generic.NumSlicesIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Return a buffer with only the first point at angle 0 if the number of slices is zero or one.
if (slices <= 1)
{
// The buffer contains one coordinate pair.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(2);
buffer.put(x + majorRadius);
buffer.put(y);
buffer.rewind();
return buffer;
}
float step = (float) Math.PI * 2f / (float) slices;
float angle = 0;
// The buffer contains one coordinate pair per slice.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(2 * slices);
// Add each vertex on the circumference of the ellipse, starting at zero and ending one step before 360.
for (int i = 0; i < slices; i++, angle += step)
{
buffer.put(x + (float) Math.cos(angle) * majorRadius);
buffer.put(y + (float) Math.sin(angle) * minorRadius);
}
// Rewind and return.
buffer.rewind();
return buffer;
}
/**
* Creates a vertex buffer for a two-dimensional ellipse centered at the specified location and with the specified
* radii. The ellipse's center is placed at (x, y)
, it has a width of 2 * majorRadius
, and
* a height of 2 * minorRadius
.
*
* If the specified slices
is greater than 1 this returns a buffer with vertices evenly spaced along
* the circumference of the ellipse. Otherwise this returns a buffer with one vertex.
*
* If the specified leaderWidth
is greater than zero and the location (leaderX, leaderY)
* is outside of the rectangle that encloses the ellipse, the ellipse has a triangle attached to one side with with
* its top pointing at (leaderX, leaderY)
. Otherwise this returns an ellipse with no leader and is
* equivalent to calling {@link #makeEllipse(float, float, float, float, int)}
. The leader is attached
* at the center of either the top, bottom, left, or right side, depending on the leader's location relative to the
* ellipse. The leader width is limited in size by the side it is attached to. For example, if the leader is
* attached to the ellipse's bottom, its width is limited by the ellipse's major radius.
*
* @param x the x-coordinate of the ellipse's center.
* @param y the y-coordinate of the ellipse's center.
* @param majorRadius the ellipse's radius along the x axis.
* @param minorRadius the ellipse's radius along the y axis.
* @param slices the number of slices in the ellipse.
* @param leaderX the x-coordinate the leader points to.
* @param leaderY the y-coordinate the leader points to.
* @param leaderWidth the leader triangle's width.
*
* @return a buffer containing the ellipse's x and y locations.
*
* @throws IllegalArgumentException if any of majorRadius
, minorRadius
,
* slices
, or leaderWidth
are less than zero.
*/
public FloatBuffer makeEllipseWithLeader(float x, float y, float majorRadius, float minorRadius, int slices,
float leaderX, float leaderY, float leaderWidth)
{
if (majorRadius < 0)
{
String message = Logging.getMessage("Geom.RadiusIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (minorRadius < 0)
{
String message = Logging.getMessage("Geom.RadiusIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (slices < 0)
{
String message = Logging.getMessage("generic.NumSlicesIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (leaderWidth < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Return an ellipse without a leader if the leader width is zero.
if (leaderWidth == 0)
return this.makeEllipse(x, y, majorRadius, minorRadius, slices);
int leaderCode = this.computeLeaderLocationCode(x - majorRadius, y - minorRadius, x + majorRadius,
y + minorRadius, leaderX, leaderY);
// Return an ellipse without a leader if the leader point is inside the rectangle.
if (leaderCode == LEADER_LOCATION_INSIDE)
return this.makeEllipse(x, y, majorRadius, minorRadius, slices);
// Return a buffer with only the first point at angle 0 if the number of slices is zero or one.
if (slices <= 1)
{
// The buffer contains one coordinate pair.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(2);
buffer.put(x + majorRadius);
buffer.put(y);
buffer.rewind();
return buffer;
}
// Determine the leader's size in radians and the starting angle according to the leader's location relative to
// the ellipse.
float leaderAngle;
float startAngle;
if ((leaderCode & LEADER_LOCATION_BOTTOM) != 0)
{
// Limit the leader's width by the ellipse's major radius.
float maxLeaderWidth = 2f * majorRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
leaderAngle = leaderWidth / majorRadius;
startAngle = 3f * (float) Math.PI / 2f;
}
else if ((leaderCode & LEADER_LOCATION_TOP) != 0)
{
// Limit the leader's width by the ellipse's major radius.
float maxLeaderWidth = 2f * majorRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
leaderAngle = leaderWidth / majorRadius;
startAngle = (float) Math.PI / 2f;
}
else if ((leaderCode & LEADER_LOCATION_LEFT) != 0)
{
// Limit the leader's width by the ellipse's minor radius.
float maxLeaderWidth = 2f * minorRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
leaderAngle = leaderWidth / minorRadius;
startAngle = (float) Math.PI;
}
else if ((leaderCode & LEADER_LOCATION_RIGHT) != 0)
{
// Limit the leader's width by the ellipse's minor radius.
float maxLeaderWidth = 2f * minorRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
leaderAngle = leaderWidth / minorRadius;
startAngle = 0f;
}
else
{
// Return an ellipse without a leader if the leader location code is unrecognized. This should never happen,
// but we check anyway.
return this.makeEllipse(x, y, majorRadius, minorRadius, slices);
}
float step = (float) (Math.PI * 2f - leaderAngle) / (float) slices;
float angle = startAngle + leaderAngle / 2f;
// The buffer contains one coordinate pair per slice, and three coordinate pairs for the leader.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(2 * slices + 6);
// Start in the leader right corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + (float) Math.cos(startAngle + leaderAngle / 2f) * majorRadius);
buffer.put(y + (float) Math.sin(startAngle + leaderAngle / 2f) * minorRadius);
// Add each vertex on the circumference of the ellipse, starting at the right side of the leader, and ending at
// the left side of the leader.
for (int i = 0; i < slices; i++, angle += step)
{
buffer.put(x + (float) Math.cos(angle) * majorRadius);
buffer.put(y + (float) Math.sin(angle) * minorRadius);
}
// Leader left corner.
buffer.put(x + (float) Math.cos(startAngle - leaderAngle / 2f) * majorRadius);
buffer.put(y + (float) Math.sin(startAngle - leaderAngle / 2f) * minorRadius);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
/**
* Creates a vertex buffer for a two-dimensional rectangle at the specified location, and with the specified size.
* The rectangle's lower left corner is placed at (x, y)
, and its upper right corner is placed at
* (x + width, y + height)
.
*
* The returned buffer contains pairs of xy coordinates representing the location of each vertex in the rectangle in
* a counter-clockwise winding order relative to the z axis. The buffer may be rendered in OpenGL as either a
* triangle fan or a line loop.
*
* @param x the x-coordinate of the rectangle's lower left corner.
* @param y the y-coordinate of the rectangle's lower left corner.
* @param width the rectangle's width.
* @param height the rectangle's height.
*
* @return a buffer containing the rectangle's x and y locations.
*
* @throws IllegalArgumentException if either width
or height
are less than zero.
*/
public FloatBuffer makeRectangle(float x, float y, float width, float height)
{
if (width < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (height < 0)
{
String message = Logging.getMessage("Geom.HeightIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// The buffer contains eight coordinate pairs: two pairs for each corner.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(8);
// Lower left corner.
buffer.put(x);
buffer.put(y);
// Lower right corner.
buffer.put(x + width);
buffer.put(y);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height);
// Upper left corner.
buffer.put(x);
buffer.put(y + height);
// Rewind and return.
buffer.rewind();
return buffer;
}
/**
* Creates a vertex buffer for a two-dimensional rectangle at the specified location, with the specified size, and
* with optionally rounded corners. The rectangle's lower left corner is placed at the (x, y)
, and its
* upper right corner is placed at (x + width, y + height)
.
*
* If the specified cornerRadius
and cornerSlices
are greater than 0, the rectangle's
* corners have a rounded appearance. The radius specifies the size of a rounded corner, and the slices specifies
* the number of segments that make a rounded corner. If either cornerRadius
or
* cornerSlices
are 0, this returns a rectangle with sharp corners and is equivalent to calling
* {@link #makeRectangle(float, float, float, float)}
. The cornerRadius
is limited by the
* rectangle's width and height. For example, if the corner radius is 100 and the width and height are 50 and 100,
* the actual corner radius used is 25 - half of the rectangle's smallest dimension.
*
* The returned buffer contains pairs of xy coordinates representing the location of each vertex in the rectangle in
* a counter-clockwise winding order relative to the z axis. The buffer may be rendered in OpenGL as either a
* triangle fan or a line loop.
*
* @param x the x-coordinate of the rectangle's lower left corner.
* @param y the y-coordinate of the rectangle's lower left corner.
* @param width the rectangle's width.
* @param height the rectangle's height.
* @param cornerRadius the rectangle's rounded corner radius, or 0 to disable rounded corners.
* @param cornerSlices the number of slices in each rounded corner, or 0 to disable rounded corners.
*
* @return a buffer containing the rectangle's x and y locations.
*
* @throws IllegalArgumentException if any of width
, height
, cornerRadius
, or
* cornerSlices
are less than zero.
*/
public FloatBuffer makeRectangle(float x, float y, float width, float height, float cornerRadius, int cornerSlices)
{
if (width < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (height < 0)
{
String message = Logging.getMessage("Geom.HeightIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (cornerRadius < 0)
{
String message = Logging.getMessage("Geom.RadiusIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (cornerSlices < 0)
{
String message = Logging.getMessage("generic.NumSlicesIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Limit the corner radius to half of the rectangles width or height, whichever is smaller.
float maxCornerRadius = Math.min(width, height) / 2f;
if (cornerRadius > maxCornerRadius)
cornerRadius = maxCornerRadius;
// Create a rectangle with sharp corners if either the corner radius or the number of corner slices is 0.
if (cornerRadius == 0f || cornerSlices == 0)
return this.makeRectangle(x, y, width, height);
float piOver2 = (float) Math.PI / 2f;
// The buffer contains four coordinate pairs for each corner, and two coordinate pairs per corner vertex.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(16 + 8 * (cornerSlices - 1));
// Lower left corner.
buffer.put(x);
buffer.put(y + cornerRadius);
this.addRectangleRoundedCorner(x + cornerRadius, x + cornerRadius, cornerRadius, (float) Math.PI, piOver2,
cornerSlices, buffer);
buffer.put(x + cornerRadius);
buffer.put(y);
// Lower right corner.
buffer.put(x + width - cornerRadius);
buffer.put(y);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + cornerRadius, cornerRadius, -piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x + width);
buffer.put(y + cornerRadius);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height - cornerRadius);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0f, piOver2,
cornerSlices, buffer);
buffer.put(x + width - cornerRadius);
buffer.put(y + height);
// Upper left corner.
buffer.put(x + cornerRadius);
buffer.put(y + height);
this.addRectangleRoundedCorner(x + cornerRadius, y + height - cornerRadius, cornerRadius, piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x);
buffer.put(y + height - cornerRadius);
// Rewind and return.
buffer.rewind();
return buffer;
}
/**
* Creates a vertex buffer for a two-dimensional rectangle at the specified location, with the specified size, and
* with an optional leader pointing to the specified leader location. The rectangle's lower left corner is placed at
* (x, y)
, and its upper right corner is placed at (x + width, y + height)
.
*
* If the specified leaderWidth
is greater than zero and the location (leaderX, leaderY)
* is outside of the rectangle, the rectangle has a triangle attached to one side with with its top pointing at
* (leaderX, leaderY)
. Otherwise this returns a rectangle with no leader and is equivalent to calling
* {@link #makeRectangle(float, float, float, float)}
. The leader is attached at the center of either
* the top, bottom, left, or right side, depending on the leader's location relative to the rectangle. The leader
* width is limited in size by the side it is attached to. For example, if the leader is attached to the rectangle's
* bottom, its width is limited by the rectangle's width.
*
* The returned buffer contains pairs of xy coordinates representing the location of each vertex in the rectangle in
* a counter-clockwise winding order relative to the z axis. The buffer may be rendered in OpenGL as either a
* triangle fan or a line loop.
*
* @param x the x-coordinate of the rectangle's lower left corner.
* @param y the y-coordinate of the rectangle's lower left corner.
* @param width the rectangle's width.
* @param height the rectangle's height.
* @param leaderX the x-coordinate the leader points to.
* @param leaderY the y-coordinate the leader points to.
* @param leaderWidth the leader triangle's width.
*
* @return a buffer containing the rectangle's x and y locations.
*
* @throws IllegalArgumentException if any of width
, height
, or leaderWidth
* are less than zero.
*/
@SuppressWarnings({"SuspiciousNameCombination"})
public FloatBuffer makeRectangleWithLeader(float x, float y, float width, float height, float leaderX,
float leaderY, float leaderWidth)
{
if (width < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (height < 0)
{
String message = Logging.getMessage("Geom.HeightIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (leaderWidth < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Return a rectangle without a leader if the leader width is zero.
if (leaderWidth == 0)
return this.makeRectangle(x, y, width, height);
int leaderCode = this.computeLeaderLocationCode(x, y, x + width, y + height, leaderX, leaderY);
// Return a rectangle without a leader if the leader point is inside the rectangle.
if (leaderCode == LEADER_LOCATION_INSIDE)
return this.makeRectangle(x, y, width, height);
if ((leaderCode & LEADER_LOCATION_BOTTOM) != 0)
{
// Limit the leader's width by the rectangle's width.
if (leaderWidth > width)
leaderWidth = width;
// The buffer contains seven xy coordinate pairs: two pairs for each corner and three pairs for the leader.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(14);
// Start in the leader right corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + width / 2f + leaderWidth / 2f);
buffer.put(y);
// Lower right corner.
buffer.put(x + width);
buffer.put(y);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height);
// Upper left corner.
buffer.put(x);
buffer.put(y + height);
// Lower left corner.
buffer.put(x);
buffer.put(y);
// Leader left corner.
buffer.put(x + width / 2f - leaderWidth / 2f);
buffer.put(y);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else if ((leaderCode & LEADER_LOCATION_TOP) != 0)
{
// Limit the leader's width by the rectangle's width.
if (leaderWidth > width)
leaderWidth = width;
// The buffer contains seven xy coordinate pairs: two pairs for each corner and three pairs for the leader.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(14);
// Start in the leader left corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + width / 2f - leaderWidth / 2f);
buffer.put(y + height);
// Upper left corner.
buffer.put(x);
buffer.put(y + height);
// Lower left corner.
buffer.put(x);
buffer.put(y);
// Lower right corner.
buffer.put(x + width);
buffer.put(y);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height);
// Leader right corner.
buffer.put(x + width / 2f + leaderWidth / 2f);
buffer.put(y + height);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else if ((leaderCode & LEADER_LOCATION_LEFT) != 0)
{
// Limit the leader's width by the rectangle's height.
if (leaderWidth > height)
{
//noinspection SuspiciousNameCombination
leaderWidth = height;
}
// The buffer contains seven xy coordinate pairs: two pairs for each corner and three pairs for the leader.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(14);
// Start in the leader bottom corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x);
buffer.put(y + height / 2f - leaderWidth / 2f);
// Lower left corner.
buffer.put(x);
buffer.put(y);
// Lower right corner.
buffer.put(x + width);
buffer.put(y);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height);
// Upper left corner.
buffer.put(x);
buffer.put(y + height);
// Leader top corner.
buffer.put(x);
buffer.put(y + height / 2f + leaderWidth / 2f);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else if ((leaderCode & LEADER_LOCATION_RIGHT) != 0)
{
// Limit the leader's width by the rectangle's height.
if (leaderWidth > height)
{
//noinspection SuspiciousNameCombination
leaderWidth = height;
}
// The buffer contains seven xy coordinate pairs: two pairs for each corner and three pairs for the leader.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(14);
// Start in the leader top corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + width);
buffer.put(y + height / 2f + leaderWidth / 2f);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height);
// Upper left corner.
buffer.put(x);
buffer.put(y + height);
// Lower left corner.
buffer.put(x);
buffer.put(y);
// Lower right corner.
buffer.put(x + width);
buffer.put(y);
// Leader bottom corner.
buffer.put(x + width);
buffer.put(y + height / 2f - leaderWidth / 2f);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else
{
// Return a rectangle without a leader if the leader location code is unrecognized. This should never
// happen, but we check anyway.
return this.makeRectangle(x, y, width, height);
}
}
/**
* Creates a vertex buffer for a two-dimensional rectangle at the specified location, with the specified size, and
* with optionally rounded corners. The rectangle's lower left corner is placed at the (x, y)
, and its
* upper right corner is placed at (x + width, y + height)
.
*
* If the specified cornerRadius
and cornerSlices
are greater than 0, the rectangle's
* corners have a rounded appearance. The radius specifies the size of a rounded corner, and the slices specifies
* the number of segments that make a rounded corner. If either cornerRadius
or
* cornerSlices
are 0, this returns a rectangle with sharp corners and is equivalent to calling
* {@link #makeRectangleWithLeader(float, float, float, float, float, float, float)}
. The
* cornerRadius
is limited by the rectangle's width and height. For example, if the corner radius is
* 100 and the width and height are 50 and 100, the actual corner radius used is 25 - half of the rectangle's
* smallest dimension.
*
* If the specified leaderWidth
is greater than zero and the location (leaderX, leaderY)
* is outside of the rectangle, the rectangle has a triangle attached to one side with with its top pointing at
* (leaderX, leaderY)
. Otherwise this returns a rectangle with no leader and is equivalent to calling
* {@link #makeRectangle(float, float, float, float, float, int)}
. The leader is attached at the center
* of either the top, bottom, left, or right side, depending on the leader's location relative to the rectangle. The
* leader width is limited in size by the side it is attached to. For example, if the leader is attached to the
* rectangle's bottom, its width is limited by the rectangle's width minus any area used by the rounded corners.
*
* The returned buffer contains pairs of xy coordinates representing the location of each vertex in the rectangle in
* a counter-clockwise winding order relative to the z axis. The buffer may be rendered in OpenGL as either a
* triangle fan or a line loop.
*
* @param x the x-coordinate of the rectangle's lower left corner.
* @param y the y-coordinate of the rectangle's lower left corner.
* @param width the rectangle's width.
* @param height the rectangle's height.
* @param cornerRadius the rectangle's rounded corner radius, or 0 to disable rounded corners.
* @param cornerSlices the number of slices in each rounded corner, or 0 to disable rounded corners.
* @param leaderX the x-coordinate the leader points to.
* @param leaderY the y-coordinate the leader points to.
* @param leaderWidth the leader triangle's width.
*
* @return a buffer containing the rectangle's x and y locations.
*
* @throws IllegalArgumentException if any of width
, height
, cornerRadius
,
* cornerSlices
, or leaderWidth
are less than zero.
*/
public FloatBuffer makeRectangleWithLeader(float x, float y, float width, float height, float cornerRadius,
int cornerSlices, float leaderX, float leaderY, float leaderWidth)
{
if (width < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (height < 0)
{
String message = Logging.getMessage("Geom.HeightIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (cornerRadius < 0)
{
String message = Logging.getMessage("Geom.RadiusIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (cornerSlices < 0)
{
String message = Logging.getMessage("generic.NumSlicesIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (leaderWidth < 0)
{
String message = Logging.getMessage("Geom.WidthIsNegative");
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
// Limit the corner radius to half of the rectangles width or height, whichever is smaller.
float maxCornerRadius = Math.min(width, height) / 2f;
if (cornerRadius > maxCornerRadius)
cornerRadius = maxCornerRadius;
// Create a rectangle with sharp corners if either the corner radius or the number of corner slices is 0.
if (cornerRadius == 0f || cornerSlices == 0)
return this.makeRectangleWithLeader(x, y, width, height, leaderX, leaderY, leaderWidth);
// Return a rectangle without a leader if the leader width is zero.
if (leaderWidth == 0)
return this.makeRectangle(x, y, width, height, cornerRadius, cornerSlices);
int leaderCode = this.computeLeaderLocationCode(x, y, x + width, y + height, leaderX, leaderY);
// Return a rectangle without a leader if the leader point is inside the rectangle.
if (leaderCode == LEADER_LOCATION_INSIDE)
return this.makeRectangle(x, y, width, height, cornerRadius, cornerSlices);
float piOver2 = (float) Math.PI / 2f;
if ((leaderCode & LEADER_LOCATION_BOTTOM) != 0)
{
// Limit the leader width by the rectangle's width minus any width used by the rounded corners.
float maxLeaderWidth = width - 2f * cornerRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
// The buffer contains two coordinate pairs for each corner, three coordinate pairs for the leader, and two
// coordinate pairs per corner vertex.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(22 + 8 * (cornerSlices - 1));
// Start in the leader right corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + width / 2f + leaderWidth / 2f);
buffer.put(y);
// Lower right corner.
buffer.put(x + width - cornerRadius);
buffer.put(y);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + cornerRadius, cornerRadius, -piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x + width);
buffer.put(y + cornerRadius);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height - cornerRadius);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0f,
piOver2,
cornerSlices, buffer);
buffer.put(x + width - cornerRadius);
buffer.put(y + height);
// Upper left corner.
buffer.put(x + cornerRadius);
buffer.put(y + height);
this.addRectangleRoundedCorner(x + cornerRadius, y + height - cornerRadius, cornerRadius, piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x);
buffer.put(y + height - cornerRadius);
// Lower left corner.
buffer.put(x);
buffer.put(y + cornerRadius);
this.addRectangleRoundedCorner(x + cornerRadius, x + cornerRadius, cornerRadius, (float) Math.PI, piOver2,
cornerSlices, buffer);
buffer.put(x + cornerRadius);
buffer.put(y);
// Leader left corner.
buffer.put(x + width / 2f - leaderWidth / 2f);
buffer.put(y);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else if ((leaderCode & LEADER_LOCATION_TOP) != 0)
{
// Limit the leader width by the rectangle's width minus any width used by the rounded corners.
float maxLeaderWidth = width - 2f * cornerRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
// The buffer contains two coordinate pairs for each corner, three coordinate pairs for the leader, and two
// coordinate pairs per corner vertex.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(22 + 8 * (cornerSlices - 1));
// Start in the leader left corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + width / 2f - leaderWidth / 2f);
buffer.put(y + height);
// Upper left corner.
buffer.put(x + cornerRadius);
buffer.put(y + height);
this.addRectangleRoundedCorner(x + cornerRadius, y + height - cornerRadius, cornerRadius, piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x);
buffer.put(y + height - cornerRadius);
// Lower left corner.
buffer.put(x);
buffer.put(y + cornerRadius);
this.addRectangleRoundedCorner(x + cornerRadius, x + cornerRadius, cornerRadius, (float) Math.PI, piOver2,
cornerSlices, buffer);
buffer.put(x + cornerRadius);
buffer.put(y);
// Lower right corner.
buffer.put(x + width - cornerRadius);
buffer.put(y);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + cornerRadius, cornerRadius, -piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x + width);
buffer.put(y + cornerRadius);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height - cornerRadius);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0f,
piOver2,
cornerSlices, buffer);
buffer.put(x + width - cornerRadius);
buffer.put(y + height);
// Leader right corner.
buffer.put(x + width / 2f + leaderWidth / 2f);
buffer.put(y + height);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else if ((leaderCode & LEADER_LOCATION_LEFT) != 0)
{
// Limit the leader width by the rectangle's height minus any width used by the rounded corners.
float maxLeaderWidth = height - 2f * cornerRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
// The buffer contains two coordinate pairs for each corner, three coordinate pairs for the leader, and two
// coordinate pairs per corner vertex.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(22 + 8 * (cornerSlices - 1));
// Start in the leader bottom corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x);
buffer.put(y + height / 2f - leaderWidth / 2f);
// Lower left corner.
buffer.put(x);
buffer.put(y + cornerRadius);
this.addRectangleRoundedCorner(x + cornerRadius, x + cornerRadius, cornerRadius, (float) Math.PI, piOver2,
cornerSlices, buffer);
buffer.put(x + cornerRadius);
buffer.put(y);
// Lower right corner.
buffer.put(x + width - cornerRadius);
buffer.put(y);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + cornerRadius, cornerRadius, -piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x + width);
buffer.put(y + cornerRadius);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height - cornerRadius);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0f,
piOver2,
cornerSlices, buffer);
buffer.put(x + width - cornerRadius);
buffer.put(y + height);
// Upper left corner.
buffer.put(x + cornerRadius);
buffer.put(y + height);
this.addRectangleRoundedCorner(x + cornerRadius, y + height - cornerRadius, cornerRadius, piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x);
buffer.put(y + height - cornerRadius);
// Leader top corner.
buffer.put(x);
buffer.put(y + height / 2f + leaderWidth / 2f);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else if ((leaderCode & LEADER_LOCATION_RIGHT) != 0)
{
// Limit the leader width by the rectangle's height minus any width used by the rounded corners.
float maxLeaderWidth = height - 2f * cornerRadius;
if (leaderWidth > maxLeaderWidth)
leaderWidth = maxLeaderWidth;
// The buffer contains two coordinate pairs for each corner, three coordinate pairs for the leader, and two
// coordinate pairs per corner vertex.
FloatBuffer buffer = Buffers.newDirectFloatBuffer(22 + 8 * (cornerSlices - 1));
// Start in the leader top corner to ensure the vertices can be drawn as a triangle fan.
buffer.put(x + width);
buffer.put(y + height / 2f + leaderWidth / 2f);
// Upper right corner.
buffer.put(x + width);
buffer.put(y + height - cornerRadius);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + height - cornerRadius, cornerRadius, 0f,
piOver2,
cornerSlices, buffer);
buffer.put(x + width - cornerRadius);
buffer.put(y + height);
// Upper left corner.
buffer.put(x + cornerRadius);
buffer.put(y + height);
this.addRectangleRoundedCorner(x + cornerRadius, y + height - cornerRadius, cornerRadius, piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x);
buffer.put(y + height - cornerRadius);
// Lower left corner.
buffer.put(x);
buffer.put(y + cornerRadius);
this.addRectangleRoundedCorner(x + cornerRadius, x + cornerRadius, cornerRadius, (float) Math.PI, piOver2,
cornerSlices, buffer);
buffer.put(x + cornerRadius);
buffer.put(y);
// Lower right corner.
buffer.put(x + width - cornerRadius);
buffer.put(y);
this.addRectangleRoundedCorner(x + width - cornerRadius, y + cornerRadius, cornerRadius, -piOver2, piOver2,
cornerSlices, buffer);
buffer.put(x + width);
buffer.put(y + cornerRadius);
// Leader bottom corner.
buffer.put(x + width);
buffer.put(y + height / 2f - leaderWidth / 2f);
// Leader point.
buffer.put(leaderX);
buffer.put(leaderY);
// Rewind and return.
buffer.rewind();
return buffer;
}
else
{
// Return a rectangle without a leader if the leader location code is unrecognized. This should never
// happen, but we check anyway.
return this.makeRectangle(x, y, width, height, cornerRadius, cornerSlices);
}
}
/**
* Adds the vertices for one rounded corner of a two-dimensional rectangular to the specified buffer
.
* This assumes that the first and last vertices of each corner are created by the caller, so this adds only the
* intermediate vertices. The number of intermediate vertices is equal to slices - 2
. This does nothing
* if slices
is one or zero.
*
* @param x the x-coordinate of the corner's origin.
* @param y the y-coordinate of the corner's origin.
* @param radius the corner's radius.
* @param start the corner's starting angle, in radians.
* @param sweep the corner's angular distance, in radians.
* @param slices the number of slices in the corner.
* @param buffer the buffer the corner's xy coordinates are added to.
*/
protected void addRectangleRoundedCorner(float x, float y, float radius, float start, float sweep, int slices,
FloatBuffer buffer)
{
if (slices == 0f)
return;
float step = sweep / (float) slices;
float angle = start + step;
for (int i = 1; i < slices; i++, angle += step)
{
buffer.put(x + (float) Math.cos(angle) * radius);
buffer.put(y + (float) Math.sin(angle) * radius);
}
}
/**
* Returns a four bit code indicating the leader's location within the specified rectangle. The rectangle's lower
* left corner is located at (x1, y1)
and its upper right corner is located at (x2, y2)
.
* The returned code includes the bit for any of LEADER_LOCATION_LEFT
,
* LEADER_LOCATION_RIGHT
, LEADER_LOCATION_BOTTOM
, and LEADER_LOCATION_TOP
,
* depending on whether the leader is located to the left, right, bottom, or top of the rectangle. If the leader is
* inside the rectangle, this returns LEADER_LOCATION_INSIDE
.
*
* @param x1 the rectangle's minimum x-coordinate.
* @param y1 the rectangle's maximum x-coordinate.
* @param x2 the rectangle's minimum y-coordinate.
* @param y2 the rectangle's maximum y-coordinate.
* @param leaderX the leader's x-coordinate.
* @param leaderY the leader's y-coordinate.
*
* @return a four bit code indicating the leader's location relative to the rectangle.
*/
protected int computeLeaderLocationCode(float x1, float y1, float x2, float y2, float leaderX, float leaderY)
{
return (leaderY > y2 ? LEADER_LOCATION_TOP : 0) // bit 0: top
| (leaderY < y1 ? LEADER_LOCATION_BOTTOM : 0) // bit 1: bottom
| (leaderX > x2 ? LEADER_LOCATION_RIGHT : 0) // bit 2: right
| (leaderX < x1 ? LEADER_LOCATION_LEFT : 0); // bit 3: left
}
//**************************************************************//
//******************** Geometry Support ********************//
//**************************************************************//
public void reversePoints(int pos, int count, T[] points)
{
if (pos < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "pos=" + pos);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (count < 0)
{
String message = Logging.getMessage("generic.ArgumentOutOfRange", "count=" + count);
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points == null)
{
String message = "nullValue.PointsIsNull";
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
if (points.length < (pos + count))
{
String message = Logging.getMessage("generic.ArrayInvalidLength", "points.length < " + (pos + count));
Logging.logger().severe(message);
throw new IllegalArgumentException(message);
}
T tmp;
int i, j, mid;
for (i = 0, mid = count >> 1, j = count - 1; i < mid; i++, j--)
{
tmp = points[pos + i];
points[pos + i] = points[pos + j];
points[pos + j] = tmp;
}
}
private int[] copyOf(int[] original, int newLength)
{
int[] copy;
copy = new int[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
private float[] copyOf(float[] original, int newLength)
{
float[] copy;
copy = new float[newLength];
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
private IntBuffer copyOf(IntBuffer original, int newLength)
{
IntBuffer copy;
copy = Buffers.newDirectIntBuffer(newLength);
original.rewind();
copy.put(original);
return copy;
}
private FloatBuffer copyOf(FloatBuffer original, int newLength)
{
FloatBuffer copy;
copy = Buffers.newDirectFloatBuffer(newLength);
original.rewind();
copy.put(original);
return copy;
}
private void facenorm(float[] srcVerts, int vertA, int vertB, int vertC, float[] dest)
{
int ia, ib, ic;
float[] ab, ac;
ia = 3 * vertA;
ib = 3 * vertB;
ic = 3 * vertC;
ab = new float[3];
ac = new float[3];
this.sub3(srcVerts, ib, srcVerts, ia, ab, 0);
this.sub3(srcVerts, ic, srcVerts, ia, ac, 0);
this.cross3(ab, ac, dest);
this.norm3AndSet(dest, 0);
}
private void facenorm(FloatBuffer srcVerts, int vertA, int vertB, int vertC, float[] dest)
{
int ia, ib, ic;
float[] ab, ac;
ia = 3 * vertA;
ib = 3 * vertB;
ic = 3 * vertC;
ab = new float[3];
ac = new float[3];
this.sub3(srcVerts, ib, srcVerts, ia, ab, 0);
this.sub3(srcVerts, ic, srcVerts, ia, ac, 0);
this.cross3(ab, ac, dest);
this.norm3AndSet(dest, 0);
}
private void add3AndSet(float[] a, int aPos, float[] b, int bPos)
{
a[aPos] = a[aPos] + b[bPos];
a[aPos + 1] = a[aPos + 1] + b[bPos + 1];
a[aPos + 2] = a[aPos + 2] + b[bPos + 2];
}
private void add3AndSet(FloatBuffer a, int aPos, float[] b, int bPos)
{
a.put(aPos, a.get(aPos) + b[bPos]);
a.put(aPos + 1, a.get(aPos + 1) + b[bPos + 1]);
a.put(aPos + 2, a.get(aPos + 2) + b[bPos + 2]);
}
private void sub3(float[] a, int aPos, float[] b, int bPos, float[] dest, int destPos)
{
dest[destPos] = a[aPos] - b[bPos];
dest[destPos + 1] = a[aPos + 1] - b[bPos + 1];
dest[destPos + 2] = a[aPos + 2] - b[bPos + 2];
}
private void sub3(FloatBuffer a, int aPos, FloatBuffer b, int bPos, float[] dest, int destPos)
{
dest[destPos] = a.get(aPos) - b.get(bPos);
dest[destPos + 1] = a.get(aPos + 1) - b.get(bPos + 1);
dest[destPos + 2] = a.get(aPos + 2) - b.get(bPos + 2);
}
private void cross3(float[] a, float[] b, float[] dest)
{
dest[0] = a[1] * b[2] - a[2] * b[1];
dest[1] = a[2] * b[0] - a[0] * b[2];
dest[2] = a[0] * b[1] - a[1] * b[0];
}
private void mul3AndSet(float[] src, int srcPos, float c)
{
src[srcPos] *= c;
src[srcPos + 1] *= c;
src[srcPos + 2] *= c;
}
private void mul3AndSet(FloatBuffer src, int srcPos, float c)
{
src.put(srcPos, src.get(srcPos) * c);
src.put(srcPos + 1, src.get(srcPos + 1) * c);
src.put(srcPos + 2, src.get(srcPos + 2) * c);
}
private void mulAndSet(FloatBuffer src, int srcPos, float b, int offset)
{
src.put(srcPos + offset, src.get(srcPos + offset) * b);
}
private void norm3AndSet(float[] src, int srcPos)
{
float len;
len = src[srcPos] * src[srcPos] + src[srcPos + 1] * src[srcPos + 1] + src[srcPos + 2] * src[srcPos + 2];
if (len != 0.0f)
{
len = (float) Math.sqrt(len);
src[srcPos] /= len;
src[srcPos + 1] /= len;
src[srcPos + 2] /= len;
}
}
private void norm3AndSet(FloatBuffer src, int srcPos)
{
float len;
len = src.get(srcPos) * src.get(srcPos)
+ src.get(srcPos + 1) * src.get(srcPos + 1)
+ src.get(srcPos + 2) * src.get(srcPos + 2);
if (len != 0.0f)
{
len = (float) Math.sqrt(len);
src.put(srcPos, src.get(srcPos) / len);
src.put(srcPos + 1, src.get(srcPos + 1) / len);
src.put(srcPos + 2, src.get(srcPos + 2) / len);
}
}
private int nextPowerOfTwo(int n)
{
int i = 1;
while (i < n)
{
i <<= 1;
}
return i;
}
}