From 88376587e58d5adf4575bfb8354ae1f849020493 Mon Sep 17 00:00:00 2001 From: Peder Bergebakken Sundt Date: Fri, 15 Mar 2019 16:34:17 +0100 Subject: [PATCH] Clean up some types here and there --- src/gamelogic.cpp | 110 ++++++++++++++++++---------------- src/gamelogic.h | 4 +- src/sceneGraph.hpp | 25 +++----- src/utilities/glutils.cpp | 41 +++++++------ src/utilities/imageLoader.cpp | 85 +++++++++++++------------- src/utilities/imageLoader.hpp | 4 +- src/utilities/shader.hpp | 3 +- src/utilities/shapes.cpp | 25 ++++---- 8 files changed, 153 insertions(+), 144 deletions(-) diff --git a/src/gamelogic.cpp b/src/gamelogic.cpp index 647054b..b1846a6 100644 --- a/src/gamelogic.cpp +++ b/src/gamelogic.cpp @@ -1,23 +1,26 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "gamelogic.h" #include "sceneGraph.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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), diff --git a/src/gamelogic.h b/src/gamelogic.h index 3965eff..5861739 100644 --- a/src/gamelogic.h +++ b/src/gamelogic.h @@ -1,8 +1,8 @@ #pragma once - +#include #include void initGame(GLFWwindow* window, CommandLineOptions options); void updateFrame(GLFWwindow* window); -void renderFrame(GLFWwindow* window); \ No newline at end of file +void renderFrame(GLFWwindow* window); diff --git a/src/sceneGraph.hpp b/src/sceneGraph.hpp index 051558b..4b9d496 100644 --- a/src/sceneGraph.hpp +++ b/src/sceneGraph.hpp @@ -1,26 +1,22 @@ #pragma once -#include -#include -#include - -#include -#include +#include #include -#include #include #include -#include #include +#include +#include +#include +#include +#include +#include +#include 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 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. diff --git a/src/utilities/glutils.cpp b/src/utilities/glutils.cpp index 02a394f..12b8f53 100644 --- a/src/utilities/glutils.cpp +++ b/src/utilities/glutils.cpp @@ -1,7 +1,12 @@ +#include #include #include #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 tangents(mesh.vertices.size()); + vector 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); } diff --git a/src/utilities/imageLoader.cpp b/src/utilities/imageLoader.cpp index ac09551..7b2ee87 100644 --- a/src/utilities/imageLoader.cpp +++ b/src/utilities/imageLoader.cpp @@ -1,41 +1,44 @@ -#include "imageLoader.hpp" -#include - -// Original source: https://raw.githubusercontent.com/lvandeve/lodepng/master/examples/example_decode.cpp -PNGImage loadPNGFile(std::string fileName) -{ - std::vector png; - std::vector 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 +#include +#include + +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 png; + vector 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; +} diff --git a/src/utilities/imageLoader.hpp b/src/utilities/imageLoader.hpp index 5f3099f..34408a1 100644 --- a/src/utilities/imageLoader.hpp +++ b/src/utilities/imageLoader.hpp @@ -4,9 +4,11 @@ #include #include +typedef unsigned int uint; + typedef struct PNGImage { unsigned int width, height; - std::vector pixels; + std::vector pixels; // RGBA } PNGImage; PNGImage loadPNGFile(std::string fileName); \ No newline at end of file diff --git a/src/utilities/shader.hpp b/src/utilities/shader.hpp index c73e4c9..cdd6f01 100644 --- a/src/utilities/shader.hpp +++ b/src/utilities/shader.hpp @@ -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", diff --git a/src/utilities/shapes.cpp b/src/utilities/shapes.cpp index c0cf322..fbbad40 100644 --- a/src/utilities/shapes.cpp +++ b/src/utilities/shapes.cpp @@ -1,12 +1,17 @@ #include #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 vertices = { + vector 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 normals = { + vector 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 textureCoordinates = { + vector textureCoordinates = { {0, 0}, {texScaleFactorX, 0}, {texScaleFactorX, 1}, @@ -166,7 +171,7 @@ Mesh generateBox(float width, float height, float depth, bool flipFaces) { }; - std::vector indices = { + vector 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 vertices; - std::vector normals; - std::vector indices; + vector vertices; + vector normals; + vector 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++) {