Tina J Tina J - 5 months ago 15
Java Question

How to draw the middle half of a sphere (in code)

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%? (25% in terms of angle, i.e.
theta
)

Here is the popular code for generating a sphere programmatically:

enter image description here

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;
}
}

}

Answer

The code here is using spherical coordinates to calculate the sphere. theta is the variable that represents the up/down coordinate that you're interested in, and theta goes from 0 to PI. You want to go from PI/4 to 3PI/4. stackNumbers simply represent the number of divisions in the sphere, since you can see that it is used as a denominator for stack, which is the wrong variable to change. So you can make the following changes to the code instead. From:

double theta0 = stackPercentage0 * Math.PI;
double theta1 = stackPercentage1 * Math.PI;

to:

double startTheta = Math.PI / 4;
double endTheta = 3 * Math.PI / 4;
double theta0 = stackPercentage0 * (endTheta - startTheta) + startTheta;
double theta1 = stackPercentage1 * (endTheta - startTheta) + startTheta;

And since you aren't using the caps you need to change the start and end stack numbers to reflect that:

for (int stack = 1; stack < stackNumber - 1; stack++) { // old
for (int stack = 0; stack < stackNumber; stack++) { // new

Also, you because you have more body faces now, you need to update the appropriate container for them. Replace (stackNumber - 2) with (stackNumber - 1).

Comments