engineer-thesis-WUT/Engine/engine/match.cpp

380 lines
14 KiB
C++
Raw Normal View History

2022-08-29 21:01:55 +02:00
#ifndef MAIN_CPP
2022-08-28 19:20:00 +02:00
#include <glad/glad.h>
2022-08-27 21:43:58 +02:00
#include <GLFW/glfw3.h>
2022-08-28 19:20:00 +02:00
2022-08-29 19:53:36 +02:00
#include <iostream>
2022-08-30 20:02:22 +02:00
#include <random> // I am using standart library RNG because I am lazy and wanted to create quick code snippet
// upgrade to this: https://arvid.io/2018/06/30/on-cxx-random-number-generator-quality/ whenever, if ever I feel like it
#include <chrono> // for std::chrono
2022-08-29 21:01:55 +02:00
#include "constants.hpp"
void configureGLFW() {
// 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
2022-08-29 19:53:36 +02:00
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
2022-08-28 19:20:00 +02:00
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
2022-08-29 21:01:55 +02:00
// 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);
#endif
}
2022-08-28 19:20:00 +02:00
2022-08-29 21:01:55 +02:00
void instantiateGLFWwindow() {
// Initialize GLFW
glfwInit();
configureGLFW();
}
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, NULL, NULL);
return window;
}
int initializeGLAD()
2022-08-29 19:53:36 +02:00
{
2022-08-29 21:01:55 +02:00
// we load address of OGL OS-specific function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
return 0;
2022-08-28 19:20:00 +02:00
}
2022-08-27 21:43:58 +02:00
2022-08-29 21:01:55 +02:00
// resizes viewport when user resizes window
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
glViewport(0, 0, width, height);
}
2022-08-29 19:53:36 +02:00
2022-08-29 21:01:55 +02:00
void viewPort(GLFWwindow* window)
2022-08-28 19:20:00 +02:00
{
2022-08-29 21:01:55 +02:00
// 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
}
2022-09-04 21:25:01 +02:00
bool processInput(GLFWwindow *window, bool whatToDraw)
2022-08-30 20:02:22 +02:00
{
// 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);
2022-09-04 21:25:01 +02:00
if(glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS || glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS ) return !whatToDraw;
return whatToDraw;
2022-08-30 20:02:22 +02:00
}
2022-09-01 20:08:15 +02:00
int shaderProgramLinkingSuccessful(const unsigned int shaderProgram)
{
// check if compilation was successful
// int because glGetShaderiv requires int
int successfulLinking;
// here we store info about compilation
char infoLog[512];
// check if compilation was successful
glGetProgramiv(shaderProgram, GL_LINK_STATUS, &successfulLinking);
// if not display compilation log
if(!successfulLinking)
{
glGetProgramInfoLog(shaderProgram, sizeof(infoLog) / sizeof(infoLog[0]), NULL, infoLog);
std::cout << "ERROR shaderProgram compilation failed \n" << infoLog << std::endl;
}
return successfulLinking;
}
int shaderCompilationSuccessful(const unsigned int shader)
{
// check if compilation was successful
// int because glGetShaderiv requires int
int successfulCompilation;
// here we store info about compilation
char infoLog[512];
// check if compilation was successful
glGetShaderiv(shader, GL_COMPILE_STATUS, &successfulCompilation);
// if not display compilation log
if(!successfulCompilation)
{
glGetShaderInfoLog(shader, sizeof(infoLog) / sizeof(infoLog[0]), NULL, infoLog);
std::cout << "ERROR vertex shader compilation failed \n" << infoLog << std::endl;
}
return successfulCompilation;
}
unsigned int compileShader(const GLenum shaderType, const char * shaderSource)
{
// we create vertex shader and assign its id to shader variable
unsigned int shaderID;
2022-09-02 19:57:09 +02:00
shaderID = glCreateShader(shaderType);
2022-09-01 20:08:15 +02:00
// 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 NULL)
glShaderSource(shaderID, 1, &shaderSource, NULL);
// compile shader
glCompileShader(shaderID);
if(!shaderCompilationSuccessful(shaderID)) return 0;
return shaderID;
}
2022-09-04 21:25:01 +02:00
template <typename T>
2022-09-02 19:57:09 +02:00
// https://stackoverflow.com/a/25680092
2022-09-04 21:25:01 +02:00
unsigned int copyVerticesMemory(const T vertices[], const size_t sizeOfVertices, const GLenum boundBufferTarget)
2022-08-30 20:02:22 +02:00
{
2022-08-31 20:40:41 +02:00
// 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
2022-09-04 21:25:01 +02:00
glBindBuffer(boundBufferTarget, vertexBufferObject);
2022-08-31 20:40:41 +02:00
// 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.
*/
2022-09-04 21:25:01 +02:00
glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW);
2022-09-02 19:57:09 +02:00
return vertexBufferObject;
}
2022-08-30 20:02:22 +02:00
2022-09-02 19:57:09 +02:00
std::pair<unsigned int, unsigned int> compileShaders()
{
2022-09-01 20:08:15 +02:00
unsigned int vertexShader = compileShader(GL_VERTEX_SHADER, constants::vertexShaderSource);
2022-09-04 21:25:01 +02:00
if(vertexShader == 0)
{
std::cout << "Vertex Shader Compilation Failed" << std::endl;
return std::make_pair(0, 0);
}
2022-09-01 20:08:15 +02:00
unsigned int fragmentShader = compileShader(GL_FRAGMENT_SHADER, constants::fragmentShaderSource);
2022-09-04 21:25:01 +02:00
if(fragmentShader == 0)
{
std::cout << "Fragment Shader Compilation Failed" << std::endl;
return std::make_pair(0, 0);
}
2022-09-02 19:57:09 +02:00
return std::make_pair(vertexShader, fragmentShader);
}
2022-09-01 20:08:15 +02:00
2022-09-02 19:57:09 +02:00
unsigned int linkShaderObjectsShaderProgram(unsigned int vertexShaders, unsigned int fragmentShader)
{
2022-09-01 20:08:15 +02:00
// link shader objects into shader program
// will store shader program id
unsigned int shaderProgram;
// creates program
shaderProgram = glCreateProgram();
// attachShaders
2022-09-02 19:57:09 +02:00
glAttachShader(shaderProgram, vertexShaders);
2022-09-01 20:08:15 +02:00
glAttachShader(shaderProgram, fragmentShader);
// link shaders
glLinkProgram(shaderProgram);
2022-09-02 19:57:09 +02:00
if(!shaderProgramLinkingSuccessful(shaderProgram)) return 0;
2022-09-01 20:08:15 +02:00
// 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)
2022-09-02 19:57:09 +02:00
glDeleteShader(vertexShaders);
2022-09-01 20:08:15 +02:00
glDeleteShader(fragmentShader);
2022-09-04 21:25:01 +02:00
if(shaderProgram == 0) std::cout << "Shader Program Linking Failed" << std::endl;
2022-09-02 19:57:09 +02:00
return shaderProgram;
}
void configureVertexAttribute()
{
// 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
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// enable vertex attribute
glEnableVertexAttribArray(0);
}
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;
}
2022-09-04 21:25:01 +02:00
void copyVerticesArray(unsigned int vertexBufferObject, const float vertices[], const size_t sizeOfVertices, const GLenum boundBufferTarget)
2022-09-02 19:57:09 +02:00
{
// copy vertices array in array useful for OGL
2022-09-04 21:25:01 +02:00
glBindBuffer(boundBufferTarget, vertexBufferObject);
glBufferData(boundBufferTarget, sizeOfVertices, vertices, GL_STATIC_DRAW);
2022-09-02 19:57:09 +02:00
}
2022-09-04 21:25:01 +02:00
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);
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);
}
2022-09-02 19:57:09 +02:00
2022-09-04 21:25:01 +02:00
int drawTriangle()
{
unsigned int vertexBufferObject = copyVerticesMemory(constants::TRIANGLE_VERTICES, constants::TRIANGLE_VERTICES_SIZE, GL_ARRAY_BUFFER);
2022-09-02 19:57:09 +02:00
std::pair<unsigned int, unsigned int> shaders = compileShaders();
2022-09-04 21:25:01 +02:00
if(shaders.first == 0 || shaders.second == 0) return -1;
2022-09-02 19:57:09 +02:00
unsigned int shaderProgram = linkShaderObjectsShaderProgram(shaders.first, shaders.second);
2022-09-04 21:25:01 +02:00
if(shaderProgram == 0) return -1;
2022-09-02 19:57:09 +02:00
configureVertexAttribute();
unsigned int vertexArrayObject = generateBindVAO();
2022-09-04 21:25:01 +02:00
copyVerticesArray(vertexBufferObject, constants::TRIANGLE_VERTICES, constants::TRIANGLE_VERTICES_SIZE, GL_ARRAY_BUFFER);
2022-09-02 19:57:09 +02:00
// set vertex attribute pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
2022-09-04 21:25:01 +02:00
doDrawArrays(shaderProgram, vertexArrayObject, GL_TRIANGLES, 0, 3);
return 0;
}
void doDrawElements(const unsigned int shaderProgram, const unsigned int vertexArrayObject, const GLenum drawArrayMode, const GLenum drawType, const int numberOfElementsToDraw)
{
2022-09-02 19:57:09 +02:00
glUseProgram(shaderProgram);
glBindVertexArray(vertexArrayObject);
2022-09-04 21:25:01 +02:00
glDrawElements(drawArrayMode, numberOfElementsToDraw, drawType, 0);
glBindVertexArray(0);
}
int drawSquare()
{
std::pair<unsigned int, unsigned int> shaders = compileShaders();
if(shaders.first == 0 || shaders.second == 0) return -1;
unsigned int shaderProgram = linkShaderObjectsShaderProgram(shaders.first, shaders.second);
if(shaderProgram == 0) return -1;
unsigned int VAO = generateBindVAO();
copyVerticesMemory<float>(constants::SQUARE_VERTICES, constants::SQUARE_VERTICES_SIZE, GL_ARRAY_BUFFER);
copyVerticesMemory<unsigned int>(constants::SQUARE_INDICES, constants::SQUARE_INDICES_SIZE, GL_ELEMENT_ARRAY_BUFFER);
// set vertex attribute pointers
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
doDrawElements(shaderProgram, VAO, GL_TRIANGLES, GL_UNSIGNED_INT, std::size(constants::SQUARE_INDICES));
return 0;
}
int drawFigure(const bool whatToDraw)
{
if(whatToDraw)
{
if(drawTriangle() == -1)
{
std::cout << "Error with drawing triangle! " << std::endl;
return -1;
}
}else
2022-09-02 19:57:09 +02:00
{
2022-09-04 21:25:01 +02:00
if(drawSquare() == -1)
{
std::cout << "Error with drawing square! " << std::endl;
return -1;
}
2022-09-02 19:57:09 +02:00
}
2022-09-04 21:25:01 +02:00
return 0;
2022-08-30 20:02:22 +02:00
}
2022-09-01 20:08:15 +02:00
2022-08-29 21:01:55 +02:00
void renderLoop(GLFWwindow* window)
{
2022-09-04 21:25:01 +02:00
bool whatToDraw = true;
2022-08-30 20:02:22 +02:00
// glfwWindowShouldClose checks if GLFW was instructed to close
2022-08-29 21:01:55 +02:00
while(!glfwWindowShouldClose(window))
{
2022-08-30 20:02:22 +02:00
// input
2022-09-04 21:25:01 +02:00
whatToDraw = processInput(window, whatToDraw);
2022-08-31 20:40:41 +02:00
// 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
2022-08-30 20:02:22 +02:00
glClear(GL_COLOR_BUFFER_BIT);
2022-09-04 21:25:01 +02:00
if(drawFigure(whatToDraw) == -1)
{
std::cout << "error with drawing!" << std::endl;
glfwSetWindowShouldClose(window, true);
};
2022-08-30 20:02:22 +02:00
// 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
2022-08-29 21:01:55 +02:00
glfwSwapBuffers(window);
2022-08-30 20:02:22 +02:00
// glfwPollEvents checks if any event (like mouse/keyboard input was triggered), updates window state and calls functions (which we register via callback methods)
2022-08-29 21:01:55 +02:00
glfwPollEvents();
}
}
int main()
{
instantiateGLFWwindow();
GLFWwindow* window = createWindowObject();
// function returns GLFWWindow object
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
// we make context of this window main context of current thread
glfwMakeContextCurrent(window);
if(initializeGLAD() == -1) return -1;
viewPort(window);
2022-09-02 19:57:09 +02:00
2022-08-29 21:01:55 +02:00
renderLoop(window);
2022-08-30 20:02:22 +02:00
// clean GLFW resources
glfwTerminate();
2022-08-28 19:20:00 +02:00
return 0;
2022-08-29 21:01:55 +02:00
}
#endif