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