Clean up some types here and there

This commit is contained in:
Peder Bergebakken Sundt 2019-03-15 16:34:17 +01:00
parent e00637d46d
commit 88376587e5
8 changed files with 153 additions and 144 deletions

View File

@ -1,23 +1,26 @@
#include <chrono>
#include <GLFW/glfw3.h>
#include <glad/glad.h>
#include <SFML/Audio/SoundBuffer.hpp>
#include <utilities/shader.hpp>
#include <glm/vec3.hpp>
#include <iostream>
#include <utilities/timeutils.h>
#include <utilities/mesh.h>
#include <utilities/shapes.h>
#include <utilities/glutils.h>
#include <utilities/glfont.h>
#include <utilities/imageLoader.hpp>
#include <SFML/Audio/Sound.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <string>
#include "gamelogic.h"
#include "sceneGraph.hpp"
#include <GLFW/glfw3.h>
#include <SFML/Audio/Sound.hpp>
#include <SFML/Audio/SoundBuffer.hpp>
#include <chrono>
#include <glad/glad.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/vec3.hpp>
#include <iostream>
#include <string>
#include <utilities/glfont.h>
#include <utilities/glutils.h>
#include <utilities/imageLoader.hpp>
#include <utilities/mesh.h>
#include <utilities/shader.hpp>
#include <utilities/shapes.h>
#include <utilities/timeutils.h>
using glm::vec3;
using glm::mat4;
typedef unsigned int uint;
enum KeyFrameAction {
BOTTOM, TOP
@ -28,8 +31,8 @@ enum KeyFrameAction {
double padPositionX = 0;
double padPositionY = 0;
unsigned int currentKeyFrame = 0;
unsigned int previousKeyFrame = 0;
uint currentKeyFrame = 0;
uint previousKeyFrame = 0;
SceneNode* rootNode;
SceneNode* boxNode;
@ -43,15 +46,15 @@ SceneNode* lightNode[3];
double ballRadius = 3.0f;
// These are heap allocated, because they should not be initialised at the start of the program
sf::Sound* sound;
sf::SoundBuffer* buffer;
Gloom::Shader* shader;
sf::Sound* sound;
const glm::vec3 boxDimensions(180, 90, 50);
const glm::vec3 padDimensions(30, 3, 40);
const vec3 boxDimensions(180, 90, 50);
const vec3 padDimensions(30, 3, 40);
glm::vec3 ballPosition(0, ballRadius + padDimensions.y, boxDimensions.z / 2);
glm::vec3 ballDirection(1, 1, 0.02f);
vec3 ballPosition(0, ballRadius + padDimensions.y, boxDimensions.z / 2);
vec3 ballDirection(1, 1, 0.02f);
const float BallVerticalTravelDistance = boxDimensions.y - 2.0 * ballRadius - padDimensions.y;
@ -114,18 +117,18 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
Mesh pad = generateBox(padDimensions.x, padDimensions.y, padDimensions.z, false);
Mesh sphere = generateSphere(1.0, 40, 40);
unsigned int ballVAO = generateBuffer(sphere);
unsigned int boxVAO = generateBuffer(box, true);
unsigned int padVAO = generateBuffer(pad);
uint ballVAO = generateBuffer(sphere);
uint boxVAO = generateBuffer(box, true);
uint padVAO = generateBuffer(pad);
// textures
t_charmap = loadPNGFile("../res/textures/charmap.png");
t_cobble_diff = loadPNGFile("../res/textures/cobble_diff.png");
t_cobble_normal = loadPNGFile("../res/textures/cobble_normal.png");
unsigned int t_charmapID = generateTexture(t_charmap);
unsigned int t_cobble_diffID = generateTexture(t_cobble_diff);
unsigned int t_cobble_normalID = generateTexture(t_cobble_normal);
uint t_charmapID = generateTexture(t_charmap);
uint t_cobble_diffID = generateTexture(t_cobble_diff);
uint t_cobble_normalID = generateTexture(t_cobble_normal);
rootNode = createSceneNode();
boxNode = createSceneNode(NORMAL_TEXTURED_GEOMETRY);
@ -172,8 +175,8 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
// hud
Mesh hello_world = generateTextGeometryBuffer("Skjer'a bagera?", 1.3, 2);
textNode->position = glm::vec3(-1.0, 0.0, 0.0);
textNode->rotation = glm::vec3(0.0, 0.0, 0.0);
textNode->position = vec3(-1.0, 0.0, 0.0);
textNode->rotation = vec3(0.0, 0.0, 0.0);
textNode->vertexArrayObjectID = generateBuffer(hello_world);
textNode->VAOIndexCount = hello_world.indices.size();
textNode->diffuseTextureID = t_charmapID;
@ -186,37 +189,37 @@ void initGame(GLFWwindow* window, CommandLineOptions gameOptions) {
std::cout << "Ready. Click to start!" << std::endl;
}
void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar, glm::mat4 V, glm::mat4 P) {
void updateNodeTransformations(SceneNode* node, mat4 transformationThusFar, mat4 V, mat4 P) {
glm::mat4 transformationMatrix(1.0);
mat4 transformationMatrix(1.0);
switch(node->nodeType) {
case HUD:
// We orthographic now, bitches!
// set orthographic VP
V = glm::mat4(1.0);
V = mat4(1.0);
P = glm::ortho(-float(windowWidth) / float(windowHeight), float(windowWidth) / float(windowHeight), -1.0f, 1.0f);//, -10.0f, 120.0f);
break;
case NORMAL_TEXTURED_GEOMETRY:
case TEXTURED_GEOMETRY:
case GEOMETRY:
transformationMatrix =
glm::translate(glm::mat4(1.0), node->position)
* glm::translate(glm::mat4(1.0), node->referencePoint)
* glm::rotate(glm::mat4(1.0), node->rotation.z, glm::vec3(0,0,1))
* glm::rotate(glm::mat4(1.0), node->rotation.y, glm::vec3(0,1,0))
* glm::rotate(glm::mat4(1.0), node->rotation.x, glm::vec3(1,0,0))
* glm::translate(glm::mat4(1.0), -node->referencePoint)
* glm::scale(glm::mat4(1.0), node->scale);
glm::translate(mat4(1.0), node->position)
* glm::translate(mat4(1.0), node->referencePoint)
* glm::rotate(mat4(1.0), node->rotation.z, vec3(0,0,1))
* glm::rotate(mat4(1.0), node->rotation.y, vec3(0,1,0))
* glm::rotate(mat4(1.0), node->rotation.x, vec3(1,0,0))
* glm::translate(mat4(1.0), -node->referencePoint)
* glm::scale(mat4(1.0), node->scale);
break;
case POINT_LIGHT:
case SPOT_LIGHT:
transformationMatrix =
glm::translate(glm::mat4(1.0), node->position);
glm::translate(mat4(1.0), node->position);
break;
}
glm::mat4 M = transformationThusFar * transformationMatrix;
glm::mat4 MV = V*M;
mat4 M = transformationThusFar * transformationMatrix;
mat4 MV = V*M;
node->MV = MV;
node->MVP = P*MV;
@ -228,7 +231,7 @@ void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar,
if (node->targeted_by != nullptr) {
assert(node->targeted_by->nodeType == SPOT_LIGHT);
node->targeted_by->rotation = glm::vec3(MV*glm::vec4(node->position, 1.0));
node->targeted_by->rotation = vec3(MV*glm::vec4(node->position, 1.0));
//std::cout << node->targeted_by->rotation[0]
// << " " << node->targeted_by->rotation[1]
@ -239,7 +242,6 @@ void updateNodeTransformations(SceneNode* node, glm::mat4 transformationThusFar,
void updateFrame(GLFWwindow* window) {
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
double timeDelta = getTimeDeltaSeconds();
if(!hasStarted) {
@ -271,7 +273,7 @@ void updateFrame(GLFWwindow* window) {
ballRadius = 999999;
}
} else {
for (unsigned int i = currentKeyFrame; i < keyFrameTimeStamps.size(); i++) {
for (uint i = currentKeyFrame; i < keyFrameTimeStamps.size(); i++) {
if (totalElapsedTime < keyFrameTimeStamps.at(i)) {
continue;
}
@ -365,14 +367,16 @@ void updateFrame(GLFWwindow* window) {
}
}
glm::mat4 projection = glm::perspective(glm::radians(90.0f), float(windowWidth) / float(windowHeight), 0.1f,
mat4 projection = glm::perspective(glm::radians(90.0f), float(windowWidth) / float(windowHeight), 0.1f,
120.f);
glm::mat4 cameraTransform = glm::translate(glm::mat4(1), glm::vec3(0, 0, 0))
* glm::rotate(glm::mat4(1.0), 0.2f, glm::vec3(1, 0, 0))
* glm::rotate(glm::mat4(1.0), float(M_PI), glm::vec3(0, 1, 0));
// hardcoded camera position...
mat4 cameraTransform
= glm::translate(mat4(1), vec3(0, 0, 0))
* glm::rotate(mat4(1.0), 0.2f, vec3(1, 0, 0))
* glm::rotate(mat4(1.0), float(M_PI), vec3(0, 1, 0));
updateNodeTransformations(rootNode, glm::mat4(1.0), cameraTransform, projection);
updateNodeTransformations(rootNode, mat4(1.0), cameraTransform, projection);
boxNode->position = {-boxDimensions.x / 2, -boxDimensions.y / 2 - 15, boxDimensions.z - 10};
padNode->position = {-boxDimensions.x / 2 + (1 - padPositionX) * (boxDimensions.x - padDimensions.x),

View File

@ -1,8 +1,8 @@
#pragma once
#include <GLFW/glfw3.h>
#include <utilities/window.hpp>
void initGame(GLFWwindow* window, CommandLineOptions options);
void updateFrame(GLFWwindow* window);
void renderFrame(GLFWwindow* window);
void renderFrame(GLFWwindow* window);

View File

@ -1,26 +1,22 @@
#pragma once
#include <glm/glm.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <stack>
#include <vector>
#include <chrono>
#include <cstdio>
#include <stdbool.h>
#include <cstdlib>
#include <ctime>
#include <chrono>
#include <fstream>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/mat4x4.hpp>
#include <stack>
#include <stdbool.h>
#include <utilities/shader.hpp>
#include <vector>
enum SceneNodeType {
GEOMETRY, POINT_LIGHT, SPOT_LIGHT, HUD, TEXTURED_GEOMETRY, NORMAL_TEXTURED_GEOMETRY
};
// In case you haven't got much experience with C or C++, let me explain this "typedef" you see below.
// The point of a typedef is that you it, as its name implies, allows you to define arbitrary data types based upon existing ones. For instance, "typedef float typeWhichMightBeAFloat;" allows you to define a variable such as this one: "typeWhichMightBeAFloat variableName = 5.0;". The C/C++ compiler translates this type into a float.
// What is the point of using it here? A smrt person, while designing the C language, thought it would be a good idea for various reasons to force you to explicitly state that you are using a data structure datatype (struct). So, when defining a variable, you'd have to type "struct SceneNode node = ..." in the case of a SceneNode. Which can get in the way of readability.
// If we just use typedef to define a new type called "SceneNode", which really is the type "struct SceneNode", we can omit the "struct" part when creating an instance of SceneNode.
struct SceneNode {
SceneNode() {
position = glm::vec3(0, 0, 0);
@ -41,8 +37,6 @@ struct SceneNode {
nodeType = type;
}
// A list of all children that belong to this node.
// For instance, in case of the scene graph of a human body shown in the assignment text, the "Upper Torso" node would contain the "Left Arm", "Right Arm", "Head" and "Lower Torso" nodes in its list of children.
std::vector<SceneNode*> children;
// The node's position and rotation relative to its parent
@ -82,6 +76,3 @@ SceneNode* createSceneNode();
SceneNode* createSceneNode(SceneNodeType type);
void addChild(SceneNode* parent, SceneNode* child);
void printNode(SceneNode* node);
// For more details, see SceneGraph.cpp.

View File

@ -1,7 +1,12 @@
#include <vector>
#include <glad/glad.h>
#include <program.hpp>
#include "glutils.h"
using std::vector;
using glm::vec3;
using glm::vec2;
unsigned int generateBuffer(Mesh &mesh, bool isNormalMapped) {
unsigned int vaoID;
glGenVertexArrays(1, &vaoID);
@ -10,14 +15,14 @@ unsigned int generateBuffer(Mesh &mesh, bool isNormalMapped) {
unsigned int vertexBufferID;
glGenBuffers(1, &vertexBufferID);
glBindBuffer(GL_ARRAY_BUFFER, vertexBufferID);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3), mesh.vertices.data(), GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(vec3), mesh.vertices.data(), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(0);
unsigned int normalBufferID;
glGenBuffers(1, &normalBufferID);
glBindBuffer(GL_ARRAY_BUFFER, normalBufferID);
glBufferData(GL_ARRAY_BUFFER, mesh.normals.size() * sizeof(glm::vec3), mesh.normals.data(), GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.normals.size() * sizeof(vec3), mesh.normals.data(), GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(1);
@ -31,7 +36,7 @@ unsigned int generateBuffer(Mesh &mesh, bool isNormalMapped) {
unsigned int textureBufferID;
glGenBuffers(1, &textureBufferID);
glBindBuffer(GL_ARRAY_BUFFER, textureBufferID);
glBufferData(GL_ARRAY_BUFFER, mesh.textureCoordinates.size() * sizeof(glm::vec2), mesh.textureCoordinates.data(), GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.textureCoordinates.size() * sizeof(vec2), mesh.textureCoordinates.data(), GL_STATIC_DRAW);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0);
glEnableVertexAttribArray(2);
@ -41,26 +46,26 @@ unsigned int generateBuffer(Mesh &mesh, bool isNormalMapped) {
}
void addTangents(unsigned int vaoID, Mesh& mesh) {
glm::vec3 tangents[mesh.vertices.size()];
glm::vec3 bitangents[mesh.vertices.size()];
vector<vec3> tangents(mesh.vertices.size());
vector<vec3> bitangents(mesh.vertices.size());
for (unsigned int i = 0; i < mesh.indices.size(); i+=3) {
const glm::vec3& pos1 = mesh.vertices[mesh.indices[i+0]];
const glm::vec3& pos2 = mesh.vertices[mesh.indices[i+1]];
const glm::vec3& pos3 = mesh.vertices[mesh.indices[i+2]];
const vec3& pos1 = mesh.vertices[mesh.indices[i+0]];
const vec3& pos2 = mesh.vertices[mesh.indices[i+1]];
const vec3& pos3 = mesh.vertices[mesh.indices[i+2]];
const glm::vec2& uv1 = mesh.textureCoordinates[mesh.indices[i+0]];
const glm::vec2& uv2 = mesh.textureCoordinates[mesh.indices[i+1]];
const glm::vec2& uv3 = mesh.textureCoordinates[mesh.indices[i+2]];
const vec2& uv1 = mesh.textureCoordinates[mesh.indices[i+0]];
const vec2& uv2 = mesh.textureCoordinates[mesh.indices[i+1]];
const vec2& uv3 = mesh.textureCoordinates[mesh.indices[i+2]];
glm::vec3 edge1 = pos2 - pos1;
glm::vec3 edge2 = pos3 - pos1;
glm::vec2 deltaUV1 = uv2 - uv1;
glm::vec2 deltaUV2 = uv3 - uv1;
vec3 edge1 = pos2 - pos1;
vec3 edge2 = pos3 - pos1;
vec2 deltaUV1 = uv2 - uv1;
vec2 deltaUV2 = uv3 - uv1;
float f = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV2.x * deltaUV1.y);
glm::vec3 tangent, bitangent;
vec3 tangent, bitangent;
tangent.x = f * (deltaUV2.y * edge1.x - deltaUV1.y * edge2.x);
tangent.y = f * (deltaUV2.y * edge1.y - deltaUV1.y * edge2.y);
tangent.z = f * (deltaUV2.y * edge1.z - deltaUV1.y * edge2.z);
@ -86,14 +91,14 @@ void addTangents(unsigned int vaoID, Mesh& mesh) {
unsigned int tangentBufferID;
glGenBuffers(1, &tangentBufferID);
glBindBuffer(GL_ARRAY_BUFFER, tangentBufferID);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3), tangents, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(vec3), tangents.data(), GL_STATIC_DRAW);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(3);
unsigned int bitangentBufferID;
glGenBuffers(1, &bitangentBufferID);
glBindBuffer(GL_ARRAY_BUFFER, bitangentBufferID);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(glm::vec3), bitangents, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, mesh.vertices.size() * sizeof(vec3), bitangents.data(), GL_STATIC_DRAW);
glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
glEnableVertexAttribArray(4);
}

View File

@ -1,41 +1,44 @@
#include "imageLoader.hpp"
#include <iostream>
// Original source: https://raw.githubusercontent.com/lvandeve/lodepng/master/examples/example_decode.cpp
PNGImage loadPNGFile(std::string fileName)
{
std::vector<unsigned char> png;
std::vector<unsigned char> pixels; //the raw pixels
unsigned int width, height;
//load and decode
unsigned error = lodepng::load_file(png, fileName);
if(!error) error = lodepng::decode(pixels, width, height, png);
//if there's an error, display it
if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
//the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
// Unfortunately, images usually have their origin at the top left.
// OpenGL instead defines the origin to be on the _bottom_ left instead, so
// here's the world's most inefficient way to flip the image vertically.
// You're welcome :)
unsigned int widthBytes = 4 * width;
for(unsigned int row = 0; row < (height / 2); row++) {
for(unsigned int col = 0; col < widthBytes; col++) {
std::swap(pixels[row * widthBytes + col], pixels[(height - 1 - row) * widthBytes + col]);
}
}
PNGImage image;
image.width = width;
image.height = height;
image.pixels = pixels;
return image;
}
#include "imageLoader.hpp"
#include <glm/vec2.hpp>
#include <glm/gtc/noise.hpp>
#include <iostream>
using glm::vec2;
using std::vector;
// Original source: https://raw.githubusercontent.com/lvandeve/lodepng/master/examples/example_decode.cpp
PNGImage loadPNGFile(std::string fileName) {
vector<unsigned char> png;
vector<unsigned char> pixels; //the raw pixels
unsigned int width, height;
//load and decode
unsigned error = lodepng::load_file(png, fileName);
if(!error) error = lodepng::decode(pixels, width, height, png);
//if there's an error, display it
if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl;
//the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ...
// Unfortunately, images usually have their origin at the top left.
// OpenGL instead defines the origin to be on the _bottom_ left instead, so
// here's the world's most inefficient way to flip the image vertically.
// You're welcome :)
unsigned int widthBytes = 4 * width;
for(unsigned int row = 0; row < (height / 2); row++) {
for(unsigned int col = 0; col < widthBytes; col++) {
std::swap(pixels[row * widthBytes + col], pixels[(height - 1 - row) * widthBytes + col]);
}
}
PNGImage image;
image.width = width;
image.height = height;
image.pixels = pixels;
return image;
}

View File

@ -4,9 +4,11 @@
#include <vector>
#include <string>
typedef unsigned int uint;
typedef struct PNGImage {
unsigned int width, height;
std::vector<unsigned char> pixels;
std::vector<unsigned char> pixels; // RGBA
} PNGImage;
PNGImage loadPNGFile(std::string fileName);

View File

@ -35,8 +35,7 @@ namespace Gloom
{
// Load GLSL Shader from source
std::ifstream fd(filename.c_str());
if (fd.fail())
{
if (fd.fail()) {
fprintf(stderr,
"Something went wrong when attaching the Shader file at \"%s\".\n"
"The file may not exist or is currently inaccessible.\n",

View File

@ -1,12 +1,17 @@
#include <iostream>
#include "shapes.h"
typedef uint uint;
using glm::vec3;
using glm::vec2;
using std::vector;
Mesh generateBox(float width, float height, float depth, bool flipFaces) {
// Hardcoded. Sue me.
// Edit: well, that backfired..
std::vector<glm::vec3> vertices = {
vector<vec3> vertices = {
{0, 0, 0},
{0, 0, depth},
{0, height, depth},
@ -60,7 +65,7 @@ Mesh generateBox(float width, float height, float depth, bool flipFaces) {
// But for some strange reason the faces are rendered inverted.
// So to make the assignment work this is the best I can do.
std::vector<glm::vec3> normals = {
vector<vec3> normals = {
{1.0, 0.0, 0.0},
{1.0, 0.0, 0.0},
{1.0, 0.0, 0.0},
@ -114,7 +119,7 @@ Mesh generateBox(float width, float height, float depth, bool flipFaces) {
float texScaleFactorY = width / depth;
float texScaleFactorZ = width / height;
std::vector<glm::vec2> textureCoordinates = {
vector<vec2> textureCoordinates = {
{0, 0},
{texScaleFactorX, 0},
{texScaleFactorX, 1},
@ -166,7 +171,7 @@ Mesh generateBox(float width, float height, float depth, bool flipFaces) {
};
std::vector<unsigned int> indices = {
vector<uint> indices = {
0, 1, 2,
3, 4, 5,
6, 7, 8,
@ -183,7 +188,7 @@ Mesh generateBox(float width, float height, float depth, bool flipFaces) {
if(flipFaces) {
for(int i = 0; i < 36; i += 3) {
unsigned int temp = indices[i + 1];
uint temp = indices[i + 1];
indices[i + 1] = indices[i + 2];
indices[i + 2] = temp;
@ -203,11 +208,11 @@ Mesh generateBox(float width, float height, float depth, bool flipFaces) {
}
Mesh generateSphere(float sphereRadius, int slices, int layers) {
const unsigned int triangleCount = slices * layers * 2;
const uint triangleCount = slices * layers * 2;
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> normals;
std::vector<unsigned int> indices;
vector<vec3> vertices;
vector<vec3> normals;
vector<uint> indices;
vertices.reserve(3 * triangleCount);
normals.reserve(3 * triangleCount);
@ -218,7 +223,7 @@ Mesh generateSphere(float sphereRadius, int slices, int layers) {
const float degreesPerLayer = 180.0 / (float) layers;
const float degreesPerSlice = 360.0 / (float) slices;
unsigned int i = 0;
uint i = 0;
// Constructing the sphere one layer at a time
for (int layer = 0; layer < layers; layer++) {