Преглед на файлове

* Apply fix for cylinder normal generation

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@8425 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
sha..rd преди 14 години
родител
ревизия
1fb4a9be00
променени са 1 файла, в които са добавени 421 реда и са изтрити 395 реда
  1. 421 395
      engine/src/core/com/jme3/scene/shape/Cylinder.java

+ 421 - 395
engine/src/core/com/jme3/scene/shape/Cylinder.java

@@ -1,395 +1,421 @@
-/*
- * Copyright (c) 2009-2010 jMonkeyEngine
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in the
- *   documentation and/or other materials provided with the distribution.
- *
- * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
- *   may be used to endorse or promote products derived from this software
- *   without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
- * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-// $Id: Cylinder.java 4131 2009-03-19 20:15:28Z blaine.dev $
-package com.jme3.scene.shape;
-
-import com.jme3.export.InputCapsule;
-import com.jme3.export.JmeExporter;
-import com.jme3.export.JmeImporter;
-import com.jme3.export.OutputCapsule;
-import com.jme3.math.FastMath;
-import com.jme3.math.Vector3f;
-import com.jme3.scene.mesh.IndexBuffer;
-import com.jme3.scene.Mesh;
-import com.jme3.scene.VertexBuffer.Type;
-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.
- * (Ported to jME3)
- *
- * @author Mark Powell
- * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
- */
-public class Cylinder extends Mesh {
-
-    private int axisSamples;
-
-    private int radialSamples;
-
-    private float radius;
-    private float radius2;
-
-    private float height;
-    private boolean closed;
-    private boolean inverted;
-
-    /**
-     * Default constructor for serialization only. Do not use.
-     */
-    public Cylinder() {
-    }
-
-    /**
-     * Creates a new Cylinder. By default its center is the origin. Usually, a
-     * higher sample number creates a better looking cylinder, but at the cost
-     * of more vertex information.
-     *
-     * @param axisSamples
-     *            Number of triangle samples along the axis.
-     * @param radialSamples
-     *            Number of triangle samples along the radial.
-     * @param radius
-     *            The radius of the cylinder.
-     * @param height
-     *            The cylinder's height.
-     */
-    public Cylinder(int axisSamples, int radialSamples,
-            float radius, float height) {
-        this(axisSamples, radialSamples, radius, height, false);
-    }
-
-    /**
-     * Creates a new Cylinder. By default its center is the origin. Usually, a
-     * higher sample number creates a better looking cylinder, but at the cost
-     * of more vertex information. <br>
-     * If the cylinder is closed the texture is split into axisSamples parts:
-     * top most and bottom most part is used for top and bottom of the cylinder,
-     * rest of the texture for the cylinder wall. The middle of the top is
-     * 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 axis.
-     * @param radialSamples
-     *            Number of triangle samples along the radial.
-     * @param radius
-     *            The radius of the cylinder.
-     * @param height
-     *            The cylinder's height.
-     * @param closed
-     *            true to create a cylinder with top and bottom surface
-     */
-    public Cylinder(int axisSamples, int radialSamples,
-            float radius, float height, boolean closed) {
-        this(axisSamples, radialSamples, radius, height, closed, false);
-    }
-
-    /**
-     * Creates a new Cylinder. By default its center is the origin. Usually, a
-     * higher sample number creates a better looking cylinder, but at the cost
-     * of more vertex information. <br>
-     * If the cylinder is closed the texture is split into axisSamples parts:
-     * top most and bottom most part is used for top and bottom of the cylinder,
-     * rest of the texture for the cylinder wall. The middle of the top is
-     * 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 axis.
-     * @param radialSamples
-     *            Number of triangle samples along the radial.
-     * @param radius
-     *            The radius of the cylinder.
-     * @param height
-     *            The cylinder's height.
-     * @param closed
-     *            true to create a cylinder with top and bottom surface
-     * @param inverted
-     *            true to create a cylinder that is meant to be viewed from the
-     *            interior.
-     */
-    public Cylinder(int axisSamples, int radialSamples,
-            float radius, float height, boolean closed, boolean inverted) {
-        this(axisSamples, radialSamples, radius, radius, height, closed, inverted);
-    }
-
-    public Cylinder(int axisSamples, int radialSamples,
-            float radius, float radius2, float height, boolean closed, boolean inverted) {
-        super();
-        updateGeometry(axisSamples, radialSamples, radius, radius2, height, closed, inverted);
-    }
-
-    /**
-     * @return the number of samples along the cylinder axis
-     */
-    public int getAxisSamples() {
-        return axisSamples;
-    }
-
-    /**
-     * @return Returns the height.
-     */
-    public float getHeight() {
-        return height;
-    }
-
-    /**
-     * @return number of samples around cylinder
-     */
-    public int getRadialSamples() {
-        return radialSamples;
-    }
-
-    /**
-     * @return Returns the radius.
-     */
-    public float getRadius() {
-        return radius;
-    }
-
-    public float getRadius2() {
-        return radius2;
-    }
-
-    /**
-     * @return true if end caps are used.
-     */
-    public boolean isClosed() {
-        return closed;
-    }
-
-    /**
-     * @return true if normals and uvs are created for interior use
-     */
-    public boolean isInverted() {
-        return inverted;
-    }
-
-    /**
-     * 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];
-
-        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;
-                }
-            }
-            float z = -halfHeight + height * axisFraction;
-
-            // compute center of slice
-            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);
-                if (topBottom == 0) {
-                    if (!inverted)
-                        nb.put(tempNormal.x).put(tempNormal.y).put(tempNormal.z);
-                    else
-                        nb.put(-tempNormal.x).put(-tempNormal.y).put(-tempNormal.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();
-    }
-
-    public void read(JmeImporter e) throws IOException {
-        super.read(e);
-        InputCapsule capsule = e.getCapsule(this);
-        axisSamples = capsule.readInt("axisSamples", 0);
-        radialSamples = capsule.readInt("radialSamples", 0);
-        radius = capsule.readFloat("radius", 0);
-        radius2 = capsule.readFloat("radius2", 0);
-        height = capsule.readFloat("height", 0);
-        closed = capsule.readBoolean("closed", false);
-        inverted = capsule.readBoolean("inverted", false);
-    }
-
-    public void write(JmeExporter e) throws IOException {
-        super.write(e);
-        OutputCapsule capsule = e.getCapsule(this);
-        capsule.write(axisSamples, "axisSamples", 0);
-        capsule.write(radialSamples, "radialSamples", 0);
-        capsule.write(radius, "radius", 0);
-        capsule.write(radius2, "radius2", 0);
-        capsule.write(height, "height", 0);
-        capsule.write(closed, "closed", false);
-        capsule.write(inverted, "inverted", false);
-    }
-
-
-}
+/*
+ * Copyright (c) 2009-2010 jMonkeyEngine
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
+ *   may be used to endorse or promote products derived from this software
+ *   without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+// $Id: Cylinder.java 4131 2009-03-19 20:15:28Z blaine.dev $
+package com.jme3.scene.shape;
+
+import com.jme3.export.InputCapsule;
+import com.jme3.export.JmeExporter;
+import com.jme3.export.JmeImporter;
+import com.jme3.export.OutputCapsule;
+import com.jme3.math.FastMath;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.mesh.IndexBuffer;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.VertexBuffer.Type;
+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.
+ * (Ported to jME3)
+ *
+ * @author Mark Powell
+ * @version $Revision: 4131 $, $Date: 2009-03-19 16:15:28 -0400 (Thu, 19 Mar 2009) $
+ */
+public class Cylinder extends Mesh {
+
+    private int axisSamples;
+
+    private int radialSamples;
+
+    private float radius;
+    private float radius2;
+
+    private float height;
+    private boolean closed;
+    private boolean inverted;
+
+    /**
+     * Default constructor for serialization only. Do not use.
+     */
+    public Cylinder() {
+    }
+
+    /**
+     * Creates a new Cylinder. By default its center is the origin. Usually, a
+     * higher sample number creates a better looking cylinder, but at the cost
+     * of more vertex information.
+     *
+     * @param axisSamples
+     *            Number of triangle samples along the axis.
+     * @param radialSamples
+     *            Number of triangle samples along the radial.
+     * @param radius
+     *            The radius of the cylinder.
+     * @param height
+     *            The cylinder's height.
+     */
+    public Cylinder(int axisSamples, int radialSamples,
+            float radius, float height) {
+        this(axisSamples, radialSamples, radius, height, false);
+    }
+
+    /**
+     * Creates a new Cylinder. By default its center is the origin. Usually, a
+     * higher sample number creates a better looking cylinder, but at the cost
+     * of more vertex information. <br>
+     * If the cylinder is closed the texture is split into axisSamples parts:
+     * top most and bottom most part is used for top and bottom of the cylinder,
+     * rest of the texture for the cylinder wall. The middle of the top is
+     * 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 axis.
+     * @param radialSamples
+     *            Number of triangle samples along the radial.
+     * @param radius
+     *            The radius of the cylinder.
+     * @param height
+     *            The cylinder's height.
+     * @param closed
+     *            true to create a cylinder with top and bottom surface
+     */
+    public Cylinder(int axisSamples, int radialSamples,
+            float radius, float height, boolean closed) {
+        this(axisSamples, radialSamples, radius, height, closed, false);
+    }
+
+    /**
+     * Creates a new Cylinder. By default its center is the origin. Usually, a
+     * higher sample number creates a better looking cylinder, but at the cost
+     * of more vertex information. <br>
+     * If the cylinder is closed the texture is split into axisSamples parts:
+     * top most and bottom most part is used for top and bottom of the cylinder,
+     * rest of the texture for the cylinder wall. The middle of the top is
+     * 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 axis.
+     * @param radialSamples
+     *            Number of triangle samples along the radial.
+     * @param radius
+     *            The radius of the cylinder.
+     * @param height
+     *            The cylinder's height.
+     * @param closed
+     *            true to create a cylinder with top and bottom surface
+     * @param inverted
+     *            true to create a cylinder that is meant to be viewed from the
+     *            interior.
+     */
+    public Cylinder(int axisSamples, int radialSamples,
+            float radius, float height, boolean closed, boolean inverted) {
+        this(axisSamples, radialSamples, radius, radius, height, closed, inverted);
+    }
+
+    public Cylinder(int axisSamples, int radialSamples,
+            float radius, float radius2, float height, boolean closed, boolean inverted) {
+        super();
+        updateGeometry(axisSamples, radialSamples, radius, radius2, height, closed, inverted);
+    }
+
+    /**
+     * @return the number of samples along the cylinder axis
+     */
+    public int getAxisSamples() {
+        return axisSamples;
+    }
+
+    /**
+     * @return Returns the height.
+     */
+    public float getHeight() {
+        return height;
+    }
+
+    /**
+     * @return number of samples around cylinder
+     */
+    public int getRadialSamples() {
+        return radialSamples;
+    }
+
+    /**
+     * @return Returns the radius.
+     */
+    public float getRadius() {
+        return radius;
+    }
+
+    public float getRadius2() {
+        return radius2;
+    }
+
+    /**
+     * @return true if end caps are used.
+     */
+    public boolean isClosed() {
+        return closed;
+    }
+
+    /**
+     * @return true if normals and uvs are created for interior use
+     */
+    public boolean isInverted() {
+        return inverted;
+    }
+
+    /**
+     * 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();
+    }
+
+    public void read(JmeImporter e) throws IOException {
+        super.read(e);
+        InputCapsule capsule = e.getCapsule(this);
+        axisSamples = capsule.readInt("axisSamples", 0);
+        radialSamples = capsule.readInt("radialSamples", 0);
+        radius = capsule.readFloat("radius", 0);
+        radius2 = capsule.readFloat("radius2", 0);
+        height = capsule.readFloat("height", 0);
+        closed = capsule.readBoolean("closed", false);
+        inverted = capsule.readBoolean("inverted", false);
+    }
+
+    public void write(JmeExporter e) throws IOException {
+        super.write(e);
+        OutputCapsule capsule = e.getCapsule(this);
+        capsule.write(axisSamples, "axisSamples", 0);
+        capsule.write(radialSamples, "radialSamples", 0);
+        capsule.write(radius, "radius", 0);
+        capsule.write(radius2, "radius2", 0);
+        capsule.write(height, "height", 0);
+        capsule.write(closed, "closed", false);
+        capsule.write(inverted, "inverted", false);
+    }
+
+
+}