/** builds the vertices based on the radius, center and radial and zSamples. */
  private void setGeometryData(PApplet pa) {
    // allocate vertices
    //        setVertexCount((zSamples - 2) * (radialSamples + 1) + 2);
    //        setVertexBuffer(ToolsBuffers.createVector3Buffer(vertBuff,
    //                getVertexCount()));

    int vertexCount = (zSamples - 2) * (radialSamples + 1) + 2;
    FloatBuffer vertexBuff = ToolsBuffers.createFloatBuffer(3 * vertexCount);

    // allocate normals if requested
    //        setNormalBuffer(ToolsBuffers.createVector3Buffer(normBuff,
    //                getVertexCount()));
    FloatBuffer normBuff = ToolsBuffers.createFloatBuffer(3 * vertexCount);

    // allocate texture coordinates
    //        setTextureCoords(new TexCoords(ToolsBuffers.createVector2Buffer(getVertexCount())));
    FloatBuffer texBuff = ToolsBuffers.createFloatBuffer(2 * vertexCount);

    // generate geometry
    float fInvRS = 1.0f / radialSamples;
    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++) {
      float fAngle = ToolsMath.TWO_PI * fInvRS * iR;
      afCos[iR] = ToolsMath.cos(fAngle);
      afSin[iR] = ToolsMath.sin(fAngle);
    }
    afSin[radialSamples] = afSin[0];
    afCos[radialSamples] = afCos[0];

    // generate the sphere itself
    int i = 0;
    for (int iZ = 1; iZ < (zSamples - 1); iZ++) {
      float fAFraction = ToolsMath.HALF_PI * (-1.0f + fZFactor * iZ); // in (-pi/2, pi/2)
      float fZFraction;
      if (useEvenSlices) fZFraction = -1.0f + fZFactor * iZ; // in (-1, 1)
      else fZFraction = ToolsMath.sin(fAFraction); // in (-1,1)

      float fZ = radius * fZFraction;

      // compute center of slice
      Vector3D kSliceCenter = tempVb.setValues(center);
      kSliceCenter.z += fZ;

      // compute radius of slice
      float fSliceRadius = ToolsMath.sqrt(ToolsMath.abs(radius * radius - fZ * fZ));

      // compute slice vertices with duplication at end point
      Vector3D kNormal;
      int iSave = i;
      for (int iR = 0; iR < radialSamples; iR++) {
        float fRadialFraction = iR * fInvRS; // in [0,1)
        tempVc.setXYZ(afCos[iR], afSin[iR], 0);
        Vector3D kRadial = tempVc;
        tempVa = kRadial.getScaled(fSliceRadius);

        vertexBuff
            .put(kSliceCenter.x + tempVa.x)
            .put(kSliceCenter.y + tempVa.y)
            .put(kSliceCenter.z + tempVa.z);

        ToolsBuffers.populateFromBuffer(tempVa, vertexBuff, i);
        kNormal = tempVa.subtractLocal(center);
        kNormal.normalizeLocal();
        if (true) // later we may allow interior texture vs. exterior
        normBuff.put(kNormal.x).put(kNormal.y).put(kNormal.z);
        else normBuff.put(-kNormal.x).put(-kNormal.y).put(-kNormal.z);

        if (textureMode == TextureMode.Original)
          texBuff.put(fRadialFraction).put(0.5f * (fZFraction + 1.0f));
        else if (textureMode == TextureMode.Projected)
          texBuff
              .put(fRadialFraction)
              .put(ToolsMath.INV_PI * (ToolsMath.HALF_PI + ToolsMath.asin(fZFraction)));
        else if (textureMode == TextureMode.Polar) {
          float r = (ToolsMath.HALF_PI - ToolsMath.abs(fAFraction)) / ToolsMath.PI;
          float u = r * afCos[iR] + 0.5f;
          float v = r * afSin[iR] + 0.5f;
          texBuff.put(u).put(v);
        }

        i++;
      }

      copyInternalVector3(vertexBuff, iSave, i);
      copyInternalVector3(normBuff, iSave, i);

      if (textureMode == TextureMode.Original) texBuff.put(1.0f).put(0.5f * (fZFraction + 1.0f));
      else if (textureMode == TextureMode.Projected)
        texBuff.put(1.0f).put(ToolsMath.INV_PI * (ToolsMath.HALF_PI + ToolsMath.asin(fZFraction)));
      else if (textureMode == TextureMode.Polar) {
        float r = (ToolsMath.HALF_PI - ToolsMath.abs(fAFraction)) / ToolsMath.PI;
        texBuff.put(r + 0.5f).put(0.5f);
      }

      i++;
    }

    // south pole
    vertexBuff.position(i * 3);
    vertexBuff.put(center.x).put(center.y).put(center.z - radius);

    normBuff.position(i * 3);
    if (true) normBuff.put(0).put(0).put(-1); // allow for inner
    // texture orientation
    // later.
    else normBuff.put(0).put(0).put(1);

    texBuff.position(i * 2);

    if (textureMode == TextureMode.Polar) {
      texBuff.put(0.5f).put(0.5f);
    } else {
      texBuff.put(0.5f).put(0.0f);
    }

    i++;

    // north pole
    vertexBuff.put(center.x).put(center.y).put(center.z + radius);

    if (true) normBuff.put(0).put(0).put(1);
    else normBuff.put(0).put(0).put(-1);

    if (textureMode == TextureMode.Polar) {
      texBuff.put(0.5f).put(0.5f);
    } else {
      texBuff.put(0.5f).put(1.0f);
    }

    Vertex[] verts = ToolsBuffers.getVertexArray(vertexBuff);
    Vector3D[] norms = ToolsBuffers.getVector3DArray(normBuff);

    // Set texcoords to vertices
    float[] tex = ToolsBuffers.getFloatArray(texBuff);
    for (int j = 0; j < tex.length / 2; j++) {
      float u = tex[j * 2];
      float v = tex[j * 2 + 1];
      verts[j].setTexCoordU(u);
      verts[j].setTexCoordV(v);
    }

    // get indices
    short[] indices = this.getIndexData();

    GeometryInfo geomInfo = new GeometryInfo(pa, verts, norms, indices);
    this.setGeometryInfo(geomInfo);
  }