@ -40,11 +40,8 @@ import com.jme3.math.FastMath;
import com.jme3.math.Vector3f ;
import com.jme3.scene.Mesh ;
import com.jme3.scene.VertexBuffer.Type ;
import com.jme3.scene.mesh.IndexBuffer ;
import com.jme3.util.BufferUtils ;
import static com.jme3.util.BufferUtils.* ;
import java.io.IOException ;
import java.nio.FloatBuffer ;
/ * *
* A simple cylinder , defined by it ' s height and radius .
@ -127,10 +124,10 @@ public class Cylinder extends Mesh {
* mapped to texture coordinates ( 0 . 5 , 1 ) , bottom to ( 0 . 5 , 0 ) . Thus you need
* a suited distorted texture .
*
* @param axisSamples
* Number of triangle samples along the axi s .
* @param radialSamples
* Number of triangle samples along the radial .
* @param axisSamples The number of vertices samples along the axis . It is equal to the number of segments + 1 ; so
* that , for instance , 4 samples mean the cylinder will be made of 3 segment s.
* @param radialSamples The number of triangle samples along the radius . For instance , 4 means that the sides of the
* cylinder are made of 4 rectangles , and the top and bottom are made of 4 triangles .
* @param radius
* The radius of the cylinder .
* @param height
@ -201,194 +198,225 @@ public class Cylinder extends Mesh {
/ * *
* 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 axisSamples The number of vertices samples along the axis . It is equal to the number of segments + 1 ; so
* that , for instance , 4 samples mean the cylinder will be made of 3 segments .
* @param radialSamples The number of triangle samples along the radius . For instance , 4 means that the sides of the
* cylinder are made of 4 rectangles , and the top and bottom are made of 4 triangles .
* @param topRadius the radius of the top of the cylinder .
* @param bottomRadius the radius of the bottom 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 ) {
float topRadius , float bottomRadius , float height , boolean closed , boolean inverted ) {
// Ensure there's at least two axis samples and 3 radial samples, and positive dimensions.
if ( axisSamples < 2
| | radialSamples < 3
| | topRadius < = 0
| | bottomRadius < = 0
| | height < = 0 ) {
throw new IllegalArgumentException ( "Cylinders must have at least 2 axis samples and 3 radial samples, and positive dimensions." ) ;
}
this . axisSamples = axisSamples ;
this . radialSamples = radialSamples ;
this . radius = radius ;
this . radius2 = radius2 ;
this . radius = bottomR adius;
this . radius2 = topRadius ;
this . height = height ;
this . closed = closed ;
this . inverted = inverted ;
// VertexBuffer pvb = getBuffer(Type.Position);
// VertexBuffer nvb = getBuffer(Type.Normal);
// VertexBuffer tvb = getBuffer(Type.TexCoord);
axisSamples + = ( closed ? 2 : 0 ) ;
// 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 ) ) ;
// Vertices : One per radial sample plus one duplicate for texture closing around the sides.
int verticesCount = axisSamples * ( radialSamples + 1 ) ;
// Triangles: Two per side rectangle, which is the product of numbers of samples.
int trianglesCount = axisSamples * radialSamples * 2 ;
if ( closed ) {
// If there are caps, add two additional rims and two summits.
verticesCount + = 2 + 2 * ( radialSamples + 1 ) ;
// Add one triangle per radial sample, twice, to form the caps.
trianglesCount + = 2 * radialSamples ;
}
int triCount = ( ( closed ? 2 : 0 ) + 2 * ( axisSamples - 1 ) ) * radialSamples ;
// Compute the points along a unit circle:
float [ ] [ ] circlePoints = new float [ radialSamples + 1 ] [ 2 ] ;
for ( int circlePoint = 0 ; circlePoint < radialSamples ; circlePoint + + ) {
float angle = FastMath . TWO_PI / radialSamples * circlePoint ;
circlePoints [ circlePoint ] [ 0 ] = FastMath . cos ( angle ) ;
circlePoints [ circlePoint ] [ 1 ] = FastMath . sin ( angle ) ;
}
// Add an additional point for closing the texture around the side of the cylinder.
circlePoints [ radialSamples ] [ 0 ] = circlePoints [ 0 ] [ 0 ] ;
circlePoints [ radialSamples ] [ 1 ] = circlePoints [ 0 ] [ 1 ] ;
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 ) ;
// Calculate normals.
//
// A---------B
// \ |
// \ |
// \ |
// D-----C
//
// Let be B and C the top and bottom points of the axis, and A and D the top and bottom edges.
// The normal in A and D is simply orthogonal to AD, which means we can get it once per sample.
//
Vector3f [ ] circleNormals = new Vector3f [ radialSamples + 1 ] ;
for ( int circlePoint = 0 ; circlePoint < radialSamples + 1 ; circlePoint + + ) {
// The normal is the orthogonal to the side, which can be got without trigonometry.
// The edge direction is oriented so that it goes up by Height, and out by the radius difference; let's use
// those values in reverse order.
Vector3f normal = new Vector3f ( height * circlePoints [ circlePoint ] [ 0 ] , height * circlePoints [ circlePoint ] [ 1 ] , bottomRadius - topRadius ) ;
circleNormals [ circlePoint ] = normal . normalizeLocal ( ) ;
}
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 ( ) ;
float [ ] vertices = new float [ verticesCount * 3 ] ;
float [ ] normals = new float [ verticesCount * 3 ] ;
float [ ] textureCoords = new float [ verticesCount * 2 ] ;
int currentIndex = 0 ;
// Add a circle of points for each axis sample.
for ( int axisSample = 0 ; axisSample < axisSamples ; axisSample + + ) {
float currentHeight = - height / 2 + height * axisSample / ( axisSamples - 1 ) ;
float currentRadius = bottomRadius + ( topRadius - bottomRadius ) * axisSample / ( axisSamples - 1 ) ;
for ( int circlePoint = 0 ; circlePoint < radialSamples + 1 ; circlePoint + + ) {
// Position, by multipliying the position on a unit circle with the current radius.
vertices [ currentIndex * 3 ] = circlePoints [ circlePoint ] [ 0 ] * currentRadius ;
vertices [ currentIndex * 3 + 1 ] = circlePoints [ circlePoint ] [ 1 ] * currentRadius ;
vertices [ currentIndex * 3 + 2 ] = currentHeight ;
// Normal
Vector3f currentNormal = circleNormals [ circlePoint ] ;
normals [ currentIndex * 3 ] = currentNormal . x ;
normals [ currentIndex * 3 + 1 ] = currentNormal . y ;
normals [ currentIndex * 3 + 2 ] = currentNormal . z ;
// Texture
// The X is the angular position of the point.
textureCoords [ currentIndex * 2 ] = ( float ) circlePoint / radialSamples ;
// Depending on whether there is a cap, the Y is either the height scaled to [0,1], or the radii of
// the cap count as well.
if ( closed )
textureCoords [ currentIndex * 2 + 1 ] = ( bottomRadius + height / 2 + currentHeight ) / ( bottomRadius + height + topRadius ) ;
else
textureCoords [ currentIndex * 2 + 1 ] = height / 2 + currentHeight ;
currentIndex + + ;
}
}
// If closed, add duplicate rims on top and bottom, with normals facing up and down.
if ( closed ) {
// Bottom
for ( int circlePoint = 0 ; circlePoint < radialSamples + 1 ; circlePoint + + ) {
vertices [ currentIndex * 3 ] = circlePoints [ circlePoint ] [ 0 ] * bottomRadius ;
vertices [ currentIndex * 3 + 1 ] = circlePoints [ circlePoint ] [ 1 ] * bottomRadius ;
vertices [ currentIndex * 3 + 2 ] = - height / 2 ;
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 ;
}
}
normals [ currentIndex * 3 ] = 0 ;
normals [ currentIndex * 3 + 1 ] = 0 ;
normals [ currentIndex * 3 + 2 ] = - 1 ;
// 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 ) ;
textureCoords [ currentIndex * 2 ] = ( float ) circlePoint / radialSamples ;
textureCoords [ currentIndex * 2 + 1 ] = bottomRadius / ( bottomRadius + height + topRadius ) ;
currentIndex + + ;
}
// Top
for ( int circlePoint = 0 ; circlePoint < radialSamples + 1 ; circlePoint + + ) {
vertices [ currentIndex * 3 ] = circlePoints [ circlePoint ] [ 0 ] * topRadius ;
vertices [ currentIndex * 3 + 1 ] = circlePoints [ circlePoint ] [ 1 ] * topRadius ;
vertices [ currentIndex * 3 + 2 ] = height / 2 ;
BufferUtils . copyInternalVector3 ( pb , save , i ) ;
BufferUtils . copyInternalVector3 ( nb , save , i ) ;
normals [ currentIndex * 3 ] = 0 ;
normals [ currentIndex * 3 + 1 ] = 0 ;
normals [ currentIndex * 3 + 2 ] = 1 ;
tb . put ( ( inverted ? 0 . 0f : 1 . 0f ) )
. put ( axisFractionTexture ) ;
}
textureCoords [ currentIndex * 2 ] = ( float ) circlePoint / radialSamples ;
textureCoords [ currentIndex * 2 + 1 ] = ( bottomRadius + height ) / ( bottomRadius + height + topRadius ) ;
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 ) ;
currentIndex + + ;
}
// Add the centers of the caps.
vertices [ currentIndex * 3 ] = 0 ;
vertices [ currentIndex * 3 + 1 ] = 0 ;
vertices [ currentIndex * 3 + 2 ] = - height / 2 ;
normals [ currentIndex * 3 ] = 0 ;
normals [ currentIndex * 3 + 1 ] = 0 ;
normals [ currentIndex * 3 + 2 ] = - 1 ;
textureCoords [ currentIndex * 2 ] = 0 . 5f ;
textureCoords [ currentIndex * 2 + 1 ] = 0f ;
currentIndex + + ;
vertices [ currentIndex * 3 ] = 0 ;
vertices [ currentIndex * 3 + 1 ] = 0 ;
vertices [ currentIndex * 3 + 2 ] = height / 2 ;
normals [ currentIndex * 3 ] = 0 ;
normals [ currentIndex * 3 + 1 ] = 0 ;
normals [ currentIndex * 3 + 2 ] = 1 ;
textureCoords [ currentIndex * 2 ] = 0 . 5f ;
textureCoords [ currentIndex * 2 + 1 ] = 1f ;
}
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 + + ) ;
}
// Add the triangles indexes.
short [ ] indices = new short [ trianglesCount * 3 ] ;
currentIndex = 0 ;
for ( short axisSample = 0 ; axisSample < axisSamples - 1 ; axisSample + + ) {
for ( int circlePoint = 0 ; circlePoint < radialSamples ; circlePoint + + ) {
indices [ currentIndex + + ] = ( short ) ( axisSample * ( radialSamples + 1 ) + circlePoint ) ;
indices [ currentIndex + + ] = ( short ) ( axisSample * ( radialSamples + 1 ) + circlePoint + 1 ) ;
indices [ currentIndex + + ] = ( short ) ( ( axisSample + 1 ) * ( radialSamples + 1 ) + circlePoint ) ;
indices [ currentIndex + + ] = ( short ) ( ( axisSample + 1 ) * ( radialSamples + 1 ) + circlePoint ) ;
indices [ currentIndex + + ] = ( short ) ( axisSample * ( radialSamples + 1 ) + circlePoint + 1 ) ;
indices [ currentIndex + + ] = ( short ) ( ( axisSample + 1 ) * ( radialSamples + 1 ) + circlePoint + 1 ) ;
}
}
// Add caps if needed.
if ( closed ) {
short bottomCapIndex = ( short ) ( verticesCount - 2 ) ;
short topCapIndex = ( short ) ( verticesCount - 1 ) ;
int bottomRowOffset = ( axisSamples ) * ( radialSamples + 1 ) ;
int topRowOffset = ( axisSamples + 1 ) * ( radialSamples + 1 ) ;
for ( int circlePoint = 0 ; circlePoint < radialSamples ; circlePoint + + ) {
indices [ currentIndex + + ] = ( short ) ( bottomRowOffset + circlePoint + 1 ) ;
indices [ currentIndex + + ] = ( short ) ( bottomRowOffset + circlePoint ) ;
indices [ currentIndex + + ] = bottomCapIndex ;
indices [ currentIndex + + ] = ( short ) ( topRowOffset + circlePoint ) ;
indices [ currentIndex + + ] = ( short ) ( topRowOffset + circlePoint + 1 ) ;
indices [ currentIndex + + ] = topCapIndex ;
}
}
// If inverted, the triangles and normals are all reverted.
if ( inverted ) {
for ( int i = 0 ; i < indices . length / 2 ; i + + ) {
short temp = indices [ i ] ;
indices [ i ] = indices [ indices . length - 1 - i ] ;
indices [ indices . length - 1 - i ] = temp ;
}
for ( int i = 0 ; i < normals . length ; i + + ) {
normals [ i ] = - normals [ i ] ;
}
}
// Fill in the buffers.
setBuffer ( Type . Position , 3 , BufferUtils . createFloatBuffer ( vertices ) ) ;
setBuffer ( Type . Normal , 3 , BufferUtils . createFloatBuffer ( normals ) ) ;
setBuffer ( Type . TexCoord , 2 , BufferUtils . createFloatBuffer ( textureCoords ) ) ;
setBuffer ( Type . Index , 3 , BufferUtils . createShortBuffer ( indices ) ) ;
updateBound ( ) ;
setStatic ( ) ;
}
@ -418,6 +446,4 @@ public class Cylinder extends Mesh {
capsule . write ( closed , "closed" , false ) ;
capsule . write ( inverted , "inverted" , false ) ;
}
}