/** * Rebuilds the cylinder based on a new set of parameters. * * @param axisSamples the number of samples along the axis. * @param radialSamples the number of samples around the radial. * @param radius the radius of the bottom of the cylinder. * @param radius2 the radius of the top of the cylinder. * @param height the cylinder's height. * @param closed should the cylinder have top and bottom surfaces. * @param inverted is the cylinder is meant to be viewed from the inside. */ public void updateGeometry( int axisSamples, int radialSamples, float radius, float radius2, float height, boolean closed, boolean inverted) { this.axisSamples = axisSamples + (closed ? 2 : 0); this.radialSamples = radialSamples; this.radius = radius; this.radius2 = radius2; this.height = height; this.closed = closed; this.inverted = inverted; // VertexBuffer pvb = getBuffer(Type.Position); // VertexBuffer nvb = getBuffer(Type.Normal); // VertexBuffer tvb = getBuffer(Type.TexCoord); // Vertices int vertCount = axisSamples * (radialSamples + 1) + (closed ? 2 : 0); setBuffer(Type.Position, 3, createVector3Buffer(getFloatBuffer(Type.Position), vertCount)); // Normals setBuffer(Type.Normal, 3, createVector3Buffer(getFloatBuffer(Type.Normal), vertCount)); // Texture co-ordinates setBuffer(Type.TexCoord, 2, createVector2Buffer(vertCount)); int triCount = ((closed ? 2 : 0) + 2 * (axisSamples - 1)) * radialSamples; setBuffer(Type.Index, 3, createShortBuffer(getShortBuffer(Type.Index), 3 * triCount)); // generate geometry float inverseRadial = 1.0f / radialSamples; float inverseAxisLess = 1.0f / (closed ? axisSamples - 3 : axisSamples - 1); float inverseAxisLessTexture = 1.0f / (axisSamples - 1); float halfHeight = 0.5f * height; // Generate points on the unit circle to be used in computing the mesh // points on a cylinder slice. float[] sin = new float[radialSamples + 1]; float[] cos = new float[radialSamples + 1]; for (int radialCount = 0; radialCount < radialSamples; radialCount++) { float angle = FastMath.TWO_PI * inverseRadial * radialCount; cos[radialCount] = FastMath.cos(angle); sin[radialCount] = FastMath.sin(angle); } sin[radialSamples] = sin[0]; cos[radialSamples] = cos[0]; // calculate normals Vector3f[] vNormals = null; Vector3f vNormal = Vector3f.UNIT_Z; if ((height != 0.0f) && (radius != radius2)) { vNormals = new Vector3f[radialSamples]; Vector3f vHeight = Vector3f.UNIT_Z.mult(height); Vector3f vRadial = new Vector3f(); for (int radialCount = 0; radialCount < radialSamples; radialCount++) { vRadial.set(cos[radialCount], sin[radialCount], 0.0f); Vector3f vRadius = vRadial.mult(radius); Vector3f vRadius2 = vRadial.mult(radius2); Vector3f vMantle = vHeight.subtract(vRadius2.subtract(vRadius)); Vector3f vTangent = vRadial.cross(Vector3f.UNIT_Z); vNormals[radialCount] = vMantle.cross(vTangent).normalize(); } } FloatBuffer nb = getFloatBuffer(Type.Normal); FloatBuffer pb = getFloatBuffer(Type.Position); FloatBuffer tb = getFloatBuffer(Type.TexCoord); // generate the cylinder itself Vector3f tempNormal = new Vector3f(); for (int axisCount = 0, i = 0; axisCount < axisSamples; axisCount++, i++) { float axisFraction; float axisFractionTexture; int topBottom = 0; if (!closed) { axisFraction = axisCount * inverseAxisLess; // in [0,1] axisFractionTexture = axisFraction; } else { if (axisCount == 0) { topBottom = -1; // bottom axisFraction = 0; axisFractionTexture = inverseAxisLessTexture; } else if (axisCount == axisSamples - 1) { topBottom = 1; // top axisFraction = 1; axisFractionTexture = 1 - inverseAxisLessTexture; } else { axisFraction = (axisCount - 1) * inverseAxisLess; axisFractionTexture = axisCount * inverseAxisLessTexture; } } // compute center of slice float z = -halfHeight + height * axisFraction; Vector3f sliceCenter = new Vector3f(0, 0, z); // compute slice vertices with duplication at end point int save = i; for (int radialCount = 0; radialCount < radialSamples; radialCount++, i++) { float radialFraction = radialCount * inverseRadial; // in [0,1) tempNormal.set(cos[radialCount], sin[radialCount], 0.0f); if (vNormals != null) { vNormal = vNormals[radialCount]; } else if (radius == radius2) { vNormal = tempNormal; } if (topBottom == 0) { if (!inverted) nb.put(vNormal.x).put(vNormal.y).put(vNormal.z); else nb.put(-vNormal.x).put(-vNormal.y).put(-vNormal.z); } else { nb.put(0).put(0).put(topBottom * (inverted ? -1 : 1)); } tempNormal.multLocal((radius - radius2) * axisFraction + radius2).addLocal(sliceCenter); pb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z); tb.put((inverted ? 1 - radialFraction : radialFraction)).put(axisFractionTexture); } BufferUtils.copyInternalVector3(pb, save, i); BufferUtils.copyInternalVector3(nb, save, i); tb.put((inverted ? 0.0f : 1.0f)).put(axisFractionTexture); } if (closed) { pb.put(0).put(0).put(-halfHeight); // bottom center nb.put(0).put(0).put(-1 * (inverted ? -1 : 1)); tb.put(0.5f).put(0); pb.put(0).put(0).put(halfHeight); // top center nb.put(0).put(0).put(1 * (inverted ? -1 : 1)); tb.put(0.5f).put(1); } IndexBuffer ib = getIndexBuffer(); int index = 0; // Connectivity for (int axisCount = 0, axisStart = 0; axisCount < axisSamples - 1; axisCount++) { int i0 = axisStart; int i1 = i0 + 1; axisStart += radialSamples + 1; int i2 = axisStart; int i3 = i2 + 1; for (int i = 0; i < radialSamples; i++) { if (closed && axisCount == 0) { if (!inverted) { ib.put(index++, i0++); ib.put(index++, vertCount - 2); ib.put(index++, i1++); } else { ib.put(index++, i0++); ib.put(index++, i1++); ib.put(index++, vertCount - 2); } } else if (closed && axisCount == axisSamples - 2) { ib.put(index++, i2++); ib.put(index++, inverted ? vertCount - 1 : i3++); ib.put(index++, inverted ? i3++ : vertCount - 1); } else { ib.put(index++, i0++); ib.put(index++, inverted ? i2 : i1); ib.put(index++, inverted ? i1 : i2); ib.put(index++, i1++); ib.put(index++, inverted ? i2++ : i3++); ib.put(index++, inverted ? i3++ : i2++); } } } updateBound(); }
/** * Updates the geometry with the given parameters. Bounds are expressed as if the arrow was * pointing to the right, i.e.: x = length of the arrow (distance to the pointy bit.) y = height * of the arrow z = extrusion length. */ public void updateGeometry(Orientation orientation, Vector3f bounds, boolean isExtruded) { // Create three points, according to the size and orientation. Vector3f p0 = new Vector3f(), p1, p2; switch (orientation) { case LEFT: p1 = new Vector3f(0, bounds.y, 0); p2 = new Vector3f(-bounds.x, bounds.y / 2, 0); break; case RIGHT: p1 = new Vector3f(bounds.x, bounds.y / 2, 0); p2 = new Vector3f(0, bounds.y, 0); break; case UP: p1 = new Vector3f(bounds.y, 0, 0); p2 = new Vector3f(bounds.y / 2, bounds.x, 0); break; case DOWN: p1 = new Vector3f(bounds.y / 2, -bounds.x, 0); p2 = new Vector3f(bounds.y, 0, 0); break; default: p2 = p1 = p0; } // Then, make a list of vertices and indices. Vector3f[] vertices, normals; int[] indices; if (!isExtruded) { // For a flat triangle, easy one, just take the initial point and link them. vertices = new Vector3f[3]; vertices[0] = p0; vertices[1] = p1; vertices[2] = p2; // The normals are all along Z. normals = new Vector3f[3]; normals[0] = Vector3f.UNIT_Z; normals[1] = Vector3f.UNIT_Z; normals[2] = Vector3f.UNIT_Z; indices = new int[] {0, 1, 2}; } else { // For an extruded triangle vertices = new Vector3f[18]; // First (front) triangle is regular. vertices[0] = p0; vertices[1] = p1; vertices[2] = p2; // Back triangle is easy too: retake first triangle and shift the z-coordinate. vertices[3] = new Vector3f(p0.x, p0.y, bounds.z); vertices[4] = new Vector3f(p1.x, p1.y, bounds.z); vertices[5] = new Vector3f(p2.x, p2.y, bounds.z); // Then duplicate side vertices to allow hard edges normals, resulting in 4 quads. vertices[6] = vertices[0]; vertices[7] = vertices[3]; vertices[8] = vertices[1]; vertices[9] = vertices[4]; vertices[10] = vertices[1]; vertices[11] = vertices[4]; vertices[12] = vertices[2]; vertices[13] = vertices[5]; vertices[14] = vertices[2]; vertices[15] = vertices[5]; vertices[16] = vertices[0]; vertices[17] = vertices[3]; // Normals: normals = new Vector3f[18]; // The front side normal is still the same as for a flat. normals[0] = Vector3f.UNIT_Z; normals[1] = Vector3f.UNIT_Z; normals[2] = Vector3f.UNIT_Z; // Backside is the same, but reversed (d'uh). normals[3] = Vector3f.UNIT_Z.negate(); normals[4] = normals[3]; normals[5] = normals[3]; // Finally, sides normals are computed from the triangle sides. Vector3f normal1 = new Vector3f(p1.y - p0.y, p0.x - p1.x, 0); Vector3f normal2 = new Vector3f(p2.y - p1.y, p1.x - p2.x, 0); Vector3f normal3 = new Vector3f(p0.y - p2.y, p2.x - p0.x, 0); normals[6] = normal1; normals[7] = normal1; normals[8] = normal1; normals[9] = normal1; normals[10] = normal2; normals[11] = normal2; normals[12] = normal2; normals[13] = normal2; normals[14] = normal3; normals[15] = normal3; normals[16] = normal3; normals[17] = normal3; // Finally, the indices. indices = new int[] { // Front 0, 1, 2, // Back 5, 4, 3, // Sides 6, 7, 8, 9, 8, 7, 10, 11, 12, 13, 12, 11, 14, 15, 16, 17, 16, 15 }; } // Set the mesh data setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(vertices)); setBuffer(Type.Normal, 3, BufferUtils.createFloatBuffer(normals)); setBuffer(Type.Index, 3, BufferUtils.createIntBuffer(indices)); updateBound(); // setStatic(); }