# Draw the middle half of a sphere programmatically

I'm trying to create the middle half of a sphere. Basically to create a sphere, stack numbers and slice numbers are given, and there are two variables phi (for slices) and theta (for stacks) responsible for how much to progress. And the process is divided into creating bottom cap, body, and top cap (as seen below). To achieve middle half (theta of middle 50% as below), we need to omit the caps, and somehow modify the body. I was playing around with stack numbers (1/4*stackNumbers to 3/4*stackNumbers) but didn't give the result I wanted.

How should I modify the sphere generation to achieve the middle half (pi/4 <theta <pi*3/4)? My overall problem is how can I split the sphere into 3 different parts upper 25%, middle 50%, and bottom 25%?

Here is the popular code for generating a sphere programmatically:

private void generateSphere(int stackNumber, int sliceNumber, boolean facingOut) {
int capVertexNumber = 3 * sliceNumber;
int bodyVertexNumber = 4 * sliceNumber * (stackNumber - 2);
int vertexNumber = (2 * capVertexNumber) + bodyVertexNumber;
int triangleNumber = (2 * capVertexNumber) + (6 * sliceNumber * (stackNumber - 2));

vertices = new float[3 * vertexNumber];
normals = new float[3 * vertexNumber];
texCoords = new float[2 * vertexNumber];
indices = new char[triangleNumber];

// bottom cap
// createCap(stackNumber, sliceNumber, false, facingOut);

// body
createBody(stackNumber, sliceNumber, facingOut);

// top cap
createCap(stackNumber, sliceNumber, true, facingOut);
}

private void createCap(int stackNumber, int sliceNumber, boolean top, boolean facingOut) {

float stackPercentage0;
float stackPercentage1;

if (!top) {
stackPercentage0 = ((float) (stackNumber - 1) / stackNumber);
stackPercentage1 = 1.0f;

} else {
stackPercentage0 = (1.0f / stackNumber);
stackPercentage1 = 0.0f;
}

float t0 = stackPercentage0;
float t1 = stackPercentage1;
double theta0 = stackPercentage0 * Math.PI;
double theta1 = stackPercentage1 * Math.PI;
double cosTheta0 = Math.cos(theta0);
double sinTheta0 = Math.sin(theta0);
double cosTheta1 = Math.cos(theta1);
double sinTheta1 = Math.sin(theta1);

for (int slice = 0; slice < sliceNumber; slice++) {
float slicePercentage0 = ((float) (slice) / sliceNumber);
float slicePercentage1 = ((float) (slice + 1) / sliceNumber);
double phi0 = slicePercentage0 * 2.0 * Math.PI;
double phi1 = slicePercentage1 * 2.0 * Math.PI;
float s0, s1;
if (facingOut) {
s0 = 1 - slicePercentage0;
s1 = 1 - slicePercentage1;
} else {
s0 = slicePercentage0;
s1 = slicePercentage1;
}
float s2 = (s0 + s1) / 2.0f;
double cosPhi0 = Math.cos(phi0);
double sinPhi0 = Math.sin(phi0);
double cosPhi1 = Math.cos(phi1);
double sinPhi1 = Math.sin(phi1);

float x0 = (float) (sinTheta0 * cosPhi0);
float y0 = (float) cosTheta0;
float z0 = (float) (sinTheta0 * sinPhi0);

float x1 = (float) (sinTheta0 * cosPhi1);
float y1 = (float) cosTheta0;
float z1 = (float) (sinTheta0 * sinPhi1);

float x2 = (float) (sinTheta1 * cosPhi0);
float y2 = (float) cosTheta1;
float z2 = (float) (sinTheta1 * sinPhi0);

vertices[vertexCount + 0] = x0;
vertices[vertexCount + 1] = y0;
vertices[vertexCount + 2] = z0;

vertices[vertexCount + 3] = x1;
vertices[vertexCount + 4] = y1;
vertices[vertexCount + 5] = z1;

vertices[vertexCount + 6] = x2;
vertices[vertexCount + 7] = y2;
vertices[vertexCount + 8] = z2;

if (facingOut) {
normals[vertexCount + 0] = x0;
normals[vertexCount + 1] = y0;
normals[vertexCount + 2] = z0;

normals[vertexCount + 3] = x1;
normals[vertexCount + 4] = y1;
normals[vertexCount + 5] = z1;

normals[vertexCount + 6] = x2;
normals[vertexCount + 7] = y2;
normals[vertexCount + 8] = z2;
} else {
normals[vertexCount + 0] = -x0;
normals[vertexCount + 1] = -y0;
normals[vertexCount + 2] = -z0;

normals[vertexCount + 3] = -x1;
normals[vertexCount + 4] = -y1;
normals[vertexCount + 5] = -z1;

normals[vertexCount + 6] = -x2;
normals[vertexCount + 7] = -y2;
normals[vertexCount + 8] = -z2;
}

texCoords[texCoordCount + 0] = s0;
texCoords[texCoordCount + 1] = t0;
texCoords[texCoordCount + 2] = s1;
texCoords[texCoordCount + 3] = t0;
texCoords[texCoordCount + 4] = s2;
texCoords[texCoordCount + 5] = t1;

if ((facingOut && top) || (!facingOut && !top)) {
indices[indexCount + 0] = (char) (triangleCount + 1);
indices[indexCount + 1] = (char) (triangleCount + 0);
indices[indexCount + 2] = (char) (triangleCount + 2);
} else {
indices[indexCount + 0] = (char) (triangleCount + 0);
indices[indexCount + 1] = (char) (triangleCount + 1);
indices[indexCount + 2] = (char) (triangleCount + 2);
}

vertexCount += 9;
texCoordCount += 6;
indexCount += 3;
triangleCount += 3;
}

}

private void createBody(int stackNumber, int sliceNumber, boolean facingOut) {
for (int stack = 1; stack < stackNumber - 1; stack++) {
float stackPercentage0 = ((float) (stack) / stackNumber);
float stackPercentage1 = ((float) (stack + 1) / stackNumber);

float t0 = stackPercentage0;
float t1 = stackPercentage1;

double theta0 = stackPercentage0 * Math.PI;
double theta1 = stackPercentage1 * Math.PI;
double cosTheta0 = Math.cos(theta0);
double sinTheta0 = Math.sin(theta0);
double cosTheta1 = Math.cos(theta1);
double sinTheta1 = Math.sin(theta1);

for (int slice = 0; slice < sliceNumber; slice++) {
float slicePercentage0 = ((float) (slice) / sliceNumber);
float slicePercentage1 = ((float) (slice + 1) / sliceNumber);
double phi0 = slicePercentage0 * 2.0 * Math.PI;
double phi1 = slicePercentage1 * 2.0 * Math.PI;
float s0, s1;
if (facingOut) {
s0 = 1.0f - slicePercentage0;
s1 = 1.0f - slicePercentage1;
} else {
s0 = slicePercentage0;
s1 = slicePercentage1;
}
double cosPhi0 = Math.cos(phi0);
double sinPhi0 = Math.sin(phi0);
double cosPhi1 = Math.cos(phi1);
double sinPhi1 = Math.sin(phi1);

float x0 = (float) (sinTheta0 * cosPhi0);
float y0 = (float) cosTheta0;
float z0 = (float) (sinTheta0 * sinPhi0);

float x1 = (float) (sinTheta0 * cosPhi1);
float y1 = (float) cosTheta0;
float z1 = (float) (sinTheta0 * sinPhi1);

float x2 = (float) (sinTheta1 * cosPhi0);
float y2 = (float) cosTheta1;
float z2 = (float) (sinTheta1 * sinPhi0);

float x3 = (float) (sinTheta1 * cosPhi1);
float y3 = (float) cosTheta1;
float z3 = (float) (sinTheta1 * sinPhi1);

vertices[vertexCount + 0] = x0;
vertices[vertexCount + 1] = y0;
vertices[vertexCount + 2] = z0;

vertices[vertexCount + 3] = x1;
vertices[vertexCount + 4] = y1;
vertices[vertexCount + 5] = z1;

vertices[vertexCount + 6] = x2;
vertices[vertexCount + 7] = y2;
vertices[vertexCount + 8] = z2;

vertices[vertexCount + 9] = x3;
vertices[vertexCount + 10] = y3;
vertices[vertexCount + 11] = z3;

if (facingOut) {
normals[vertexCount + 0] = x0;
normals[vertexCount + 1] = y0;
normals[vertexCount + 2] = z0;

normals[vertexCount + 3] = x1;
normals[vertexCount + 4] = y1;
normals[vertexCount + 5] = z1;

normals[vertexCount + 6] = x2;
normals[vertexCount + 7] = y2;
normals[vertexCount + 8] = z2;

normals[vertexCount + 9] = x3;
normals[vertexCount + 10] = y3;
normals[vertexCount + 11] = z3;
} else {
normals[vertexCount + 0] = -x0;
normals[vertexCount + 1] = -y0;
normals[vertexCount + 2] = -z0;

normals[vertexCount + 3] = -x1;
normals[vertexCount + 4] = -y1;
normals[vertexCount + 5] = -z1;

normals[vertexCount + 6] = -x2;
normals[vertexCount + 7] = -y2;
normals[vertexCount + 8] = -z2;

normals[vertexCount + 9] = -x3;
normals[vertexCount + 10] = -y3;
normals[vertexCount + 11] = -z3;
}

texCoords[texCoordCount + 0] = s0;
texCoords[texCoordCount + 1] = t0;
texCoords[texCoordCount + 2] = s1;
texCoords[texCoordCount + 3] = t0;
texCoords[texCoordCount + 4] = s0;
texCoords[texCoordCount + 5] = t1;
texCoords[texCoordCount + 6] = s1;
texCoords[texCoordCount + 7] = t1;

// one quad looking from outside toward center
//
// @formatter:off
//
// s1 --> s0
//
// t0 1-----0
// | | |
// v | |
// t1 3-----2
//
// @formatter:on
//
// Note that tex_coord t increase from top to bottom because the
// texture image is loaded upside down.
if (facingOut) {
indices[indexCount + 0] = (char) (triangleCount + 0);
indices[indexCount + 1] = (char) (triangleCount + 1);
indices[indexCount + 2] = (char) (triangleCount + 2);

indices[indexCount + 3] = (char) (triangleCount + 2);
indices[indexCount + 4] = (char) (triangleCount + 1);
indices[indexCount + 5] = (char) (triangleCount + 3);
} else {
indices[indexCount + 0] = (char) (triangleCount + 0);
indices[indexCount + 1] = (char) (triangleCount + 2);
indices[indexCount + 2] = (char) (triangleCount + 1);

indices[indexCount + 3] = (char) (triangleCount + 2);
indices[indexCount + 4] = (char) (triangleCount + 3);
indices[indexCount + 5] = (char) (triangleCount + 1);
}

vertexCount += 12;
texCoordCount += 8;
indexCount += 6;
triangleCount += 4;
}
}

}


Think of your question in terms of tessellation with your sphere formed by rings. If you have a tessellation factor of 5, you will have a top cap, 2 middle sections, and a bottom cap. The bottom two tessellation ring being the 25% bottom cap and the top tessellation two rings the 25% top cap. The center is therefore one ring at the equator with a set of faces on either side of it at 50%. Here is a sample of how to tessellate a sphere by first calculating the rings at each height and then generating the faces between each ring.

float radius = 1.0f;
const int tesselationFactor = 5;

for (int i = 0; i <= tesselationFactor; i++)
{
const float PI = 3.14159265359f;
const float PIDIV2 = PI / 2.0f;

// find height of ring
float height = (i*PI / tesselationFactor) - PIDIV2;
float dy = sinf(height);
float theta = cosf(height);

// locate verticies equally around center to form a ring
for (int j = 0; j <= tessellation * 2; j++)
{

float longitude = static_cast<float>(j) * PIDIV2 / (tessellation * 2);
float dx = sinf(longitude) * theta;
float dz = cosf(longitude) * theta;

Vector normal{ dx, dy, dz }; // initialize vector
}
}
// Fill the index buffer with triangles joining each pair of latitude rings.
int stride = tessellation * 2 + 1;
for (int i = 0; i < tesselationFactor; i++) // verticle rings
{
for (int j = 0; j <= tessellation * 2; j++) // horizontal verticies
{
//
int i1 = i + 1;
int j1 = (j + 1) % stride;
/* connecting face */
// triangle 1
index_push_back(indices, i * stride + j);
index_push_back(indices, i1 * stride + j);
index_push_back(indices, i * stride + j1);
// triangle 2
index_push_back(indices, i * stride + j1);
index_push_back(indices, i1 * stride + j);
index_push_back(indices, i1 * stride + j1);
}
}