From d79b634e845176e606801e75148c305b050deb62 Mon Sep 17 00:00:00 2001 From: Krzysztof Rudnicki Date: Tue, 14 Mar 2023 01:56:29 +0100 Subject: [PATCH] chore: ran clang-formatter on root folder --- .vscode/extensions.json | 22 +- Engine/engine/.clang-format | 1 + Engine/engine/beforeRender.cpp | 114 ++++---- Engine/engine/beforeRender.hpp | 6 +- Engine/engine/constants.hpp | 328 +++++++++++----------- Engine/engine/draw.cpp | 489 +++++++++++++++------------------ Engine/engine/draw.hpp | 67 ++--- Engine/engine/match | Bin 713448 -> 713472 bytes Engine/engine/match.cpp | 19 +- Engine/engine/misc.cpp | 7 +- Engine/engine/renderLoop.cpp | 360 ++++++++++++------------ Engine/engine/renderLoop.hpp | 28 +- Engine/engine/shader.cpp | 169 ++++++------ Engine/engine/shader.hpp | 48 ++-- Engine/engine/shaders.cpp | 143 +++++----- Engine/engine/shaders.hpp | 13 +- Engine/engine/textures.cpp | 244 ++++++++-------- Engine/engine/textures.hpp | 39 ++- 18 files changed, 1005 insertions(+), 1092 deletions(-) create mode 100644 Engine/engine/.clang-format diff --git a/.vscode/extensions.json b/.vscode/extensions.json index eafb6d5..ec4c01f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,24 +1,20 @@ { "recommendations": [ "aaron-bond.better-comments", - "jbenden.c-cpp-flylint", + "ms-vscode.cpptools", "ms-vscode.cpptools-themes", - "twxs.cmake", - "ms-vscode.cmake-tools", - "stackbreak.comment-divider", - "donjayamanne.githistory", - "james-yu.latex-workshop", - "ms-vscode.makefile-tools", - "pkief.material-icon-theme", - "ms-vscode-remote.vscode-remote-extensionpack", - "henriiik.vscode-sort", - "hbenl.test-adapter-converter", - "streetsidesoftware.code-spell-checker", "wmaurer.change-case", + "xaver.clang-format", + "streetsidesoftware.code-spell-checker", + "vadimcn.vscode-lldb", "usernamehw.errorlens", + "donjayamanne.githistory", + "dtoplak.vscode-glsllint", "kisstkondoros.vscode-gutter-preview", + "james-yu.latex-workshop", "davidanson.vscode-markdownlint", + "pkief.material-icon-theme", "streetsidesoftware.code-spell-checker-polish", - "tyriar.sort-lines" + "slevesque.shader" ] } \ No newline at end of file diff --git a/Engine/engine/.clang-format b/Engine/engine/.clang-format new file mode 100644 index 0000000..2593ef5 --- /dev/null +++ b/Engine/engine/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: Google \ No newline at end of file diff --git a/Engine/engine/beforeRender.cpp b/Engine/engine/beforeRender.cpp index 7eef896..9fe1da2 100644 --- a/Engine/engine/beforeRender.cpp +++ b/Engine/engine/beforeRender.cpp @@ -1,101 +1,95 @@ // "Copyright [2023] " #ifndef BEFORE_RENDER_CPP #define BEFORE_RENDER_CPP -#include +#include "./beforeRender.hpp" + #include +#include #include -#include "./beforeRender.hpp" + #include "./constants.hpp" #include "./misc.hpp" void configureGLFW(const int GLFWMajorVersion, const int GLFWMinorVersion) { - // first argument tells us what option to configure - // second is to what we set the value of this option - // see: https://www.glfw.org/docs/latest/window.html#window_hints - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GLFWMajorVersion); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GLFWMinorVersion); - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + // first argument tells us what option to configure + // second is to what we set the value of this option + // see: https://www.glfw.org/docs/latest/window.html#window_hints + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GLFWMajorVersion); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GLFWMinorVersion); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // we set GLFW to 3.3 CORE // core profile gives us access to smaller subset of // OGL without backwards compatible features // if we are on Mac OS X we need this for our code to work #ifdef __APPLE__ - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); #endif } void instantiateGLFWwindow() { - // Initialize GLFW - glfwInit(); - configureGLFW(constants::GLFW_MAJOR_VERSION, constants::GLFW_MINOR_VERSION); + // Initialize GLFW + glfwInit(); + configureGLFW(constants::GLFW_MAJOR_VERSION, constants::GLFW_MINOR_VERSION); } GLFWwindow *createWindowObject() { - // First two arguments are width and height - // Third is the name of the window - // We ignore last two - GLFWwindow *window = glfwCreateWindow( - constants::MAIN_WINDOW_WIDTH, - constants::MAIN_WINDOW_HEIGHT, - constants::MAIN_WINDOW_NAME, - nullptr, - nullptr); - return window; + // First two arguments are width and height + // Third is the name of the window + // We ignore last two + GLFWwindow *window = glfwCreateWindow( + constants::MAIN_WINDOW_WIDTH, constants::MAIN_WINDOW_HEIGHT, + constants::MAIN_WINDOW_NAME, nullptr, nullptr); + return window; } int initializeGLAD() { - // we load address of OGL OS-specific function pointers - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { - print("Failed to initialize GLAD"); - return -1; - } - return 0; + // we load address of OGL OS-specific function pointers + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { + print("Failed to initialize GLAD"); + return -1; + } + return 0; } // resizes viewport when user resizes window -void framebuffer_size_callback(GLFWwindow *window, - const int width, - const int height) { - glViewport(0, 0, width, height); +void framebuffer_size_callback(GLFWwindow *window, const int width, + const int height) { + glViewport(0, 0, width, height); } void viewPort(GLFWwindow *window) { - // We tell OGL size of rendering window - // First two define left corner of window - // 3th and 4th width and height of rendering window - // we could set them to be smaller than window dimension, - // ogl rendering will be then displayed in smaller window - glViewport(0, - 0, - constants::MAIN_WINDOW_WIDTH, - constants::MAIN_WINDOW_HEIGHT); - // processed coordinates are between -1 and 1 so here we map: - // (-1 to 1) to (0, constants::MAIN_WINDOW_WIDTH) - // and (0, constants::MAIN_WINDOW_HEIGHT) - glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); - // we call framebuffer_size_callback on every window resize + // We tell OGL size of rendering window + // First two define left corner of window + // 3th and 4th width and height of rendering window + // we could set them to be smaller than window dimension, + // ogl rendering will be then displayed in smaller window + glViewport(0, 0, constants::MAIN_WINDOW_WIDTH, constants::MAIN_WINDOW_HEIGHT); + // processed coordinates are between -1 and 1 so here we map: + // (-1 to 1) to (0, constants::MAIN_WINDOW_WIDTH) + // and (0, constants::MAIN_WINDOW_HEIGHT) + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + // we call framebuffer_size_callback on every window resize } GLFWwindow *prepareForRender() { - instantiateGLFWwindow(); + instantiateGLFWwindow(); - GLFWwindow *window = createWindowObject(); - // function returns GLFWWindow object + GLFWwindow *window = createWindowObject(); + // function returns GLFWWindow object - if (window == nullptr) { - print("Failed to create GLFW window"); - glfwTerminate(); - return window; - } - // we make context of this window main context of current thread - glfwMakeContextCurrent(window); - - if (initializeGLAD() == -1) - return nullptr; - viewPort(window); + if (window == nullptr) { + print("Failed to create GLFW window"); + glfwTerminate(); return window; + } + // we make context of this window main context of current thread + glfwMakeContextCurrent(window); + + if (initializeGLAD() == -1) return nullptr; + viewPort(window); + return window; } #endif diff --git a/Engine/engine/beforeRender.hpp b/Engine/engine/beforeRender.hpp index 0863d12..9ac85fb 100644 --- a/Engine/engine/beforeRender.hpp +++ b/Engine/engine/beforeRender.hpp @@ -7,10 +7,8 @@ void configureGLFW(const int GLFWMajorVersion, const int GLFWMinorVersion); void instantiateGLFWwindow(); GLFWwindow *createWindowObject(); int initializeGLAD(); -void framebuffer_size_callback( - GLFWwindow *window, - const int width, - const int height); +void framebuffer_size_callback(GLFWwindow *window, const int width, + const int height); void viewPort(GLFWwindow *window); GLFWwindow *prepareForRender(); diff --git a/Engine/engine/constants.hpp b/Engine/engine/constants.hpp index 23ee1b1..ad5e39d 100644 --- a/Engine/engine/constants.hpp +++ b/Engine/engine/constants.hpp @@ -2,217 +2,195 @@ #ifndef ENGINE_ENGINE_CONSTANTS_HPP_ #define ENGINE_ENGINE_CONSTANTS_HPP_ #include + #include #include - namespace constants { - inline constexpr int GLFW_MAJOR_VERSION { 3 }; - inline constexpr int GLFW_MINOR_VERSION { 3 }; - // best practice is to use inline constexpr - // std::string_view but glfwCreateWindow takes only char* as input - inline const char* MAIN_WINDOW_NAME { "Match" }; - inline constexpr int MAIN_WINDOW_WIDTH { 800 }; - inline constexpr int MAIN_WINDOW_HEIGHT { 600 }; - inline constexpr struct { - GLfloat red = 1.0f; - GLfloat green = 0.0f; - GLfloat blue = 0.0f; - GLfloat alpha = 1.0f; - } RED; +inline constexpr int GLFW_MAJOR_VERSION{3}; +inline constexpr int GLFW_MINOR_VERSION{3}; +// best practice is to use inline constexpr +// std::string_view but glfwCreateWindow takes only char* as input +inline const char* MAIN_WINDOW_NAME{"Match"}; +inline constexpr int MAIN_WINDOW_WIDTH{800}; +inline constexpr int MAIN_WINDOW_HEIGHT{600}; +inline constexpr struct { + GLfloat red = 1.0f; + GLfloat green = 0.0f; + GLfloat blue = 0.0f; + GLfloat alpha = 1.0f; +} RED; - inline constexpr struct { - GLfloat red = 1.0f; - GLfloat green = 1.0f; - GLfloat blue = 1.0f; - GLfloat alpha = 1.0f; - } WHITE; +inline constexpr struct { + GLfloat red = 1.0f; + GLfloat green = 1.0f; + GLfloat blue = 1.0f; + GLfloat alpha = 1.0f; +} WHITE; - inline constexpr struct { - GLfloat red = 0.2f; - GLfloat green = 0.3f; - GLfloat blue = 0.3f; - GLfloat alpha = 1.0f; - } LEARN_OPEN_GL_COLOR; +inline constexpr struct { + GLfloat red = 0.2f; + GLfloat green = 0.3f; + GLfloat blue = 0.3f; + GLfloat alpha = 1.0f; +} LEARN_OPEN_GL_COLOR; - inline const char* VERTEX_SHADER_COLOR_FILENAME { - "./Shaders/vertexShaderColor.vs" - }; +inline const char* VERTEX_SHADER_COLOR_FILENAME{ + "./Shaders/vertexShaderColor.vs"}; - inline const char* VERTEX_SHADER_UPSIDE_DOWN_FILENAME { - "./Shaders/vertexShaderUpsideDown.vs" - }; +inline const char* VERTEX_SHADER_UPSIDE_DOWN_FILENAME{ + "./Shaders/vertexShaderUpsideDown.vs"}; - inline const char* VERTEX_SHADER_OFFSET_FILENAME { - "./Shaders/vertexShaderOffset.vs" - }; - // we write vertex shader - // version of glsl (since ogl 3.3 same as ogl so we pick 330) - // in this shader we just forward input data to shader output - // DO NOT REMOVE UNTIL CAN DRAW SQUARE USING CLASS - inline const char *VERTEX_SHADER_SOURCE { "#version 330 core\n" +inline const char* VERTEX_SHADER_OFFSET_FILENAME{ + "./Shaders/vertexShaderOffset.vs"}; +// we write vertex shader +// version of glsl (since ogl 3.3 same as ogl so we pick 330) +// in this shader we just forward input data to shader output +// DO NOT REMOVE UNTIL CAN DRAW SQUARE USING CLASS +inline const char* VERTEX_SHADER_SOURCE{ + "#version 330 core\n" "layout (location = 0) in vec3 aPos;\n" "void main()\n" "{\n" " gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n" - "}\0" }; - inline const char* VERTEX_SHADER_SOURCE_FILENAME { - "./Shaders/vertexShaderSource.vs" - }; + "}\0"}; +inline const char* VERTEX_SHADER_SOURCE_FILENAME{ + "./Shaders/vertexShaderSource.vs"}; - inline const char* VERTEX_SHADER_VERTICE_COLOR_FILENAME { - "./Shaders/vertexShaderVerticeColor.vs" - }; +inline const char* VERTEX_SHADER_VERTICE_COLOR_FILENAME{ + "./Shaders/vertexShaderVerticeColor.vs"}; - inline const char* VERTEX_SHADER_TASK_THREE_FILENAME { - "./Shaders/vertexShaderTaskThree.vs" - }; +inline const char* VERTEX_SHADER_TASK_THREE_FILENAME{ + "./Shaders/vertexShaderTaskThree.vs"}; - inline const char* VERTEX_SHADER_TEXTURE_FILENAME { - "./Shaders/vertexShaderTexture.vs" - }; +inline const char* VERTEX_SHADER_TEXTURE_FILENAME{ + "./Shaders/vertexShaderTexture.vs"}; - inline const char* FRAGMENT_SHADER_TASK_THREE_FILENAME { - "./Shaders/fragmentShaderTaskThree.fs" - }; +inline const char* FRAGMENT_SHADER_TASK_THREE_FILENAME{ + "./Shaders/fragmentShaderTaskThree.fs"}; - inline const char* FRAGMENT_SHADER_COLOR_FROM_VERTEX_FILENAME { - "./Shaders/fragmentShaderColorFromVertex.fs" - }; +inline const char* FRAGMENT_SHADER_COLOR_FROM_VERTEX_FILENAME{ + "./Shaders/fragmentShaderColorFromVertex.fs"}; - inline const char* FRAGMENT_SHADER_UNIFORMS_FILENAME { - "./Shaders/fragmentShaderUniforms.fs" - }; +inline const char* FRAGMENT_SHADER_UNIFORMS_FILENAME{ + "./Shaders/fragmentShaderUniforms.fs"}; - // write fragment shader - // we set the color of each pixel to be orange - // DO NOT REMOVE UNTIL CAN DRAW SQUARE USING CLASS - inline const char *FRAGMENT_SHADER_SOURCE { - "#version 330 core\n" - "out vec4 FragColor;\n" - "void main()\n" - "{\n" - "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" - "}\0" - }; +// write fragment shader +// we set the color of each pixel to be orange +// DO NOT REMOVE UNTIL CAN DRAW SQUARE USING CLASS +inline const char* FRAGMENT_SHADER_SOURCE{ + "#version 330 core\n" + "out vec4 FragColor;\n" + "void main()\n" + "{\n" + "FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" + "}\0"}; - inline const char* FRAGMENT_SHADER_SOURCE_FILENAME { - "./Shaders/fragmentShaderSource.fs" - }; - inline const char* FRAGMENT_SHADER_SOURCE_YELLOW_FILENAME { - "./Shaders/fragmentShaderSourceYellow.fs" - }; +inline const char* FRAGMENT_SHADER_SOURCE_FILENAME{ + "./Shaders/fragmentShaderSource.fs"}; +inline const char* FRAGMENT_SHADER_SOURCE_YELLOW_FILENAME{ + "./Shaders/fragmentShaderSourceYellow.fs"}; - inline const char* FRAGMENT_SHADER_TEXTURE_FILENAME { - "./Shaders/fragmentShaderTexture.fs" - }; +inline const char* FRAGMENT_SHADER_TEXTURE_FILENAME{ + "./Shaders/fragmentShaderTexture.fs"}; - // we specify three vertices - // each of them with position in 3d space - // x y z - inline constexpr float TRIANGLE_VERTICES[] { - -0.5f, -0.5f, 0.0f, - 0.5f, -0.5f, 0.0f, - 0.0f, 0.5f, 0.0f - }; +// we specify three vertices +// each of them with position in 3d space +// x y z +inline constexpr float TRIANGLE_VERTICES[]{-0.5f, -0.5f, 0.0f, 0.5f, -0.5f, + 0.0f, 0.0f, 0.5f, 0.0f}; - inline constexpr size_t TRIANGLE_VERTICES_SIZE = { - sizeof(TRIANGLE_VERTICES) - }; +inline constexpr size_t TRIANGLE_VERTICES_SIZE = {sizeof(TRIANGLE_VERTICES)}; - inline constexpr float TRIANGLES_VERTICES[] { - // first triangle - -0.9f, -0.5f, 0.0f, // left - -0.0f, -0.5f, 0.0f, // right - -0.45f, 0.5f, 0.0f, // top - // second triangle - 0.0f, -0.5f, 0.0f, // left - 0.9f, -0.5f, 0.0f, // right - 0.45f, 0.5f, 0.0f // top - }; - - inline constexpr size_t TRIANGLES_VERTICES_SIZE = { - sizeof(TRIANGLES_VERTICES) }; - - inline constexpr float TRIANGLE_ONE[] { - // first triangle - -0.9f, -0.5f, 0.0f, // left - -0.0f, -0.5f, 0.0f, // right - -0.45f, 0.5f, 0.0f, // top - }; - - inline constexpr size_t TRIANGLE_ONE_SIZE = { sizeof(TRIANGLE_ONE) }; - - inline constexpr float TRIANGLE_TWO[] { - // second triangle - 0.0f, -0.5f, 0.0f, // left - 0.9f, -0.5f, 0.0f, // right - 0.45f, 0.5f, 0.0f // top - }; - - inline constexpr size_t TRIANGLE_TWO_SIZE = { sizeof(TRIANGLE_TWO) }; - - inline constexpr float TRIANGLE_COLORS[] { - // positions // colors - 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right - -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left - 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top - }; - - inline constexpr size_t TRIANGLE_COLORS_SIZE = { sizeof(TRIANGLE_COLORS) }; - - // compare with square done with only vertices: - /* - float vertices[] = { +inline constexpr float TRIANGLES_VERTICES[]{ // first triangle - 0.5f, 0.5f, 0.0f, // top right - 0.5f, -0.5f, 0.0f, // bottom right - -0.5f, 0.5f, 0.0f, // top left + -0.9f, -0.5f, 0.0f, // left + -0.0f, -0.5f, 0.0f, // right + -0.45f, 0.5f, 0.0f, // top + // second triangle + 0.0f, -0.5f, 0.0f, // left + 0.9f, -0.5f, 0.0f, // right + 0.45f, 0.5f, 0.0f // top +}; + +inline constexpr size_t TRIANGLES_VERTICES_SIZE = {sizeof(TRIANGLES_VERTICES)}; + +inline constexpr float TRIANGLE_ONE[]{ + // first triangle + -0.9f, -0.5f, 0.0f, // left + -0.0f, -0.5f, 0.0f, // right + -0.45f, 0.5f, 0.0f, // top +}; + +inline constexpr size_t TRIANGLE_ONE_SIZE = {sizeof(TRIANGLE_ONE)}; + +inline constexpr float TRIANGLE_TWO[]{ // second triangle - 0.5f, -0.5f, 0.0f, // bottom right - -0.5f, -0.5f, 0.0f, // bottom left - -0.5f, 0.5f, 0.0f // top left - }; - bottom right and top left is specified twice ! - */ - inline constexpr float SQUARE_VERTICES[] { - 0.5f, 0.5f, 0.0f, // top right - 0.5f, -0.5f, 0.0f, // bottom right - -0.25f, -0.5f, 0.0f, // bottom left - -0.25f, 0.5f, 0.0f // top left - }; + 0.0f, -0.5f, 0.0f, // left + 0.9f, -0.5f, 0.0f, // right + 0.45f, 0.5f, 0.0f // top +}; - inline constexpr unsigned int SQUARE_INDICES[] { - 0, 1, 3, // first triangle - 1, 2, 3, // second triangle - }; +inline constexpr size_t TRIANGLE_TWO_SIZE = {sizeof(TRIANGLE_TWO)}; - inline constexpr size_t SQUARE_INDICES_SIZE = { sizeof(SQUARE_INDICES) }; +inline constexpr float TRIANGLE_COLORS[]{ + // positions // colors + 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // bottom left + 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f // top +}; - inline constexpr size_t SQUARE_VERTICES_SIZE = { sizeof(SQUARE_VERTICES) }; +inline constexpr size_t TRIANGLE_COLORS_SIZE = {sizeof(TRIANGLE_COLORS)}; - inline constexpr int MAX_DRAW_CALL = { 11 }; +// compare with square done with only vertices: +/* +float vertices[] = { +// first triangle + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right +-0.5f, 0.5f, 0.0f, // top left +// second triangle + 0.5f, -0.5f, 0.0f, // bottom right +-0.5f, -0.5f, 0.0f, // bottom left +-0.5f, 0.5f, 0.0f // top left +}; +bottom right and top left is specified twice ! +*/ +inline constexpr float SQUARE_VERTICES[]{ + 0.5f, 0.5f, 0.0f, // top right + 0.5f, -0.5f, 0.0f, // bottom right + -0.25f, -0.5f, 0.0f, // bottom left + -0.25f, 0.5f, 0.0f // top left +}; - // https://learnopengl.com/img/getting-started/tex_coords.png - inline constexpr float TEXTURE_VERTICES[] { - // positions // colors // texture coords - 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right - 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right - -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left - -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left - }; +inline constexpr unsigned int SQUARE_INDICES[]{ + 0, 1, 3, // first triangle + 1, 2, 3, // second triangle +}; - inline constexpr size_t TEXTURE_VERTICES_SIZE = { - sizeof(TEXTURE_VERTICES) - }; +inline constexpr size_t SQUARE_INDICES_SIZE = {sizeof(SQUARE_INDICES)}; +inline constexpr size_t SQUARE_VERTICES_SIZE = {sizeof(SQUARE_VERTICES)}; - inline constexpr unsigned int TEXTURE_INDICES[] { - 0, 1, 3, // first triangle - 1, 2, 3 // second triangle - }; +inline constexpr int MAX_DRAW_CALL = {11}; - inline constexpr size_t TEXTURE_INDICES_SIZE = { sizeof(TEXTURE_INDICES) }; +// https://learnopengl.com/img/getting-started/tex_coords.png +inline constexpr float TEXTURE_VERTICES[]{ + // positions // colors // texture coords + 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right + 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right + -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left + -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left +}; + +inline constexpr size_t TEXTURE_VERTICES_SIZE = {sizeof(TEXTURE_VERTICES)}; + +inline constexpr unsigned int TEXTURE_INDICES[]{ + 0, 1, 3, // first triangle + 1, 2, 3 // second triangle +}; + +inline constexpr size_t TEXTURE_INDICES_SIZE = {sizeof(TEXTURE_INDICES)}; } // namespace constants #endif // ENGINE_ENGINE_CONSTANTS_HPP_ diff --git a/Engine/engine/draw.cpp b/Engine/engine/draw.cpp index 7990d51..db7346d 100644 --- a/Engine/engine/draw.cpp +++ b/Engine/engine/draw.cpp @@ -1,332 +1,281 @@ // "Copyright [2023] " #ifndef DRAW_CPP #define DRAW_CPP -#include +#include "./draw.hpp" + #include -#include +#include + #include #include +#include #include -#include "./draw.hpp" -#include "./renderLoop.hpp" -#include "./shaders.hpp" + #include "./constants.hpp" #include "./misc.hpp" +#include "./renderLoop.hpp" #include "./shader.hpp" -#include "./textures.hpp" +#include "./shaders.hpp" #include "./stb_image.h" +#include "./textures.hpp" drawFigureReturn drawFigure(const int whatToDraw) { - switch (whatToDraw) { + switch (whatToDraw) { case 0: - return drawTriangleClass( - constants::TRIANGLE_VERTICES, - constants::TRIANGLE_VERTICES_SIZE, - constants::VERTEX_SHADER_SOURCE_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME); + return drawTriangleClass(constants::VERTEX_SHADER_SOURCE_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLE_VERTICES, + constants::TRIANGLE_VERTICES_SIZE); case 1: - return drawSquare( - constants::SQUARE_VERTICES, - constants::SQUARE_VERTICES_SIZE, - constants::SQUARE_INDICES, constants::SQUARE_INDICES_SIZE, - constants::VERTEX_SHADER_SOURCE, - constants::FRAGMENT_SHADER_SOURCE); + return drawSquare( + constants::SQUARE_VERTICES, constants::SQUARE_VERTICES_SIZE, + constants::SQUARE_INDICES, constants::SQUARE_INDICES_SIZE, + constants::VERTEX_SHADER_SOURCE, constants::FRAGMENT_SHADER_SOURCE); case 2: - // Try to draw 2 triangles next to each other - // using glDrawArrays by adding more vertices to your data. - return drawTriangleClass( - constants::TRIANGLES_VERTICES, - constants::TRIANGLES_VERTICES_SIZE, - constants::VERTEX_SHADER_SOURCE_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME); + // Try to draw 2 triangles next to each other + // using glDrawArrays by adding more vertices to your data. + return drawTriangleClass(constants::VERTEX_SHADER_SOURCE_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLES_VERTICES, + constants::TRIANGLES_VERTICES_SIZE); case 3: - drawTriangleClass(constants::TRIANGLE_ONE, - constants::TRIANGLE_ONE_SIZE, - constants::VERTEX_SHADER_SOURCE_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME); - // Now create the same 2 triangles - // using two different VAOs and VBOs for their data - return drawTriangleClass( - constants::TRIANGLE_TWO, - constants::TRIANGLE_TWO_SIZE, - constants::VERTEX_SHADER_SOURCE_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME); + drawTriangleClass(constants::VERTEX_SHADER_SOURCE_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLE_ONE, constants::TRIANGLE_ONE_SIZE); + // Now create the same 2 triangles + // using two different VAOs and VBOs for their data + return drawTriangleClass(constants::VERTEX_SHADER_SOURCE_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLE_TWO, + constants::TRIANGLE_TWO_SIZE); case 4: - drawTriangleClass(constants::TRIANGLE_ONE, - constants::TRIANGLE_ONE_SIZE, + drawTriangleClass(constants::VERTEX_SHADER_SOURCE_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLE_ONE, constants::TRIANGLE_ONE_SIZE); + /* + Create two shader programs where + the second program uses a different + fragment shader that outputs the color yellow; + draw both triangles again where one outputs the color yellow + */ + return drawTriangleClass( constants::VERTEX_SHADER_SOURCE_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME); - /* - Create two shader programs where - the second program uses a different - fragment shader that outputs the color yellow; - draw both triangles again where one outputs the color yellow - */ - return drawTriangleClass( - constants::TRIANGLE_TWO, - constants::TRIANGLE_TWO_SIZE, - constants::VERTEX_SHADER_SOURCE_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_YELLOW_FILENAME); + constants::FRAGMENT_SHADER_SOURCE_YELLOW_FILENAME, + constants::TRIANGLE_TWO, constants::TRIANGLE_TWO_SIZE); case 5: - // Get color from vertex shader to fragment shader - return drawTriangleClass(constants::TRIANGLE_VERTICES, - constants::TRIANGLE_VERTICES_SIZE, + // Get color from vertex shader to fragment shader + return drawTriangleClass( constants::VERTEX_SHADER_COLOR_FILENAME, - constants::FRAGMENT_SHADER_COLOR_FROM_VERTEX_FILENAME); + constants::FRAGMENT_SHADER_COLOR_FROM_VERTEX_FILENAME, + constants::TRIANGLE_VERTICES, constants::TRIANGLE_VERTICES_SIZE); case 6: - // set color from opengl code to uniform value in fragment shader - return drawTriangleClass(constants::TRIANGLE_VERTICES, - constants::TRIANGLE_VERTICES_SIZE, - constants::VERTEX_SHADER_COLOR_FILENAME, - constants::FRAGMENT_SHADER_UNIFORMS_FILENAME); + // set color from opengl code to uniform value in fragment shader + return drawTriangleClass(constants::VERTEX_SHADER_COLOR_FILENAME, + constants::FRAGMENT_SHADER_UNIFORMS_FILENAME, + constants::TRIANGLE_VERTICES, + constants::TRIANGLE_VERTICES_SIZE); case 7: - // set color from opengl code to uniform value in fragment shader - return drawTriangleClass(constants::TRIANGLE_COLORS, - constants::TRIANGLE_COLORS_SIZE, - constants::VERTEX_SHADER_VERTICE_COLOR_FILENAME, - constants::FRAGMENT_SHADER_COLOR_FROM_VERTEX_FILENAME, - true); + // set color from opengl code to uniform value in fragment shader + return drawTriangleClass( + constants::VERTEX_SHADER_VERTICE_COLOR_FILENAME, + constants::FRAGMENT_SHADER_COLOR_FROM_VERTEX_FILENAME, + constants::TRIANGLE_COLORS, constants::TRIANGLE_COLORS_SIZE, true); case 8: - // upside down triangle - return drawTriangleClass(constants::TRIANGLE_VERTICES, - constants::TRIANGLE_VERTICES_SIZE, - constants::VERTEX_SHADER_UPSIDE_DOWN_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME); - case 9: - { - // offset triangle - offsetsStruct offsets = offsetsStruct(); - offsets.xOffset = 0.5f; - return drawTriangleClass(constants::TRIANGLE_VERTICES, - constants::TRIANGLE_VERTICES_SIZE, - constants::VERTEX_SHADER_OFFSET_FILENAME, - constants::FRAGMENT_SHADER_SOURCE_FILENAME, - false, - false, - offsets); + // upside down triangle + return drawTriangleClass(constants::VERTEX_SHADER_UPSIDE_DOWN_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLE_VERTICES, + constants::TRIANGLE_VERTICES_SIZE); + case 9: { + // offset triangle + offsetsStruct offsets = offsetsStruct(); + offsets.xOffset = 0.5f; + return drawTriangleClass(constants::VERTEX_SHADER_OFFSET_FILENAME, + constants::FRAGMENT_SHADER_SOURCE_FILENAME, + constants::TRIANGLE_VERTICES, + constants::TRIANGLE_VERTICES_SIZE, false, false, + offsets); } case 10: - return drawTriangleClass(constants::TRIANGLE_VERTICES, - constants::TRIANGLE_VERTICES_SIZE, - constants::VERTEX_SHADER_TASK_THREE_FILENAME, - constants::FRAGMENT_SHADER_TASK_THREE_FILENAME); + return drawTriangleClass(constants::VERTEX_SHADER_TASK_THREE_FILENAME, + constants::FRAGMENT_SHADER_TASK_THREE_FILENAME, + constants::TRIANGLE_VERTICES, + constants::TRIANGLE_VERTICES_SIZE); case constants::MAX_DRAW_CALL: - return drawDebilMode( - constants::VERTEX_SHADER_TEXTURE_FILENAME, - constants::FRAGMENT_SHADER_TEXTURE_FILENAME, - constants::TEXTURE_VERTICES, - constants::TEXTURE_VERTICES_SIZE, - constants::TEXTURE_INDICES, - constants::TEXTURE_INDICES_SIZE); + return drawDebilMode( + constants::VERTEX_SHADER_TEXTURE_FILENAME, + constants::FRAGMENT_SHADER_TEXTURE_FILENAME, + constants::TEXTURE_VERTICES, constants::TEXTURE_VERTICES_SIZE, + constants::TEXTURE_INDICES, constants::TEXTURE_INDICES_SIZE); default: - throw "No function for this draw call"; - } + throw "No function for this draw call"; + } } -unsigned int getShaderProgram( - const char* vertexShaderSource, - const char* fragmentShaderSource) { - const std::pair shaders = - compileShaders(vertexShaderSource, fragmentShaderSource); - if (shaders.first == 0 || shaders.second == 0) - return 0; +unsigned int getShaderProgram(const char* vertexShaderSource, + const char* fragmentShaderSource) { + const std::pair shaders = + compileShaders(vertexShaderSource, fragmentShaderSource); + if (shaders.first == 0 || shaders.second == 0) return 0; - const unsigned int shaderProgram = - linkShaderObjectsShaderProgram(shaders.first, shaders.second); + const unsigned int shaderProgram = + linkShaderObjectsShaderProgram(shaders.first, shaders.second); - if (shaderProgram == 0) - return 0; - return shaderProgram; + if (shaderProgram == 0) return 0; + return shaderProgram; } void setOffsets(Shader shader, const offsetsStruct offsets) { - shader.setFloat("xOffset", offsets.xOffset); - shader.setFloat("yOffset", offsets.yOffset); - shader.setFloat("zOffset", offsets.zOffset); + shader.setFloat("xOffset", offsets.xOffset); + shader.setFloat("yOffset", offsets.yOffset); + shader.setFloat("zOffset", offsets.zOffset); } -drawFigureReturn drawTriangleClass( - const float triangleVertices[], - const size_t triangleVerticesSize, - const char* vertexPath, - const char* fragmentPath, - const bool colorIncluded, - const bool textureIncluded, - const offsetsStruct offsets) { - // In your CPP file: - // ====================== - // float offset = 0.5f; +drawFigureReturn drawTriangleClass(const char* vertexPath, + const char* fragmentPath, + const float triangleVertices[], + const size_t triangleVerticesSize, + const bool colorIncluded, + const bool textureIncluded, + const offsetsStruct offsets) { + // In your CPP file: + // ====================== + // float offset = 0.5f; - Shader ourShader(vertexPath, fragmentPath); - ourShader.use(); - setOffsets(ourShader, offsets); + Shader ourShader(vertexPath, fragmentPath); + ourShader.use(); + setOffsets(ourShader, offsets); - const unsigned int vertexBufferObject = - copyVerticesMemory( - triangleVertices, - triangleVerticesSize, - GL_ARRAY_BUFFER); - const unsigned int vertexArrayObject = generateBindVAO(); - copyVerticesArray( - vertexBufferObject, - triangleVertices, - triangleVerticesSize, - GL_ARRAY_BUFFER); - // set vertex attribute pointers - configureVertexAttribute(colorIncluded, textureIncluded); - doDrawArrays( - ourShader.ID, - vertexArrayObject, - GL_TRIANGLES, - 0, - triangleVerticesSize); - drawFigureReturn newReturn; - newReturn.success = 0; - newReturn.VAO = vertexArrayObject; - newReturn.VBO = vertexBufferObject; - newReturn.EBO = 0; - return newReturn; + const unsigned int vertexBufferObject = copyVerticesMemory( + triangleVertices, triangleVerticesSize, GL_ARRAY_BUFFER); + const unsigned int vertexArrayObject = generateBindVAO(); + copyVerticesArray(vertexBufferObject, triangleVertices, triangleVerticesSize, + GL_ARRAY_BUFFER); + // set vertex attribute pointers + configureVertexAttribute(colorIncluded, textureIncluded); + doDrawArrays(ourShader.ID, vertexArrayObject, GL_TRIANGLES, 0, + triangleVerticesSize); + drawFigureReturn newReturn; + newReturn.success = 0; + newReturn.VAO = vertexArrayObject; + newReturn.VBO = vertexBufferObject; + newReturn.EBO = 0; + return newReturn; } drawFigureReturn drawSquare( - const float squareVertices[], - const size_t squareVerticesSize, - const unsigned int squareIndices[], - const size_t squareIndicesSize, - const char* vertexShaderSource, - const char* fragmentShaderSource, - const bool colorIncluded, - const bool textureIncluded, + const float squareVertices[], const size_t squareVerticesSize, + const unsigned int squareIndices[], const size_t squareIndicesSize, + const char* vertexShaderSource, const char* fragmentShaderSource, + const bool colorIncluded, const bool textureIncluded, const offsetsStruct offsets) { - const unsigned int shaderProgram = - getShaderProgram(vertexShaderSource, fragmentShaderSource); - drawFigureReturn newReturn = drawFigureReturn(); - if (shaderProgram == 0) { - newReturn.success = -1; - return newReturn; - } - const unsigned int VAO = generateBindVAO(); - copyVerticesMemory(squareVertices, squareVerticesSize, GL_ARRAY_BUFFER); - copyVerticesMemory( - squareIndices, - squareIndicesSize, - GL_ELEMENT_ARRAY_BUFFER); - - // set vertex attribute pointers - configureVertexAttribute(colorIncluded, textureIncluded); - - doDrawElements( - shaderProgram, - VAO, - GL_TRIANGLES, - GL_UNSIGNED_INT, - squareIndicesSize); - newReturn.success = 0; - newReturn.VAO = VAO; + const unsigned int shaderProgram = + getShaderProgram(vertexShaderSource, fragmentShaderSource); + drawFigureReturn newReturn = drawFigureReturn(); + if (shaderProgram == 0) { + newReturn.success = -1; return newReturn; + } + const unsigned int VAO = generateBindVAO(); + copyVerticesMemory(squareVertices, squareVerticesSize, GL_ARRAY_BUFFER); + copyVerticesMemory(squareIndices, squareIndicesSize, GL_ELEMENT_ARRAY_BUFFER); + + // set vertex attribute pointers + configureVertexAttribute(colorIncluded, textureIncluded); + + doDrawElements(shaderProgram, VAO, GL_TRIANGLES, GL_UNSIGNED_INT, + squareIndicesSize); + newReturn.success = 0; + newReturn.VAO = VAO; + return newReturn; } -drawFigureReturn drawDebilMode( - const char* vertexPath, - const char* fragmentPath, - const float vertices[], - const size_t verticesSize, - const unsigned int indices[], - const size_t indicesSize) { - // https://stackoverflow.com/questions/33883609/opengl-linker-error-linking-with-uncompiled-shader - Shader ourShader( - constants::VERTEX_SHADER_TEXTURE_FILENAME, - constants::FRAGMENT_SHADER_TEXTURE_FILENAME); +drawFigureReturn drawDebilMode(const char* vertexPath, const char* fragmentPath, + const float vertices[], + const size_t verticesSize, + const unsigned int indices[], + const size_t indicesSize) { + // https://stackoverflow.com/questions/33883609/opengl-linker-error-linking-with-uncompiled-shader + Shader ourShader(constants::VERTEX_SHADER_TEXTURE_FILENAME, + constants::FRAGMENT_SHADER_TEXTURE_FILENAME); - unsigned int VAO = generateBindVAO(); - unsigned int VBO = copyVerticesMemory( - vertices, - verticesSize, - GL_ARRAY_BUFFER); - unsigned int EBO = copyVerticesMemory(indices, - indicesSize, - GL_ELEMENT_ARRAY_BUFFER); + unsigned int VAO = generateBindVAO(); + unsigned int VBO = + copyVerticesMemory(vertices, verticesSize, GL_ARRAY_BUFFER); + unsigned int EBO = + copyVerticesMemory(indices, indicesSize, GL_ELEMENT_ARRAY_BUFFER); - configureVertexAttribute(true, true); + configureVertexAttribute(true, true); - unsigned int texture1 = loadAndCreateTexture("assets/container.png", true); - unsigned int texture2 = loadAndCreateTexture( - "assets/awesomeface.png", - false); + unsigned int texture1 = loadAndCreateTexture("assets/container.png", true); + unsigned int texture2 = loadAndCreateTexture("assets/awesomeface.png", false); - ourShader.use(); // don't forget to activate/use - // the shader before setting uniforms! - // set it via the texture class - ourShader.setInt("texture1", 0); - ourShader.setInt("texture2", 1); + ourShader.use(); // don't forget to activate/use + // the shader before setting uniforms! + // set it via the texture class + ourShader.setInt("texture1", 0); + ourShader.setInt("texture2", 1); + activateAndBindTextures(texture1, GL_TEXTURE0); + activateAndBindTextures(texture2, GL_TEXTURE1); - activateAndBindTextures(texture1, GL_TEXTURE0); - activateAndBindTextures(texture2, GL_TEXTURE1); + glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); - - drawFigureReturn newReturn; - newReturn.success = 0; - newReturn.VAO = VAO; - newReturn.VBO = VBO; - newReturn.EBO = EBO; - return newReturn; + drawFigureReturn newReturn; + newReturn.success = 0; + newReturn.VAO = VAO; + newReturn.VBO = VBO; + newReturn.EBO = EBO; + return newReturn; } -void doDrawElements( - const unsigned int shaderProgram, - const unsigned int vertexArrayObject, - const GLenum drawArrayMode, - const GLenum drawType, - const int numberOfElementsToDraw) { - glUseProgram(shaderProgram); - glBindVertexArray(vertexArrayObject); - glDrawElements(drawArrayMode, numberOfElementsToDraw, drawType, 0); - glBindVertexArray(0); +void doDrawElements(const unsigned int shaderProgram, + const unsigned int vertexArrayObject, + const GLenum drawArrayMode, const GLenum drawType, + const int numberOfElementsToDraw) { + glUseProgram(shaderProgram); + glBindVertexArray(vertexArrayObject); + glDrawElements(drawArrayMode, numberOfElementsToDraw, drawType, 0); + glBindVertexArray(0); } -void updateUniformColor( - const unsigned int shaderProgram, - const GLchar* uniformName) { - // update the uniform color - const float timeValue = glfwGetTime(); // retrieve running time - const float greenValue = - sin(timeValue) / 2.0f + 0.5f; - // vary the color from 0.0 to 1.0 using sin - // query the location of our uniform - const int vertexColorLocation = - glGetUniformLocation(shaderProgram, uniformName); - // if glGetUniformLocation returns -1 it could not find the location - if (vertexColorLocation != -1) { - glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); - /* we set the uniform value using glUniform4f - 4f means that it expects 4 floats - few of possible postfixes: - f: the function expects a float as its value. - i: the function expects an int as its value. - ui: the function expects an unsigned int as its value. - 3f: the function expects 3 floats as its value. - fv: the function expects a float vector/array as its value. - */ - } +void updateUniformColor(const unsigned int shaderProgram, + const GLchar* uniformName) { + // update the uniform color + const float timeValue = glfwGetTime(); // retrieve running time + const float greenValue = sin(timeValue) / 2.0f + 0.5f; + // vary the color from 0.0 to 1.0 using sin + // query the location of our uniform + const int vertexColorLocation = + glGetUniformLocation(shaderProgram, uniformName); + // if glGetUniformLocation returns -1 it could not find the location + if (vertexColorLocation != -1) { + glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f); + /* we set the uniform value using glUniform4f + 4f means that it expects 4 floats + few of possible postfixes: + f: the function expects a float as its value. + i: the function expects an int as its value. + ui: the function expects an unsigned int as its value. + 3f: the function expects 3 floats as its value. + fv: the function expects a float vector/array as its value. + */ + } } -void doDrawArrays( - const unsigned int shaderProgram, - const unsigned int vertexArrayObject, - const GLenum drawArrayMode, - const int firstIndex, - const unsigned int numberOfIndicesToBeRendered) { - // use shader program to render an object - glUseProgram(shaderProgram); - updateUniformColor(shaderProgram, "ourColor"); - glBindVertexArray(vertexArrayObject); - // From left: - // primitive type we want to draw - // starting index of vertex array - // how many vertices we want to draw - glDrawArrays(drawArrayMode, firstIndex, numberOfIndicesToBeRendered); +void doDrawArrays(const unsigned int shaderProgram, + const unsigned int vertexArrayObject, + const GLenum drawArrayMode, const int firstIndex, + const unsigned int numberOfIndicesToBeRendered) { + // use shader program to render an object + glUseProgram(shaderProgram); + updateUniformColor(shaderProgram, "ourColor"); + glBindVertexArray(vertexArrayObject); + // From left: + // primitive type we want to draw + // starting index of vertex array + // how many vertices we want to draw + glDrawArrays(drawArrayMode, firstIndex, numberOfIndicesToBeRendered); } #endif // #ifndef DRAW_CPP diff --git a/Engine/engine/draw.hpp b/Engine/engine/draw.hpp index 95dc656..f50b282 100644 --- a/Engine/engine/draw.hpp +++ b/Engine/engine/draw.hpp @@ -1,22 +1,24 @@ // "Copyright [2023] " #ifndef ENGINE_ENGINE_DRAW_HPP_ #define ENGINE_ENGINE_DRAW_HPP_ -#include #include +#include + #include + #include "./constants.hpp" struct offsetsStruct { - offsetsStruct(): xOffset(0), yOffset(0), zOffset(0) { } - float xOffset, yOffset, zOffset; + offsetsStruct() : xOffset(0), yOffset(0), zOffset(0) {} + float xOffset, yOffset, zOffset; }; struct drawFigureReturn { - drawFigureReturn(): success(-1), VAO(0), VBO(0), EBO(0) { } - int success; - unsigned int VAO; - unsigned int VBO; - unsigned int EBO; + drawFigureReturn() : success(-1), VAO(0), VBO(0), EBO(0) {} + int success; + unsigned int VAO; + unsigned int VBO; + unsigned int EBO; }; drawFigureReturn drawFigure(const int whatToDraw); @@ -28,43 +30,34 @@ drawFigureReturn drawSquare( const size_t squareIndicesSize = constants::SQUARE_INDICES_SIZE, const char* vertexShaderSource = constants::VERTEX_SHADER_SOURCE, const char* fragmentShaderSource = constants::FRAGMENT_SHADER_SOURCE, - const bool colorIncluded = false, - const bool textureIncluded = false, + const bool colorIncluded = false, const bool textureIncluded = false, const offsetsStruct offsets = offsetsStruct()); -void doDrawElements( - const unsigned int shaderProgram, - const unsigned int vertexArrayObject, - const GLenum drawArrayMode, - const GLenum drawType, - const int numberOfElementsToDraw); +void doDrawElements(const unsigned int shaderProgram, + const unsigned int vertexArrayObject, + const GLenum drawArrayMode, const GLenum drawType, + const int numberOfElementsToDraw); drawFigureReturn drawTriangleClass( - const float triangleVertices[], - const size_t triangleVerticesSize, - const char* vertexPath, - const char* fragmentPath, - const bool colorIncluded = false, - const bool textureIncluded = false, + const char* vertexPath, const char* fragmentPath, + const float triangleVertices[], const size_t triangleVerticesSize, + const bool colorIncluded = false, const bool textureIncluded = false, const offsetsStruct offsets = offsetsStruct()); -void updateUniformColor( - const unsigned int shaderProgram, - const GLchar* uniformName); +void updateUniformColor(const unsigned int shaderProgram, + const GLchar* uniformName); -void doDrawArrays( - const unsigned int shaderProgram, - const unsigned int vertexArrayObject, - const GLenum drawArrayMode, - const int firstIndex, - const unsigned int numberOfIndicesToBeRendered); +void doDrawArrays(const unsigned int shaderProgram, + const unsigned int vertexArrayObject, + const GLenum drawArrayMode, const int firstIndex, + const unsigned int numberOfIndicesToBeRendered); drawFigureReturn drawDebilMode( - const char* vertexPath, - const char* fragmentPath, - const float vertices[], - const size_t verticesSize, - const unsigned int indices[], - const size_t indicesSize); + const char* vertexPath = constants::VERTEX_SHADER_SOURCE_FILENAME, + const char* fragmentPath = constants::FRAGMENT_SHADER_SOURCE_FILENAME, + const float vertices[] = constants::TRIANGLE_VERTICES, + const size_t verticesSize = constants::TRIANGLE_VERTICES_SIZE, + const unsigned int indices[] = constants::TEXTURE_INDICES, + const size_t indicesSize = constants::TEXTURE_INDICES_SIZE); #endif // ENGINE_ENGINE_DRAW_HPP_ diff --git a/Engine/engine/match b/Engine/engine/match index 9976563e4efaa9a7733421203103c786e1222cc0..556d71c18cbeb7f67c6638cec0af9be44e9495fe 100755 GIT binary patch delta 50437 zcma%k2V50L*Z!Tk?4>saq=`bK9WO&1C{|x>&z;r_|)Ei-8kEkOqaX4wjgEYjjpBq z3Q|-ZLqnpW;$$#{8r%%q<*ShSLiTg3N=$M~w-#ikoPq0pat^Mm$&T(-{iaXWbe?En z2u1l^H$m>;UX{KZFVA#uPDab;+$Z?mpQ*{Tu!@E`XrhbcZXSII-G9*I7q9B?`dM=e zwTeJS{e0}Np~Js;Cg((y@+&=D%k}+Nu9N&0x!dMzxsm_Mb=eW+ zU%REW{Xx4gY*1Y~t*4(Vtt_ZPiqS*=1>|EIOUt(qctmRBrL-S$i zc`d=KKV8&1?orK1eg2!Z&y3wFqjiIZ+}JY=OIsDUX~u?fE9Nw*#Q(|KLaVh`Xu{{K z1#3MtYhIdbzwi7n70u%F!a-}N=o6&5w$W&YwKjq^H_h4u)r>U#zgmN_;{N_FP_x#V zyJirKjvhi9wK7dyZzgiq*3De;-OKt)~A$TRi_p?a|s$8Vu90=BrsF z|0h3NTaBSh7+&;*w$n~J3|6G`JE$S{pfAiPq)T%Vn}gHUqP+jH2K@|r>5GcT`J|HX z^ITCd$R7moN_!jvg@$!?*us;S_$ZHf34ydW9|pu%bcu*WcCwVe@i#@0b5@1OSzRXv zY<|HlBpye9$cAd7YDrb?Ew}C#TDRmoo*#Fd8(V|Y(t2&MB@;eZm2_3pJ9%@rkUEFo z>bgx;-OV=L_wYd|_6PrBkN3!*x`n#-7gS%!6}p!*R|WIJ7@vC5X5zbim`J_J9qYoa zsX5s^Y4>l@dAew&&qZ&&`Ipt6x+sBY{s^8<>-9mt(tSjem1?*Xm&B)~k5+gE5Hl^# z9bpH_S-kkMn$#>vP1K||SfwhxkyCnvMC85Jg@-<~*~!g6r}0a;kq+{y9@QL@xPIE4 zA#y>F5NqOxA#y~|N+e0{*mG#i=~ue@YT5C>>gKiynX1i-JX4F<6%?D_@YnsHdIk_$ z=Be!4yF-iNJUh+x)Mm6rIx4BxF5-7?bdRgeQ5la`Ts{S#hdIzE9+$-h=$&y?OKJ=3jk445@MRk7@l_YVIvh>k}SO^C?%h zI+X4jsS2xI^mqA4pRzUAsr^thSoX16kZSz4T4=G}O|RlUwoLX;D(f02=v|cKlfubf zd1TTE^H#LGwWk$Vx!fbY0`v5ItxzYPsm1kMUcTNFd;VMWL0+-w?9WB3+eG~kDAoV( zb=hu^%|9iN=^I|R4`z(CxPsJN1Ic;$x4ywOZ{JrG>=#n9-(fneU6LC3=Hcu+_ulw=NR>dmbMKn-&b@m! zpC><{OU##}`vsQaYXfG;xDO~E82GLPRNY@+bB{cm=6z0{*spwzP7tv=2f)l!4>Iwo zH_*0zYFGB|`?ctB#Vztr{c2ZkyGd(*FJ9wxY597dJb|m)YQ`B<;A5sH67oRqGdxJ{ z+de>7V>f;P>xD8wGy@dyX;4J z>3$%tXx1A7JN9R!qj28mnt_U+8Mr6c8xraM9u8RzpsBu* zzZ?=tj>;>Cl%sJQv@`Vj0o2WUVH*73JK?#Q4;w@v~kAr%o$~PaXFr zJ~c5vwGAH@=g}wA5)1hFvjwK!TDk1d_ARQw5{8I(19uf&>u4xd0j5cn@Y6l=wtM7N z44pylqq2FJrE9Qu+z`X-oEEnX9irTC^3kFGLki&proV61`7_;hlpF7M>hxz(hl@6= zCjP^vwCf2P|1IxT%1yObtt{?ly>Dp*&m4>E{9dYrlp;4C<{J>mJ9T>E-uPK3958P6 zaM`Vu#}2C*dI!VGwhF8Edvk4V?lZ#C{84i5umB-`OWasN%d}UtvT%rcmcWj2Y*(R4-2(qE45r0 z(*$N^d%#^TmvQ|wxv^TZ8n=?ZTr;N1O_E29C>Q)YA9Q*l9Z?7tzB#s1I?DS;RF3ij zn~?f2KK1qs+z0(bk|j*>FQ)R+>T~m{b0Ssq(qU&_n=`0kb+xX!`K#p8BP;Y8#eGln z{&p$PR!iRnCcoS-it;>0T~trz`y*jnb!^4Y{mI6q&GEIy;T4~tw19K{{H5~OBh93@ z95$*U4ZSBPjjHH#YbAP8b=+(!S5oH;fqjhFxOMWzQ6Y6}APf5maOwh*K4-96d~W_n z?qd3KHOaeCx~tKNd#iS|z4FIVWh?f*%SDPceuZ;r!K%ElYSzL=XoanjTYVWy=F20$ z^bKnFKMW^178^F*l{bGG5of_nCs>ZsEKfrW`S@SnmAgwZ;7%@wX4)K{PBWw}SF53p zJmhVa-9`tH9&&}z0YQt^Mi(oy-J!V5=6B=)qa$0rSBF(`Z6;mgwehAXQ;1KR_uCbm z;Pui1#`ih7`4&|XHXIndbd>8aJvAqxWE*U*WM$bG;%5CRZS9<@n?XFZW^(E-uzQsC|ihFzm`j zn+GfHG$&D*v#l`EgTIO;lXb~FPNsQ>>K+6^7xx2k{0_^`c8Apfq`Di4vev(Vmyj+^ zOXp?s`iH3*dkw=VcMwZC?#x13u=jFKZ9U3;`%4uq^6D&jLQAR*^Ncgd zEiN*W+enMM!hM{tiWl3PByX7LgCjl5#GYjaTE~vn@E1rXq&|vIz0dX5$kitWVybI5 zDb!=@=YlKc8I$S+ehE_B05!_LfQ15kqcS!hx5(EgMF!90i;C5a(R|3KieAV3B$u9C z+OI3O&4tq1duZxvog#jcJ52WVdaKS9iTRotHRppoZL%*pEw7l|yG>7C0<5OTJ+pSw z9XN{6%;e@z#K_dz54pDXb5~anE}xs9muoE>*Y&T;u~SOZfU9z!Dd8b`m$|Ci0(H)* zLZ3C?(p-7*%yRPiDM2*qs{C@w*J136EgH2@{9bN-PjTe4cyh4(-PA7bQ9 zLH3bqf(8|E_G%|yb0qrYCotK%{3w}pASZJT^x8jP7fmOAYP%gL?PCt@@v7=5{P})&9=&mW=1-^I>yE0KFIqP zmXxo~Y)f{@!Lydu+%b!<*r{fb?XspO$W>8?UdJ9czTlmz>p4@Y-xe09MkC4yJ4yZbPCNJ_OFETZ*0NbV4^Drx?CqBeo zta=}To+?`&Kpdu z)>Zl}C)2zgCvq9JN19Ls?=zn*harb$@A;MdOCrdqEN&!U9-Dy;BcgrvMJe=)rE-t? z(RP_IrY@T6twr|c?xsr*CUNV0(VVMh@I|xf1kJ1A2h~M0PP-l!Ck(wqO=^nvSC=}W6i`T&4jm_l?YuFJ^MUo~M{uM zJ=P`29&2Oi<1KQ?rU9}Amr?xE2RZ4KZB|+1`dIQtj$413B*@+yI@_J!qzB^?`bGDR z!Sv8Zt5$-1e`755=E*H~qc&Jwaj?2FMSioXH0dk5ejP=o$gRH4G`3x@um=sOw_Nmf zC*$LF3cFH~dfBCW8*a)YHrFwRg88N%tzlfdMqzjA(Lnj{%{7hxtVJa2(Ne}YxPW#G zdC`_I^Q^VD^8|jf#t+f>4^L@IFKSrQOFd}#JY&%R%#cnzB{*B?t!cLW4+0xSfl9hS z{Qhd+e4wO2F26mK^x1!Adol@aa#iaf7x8!y4cF-kPx20us%GTo50!iFtmyDNLzARS z+vUYOXSlwBP?{Z$Uy1X*C;J0;y)}l8x}cf)O*3OYZ!>dU&B)DPE?3(dK+DT=o4q4K z_gke+_G+Qmf3!(ES*3rI@9eGYIAW<d6 zVo^@b$juL$A#cszMc*EgeNU97=Z?rRC&J_nIc{XU{B6!)T1DfrCd8b0i)~7clc6*w zW&al^YZ7utUT`W2li7dpK`xo7)(veKG z9E~J}lveD_j#MO7sB>rbvLe|UFsc);MsEJRBp*nY4eRKUZ`MSwx<&5Li#XM zW%3Jc+|lx&GHF4*rS_V_`<_sEpg#YT3R%NyWC*R>j%}$%Hj@>sRdte0>$hbtHHg2X zn6&B!>1L@=i+B^#j>Xg_WoWh5 zEU7lBNR6#odTp|Zc5`KY>XI_dqYjZ+OdT?nX1Qq0-8y6jEu|(>$PsqGE~!W|nNvMd zlYGS*)g#qOM>e`12??tguQ{LE8O|@qOB0xsKBO6P`)F8mcBTPICF@xGhGaOc7srk^B>l;DOGp%v2+3jPqDd^7V;LGv zMj1(xS8Kj9vY>56Ly) z$$m?}=43}GE#+%*8%jbbb?~rMA5KBuREAA6WDx6nBuBCy`EM6Z>frsZ8q< zMkkZjwA?w?ZZdgbbTklFeF~{hozAimQ^<X=u0y(%rU29lslJ+#o4=8T zPbEXC{b{y%Dk()?v8_|dY+B2XRh~w!kzof?NO?+~Pg*KYCvAoa3W)p1sKCvHWlT86_3JiR!2iV7jB#|)IE}b;5)&4IYv)ueSN|ExS zE_Nrg9tO|hPaD#uIBV80o>dyPcTnl*zu38Sa-Ga$>lTq}WW43_B65h(7vHfZGGe^c z^7Rr@h0w3}Y3Ww%MFu&Hj*=z1|&LPE94RYL5p5*J$8Z{#~ zzXuz(f<%!Fwsi#}`HJ0LL0Xd{7Rg8tM1M9T;fV5fhA4c&6h^AM#k^8mP+g^Jy|Oe~ zNxDFl?0|sAI{q(+p8J5`EE-sS>KXSLV8*5?8O{Pn`E$B-;oe+cg(ideTZ#e zYv{y=eMjzv242^UPS=b!KWj4@p=RXf_hdu&lK|9Z&VKTdQopNg`T=z7BDU=S8s!2j zIzYn6HWq3jlc~=Yw$6gt?@xBkg1PV2n=K>*tPGRw>&HEGyob~Ot# zhvVfH2gyPjoy!&-BuV5Bdwr0UrcRfb>md?^&{aM}nv;Dj`4G84erBT&WD8EY|SV}z1F*!UcBl~$g? zTAsjEy@E|YK`PWZ6oc){oGUPulkYKK+9f0%=1c2}lk+7R*C;EM-916tQ0M8a(IgVd zI-SHoJjjNhBtyv$mY+{z(rI8$ft&p9CO3w^FTz)g{P?sO4?g(&>v^+TfLz39fXUHsicM98gn)q7A=8`)kvzOyy>2sI-&{|olz zITF;#$13+&lT-HDwyeAgncKrQklW0zTbu;JwEpDC8{5$iBCGQ-11$oL`&yyc$ z-J|T(c~YA?9A%%*lUZa6GhZO>XyYU7$^{ZgBag7x7toh}v4mYD?TPUM9MNK%#h6Fdm}tdSY{N4WOdVIT6VJ$UlE}K|lVQ|;B|DW*8qoSnS3D;V z9F})e4PIr>o|8@v4Ybr>tjTYrW>~xRyq-CSKKKCBCEpBN+~=wpx%pMPu$8}&30?!c z!ie?Avm3w019nT;3(|;$^e*TA zBhjzPNJ7kP=Ns%^64|{sq!smD#mc|M#MP6HdW#)FgT*Z4Erv%r%Y6%>G3?`8tcBy) zl6P1dv3UIW4x{B0i~fV;I*e|o`ZdPl@}6{{wFfWf?`rkV;WtCy^L=tFjo=B-xGc`p z1B0ihf4KK-qo!*uw=DgOIFnjI%XHmsT+&G+;*ju++r^m=aGd z{6EC5d?dp$tfT%RZ_tXBKauarZuaOCrj)JBR6zD)j2|i>-_R=QtY0DaPAysUA}mLR zY-th6zyubqkey^8&R@wwGIDt@8bVqxH`B`G(emB29(l6-SK1pU;s~uugJaluLaSr0 z+D2$!S}|6OZy2Q&C~3vo+R-XBU@l9sqvgEcVGqK)_PF_2vdU<^`d4L)>(DyUEXG8ekvEn(CK_)?eqxuLXeYEts56~RL(FWQGd#&*h0b&h zt)9$QyU-QXeiWR3c&76lczW)_;puK!*DLxeSsl}$dQa|#^vf7naA{Z{Y zZnPZvhP`&98)>sCmZk3WH6cSRGd(Gmwk~XINm{?wvZ`ud#A!l|FR#gLJ+01K$vn-E z5t3a{;u|i~mw9>75QoM*CtX5+@uFw24Y2d3_pvd5>`hZ?WEIOOADTq4^j|JT#{}1^ z$V-{CassArScuS)ORPzsiY&>O_I0{3MvG>*WNlZ|kv;aMLrFYq<41pv?pXda8%?#O z%|>UpU(pYko6$H}7b>tD;N%feeQA80xo}w@6pl&z$ z(v}0$>B#mY{WoR3h3VP;Y<{w0ZsrLvL6-1ksdOVz4RgX_^hj6Q^+Eun9 zh{jUaaCR$**7qHu3$@UN8VzR^f@wp4b6Am*I_G=1G2`f7zChv@C50E>nwf*q;$@TA zmSDONJJlK?v<=C!Ob?-N2{i_@ufk~KQYV7c@bdMLH_?8cOqY(Zb78cjV>d13-GzmR z(*>o@l=)27!jtJz7CRkIQ+>;Ro>@xE^c`p!6hS)^jOD{+slU{xbdhpSemk}#`@JlE z;4-3raa0T0&2n_0YlOe7~Uj|011kCtJ-)}`^}FIKf4^`kGHcE4dbitE^^FCxaz@QQDDUNdt&G*XBY z+ft9##LeifdbA_`=*X(oqrS%1jz#i@b>28(3t(;ys5@KNfCjKl^=Un0cQvazr~8wW z>|O&}3g=?g*ue%gjCAGc5#$AHg;x>a<;aG#TZJjR&@sspD|En)n9izJ+XW zLmELYv*;*Vh1o^X64))3iUKu+Q*~**t89D}qUFSs!E$;(S9UgvcEJP|98JB+1Xd@S z*6~@wH@ND#GR}EzuLG=MGo#_e61F9p_BWqu!-K)^Cu*GGSGoDC?RZR1q{Te}jc33M zOlleu-&kcqj#77p@n6~mSd&yup zM9Om8kp{!@JXh;$Q*M{ohQ>73<2+2^9KrfLrSci}wlS?<_FvID5+4#n709IVT~5yW(S%uRRE2IM+>;e!vrWi0|QmxugcJH}UIq@4>CP zpgMhRBdTnMCcA*$iJ@f!dstQcq0)|D!>D3_V6GH(g_Vh=!69Wdk#Y9wJANsh`J_%h1N5zO}U9+5A}A-n9)qbXxel0$gZ_~Ah5~tXVCKPX661JiV4G$Zp6!MO85-pmR z_#{5(kujd|2hRX`?`= z>h#CvP_zciTtbi5Yk?n*7+YO@D9}@N`i4M{)9FhB{e@1S66nQeiftb@3w(?&zE_~D z^Eps!pe+JjL#I~?^emlTAkdq2da6J#H|zXJfv>|SEv?Lc0zLRtF`X#Td--IiNhb*O zL7k2j=ojiwO>0x6>BcJy;!|}xT%eDhVjeiDHt*51J%ntbUiJdLQm<}doL+RQ zPQMlC$vXY3K(Ep1d#(cSa)RySF1hMm>YR{WMyJmRw7X896zD5@A*Tg8N2hZIx|MF^ zNVvd1#;u06Wxf;WW@lMW67}xh_lP!=R3^n@^8~SDy7n}I&eiF00{u|;VN@K$;exk$ zh>*SMXmJ_+1X|YVJ_0>kr+W(YJ$({O6zEGj-BzGmxTAj7>cW|w($A5E6T z`W?f|AOEUfmLS$guip;>y+NOX_6l@{PVW}z-*kGLK;HpvtKVjUzo3h+7wDrpy+)wd zp#s(*WeD`*gIfKrW0mIhv*@E}xFF`Jcf!5`y;`q!cY$uGcfvLTy%}X#U1%!MwGsJ& zR=%#lpV7;#EYM7+%M0`poh~cTE%h=>H)Bf@F`&JL?2>w`I}3C(eQ+sF;k320kJOF* zDbT%j`jtTU(QW@G&@Ga5;|~OWqrRlv5NIdeib#Jtq_vx!MQgWS4#lzkUeHcQwl%ar z2y|P$Gwc%Rx_Y~97ifb{ZxiT`sE65Ve5=4OJy2ZcdVyY}(`yBKv+m&vf&Npc7Yp>S zIz5lmW^2p5*7@mz_y?VyDA0a-gN+vG5;~nM(EW9Kf*XVQyfnKQ72?G7QPPY>1`#RmkEby0g zzJWkL)akkceO0Gx3AEsNb%B=uv-Bym&X*U&FY0ulKntFG33NGq1i1)wus#f_K)=x~wI23^z`xPOcL;Q> zPHzzC?mE3fpuf`Tg#x`$r)M@XI2Tv{&qFM!BlRBYrst0m)Pxuh5a<|Py1PKP)afq- zx|L3MY{JZ~(f`{D***2_mIB>Ir<)3NBb{y}&{cK1o4I3j7f)fAJ5e9= z2tD_$klRbAUkG$to&Hsz8|(BVfv%?0_XRpcr*E4DzJ$)-5NM-L|0K}-S6r<@xFXO` zb^4+}-_Yr^0-Y<&HYWr=OBX*X(A#y|BG9XK`a6M6)9Kv;JyoZ-#)jz)GE(O^3F5sE zvKVY(s(vzLjb^=tM1GTArf6K&X$Qgh5q&df6zJ1BT@(YatZU~b zo&Fdjtf>!l{=FbRTBqL#^dy~rDbO=@I$xl_*6Ak#?Q%e0Qy&Vvw=RBHpv&m=O@Xee z)7J$$TW`**0)0-WFU1IJ>a`!()voA}f9UyV1+`r|eM+Er>vXn2uhZ$H0=-_R4+-=J zy>sjzD)1Y36!)e50^LL(f8Po86n)5S6X<0+y-uKaf5&3G^YN!=FA%cJ>GTYNR$n*K zhUG+o?xstBDbQnedXPYO(tB}Vv%ojg`CbAYp;xzuK>O%)cY#*67ssrVK)=!HHUfRl ztn-i2LfDBSN4z z>hp|fe3R~BcnovzgBA`Fw2tWY3KVEbFWOh2Q}hb>3-ngK0wTTgKhx&a|IByPY0<+@ zx+_(LGMDMnkpg{sYjMmf2=skD3bn)@X0KbRD~O-fV_99Gx9h>GDbS&MnWBeZ>%pod zNMHBWt&|h^%Dc29gC^f-Yi$owToWRQ&C{(K1bU@Ti_&BCdjHi}?^#Rr`u#1?olp_m zk->X`zo19ytw7h-EAmF5ef~3T){ThL4|T`?;6?^o7o{AX{~#C-)kl_deZ4|GwiE}; zB+yYhJ*A#5?YdnJb-PQOwNMMEYO+vPRlQx83v@l5UMtX^TUjs;yv>vKdVVWpJL}ze zhd_Jj#?}k;Q$230JCUU%iSKtEM! zv-*Vlg}~p?#q$OFvQFO@=xm+-S)ljn^m&2aq|+xfn!WB%C9|yaS%Ua%o&Hv!+vp=| zt3Wr>=?wxc%zP^aI#`#^Fblk=&Zi1A)#;f6{fFMQCJXcvo&HLoE9-q_v_O|J>-;Ez z7pB7D0&UQxhYIvteUJ|l=tnv|K%jrp>AsE3dXGD$^L+&IA9OlVpuf@S1cC0e9tO36 z6)Vu~b-J!V$EuH%H1SFTUqcrU6=z#}K^Nc8H}7!AmCx*{&h9W{vKuU~U!{%Fnu0h?h~nPnFD>{AHS~ zRB5t$0cIfP2$e};RNJN^>i-|Bw;{n5%5 z09)1mJdewI)G1Pi;mfag{)hal$g7CF<(B8uXj6xdSMhs)AMgz-p(x$(W%xe9*P*1M z1bQh-BYcuPUmjf39n;a=rb^$j7F981nz681)EmS%~zY#DQm<)9BF&K6ND*+3E3BVG^ zV9&>3HnhaWLJ(_!yMTLv7l7A~!yvFzHVl+97_I>;0sEeS0pRz*5x@fALg2zvs0i>9 z@H#N~bfNMdSO*y8XE2Pgk_z6L{cU@f5O3Tg`U1ttS40G9#(0v-X@xmu{)1vUc~0NVjeqnz%* z0l;KnPcsUdi;Jni+1Cq|&A=f)!!zJg;7j0fpi2NE0IURj3rqkiz!5;F8;Ag~{Y^vw zI2w2zcm((!cmwEF8lxQZUp-*M+l5M3;2@xRA};*y6e^j(aNrSOE#O^Xr@MuU0X3fr z3zoKHmFM)S~nZN?zPrsUBAOr=0hyoS?djfr*!2qxcFcUZ(cm#L>co$eF z9|nLYfu%9UdOn9fa4@hZ@H}uT@K<0a@FVaD(Em5+1Alu7ec&SV?=TQ%FtmOJ13=|9 z3;+kefdSxmZ(#sv`~wDnX5d}mE?@z$=6mRe8w{?0LLc}H*b_MFFX#hH{tbQLA>a{U zpAXOnnm$4wXs-AV3`7_VQJ-J{mL~K=(oz0B!>w0hTO+0pMU@0nmgOI7^o` z7`_5V0T%*$0=EFC0?z<5fzN0`9~6z&(Ml zfm4Cy@ZNJKFbQ}BxDUPK5(Wd^ntrdLLd0n8~VUzrJxV2 zjd$(~BFzTFxBf6t9tr_401U-@{yl-|z^TBt_)UUL;Pqe_0M;%G1Hd7`0$?XBNx>Bi zh7V(llo;TbUll1yz-q}w%5-4L%pzq2Fb|jwd^fvDc?f)&R;1XOE20-KE>gmQ-7<=l zX23r)iX3?Z8vOu#H8^6X2yyFi^>0sJI#Wz>wY02g<+!K!ALwuo`oN0!p$~M=D>5ryRSkyy zPhbFe@K+cBu6zaqz<2pD0Q}-P3;+lG1_Qul!1usiFW^8mG{sBk1Bd<&ePF~Z=mRGL zmjQnUeh+N%8u~z!5z994roCBFe5)G_lB1&31J-v^l&(DOq9_xAzQC2hioh&j0`Mkq zB=7@pvK#bkptF`%lqjH5Mp1eK#{?eJ}hL4sdB7MHvEYjX$<9ADGflQMLik4^))1z^Nng zM}vUQqp(Y=i}AmCtfEBLH5dYuVF1_;I0SeaI3HMc91H;e1fB(6o&W_K_7Sw=n`cx zc&vs#FanqWJOCU4d<9x@Es+X2v87Py1NmYbv^zJU+6Z^Vpko8 z7({F?&qn^kZP;&p=Fa3|cQzxxG4njC+ON*Rm380{!^UtJ!ZME1a`ehBcIYTAL*MOU zw}B*_edM|J%^O?~Z8uwhbm*mh*Z_*2_fQ=-U;|+o;EG9LAG-!QLfOaT z_#LiVEHE1|jKyZdwg-112zfKIQG=TY*^X?8E@M|YyNc7gM|ISOyqH%kITCbNMjQQpSm%%7`l z;3|ucvwwIZnmqR5sgF_VC$YB)A;gH7iIILtO7w|0$;joUya#+p! za#+S@Xyir!J#w1e&Y}Jh$=*0sIbEm>6T1c-Slp?XA-@JAXJKGH3jr7}o+)JC-bWSQ zBg@G8@~jqY28SkWBZt~7heI8f2MGD~9Bzp|v%bFA`u9GVfmp~nNZYem4yYs`EQG^34gnIsf1Jhof(iKzH)&fw^JGJ@ zC;V>kOSX|K{>5f+MSedxh2?REGIAbZ&q4qpA8^n1-^{#+(lpkWOWUy-9EP)v0OONi z5VeQY+mTOx$$r?7u{@sLpOFj5esr^tUAARkSF>Zm;}1Uc}(MTt+TJnz0ZLQ7o228`hV@a5jU(EVhwDCd=Wlhvfl658~FavjoG% z({?+F8|I$#d(+?Z`1tfG&(r)K^#g7Rr++3trC6TdyLM%LFTvd++-FswqS**)Gg}L` z9N0#Pk;*KGLlVp5P@R#>fHW4up%IG(7>~UZV(Ar#9Tc0vvv;tK96VSKhYl={!vIFE zU`$tJ5m%7f%i=iLvwZo;h6utxRAQIQ~4HwE~iIZ0I!@ znaWbH;n4BSGIjv0vFh?7b!uR5uEC3j>`yL}z`TEg7iYGx20uYFigo)5>)1)_~)Sovb?$f1_a1bttW7s{lsaXRt5tVla=u zoopp`i)U439|81_ea!!7C`@4We+Ju&b>mQ)P2%tcTg72DJIG-^yTxH8`^aGl^Sc44 z#OiZ!W!*SzXOjT*?oqb-28{Z#gPa}CZrwoRm{=jt%(}tK`~p``|H2w^ymgcH29h~! z+Ak2E$kuWg&W>^z!R~Pw$qG4iXMs0iXeW!ki4-OgfbsX2T0|pi;b!$C?k_UY`-*!t zWyPbVGxEo=9IpBu%j4j~$SuG=76J%4ihGYCpXv55)_o6MiS^~uZP^SC_G}|p%6U_y zT=;DFldJB2f?+5bH@zcS-YqokSQC477YkAFZ7f82CRX+~YTS^;-9|vxJ2AHh$eWHl zRB;YC63w=7=*&)YsK}mj!Jh-!p8MR$9SAzJh&#v{%Hr<8{?#&U*d3U3VyR&1(=fIJ zNvcG!A35#Jo^#<9%<(Q@6DxlgVQk0R@Kh)p3806|vqg8Ia;YNQ!|_uUb_s|b+V4F3 zQyu1Z56NU!`5uxNqgY!c>4PXXiqo}OI%wD$_nv7>2 z*!$hNroZ$Ax4)%X2T<6QjNy>XGB|w4_HiiBu5wt(-g4N%JRhM?-tDEZhKH!1{S(~# zUPWR`7Dk^PZe*LVF%UrPGdQee`#2n6R{y z0fhWBNKsyWru)3O^ESi%?^%}d7|Y!NT&`eOxne8!mcwS|`2M;fHBC_xKU)mi7cU0QAog*lqC|+^B&p7*i^~Ay7a_l&5dy`FPcI{v z;ET;{ZgI_!r$}({-_<>^$(sGW`QDeJ~!80`0-I`m?nhxS!5ubK5HN+vF`k81=+Zt}W zHC@}9e}5*sM~!VgBh;M}Hj-ApCW|r}7h`q2U^HG1IXYjXM_F?#rD_+=tm)D0h@DZX ze&0&ljJ&j_V{B>*vU@*DSRwXkofh1Aw@DzHp@grFn4z#+s^U~e2W zg1A}wCmZc3wjx8u8%vjJw9km!Ul0Bxg#W0IABNg9*&1X*CTqNLCZOR26V0bMbCd)ftf8l!R1+|R{e2q;mK z>om9cO*D?Qb80-dA9nt$oLn2TB~y)Uusgj!)#w)x5{*NtkiISrN8^0c38yspQjd;M zjIorMW?V>co3K8`SlOv@e{f;7jL^T4VhkaTEgw>hcZ?*)a($-JrXHc+B4!SqNSiNlCIvns1jmZ_s%03o+^(cfQ{37iw<&U3@?6ct9ee2Z zA)63k?oD6h4Aym7`(?)Ve1pHtSdN5R&Mh-0+o3l$T7|}JY3Z@bm`9O}&NK!i**nu% zXb<6@n~l{0i&U^=Z#MpILbS4W8V>@-?lSH(wWyAP)521IxA8QO=j*+we+YBiXY2>P zG5d_6C~Mh1<6a|aWr_R2XmWRpU&M!b{NES>BtC<6J7wHMT3Z}W8~-3K9w3|9mz+d! zUc~HkjT1;5Gv^v>0`{w5d6sLuLtyLVIpc2FWMeKG{~@i}h)c#OSlNEb_?(b1i+sh{ zkT~s_YcSvt&kz{QZeBIkz&h=G&Df7Vna94ohFbTvEWBpiKu9T8`MU9zbJ(X|=rrAX z;|KHn8CJuX%MGJng|Mf6Kt(6v#~A)&3x1S$##rc!URHrGn$8Bt4!90u?I2$(EV{2% zV7W~o$ly4K3k*a~*q`{}6^w!GA~`zz0Xif&m1W*A_CVY|+%OLJa4VsXB(jD$XX1I8 zC;RFbV+1xLtA0UW^~UDZ=#NdsO`h^+1sqDV;G4!k`;ddPARl6BcGK90ASjz}8HXW6 z?zfEzc7bm9=CVGwjS;0wnB>XmU4s#R-VfCuhU(*H8w?$t*oNE2@}3@UU(e76v1_L( zpncix+s0^D&oUhv;2`p9it1GO9b*^b$fn*wgS)adcZ>;s?=Wil2e<9&hh%^T+}T`6^&(X_l@fzu;)IPv$#DlR;R$2Jp5e~ z>KbQD5Ap1bHZnCQB}yB4{s2!Cv`j6CJ6}&wV^8OqNXOgcdm6g8nuP8|&cRFudJg~W z*1$kAdto*99F6g0s!*)q`EhOw5EV(up=uHBqX(kBD-%!NXHjBw78e{& zPeao>7?Qu2D2F;y?5D0{#PJXR554?DGEYy$xOg)MTCY6$&^2bKcqxon1y>Z zTe=`6Iu;Ii6CYkjW5#r|p=mF8oLj;GKR9<-9pHGTj$Y0`By$_`UDhBtrnA;+Wtm;Z zb2rbKZGM|wPVzYXXsy>avrFW3q|aOPH<(@8qR~tjtZ`atcInFd(nV`}iP@#)G^8(i z{Dom}%vfl43FmRTY)#KMyLfVYSN!;-WX$-G5~232dRMK@9UsA~W%^027R_j{h3%#_ zH^I=<4EHiiG=kT>k_*^|mqs76+L&vtKIB{5a-B8(FvX=0_i4Sg(l=9Fe&ubs!CEJ@ zwd(UmtB)5_G@mzF(`b9u=dZ0kXIa~Jo3(AnrR78?gDW zj7^Dy<-#kYFP{XTzBb0wlP+=R!cG9D63eU@QW2K8YGcgJ8@ zQrvP2hT$pnc36`3hjJUnjQM6))yV!TT*H_#+ZnU!2cL@m-9@^Ak z|DNKx?(nLyKQ?>n(r=}c`{R7DGa-d3LLyy z+qx3pT@2+Zf0oXnG~34^#PV}psS{~tyYbcAaEIE4S;<;luqvt_lKHA1dMJPR==7zn zT}$$Ib^6jWqP{egkecj91F02OXBeecq1tb|Gl2wYb@==F-mHTl!AjChghjOEl9DnkCh}s zvr_Q)(WDymS_(^RB2~em(4i($AdV4lHj(%_--jkrcP#Rqno1RMQfO`}CDV&j*`H0N zAbMje^JymGNNNhJ(@g41aqGIO8BAT8!Y($G%oMk{ZR22ak!4z(6lq5WSiWf~RVDai zt~XjqIb<%jH)hu?W9N?2@P*2HFs!dYK|WF&lq;F zomAGNUF1^iRxvr3{2oS~+OgN|ByT*Yk=jcw?Bh&%5KUp7+C#l8o7!ITD_b_SD@u#! zE38x57=GQU?zC@^C>OvJbyL^lI+7i3FEt}$S9FlB;$-$(2h$rhr zw%86jhJDvjdXG}Jc9MqEcjGNyouw}cyq=wizBrFLNAA{M5UPRF(u;=5&!vxHGgY>?(~<^0OV+=)HHj+IzoX0SBc3kAa_cRB524 z`9Ue#gqd;nadfvz?C5c6Hhr1Onr2Ha>HSp8qHL*;gtA_pmKGv=${8t|9-VJFbVm9_ zUAju?JG9B@>O4G0lKjnG%VTLp^YkqFXd8xe*Qiq1bz&}ysqnt$>%z=S}@Vqoz#NNQJ^8#HEIn4(#17sD-LQ#2)#_Z6wV6PGn+Je(3aXa`;! zmO8LrKT09y2IZ<@O6wej^D@kD{PTD5JUN+L=UWM>p>>&_Z!XEV5>k{kJ>4AnBlj=b zx{59_M|R`sIBR;D+0f#|R)ZnZ&9q&clNMM@!F?_Nu)-fDS9AFie^4zNry(X)u&sQN zrx7-(l5I*gwAhPo>*RJ7-I5QLmROhhC$`w*u&?0pX0GIH^&rz+QuWBinqFy+?9<9X^jiw&>Lx%f!@0{=G!1VqEOIdIqvr!ztfOfPnQ1xdXu=;9pTRCTnIdp1{0=~sFy`lMsz=`j zuujgn^C-nuIpc01l$~}q^`treEX>7(du5j70>}K=9v4$j{QnVWSJM_eSlH)^(gw3A zH`6jaKDz3L&AgHQ;by9h*|@yBDUs$nv+3@pO`$vR-+}oj*G>h4F}d$x!$^PUG$d(8 zg4+`}*383{h(}1PJy6_a_KSxppB{5$Ii3ha3--#>G!!pYbS(i`!%hI`kCas|Y5EEw z*jUmOP5&V5A;(<8g1lfNo%QrGoxva35A!x1pm+XZcf3vE#9@VxDH!ny@j*oAv5r2b zFnX_mP4F>Qr!NZF4$$;?A-n5i!jpqSR_KHO;u6RzmO@18vo57fqY(cyrA#C7P^y+M zO1t%eedTLPAnulIUz0n*!NV0lQ`rBj>&xR~y8id)-pRdpWDd&gAS4R(OmtHuS^45(77_RARix9Hg|ECX?ztX zxeZW-Svv&#s+jS7ll@V}jN3or>ZgYA034~mgzcm6seTl_?i}LX0jNM$FeCjfHRIEug3FnW)}C_PRFqf9vne< zb#sw^?PF}JZqC;xc@k>SK0nAms3xbzoAGW&( zj=qh|{yctkun}AL4umJ0!?_bTJJ~#{e>fLy8#9kOV@zX{d!h5Nq_H`e*>bxvbEg?% zQ_M*H^vh_KVkWbgW~Q*c`r*A47DOhJny}g=Vr3I^h+a4YkD72PBMmK^vUr@BNTFYz zj!jL?crjx7v8lO?4QNC&R-9ZcXvXZWgZ<5zqIYn&8PmrPq&KI}y~u0M-Yo!SQW@Lj z7@2Ce(H~F7zEpD+6WFPR3C)HpEtrPJ=$gj5xgL|#%u4!iqwsnf+vBw{IGM)W?ts4q z`z}ISlKpZb$82oMb1=IlvnU?lx8y>L9Z}CRMK2=rS@r>bc>Y;)o;Gl0D|5EC#q)A2 zbEl?%(jU*HvmZIvA7y%&ZcIow1NDdfF(;iK8)J96Sw-(TpqLBVIZ=yuYAnXh6U(Oa z#{}3j*v0HdLI$D<&i2wNT?G*plK&&Cpnsc0H__6%^LI81@W*zBJm9a-j6S5L0kbh&>2yOP#+4A+2q(fm}Xny|7M*JbWd{lY{X@kQQpUBibB&g?IrA zh1i7y6#BJnTy004$_Q^yv@{#?z!5n~wWJ8=1-K6=vI5I?fzt zYCI@%r916cML-XB0;y=!gXx)p=Xwx)Q-BfJ*n?GKe;OVF(sGwaLtc%>J;7}(864ikP8;&}x1rr3$* z6#A787~7jVF<2|m?l>XDVmuV&7adWn56K|(?qk;1kEdg4AGVf?i0jMY%Pws0%M@(D zfBKqJd5EH8KW0W(eALfO;K93F{rJxM@>zuT7o&w1=-8i)r5oPt&tb@5qz+)d=U~AA zv!DJ)0GH>8CD+5-m@0Q59VuaE>;ibc%TyO*=Dr< zZ6vB?vzO4YB%3`5w^Ops=2X3rZT8kbjX~xRzS9dUW8@GPe+?`iVs@|8qpE5*a&%&| zkG38050f43@@f7n;g@6fWQFUO!>+0*97}S{hFS;D(HwJ$rbT&r3^iNYwIr+=VNRnc zKax3}i#9@R#S0Yrh3nWdk{MnBKM1rN%uy8c(O?w&yaP9o!$C^~tKDc?Uym@o+R%Ls4T44Rt^-A?9Hog$QRDcZ>`VY78{a7=W?cniPnh(TFxzx$PHX&x=Ckp-GZTOF)y`tMl zrsU51&~aq5fN?am3`Z&SeMPt@l>RJ2?0B+~=suoo8s>~=?5ASKc$%MzIuppfkA4*T z^*dNJf%al?P@n^GRfyGaPb7KbA7lvm_#eDL$z`R@LiEHB6KQrntVt9*&~OsFi6~?X zk%_!XG&dQCCsA_)u2bkgUO?3IM1#=wd7^DFMTpthN)fU4qU3Kxagh~O6)&aan{_97!2j$A(SsMtCo7T_yEesUR4Na_zSqs|m6 zH9+4fWJhD65Sws-LO=Hht_sqP@To*QqqPtVF^NJyas?Zw(!!%F_>Ph%A?+`b9fL$6 zHY1BdKYtZ@FOiJEVS#qRbs=6wUPHTSwD9;Erc!dvWGi9LWKRpR1&--d`t467 zO($6%IecW#u|kMNI3mc;{z4H+{mEabJj0CDiynE}&M+&9<}+%xIho?pY-;DC+{+YO z(UL;H;KYQNX|V#{7HBt|5@PkG3oO2=T73zdw31POL!-5^*kDH75GIq}4 zlxID{=P-!%$eTkCzxbnY4n2h81|QjmqUKV}Lput+uoPaJ%V^#yg>3?^hM(rrYFqfv ziG)LjP666DqOk)m}!l6Z#6V01GMfPjnnyMhg#h zT%}~w3xAF5XtWk$6DCpU=j_<{8c8?46=-MJms2c6B87g$4}+G|!XrQAQL^bB7VH>Y z7h*FaS5WD^fp#lMMqmmb+4Qyw@hVOW@>3Jem9+5KL=q*NUd~FgW3fUAE`CzzzgZ{} zWO>AZXjgO+Vi9Ig=$|>T6SVNeA*xU?Zov%E|0oN`Dzl4_{Z`RuG#0L+m)|Fsx192V$6 zTo+{?>IF?lVquTKjs0#DX*;IIuH-Xa$Blhpg} zEl#2y|Ag3e=5T$&&l23Wj-ET>(K;%A_KU=3=M#Gy-{zYmn4acuQ@!X{Y!h<$IjNHH z4povc>m3eEk`T0>L4N!W-+=GontUfinW#{}8S%L^ULBA)1WmgqVeu6#BtO_)I)z!C@P8dXLkw`JOZHndLbk!#_JXTbzdaJGo@=E@tdBdozrmc5>Nf z9@-ReQr{eN3OFopi+2mmNG^IDD=@3+e;vfl0<(FvaY*)F)nun*(+8j8;Tx;W?ebzF zri*`lblye7%dvJB~K(e;sR)~vyZp^VyShlZ2ytS+HEE=O0VzcjG_Xn z?BO8##y+I)F{|tQ-pBYoTscdE<9&0Teq|@NzRyZp8vnj;w$=AYo<5>5|W}Mi^@u~;Le$ILy zZ$hK}oOZOrkp0Z`Pc{O4_M(5oX*Fh<%e8gcn;w)J$a@i^S6qEcncf(;uwBp3j)Y zt&wrmT*lP{k1oNCcrm7Y!G+O89Q%Tu!RL$6;7cy+PDS3A%%P?@ z_a)~+A*gu5Y^wkJDh8b3I^)?_vFwDI81WVN7KQU3CDhi`6#q3e-o6TFPcU60;D3_7 z>!8g^hX3Kxx!YRbCA^Wpbq-&l94=5oICf4dre#qR5YW~ler{DNiwl8XjWUk_zE?vN7-m)yj?AsJm+ms4c&Si z#P#RuA=aQy?7LnaV%5@5K8FKCtikM#qH?UJe3A9bvBLT0nv`SJVi)s{5Dt7TL_a(d zVh?ION0E#kLge6OAts|hhy^$+L@@k^()O`*G#YBvH1RY5zw48NqrukMq^H*#kWNCR>ZOYT{>dv`N2; zY|4N9rl)3&1R-qti(RlAqpa}g)c-cbgWUA{l-)EWy=Xq5}W7H6Md|$(T(1<=}1nMbnFY2lFmE~u5-g{}OpJ{0Jd7eh! zSTPQ5tf%i8Ypce}YyE81*BX0LezsoMwQZgs&RXTP>7j=62ZOtGQEwSBdi78v>c{_< zljH9tp>VToW*5>AwC*`^*%~M|62AV}s)Ei}tdU$8 zI(&rz5S{c$?ointJX7ohwIm@kGR*0BiF3T>;eC{W<~b&9uK!)v1vw& zOe3SG&VLcp{G3L4n^X7h-r8@Ft;xM^#k!1EWD`fo7HBV}>6ObGq8zu4z##^MZ+3`4FQb~{fDxEOm3Sj<9g&Iuhh!S*>m1)3L6?mH z$1G6`LT4MLnG9!+QIWs&YN19seH0^LFYTyggxM$c^wO0`*Z)7>QQ48>E#_cR%zc*n zx#+E`o~Wg?afsYD42${M-822VW$+vUUb$gqu>bw)25UI`*5ChTznksJxM|(fIIw7O z+iE0^CcJdp;(p8&6yCNfh@%UIKU(Ebbla+1(R=jXe(C8_mEo93E3!^-P!)8?O5xIF z`#TmdPff#;TeQPd6x(lEoI*`Q;RP!OXYcUrV?)IJ!+JFc8UI*Ec^o6?uJsew@&3AN zMez0DxM#KJw7kbXD@va{3w7^WmH2vCb%X*nuz;84wSROC3KmF4*0U9GdnZ@1%Pjc>4{dmOK<^t_bm z+@>Kj(;4O-+@)o@0!7h6qh5FX!mR%Anr z`q!D5QgX+|^4@NFOSeicwpb-sTC9@GEY{gs(o}2wTI8(mUvitps)vvMb#~@DSnvbq z%jHV$u~;QHSS(&)df;4FvE=TGRdQ>^s>L%a!Ub5#81c|qG=1$S67aLFviq&KZ{P`) z*+;z4Zq4to_7N*kjlFWH=4V@E`7Lw5=C&DUzV$*YbidvD9^GaP`Q8g%wENBSo9$<_ zO76j&H?5L8FjfsUijK2kgJloJqnWzpdHBegqD8D5Bm-BaWc!n1&VmU5DMrhd=FcB*&P-V+G`CuGVhf!*LrF?ZQpXwS?2( znL15v#%kRa@Ak%;{a&$~VwbjZah_UP4^$==DEfCrU)Ugla|#b&e<-5hy^2;>;nfxH zy_)7tYYTZrukG{;s?;qAVXc=aIZVS>WfVkXT^Ul%Te z5tmkkJFV#R*Co(U!nQ{WFY~v=k2@sMj8bIKQBB(M20vKgi=IgRWR;rr3STY*M40Ic zaz8G!$Ea%jENVDQpuJ&JVI!G}n5$#P=w3yA}Olg~W#{Z>JSL zSf#S~`wis6*ib|czK1NDb3a)n^?BZ_*iBRP6yyoo3Xy4B%?c~rNkzw^khECCIb*pd zC49ouX2Q*u!nU-O45Za23FIn#m%=@m>7ezgnAGEv;6@ zc|rHxD)B@`&r8bL2Z+@myCsCB(bXTvVP`D3#YqVT<9ShREY6B)F(GA6eAO5dZBg+#F@{s?z%U zGl^%heF~QlD19e9nDgF;tc+=Ri3U^WAIXLF; ziqGiQP!#74-J|l73au$x+gHBV>`zSjOpya1T`9 zKTFZK)w}myTa()eA4FIV2;6~3^h)b}R6i^BUNs2oGPmMH1hl(%;kT@|T<9-JU) zZz&cjx+!u6ovnP&Rgp=Gk&)r}LeL+muJRpae7>Rw;#4^frVlie#yczDhZJ2338Aj% z-QM-;Jg@P}()cbEhVli^CEYWjuElQOW#BzZZ8K(uF%E%gG7xn}%=V3Dbiw@{%W{u64!-1gWN7ta}QBvcu2)HHJ10$ z*&xedW(2#KiYSPnH=gG_9^n$_C+-tmML#}|!Jn||sg3VD_k#nfAo=%lq&uLt@+#l__q2GTQTVojVUfhgfrg&$`ja7VG zB}yClh_1|#W?^P!n$l5F+0~j$DEBM7o)XPSQ1$p}Hy3(B?duE8ZHAPCUp~!S;v4UaaX*`p+S?CiC(I(xQ|~)-n|R zA#w$MQu+SdeA@R#LtmwLBo~A>Z$UtX^%W-txK&vTymGf!5DYRM)%|lv5ret+W zMdl*@7HeX!P*f-*QWl@N;MgFy4iu zXJ!4ggur-&laQNg=n8IiH*>gUPF!TUUlW)(-zBb0s%sgI-at{_AFldY^I&*LUzys%N3g77y|6WKQgIcAeVH) zaz!Wk)PfDfMJ!5|a#buYV1I%u-iUbCo5}5y(t|u;{zz<&+Bi>?tAD!y4nq5}jN|xi5 zx2jCJ@OH2C)83vTE~{}m3TVLHTPfD{=`Kd%yr5quEu!W7HK+Rss?YK%%N|t(Gqlvx z!PHcEPamtT6yDrNe5D)pfX z5BI4y)rd<2Ymv}^0q$0c>wL25Q)CJHS4FS#X&nC`k90=4Od0$tpJ3`Je4S4!TM!qS zSy#2e@+!J>ag*kFo=AwSV0K$Vt7QFOX?0V0$$pE%M-vwvLYX%uuu0(yh>NoM+NTA- ziJ8wZZ3W8PFZoY<`vL`|GcGINlYNqIW88(+IG_3$rSNk;coPIQWN2MTi$s-d5vfSN zfmA^kkQQn1{q)EwcdLnV(vOVh&YbfAXzQ|6uZ4hNMaIBD0->-w1HY2v;B)K(nhHcs_G>~@|OtA zF*snv&YiZuVbsW^-)#UkUb zQV#b~F2y;j2;1RPxhpHYxlb0QB8xG1_fm9{kGHYN6Z9NK&+~aFu16th;{(#N^Erc? z1g76r`U8DB$ufL-iP%l{sqJyZ|5qCtOI%b;-y7)*6y9;1EC)3UvHc+JU~UR)b#zy0 zbkGc`v|9>WHVRUhrs<@G?RKepG0{lT3vgc0`$&tZF7+v_(+Fzf3ag=alk`rX_7+K8 zq@)Tvbm5xXFuaMYseZ7pXKNEqe!1{h(3EqyWaKyF%7X8h^kxqkDSqR(IkmRnW^+!s z`gx*LT@c4G_P5|tTmiDuxRS_k)TVJ7*a^`s`4rFZL{N1cjzc&P(~MS}LvK~trLI!j#D~U2WRpF4tz4ESwgX$!ej-xSS>a6dN*7NaEklrt;cUYx zqK;v07(gFX&twE!z}=Q{sD!Mxj6=5PbX!+Njpz98x8vh*8ST#I?+uTw{6z_eVMPnD#weaf~eN+kdV9qB0@$2u|&Wsuv+^@GljPPFOlY9&sZ zF6+#tfq9<7&aNHedmEl{U0o|R9ys*4y7LDQBjoqsssIl)9O%In(2*&!j9I3|?u!XB zlssrXT?51kq(MEoLctR(XDE3Tuv0H4wJi#IF-r&Id@tH!#Q*K(>dzx5y?Qgrosr+0 zWCxrQqC2!c6d_3H!+AUpO>FH$JK3J-zOH$V>kjCh&@g*k*7IG*4a*w++{htmBeTYj z@0j`jKL@cHt^&7bL4oUW4RP!6XIH$_I~5q~Dg4eQ*+(=$vzQO2BAW??8bUr!0UTQI9E z)$v!>Za1=nx;JdGsz8ww1BgSF3)OGknr40)>APq5W&O;8R=x=G>H=mQ!%OMb5zWB)OnV1@DtnRGr5v=mJna z*Ig{PbE!aUPn3UlX+qNEQ!W#|L*iALrdH8FdsXzR+|9K&X}){E>oxaEwSTha=BgEe zjPi=;ucH0G_@>Yzxv&)( zG*9Kz|HV((?HzilI61ZZW#a{ee3fIH)F!FBO-t!=ycCp z*l{lMB_|wJlgX;dCUUcmfyV9#IoGYI>iQ_jZ1V(EBP#y5>+`=+E1UjyXIv+F)KK-` zY~!B&e_V=E&CayC^h$zDzX&FcJye^I6=TxM|BX#+HB+oMSHPytLnqbRQq@|U|Gzb3 ze$?Wv=2~$VzoPrtY#wk@YTZb+`F-pEqSjUq-}tHx@l9xh;i|P<#f-Gw_&}wcg z%suZac<8O#oU7Wb^M5e8!&p*vu^YE32{unSDz$E)+BE zlaCc!(qRRR|Ed4*iNxBat^KF8wd%Xkb)NSYZ2GA-H>)@rSiBPM}x6D07rgPUdrFizO=A zgZZHRa*3`7z@d$t(%18VoZ}P4ofnWuYW1amR2j$MP-MqQ=5BvkEQ~xM?sD>SCwX+& zNj}kBD!J29v~N1}owz+!-&59i3#f7CEzgfU$}M(yZm}fwTw_bx{iu4lSib6!w{mnh z|LWre-GAV3QqT1^-K}s$D0YOVJ5b);Ex^e~Q2kYY*sYZD*&B=Hg+40rn9amc9)ZN` z+%Xq!P02~;$wU7ZZJ>!hMjZsvlz)r5YohxhY7XM*q@G{oIo*dhY|E9CJ1vY#O73If zmx!4fEC<*@GM*P7rb@*_YOpG`#wzvrwOqZ2fAI3xny~*fo1JX)43%HVjTDq)dsMU^ z&GnNKCdqqx_*)ZuC&_nulqI8Ohv*^U(_U#oQOedm)y(w~G8LPTo*+|=*c=pVe*a|m z?r0xEuRfM9^laBOkn1Kn#quu1E2-7)&P#4JVIubizJ23%lAL@L!6gct9BHfLE_raT z;L<<+s9}D@4e)4Xo1>JhNX4TIkK}#5272o}c-P4`U%W?jiIH=TsGV8LbC;|24)S^N zm@8WyN_M*Mp5M=YlVf^^ROzhrL)GBjhe|<;@kKxCE!6Z{%4d6rIOQsRP}NIV)F+4x zmV^5YH}(c??P&QmzV|@$zk!yo>SppxHLQbpJ++SL{%=vHiPrs5^f@|>QmP-~q_{s^ zlXZivIhLn+Pwcy(m=RM?QOqAnk>3-uEtin0g~mvV{K8W$lcc~6NOgLXH2!5$&kZyng)N zrTYNBRf)UL_bchNX4q_eK{pBCg!W46 zbU%qtoLAxC#l*<(@zcIrV#*@-H0ET?(~)0L6<~=i+@#CvOD>@&lRRd~75kO0TnNjT zwW0t_dT-o#4}dANw=zCFynhud|7g9O)UR4O!#cH7<=p3OC$(D5lbN{sdo8~@fne-P zRYh*xmH+NHk6w8sPyNZCp1dos`l)nTog$WP&bp`QH(6!Vf40V^zh#B0e@Ra1A0T`5 zckx-Ml#*@UbcZ*VM=eF*D(~CX;>Z?0VeBLiE~)ddQCk z6fS*%hx*^T#}M#;=~m>r^!6?J^uQqOuwM@hCEH}LL4l;1Tz62YOCQA0+PBfTh2?RB zLP=>kbx?7dvq?TMsFceixT6Uyk>5aRE9Vr4`S9o^ml_;O#>;I7m-5U-&cD6#u$Pku zm!RKn$Xf@O@Q6qMN$T{Sk2|Gfqk3;r<-3F18lNHu-Pz-vvw}{qRFs|qlcdM^>k@j? zCG;Y?`9O>-9xQ1tbdKu6`Or*?T#N?0#}|^s$emG%ozjvbcgBCQjPC)5cJRjBdz1Id zPCJlJ$GRN?i`GSE6y}Nk3JtMN-?*4M@mAmyh*jNm9oI>b%Z~7xb9 zNJ!Or4^8g0Gb%pQ4r5M>)Rk1(ZD>(?C{+#{S|vcnAhWH`ihrJ5U7ZvUa&sZFacFJw zyL@11#d7zSb7!;7>$ps^6v%jzkIWNF1|ONT7po#Wu3}{R%GHNOk?OK>SSfn^aMgsi-+3w7<}qsWey*CNO5w1m3dEnCxsepzMG`uqr0lv>oJO!^_ppfsK}l_oEVTzQAGY8;Oyy0KAChrM>-=yOR*A zn3u*C;#E3VH*d>cBg=TEEJH7=g!8WDLSh2npPuVUSXMwt1Hl-X%&>?QaJPa&MJ==3sf($dEGLx0P7gG*
@n}VBVZ+tJI>>ZXK+SNaEKdEatOEa|tfE?3M{dc{qk>D1!qkU`uod>ZAN%}T z=iQWxj}C3VRT+l)o>Wpi@i>4676O^%u~{Ls+Vb5}wt1AI966m||0K^JUCLO8Giv+A zD4mDzcGG#jKXRVOD}yzka{_1U6}DI;Uj)}m*n^lc`u;66mrF=ZXsBHS+fdgioD zDmPNj9@Emk8Hkoi!Np{sv26=J_(e%%GC5}KNZ(|fWUT{Y9F9E7;^Z>(3J*b&Q`S`2P@Yb%)N7j(Eq1DEE=SH^{rPK#oKjoEfwg!_}QK=tSEv;V=LIaV@R zYHL-1KgVpO=+AZ=DV5bEmvb`71621syl#%XBwvWFUb;5MF#L!-sqW+<+Z?kIXUw8whtJ5*C)6N~W&erI?BX!m z7ESPyXH6_hzh0JCPYiKA#s?s}WKyJZ%G)DfnOL*mgKJ883iCi2BWPtVpiG^sxeAi` z1>aYR3*6!GAMI6Al6QH|U>xtErbva#qb5a{EMXmf*0P5E83mR0<{y=3+$|6GP$j*kTFeeJ*5{;{@bRMAQj~^Q~aEl zXKS9ly&!*`Qk?#IK`uHq$Zz?1?xzytS0@#r9~utJ%G9Rx!v%T$)L#SJT)+-h*-KVM zX*+p12j&MljVFEOp|M?D%JD!YMS?*`v7F%@CFFxT&UnuNuT*IlFB-}G7JPSJE<4S) z$ap2r=&h02R!NiGYnrEVrNX1O+(eR6W1FeoYIM{r)q1F6z0*mdzzYA9>c}ajpltKb z%e8yaDBivESTQgY;d z9^74dxW*1t99ZMz%F_ptY4R`AOOs&v`1BTJiR?OKjd23kON5>h?rih?(_B~SQMQ-F&Jec&Cum8NLO22r%lqW_W!o0eD2H#|FO^Q6EZn8nGP^e7;<2Nf;(?O*9R(keFf@x9N z+UWQIu`a+*?`hZRCzNYNJL(9w72MHI=KgZj%%TA)$h0k+iiA5Pti5CZQS^==dB#ki z!Zm;9&b{RB;5>{?*hYE#%;Mx1`OM7m4eTHXm(r{*4fv4n(h>A8n@j7J{i(V?h*No` zISjHfVOV%~UaV&0(ffQG?t+bPe8*_5;$_~Alhr1TJ|s_1@Rgg*YS+3mXj_F8`!^0k zPVMe-hxujxj-<@LMYn6B9Uy8xa!@wUYDyC1inB}6+!=DE+5VnbfM&nr&hZ7aTr6Ld zvdv56g|ppDPd@STx3o?(ot?VC%9Y}YXP3zvt)@)=BD`~SYOHDi{L*KR38HOD~sUaIsFU8@8^yv zI~)1jR4=)4;zD{MP5$1(NAA+WZ8x2JnUL-BwWKg-c^{XIH1kSin`D=HUUp}9W1dLg z;O+scpUGmuN!pcHfr|vB$-9z$j6Lbft%O?ry#?ko0a8q^aC` zi7%F;k@#Ccp1))e*&%;g@;c!BBOdUK0}qr>H6*Xcw)nSJuCmSLXnRa^P^3CK)j6q;wrnc5P8P3QY2K~ysRz#eX~4$ zouBNp+>5M~t1cgiDQ(5_io`|ESzered?5d|ybcl}zhwGu-N<8@V2>R;#uaaBciU!W zEwbctzjTy)tQb_{(uV&km%Ranl`3@^cNDEXXP~mWM!vyR_C`0J_qqd0X10098~NP2 zK)G$ItNrEQl-B#gNG)1HW~pPz3VGMcqVnstt>sZGBlI8ETb9CFMBFH8=chz}fC!by2(Jp?S61H-3`!z?M<8N?Bu(86;$}}D88%m1k$F9|kEnOR- zKf8vfxv>QV=jC;wq>8+0U2)7y=v0Rq?5dGef#2ntVE+E*!_oqZdxg?`PGwctxYS-O@0eB)`6sY zW02nj8Rc2-@Z_? zM}Dv=l@#3_w>gFce7nFymvP`M-)ARFg_I4qjjar7=g{ix*{Qg!S8Iq%#`AaoZ;|x!7GxL;;Y;&s=Y-A!I6RgCwA|JcV8}W}4 z@aw$U9xPIWFVEZ3LGHD)NWd_wHA4Z#sd=Yu)-n~lxVa(AJIfUe;(E!_{ZsO(opYR@ zAl74ink>FN!dB@(K_Sferg)Elc_cbG3 zj8yL9-HJU%U?PztCCPMnx6Z%7;Mqa=)!IO*wwp!m)GP2Fr zrpvw4x022B`HWJS-#%vq$z3y?NqKot#vuCipnM@CgkmxIlJOSP$X}TO^vkr}&c~_{ zBFhbr_kqT*$6L^+`{g&sf2NuH<(RD2q@jE)E0i?b{W8mmkUG1+op2^JXP@kFvLnsf zC-*tIfu7qd+n?G&O2}!a&S8$4eR=_Ll8c^cM~3ena^|)J>Akzj)$;90Mbr3Dl1u5M z4lJe&DNm1eVC&0}jUpi7qW2$YY`{bumQfKuSu$twd~iLWC^|4gw?J^7LvN`Of9mD-il;# zwMioB%WPy6!Vok`F0D8Wt>BL|-LepJL&xewwl!Th5k03FH?025Z zLqL0CcCSDKSjox6i;Q74CZj^#S<+-uj=t8h1CvRM0{2elVGa8rx9w1rZO=kH!={i! z`V5PXJ)eS=Av2o=O(jbM{<2eh@g4q5MzYk%0#K%uw@OB~xf{DZl?*9%^f)g!Aqvy) z0Ug%O{Iksl);pHWq%ZaCNi4ZU0!(SsNNGxsWttutNoze#%VkyKN&DgfpSfjBQ3cU= z7T^{L7TARJ$!;jL09zYRD!5loM-hqGQ^ZgEq8raMvOclL@nkkl`^HAhB0qpq8t2U zV!zKNztLx(S^Fe1mW*RrNu&bY`Itn;k+N*aJko@I+{<>&Bb`VaRv?))A@;0wGAZVr z`%w+TP)nZWMRC;AK8Dxx<3~0lnOr6fSm*hqA}Md0J)ayPhF|~4vn{lwxv9rOQl1!k z;IL#}d@0?<`pRSid%b{n{zaq#I#=&SCH zD(PMXm@Mr)W0SO3GP2DlQcRQZkv`AX@<3#{fobmo1m&px!tOtx?eJEtftd=ghM#Yd>bTo$SeejDsPp z>;aNPlG)b-a3X<~I7ph4=4|jm;!kR^xd+L3df^Nshe#+{&cY6nl^A>{50R$m<*tWG zFxkSYA0~f#|C7L{iiC8&kBC3aK{uY4Hj4!uA-%~!_VW=qdi5mRb%a#ID)8(G`ICg1 z4j#pDCBL)s8RQ~;GM$l3Ozy2&%}f$jd2Bc~HVGGCD#PqLN9r#m?dM3H^OJL=7Pv-P z5o~cLX=Q)Rs7=U$%<&lU#DpApj0_{W#jghn)sc%QW`vIGJu&Z(1JT z?|PbMWRXIIc(LRYB%Ulbxn+|NB)|oq@$qi|z#W(@ec5GecA~Nj{4gO-X0fM8agu6UehO_)Ra()vu=oVC$a5I*I@8c|D&e8bM2z_LTBJO<`$fa?ie1H^pKiTm77Luz%N8No{A}o|7tp1y-v8 z{o<(v{=!0A;Oi?H+2*I6S=+zJME4S0&3eDpO*PTVbn7otp9K1>RBeN@Thqh7bR@!v@J!wa) z`7Ys4l=aHsr=LB1J4{uAC)^^_IP+#PR)$b)o}*hS>6eR5%|D@oR=p-`x|_IUrbkU|Wf}Lmr>qBT?yGP;H_}(Hkj1tNWcC&+ft6IZwX4`zJ31e85EWv0oI@4057u)De*V6A(OwC>BYeM`?wcRLIDHqne z2(4AMWd)@t;xnpnFJ6thTG~~amwB4sF^F+QNpCpi&2oxRfBVnnmF|JgQiPtsf_})I z-o-|Ig$GTf_sf|=JZT?-MSONqI>zs17%wGZ8J?Bm7VfCYh1TS`Fy`e&`;z%=iWl|6 zK6-^09YSf-GeNTfP|GfStybVl<4*-seNn zw%7semv3EgM z~6~pKh7xCzwE_m zC~bjLN$XJBz_G8EQA*o3Un?X#T97}>~&c)8wl3261fInERJ)p57TYGBy-pW2)2fz8@4;=_IrIp`Je+P;NG6QVm)G9lcr& z+6?;+V+}gUXS)MN_J6!nwVq*)_M#b$m`_dWLpHJ+HPMVW3bLLxX%(DBQ)<%oKD!G3 zH}#DyuO?kg3UlB$^op~JwWy1{dR1}uwid0apK6~cul+TWjj2ro^{z_7gWneGM1Ezo z4h?{x!F4d9Ok%C-&|(-mgX_>>GMmk>L&LETI?q#nJXOBXsOdW0_;B2Kesh79uL~zL zFR%r5C_V>U;KZiarCo|fUcd^B2lOJpA0eUYooajpP=URwORIZEok1*>``EaPY<*I} zsROHB4^Bj}p7rQY#@JRo+u&} zzECqw$lfOOQWg3l@gC|?D}xuLv}N^TTqH?qa297-K4y=8bmR||v`q4t$-332_34vS zYN*#Xi)h&FS5EYF);k7a9Dt5cREGEB+Z09;r4eW)=ndq638ylyyYuU058oDCPzgpKqR2K=Wg9X% zoQ9MrVpS;yl>+=4Mol^hW=kK=v#a6Muld#Uiq-K2ly85$h=k-BmBQXX$>S)$RzF9; z(_#4ohZ{^)vhb;|>cDR-Jc710wgemhg&Mg;#gk|gy)SPQRHHU>p5tJu%a*F(lD{A` zlb?N(J%YJ0JlIv@XD*#2c_5vT9_N!J72-L|qB8E|8IWwVhg!|NbBgk34w&;D6z88W z)#;c^L+V7+vRTQ7w3ugXUM?T)Z%_l=a8D=P0#4qo8`2>A01NVxq^WEQB#m8RiQjJh zc@la+8t+W`kEP;65-CD0D5hO@B<@jQbqEAm79^g#i~8wORK`O+PQ+13N`&nY;rPh! zBuS!*)$g(Vxw<^`QeOU3UT(JZ0!jW-KUpcxpMMRJ-c^lrYV=Wrf62_V7JoGLz+uQNtaW5y?qjFR3`!%H*JMk{9(jfx9O6k0c zw2wfa=i@<@E+o)?{Jg2sI)Od~+GyoJHqpvFpozZ}=rY;)R_+P(V~xHp&{s72l0cu( z=rf!)TI*@j_$)!ZtwtXd=m?G8CD5lc@;%%r(18fCwVKNWx+UoHF~N*_nM>e(*%C9MjHj1YEwXrK(El6b*Mmp&t_v$)On>G$#4Azg4hI2 zyP7~7HM*QYFVkEI5$GX@^KJVHba&9Ud8L@Zx6s6k3UpnKb{FU++7#y`(6cn!Akf(d z^IgepjLRC<{u!ki|IB$SJz1;gD}lb0t^}{&pZIDO*8X1BVRKRt8Lb)266obxgboVy zC5_%A(2WkMjnqde$7SY4UB35=1hE0gwz`lc(B-v$k|5Ab3xP;a`pzIoUP@%ovxF%~F@WoW_i7U=d` zwF?XMd#z(h0v(MqtSGX zyE)3VHdGgZ?x4|~1Ug#tsI@?E)99uGy-K4S2()pd#@7`1of=(Hpf754s6e0A=&}Os zqqScJfj+L$VMc-9tMMTMeL|yy1lpw0{sJAYHHf!B`)jnPKuj@`(5UgtH2$?9o~Y3;1$v4`KNsj08ZAo4YV-p^I$=LA zUmY@61YYnwN1*R%L*}?ZU)Kip0fFAE4bvS0ovG358|sYtVY#L8%LVZd8a-d2dunvN zKu^%0Cfo`nPwFP>cX1gNj8n#|> zLgPaOaUtZz1^NqX&=HF%(aLZZa;Zi;2((_K4Fc`1(L|snjs7P>>!qJGKR*Qu{4Qc1`d5KAX!I(9{>!iA0Y5m zHSyj89jwva1=>@iI}3CHjczZ{U$m8`bp-Bj+J@{6b;~!f$#!+CFi+dDmlxFQ2#cdY z*VX7Sf%eeoP=O9Hv5Q@(hqp!B3j_(-I+MElT)RzmWqyJD-mDkI{(z^}PX0Aqt4JXK zz+0UL-wE_gjea4}xjXWu?+SDrXxr}dlEAP2BVYWaK!?IpYni77dcS5|q>E|NS%UOG zTl0+^<+N@0d3jGhe_Rk>qE+aOKsV8hX9)B}&G<2aw%0rq>2AE;%BVpU`R-boqVX2H z*sku_{>5nRc~B^1nnv#v=vf-QOQ4xXZx!fJjou{Cl{9+2QQ&K9{2GDoq0y-VeSB|z zh?fcUS&d#S&{s8jp+G+~YWzHbpQO==0zE^cX9@INjgA-S-!*!=K=;?^*l=O{H9+Gh z3F4hKdb~h)(de-P-Bs%x!vwn9mi%Ce^tWx=_G_phUPT)+eFVCtMt2tI0UF&>pyM>U zp+Mi$`eUtdVf&>#9HVyMih{VCW~G!shiP<4fi9#O_ZH~yJM*jWA<%cWY1^+N0)I{$ zkA(%=v^n2OA%Whc(UL&V(CEAdP_%Bo`)TxN&_)|yMl17gLHxT$KNINRHs!nWNT5Bn zW_~EpU4GA(zAMldFd5q>%sT?#O`CT_yG*_+(bc|NdD*}C3^Y8+vZ4+j7 z&B`S~ytgJU(ry1U9r2%O2aOgzEX1S2ocmIP@u^T`OHKN|Ku_D4A44&;OSPc96r_)8 zwSO(p)3wMx7wBGnHSre$e{(~AnWBe1v{*eBq~~cy?g{jh-_@1*+i&X1+=JvB-7knW z(2S-DbX$!UrAuqo-Y!Tt*Xp-JpbH^(wvM({;Ad)~-Yn36Yh`W{=yU&>uAvzbrI%^a zzjGr4Y&Tgoey3pkrdFY2H6d=5E`p$0Hwi}s`osEsx{5$&ZBjzrW)`ylbqlVAP}Vc8 zSz8J88;$NH(5E&q-+oxAD{J)}BxE1c=zap7qZ#WW&?~h*(oCSwY1M2X(AiowYZ(PT zN?T;g3G`ZRh=d4qWi8@90zFKV_7v#vn&Vyq9o$pnMe(~@^@|GP*ECwR;-!`8E=cd+ ztPYWV>(q9uDdjis%lcYpc!6wdXLv8rv6^SE1$wU*)29O6M2qQtfqo_okt+hfP%AS> zp#8OWJSEVDH9A|MA89RcT%a#&bS9^5bFr|vWC-F*HF1$Hs#RaHVjVeVt@>h_4>USU zu${?i>&Koa1^$uNVCe!KtywuD(492;ut4|HmcK&+{aK^a6uQ6qv8P$%_X^@SG^1teK!078Fa21c|I+B2^#U|22emS<3*uYZ+Cj8wnO_u& z|GtB?QoX#6%Qfod{iy6nvkosjb8>JkD>9f?2pD@^c^Ky;|C;);sHB!zcwR>N350a( z6NO_~|G~6M<-&@IY~1$14@{1}k&7p6_asU+Qu*UnX^B#0TW$2d` z?9*U88hl{|3m8K2O*qzi2%f-=V^cYFVw*UWWmkBf9m6x__zkaLSoxuN9(51vh9sHC z5{5$NC-w)RO6^}@U((@k{1?5^GfDcmTzN1jE+9#IWxXt#E&YjkM-NxCrL(^=w_((~ z%am)m*6*(3p&mTGJMBxhvdJQ#%u_c=^jGivE zd>LP+#yccQvZ?cM+E2f4G!3!~^TO{jO~CsU-a{YcS_(bPwe-S!7v6S{axGo(HsM|1 zv8i=_wX)T#A0cS_W*Z`WftDo@h;_Iu`I#6q^HF) z2Jdo3EtVa4xA3x99^<{*+hQ4C%wqBKF~!ZIvuHm#`fH~eqovuj^1VN+%y^85{pMZ6NcIR^~=_Pc!&cOS?VL*2;oo+s`DzNnh z)D+kU7y}#&Tnwy#G1qbk7zex!oCo|4WWeGmXC1IV@DMQCh=R`HA{Ll)IoGltxbq4; z1KtF_1Qxi82>2iZz_P$vz?Q(Kz~R95zb080Yhi(`NTYXakM z=32S}w*!rnaMAZxt|b*X9C!#A3%m_naXZ(dL(Pu^{eT~V;lQvvxt2b_F~I3SBXA9H z3DB-2`p{iC3XHr5M}c)QS;PQKV4_$ITn9|`)#*Mw%r#mL;i4dBjXlVi1iTJB1pERF zdyJ4Fzb3FQa1t;YI13mH{DbHF>2!yAKCtN%&G$L!h-SQoeh7!54)7xaPU zfvLa<;2~f?;B8>#zo8GjYQ#nHK%Gv01p~mQZ(snp0~ibZ_7(Mz|!Ag z0C)iy3+$2$1Hd=HL%<$+FaX>E{0?l5r#y>?=yV5xb%B?G(ZIifu|Q`$sG16_06YYA z!Naw;fs26OLoojJcmTP0DTEkU7x)!VHAe$$;KA!y;3!}!a1-zl@Fws!P{Om?-+|rm z6n*hfQ~+2PXy*!j;AuDL13%zV|5RWtPv`^Z;Tr+Bfz!O8A8OR;J{N<5(opb$0pL)4 zO(7b11sDrlif=%q0*m{@0B~9e3;=fmzXMlb$?^-+>B7h5S;B!PU?1S5m^{mL;EA8} zENg(JF-qS@rbpJet0U&t>1Hk*h7+~$^ zFaTTy+ymVD7YqPz13v&iynq80(G)MC58U-P^noK@K_8e7TnsGn8v4Kmz{|kadW+=? z(9hn8HMf#Z$G>e}6F9@cV(H4$_;r9uz&^lbz;VDd;9}r);9lStV5T$lE2FcPuvqE> zoBCQT(ZKz{Sl|<2DsYCM#c~LE7kC@k&);JC4lH2|uvm&$(dn82>jHNHqk+W&VF36Z zm0X0faR_6Mc| zdo;IL?gN86K))Ks|5RM~;o>VW9GK7%zo`nm3!DkO(amC64=jS;aySlb0DKJm3uspz zov^3H5(In(i~`>5ZLtgn%6%-BIlvSBES62cA_Fa!lfdI6@Y_hh4kIlV`x+SkFUMLe zp*3{60WmNDWWd2dr|~cV90}Y6tTzD$fJG<40Px;)7^sORiibY1(M;$AuO&bqxM>#j zfnVo9AJ`@l`oJ;xMG!M^2hhG2#{b-TFi;Df2p9#dkqiUC)AL~fSa1Oh0JkoL0bmjQ zc#0XA2(+(_<#!SEfjfawz&0}UfscW6fKwMkANUk_64+r0nh=ZE1mJfgE>_{*Ar%II(JNt~o=&#`zp+ym=k=ae@%5xztn@+ZOD?l! zNclT#$hGu_54s3v*IBfYlYw@3ECAi&pn?iwgs~JZeU3s<=j~6!+`Jv41FbY_Y z%h~*r+p5H&6Kls|4jaQ^Crdd@OHr?F?7(4KlGfhFZUXW92w!<_8|Hlk(4N)e@Q(FO zr$yQ5Bh;Qe=GQ*b9+e?3YANh~Xo4)}vbq{DVs?t(A! zW~8GAeh1i=bco(&7diWsz2)$pxn(G+N*tcBb{zg>V>ldPDH-tlHrvlrx7iHcI0d2B7uuXs%9YElfSQS1RId@q`u0L*9p944~}4v$!0 z4*0SrhnZ|GhkY!A!=LN{hx?3V13oi<4o_JGhli{$hudt1O0ES^yA$kY2Ksl5hfY`V z1P=LP*PsLPhtp!@PhsRF3_N500KG?cF57VzRrr9c7OXGNTEJ#-n9J63n8q?V#IXke z|MI7C)cL{s>U`^aJTbE{|5K1|!y-7Kk^uiQXLBtrei(91@`qg2qBy9rwOo2O%TSdb zaCpPWX+Rg|5Ab)xQS0Up=t%U(w)@8c)&_)HSxjolY} zz_Wj6BnR1HH*(o|TlQroI|4iz&H8c}!e($7%GPoi#xel@WpL1o|H1jVeCHPz!-KTJ0ep zw4SATnjc8RaqgS`gZ$Kdd45n`!}^|syG?M23ZtUgU}`g46}H;4wGhL%voknsVh=b> zX5>8JPv*~I7K;Gr4ey0mx|hIqiOt~I@7P)nU0DW))wI*EmGEHQ&M2a-{2$R!v#&Jr)-PNMS?whyd+ z;*vaNzF==I!HanIk;^P*9)H3M=MAj(pU}jYQ~v~emrdp{kS*tMknIQ1BAeJvF4Ka2 z<;kL3S+UC~)O#zd&9TZ>)*VPv*yPJlddQXo^f*+oQMWO|hvV29!*1}biR>$Y*7<`K zy8?xytkxB4^NpEQ70lV-Gm= zV&n$kEAt2VOE_Q*{z3PreBBSwZN>U>>7{H2hc;|2S1R~6&yr&&6sXU(<63^8VsOIU z%O2c7(;hIeSGW1ja|CTbkdB5xuf&m5TZJ>*SC9;%oC4!>#f8;4Yu#bF$K z!Uao|WZUm@Bex*ffd$_})-D!#3--Nz+0a`s*`6hWrR9Ry79?rY5O$i=9oTa&e3uox z4fu}2M2 zAIX5Oto(f>p^7AC$q^hnvSbct*fuWX*`1xck8Q~o_Ewc~dw|Rkmhu40$YUIJ`?GeO zS;oe2IK(q}7uv;P47Fm)HDn}I~-c)vM~@q z=u8K!hC@TP>k;({Zj58C>tKuJ z)eqhHd44zUkNmsrBG*{N-g0=!+#UnQvq}Je-BcW%e^{l*<*(9qIP!L7V<6VoG0tM? z^ut=vHh(Q>46z6t>w-lW`Y0YKYfFFRH$i?sJp}UCo}PLx!PlCZ$e&|+`1@`zMO+$E&>U-zIisrT?bcB+c~pZ+cX6v;Dh7+rLMgV9Iz;7wPFP7v}MS z4s>h#ld6uOD6gMQIiKl0O48WYZ}`Cn{Avb=4<^TdXahZ&!dm9hHg1EgW#ayw@|w%G z=Ftjn%_peI!d5z(z05;gM>0nXZ4*&@qDqHbbEjC-&errCYr2p%E&nUMiltg;Z#eed zLVev%TXS>yI*ZrIC1r7d*}9~q{(Rx9Q&kRkc$Aku7C-B#FIm_#R!!r`s=TVjvYz1m z8(Dc<$*ryFNUQC|ES2be+wF-{)mmEBarReU*np^LO@~?2jjd_hKk772S2J+uOL=W! zj#OWP^kj9ZzD@hVGgQ{anmfsw_T^m>FZQ#FxM8Qf(yeJ*!@aept6B3y;@KUlZ|ych z**Nk0nCfdPtE<;9z~Y#r*Pr*75>Sy^j;!mDObMG2G z#R|l)>>8z)*XeNQMrYrM-JhuU3~X%g<4k@YiXn}09Bt?0rmu&KNN-2nST9_J>qMHo zC+SDnInmN`g>!s@bknEMJ z&n*Dq==J(afcXlT(%0)h8xXCu-}UL0+Icj@~< zZ_F-z00}fL-lgBEC(TTed-Vnv=ce=dcyIc74x-jHlXW|;-;ST5w9nGNCyo&y8y6@t znc)5%E0C?9NE$O^w!R8rw*sbT+4@@qwvL_BZ-Y%X=B)l3j#0zU>FdJE=5zYzgan!7 z3;H_5;npmC6oj%$v}4yV>MN5hR_Kx*kFL#Oqb{M=i%jz_>DLg{y4+>`jY2{H^u%qN z?!EAbxm|(PAm(^g?;RHOq&KK~eeh=t|MMIElrDs^&=+%1n}hSOGt@!s z)L|-UXLj?ZzMfOzlI?2a)O;~caVqGRz6*YTH1-x6ydYa~OW)Gl_V0;trdM7h^;a^s zGoF}OdRy<~)|`+q&@^SZ+gU8QhUTp7ZGCqV%x2%#*9K(X*0*yF?%jK|&UJp#m4r+7 z31)$J(E7ow%N^xOCRaW5eOfDtl#GhGktaT*=5^xgmW!SbDkj9+|W~f3!Heq zJ;k)rg3W%0vD3nI{TaHA>Z6^hL|4o_G$ks6**(_}C{TwCosL_ZZ1i)zkFhTCLc=O` ztv(xdt!GOQcI${XGSnx9i|cuQe>VfP3=N12UszCMw?bH04B1#ge}R3YS71DK=KcG1&O47d+!HlA1LM0p{q~1Zj5u}UC!{#N?eF@ ze8-D1yImc`1y05}cEZwccwTs&9Z#pnIsP&i>A$>El>D?f$2=^vh8NcSopJuvFwGeL zb{q^B^eN`Jh|UphbcVOeu+yi!M7kl;@2qL#o4ANbPJFTwAL9HU@J9M>O@E2=&xuDm z*P8ww*D?YQI1x`?M}5k4qpo33czm?54t^*cW*YwTMla_tlDY}`jyVX9A>CT7G^67L z?q-J3=C{%D7>`4ywO*Tyj-k_$K4#5dV{|m4(G16}aav|{?8@EDvZfar9h*%<`h@Ez z410abJfmX}k5jfaJ;&(i#_gT-=97{>1=Uh|R?Sn^z8vMxt7SN&REwsxRl|0{n%h#> z&uyT;nKAL5Mu<#^E6D_$y_Yx;YfS}Bv(R=dd@j2FXv zLj^SI*RE(DyzoB?lov_OM5d$612NxA?Lev4z|F6!25uhO`yRS*ob&oQ*tGJ=e7V(3 zAJqyPJO~r3+FnMbt~8~q+FaAE$+rH@c=OMw^q@ZCnKD>yj9J!b_t!OCIUS!Z6&?>e zJld-|sHXWppWrW&I)#@I7|bV!BBj*XrRC^}UDWJ8D(L)`@bzO|Utu5kGn?~D--xs| z<-F2+@k#K>YkdTcHl^O^2M5uJ{w&4a&V#&W8{O?{<6!X6-L5X_Z1VN68`0j&c23gf z{-J#8PiE@}*_k1gJlKv!7qgwBG^qv33Q>3%tDj|8%=MCOiE!degmlR?ILj{F$#8n{ zJUn5*wqLUIF`h250J}zn%RdlRlX41%p(pdkXUZ@Z8&-r`H&D?fAmDCIOxJ3t#H_AtXvQ zj1WKL7J2`Y)|A1R;chtbxk*~nf@fhmcS?p+(whDQRG*!WbMtFvNP3<-SXbZ!#ghih zF-(`_uerY|<=}Ha800UK>OTxC0PnWa^1|-CJ7E^rwd;y`JDM-$o`cw}KkaH4bcO`a zWvt|7yMfeeAe(jBt`82A&o0}Yz&6LU{iP#rkP?PH-{gxfCE49FFIoEnpL)^INP*19a9~Vrh zCsoBwx&!s3isTM^QBSH#2AhKFOKAj$x;qV|-*G!7FCVOg5U%w2(Fu45e|cq>-dRSHlFXbMKhBwG>*pYkA&i`jq1ni)_1u;B;-+ zvNn!SDQOc86-S_FICNLKxIX3f#ELyuAl-vCY$Ju@-q5f%QWN{uh9>B74ac(mZKM#_ z)}f2ATgBvDWDkrwv}Ui{NFMkQM`|lIEzsET0HP;Yhqh2J#bVn^-XWy|x}vlwzQQ_` z3g_1yYE0XJL}?$MsFAoD*J12nTd6TQu(X|Y5qD@WwL^{Zflhm=R$*5U)!R9f3>8mu zSvS_Qz2xQO<@>lXuT}aaUH#8&Ona$FdB4ztv7B0lx;yTuhciG{TYT{Uulo)pwdrQi zbJ!p4r4J}&V+Uy%tv%7?-ccGwaC2j3C-lYhY*Q!64=s4A6WC~0tFu%O8{UbXrPcTV zsc09e3FfC>U8E2)$du4UG7wVRG_R{Pp^vxizKzyC zlDDXnk@p;Y$Rrgrwkxep6VXK;Zo+VGSGOp3otVo)c|%K`taDj^-6pb}=QlMB2Gqp3 z=C21TU|`X)7+8D_lA4y|2K6a9Q&o#i!?{Ilil&D0z9Kbq;IjIZ`%^;)Zqey=Qghbx zwB&EBR;nVVw2pOgKMX4se|^rMC#P`hd@CVUYgiLGe4Z@Aw-QpderkHUG4wR|uZDFM zU1$vL#?$q!>BUA})69+7WjY%+t8>yN%%c2-b1i?d+|!biF{Jc+%Bvq;Bnx6vNCdZz zz8ji_ut}w@OWkl?)1BzH4$c?RE%{JMiRUKZdA`SipPF7~ z4CR{{$;Im7R-?|xlz2vJO6aG?rb{`}o67ji;h070L?$lrHnb=Amkc);NsA>f4Kwgb z&q!kEiK!un7+T{#xeqnWqb@brW@?y>&yxc6hKf|G#yaT@J;({Rizlm8VUP5Nbx@sU zXJ~~Fc`n--%JR?TB<%f1uyDyx5yy%#k|BkBWO{?4Ia$wI8w|f1paFbz*I%(pk}qD4xwh=PWx;Zl8$Mvz z<%H69u)5BM#rPQMqO-vtZoYRmRKsjs+Qra`x;V1wE{1gh{NFe8SGIM(p^xc1NH?Nb zp(JoLrKNLO7uML-&fwiF*?o>Kl=-^D#1$6pZa6`1vOo{RJ{tHByX9dBB5jv?8vGC+e@{g8 zJZtZ12&BQeY@(;360MTUwt%Ju^4M)p13p2>WB;$O?~bqe`v1T0_s#qLzW2t+Aaa8c zB0>5F_@85hFE<+Ox!7RofFq?b@1B*XTf1(H7BKtxrpbma4s5{GKQ0+yofo7n^V<78Ct|M3=cI^ zwT_;{p=LpiKPhn5&AC)3Jan5?Se#|U%qqI`5xRykYtry`m|04j@WFep~XIHDe|VL|5QX4W!Ch>xT59|?KW-}H7n@l?jW?78LvlO_jD*` z-qX0h)im6!#u9ovoayY0_2Dez12`eXAlw#QHcFS^%&!fSOPFHUF0+K$isiMhggK2{ z7IjOSF?yZf(XS-umo2cRq$J2JWFvX=cF<94>8s#NDRh zkxcR;Ad*?T6Z<30SiZ^rj5Ncw%dpC@EKVY+jG4&OD6`5i;Pu#2hPi(Sm&=%q^pKM% z9%b&K$DAl;e&Z9cmu0%#Cy+qN4X}x2&E_n|Q)Sr{-i9-pAz#DjXfuO*ZkMB328mxH zw46ClulfZ(C}+;rKJ&ztr+w`U^olW?a&vWij9Hc25I@JTUcQgQ70jW`ikTH?=wCdl zzyz&AXhpLUH!z!2WHC)c|B7Z^Z2>k_q`x7!EVy(yDp91MW+fKv+Za&Ed|x-S;U7yM z@1kO?S)OF4SaX+NY!@7r&7FL~?5k{^U`?46XU=53v8phCwjjC+bA7I7Ocj$Kg--&0 zsLHGvituVQv>gqrnT<-d`#@HOZmLGuhuSs-1&X#JI#;eBeS?kF%+|VlDIQldr*Ok( zY<06Acjtep&b5P`C|ScC!OFL^22*+$R!zDtVM0xFF@q1N#R6T4-nGmrdX4$`qZY|4 zNUv>n=Q*T9wb{C#AheD-fID(i>zH5Z<}9SvWge}>kh&)KL}&8@%w}(9%Y(YiotY>b zZx+`hrXxAttixiO9MATeie2$6h{dQ?kJV-zmen)+>dwh{QO``~XX=ycvv|@ll0uKo z!Uy%uSTSO{RNq|01~jk%E6xGTX~66rg#!(kqQCH@0n;}eJsZ;JODt^2-mL=)CNQ>F z@MeP9Oivz*{R!rBCNQm$2`t!Xf!S)nN8R{!W%P1N71}7`+!u8Zfwrdwk%6Fr)u{+(~`{( zHQm1lYBXg(65Rs@JDP5cXle#<1#)tV8HV?pn!$|4o~CA`zE<*qACA;wof?aB%gCUn z{O1#?T0DiS|L~(`R1NJZOoat{HM68jzEHxg5G4Gg7|zg4wXh|4%5$dEpGCCFB&*n;RRqziEb zbA@=0gA{tzzPQzJKJW51wU8&-l&=+MUO z!FT1pHs)K}MU-u8PS;*xXIu8QeNns}>+ukL(9T?<$29lUYtJ_1j>$bH#6h>rW83EZ z(dIByc;OczK1W~| zil=Bup~vyF^IfPj0BZ!g2Hy&C0sjgzI1LrLlI)BwUCnBGPzn}wWwUt$(dit=Jja%F zrr>XUmu^nrsfyO!m>H{ZxSJWr<9YYH@#R%086n-puptqxyR+4-#_H}Ik8DRm50(lK z0QNAu>6NSCMh|w|e;~RiUn>34uP5IzHDa)|rE7%{M__nwzTeJZMQ;ua`oZ4E zjL?gPBdQO(ixkZ7!>(i&cJ<*3=?L8EV|LN=mqzoxe7QR#@n&BZ|1iAU*X&U8{W7Xu z$YF}jKG=55zliK{mrV5k34WPoCswv@ne4OT-I$+g*3{tnD$|^=X_=mm{mdl0mW5RV z&50C6-(*f7KrhO%!^G2e|N-DSiO&pfo{P~A+Etal4Rj0ND(sm3C2?L(CQW;*5cwwn$3YVisA`sj$${F zi9SLs#==oFcNm|KqUPVYO`(_k4dJ7Sc1G%GqKhzIh_A7YBJBEavJHFt%Ze^P(=PhU z-*KOw^fJFAVhoKW@o?!FGlbdQk52aJ7-Pn|*Z=;%eSd^eG* zrOgJ{F=H(03HVrukMWxjw-G#!0hRp&iQ^dCo)}6e509-C;vCKh(*IAqBB>|+iAv+C z^fuDRlii28LfpYY3O)J;ZVEC3p%aL%L=z#t!zcp>ME z?jnOiFZUM~zD=?hJ{RbFxGlu@C_a&7e%8Z@w2*uY6DYZQvW+n3w3mgr2gf8TmH!*H zCXwuoOghestseJ&2r%~KT z5``Y)!iZ_K*azzcx*9n`*?c}1>>hvIo5$C(a|S0r zw-7pmLEOT^8T3#lACAnRhwivTCmT`tOp3E;L7_YIhIIaVD)Uf&VPJ zPN2>#x}ISGgtWJoZO%Dq(P>54lNuk>d;?!&! zk1vR4lx$ID=aAis4s&Sr2h5<*Z2|Dip;EN~To-I#6rM|XZh%ZskGV|HF$|u|K#t*$ zxzu=pkauXkMj(>kq17Q6K_}bQ1|hEDoFEGaL7zvN9E2*AY*!gVti^(P3_1sgg?NIy z^Z15}e~IvSbEB8~E_HTcybyn28--r-6@Cz8XE^2)U4)uKe2q+sqSs%&-ZYZD4a=rs z_k6R0y$t{PX}(#JU-5l?NFp@5G8eleaslm4McW0Usr?Tv(NI6ZdqV3PPEjk&ru}bQ zVH$!LGAA2oXta>(6EIYWkFi#W+c-nPQ|5R@B{sc^i-@j3x)A3umqPcq*G4>9arPE~l6EI4H!AxIv+p zE(G@qlIdu&g6Kw!65>DDM4^`{jPru*4*N=?n^8rGpOHbKmkq(fl_Y!ObAfKbZ6U6q z_IZz*Q z5ee(ryj!m}Tk|J~)~zOX{F21Vt|7J_W7ZH0{Xt-1c(sP(hKFeNKCuBmO1<;%b262D z1!dQo1N2X>NN~qmdR~qfYpGb^Cy7m6N9-oPUuOBj= zIbPZ<#6LJM$i(L`J|H;(RX-ql484TlH53ZH@jo~s$ceZk(BlZ-Lh%YMDD)=(;_WRY zv#?#DC-8$n>D9HBXbNg=B{~If2yqI_D0KTv92d?Vc(j%4S07;JHWB&L*tN~P$X<2E zcJnR|Lyq3TkyYqd*t)~)%U-_VPR`7BqUui07-wSKP7c*JVAW2}pr7E=ot%&E#?_r> zZ4PV0c5!sL2x+@GG0XA%u*)pU5g8uu=DhJZs(r}Cg9pg^(Cor6e*BONIA_r;o74MQ zn32tK`4Vi;Hj8ul<7BoOrC0w1_p;4~5iLHEy;qd%PHcMb93I253O*<*mSo!cPe7ZG zX!r`&e8f21#iftT0VTw@;>DxZUv2i`w*Odag{zPMUS#YsYcWbI_i#$l2a$U@imrJ8 zP4}ARblW}*-^(?$EI2+k*XnWE*!D52X?r~X*i6+qs&2Z^e4pzlf9_+HZ%8bm}piaqIwx^sdc#d4Q#PgTIo=ayyHzLU=IeAXh$`;nRbh9dAd#A4-ssOpiek^_y&tUG3V-K)}zFyEbuI3f69sd5F~xZrH0)&@EI2hn&IJR z<`EX@zR$_MM9N``j<|A|laP-v;)uCOk68uBQ7$j0VewIpvPbjhEGh0{&ll!gPLdiN zGn?y8R^XjuoY~F6on!109;4Wo=2CVaAAM;iFpdv}i$5xQ+>F)}mtpU5X7PNad}S_U zOSt+KX9YcY!GtrDbG%bx&ekg}#;p^qM%^*;Bo}gLGEdeV@99EqRZOe1ycTM|il4q^y7s{TG<}akv(pSe z@*Rvj&5$1A#Az=2CeB6Z89EOj^Ng9M?egqDV-7Sqv5CCE%F+rwFPJR?SE{3ZXVjc^ zl;^VxW_nq7yM}VY^WXiAbg>VsH$>H!=GQFyS1&nJOK5;bub7H$Son%dS*c6iRx{mE zZ)snvqqcYHr`A9&#+1{nW_%Bh(5zIx_>O2+$Dsboc#8@$d3W!7%5!x!P}}1vSIFv> z#-8h)zE%Z2q#q9UwR*EV3eU9a^F`Jz(+cIAYgDFHfnCf7AzI_C5S#Eqh<{Q34T>q~ zD8w#I6XGzkg*bty^JJTc>!h_3yua-pXaBwb4N3fih?g8m1q`di{E!J+9+=0#dv0N-T-TA{=jNBdx1Lsb^UAmrv&Sv;s{{C7%L>I zbS15UJC`wPM_7Xkqy)#i`vhyjHd|yhTN8>hhRyEqbQ@#6s}@dQ5ePpM?fi#JmwRGKxi`A0ZXXrwugY~>BK)Z8>jm~|Ob zndlaXn`?!7g2!9!+p=K$pSDV|DBn44)yb@>w+u1Dx)_!8&P9xfbpEWJbVukdLybDh zQOfSpTWUsq;f&~NG}K!b7P6L-`4wJ6xMR}|_aP(Vq!FMu3Kd$kSlCnHjOAx{W=&k+ zXBwJ4%TxQDRm!35_oV+|Z44-Np(;n=|+-*1y8OA6hs2$*V=nxg9*hBIUBHm9DBvp208e9K#ZS%2nHrk4&f3ST#h{BD%E zZWPm_L(uD%6y)LJHoMVLJkP$7=9Y&fE*NxUfIF!>7WOT)uTUOzy z>xBJGy>d{lf}eSaZryZr60l=|Q74x^1lQlz@{qNTU>0bw(bE0~f49+S?O13OwL7u@ zZ!2O_h+ea>5qL=O4U7OXQI7jYK&GMn{wbZE={%V0bi}Tsd*pxJ&DS|D7=eEn1sqdE zH3*qzQ%J(W)4v_hG8*d*LdE!Z5cdefJJw#6z&dQxx-q{{`US|_Qg9q9qw6A zH4ZjfJ+Nwvg9~#WSlq3dfYT4GV&Y)KkxN!#Xb-K*CA=r}?blg=jhY)NssLp;Ez$a+ z^(_Zl;g76%u4i_AWbumCB;5Yq3UTB!{1_FUw8(efDodA$SkffaJ#Ur5UyrOr&T(QN zv$AbNhsV}eJh&15#QKp7eb1j*VSIfQermOh@SeT5XZx`RK443#q?;n0frdeIho_+4 z6RRX&E1RBLrNpU`lTWSP`k<*Y1@oU-CQp_uduILO+|Qm(G=r6|HG^Hx*$mD-w;F4v z?Xq}(ineg4fT*0_y=A+LKNW11;CgeN7gmg8w-I;U2zG>E$O|hZIF8k<+V4hZijaD_ zZG0M5zOX6=)SzMyqM`p6h2KjnFl=OAn>HPTSou`zb`02!xR+L`qK^Lejli2mNkL^A z5$q0}j();+u~fYE(kkp2ARBmy=g3tnQsW^5?0LnOHv$)5S>?6;AM1`xoi~Vf>khZ6 zGbKEvFiUqtQGrl#ZHnhx-OJ9AL$=Dpii>CvlKT-70&nZx^z7~Xcou#vq-czQQ? z?$SJsS~yE=*0W1s;v;80|GfJy))de72hNikJKvFyoTgsqzUl`2Y>2!QcMQK-=2^>TjOQr@@%Y&`X?_p1dAiLShmW5)t2E8K6l2X%2agVGR<9JPUd39X z{_Aad`?DY`ohR_c6J(WaF8?cP4Myx!XO(h!w`Hundootu4H?VZq4`^DFz2Z=zQ%ra zXeG$%*FstvV&z?tF{~5gB>04(ZmUtX$d_V{KFa?a}bzLuUxvX3gi$D%JCD%2?hzGVkk_w~3as@-D_$c~@dA zHpw;5ot6FbF2h*karwEk4cE*n{NtQfJntHem3INg+CN*yV=(SLa4s*KcLm1EyZmB} z;E5OU6tJeK|0-bqW9QEp_`;cg()BMS>t|bT_uFXS#KSIMAM-+cG{4Wa!>nV&_Q|oH zpKYGyx5&NNZ8J)p_d-i`zdib1-Dd2(;Dvs(`%Uqi>Swd^?$%s0EpH<#Z4Fby@Ms&J zyl}cb1z$Q7G|tk}UO9{Cb$VmKE9dR7JK_uqA61OD6*R8%hEPJErNdmw?s0uzj#qb2V}Mp1Rwl=r(ZbuKDgYvtGAdz%G5?MYisWHH*b_1DS8x! z+8HP>u2YNm7Z1>t5~Zd}#gwfFO1Y83y$kuKI6=uU(iL7_>6cRY48-}licC7D=u|~_ zQ*^+)5*VQH2?`&laOKUmO5xUgsmLQ6xz9d@w^umlRJl)%!naEtU-`K@bGjK}Fk3(V zQr03hTe6)tWfBq1Jdpl&H=MiW!u2;mJ8$PP%vYk|Pvci`stNu>u?NKEe8E-zsDx8O!ggIwX zNoOc;OB5X(F7XpIFP`%XKkkw4+m2^i%X@MQ=w2>6FKic$y5<*1=zf)?|gmRmW_bsPHcoUeOzx z!du}8&4mqFEAc>O?uDYcMs(kDjdF{xk&9=l6t)HVWN`0dvV)O*rRXme{hgwZA)9o{ zqpcF>De>ILonQK{x=p&pnL6>rD}12BYfISHL*W|{=%l#|ijGnAN=0kiC6N352L5|c z;q5<^xL5zY!m}`x=Hl)ikhJ%1XQ=`*cGW+Tcw3bxEfhWr2b}C8#^MHr`@vzU^_>*9 zf0Wj7#JSjmWFo~yt(>EhJ|e4@Z4*~rL{G5@3#sJ}{YugqY^maj3y}0!91--6ZzTMe zs#@I@4crrSyj#Xlz3^@CDS8hg{7H`plXOeQOgwiKeH3Y=#h&*7e^+u`jTou+oIQYe z_6N#1RIVcNiR@U#^P9riFZ(kaRwFPU1F4-TwR%foTNfmKuR?;L&n8Q}xU^yWL(zX@ zDCv|pnn-%P@*T^WyvUxQ6bX3GB(+ockY*BpteWUVh4)2{2<@UOvXRQ%8AYc;%TM~~ z8xo(bB2%o846Qlh1UoNZ@HE%8bfPx~TL>j@GHvI@UI=Yp;#Tz6ham%GM%soY2E z+TysxBNEUbIR#lAK7khCiWP^%ssy;iE=ooKQ{8N<^mL9dPVv0qmY$03lDPP7Tk$Ma zc*<@GR8#nu3g;Jh0$d;PAYK1JzLr1RE9sG{Vke1(az>@<7ZMLpjcuaBuc>$1DjAq< ztHR&LwLqrqhvU-QG(|sCbaw;>k#4NseBKVZ21f%Tw4F!@;vgsFr1X7R`QEGOJ6I=t zpFAb$+>c^VC6X;c`2Gwzf^Kn5(&JQ=-&AySXu-_Cp6b2%L>d25(Gkz2H}4znFNKd* zxEgia3Wdwi9wCF~+><$z5l?4juC1bfz(NsPUG*AP-KlMxqJ!)*b~(!1HHH7G@CVYW zt#An$+6CMbq0Q%9Pdrss+WIT{C?X1xE|^c^*&GXq=SM{!Mw*~A!X#Zqvb;hgjW5Pz z(kTPfOZ<$Y=PSB)G3o6G6{qbA|1nD9lclg-RCqeBiO?FTm-`asEjB`i_8kHXGqjBr zq_;HXdy=B7sLX(gAIVRH@neu(Q` zp6#1lgi-Weo&G|pbwW1jl=Vq6tRbplKTvdFio{hvZ7UQht#@rEaqp|Dy28&PFqEMk zZZ7HDDpI2ry$cC~p3+j%<5ahQK+*qVsGv7>l=OC0lm1aOH)sWYqNk+4SJ_stjEvJN zzi`O)EVsaR}#co&;=86r=o8v2Qr^|AIoeev2 z0)7c&4>%ZsMcFTM+EJ8&bcmOMbXVFdqB+&4c9D4)R@B99o|;%+)Rn9~_1q}xYQ;Y{BC&G0{PneFQrTEr=xmudqq%8i-x1O-*oqc=1RFTZ~Cn9@&wwvK(X}%g-Au!T4TI^NLiDcxPBPWvKpT&(x*Bx<4rwn!WA|i?}2HyB;6Xm)f zR^Q5(b=BaOSVmdb5boFIl%&zU@z>y@3)YNDX-G>inxgT-G#E`KT&v*cy6TFirM~j$QPfg zRkj)!8pHb1l(fi-yrm80Z64MMdbRR>*L>ag9^{Zt`Bu@D3%*YOs_-nXW{Jle=p%(M zcfOA2M_dJlHlv=5Ox|LZip*+c2wGfK5=lg+#3w2GYb+$4BEDpqo6>;SjlWd*elh3f z69Ny=f=(_tL`gr-gPAJ0KGap#0b5pZH)eUtkZB!gw~(5FxStgSf?Yf5|3eQ_TQuqYoBKX#>*CP+& zBFj_jz1~CYRr;5FQuB?}53!9IC_U6y-P(Tyk(>J!5Eskh=#}6FI2t1`={*7>3O#+Q z+8&{(7qsmc>o@=BIY3+#cTlPXBNTT{+3Dm{ZqI}r>fhQR6$dK)z(%>@;z_J4&?rZT zjAk+-Dzn(SNY>`CUeB1}NF!bS9Ysg`(5r}x$W6+Up?D{GALZHCkWB+A;uoYvs%!a_ zZzbX)w)GZB1K!kiQ2Lj|HwpN7+r}US#?jmi(jweTs{`lwO|Bs5sSRlT$u*jN4OE^iZyPKmYG}(aa9=WxW+OT z(LRZ4MErl*$x^thtu)+J+24YNG&kv}qN}Azyqu!_l4YK{XGvhX!iy3YWw6$#=2cVr z*VQ~GUFl~L7ttC&RRWy8CVq4(T1q8DBc}vvF>u};C3O1i^ zdm1KJWi{KUyydOPD{l{#x2O4EuMGxsMCNc2T%!SZIRe6dWuNY%E&{8OZcAE3OD=TF zo0lR8-aI0ma!vW}m+$ppo-4eQkJZBLfkkKyee~;Mod~TXY37sdl20~GL=I_V18Eug zkDyg&>T{I2cB&Tosnq|j@KB#>^C-{2AY{;hdrC7^z&_cu1PcYdOVP`H8poG7LOSI) zFYOb|3$In5R0cMenORx2K~C6mpALwqkvl{XBI6gOmA4C^v?AG^iK58ca#46a;-W+7 zxLN`qD7-Uqk=JK^TJW35W-#t7<*m&6*S+!U#iUdADc@s!l72?v(LVL@Z-rm=!Sf-Z zCPOR5B#X%B?GmX-4!}@BXOR|Z@cnwpTC5{I=~L1oDQkWD!t)BZ`4ry`h4=BP7cUfk z)~8@XTFTfZGZEqmm%`SKIKxHFTCPC%5IRI0J)0!UIezXl9T~NlgjI^Zq`r>ey{UK- z3rQP43R=A8;%@Ojkrlo}BYP{E$Spp7W>MS|25OR)mA@GxYBRv@N`Jpk?RyJpq>X8L z`kZf8*g&n&6cKA>v*sBY*ltKZJd=DC=mSGL1$Ky}3szx}i_4o(rum zjc>qE5yt*pk#bxqhx#c0sBni*<-YGV?vq6USV-gUXiiZ@)N1*7Yk(tyZm;NBKJUcg zxJTM}hqNrft%!(ctv;^wd-`;eKPh~SPi=of{C~Bf27K9xlJmXP-dW+xhshG{B86=; zaoT|=p0)a}blA3KNp_D?s)}ojqq}80X?tmco=enLbY}$CBR!L}$dLs;g|!6c4FxV557yRmmDKIsJV)z^scl=Ftj{Ui z6yAX3lx+mGhAjIXa5p4)56%QmxHfreC%7O^aO5=NQrt6)P2{4&S=>vcj}@q$MAv9! zCsFkvt|f6gx&`YRb2i7*7dIQbMsa^NBbnrVEKH{HDW0v#E=%JP%j_nu58Qb>+bRLm zGGqc;VrWx_HqPTt5!x3J(Tr0;ey6<|1K5Cs=8WJR#HBJ0{V+C_aoFLxm+C5^@mOGB zOFBQo?3UbX%-fz-_P_2g?Q>_YmFoj>AmN8rT>Q9${;g?>7v)>CP&)LH+BKD@UL4Sh z`mq4lS~F4|d9}}VQRn9^dbM#Si<7L|+Hm53)^oXyYq!R9d*-xrEz@|Q(Nn4e{|Wo= zhO9JLXoM_1o|ib+kv{iA>%^?N40k724{?5JTqmwS@HEVQN**=r-BZ1a)zB=NXovU6_)UIM9We4%`r84cuKR(vi}Y)Ap!2INFtlcX(>2 zyJpq(+=uH!H-cL@3;%x(QoNt-(qhCl_$#hhr+31z-*fqjYo;I1ux4Cy)nxgvxaMlW c&v;)Jq5|B%P+Uc4A(~=^5UIHQi>vJa137H^s{jB1 diff --git a/Engine/engine/match.cpp b/Engine/engine/match.cpp index 695e1cd..85ae73d 100644 --- a/Engine/engine/match.cpp +++ b/Engine/engine/match.cpp @@ -1,8 +1,8 @@ // "Copyright [2023] " #ifndef MAIN_CPP #define MAIN_CPP -#include #include +#include #include #include @@ -11,21 +11,20 @@ // https://arvid.io/2018/06/30/on-cxx-random-number-generator-quality/ // whenever, if ever I feel like it -#include "./constants.hpp" #include "./beforeRender.hpp" +#include "./constants.hpp" #include "./misc.hpp" #include "./renderLoop.hpp" int main() { - // changed in glfwWindowShouldClose - GLFWwindow *window = prepareForRender(); - if (window == nullptr) - return -1; - renderLoop(window); + // changed in glfwWindowShouldClose + GLFWwindow *window = prepareForRender(); + if (window == nullptr) return -1; + renderLoop(window); - // clean GLFW resources - glfwTerminate(); - return 0; + // clean GLFW resources + glfwTerminate(); + return 0; } #endif // #ifndef MAIN_CPP diff --git a/Engine/engine/misc.cpp b/Engine/engine/misc.cpp index 7d9996e..a372e44 100644 --- a/Engine/engine/misc.cpp +++ b/Engine/engine/misc.cpp @@ -1,10 +1,9 @@ // "Copyright [2023] " #ifndef MISC_CPP #define MISC_CPP -#include #include "./misc.hpp" -void print(const std::string s) { - std::cout << s << std::endl; -} + +#include +void print(const std::string s) { std::cout << s << std::endl; } #endif diff --git a/Engine/engine/renderLoop.cpp b/Engine/engine/renderLoop.cpp index 1d0174a..baaa7d2 100644 --- a/Engine/engine/renderLoop.cpp +++ b/Engine/engine/renderLoop.cpp @@ -1,229 +1,215 @@ // "Copyright [2023] " #ifndef RENDER_LOOP_CPP #define RENDER_LOOP_CPP -#include -#include -#include #include "./renderLoop.hpp" -#include "./draw.hpp" -#include "./shaders.hpp" + +#include +#include + +#include + #include "./constants.hpp" +#include "./draw.hpp" #include "./misc.hpp" +#include "./shaders.hpp" int processInput(GLFWwindow *window, const int whatToDraw) { - static bool lockedLeft = false; - static bool lockedRight = false; - const bool PRESSED_CHANGE_LEFT = - (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS); - const bool PRESSED_CHANGE_RIGHT = - (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS); + static bool lockedLeft = false; + static bool lockedRight = false; + const bool PRESSED_CHANGE_LEFT = + (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS); + const bool PRESSED_CHANGE_RIGHT = + (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS); - // glfwGetKey takes window and key as - // an input and checks is currently being pressed - // if the user pressed escape we close window - if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) - glfwSetWindowShouldClose(window, true); - if (glfwGetKey(window, GLFW_KEY_C) == GLFW_PRESS) - glfwSetWindowShouldClose(window, true); - if ( !PRESSED_CHANGE_LEFT ) - lockedLeft = 0; - if ( !PRESSED_CHANGE_RIGHT ) - lockedRight = 0; - if ( PRESSED_CHANGE_RIGHT && lockedRight == 0 ) { - lockedRight = 1; - return (whatToDraw == constants::MAX_DRAW_CALL ? 0 : whatToDraw + 1); - } - if ( PRESSED_CHANGE_LEFT && lockedLeft == 0 ) { - lockedLeft = 1; - return (whatToDraw == 0 ? constants::MAX_DRAW_CALL : whatToDraw - 1); - } - return whatToDraw; + // glfwGetKey takes window and key as + // an input and checks is currently being pressed + // if the user pressed escape we close window + if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + if (glfwGetKey(window, GLFW_KEY_C) == GLFW_PRESS) + glfwSetWindowShouldClose(window, true); + if (!PRESSED_CHANGE_LEFT) lockedLeft = 0; + if (!PRESSED_CHANGE_RIGHT) lockedRight = 0; + if (PRESSED_CHANGE_RIGHT && lockedRight == 0) { + lockedRight = 1; + return (whatToDraw == constants::MAX_DRAW_CALL ? 0 : whatToDraw + 1); + } + if (PRESSED_CHANGE_LEFT && lockedLeft == 0) { + lockedLeft = 1; + return (whatToDraw == 0 ? constants::MAX_DRAW_CALL : whatToDraw - 1); + } + return whatToDraw; } // https://stackoverflow.com/a/25680092 -unsigned int copyVerticesMemory( - const float vertices[], - const size_t sizeOfVertices, - const GLenum boundBufferTarget) { - // stores vertices in gpu memory - unsigned int vertexBufferObject; - // this is open gl object so we refer - // it by its ID generated here and stored in vertexBufferObject variable - glGenBuffers(1, &vertexBufferObject); - // buffer type of vertex buffer object is GL_ARRAY_BUFFER - glBindBuffer(boundBufferTarget, vertexBufferObject); - // now whenever we change GL_ARRAY_BUFFER - // we change bound buffer vertexBufferObject +unsigned int copyVerticesMemory(const float vertices[], + const size_t sizeOfVertices, + const GLenum boundBufferTarget) { + // stores vertices in gpu memory + unsigned int vertexBufferObject; + // this is open gl object so we refer + // it by its ID generated here and stored in vertexBufferObject variable + glGenBuffers(1, &vertexBufferObject); + // buffer type of vertex buffer object is GL_ARRAY_BUFFER + glBindBuffer(boundBufferTarget, vertexBufferObject); + // now whenever we change GL_ARRAY_BUFFER + // we change bound buffer vertexBufferObject - /* we copy vertex data into buffer memory - GL_STREAM_DRAW: the data is set only once and used by the GPU at most a few times. - GL_STATIC_DRAW: the data is set only once and used many times. - GL_DYNAMIC_DRAW: the data is changed a lot and used many times. - */ - glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW); - return vertexBufferObject; + /* we copy vertex data into buffer memory + GL_STREAM_DRAW: the data is set only once and used by the GPU at most a few + times. GL_STATIC_DRAW: the data is set only once and used many times. + GL_DYNAMIC_DRAW: the data is changed a lot and used many times. + */ + glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW); + return vertexBufferObject; } -unsigned int copyVerticesMemory( - const unsigned int vertices[], - const size_t sizeOfVertices, - const GLenum boundBufferTarget) { - // stores vertices in gpu memory - unsigned int vertexBufferObject; - // this is open gl object so we refer it - // by its ID generated here and stored in vertexBufferObject variable - glGenBuffers(1, &vertexBufferObject); - // buffer type of vertex buffer object is GL_ARRAY_BUFFER - glBindBuffer( - boundBufferTarget, - vertexBufferObject); - // now whenever we change GL_ARRAY_BUFFER - // we change bound buffer vertexBufferObject +unsigned int copyVerticesMemory(const unsigned int vertices[], + const size_t sizeOfVertices, + const GLenum boundBufferTarget) { + // stores vertices in gpu memory + unsigned int vertexBufferObject; + // this is open gl object so we refer it + // by its ID generated here and stored in vertexBufferObject variable + glGenBuffers(1, &vertexBufferObject); + // buffer type of vertex buffer object is GL_ARRAY_BUFFER + glBindBuffer(boundBufferTarget, vertexBufferObject); + // now whenever we change GL_ARRAY_BUFFER + // we change bound buffer vertexBufferObject - /* we copy vertex data into buffer memory - GL_STREAM_DRAW: the data is set only once and used by the GPU at most a few times. - GL_STATIC_DRAW: the data is set only once and used many times. - GL_DYNAMIC_DRAW: the data is changed a lot and used many times. - */ - glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW); - return vertexBufferObject; + /* we copy vertex data into buffer memory + GL_STREAM_DRAW: the data is set only once and used by the GPU at most a few + times. GL_STATIC_DRAW: the data is set only once and used many times. + GL_DYNAMIC_DRAW: the data is changed a lot and used many times. + */ + glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW); + return vertexBufferObject; } -void configureVertexAttribute( - const bool colorIncluded, - const bool textureIncluded) { - /* specify how OGL interprets vertex data - From left: - which vertex attribute we configure (from shader source code layout (location = 0)) - size of vertex attribute (we use vec3 so it contains 3 values) - type of data of which vec consists of - should data be normalized, (useful when we use integer data) - space between vertex attributes, each position data is 3 times the size of float - offset of where position data begins in buffer - see: https://learnopengl.com/img/getting-started/vertex_attribute_pointer.png - vertex attribute data take data from memory managed by VBO bound to GL_ARRAY_BUFFER */ - if (!colorIncluded) { - glVertexAttribPointer( - 0, 3, GL_FLOAT, GL_FALSE, - 3 * sizeof(float), reinterpret_cast(0)); - // enable vertex attribute - glEnableVertexAttribArray(0); - return; - } +void configureVertexAttribute(const bool colorIncluded, + const bool textureIncluded) { + /* specify how OGL interprets vertex data + From left: + which vertex attribute we configure (from shader source code layout (location + = 0)) size of vertex attribute (we use vec3 so it contains 3 values) type of + data of which vec consists of should data be normalized, (useful when we use + integer data) space between vertex attributes, each position data is 3 times + the size of float offset of where position data begins in buffer see: + https://learnopengl.com/img/getting-started/vertex_attribute_pointer.png + vertex attribute data take data from memory managed by VBO bound to + GL_ARRAY_BUFFER */ + if (!colorIncluded) { + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), + reinterpret_cast(0)); + // enable vertex attribute + glEnableVertexAttribArray(0); + return; + } + /* we change attribute location, color values have size of 3 floats + we do not normalize values, in order to get the next attribute value in data + array we need to move 6 floats, (3 for position and 3 for color), we also need + to specify an offset, first we have position then after 3 floats we have color + https://learnopengl.com/img/getting-started/vertex_attribute_pointer_interleaved.png +*/ + if (colorIncluded && !textureIncluded) { + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), + reinterpret_cast(0)); + // enable vertex attribute + glEnableVertexAttribArray(0); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), + reinterpret_cast(3 * sizeof(float))); + // enable vertex attribute + glEnableVertexAttribArray(1); + return; + } - /* we change attribute location, color values have size of 3 floats - we do not normalize values, in order to get the next attribute value in data array we need to move 6 floats, (3 for position and 3 for color), we also need to specify an offset, first we have position then after 3 floats we have color - https://learnopengl.com/img/getting-started/vertex_attribute_pointer_interleaved.png */ - if (colorIncluded && !textureIncluded) { - glVertexAttribPointer( - 0, 3, GL_FLOAT, GL_FALSE, - 6 * sizeof(float), reinterpret_cast(0)); - // enable vertex attribute - glEnableVertexAttribArray(0); + // Since we've added an extra vertex + // attribute we again have to notify OpenGL of the new vertex format + // this time for textures we add just + // 2 more attributes and not 3 as with color + if (textureIncluded) { + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), + reinterpret_cast(0)); + // enable vertex attribute + glEnableVertexAttribArray(0); - glVertexAttribPointer( - 1, 3, GL_FLOAT, GL_FALSE, - 6 * sizeof(float), reinterpret_cast(3* sizeof(float))); - // enable vertex attribute - glEnableVertexAttribArray(1); - return; - } + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), + reinterpret_cast(3 * sizeof(float))); + // enable vertex attribute + glEnableVertexAttribArray(1); - // Since we've added an extra vertex - // attribute we again have to notify OpenGL of the new vertex format - // this time for textures we add just - // 2 more attributes and not 3 as with color - if (textureIncluded) { - glVertexAttribPointer( - 0, 3, GL_FLOAT, GL_FALSE, - 8 * sizeof(float), reinterpret_cast(0)); - // enable vertex attribute - glEnableVertexAttribArray(0); - - glVertexAttribPointer( - 1, 3, GL_FLOAT, GL_FALSE, - 8 * sizeof(float), reinterpret_cast(3* sizeof(float))); - // enable vertex attribute - glEnableVertexAttribArray(1); - - glVertexAttribPointer( - 2, 2, GL_FLOAT, GL_FALSE, - 8 * sizeof(float), reinterpret_cast(6 * sizeof(float))); - glEnableVertexAttribArray(2); - return; - } + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), + reinterpret_cast(6 * sizeof(float))); + glEnableVertexAttribArray(2); + return; + } } unsigned int generateBindVAO() { - // vertex array object is used to draw objects by binding them to vao - // generate vao - unsigned int vertexArrayObject; - glGenVertexArrays(1, &vertexArrayObject); - // bind vao - glBindVertexArray(vertexArrayObject); - return vertexArrayObject; + // vertex array object is used to draw objects by binding them to vao + // generate vao + unsigned int vertexArrayObject; + glGenVertexArrays(1, &vertexArrayObject); + // bind vao + glBindVertexArray(vertexArrayObject); + return vertexArrayObject; } -void copyVerticesArray( - const unsigned int vertexBufferObject, - const float vertices[], - const size_t sizeOfVertices, - const GLenum boundBufferTarget) { - // copy vertices array in array useful for OGL - glBindBuffer(boundBufferTarget, vertexBufferObject); - glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW); +void copyVerticesArray(const unsigned int vertexBufferObject, + const float vertices[], const size_t sizeOfVertices, + const GLenum boundBufferTarget) { + // copy vertices array in array useful for OGL + glBindBuffer(boundBufferTarget, vertexBufferObject); + glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW); } int renderLoopInside(GLFWwindow *window, int whatToDraw) { - // input - whatToDraw = processInput(window, whatToDraw); - // We specify the color to clear the screen with - // RGB and alpha value - glClearColor( - constants::LEARN_OPEN_GL_COLOR.red, - constants::LEARN_OPEN_GL_COLOR.green, - constants::LEARN_OPEN_GL_COLOR.blue, - constants::LEARN_OPEN_GL_COLOR.alpha); - // There is - // GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT and GL_STENCIL_BUFFER_BIT - glClear(GL_COLOR_BUFFER_BIT); + // input + whatToDraw = processInput(window, whatToDraw); + // We specify the color to clear the screen with + // RGB and alpha value + glClearColor(constants::LEARN_OPEN_GL_COLOR.red, + constants::LEARN_OPEN_GL_COLOR.green, + constants::LEARN_OPEN_GL_COLOR.blue, + constants::LEARN_OPEN_GL_COLOR.alpha); + // There is + // GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT and GL_STENCIL_BUFFER_BIT + glClear(GL_COLOR_BUFFER_BIT); + drawFigureReturn successAndVao = drawFigure(whatToDraw); + if (successAndVao.success == -1) { + print("error with drawing!"); + glfwSetWindowShouldClose(window, true); + } - drawFigureReturn successAndVao = drawFigure(whatToDraw); - if (successAndVao.success == -1) { - print("error with drawing!"); - glfwSetWindowShouldClose(window, true); - } + // swaps buffer containing color values of each pixel in window + // there is front buffer (final image) + // and back buffer (where all rendering commands draw to) + // when back buffer is ready we swap it + // with front buffer to eliminate flickering + glfwSwapBuffers(window); - // swaps buffer containing color values of each pixel in window - // there is front buffer (final image) - // and back buffer (where all rendering commands draw to) - // when back buffer is ready we swap it - // with front buffer to eliminate flickering - glfwSwapBuffers(window); + // glfwPollEvents checks if any event + // (like mouse/keyboard input was triggered), + // updates window state and calls functions + // (which we register via callback methods) + glfwPollEvents(); - // glfwPollEvents checks if any event - // (like mouse/keyboard input was triggered), - // updates window state and calls functions - // (which we register via callback methods) - glfwPollEvents(); + glDeleteVertexArrays(1, &successAndVao.VAO); + glDeleteBuffers(1, &successAndVao.VBO); + glDeleteBuffers(1, &successAndVao.EBO); - glDeleteVertexArrays(1, &successAndVao.VAO); - glDeleteBuffers(1, &successAndVao.VBO); - glDeleteBuffers(1, &successAndVao.EBO); - - - return whatToDraw; + return whatToDraw; } void renderLoop(GLFWwindow *window) { - int whatToDraw = 0; - // glfwWindowShouldClose checks if GLFW was instructed to close - while (!glfwWindowShouldClose(window)) { - whatToDraw = renderLoopInside(window, whatToDraw); - } + int whatToDraw = 0; + // glfwWindowShouldClose checks if GLFW was instructed to close + while (!glfwWindowShouldClose(window)) { + whatToDraw = renderLoopInside(window, whatToDraw); + } } #endif diff --git a/Engine/engine/renderLoop.hpp b/Engine/engine/renderLoop.hpp index ee5adab..abc2932 100644 --- a/Engine/engine/renderLoop.hpp +++ b/Engine/engine/renderLoop.hpp @@ -2,33 +2,29 @@ #ifndef ENGINE_ENGINE_RENDERLOOP_HPP_ #define ENGINE_ENGINE_RENDERLOOP_HPP_ #include + #include void renderLoop(GLFWwindow *window); int renderLoopInside(GLFWwindow *window, int whatToDraw); -void copyVerticesArray( - unsigned int vertexBufferObject, - const float vertices[], - const size_t sizeOfVertices, - const GLenum boundBufferTarget); +void copyVerticesArray(unsigned int vertexBufferObject, const float vertices[], + const size_t sizeOfVertices, + const GLenum boundBufferTarget); unsigned int generateBindVAO(); -void configureVertexAttribute( - const bool colorIncluded, - const bool textureIncluded); +void configureVertexAttribute(const bool colorIncluded, + const bool textureIncluded); int processInput(GLFWwindow *window, int whatToDraw); -unsigned int copyVerticesMemory( - const float vertices[], - const size_t sizeOfVertices, - const GLenum boundBufferTarget); +unsigned int copyVerticesMemory(const float vertices[], + const size_t sizeOfVertices, + const GLenum boundBufferTarget); -unsigned int copyVerticesMemory( - const unsigned int vertices[], - const size_t sizeOfVertices, - const GLenum boundBufferTarget); +unsigned int copyVerticesMemory(const unsigned int vertices[], + const size_t sizeOfVertices, + const GLenum boundBufferTarget); #endif // ENGINE_ENGINE_RENDERLOOP_HPP_ diff --git a/Engine/engine/shader.cpp b/Engine/engine/shader.cpp index 7fd9f87..a506214 100644 --- a/Engine/engine/shader.cpp +++ b/Engine/engine/shader.cpp @@ -2,113 +2,106 @@ #ifndef SHADER_CPP #define SHADER_CPP -#include - -#include -#include -#include -#include #include "./shader.hpp" +#include + +#include +#include +#include +#include + const std::string Shader::fileToShader(const std::string shaderPath) { - // 1. retrieve the vertex/fragment source code from filePath - std::string shaderCode; - std::ifstream shaderFile; - // ensure ifstream objects can throw exceptions: - shaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try { - // open files - shaderFile.open(shaderPath); - std::stringstream shaderStream; - // read file's buffer contents into streams - shaderStream << shaderFile.rdbuf(); - // close file handlers - shaderFile.close(); - // convert stream into string - shaderCode = shaderStream.str(); - } - catch (std::ifstream::failure& e) { - std::cout << - "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " - << e.what() << std::endl; - } - return shaderCode; + // 1. retrieve the vertex/fragment source code from filePath + std::string shaderCode; + std::ifstream shaderFile; + // ensure ifstream objects can throw exceptions: + shaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try { + // open files + shaderFile.open(shaderPath); + std::stringstream shaderStream; + // read file's buffer contents into streams + shaderStream << shaderFile.rdbuf(); + // close file handlers + shaderFile.close(); + // convert stream into string + shaderCode = shaderStream.str(); + } catch (std::ifstream::failure &e) { + std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ: " << e.what() + << std::endl; + } + return shaderCode; } -Shader::Shader(const std::string vertexPath, const std::string fragmentPath) { - const std::string vshaderCodeString = this -> fileToShader(vertexPath); - - const std::string fShaderCodeString = - this -> fileToShader(fragmentPath); +Shader::Shader(const std::string vertexPath, const std::string fragmentPath) { + const std::string vshaderCodeString = this->fileToShader(vertexPath); + const std::string fShaderCodeString = this->fileToShader(fragmentPath); - const char* vShaderCode = vshaderCodeString.c_str(); - const char * fShaderCode = fShaderCodeString.c_str(); - // 2. compile shaders - unsigned int vertex, fragment; - // vertex shader - vertex = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vertex, 1, &vShaderCode, NULL); - glCompileShader(vertex); - checkCompileErrors(vertex, "VERTEX"); - // fragment Shader - fragment = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(fragment, 1, &fShaderCode, NULL); - glCompileShader(fragment); - checkCompileErrors(fragment, "FRAGMENT"); - // shader Program - ID = glCreateProgram(); - glAttachShader(ID, vertex); - glAttachShader(ID, fragment); - glLinkProgram(ID); - checkCompileErrors(ID, "PROGRAM"); - // delete the shaders as they're linked - // into our program now and no longer necessary - glDeleteShader(vertex); - glDeleteShader(fragment); + const char *vShaderCode = vshaderCodeString.c_str(); + const char *fShaderCode = fShaderCodeString.c_str(); + // 2. compile shaders + unsigned int vertex, fragment; + // vertex shader + vertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex, 1, &vShaderCode, NULL); + glCompileShader(vertex); + checkCompileErrors(vertex, "VERTEX"); + // fragment Shader + fragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment, 1, &fShaderCode, NULL); + glCompileShader(fragment); + checkCompileErrors(fragment, "FRAGMENT"); + // shader Program + ID = glCreateProgram(); + glAttachShader(ID, vertex); + glAttachShader(ID, fragment); + glLinkProgram(ID); + checkCompileErrors(ID, "PROGRAM"); + // delete the shaders as they're linked + // into our program now and no longer necessary + glDeleteShader(vertex); + glDeleteShader(fragment); } -void Shader::use() { - glUseProgram(ID); -} +void Shader::use() { glUseProgram(ID); } void Shader::setBool(const std::string &name, bool value) const { - glUniform1i( - glGetUniformLocation(ID, name.c_str()), - reinterpret_cast(value)); + glUniform1i(glGetUniformLocation(ID, name.c_str()), int(value)); } void Shader::setInt(const std::string &name, int value) const { - glUniform1i(glGetUniformLocation(ID, name.c_str()), value); + glUniform1i(glGetUniformLocation(ID, name.c_str()), value); } void Shader::setFloat(const std::string &name, float value) const { - glUniform1f(glGetUniformLocation(ID, name.c_str()), value); + glUniform1f(glGetUniformLocation(ID, name.c_str()), value); } void Shader::checkCompileErrors(unsigned int shader, std::string type) { - int success; - char infoLog[1024]; - if (type != "PROGRAM") { - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - glGetShaderInfoLog(shader, 1024, NULL, infoLog); - std::cout << - "ERROR::SHADER_COMPILATION_ERROR of type: " - << type << "\n" << infoLog << - "\n -- --------------------------------------------------- -- " - << std::endl; - } - } else { - glGetProgramiv(shader, GL_LINK_STATUS, &success); - if (!success) { - glGetProgramInfoLog(shader, 1024, NULL, infoLog); - std::cout << - "ERROR::PROGRAM_LINKING_ERROR of type: " - << type << "\n" << infoLog << - "\n -- --------------------------------------------------- -- " - << std::endl; - } + int success; + char infoLog[1024]; + if (type != "PROGRAM") { + glGetShaderiv(shader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(shader, 1024, NULL, infoLog); + std::cout + << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n" + << infoLog + << "\n -- --------------------------------------------------- -- " + << std::endl; } + } else { + glGetProgramiv(shader, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(shader, 1024, NULL, infoLog); + std::cout + << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" + << infoLog + << "\n -- --------------------------------------------------- -- " + << std::endl; + } + } } -#endif // SHADER_CPP \ No newline at end of file +#endif // SHADER_CPP diff --git a/Engine/engine/shader.hpp b/Engine/engine/shader.hpp index 122315d..41bfc82 100644 --- a/Engine/engine/shader.hpp +++ b/Engine/engine/shader.hpp @@ -4,38 +4,36 @@ #include -#include #include -#include #include +#include +#include class Shader { public: - unsigned int ID; - // constructor generates the shader on the fly - // ------------------------------------------------------------------------ - Shader(const std::string vertexPath, const std::string fragmentPath); - const std::string fileToShader(const std::string shaderPath); - // activate the shader - // ------------------------------------------------------------------------ - void use(); - // utility uniform functions - // ------------------------------------------------------------------------ - void setBool(const std::string &name, bool value) const; - // ------------------------------------------------------------------------ - void setInt(const std::string &name, int value) const; - // ------------------------------------------------------------------------ - void setFloat(const std::string &name, float value) const; + unsigned int ID; + // constructor generates the shader on the fly + // ------------------------------------------------------------------------ + Shader(const std::string vertexPath, const std::string fragmentPath); + const std::string fileToShader(const std::string shaderPath); + // activate the shader + // ------------------------------------------------------------------------ + void use(); + // utility uniform functions + // ------------------------------------------------------------------------ + void setBool(const std::string &name, bool value) const; + // ------------------------------------------------------------------------ + void setInt(const std::string &name, int value) const; + // ------------------------------------------------------------------------ + void setFloat(const std::string &name, float value) const; private: - // utility function for checking shader compilation/linking errors. - // ------------------------------------------------------------------------ - void checkCompileErrors(unsigned int shader, std::string type); + // utility function for checking shader compilation/linking errors. + // ------------------------------------------------------------------------ + void checkCompileErrors(unsigned int shader, std::string type); - void compileErrorsMessage( - const unsigned int shader, - const bool compilation, - const std::string type, - const std::string errorMessage); + void compileErrorsMessage(const unsigned int shader, const bool compilation, + const std::string type, + const std::string errorMessage); }; #endif // ENGINE_ENGINE_SHADER_HPP_ diff --git a/Engine/engine/shaders.cpp b/Engine/engine/shaders.cpp index 23a2af5..a69fe2d 100644 --- a/Engine/engine/shaders.cpp +++ b/Engine/engine/shaders.cpp @@ -1,103 +1,98 @@ // "Copyright [2023] " #ifndef SHADERS_CPP #define SHADERS_CPP -#include -#include #include "./shaders.hpp" + +#include +#include + #include "./constants.hpp" #include "./misc.hpp" -unsigned int linkShaderObjectsShaderProgram( - const unsigned int vertexShaders, - const unsigned int fragmentShader) { - // link shader objects into shader program - // will store shader program id - // creates program - const unsigned int shaderProgram = glCreateProgram(); +unsigned int linkShaderObjectsShaderProgram(const unsigned int vertexShaders, + const unsigned int fragmentShader) { + // link shader objects into shader program + // will store shader program id + // creates program + const unsigned int shaderProgram = glCreateProgram(); - // attachShaders - glAttachShader(shaderProgram, vertexShaders); - glAttachShader(shaderProgram, fragmentShader); + // attachShaders + glAttachShader(shaderProgram, vertexShaders); + glAttachShader(shaderProgram, fragmentShader); - // link shaders - glLinkProgram(shaderProgram); - if (!shaderSuccessful(shaderProgram, false)) - return 0; + // link shaders + glLinkProgram(shaderProgram); + if (!shaderSuccessful(shaderProgram, false)) return 0; - // activate program - // after that every shader and rendering call will use this program object - glUseProgram(shaderProgram); + // activate program + // after that every shader and rendering call will use this program object + glUseProgram(shaderProgram); - // delete shaders - // (they are linked into shaderProgram and we do not need them anymore) - glDeleteShader(vertexShaders); - glDeleteShader(fragmentShader); - if (shaderProgram == 0) - print("Shader Program Linking Failed"); - return shaderProgram; + // delete shaders + // (they are linked into shaderProgram and we do not need them anymore) + glDeleteShader(vertexShaders); + glDeleteShader(fragmentShader); + if (shaderProgram == 0) print("Shader Program Linking Failed"); + return shaderProgram; } -unsigned int compileShader( - const GLenum shaderType, - const char *shaderSource) { - // we create vertex shader and assign its id to shader variable - const unsigned int shaderID = glCreateShader(shaderType); +unsigned int compileShader(const GLenum shaderType, const char* shaderSource) { + // we create vertex shader and assign its id to shader variable + const unsigned int shaderID = glCreateShader(shaderType); - // attach shader source code to shader object - // from left: shader object to compile, - // how many strings as source code, actual source code - // (we leave the 4th as nullptr) - glShaderSource(shaderID, 1, &shaderSource, nullptr); - // compile shader - glCompileShader(shaderID); - if (!shaderSuccessful(shaderID, true)) - return 0; - return shaderID; + // attach shader source code to shader object + // from left: shader object to compile, + // how many strings as source code, actual source code + // (we leave the 4th as nullptr) + glShaderSource(shaderID, 1, &shaderSource, nullptr); + // compile shader + glCompileShader(shaderID); + if (!shaderSuccessful(shaderID, true)) return 0; + return shaderID; } std::pair compileShaders( - const char* vertexShaderSource, - const char* fragmentShaderSource) { - const unsigned int vertexShader = - compileShader(GL_VERTEX_SHADER, vertexShaderSource); + const char* vertexShaderSource, const char* fragmentShaderSource) { + const unsigned int vertexShader = + compileShader(GL_VERTEX_SHADER, vertexShaderSource); - if (vertexShader == 0) { - print("Vertex Shader Compilation Failed"); - return std::make_pair(0, 0); - } + if (vertexShader == 0) { + print("Vertex Shader Compilation Failed"); + return std::make_pair(0, 0); + } - const unsigned int fragmentShader = - compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); + const unsigned int fragmentShader = + compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource); - if (fragmentShader == 0) { - print("Fragment Shader Compilation Failed"); - return std::make_pair(0, 0); - } - return std::make_pair(vertexShader, fragmentShader); + if (fragmentShader == 0) { + print("Fragment Shader Compilation Failed"); + return std::make_pair(0, 0); + } + return std::make_pair(vertexShader, fragmentShader); } void shaderFailedMessage(const unsigned int shader, const bool compilation) { - char infoLog[512]; - const size_t sizeOfInfoLog = sizeof(infoLog) / sizeof(infoLog[0]); - compilation ? glGetShaderInfoLog(shader, sizeOfInfoLog, nullptr, infoLog) - : glGetProgramInfoLog(shader, sizeOfInfoLog, nullptr, infoLog); - std::cout << "ERROR vertex shader compilation failed \n" - << infoLog << std::endl; + char infoLog[512]; + const size_t sizeOfInfoLog = sizeof(infoLog) / sizeof(infoLog[0]); + compilation ? glGetShaderInfoLog(shader, sizeOfInfoLog, nullptr, infoLog) + : glGetProgramInfoLog(shader, sizeOfInfoLog, nullptr, infoLog); + std::cout << "ERROR vertex shader compilation failed \n" + << infoLog << std::endl; } int shaderSuccessful(const unsigned int shader, const bool compilation) { - // check if compilation was successful - // int because glGetShaderiv requires int - int success; - // here we store info about compilation - // check if compilation was successful - compilation ? glGetShaderiv(shader, GL_COMPILE_STATUS, &success) - : glGetProgramiv(shader, GL_LINK_STATUS, &success); - // if not display compilation log - if (!success) { - shaderFailedMessage(shader, compilation); - } - return success; + // check if compilation was successful + // int because glGetShaderiv requires int + int success; + // here we store info about compilation + // check if compilation was successful + compilation ? glGetShaderiv(shader, GL_COMPILE_STATUS, &success) + : glGetProgramiv(shader, GL_LINK_STATUS, &success); + // if not display compilation log + if (!success) { + shaderFailedMessage(shader, compilation); + } + return success; } #endif diff --git a/Engine/engine/shaders.hpp b/Engine/engine/shaders.hpp index 3a95690..e90fc83 100644 --- a/Engine/engine/shaders.hpp +++ b/Engine/engine/shaders.hpp @@ -1,20 +1,19 @@ // "Copyright [2023] " #ifndef ENGINE_ENGINE_SHADERS_HPP_ #define ENGINE_ENGINE_SHADERS_HPP_ -#include #include +#include + #include #include -unsigned int linkShaderObjectsShaderProgram( - const unsigned int vertexShaders, - const unsigned int fragmentShader); +unsigned int linkShaderObjectsShaderProgram(const unsigned int vertexShaders, + const unsigned int fragmentShader); std::pair compileShaders( - const char* vertexShaderSource, - const char* fragmentShaderSource); + const char* vertexShaderSource, const char* fragmentShaderSource); -unsigned int compileShader(const GLenum shaderType, const char *shaderSource); +unsigned int compileShader(const GLenum shaderType, const char* shaderSource); int shaderCompilationSuccessful(const unsigned int shader); int shaderProgramLinkingSuccessful(const unsigned int shaderProgram); int shaderSuccessful(const unsigned int shader, const bool compilation); diff --git a/Engine/engine/textures.cpp b/Engine/engine/textures.cpp index 274ef7c..099385a 100644 --- a/Engine/engine/textures.cpp +++ b/Engine/engine/textures.cpp @@ -1,138 +1,158 @@ // "Copyright [2023] " #ifndef TEXTURES_CPP #define TEXTURES_CPP -#include -#include -#include "stb_image.h" #include "textures.hpp" -void setTextureSCoordinate(const GLenum textureTarget, const GLint sCoordinateOption, const float* borderColor) -{ - // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexParameter.xhtml - // first argument is texture target (by default we work with 2D textures so it will be GL_TEXTURE_2D) - // second argument is what option we want to set and for which texture axis (s, t or r) - // Third argument is texture wrapping mode - glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, sCoordinateOption); - if(sCoordinateOption == GL_CLAMP_TO_BORDER && borderColor != NULL) - { - // if we use GL_CLAMP_TO_BORDER all of the space that is not occupied by texture will be occupied by color - // we pass the color we want to use in float array (RGB + transparency) - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); +#include +#include + +#include "stb_image.h" + +void setTextureSCoordinate(const GLenum textureTarget, + const GLint sCoordinateOption, + const float* borderColor) { + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexParameter.xhtml + // first argument is texture target (by default we work with 2D textures so it + // will be GL_TEXTURE_2D) second argument is what option we want to set and + // for which texture axis (s, t or r) Third argument is texture wrapping mode + glTexParameteri(textureTarget, GL_TEXTURE_WRAP_S, sCoordinateOption); + if (sCoordinateOption == GL_CLAMP_TO_BORDER && borderColor != NULL) { + // if we use GL_CLAMP_TO_BORDER all of the space that is not occupied by + // texture will be occupied by color we pass the color we want to use in + // float array (RGB + transparency) + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); + } +} + +void setTextureTCoordinate(const GLenum textureTarget, + const GLint tCoordinateOption, + const float* borderColor) { + // for comments concerning glTexParameteri and glTexParameterfv go to + // setTextureSCoordinate function + if (textureTarget == GL_TEXTURE_2D || textureTarget == GL_TEXTURE_3D) { + glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, tCoordinateOption); + if (tCoordinateOption == GL_CLAMP_TO_BORDER && borderColor != NULL) { + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); } + } } -void setTextureTCoordinate(const GLenum textureTarget, const GLint tCoordinateOption, const float* borderColor) -{ - // for comments concerning glTexParameteri and glTexParameterfv go to setTextureSCoordinate function - if(textureTarget == GL_TEXTURE_2D || textureTarget == GL_TEXTURE_3D) - { - glTexParameteri(textureTarget, GL_TEXTURE_WRAP_T, tCoordinateOption); - if(tCoordinateOption == GL_CLAMP_TO_BORDER && borderColor != NULL) - { - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); - } +void setTextureRCoordinate(const GLenum textureTarget, + const GLint rCoordinateOption, + const float* borderColor) { + // for comments concerning glTexParameteri and glTexParameterfv go to + // setTextureSCoordinate function + if (textureTarget == GL_TEXTURE_3D) { + glTexParameteri(textureTarget, GL_TEXTURE_WRAP_R, rCoordinateOption); + if (rCoordinateOption == GL_CLAMP_TO_BORDER && borderColor != NULL) { + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); } + } } -void setTextureRCoordinate(const GLenum textureTarget, const GLint rCoordinateOption, const float* borderColor) -{ - // for comments concerning glTexParameteri and glTexParameterfv go to setTextureSCoordinate function - if(textureTarget == GL_TEXTURE_3D) - { - glTexParameteri(textureTarget, GL_TEXTURE_WRAP_R, rCoordinateOption); - if(rCoordinateOption == GL_CLAMP_TO_BORDER && borderColor != NULL) - { - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); - } - } +// Coordinate options can be: GL_REPEAT, GL_MIRRORED_REPEAT, GL_CLAMP_TO_EDGE, +// GL_CLAMP_TO_BORDER s t r are equivalent to x y z +void setTextureParametersINT(const GLenum textureTarget, + const GLint sCoordinateOption, + const GLint tCoordinateOption, + const GLint rCoordinateOption, + const float* borderColor) { + setTextureSCoordinate(textureTarget, sCoordinateOption, borderColor); + setTextureTCoordinate(textureTarget, tCoordinateOption, borderColor); + setTextureRCoordinate(textureTarget, rCoordinateOption, borderColor); } -// Coordinate options can be: GL_REPEAT, GL_MIRRORED_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER -// s t r are equivalent to x y z -void setTextureParametersINT(const GLenum textureTarget, const GLint sCoordinateOption, const GLint tCoordinateOption, const GLint rCoordinateOption, const float* borderColor) -{ - setTextureSCoordinate(textureTarget, sCoordinateOption, borderColor); - setTextureTCoordinate(textureTarget, tCoordinateOption, borderColor); - setTextureRCoordinate(textureTarget, rCoordinateOption, borderColor); +void setTextureFilteringAndMipMap(const GLenum textureTarget, + const GLenum filterType, + const GLint textureFilteringMethod, + const GLint mipMapFilteringMethod) { + // mipMapFilteringMethod can be equal to: GL_NEAREST_MIPMAP_NEAREST, + // GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR + // NEAREST_MIPMAP - take nearest mipmap to match pixel size + // LINEAR_MIPMAP - interpolate between two mipmaps that match the size of a + // pixel NEAREST (at the end) - samples texture based on nearest neighbor + // interpolation LINEAR (at the end) - samples texture using linear + // interpolation setting anything else than GL_TEXTURE_MIN_FILTER as a second + // parameter results in GL_INVALID_ENUM error + glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, mipMapFilteringMethod); + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexParameter.xhtml + // first argument is texture target (by default we work with 2D textures so it + // will be GL_TEXTURE_2D) second argument is specifying whether this filter + // works for magnifying textures GL_TEXTURE_MAG_FILTER or minifying + // GL_TEXTURE_MIN_FILTER third argument is how we should magnify/minify + // textures, whether we should choose the color of pixel closest to the + // coordinates GL_NEAREST (default) or should we blend the neighboring pixels + // and choose blended color GL_LINEAR + glTexParameteri(textureTarget, filterType, textureFilteringMethod); } -void setTextureFilteringAndMipMap(const GLenum textureTarget, const GLenum filterType, const GLint textureFilteringMethod, const GLint mipMapFilteringMethod) -{ - // mipMapFilteringMethod can be equal to: GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR - // NEAREST_MIPMAP - take nearest mipmap to match pixel size - // LINEAR_MIPMAP - interpolate between two mipmaps that match the size of a pixel - // NEAREST (at the end) - samples texture based on nearest neighbor interpolation - // LINEAR (at the end) - samples texture using linear interpolation - // setting anything else than GL_TEXTURE_MIN_FILTER as a second parameter results in GL_INVALID_ENUM error - glTexParameteri(textureTarget, GL_TEXTURE_MIN_FILTER, mipMapFilteringMethod); - // https://registry.khronos.org/OpenGL-Refpages/gl4/html/glTexParameter.xhtml - // first argument is texture target (by default we work with 2D textures so it will be GL_TEXTURE_2D) - // second argument is specifying whether this filter works for magnifying textures GL_TEXTURE_MAG_FILTER or minifying GL_TEXTURE_MIN_FILTER - // third argument is how we should magnify/minify textures, whether we should choose the color of pixel closest to the coordinates GL_NEAREST (default) - // or should we blend the neighboring pixels and choose blended color GL_LINEAR - glTexParameteri(textureTarget, filterType, textureFilteringMethod); +unsigned int generateAndBindTexture(const GLenum textureTarget, + const GLsizei numberOfTextures) { + unsigned int texture; + // first argument is how many textures we want to generate + // second is an array of unsigned int in which we will store id of those + // textures + glGenTextures(numberOfTextures, &texture); + // then we bind texture + glBindTexture(textureTarget, texture); + return texture; } - -unsigned int generateAndBindTexture(const GLenum textureTarget, const GLsizei numberOfTextures ) { - unsigned int texture; - // first argument is how many textures we want to generate - // second is an array of unsigned int in which we will store id of those textures - glGenTextures(numberOfTextures, &texture); - // then we bind texture - glBindTexture(textureTarget, texture); +unsigned int loadAndBindTextureFile(unsigned int texture, + const char* texturePath, bool flipImage) { + int textureWidth, textureHeight, colorChannels; + stbi_set_flip_vertically_on_load(flipImage); + unsigned char* data = stbi_load(texturePath, &textureWidth, &textureHeight, + &colorChannels, STBI_rgb_alpha); + if (data) { + std::cout << "textureWidth " << textureWidth << " textureHeight " + << textureHeight << " colorChannels " << colorChannels + << std::endl; + // generate texture + /* + first argument is texture target, setting it to GL_TEXTURE_2D will only + affect 2D targets and not 1D or 3D 2nd is mipmap level for which to + create texture, base level is 0 3rd is in what format we want to store + texture (our image has only rgb values so rgb) 4th and 5th are the width + and height of texture 6th argument is always 0 (legacy, its called border + parameter) 7th and 8th are the format and datatype of source image, we + store the image data as chars (bytes) so we pass that 9th last argument + is actual data of the texture + */ + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, + GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap(GL_TEXTURE_2D); + // after generating texture, lets free the memory from image + stbi_image_free(data); return texture; + } else { + std::cout << "Failed to load texture! " << std::endl; + return -1; + } } -unsigned int loadAndBindTextureFile(unsigned int texture, const char* texturePath, bool flipImage) { - int textureWidth, textureHeight, colorChannels; - stbi_set_flip_vertically_on_load(flipImage); - unsigned char *data = stbi_load(texturePath, &textureWidth, &textureHeight, &colorChannels, STBI_rgb_alpha); - if(data) { - std::cout << "textureWidth " << textureWidth << " textureHeight " << textureHeight << " colorChannels " << colorChannels << std::endl; - // generate texture - /* - first argument is texture target, setting it to GL_TEXTURE_2D will only affect 2D targets and not 1D or 3D - 2nd is mipmap level for which to create texture, base level is 0 - 3rd is in what format we want to store texture (our image has only rgb values so rgb) - 4th and 5th are the width and height of texture - 6th argument is always 0 (legacy, its called border parameter) - 7th and 8th are the format and datatype of source image, we store the image data as chars (bytes) so we pass that - 9th last argument is actual data of the texture - */ - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - glGenerateMipmap(GL_TEXTURE_2D); - // after generating texture, lets free the memory from image - stbi_image_free(data); - return texture; - } - else - { - std::cout << "Failed to load texture! " << std::endl; - return -1; - } -} - -// const char* texturePath, int textureWidth, int textureHeight, int colorChannels, const GLenum textureTarget, const GLint sCoordinateOption, const GLint tCoordinateOption, const GLint rCoordinateOption, const float* borderColor +// const char* texturePath, int textureWidth, int textureHeight, int +// colorChannels, const GLenum textureTarget, const GLint sCoordinateOption, +// const GLint tCoordinateOption, const GLint rCoordinateOption, const float* +// borderColor unsigned int loadAndCreateTexture(const char* texturePath, bool flipImage) { - // load and create a texture - // ------------------------- - unsigned int texture = generateAndBindTexture(GL_TEXTURE_2D, 1); - setTextureParametersINT(GL_TEXTURE_2D, GL_REPEAT, GL_REPEAT); + // load and create a texture + // ------------------------- + unsigned int texture = generateAndBindTexture(GL_TEXTURE_2D, 1); + setTextureParametersINT(GL_TEXTURE_2D, GL_REPEAT, GL_REPEAT); - // set texture filtering parameters - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - // load image, create texture and generate mipmaps - texture = loadAndBindTextureFile(texture, texturePath, flipImage); - return texture; + // set texture filtering parameters + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + // load image, create texture and generate mipmaps + texture = loadAndBindTextureFile(texture, texturePath, flipImage); + return texture; } -void activateAndBindTextures(unsigned int texture, GLenum textureNumber) -{ - glActiveTexture(textureNumber); - glBindTexture(GL_TEXTURE_2D, texture); +void activateAndBindTextures(unsigned int texture, GLenum textureNumber) { + glActiveTexture(textureNumber); + glBindTexture(GL_TEXTURE_2D, texture); } -#endif // TEXTURES_CPP +#endif // TEXTURES_CPP diff --git a/Engine/engine/textures.hpp b/Engine/engine/textures.hpp index 2c6339e..0f0be80 100644 --- a/Engine/engine/textures.hpp +++ b/Engine/engine/textures.hpp @@ -1,20 +1,39 @@ // "Copyright [2023] " #ifndef TEXTURES_HPP #define TEXTURES_HPP -#include -#include #include +#include -void setTextureParametersINT(const GLenum textureTarget = GL_TEXTURE_2D, const GLint sCoordinateOption = GL_REPEAT, const GLint tCoordinateOption = GL_REPEAT, const GLint rCoordinateOption = GL_REPEAT, const float* borderColor = NULL); -void setTextureSCoordinate(const GLenum textureTarget = GL_TEXTURE_2D, const GLint sCoordinateOption = GL_REPEAT, const float* borderColor = NULL); -void setTextureTCoordinate(const GLenum textureTarget = GL_TEXTURE_2D, const GLint tCoordinateOption = GL_REPEAT, const float* borderColor = NULL); -void setTextureRCoordinate(const GLenum textureTarget = GL_TEXTURE_3D, const GLint rCoordinateOption = GL_REPEAT, const float* borderColor = NULL); -void setTextureFilteringAndMipMap(const GLenum textureTarget = GL_TEXTURE_2D, const GLenum filterType = GL_TEXTURE_MAG_FILTER, const GLint textureFilteringMethod = GL_NEAREST, const GLint mipMapFilteringMethod = GL_NEAREST_MIPMAP_NEAREST); +#include + +void setTextureParametersINT(const GLenum textureTarget = GL_TEXTURE_2D, + const GLint sCoordinateOption = GL_REPEAT, + const GLint tCoordinateOption = GL_REPEAT, + const GLint rCoordinateOption = GL_REPEAT, + const float* borderColor = NULL); +void setTextureSCoordinate(const GLenum textureTarget = GL_TEXTURE_2D, + const GLint sCoordinateOption = GL_REPEAT, + const float* borderColor = NULL); +void setTextureTCoordinate(const GLenum textureTarget = GL_TEXTURE_2D, + const GLint tCoordinateOption = GL_REPEAT, + const float* borderColor = NULL); +void setTextureRCoordinate(const GLenum textureTarget = GL_TEXTURE_3D, + const GLint rCoordinateOption = GL_REPEAT, + const float* borderColor = NULL); +void setTextureFilteringAndMipMap( + const GLenum textureTarget = GL_TEXTURE_2D, + const GLenum filterType = GL_TEXTURE_MAG_FILTER, + const GLint textureFilteringMethod = GL_NEAREST, + const GLint mipMapFilteringMethod = GL_NEAREST_MIPMAP_NEAREST); unsigned int generateAndBindTexture(const GLenum textureTarget); -// const char* texturePath, int textureWidth, int textureHeight, int colorChannels, const GLenum textureTarget, const GLint sCoordinateOption, const GLint tCoordinateOption, const GLint rCoordinateOption, const float* borderColor +// const char* texturePath, int textureWidth, int textureHeight, int +// colorChannels, const GLenum textureTarget, const GLint sCoordinateOption, +// const GLint tCoordinateOption, const GLint rCoordinateOption, const float* +// borderColor unsigned int loadAndCreateTexture(const char* texturePath, bool flipImage); void activateAndBindTextures(unsigned int texture, GLenum textureNumber); -unsigned int loadAndBindTextureFile(unsigned int texture, const char* texturePath, bool flipImage); +unsigned int loadAndBindTextureFile(unsigned int texture, + const char* texturePath, bool flipImage); -#endif // TEXTURES_HPP \ No newline at end of file +#endif // TEXTURES_HPP \ No newline at end of file