# [JME 2.0] Dynamic sphere face extrusion howto?

Hi,

for my project, I will need a quarter of a sphere (created from the Sphere shape class) and I will need dynamic extrusion of the “square” faces (ie not the ones using the north or south pole vertices).

I’ve took a look at the Extrusion class but this doesn’t seem to be what I’m looking for.

Since a picture help more than words, I would like to change this sphere: sphere

to extrude a “square” face (in fact two triangles) like that: sphere extruded

So:

1/ is it feasible? (I mean Opengl restrictions :?)

2/ is it feasible with JME?

3/ if yes, how?

TIA,

Niggle

You will need to go through the Vertex buffer and manipulate each of the vertices individually I think.

An easy test would be to see if the vertex is 'shared' more than 8 times (I think), if it is its a 'pole' vertex.

You might want to look at your own sphere building algorithm also, it might be less work…

Here's one I wrote a while ago (in GFX class), its for Jogl though so you will have to figure out how to implement the index buffer; and also it uses a QuadStrip which I don't know if jME supports…

```     private void renderBall( GL gl, double r, int lats, int longs ) {         gl.glColor4d( 1.0, 1.0, 1.0, 1.0 );         setBallMaterial( gl );         gl.glEnable( GL_NORMALIZE );     // We are scaling so request that openGL normalizes light normals         gl.glPushMatrix();         gl.glScaled( r, r, r );         gl.glBegin( GL_QUAD_STRIP );         for( int i = 0; i <= lats; ++i ){             double lat0 = Math.PI * ( -0.5 + (double) ( i - 1 ) / lats );             double z0 = Math.sin( lat0 );             double zr0 = Math.cos( lat0 );             double lat1 = Math.PI * ( -0.5 + (double) i / lats );             double z1 = Math.sin( lat1 );             double zr1 = Math.cos( lat1 );             for( int j = 0; j <= longs; ++j ){                 double lng = 2 * Math.PI * (double) ( j - 1 ) / longs;                 double x = Math.cos( lng );                 double y = Math.sin( lng );               if( i > lats / 2 ){                     gl.glNormal3d( x * zr1, y * zr1, z1 );                     gl.glTexCoord2d( x * zr1 / 2 + 0.5, -y * zr1 / 2 + 0.5 );                     gl.glVertex3d( x * zr1, y * zr1, z1 );                     gl.glNormal3d( x * zr0, y * zr0, z0 );                     gl.glTexCoord2d( x * zr0 / 2 + 0.5, -y * zr0 / 2 + 0.5 );                     gl.glVertex3d( x * zr0, y * zr0, z0 );                 // Reverse texture x on 'front' of ball, texture is backwards otherwise                 } else{                     gl.glNormal3d( x * zr1, y * zr1, z1 );                     gl.glTexCoord2d( -x * zr1 / 2 + 0.5, -y * zr1 / 2 + 0.5 );                     gl.glVertex3d( x * zr1, y * zr1, z1 );                     gl.glNormal3d( x * zr0, y * zr0, z0 );                     gl.glTexCoord2d( -x * zr0 / 2 + 0.5, -y * zr0 / 2 + 0.5 );                     gl.glVertex3d( x * zr0, y * zr0, z0 );                 }             }         }         gl.glEnd();         gl.glPopMatrix();         gl.glDisable( GL_NORMALIZE );     } ```

Thanks basixs,

now I can modify the vertex buffer and I can sort of morph some points of my sphere (inspired from com.jme.shape.Sphere) => less work (thanks though for your code).

But now I'm facing two issues:

-1- I added one more vertex for each vertices (but the 2 poles vertices) and I guess I'm now fighting with the index buffer (it is no more a Sphere  :-o)

-2- I have to find the complex math stuff in order to extrude to create a Box and not some sort of 4 sided cones (because if I use the direction center -> vertex and if I multiply by a certain amount (> 1.0) for extrusion, I get an extruded face bigger than the original one in surface I mean)

any idea for the second point?

Yes first point resolved!

now, the second one… tomorrow!

QuarterSphere.java so far:

```import com.jme.math.FastMath; import com.jme.math.Vector3f; import com.jme.scene.TexCoords; import com.jme.scene.TriMesh; import com.jme.util.export.InputCapsule; import com.jme.util.export.JMEExporter; import com.jme.util.export.JMEImporter; import com.jme.util.export.OutputCapsule; import com.jme.util.geom.BufferUtils; import java.io.IOException; import java.util.ArrayList; import java.util.List; /**  * <code>Sphere</code> represents a 3D object with all points equidistance  * from a center point.  *  * @author Joshua Slack  * @author <a href="mailto:loic.lefevre@gmail.com">Lo```

TODO:

• correct box-shaped extrusion direction
• connect 4 more new faces

Finally:

```import com.jme.math.FastMath; import com.jme.math.Vector3f; import com.jme.renderer.ColorRGBA; import com.jme.scene.TexCoords; import com.jme.scene.TriMesh; import com.jme.util.export.InputCapsule; import com.jme.util.export.JMEExporter; import com.jme.util.export.JMEImporter; import com.jme.util.export.OutputCapsule; import com.jme.util.geom.BufferUtils; import java.io.IOException; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.List; /**  * <code>Quarter of a sphere</code> used as the control panel for Oracle sessions  * Inspired from the Sphere shape.  */ public class QuarterSphere extends TriMesh {     private static final long serialVersionUID = 1L;     protected int zSamples;     protected int radialSamples;     protected static final ColorRGBA SPHERE_COLOR = new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f);     /**      * the distance from the center point each point falls on      */     public float radius;     /**      * the center of the sphere      */     public Vector3f center;     protected transient FloatBuffer originalVerticesCoords;     protected transient FloatBuffer tempExtrudedVertices;     protected transient FloatBuffer tempExtrudedColors;     public QuarterSphere() {     }     /**      * Constructs a sphere. By default the Sphere has not geometry data or      * center.      *      * @param name The name of the sphere.      */     public QuarterSphere(String name) {         super(name);     }     /**      * Constructs a sphere with center at the origin. For details, see the other      * constructor.      *      * @param name          Name of sphere.      * @param zSamples      The samples along the Z.      * @param radialSamples The samples along the radial.      * @param radius        Radius of the sphere.      * @see #QuarterSphere(java.lang.String, com.jme.math.Vector3f, int, int, float)      */     public QuarterSphere(String name, int zSamples, int radialSamples, float radius) {         this(name, new Vector3f(0, 0, 0), zSamples, radialSamples, radius);     }     /**      * Constructs a sphere. All geometry data buffers are updated automatically.      * Both zSamples and radialSamples increase the quality of the generated      * sphere.      *      * @param name          Name of the sphere.      * @param center        Center of the sphere.      * @param zSamples      The number of samples along the Z.      * @param radialSamples The number of samples along the radial.      * @param radius        The radius of the sphere.      */     public QuarterSphere(String name, Vector3f center, int zSamples,                                int radialSamples, float radius) {         super(name);         setData(center, zSamples, radialSamples, radius);     }     /**      * Changes the information of the sphere into the given values.      *      * @param center        The new center of the sphere.      * @param zSamples      The new number of zSamples of the sphere.      * @param radialSamples The new number of radial samples of the sphere.      * @param radius        The new radius of the sphere.      */     public void setData(Vector3f center, int zSamples, int radialSamples, float radius) {         if (center != null)             this.center = center;         else             this.center = new Vector3f(0, 0, 0);         this.zSamples = zSamples;         this.radialSamples = radialSamples;         this.radius = radius;         setGeometryData();         setIndexData();     }     /**      * builds the vertices based on the radius, center and radial and zSamples.      */     private void setGeometryData() {         // allocate vertices         setVertexCount(zSamples + (radialSamples - 1) * (1 + (zSamples - 3) * 5));         // System.out.println("Vertext count=" + getVertexCount());         setVertexBuffer(BufferUtils.createVector3Buffer(getVertexBuffer(), getVertexCount()));         final FloatBuffer vb = getVertexBuffer();         // new         setColorBuffer(BufferUtils.createColorBuffer(getVertexCount()));         // allocate normals if requested         setNormalBuffer(BufferUtils.createVector3Buffer(getNormalBuffer(), getVertexCount()));         // allocate texture coordinates         setTextureCoords(new TexCoords(BufferUtils.createVector2Buffer(getVertexCount())));         // generate geometry         final float fInvRS = 1.0f / radialSamples;         final float fZFactor = 2.0f / (zSamples - 1);         // Generate points on the unit circle to be used in computing the mesh         // points on a sphere slice.         float[] afSin = new float[(radialSamples + 1)];         float[] afCos = new float[(radialSamples + 1)];         for (int iR = 0; iR < radialSamples; iR++) {             final float fAngle = FastMath.HALF_PI * fInvRS * iR;             afCos[iR] = FastMath.cos(fAngle);             afSin[iR] = FastMath.sin(fAngle);         }         afSin[radialSamples] = afSin[0];         afCos[radialSamples] = afCos[0];         final Vector3f tempVa = new Vector3f();         final Vector3f tempVb = new Vector3f();         final Vector3f tempVc = new Vector3f();         // generate the sphere itself         int vertexNumber = 0;         int vertexIndex = 0;         int textureIndex = 0;         Vector3f kNormal = new Vector3f();         for (int iZ = 1; iZ < (zSamples - 1); iZ++) {             float fZFraction = -1.0f + fZFactor * iZ; // in (-1,1)             float fZ = radius * fZFraction;             // compute center of slice             final Vector3f kSliceCenter = tempVb.set(center);             kSliceCenter.z += fZ;             // compute radius of slice             final float fSliceRadius = FastMath.sqrt(FastMath.abs(radius * radius - fZ * fZ)) / 1.0f /* divise la hauteur par x */;             // compute slice vertices with duplication at end point             for (int iR = 0; iR < radialSamples; iR++) {                 //final float fRadialFraction = iR * fInvRS; // in [0,1)                 final Vector3f kRadial = tempVc.set(afCos[iR], afSin[iR], 0);                 kRadial.mult(fSliceRadius, tempVa);                 kNormal.set(kSliceCenter.x + tempVa.x, kSliceCenter.y + tempVa.y, kSliceCenter.z + tempVa.z);                 kNormal.subtractLocal(center);                 kNormal.normalizeLocal();                 // vertices + colors + normals                 vertexNumber++;                 vb.put(kSliceCenter.x + tempVa.x).put(kSliceCenter.y + tempVa.y).put(kSliceCenter.z + tempVa.z);                 vertexIndex += 3;                 getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);                 getNormalBuffer().put(kNormal.x).put(kNormal.y).put(kNormal.z);                 vertexNumber++;                 vb.put(kSliceCenter.x + tempVa.x).put(kSliceCenter.y + tempVa.y).put(kSliceCenter.z + tempVa.z);                 vertexIndex += 3;                 getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);                 getNormalBuffer().put(kNormal.x).put(kNormal.y).put(kNormal.z);                 if (iR > 0 && iR < radialSamples - 1) {                     vertexNumber++;                     vb.put(kSliceCenter.x + tempVa.x).put(kSliceCenter.y + tempVa.y).put(kSliceCenter.z + tempVa.z);                     vertexIndex += 3;                     getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);                     getNormalBuffer().put(kNormal.x).put(kNormal.y).put(kNormal.z);                 }                 if (iZ > 1 && iZ < zSamples - 2) {                     // System.out.println(vertexNumber + ": haut 1 decale");                     vertexNumber++;                     vb.put(kSliceCenter.x + tempVa.x).put(kSliceCenter.y + tempVa.y).put(kSliceCenter.z + tempVa.z);                     vertexIndex += 3;                     getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);                     getNormalBuffer().put(kNormal.x).put(kNormal.y).put(kNormal.z);                     if (iR > 0 && iR < radialSamples - 1) {                         // System.out.println(vertexNumber + ": haut 2 decale");                         vertexNumber++;                         vb.put(kSliceCenter.x + tempVa.x).put(kSliceCenter.y + tempVa.y).put(kSliceCenter.z + tempVa.z);                         vertexIndex += 3;                         getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);                         getNormalBuffer().put(kNormal.x).put(kNormal.y).put(kNormal.z);                     }                 }             }         }         // south pole         vb.position(vertexIndex);         vb.put(center.x).put(center.y).put(center.z - radius);         getNormalBuffer().position(vertexIndex);         getNormalBuffer().put(0).put(0).put(-1);         getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);         getTextureCoords().get(0).coords.position(textureIndex);         getTextureCoords().get(0).coords.put(0.5f).put(0.0f);         // north pole         vb.put(center.x).put(center.y).put(center.z + radius);         getNormalBuffer().put(0).put(0).put(1);         getColorBuffer().put(SPHERE_COLOR.r).put(SPHERE_COLOR.g).put(SPHERE_COLOR.b).put(SPHERE_COLOR.a);         getTextureCoords().get(0).coords.put(0.5f).put(1.0f);         originalVerticesCoords = BufferUtils.createVector3Buffer(getVertexCount());         vb.position(0);         originalVerticesCoords.put(vb);         tempExtrudedVertices = BufferUtils.createVector3Buffer(getVertexCount());         tempExtrudedColors = BufferUtils.createColorBuffer(getVertexCount());     }     /**      * sets the indices for rendering the sphere.      */     private void setIndexData() {         // allocate connectivity         setTriangleQuantity(/* south and north */                 (radialSamples - 1) * 2 +                         /* normal faces */                         2 /* 2 triangles per squared face */ * (zSamples - 3) * (radialSamples - 1) +                         /* extrudable faces */                         2 /* 2 triangles per squared face */ * (zSamples - 3) * (radialSamples - 1) * 5);         setIndexBuffer(BufferUtils.createIntBuffer(3 * getTriangleCount()));         // generate connectivity for "square" faces         int northPoleOffset = 0;         int iZStartOffset = -1;         for (int iZ = 0, iZStart = 0; iZ < (zSamples - 3); iZ++) {             int i0 = iZStart;             int i1 = i0 + 2 + (iZ > 0 && iZ < (zSamples - 3) ? 1 : 0);             iZStart += (iZ == 0 ? 4 + ((radialSamples - 2) * 3) : iZStartOffset + 2 * (radialSamples - 1));             if (iZStartOffset == -1) {                 iZStartOffset = iZStart;             }             if (iZ == zSamples - 4) {                 northPoleOffset = iZStart;             }             int i2 = iZStart;             int i3 = i2 + 2 + (iZ >= 0 && iZ < (zSamples - 4) ? 1 : 0);             for (int i = 0; i < radialSamples - 1; i++) {                 getIndexBuffer().put(i0);                 getIndexBuffer().put(i1);                 getIndexBuffer().put(i2);                 getIndexBuffer().put(i1);                 getIndexBuffer().put(i3);                 getIndexBuffer().put(i2);                 /*                ```

``` Z axis                  /e0--/e2                 / |  / |                i0_|_i2 |                | /e1--/e3                |/   |/                i1___i3                 */                 // computing extruded faces                 final int eFi0 = i0 + 1 + (i > 0 ? 1 : 0) + (iZ > 0 ? 1 : 0) + (iZ > 0 && i > 0 ? 1 : 0);                 final int eFi1 = i1 + 1 + (iZ > 0 ? 1 : 0) + (iZ > 0 && i == 0 ? 1 : 0) + (iZ > 0 && i > 0 && i < (radialSamples - 2) ? 1 : 0);                 final int eFi2 = i2 + 1 + (i > 0 ? 1 : 0);                 final int eFi3 = i3 + 1;                 squaredFaces.add(new SquaredFace(eFi0, eFi1, eFi2, eFi3));                 // upper face                 //System.out.println("2/ " + eFi0 + "," + eFi1 + "," + eFi2 + "," + eFi3);                 getIndexBuffer().put(eFi0);                 getIndexBuffer().put(eFi1);                 getIndexBuffer().put(eFi2);                 getIndexBuffer().put(eFi1);                 getIndexBuffer().put(eFi3);                 getIndexBuffer().put(eFi2);                 // front face                 getIndexBuffer().put(i1);                 getIndexBuffer().put(i3);                 getIndexBuffer().put(eFi1);                 getIndexBuffer().put(i3);                 getIndexBuffer().put(eFi3);                 getIndexBuffer().put(eFi1);                 // back face                 getIndexBuffer().put(i0);                 getIndexBuffer().put(i2);                 getIndexBuffer().put(eFi0);                 getIndexBuffer().put(i2);                 getIndexBuffer().put(eFi2);                 getIndexBuffer().put(eFi0);                 // left face                 getIndexBuffer().put(i0);                 getIndexBuffer().put(i1);                 getIndexBuffer().put(eFi0);                 getIndexBuffer().put(i1);                 getIndexBuffer().put(eFi1);                 getIndexBuffer().put(eFi0);                 // right face                 getIndexBuffer().put(i2);                 getIndexBuffer().put(i3);                 getIndexBuffer().put(eFi2);                 getIndexBuffer().put(i3);                 getIndexBuffer().put(eFi3);                 getIndexBuffer().put(eFi2);                 i0 = i1;                 i1 += 3 + (iZ > 0 && iZ < (zSamples - 3) ? 2 : 0);                 i2 = i3;                 i3 += 3 + (iZ >= 0 && iZ < (zSamples - 4) ? 2 : 0);             }         }         // south pole triangles         for (int i = 0, i0 = 0, i1 = 2; i < radialSamples - 1; i++) {             getIndexBuffer().put(i0);             getIndexBuffer().put(getVertexCount() - 2);             getIndexBuffer().put(i1);             i0 = i1;             i1 += 3;         }         // north pole triangles         for (int i = 0, i0 = northPoleOffset, i1 = northPoleOffset + 2; i < radialSamples - 1; i++) {             getIndexBuffer().put(i0);             getIndexBuffer().put(i1);             getIndexBuffer().put(getVertexCount() - 1);             i0 = i1;             i1 += 3;         }     }     /**      * Returns the center of this sphere.      *      * @return The sphere's center.      */     public Vector3f getCenter() {         return center;     }     /**      * Sets the center of this sphere. Note that other information (such as      * geometry buffers and actual vertex information) is not changed. In most      * cases, you'll want to use setData()      *      * @param aCenter The new center.      * @see #setData      */     public void setCenter(Vector3f aCenter) {         center = aCenter;     }     public float getRadius() {         return radius;     }     public void write(JMEExporter e) throws IOException {         super.write(e);         OutputCapsule capsule = e.getCapsule(this);         capsule.write(zSamples, "zSamples", 0);         capsule.write(radialSamples, "radialSamples", 0);         capsule.write(radius, "radius", 0);         capsule.write(center, "center", Vector3f.ZERO);     }     public void read(JMEImporter e) throws IOException {         super.read(e);         InputCapsule capsule = e.getCapsule(this);         zSamples = capsule.readInt("zSamples", 0);         radialSamples = capsule.readInt("radialSamples", 0);         radius = capsule.readFloat("radius", 0);         center = (Vector3f) capsule.readSavable("center", Vector3f.ZERO.clone());     }     public List<SquaredFace> squaredFaces = new ArrayList<SquaredFace>();     public void startExtrusion() {         originalVerticesCoords.position(0);         tempExtrudedVertices.position(0);         tempExtrudedVertices.put(originalVerticesCoords);         tempExtrudedColors.position(0);         final int size = getVertexCount();         for (int i = 0; i < size; i++) {             tempExtrudedColors.put(i * 4, SPHERE_COLOR.r).put(i * 4 + 1, SPHERE_COLOR.g).put(i * 4 + 2, SPHERE_COLOR.b).put(i * 4 + 3, SPHERE_COLOR.a);         }     }     public class SquaredFace {         public int v1;         public int v2;         public int v3;         public int v4;         public SquaredFace(final int v1, final int v2, final int v3, final int v4) {             this.v1 = v1;             this.v2 = v2;             this.v3 = v3;             this.v4 = v4;         }     }     public void extrude(int squaredFaceIndex, final float factor, final ColorRGBA color) {         final SquaredFace sq = squaredFaces.get(squaredFaceIndex);         final int v1Offset = sq.v1 * 3;         tempExtrudedVertices                 .put(v1Offset, factor * getNormalBuffer().get(v1Offset) + center.x)                 .put(v1Offset + 1, factor * getNormalBuffer().get(v1Offset + 1) + center.y)                 .put(v1Offset + 2, factor * getNormalBuffer().get(v1Offset + 2) + center.z);         tempExtrudedColors.put(v1Offset + sq.v1, color.r).put(v1Offset + sq.v1 + 1, color.g).put(v1Offset + sq.v1 + 2, color.b);         final int v2Offset = sq.v2 * 3;         tempExtrudedVertices                 .put(v2Offset, factor * getNormalBuffer().get(v2Offset) + center.x)                 .put(v2Offset + 1, factor * getNormalBuffer().get(v2Offset + 1) + center.y)                 .put(v2Offset + 2, factor * getNormalBuffer().get(v2Offset + 2) + center.z);         tempExtrudedColors.put(v2Offset + sq.v2, color.r).put(v2Offset + sq.v2 + 1, color.g).put(v2Offset + sq.v2 + 2, color.b);         final int v3Offset = sq.v3 * 3;         tempExtrudedVertices                 .put(v3Offset, factor * getNormalBuffer().get(v3Offset) + center.x)                 .put(v3Offset + 1, factor * getNormalBuffer().get(v3Offset + 1) + center.y)                 .put(v3Offset + 2, factor * getNormalBuffer().get(v3Offset + 2) + center.z);         tempExtrudedColors.put(v3Offset + sq.v3, color.r).put(v3Offset + sq.v3 + 1, color.g).put(v3Offset + sq.v3 + 2, color.b);         final int v4Offset = sq.v4 * 3;         tempExtrudedVertices                 .put(v4Offset, factor * getNormalBuffer().get(v4Offset) + center.x)                 .put(v4Offset + 1, factor * getNormalBuffer().get(v4Offset + 1) + center.y)                 .put(v4Offset + 2, factor * getNormalBuffer().get(v4Offset + 2) + center.z);         tempExtrudedColors.put(v4Offset + sq.v4, color.r).put(v4Offset + sq.v4 + 1, color.g).put(v4Offset + sq.v4 + 2, color.b);     }     public void finishExtrusion() {         getVertexBuffer().position(0);         tempExtrudedVertices.position(0);         getVertexBuffer().put(tempExtrudedVertices);         getColorBuffer().position(0);         tempExtrudedColors.position(0);         getColorBuffer().put(tempExtrudedColors);     } }```

Usage:

```QuarterSphere q = new QuarterSphere("name", 10.0f, 10.0f, 5.0f); q.setLightCombineMode(Spatial.LightCombineMode.Off); rootNode.attachChild(q); ... q.startExtrusion(); q.extrude( 10, 5.0f /* radius */ + 1.0f /* extrusion amount */, ColorRGBA.red); q.finishExtrusion(); ```

Sample of an Oracle instance: http://oraclefun.blogspot.com/2008/07/session-control-panel-r10.html